From 07100f658b07d16afe905496182685fe76f27e3f Mon Sep 17 00:00:00 2001 From: Harkamal Randhawa Date: Wed, 25 Feb 2026 10:44:10 -0700 Subject: [PATCH] Resolve stash conflicts and integrate team changes --- mvnw | 0 .../petshopdesktop/auth/UserSession.java | 33 +++- .../AdoptionDialogController.java | 22 ++- .../AppointmentDialogController.java | 9 + .../InventoryDialogController.java | 22 ++- .../PetDialogController.java | 14 +- .../ProductDialogController.java | 18 +- .../ProductSupplierDialogController.java | 26 ++- .../ServiceDialogController.java | 5 + .../SupplierDialogController.java | 14 +- .../petshopdesktop/database/AdoptionDB.java | 25 +++ .../database/AppointmentDB.java | 32 ++- .../petshopdesktop/database/ConnectionDB.java | 10 + .../petshopdesktop/database/InventoryDB.java | 25 +++ .../petshopdesktop/database/PetDB.java | 22 +++ .../petshopdesktop/database/ProductDB.java | 22 +++ .../petshopdesktop/database/ServiceDB.java | 22 +++ .../petshopdesktop/database/SupplierDB.java | 22 +++ .../petshopdesktop/database/UserDB.java | 185 +++++++++++++++--- .../example/petshopdesktop/models/User.java | 33 +++- .../example/petshopdesktop/login-view.fxml | 18 +- 21 files changed, 516 insertions(+), 63 deletions(-) mode change 100644 => 100755 mvnw diff --git a/mvnw b/mvnw old mode 100644 new mode 100755 diff --git a/src/main/java/org/example/petshopdesktop/auth/UserSession.java b/src/main/java/org/example/petshopdesktop/auth/UserSession.java index 450fb3ff..8b34353b 100644 --- a/src/main/java/org/example/petshopdesktop/auth/UserSession.java +++ b/src/main/java/org/example/petshopdesktop/auth/UserSession.java @@ -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); + } } diff --git a/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AdoptionDialogController.java b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AdoptionDialogController.java index 40b3c6cb..213426f9 100644 --- a/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AdoptionDialogController.java +++ b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AdoptionDialogController.java @@ -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); diff --git a/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AppointmentDialogController.java b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AppointmentDialogController.java index 61fa04e8..411e230b 100644 --- a/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AppointmentDialogController.java +++ b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AppointmentDialogController.java @@ -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"); } diff --git a/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/InventoryDialogController.java b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/InventoryDialogController.java index 7b0a69c0..b058bf2a 100644 --- a/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/InventoryDialogController.java +++ b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/InventoryDialogController.java @@ -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); diff --git a/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/PetDialogController.java b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/PetDialogController.java index 45be6c3c..45ad7eab 100644 --- a/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/PetDialogController.java +++ b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/PetDialogController.java @@ -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); diff --git a/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/ProductDialogController.java b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/ProductDialogController.java index 3aa29a93..5ad23f81 100644 --- a/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/ProductDialogController.java +++ b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/ProductDialogController.java @@ -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); diff --git a/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/ProductSupplierDialogController.java b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/ProductSupplierDialogController.java index 909363b2..154c62fd 100644 --- a/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/ProductSupplierDialogController.java +++ b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/ProductSupplierDialogController.java @@ -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); diff --git a/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/ServiceDialogController.java b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/ServiceDialogController.java index 4d878324..8bc80252 100644 --- a/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/ServiceDialogController.java +++ b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/ServiceDialogController.java @@ -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."); } diff --git a/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/SupplierDialogController.java b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/SupplierDialogController.java index 34cb91eb..836b5e6c 100644 --- a/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/SupplierDialogController.java +++ b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/SupplierDialogController.java @@ -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); diff --git a/src/main/java/org/example/petshopdesktop/database/AdoptionDB.java b/src/main/java/org/example/petshopdesktop/database/AdoptionDB.java index 6c436479..40efa4d2 100644 --- a/src/main/java/org/example/petshopdesktop/database/AdoptionDB.java +++ b/src/main/java/org/example/petshopdesktop/database/AdoptionDB.java @@ -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; } diff --git a/src/main/java/org/example/petshopdesktop/database/AppointmentDB.java b/src/main/java/org/example/petshopdesktop/database/AppointmentDB.java index 773957ed..747dd0f3 100644 --- a/src/main/java/org/example/petshopdesktop/database/AppointmentDB.java +++ b/src/main/java/org/example/petshopdesktop/database/AppointmentDB.java @@ -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; } } \ No newline at end of file diff --git a/src/main/java/org/example/petshopdesktop/database/ConnectionDB.java b/src/main/java/org/example/petshopdesktop/database/ConnectionDB.java index 3435c7ac..09209c3e 100644 --- a/src/main/java/org/example/petshopdesktop/database/ConnectionDB.java +++ b/src/main/java/org/example/petshopdesktop/database/ConnectionDB.java @@ -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()); } } diff --git a/src/main/java/org/example/petshopdesktop/database/InventoryDB.java b/src/main/java/org/example/petshopdesktop/database/InventoryDB.java index 737c952f..e0361321 100644 --- a/src/main/java/org/example/petshopdesktop/database/InventoryDB.java +++ b/src/main/java/org/example/petshopdesktop/database/InventoryDB.java @@ -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; } diff --git a/src/main/java/org/example/petshopdesktop/database/PetDB.java b/src/main/java/org/example/petshopdesktop/database/PetDB.java index d1b7ca37..5a748c4d 100644 --- a/src/main/java/org/example/petshopdesktop/database/PetDB.java +++ b/src/main/java/org/example/petshopdesktop/database/PetDB.java @@ -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; } } diff --git a/src/main/java/org/example/petshopdesktop/database/ProductDB.java b/src/main/java/org/example/petshopdesktop/database/ProductDB.java index b5fbeb07..d8aa4da1 100644 --- a/src/main/java/org/example/petshopdesktop/database/ProductDB.java +++ b/src/main/java/org/example/petshopdesktop/database/ProductDB.java @@ -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; } diff --git a/src/main/java/org/example/petshopdesktop/database/ServiceDB.java b/src/main/java/org/example/petshopdesktop/database/ServiceDB.java index c5ca9ff7..efa629cc 100644 --- a/src/main/java/org/example/petshopdesktop/database/ServiceDB.java +++ b/src/main/java/org/example/petshopdesktop/database/ServiceDB.java @@ -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; } } \ No newline at end of file diff --git a/src/main/java/org/example/petshopdesktop/database/SupplierDB.java b/src/main/java/org/example/petshopdesktop/database/SupplierDB.java index 325b6dd4..2d03cde4 100644 --- a/src/main/java/org/example/petshopdesktop/database/SupplierDB.java +++ b/src/main/java/org/example/petshopdesktop/database/SupplierDB.java @@ -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; } diff --git a/src/main/java/org/example/petshopdesktop/database/UserDB.java b/src/main/java/org/example/petshopdesktop/database/UserDB.java index 88efe556..ca4d8a78 100644 --- a/src/main/java/org/example/petshopdesktop/database/UserDB.java +++ b/src/main/java/org/example/petshopdesktop/database/UserDB.java @@ -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 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 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"); + } } } } diff --git a/src/main/java/org/example/petshopdesktop/models/User.java b/src/main/java/org/example/petshopdesktop/models/User.java index 02089be4..7d2199e9 100644 --- a/src/main/java/org/example/petshopdesktop/models/User.java +++ b/src/main/java/org/example/petshopdesktop/models/User.java @@ -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; } diff --git a/src/main/resources/org/example/petshopdesktop/login-view.fxml b/src/main/resources/org/example/petshopdesktop/login-view.fxml index ad6e0bca..c15d860c 100644 --- a/src/main/resources/org/example/petshopdesktop/login-view.fxml +++ b/src/main/resources/org/example/petshopdesktop/login-view.fxml @@ -8,7 +8,7 @@ - -