Resolve stash conflicts and integrate team changes

This commit is contained in:
2026-02-25 10:44:10 -07:00
parent 844c86bd52
commit 07100f658b
21 changed files with 516 additions and 63 deletions

0
mvnw vendored Normal file → Executable file
View File

View File

@@ -1,11 +1,12 @@
package org.example.petshopdesktop.auth;
public class UserSession {
private static UserSession instance;
private Integer userId;
private Integer employeeId;
private String username;
private String employeeName;
private Role role;
private UserSession() {}
@@ -17,29 +18,47 @@ public class UserSession {
return instance;
}
public void login(String username, Role role) {
public void login(int userId, int employeeId, String username, String employeeName, Role role) {
this.userId = userId;
this.employeeId = employeeId;
this.username = username;
this.employeeName = employeeName;
this.role = role;
}
public void logout() {
this.userId = null;
this.employeeId = null;
this.username = null;
this.employeeName = null;
this.role = null;
}
public Integer getUserId() {
return userId;
}
public Integer getEmployeeId() {
return employeeId;
}
public String getUsername() {
return username;
}
public Role getRole() {
return role;
public String getEmployeeName() {
return employeeName;
}
public boolean isAdmin() {
return Role.ADMIN.equals(role);
public Role getRole() {
return role;
}
public boolean isLoggedIn() {
return username != null && role != null;
}
public boolean isAdmin() {
return Role.ADMIN.equals(role);
}
}

View File

@@ -18,6 +18,7 @@ import org.example.petshopdesktop.database.PetDB;
import org.example.petshopdesktop.models.Adoption;
import org.example.petshopdesktop.models.Customer;
import org.example.petshopdesktop.models.Pet;
import org.example.petshopdesktop.util.ActivityLogger;
import java.sql.SQLException;
import java.time.LocalDate;
@@ -82,6 +83,10 @@ public class AdoptionDialogController {
}
catch (SQLException e) {
ActivityLogger.getInstance().logException(
"AdoptionDialogController.initialize",
e,
"Loading pets for combo box");
System.out.println("Error loading pets: " + e.getMessage());
}
@@ -91,6 +96,10 @@ public class AdoptionDialogController {
}
catch (SQLException e) {
ActivityLogger.getInstance().logException(
"AdoptionDialogController.initialize",
e,
"Loading customers for combo box");
System.out.println("Error loading customers: " + e.getMessage());
}
@@ -144,6 +153,10 @@ public class AdoptionDialogController {
}
catch (SQLException e) {
ActivityLogger.getInstance().logException(
"AdoptionDialogController.buttonSaveClicked",
e,
"Inserting new adoption record");
throw new RuntimeException(e);
}
}
@@ -155,6 +168,10 @@ public class AdoptionDialogController {
}
catch (SQLException e) {
ActivityLogger.getInstance().logException(
"AdoptionDialogController.buttonSaveClicked",
e,
"Updating adoption with ID: " + adoption.getAdoptionId());
throw new RuntimeException(e);
}
}
@@ -165,13 +182,12 @@ public class AdoptionDialogController {
alert.setHeaderText("Database Operation Error");
alert.setContentText(mode + " failed");
alert.showAndWait();
closeStage(mouseEvent);
}
//DB operation worked!
else {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setHeaderText("Database Operation Confirmed");
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setHeaderText("Saved");
alert.setContentText(mode + " succeeded");
alert.showAndWait();
closeStage(mouseEvent);

View File

@@ -12,6 +12,7 @@ import javafx.scene.control.ListCell;
import org.example.petshopdesktop.DTOs.AppointmentDTO;
import org.example.petshopdesktop.database.*;
import org.example.petshopdesktop.models.*;
import org.example.petshopdesktop.util.ActivityLogger;
import java.sql.Time;
@@ -71,6 +72,10 @@ public class AppointmentDialogController {
cbCustomer.setItems(CustomerDB.getCustomers());
cbPet.setItems(PetDB.getPets());
} catch (Exception e) {
ActivityLogger.getInstance().logException(
"AppointmentDialogController.initialize",
e,
"Loading combo box data for services, customers, and pets");
e.printStackTrace();
}
@@ -186,6 +191,10 @@ public class AppointmentDialogController {
closeStage(e);
} catch (Exception ex) {
ActivityLogger.getInstance().logException(
"AppointmentDialogController.buttonSaveClicked",
ex,
"Saving appointment in " + mode + " mode");
ex.printStackTrace();
showError("Error saving appointment");
}

View File

@@ -16,6 +16,7 @@ import org.example.petshopdesktop.database.InventoryDB;
import org.example.petshopdesktop.database.ProductDB;
import org.example.petshopdesktop.models.Inventory;
import org.example.petshopdesktop.models.Product;
import org.example.petshopdesktop.util.ActivityLogger;
import java.sql.SQLException;
@@ -65,6 +66,10 @@ public class InventoryDialogController {
}
catch (SQLException e) {
ActivityLogger.getInstance().logException(
"InventoryDialogController.initialize",
e,
"Loading products for combo box");
System.out.println("Error loading products: " + e.getMessage());
}
@@ -118,6 +123,10 @@ public class InventoryDialogController {
}
catch (SQLException e) {
ActivityLogger.getInstance().logException(
"InventoryDialogController.buttonSaveClicked",
e,
"Checking if product exists in inventory");
throw new RuntimeException(e);
}
}
@@ -129,6 +138,10 @@ public class InventoryDialogController {
try {
numRow = InventoryDB.insertInventory(inventory);
} catch (SQLException e) {
ActivityLogger.getInstance().logException(
"InventoryDialogController.buttonSaveClicked",
e,
"Inserting new inventory record");
throw new RuntimeException(e);
}
}
@@ -140,6 +153,10 @@ public class InventoryDialogController {
}
catch (SQLException e) {
ActivityLogger.getInstance().logException(
"InventoryDialogController.buttonSaveClicked",
e,
"Updating inventory with ID: " + inventory.getInventoryId());
throw new RuntimeException(e);
}
}
@@ -150,12 +167,11 @@ public class InventoryDialogController {
alert.setHeaderText("Database Operation Error");
alert.setContentText(mode + " failed");
alert.showAndWait();
closeStage(mouseEvent);
}
else {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setHeaderText("Database Operation Confirmed");
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setHeaderText("Saved");
alert.setContentText(mode + " succeeded");
alert.showAndWait();
closeStage(mouseEvent);

View File

@@ -13,6 +13,7 @@ import org.example.petshopdesktop.Validator;
import org.example.petshopdesktop.database.PetDB;
import org.example.petshopdesktop.models.Category;
import org.example.petshopdesktop.models.Pet;
import org.example.petshopdesktop.util.ActivityLogger;
import java.sql.SQLException;
@@ -107,6 +108,10 @@ public class PetDialogController {
numRow = PetDB.insertPet(pet);
}
catch (SQLException e) {
ActivityLogger.getInstance().logException(
"PetDialogController.buttonSaveClicked",
e,
"Inserting new pet record");
throw new RuntimeException(e);
}
}
@@ -115,6 +120,10 @@ public class PetDialogController {
numRow = PetDB.updatePet(pet.getPetId(), pet);
}
catch (SQLException e) {
ActivityLogger.getInstance().logException(
"PetDialogController.buttonSaveClicked",
e,
"Updating pet with ID: " + pet.getPetId());
throw new RuntimeException(e);
}
}
@@ -125,12 +134,11 @@ public class PetDialogController {
alert.setHeaderText("Database Operation Error");
alert.setContentText(mode + " failed");
alert.showAndWait();
closeStage(mouseEvent);
}
else {
//tell the user operation was successful
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setHeaderText("Database Operation Confirmed");
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setHeaderText("Saved");
alert.setContentText(mode + " succeeded");
alert.showAndWait();
closeStage(mouseEvent);

View File

@@ -15,6 +15,7 @@ import org.example.petshopdesktop.database.ProductDB;
import org.example.petshopdesktop.models.Category;
import org.example.petshopdesktop.models.Product;
import org.example.petshopdesktop.models.Supplier;
import org.example.petshopdesktop.util.ActivityLogger;
import java.sql.SQLException;
import java.util.ArrayList;
@@ -74,6 +75,10 @@ public class ProductDialogController {
categories = CategoryDB.getCategories();
cbProdCategory.setItems(categories);
} catch (SQLException e) {
ActivityLogger.getInstance().logException(
"ProductDialogController.initialize",
e,
"Loading categories for combo box");
throw new RuntimeException(e);
}
@@ -109,6 +114,10 @@ public class ProductDialogController {
try{
numRow = ProductDB.insertProduct(product);
} catch (SQLException e) {
ActivityLogger.getInstance().logException(
"ProductDialogController.buttonSaveClicked",
e,
"Inserting new product record");
throw new RuntimeException(e);
}
}
@@ -116,6 +125,10 @@ public class ProductDialogController {
try{
numRow = ProductDB.updateProduct(product.getProdId(),product);
} catch (SQLException e) {
ActivityLogger.getInstance().logException(
"ProductDialogController.buttonSaveClicked",
e,
"Updating product with ID: " + product.getProdId());
throw new RuntimeException(e);
}
}
@@ -126,12 +139,11 @@ public class ProductDialogController {
alert.setHeaderText("Database Operation Error");
alert.setContentText(mode + " failed");
alert.showAndWait();
closeStage(mouseEvent);
}
else {
//tell the user operation was successful
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setHeaderText("Database Operation Confirmed");
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setHeaderText("Saved");
alert.setContentText(mode + " succeeded");
alert.showAndWait();
closeStage(mouseEvent);

View File

@@ -16,6 +16,7 @@ import org.example.petshopdesktop.database.SupplierDB;
import org.example.petshopdesktop.models.Product;
import org.example.petshopdesktop.models.ProductSupplier;
import org.example.petshopdesktop.models.Supplier;
import org.example.petshopdesktop.util.ActivityLogger;
import java.sql.SQLException;
import java.sql.SQLIntegrityConstraintViolationException;
@@ -80,6 +81,10 @@ public class ProductSupplierDialogController {
cbProduct.setItems(products);
}
catch(SQLException e){
ActivityLogger.getInstance().logException(
"ProductSupplierDialogController.initialize",
e,
"Loading suppliers and products for combo boxes");
throw new RuntimeException(e);
}
@@ -116,6 +121,10 @@ public class ProductSupplierDialogController {
numRows = ProductSupplierDB.insertProductSupplier(productSupplier);
}
catch(SQLIntegrityConstraintViolationException e){
ActivityLogger.getInstance().logException(
"ProductSupplierDialogController.buttonSaveClicked",
e,
"Inserting product-supplier (integrity constraint violation)");
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setHeaderText("Database Operation Error");
alert.setContentText("Add failed \n" +
@@ -125,6 +134,10 @@ public class ProductSupplierDialogController {
closeStage(mouseEvent);
}
catch(SQLException e){
ActivityLogger.getInstance().logException(
"ProductSupplierDialogController.buttonSaveClicked",
e,
"Inserting new product-supplier record");
throw new RuntimeException(e);
}
}
@@ -133,6 +146,10 @@ public class ProductSupplierDialogController {
numRows = ProductSupplierDB.updateProductSupplier(selectedSupId, selectedProdId, productSupplier);
}
catch(SQLIntegrityConstraintViolationException e){
ActivityLogger.getInstance().logException(
"ProductSupplierDialogController.buttonSaveClicked",
e,
"Updating product-supplier (integrity constraint violation) - SupID: " + selectedSupId + ", ProdID: " + selectedProdId);
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setHeaderText("Database Operation Error");
alert.setContentText("Edit failed \n" +
@@ -142,6 +159,10 @@ public class ProductSupplierDialogController {
closeStage(mouseEvent);
}
catch(SQLException e){
ActivityLogger.getInstance().logException(
"ProductSupplierDialogController.buttonSaveClicked",
e,
"Updating product-supplier - SupID: " + selectedSupId + ", ProdID: " + selectedProdId);
throw new RuntimeException(e);
}
}
@@ -152,12 +173,11 @@ public class ProductSupplierDialogController {
alert.setHeaderText("Database Operation Error");
alert.setContentText(mode + " failed");
alert.showAndWait();
closeStage(mouseEvent);
}
else if (numRows > 0){
//tell the user operation was successful
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setHeaderText("Database Operation Confirmed");
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setHeaderText("Saved");
alert.setContentText(mode + " succeeded");
alert.showAndWait();
closeStage(mouseEvent);

View File

@@ -10,6 +10,7 @@ import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
import org.example.petshopdesktop.database.ServiceDB;
import org.example.petshopdesktop.models.Service;
import org.example.petshopdesktop.util.ActivityLogger;
import javafx.scene.control.Alert;
import javafx.scene.control.ComboBox;
@@ -134,6 +135,10 @@ public class ServiceDialogController {
close();
} catch (Exception e) {
ActivityLogger.getInstance().logException(
"ServiceDialogController.saveService",
e,
"Saving service in " + mode + " mode");
e.printStackTrace();
showError("Database error while saving service.");
}

View File

@@ -12,6 +12,7 @@ import javafx.stage.Stage;
import org.example.petshopdesktop.Validator;
import org.example.petshopdesktop.database.SupplierDB;
import org.example.petshopdesktop.models.Supplier;
import org.example.petshopdesktop.util.ActivityLogger;
import java.sql.SQLException;
@@ -99,6 +100,10 @@ public class SupplierDialogController {
try{
numRow = SupplierDB.insertSupplier(supplier);
} catch (SQLException e) {
ActivityLogger.getInstance().logException(
"SupplierDialogController.buttonSaveClicked",
e,
"Inserting new supplier record");
throw new RuntimeException(e);
}
}
@@ -106,6 +111,10 @@ public class SupplierDialogController {
try{
numRow = SupplierDB.updateSupplier(supplier.getSupId(),supplier);
} catch (SQLException e) {
ActivityLogger.getInstance().logException(
"SupplierDialogController.buttonSaveClicked",
e,
"Updating supplier with ID: " + supplier.getSupId());
throw new RuntimeException(e);
}
}
@@ -116,12 +125,11 @@ public class SupplierDialogController {
alert.setHeaderText("Database Operation Error");
alert.setContentText(mode + " failed");
alert.showAndWait();
closeStage(mouseEvent);
}
else {
//tell the user operation was successful
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setHeaderText("Database Operation Confirmed");
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setHeaderText("Saved");
alert.setContentText(mode + " succeeded");
alert.showAndWait();
closeStage(mouseEvent);

View File

@@ -5,6 +5,7 @@ import javafx.collections.ObservableList;
import org.example.petshopdesktop.models.Adoption;
import org.example.petshopdesktop.models.Customer;
import org.example.petshopdesktop.models.Pet;
import org.example.petshopdesktop.util.ActivityLogger;
import java.sql.*;
@@ -76,6 +77,14 @@ public class AdoptionDB {
int numRows = stmt.executeUpdate();
conn.close();
// Log the operation
if (numRows > 0) {
ActivityLogger.getInstance().logInsert("adoption",
"N/A",
String.format("Adoption record added for Pet ID %d, Customer ID %d", adoption.getPetId(), adoption.getCustomerId()));
}
return numRows;
}
@@ -93,6 +102,14 @@ public class AdoptionDB {
int numRows = stmt.executeUpdate();
conn.close();
// Log the operation
if (numRows > 0) {
ActivityLogger.getInstance().logUpdate("adoption",
String.valueOf(adoptionId),
String.format("Adoption ID %d updated", adoptionId));
}
return numRows;
}
@@ -106,6 +123,14 @@ public class AdoptionDB {
int numRows = stmt.executeUpdate();
conn.close();
// Log the operation
if (numRows > 0) {
ActivityLogger.getInstance().logDelete("adoption",
String.valueOf(adoptionId),
String.format("Adoption ID %d deleted", adoptionId));
}
return numRows;
}

View File

@@ -5,6 +5,7 @@ import javafx.collections.ObservableList;
import org.example.petshopdesktop.DTOs.AppointmentDTO;
import org.example.petshopdesktop.models.Appointment;
import org.example.petshopdesktop.util.ActivityLogger;
import java.sql.*;
@@ -103,6 +104,14 @@ public class AppointmentDB {
}
conn.close();
// Log the operation
if (newId > 0) {
ActivityLogger.getInstance().logInsert("appointment",
String.valueOf(newId),
String.format("Appointment created for Customer ID %d, Service ID %d", appt.getCustomerId(), appt.getServiceId()));
}
return newId;
}
@@ -121,9 +130,16 @@ public class AppointmentDB {
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, appointmentId);
ps.setInt(2, petId);
ps.executeUpdate();
int numRows = ps.executeUpdate();
conn.close();
// Log the operation
if (numRows > 0) {
ActivityLogger.getInstance().logInsert("appointmentPet",
String.valueOf(appointmentId),
String.format("Pet ID %d linked to Appointment ID %d", petId, appointmentId));
}
}
//
@@ -161,6 +177,12 @@ public class AppointmentDB {
ps2.executeUpdate();
conn.close();
// Log the operation
ActivityLogger.getInstance().logUpdate("appointment",
String.valueOf(id),
String.format("Appointment ID %d updated", id));
return 1;
}
@@ -188,6 +210,14 @@ public class AppointmentDB {
int rows = ps2.executeUpdate();
conn.close();
// Log the operation
if (rows > 0) {
ActivityLogger.getInstance().logDelete("appointment",
String.valueOf(id),
String.format("Appointment ID %d deleted", id));
}
return rows;
}
}

View File

@@ -1,5 +1,7 @@
package org.example.petshopdesktop.database;
import org.example.petshopdesktop.util.ActivityLogger;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
@@ -43,6 +45,10 @@ public class ConnectionDB {
password = prop.getProperty("password");
}
catch(IOException e){
ActivityLogger.getInstance().logException(
"ConnectionDB.getConnection",
e,
"Reading connection properties file");
throw new RuntimeException("Problem with reading connection info: "+e.getMessage());
}
@@ -50,6 +56,10 @@ public class ConnectionDB {
return DriverManager.getConnection(url,user,password);
}
catch (SQLException e) {
ActivityLogger.getInstance().logException(
"ConnectionDB.getConnection",
e,
"Establishing database connection");
throw new RuntimeException("Problem with database connection: "+e.getMessage());
}
}

View File

@@ -3,6 +3,7 @@ package org.example.petshopdesktop.database;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.example.petshopdesktop.models.Inventory;
import org.example.petshopdesktop.util.ActivityLogger;
import java.sql.*;
@@ -90,6 +91,14 @@ public class InventoryDB {
int numRows = stmt.executeUpdate();
conn.close();
// Log the operation
if (numRows > 0) {
ActivityLogger.getInstance().logInsert("inventory",
"N/A",
String.format("Inventory added for Product ID %d, Quantity %d", inventory.getProdId(), inventory.getQuantity()));
}
return numRows;
}
@@ -105,6 +114,14 @@ public class InventoryDB {
int numRows = stmt.executeUpdate();
conn.close();
// Log the operation
if (numRows > 0) {
ActivityLogger.getInstance().logUpdate("inventory",
String.valueOf(inventoryId),
String.format("Inventory ID %d updated", inventoryId));
}
return numRows;
}
@@ -118,6 +135,14 @@ public class InventoryDB {
int numRows = stmt.executeUpdate();
conn.close();
// Log the operation
if (numRows > 0) {
ActivityLogger.getInstance().logDelete("inventory",
String.valueOf(inventoryId),
String.format("Inventory ID %d deleted", inventoryId));
}
return numRows;
}

View File

@@ -3,6 +3,7 @@ package org.example.petshopdesktop.database;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.example.petshopdesktop.models.Pet;
import org.example.petshopdesktop.util.ActivityLogger;
import java.sql.*;
@@ -98,6 +99,13 @@ public class PetDB {
numRows = stmt.executeUpdate();
conn.close();
// Log the operation
if (numRows > 0) {
ActivityLogger.getInstance().logInsert("pet",
String.valueOf(pet.getPetId()),
String.format("Pet '%s' added", pet.getPetName()));
}
return numRows;
}
@@ -125,6 +133,13 @@ public class PetDB {
numRows = stmt.executeUpdate();
conn.close();
// Log the operation
if (numRows > 0) {
ActivityLogger.getInstance().logUpdate("pet",
String.valueOf(petId),
String.format("Pet '%s' updated", pet.getPetName()));
}
return numRows;
}
@@ -140,6 +155,13 @@ public class PetDB {
numRows = stmt.executeUpdate();
conn.close();
// Log the operation
if (numRows > 0) {
ActivityLogger.getInstance().logDelete("pet",
String.valueOf(petId),
String.format("Pet ID %d deleted", petId));
}
return numRows;
}
}

View File

@@ -5,6 +5,7 @@ import javafx.collections.ObservableList;
import org.example.petshopdesktop.DTOs.ProductDTO;
import org.example.petshopdesktop.models.Product;
import org.example.petshopdesktop.models.Supplier;
import org.example.petshopdesktop.util.ActivityLogger;
import java.sql.*;
@@ -102,6 +103,13 @@ public class ProductDB {
numRows = stmt.executeUpdate();
conn.close();
// Log the operation
if (numRows > 0) {
ActivityLogger.getInstance().logInsert("product",
String.valueOf(product.getProdId()),
String.format("Product '%s' added", product.getProdName()));
}
return numRows;
}
@@ -135,6 +143,13 @@ public class ProductDB {
numRows = stmt.executeUpdate();
conn.close();
// Log the operation
if (numRows > 0) {
ActivityLogger.getInstance().logUpdate("product",
String.valueOf(prodId),
String.format("Product '%s' updated", product.getProdName()));
}
return numRows;
}
@@ -157,6 +172,13 @@ public class ProductDB {
numRows = stmt.executeUpdate();
conn.close();
// Log the operation
if (numRows > 0) {
ActivityLogger.getInstance().logDelete("product",
String.valueOf(prodId),
String.format("Product ID %d deleted", prodId));
}
return numRows;
}

View File

@@ -3,6 +3,7 @@ package org.example.petshopdesktop.database;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.example.petshopdesktop.models.Service;
import org.example.petshopdesktop.util.ActivityLogger;
import java.sql.*;
@@ -58,6 +59,13 @@ public class ServiceDB {
int rows = stmt.executeUpdate();
conn.close();
// Log the operation
if (rows > 0) {
ActivityLogger.getInstance().logInsert("service",
"N/A",
String.format("Service '%s' added", service.getServiceName()));
}
return rows;
}
@@ -84,6 +92,13 @@ public class ServiceDB {
int rows = stmt.executeUpdate();
conn.close();
// Log the operation
if (rows > 0) {
ActivityLogger.getInstance().logUpdate("service",
String.valueOf(id),
String.format("Service '%s' updated", service.getServiceName()));
}
return rows;
}
@@ -102,6 +117,13 @@ public class ServiceDB {
int rows = stmt.executeUpdate();
conn.close();
// Log the operation
if (rows > 0) {
ActivityLogger.getInstance().logDelete("service",
String.valueOf(id),
String.format("Service ID %d deleted", id));
}
return rows;
}
}

View File

@@ -4,6 +4,7 @@ import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.example.petshopdesktop.models.Product;
import org.example.petshopdesktop.models.Supplier;
import org.example.petshopdesktop.util.ActivityLogger;
import java.sql.*;
@@ -68,6 +69,13 @@ public class SupplierDB {
numRows = stmt.executeUpdate();
conn.close();
// Log the operation
if (numRows > 0) {
ActivityLogger.getInstance().logInsert("supplier",
String.valueOf(supplier.getSupId()),
String.format("Supplier '%s' added", supplier.getSupCompany()));
}
return numRows;
}
@@ -103,6 +111,13 @@ public class SupplierDB {
numRows = stmt.executeUpdate();
conn.close();
// Log the operation
if (numRows > 0) {
ActivityLogger.getInstance().logUpdate("supplier",
String.valueOf(supId),
String.format("Supplier '%s' updated", supplier.getSupCompany()));
}
return numRows;
}
@@ -125,6 +140,13 @@ public class SupplierDB {
numRows = stmt.executeUpdate();
conn.close();
// Log the operation
if (numRows > 0) {
ActivityLogger.getInstance().logDelete("supplier",
String.valueOf(supId),
String.format("Supplier ID %d deleted", supId));
}
return numRows;
}

View File

@@ -1,23 +1,25 @@
package org.example.petshopdesktop.database;
import org.example.petshopdesktop.auth.Role;
import org.example.petshopdesktop.models.StaffAccount;
import org.example.petshopdesktop.models.User;
import org.example.petshopdesktop.util.ActivityLogger;
import java.sql.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
public class UserDB {
/**
* Authenticate a user by username and password.
* Passwords are stored as SHA-256 hex digests in the database.
*
* @param username the username to authenticate
* @param password the plaintext password
* @return the User if credentials are valid, or null if authentication fails
*/
public static User authenticate(String username, String password) throws SQLException {
String sql = "SELECT user_id, username, role FROM users " +
"WHERE username = ? AND password_hash = SHA2(?, 256)";
String sql = "SELECT u.user_id, u.employee_id, u.username, u.role, e.firstName, e.lastName "
+ "FROM users u "
+ "JOIN employee e ON u.employee_id = e.employeeId "
+ "WHERE u.username = ? AND u.password_hash = SHA2(?, 256) AND e.isActive = TRUE";
try (Connection conn = ConnectionDB.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
@@ -28,46 +30,169 @@ public class UserDB {
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
int userId = rs.getInt("user_id");
int employeeId = rs.getInt("employee_id");
String uname = rs.getString("username");
Role role = Role.valueOf(rs.getString("role").toUpperCase());
String firstName = rs.getString("firstName");
String lastName = rs.getString("lastName");
return new User(userId, uname, role);
return new User(userId, employeeId, uname, firstName, lastName, role);
}
}
}
return null;
}
/**
* Create the users table and seed default admin/staff accounts if they do not exist.
* Passwords are stored as SHA2-256 hashes.
*/
public static void initializeTable() throws SQLException {
String createTable = """
CREATE TABLE IF NOT EXISTS users (
user_id INT AUTO_INCREMENT PRIMARY KEY,
employee_id INT NOT NULL,
username VARCHAR(100) NOT NULL UNIQUE,
password_hash CHAR(64) NOT NULL,
role ENUM('ADMIN','STAFF') NOT NULL
role ENUM('ADMIN','STAFF') NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
FOREIGN KEY (employee_id) REFERENCES employee(employeeId)
)
""";
String seedAdmin = """
INSERT IGNORE INTO users (username, password_hash, role)
VALUES ('admin', SHA2('admin123', 256), 'ADMIN')
""";
String seedStaff = """
INSERT IGNORE INTO users (username, password_hash, role)
VALUES ('staff', SHA2('staff123', 256), 'STAFF')
""";
try (Connection conn = ConnectionDB.getConnection();
Statement st = conn.createStatement()) {
st.executeUpdate(createTable);
st.executeUpdate(seedAdmin);
st.executeUpdate(seedStaff);
}
ensureCompatibleSchema();
int adminEmployeeId = EmployeeDB.ensureDefaultEmployee("John", "Doe", "john@petshop.com", "111-222-3333", "Manager", true);
int staffEmployeeId = EmployeeDB.ensureDefaultEmployee("Sara", "Smith", "sara@petshop.com", "444-555-6666", "Staff", true);
ensureDefaultUser(adminEmployeeId, "admin", "admin123", Role.ADMIN);
ensureDefaultUser(staffEmployeeId, "staff", "staff123", Role.STAFF);
}
public static int createStaffAccount(String firstName, String lastName, String email, String phone, String username, String password) throws SQLException {
if (username == null || username.isBlank()) {
throw new SQLException("Username is required.");
}
if (password == null || password.isBlank()) {
throw new SQLException("Password is required.");
}
int storeId = EmployeeDB.getDefaultStoreId();
try (Connection conn = ConnectionDB.getConnection()) {
conn.setAutoCommit(false);
try {
int employeeId = EmployeeDB.createEmployee(conn, firstName, lastName, email, phone, "Staff", true);
EmployeeDB.assignEmployeeToStore(conn, employeeId, storeId);
String insertUser = "INSERT INTO users (employee_id, username, password_hash, role) VALUES (?, ?, SHA2(?, 256), 'STAFF')";
try (PreparedStatement ps = conn.prepareStatement(insertUser, Statement.RETURN_GENERATED_KEYS)) {
ps.setInt(1, employeeId);
ps.setString(2, username);
ps.setString(3, password);
ps.executeUpdate();
try (ResultSet keys = ps.getGeneratedKeys()) {
if (keys.next()) {
int userId = keys.getInt(1);
conn.commit();
ActivityLogger.getInstance().logInsert("users", String.valueOf(userId), "Created staff account: " + username);
return userId;
}
}
}
conn.rollback();
throw new SQLException("Could not create staff account.");
} catch (SQLException e) {
conn.rollback();
throw e;
} catch (RuntimeException e) {
conn.rollback();
throw e;
} finally {
conn.setAutoCommit(true);
}
}
}
public static List<StaffAccount> getStaffAccounts() throws SQLException {
String sql = "SELECT u.user_id, u.username, u.created_at, e.employeeId, e.firstName, e.lastName, e.email, e.phone, e.isActive "
+ "FROM users u "
+ "JOIN employee e ON u.employee_id = e.employeeId "
+ "WHERE u.role = 'STAFF' "
+ "ORDER BY u.created_at DESC";
List<StaffAccount> accounts = new ArrayList<>();
try (Connection conn = ConnectionDB.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
accounts.add(new StaffAccount(
rs.getInt("user_id"),
rs.getInt("employeeId"),
rs.getString("username"),
rs.getString("firstName"),
rs.getString("lastName"),
rs.getString("email"),
rs.getString("phone"),
rs.getBoolean("isActive"),
rs.getTimestamp("created_at")
));
}
}
return accounts;
}
private static void ensureCompatibleSchema() throws SQLException {
try (Connection conn = ConnectionDB.getConnection();
Statement st = conn.createStatement()) {
try {
st.executeUpdate("ALTER TABLE users ADD COLUMN employee_id INT NULL");
} catch (SQLException ignored) {
}
try {
st.executeUpdate("ALTER TABLE users ADD COLUMN created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL");
} catch (SQLException ignored) {
}
try {
st.executeUpdate("ALTER TABLE users ADD CONSTRAINT fk_users_employee FOREIGN KEY (employee_id) REFERENCES employee(employeeId)");
} catch (SQLException ignored) {
}
}
}
private static void ensureDefaultUser(int employeeId, String username, String password, Role role) throws SQLException {
String insert = "INSERT IGNORE INTO users (employee_id, username, password_hash, role) VALUES (?, ?, SHA2(?, 256), ?)";
String updateIfMissingEmployeeId = "UPDATE users SET employee_id = ? WHERE username = ? AND (employee_id IS NULL OR employee_id = 0)";
try (Connection conn = ConnectionDB.getConnection()) {
int rows;
try (PreparedStatement ps = conn.prepareStatement(insert)) {
ps.setInt(1, employeeId);
ps.setString(2, username);
ps.setString(3, password);
ps.setString(4, role.name());
rows = ps.executeUpdate();
}
try (PreparedStatement ps = conn.prepareStatement(updateIfMissingEmployeeId)) {
ps.setInt(1, employeeId);
ps.setString(2, username);
ps.executeUpdate();
}
if (rows > 0) {
ActivityLogger.getInstance().logInsert("users", username, "Default " + role.name().toLowerCase() + " user created");
}
}
}
}

View File

@@ -3,13 +3,19 @@ package org.example.petshopdesktop.models;
import org.example.petshopdesktop.auth.Role;
public class User {
private int userId;
private String username;
private Role role;
private final int userId;
private final int employeeId;
private final String username;
private final String employeeFirstName;
private final String employeeLastName;
private final Role role;
public User(int userId, String username, Role role) {
public User(int userId, int employeeId, String username, String employeeFirstName, String employeeLastName, Role role) {
this.userId = userId;
this.employeeId = employeeId;
this.username = username;
this.employeeFirstName = employeeFirstName;
this.employeeLastName = employeeLastName;
this.role = role;
}
@@ -17,10 +23,29 @@ public class User {
return userId;
}
public int getEmployeeId() {
return employeeId;
}
public String getUsername() {
return username;
}
public String getEmployeeFirstName() {
return employeeFirstName;
}
public String getEmployeeLastName() {
return employeeLastName;
}
public String getEmployeeFullName() {
String fn = employeeFirstName == null ? "" : employeeFirstName.trim();
String ln = employeeLastName == null ? "" : employeeLastName.trim();
String full = (fn + " " + ln).trim();
return full.isBlank() ? username : full;
}
public Role getRole() {
return role;
}

View File

@@ -8,7 +8,7 @@
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<VBox alignment="CENTER" prefHeight="400.0" prefWidth="380.0" spacing="16.0"
<VBox alignment="CENTER" prefHeight="420.0" prefWidth="380.0" spacing="16.0"
style="-fx-background-color: #2C3E50;"
xmlns="http://javafx.com/javafx/17"
xmlns:fx="http://javafx.com/fxml/1"
@@ -17,9 +17,9 @@
<Insets bottom="40.0" left="50.0" right="50.0" top="40.0" />
</padding>
<children>
<Label text="🐾 Pet Shop Manager" textFill="WHITE">
<Label text="Pet Shop Manager" textFill="WHITE">
<font>
<Font name="Comic Sans MS Bold" size="22.0" />
<Font name="System Bold" size="22.0" />
</font>
<VBox.margin>
<Insets bottom="10.0" />
@@ -76,5 +76,17 @@
<Insets top="8.0" />
</VBox.margin>
</Button>
<Button fx:id="btnCreateStaff" mnemonicParsing="false" onAction="#btnCreateStaffClicked"
prefWidth="280.0"
style="-fx-background-color: transparent; -fx-text-fill: #D5DDE6; -fx-cursor: hand; -fx-underline: true;"
text="Create Staff Account">
<font>
<Font name="System Bold" size="13.0" />
</font>
<VBox.margin>
<Insets top="4.0" />
</VBox.margin>
</Button>
</children>
</VBox>