Add DELETE key and Enter-to-confirm to all tables

This commit is contained in:
2026-02-25 09:42:34 -07:00
parent 19593af688
commit 13684f97de
11 changed files with 527 additions and 21 deletions

View File

@@ -13,6 +13,7 @@ import javafx.stage.Stage;
import org.example.petshopdesktop.controllers.dialogcontrollers.AdoptionDialogController;
import org.example.petshopdesktop.database.AdoptionDB;
import org.example.petshopdesktop.models.Adoption;
import org.example.petshopdesktop.util.ActivityLogger;
import java.io.IOException;
import java.sql.SQLException;
@@ -80,6 +81,15 @@ public class AdoptionController {
txtSearch.textProperty().addListener((observable, oldValue, newValue) -> {
displayFilteredAdoptions(newValue);
});
//EventListener for DELETE key
tvAdoptions.setOnKeyPressed(event -> {
if (event.getCode() == javafx.scene.input.KeyCode.DELETE) {
if (tvAdoptions.getSelectionModel().getSelectedItem() != null) {
btnDeleteClicked(null);
}
}
});
}
@FXML
@@ -96,6 +106,7 @@ public class AdoptionController {
Alert question = new Alert(Alert.AlertType.CONFIRMATION);
question.setHeaderText("Please confirm delete");
question.setContentText("Are you sure you want to delete this adoption record?");
question.getDialogPane().lookupButton(ButtonType.OK).requestFocus();
Optional<ButtonType> result = question.showAndWait();
if (result.isPresent() && result.get() == ButtonType.OK) {
@@ -105,12 +116,20 @@ public class AdoptionController {
numRows = AdoptionDB.deleteAdoption(adoptionId);
}
catch (SQLIntegrityConstraintViolationException e) {
ActivityLogger.getInstance().logException(
"AdoptionController.btnDeleteClicked",
e,
"Deleting adoption (integrity constraint violation) with ID: " + adoptionId);
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setHeaderText("Database Operation Error");
alert.setContentText("Delete failed\nThe selected adoption is being referred in another table");
alert.showAndWait();
return;
} catch (SQLException e) {
ActivityLogger.getInstance().logException(
"AdoptionController.btnDeleteClicked",
e,
"Deleting adoption with ID: " + adoptionId);
throw new RuntimeException(e);
}
@@ -152,6 +171,10 @@ public class AdoptionController {
tvAdoptions.setItems(data);
}
} catch (Exception e) {
ActivityLogger.getInstance().logException(
"AdoptionController.displayFilteredAdoptions",
e,
"Filtering adoptions with filter: " + filter);
System.out.println("Error while fetching table data: " + e.getMessage());
}
}
@@ -161,6 +184,10 @@ public class AdoptionController {
try {
data = AdoptionDB.getAdoptions();
} catch (SQLException e) {
ActivityLogger.getInstance().logException(
"AdoptionController.displayAdoptions",
e,
"Fetching adoption data for table display");
System.out.println("Error while fetching table data: " + e.getMessage());
}
tvAdoptions.setItems(data);
@@ -173,6 +200,10 @@ public class AdoptionController {
try {
scene = new Scene(fxmlLoader.load());
} catch (IOException e) {
ActivityLogger.getInstance().logException(
"AdoptionController.openDialog",
e,
"Loading adoption dialog in " + mode + " mode");
throw new RuntimeException(e);
}

View File

@@ -2,6 +2,7 @@ package org.example.petshopdesktop.controllers;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
@@ -14,6 +15,7 @@ import javafx.stage.Stage;
import org.example.petshopdesktop.DTOs.AppointmentDTO;
import org.example.petshopdesktop.controllers.dialogcontrollers.AppointmentDialogController;
import org.example.petshopdesktop.database.AppointmentDB;
import org.example.petshopdesktop.util.ActivityLogger;
public class AppointmentController {
@@ -33,7 +35,8 @@ public class AppointmentController {
@FXML private TextField txtSearch;
private ObservableList<AppointmentDTO> data = FXCollections.observableArrayList();
private final ObservableList<AppointmentDTO> appointments = FXCollections.observableArrayList();
private FilteredList<AppointmentDTO> filtered;
@FXML
public void initialize(){
@@ -46,18 +49,63 @@ public class AppointmentController {
colCustomerName.setCellValueFactory(new PropertyValueFactory<>("customerName"));
colAppointmentStatus.setCellValueFactory(new PropertyValueFactory<>("appointmentStatus"));
filtered = new FilteredList<>(appointments, a -> true);
tvAppointments.setItems(filtered);
if (txtSearch != null) {
txtSearch.textProperty().addListener((obs, o, n) -> applyFilter(n));
}
//EventListener for DELETE key
tvAppointments.setOnKeyPressed(event -> {
if (event.getCode() == javafx.scene.input.KeyCode.DELETE) {
if (tvAppointments.getSelectionModel().getSelectedItem() != null) {
btnDeleteClicked(null);
}
}
});
loadAppointments();
}
private void loadAppointments(){
try{
data = AppointmentDB.getAppointmentDTOs();
tvAppointments.setItems(data);
appointments.setAll(AppointmentDB.getAppointmentDTOs());
}catch(Exception e){
ActivityLogger.getInstance().logException(
"AppointmentController.loadAppointments",
e,
"Loading appointments for table display");
e.printStackTrace();
}
}
private void applyFilter(String text) {
if (filtered == null) {
return;
}
String q = text == null ? "" : text.trim().toLowerCase();
if (q.isEmpty()) {
filtered.setPredicate(a -> true);
return;
}
filtered.setPredicate(a ->
String.valueOf(a.getAppointmentId()).contains(q)
|| safe(a.getPetName()).contains(q)
|| safe(a.getServiceName()).contains(q)
|| safe(a.getAppointmentDate()).contains(q)
|| safe(a.getAppointmentTime()).contains(q)
|| safe(a.getCustomerName()).contains(q)
|| safe(a.getAppointmentStatus()).contains(q)
);
}
private static String safe(String v) {
return v == null ? "" : v.toLowerCase();
}
@FXML
void btnAddClicked(ActionEvent event){
openDialog(null, "Add");
@@ -89,6 +137,10 @@ public class AppointmentController {
AppointmentDB.deleteAppointment(selected.getAppointmentId());
loadAppointments();
}catch(Exception e){
ActivityLogger.getInstance().logException(
"AppointmentController.btnDeleteClicked",
e,
"Deleting appointment with ID: " + selected.getAppointmentId());
e.printStackTrace();
}
}
@@ -121,6 +173,10 @@ public class AppointmentController {
loadAppointments();
}catch(Exception e){
ActivityLogger.getInstance().logException(
"AppointmentController.openDialog",
e,
"Opening appointment dialog in " + mode + " mode");
e.printStackTrace();
}
}

View File

@@ -13,6 +13,7 @@ import javafx.stage.Stage;
import org.example.petshopdesktop.controllers.dialogcontrollers.InventoryDialogController;
import org.example.petshopdesktop.database.InventoryDB;
import org.example.petshopdesktop.models.Inventory;
import org.example.petshopdesktop.util.ActivityLogger;
import java.io.IOException;
import java.sql.SQLException;
@@ -79,6 +80,15 @@ public class InventoryController {
txtSearch.textProperty().addListener((observable, oldValue, newValue) -> {
displayFilteredInventory(newValue);
});
//EventListener for DELETE key
tvInventory.setOnKeyPressed(event -> {
if (event.getCode() == javafx.scene.input.KeyCode.DELETE) {
if (tvInventory.getSelectionModel().getSelectedItem() != null) {
btnDeleteClicked(null);
}
}
});
}
//Opens dialog in add mode
@@ -98,6 +108,7 @@ public class InventoryController {
Alert question = new Alert(Alert.AlertType.CONFIRMATION);
question.setHeaderText("Please confirm delete");
question.setContentText("Are you sure you want to delete this inventory record?");
question.getDialogPane().lookupButton(ButtonType.OK).requestFocus();
Optional<ButtonType> result = question.showAndWait();
//If user confirms, proceed with trying to delete...
@@ -109,6 +120,10 @@ public class InventoryController {
}
catch (SQLIntegrityConstraintViolationException e) {
ActivityLogger.getInstance().logException(
"InventoryController.btnDeleteClicked",
e,
"Deleting inventory (integrity constraint violation) with ID: " + inventoryId);
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setHeaderText("Database Operation Error");
alert.setContentText("Delete failed\nThe selected inventory record is being referred in another table");
@@ -117,6 +132,10 @@ public class InventoryController {
}
catch (SQLException e) {
ActivityLogger.getInstance().logException(
"InventoryController.btnDeleteClicked",
e,
"Deleting inventory with ID: " + inventoryId);
throw new RuntimeException(e);
}
@@ -170,6 +189,10 @@ public class InventoryController {
}
catch (Exception e) {
ActivityLogger.getInstance().logException(
"InventoryController.displayFilteredInventory",
e,
"Filtering inventory with filter: " + filter);
System.out.println("Error while fetching table data: " + e.getMessage());
}
}
@@ -182,6 +205,10 @@ public class InventoryController {
}
catch (SQLException e) {
ActivityLogger.getInstance().logException(
"InventoryController.displayInventory",
e,
"Fetching inventory data for table display");
System.out.println("Error while fetching table data: " + e.getMessage());
}
tvInventory.setItems(data);
@@ -198,6 +225,10 @@ public class InventoryController {
}
catch (IOException e) {
ActivityLogger.getInstance().logException(
"InventoryController.openDialog",
e,
"Loading inventory dialog in " + mode + " mode");
throw new RuntimeException(e);
}

View File

@@ -4,20 +4,20 @@ import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.stage.Modality;
import javafx.stage.Stage;
import org.example.petshopdesktop.auth.UserSession;
import org.example.petshopdesktop.database.ConnectionDB;
import org.example.petshopdesktop.database.UserDB;
import org.example.petshopdesktop.models.User;
import org.example.petshopdesktop.util.ActivityLogger;
import java.sql.SQLException;
/*
Petshop Desktop
Purpose: Authentication controller responsible for validating credentials and initialising the user session.
*/
public class LoginController {
@FXML
@@ -29,20 +29,34 @@ public class LoginController {
@FXML
private Label lblError;
@FXML
private Button btnCreateStaff;
@FXML
public void initialize() {
lblError.setText("");
try {
ConnectionDB.getConnection().close();
try {
UserDB.initializeTable();
} catch (Exception ignored) {
}
} catch (Exception e) {
lblError.setText("Database is not connected. Check Docker and connectionpetstore.properties.");
}
}
@FXML
void btnLoginClicked(ActionEvent event) {
// Input normalisation keeps authentication behaviour consistent.
String username = txtUsername.getText().trim();
String password = txtPassword.getText();
// Basic validation to avoid unnecessary database calls.
if (username.isEmpty() || password.isEmpty()) {
lblError.setText("Please enter username and password.");
return;
}
try {
// Credential verification returns a fully populated User on success.
User user = UserDB.authenticate(username, password);
if (user == null) {
lblError.setText("Invalid username or password.");
@@ -50,18 +64,55 @@ public class LoginController {
return;
}
// Session state is stored in memory for use by controllers and UI RBAC.
UserSession.getInstance().login(user.getUsername(), user.getRole());
UserSession.getInstance().login(
user.getUserId(),
user.getEmployeeId(),
user.getUsername(),
user.getEmployeeFullName(),
user.getRole()
);
openMainLayout();
} catch (SQLException e) {
lblError.setText("Database error: " + e.getMessage());
ActivityLogger.getInstance().logException(
"LoginController.btnLoginClicked",
e,
"Authentication attempt for username: " + username);
String msg = e.getMessage() == null ? "" : e.getMessage().toLowerCase();
if (msg.contains("doesn't exist") || msg.contains("unknown database") || msg.contains("access denied")) {
lblError.setText("Database error. Check Docker and connectionpetstore.properties.");
} else {
lblError.setText("Login failed. Check username and password.");
}
} catch (RuntimeException e) {
ActivityLogger.getInstance().logException(
"LoginController.btnLoginClicked",
e,
"Database connection");
lblError.setText("Database is not connected. Check Docker and connectionpetstore.properties.");
}
}
@FXML
void btnCreateStaffClicked(ActionEvent event) {
lblError.setText("");
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/example/petshopdesktop/dialogviews/staff-register-dialog-view.fxml"));
Stage dialog = new Stage();
dialog.initOwner(txtUsername.getScene().getWindow());
dialog.initModality(Modality.APPLICATION_MODAL);
dialog.setTitle("Create Staff Account");
dialog.setScene(new Scene(loader.load()));
dialog.setResizable(false);
dialog.showAndWait();
} catch (Exception e) {
ActivityLogger.getInstance().logException("LoginController.btnCreateStaffClicked", e, "Opening staff register dialog");
lblError.setText("Could not open staff account creation.");
}
}
private void openMainLayout() {
try {
// View transition into the post login application shell.
FXMLLoader loader = new FXMLLoader(
getClass().getResource("/org/example/petshopdesktop/main-layout-view.fxml"));
Scene scene = new Scene(loader.load());
@@ -69,6 +120,10 @@ public class LoginController {
stage.setScene(scene);
stage.setTitle("Pet Shop Manager");
} catch (Exception e) {
ActivityLogger.getInstance().logException(
"LoginController.openMainLayout",
e,
"Loading main application layout after successful login");
lblError.setText("Error loading application: " + e.getMessage());
e.printStackTrace();
}

View File

@@ -14,6 +14,7 @@ import org.example.petshopdesktop.controllers.dialogcontrollers.PetDialogControl
import org.example.petshopdesktop.database.PetDB;
import org.example.petshopdesktop.database.ProductDB;
import org.example.petshopdesktop.models.Pet;
import org.example.petshopdesktop.util.ActivityLogger;
import java.io.IOException;
import java.sql.SQLException;
@@ -73,6 +74,7 @@ public class PetController {
Alert question = new Alert(Alert.AlertType.CONFIRMATION);
question.setHeaderText("Please confirm delete");
question.setContentText("Are you sure you want to delete this pet?");
question.getDialogPane().lookupButton(ButtonType.OK).requestFocus();
Optional<ButtonType> result = question.showAndWait(); //show alert and wait for response
//if confirmed,start deletion
@@ -84,6 +86,10 @@ public class PetController {
numRows = PetDB.deletePet(petId);
}
catch (SQLIntegrityConstraintViolationException e){
ActivityLogger.getInstance().logException(
"PetController.btnDeleteClicked",
e,
"Deleting pet (integrity constraint violation) with ID: " + petId);
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setHeaderText("Database Operation Error");
alert.setContentText("Delete failed\n" +
@@ -92,6 +98,10 @@ public class PetController {
return;
}
catch (SQLException e) {
ActivityLogger.getInstance().logException(
"PetController.btnDeleteClicked",
e,
"Deleting pet with ID: " + petId);
throw new RuntimeException(e);
}
@@ -154,6 +164,15 @@ public class PetController {
txtSearch.textProperty().addListener((observable, oldValue, newValue) -> {
displayFilteredPet(newValue);
});
//EventListener for DELETE key
tvPets.setOnKeyPressed(event -> {
if (event.getCode() == javafx.scene.input.KeyCode.DELETE) {
if (tvPets.getSelectionModel().getSelectedItem() != null) {
btnDeleteClicked(null);
}
}
});
}
private void displayFilteredPet(String filter) {
@@ -167,6 +186,10 @@ public class PetController {
tvPets.setItems(data);
}
} catch (Exception e) {
ActivityLogger.getInstance().logException(
"PetController.displayFilteredPet",
e,
"Filtering pets with filter: " + filter);
System.out.println("Error while fetching table data: " + e.getMessage());
}
}
@@ -178,6 +201,10 @@ public class PetController {
data = PetDB.getPets();
}
catch(SQLException e){
ActivityLogger.getInstance().logException(
"PetController.displayPets",
e,
"Fetching pet data for table display");
System.out.println("Error while fetching table data: " + e.getMessage());
}
@@ -191,6 +218,10 @@ public class PetController {
try{
scene = new Scene(fxmlLoader.load());
} catch (IOException e) {
ActivityLogger.getInstance().logException(
"PetController.openDialog",
e,
"Loading pet dialog in " + mode + " mode");
throw new RuntimeException(e);
}
PetDialogController dialogController = fxmlLoader.getController(); //controller associated with this view

View File

@@ -17,6 +17,7 @@ import org.example.petshopdesktop.database.ProductDB;
import org.example.petshopdesktop.database.SupplierDB;
import org.example.petshopdesktop.models.Product;
import org.example.petshopdesktop.models.Supplier;
import org.example.petshopdesktop.util.ActivityLogger;
import java.io.IOException;
import java.sql.SQLException;
@@ -92,6 +93,15 @@ public class ProductController {
displayFilteredProduct(newValue);
});
//EventListener for DELETE key press
tvProducts.setOnKeyPressed(event -> {
if (event.getCode() == javafx.scene.input.KeyCode.DELETE) {
if (tvProducts.getSelectionModel().getSelectedItem() != null) {
btnDeleteClicked(null);
}
}
});
}
/**
@@ -106,6 +116,10 @@ public class ProductController {
data = ProductDB.getProductDTO();
} catch (SQLException e) {
System.out.println("Error while fetching table data: " + e.getMessage());
ActivityLogger.getInstance().logException(
"ProductController.displayProduct",
e,
"Fetching product data for table display");
}
//put data in the table
@@ -136,6 +150,7 @@ public class ProductController {
Alert question = new Alert(Alert.AlertType.CONFIRMATION);
question.setHeaderText("Please confirm delete");
question.setContentText("Are you sure you want to delete this product?");
question.getDialogPane().lookupButton(ButtonType.OK).requestFocus();
Optional<ButtonType> result = question.showAndWait(); //show alert and wait for response
//if confirmed,start deletion
@@ -147,6 +162,10 @@ public class ProductController {
numRows = ProductDB.deleteProduct(prodId);
}
catch (SQLIntegrityConstraintViolationException e){
ActivityLogger.getInstance().logException(
"ProductController.btnDeleteClicked",
e,
String.format("Attempting to delete product ID %d - foreign key constraint", prodId));
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setHeaderText("Database Operation Error");
alert.setContentText("Delete failed\n" +
@@ -155,6 +174,10 @@ public class ProductController {
return;
}
catch (SQLException e) {
ActivityLogger.getInstance().logException(
"ProductController.btnDeleteClicked",
e,
String.format("Attempting to delete product ID %d", prodId));
throw new RuntimeException(e);
}
@@ -212,6 +235,10 @@ public class ProductController {
}
} catch (Exception e) {
System.out.println("Error while fetching table data: " + e.getMessage());
ActivityLogger.getInstance().logException(
"ProductController.displayFilteredProduct",
e,
String.format("Filtering products with keyword: %s", filter));
}
}
@@ -228,6 +255,10 @@ public class ProductController {
try{
scene = new Scene(fxmlLoader.load());
} catch (IOException e) {
ActivityLogger.getInstance().logException(
"ProductController.openDialog",
e,
String.format("Loading product dialog view in %s mode", mode));
throw new RuntimeException(e);
}
ProductDialogController dialogController = fxmlLoader.getController(); //controller associated with this view

View File

@@ -17,6 +17,7 @@ import org.example.petshopdesktop.controllers.dialogcontrollers.ProductSupplierD
import org.example.petshopdesktop.database.ProductDB;
import org.example.petshopdesktop.database.ProductSupplierDB;
import org.example.petshopdesktop.models.ProductSupplier;
import org.example.petshopdesktop.util.ActivityLogger;
import java.io.IOException;
import java.sql.SQLException;
@@ -89,6 +90,15 @@ public class ProductSupplierController {
displayFilteredProductSupplier(newValue);
});
//EventListener for DELETE key
tvProductSuppliers.setOnKeyPressed(event -> {
if (event.getCode() == javafx.scene.input.KeyCode.DELETE) {
if (tvProductSuppliers.getSelectionModel().getSelectedItem() != null) {
btnDeleteClicked(null);
}
}
});
}
/**
@@ -102,6 +112,10 @@ public class ProductSupplierController {
try{
data = ProductSupplierDB.getProductSupplierDTO();
} catch (SQLException e) {
ActivityLogger.getInstance().logException(
"ProductSupplierController.displayProductSupplier",
e,
"Fetching product-supplier data for table display");
System.out.println("Error while fetching table data: " + e.getMessage());
}
@@ -125,6 +139,10 @@ public class ProductSupplierController {
tvProductSuppliers.setItems(data);
}
} catch (Exception e) {
ActivityLogger.getInstance().logException(
"ProductSupplierController.displayFilteredProductSupplier",
e,
"Filtering product-supplier data with filter: " + filter);
System.out.println("Error while fetching table data: " + e.getMessage());
}
}
@@ -153,6 +171,7 @@ public class ProductSupplierController {
Alert question = new Alert(Alert.AlertType.CONFIRMATION);
question.setHeaderText("Please confirm delete");
question.setContentText("Are you sure you want to delete this product-supplier?");
question.getDialogPane().lookupButton(ButtonType.OK).requestFocus();
Optional<ButtonType> result = question.showAndWait(); //show alert and wait for response
//if confirmed,start deletion
@@ -165,6 +184,10 @@ public class ProductSupplierController {
numRows = ProductSupplierDB.deleteProductSupplier(supId, prodId);
}
catch (SQLIntegrityConstraintViolationException e){
ActivityLogger.getInstance().logException(
"ProductSupplierController.btnDeleteClicked",
e,
"Deleting product-supplier (integrity constraint violation) - SupID: " + supId + ", ProdID: " + prodId);
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setHeaderText("Database Operation Error");
alert.setContentText("Delete failed\n" +
@@ -173,6 +196,10 @@ public class ProductSupplierController {
return;
}
catch (SQLException e) {
ActivityLogger.getInstance().logException(
"ProductSupplierController.btnDeleteClicked",
e,
"Deleting product-supplier - SupID: " + supId + ", ProdID: " + prodId);
throw new RuntimeException(e);
}
@@ -222,6 +249,10 @@ public class ProductSupplierController {
try{
scene = new Scene(fxmlLoader.load());
} catch (IOException e) {
ActivityLogger.getInstance().logException(
"ProductSupplierController.openDialog",
e,
"Loading product-supplier dialog in " + mode + " mode");
throw new RuntimeException(e);
}
ProductSupplierDialogController dialogController = fxmlLoader.getController(); //controller associated with this view

View File

@@ -1,15 +1,22 @@
package org.example.petshopdesktop.controllers;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import org.example.petshopdesktop.DTOs.PurchaseOrderDTO;
import org.example.petshopdesktop.database.PurchaseOrderDB;
import org.example.petshopdesktop.util.ActivityLogger;
public class PurchaseOrderController {
@FXML private Button btnRefresh;
@FXML
private TextField txtSearch;
@FXML private TableView<PurchaseOrderDTO> tvPurchaseOrders;
@FXML private TableColumn<PurchaseOrderDTO,Integer> colOrderId;
@@ -17,6 +24,9 @@ public class PurchaseOrderController {
@FXML private TableColumn<PurchaseOrderDTO,String> colOrderDate;
@FXML private TableColumn<PurchaseOrderDTO,String> colStatus;
private final ObservableList<PurchaseOrderDTO> purchaseOrders = FXCollections.observableArrayList();
private FilteredList<PurchaseOrderDTO> filtered;
@FXML
public void initialize() {
@@ -32,21 +42,53 @@ public class PurchaseOrderController {
colStatus.setCellValueFactory(
new PropertyValueFactory<>("status"));
filtered = new FilteredList<>(purchaseOrders, p -> true);
tvPurchaseOrders.setItems(filtered);
if (txtSearch != null) {
txtSearch.textProperty().addListener((obs, o, n) -> applyFilter(n));
}
loadPurchaseOrders();
}
private void loadPurchaseOrders() {
try {
tvPurchaseOrders.setItems(
PurchaseOrderDB.getPurchaseOrders()
);
purchaseOrders.setAll(PurchaseOrderDB.getPurchaseOrders());
} catch (Exception e) {
ActivityLogger.getInstance().logException(
"PurchaseOrderController.loadPurchaseOrders",
e,
"Loading purchase orders for table display");
e.printStackTrace();
new Alert(Alert.AlertType.ERROR,
"Unable to load purchase orders").showAndWait();
}
}
private void applyFilter(String text) {
if (filtered == null) {
return;
}
String q = text == null ? "" : text.trim().toLowerCase();
if (q.isEmpty()) {
filtered.setPredicate(p -> true);
return;
}
filtered.setPredicate(p ->
String.valueOf(p.getPurchaseOrderId()).contains(q)
|| safe(p.getSupplierName()).contains(q)
|| safe(p.getOrderDate()).contains(q)
|| safe(p.getStatus()).contains(q)
);
}
private static String safe(String v) {
return v == null ? "" : v.toLowerCase();
}
@FXML
void btnRefresh() {
loadPurchaseOrders();

View File

@@ -1,18 +1,19 @@
package org.example.petshopdesktop.controllers;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
import org.example.petshopdesktop.database.ServiceDB;
import org.example.petshopdesktop.models.Service;
import org.example.petshopdesktop.controllers.dialogcontrollers.ServiceDialogController;
import org.example.petshopdesktop.util.ActivityLogger;
import javafx.stage.Modality;
@@ -32,6 +33,9 @@ public class ServiceController {
@FXML private TextField txtSearch;
private final ObservableList<Service> services = FXCollections.observableArrayList();
private FilteredList<Service> filtered;
@FXML
public void initialize() {
@@ -41,18 +45,62 @@ public class ServiceController {
colServiceDuration.setCellValueFactory(new PropertyValueFactory<>("serviceDuration"));
colServicePrice.setCellValueFactory(new PropertyValueFactory<>("servicePrice"));
filtered = new FilteredList<>(services, s -> true);
tvServices.setItems(filtered);
if (txtSearch != null) {
txtSearch.textProperty().addListener((obs, o, n) -> applyFilter(n));
}
//EventListener for DELETE key
tvServices.setOnKeyPressed(event -> {
if (event.getCode() == javafx.scene.input.KeyCode.DELETE) {
if (tvServices.getSelectionModel().getSelectedItem() != null) {
btnDeleteClicked(null);
}
}
});
loadServices();
}
private void loadServices() {
try {
tvServices.setItems(ServiceDB.getServices());
services.setAll(ServiceDB.getServices());
} catch (Exception e) {
ActivityLogger.getInstance().logException(
"ServiceController.loadServices",
e,
"Loading services for table display");
showAlert("Database Error", "Unable to load services.");
e.printStackTrace();
}
}
private void applyFilter(String text) {
if (filtered == null) {
return;
}
String q = text == null ? "" : text.trim().toLowerCase();
if (q.isEmpty()) {
filtered.setPredicate(s -> true);
return;
}
filtered.setPredicate(s ->
String.valueOf(s.getServiceId()).contains(q)
|| safe(s.getServiceName()).contains(q)
|| safe(s.getServiceDesc()).contains(q)
|| String.valueOf(s.getServiceDuration()).contains(q)
|| String.valueOf(s.getServicePrice()).contains(q)
);
}
private static String safe(String v) {
return v == null ? "" : v.toLowerCase();
}
@FXML
void btnAddClicked(ActionEvent event) {
@@ -84,6 +132,10 @@ public class ServiceController {
ServiceDB.deleteService(service.getServiceId());
loadServices();
} catch (Exception ex) {
ActivityLogger.getInstance().logException(
"ServiceController.btnDeleteClicked",
ex,
"Deleting service with ID: " + service.getServiceId());
ex.printStackTrace();
}
}
@@ -111,6 +163,10 @@ public class ServiceController {
loadServices();
} catch (Exception e) {
ActivityLogger.getInstance().logException(
"ServiceController.openDialog",
e,
"Opening service dialog in " + mode + " mode");
e.printStackTrace();
}
}

View File

@@ -0,0 +1,112 @@
package org.example.petshopdesktop.controllers;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import org.example.petshopdesktop.auth.UserSession;
import org.example.petshopdesktop.database.UserDB;
import org.example.petshopdesktop.models.StaffAccount;
import org.example.petshopdesktop.util.ActivityLogger;
import java.sql.SQLException;
public class StaffAccountsController {
@FXML
private TableView<StaffAccount> tvStaff;
@FXML
private TableColumn<StaffAccount, String> colUsername;
@FXML
private TableColumn<StaffAccount, String> colName;
@FXML
private TableColumn<StaffAccount, String> colEmail;
@FXML
private TableColumn<StaffAccount, String> colPhone;
@FXML
private TableColumn<StaffAccount, String> colStatus;
@FXML
private TableColumn<StaffAccount, java.sql.Timestamp> colCreated;
@FXML
private TextField txtSearch;
@FXML
private Label lblError;
private final ObservableList<StaffAccount> staffAccounts = FXCollections.observableArrayList();
private FilteredList<StaffAccount> filtered;
@FXML
public void initialize() {
colUsername.setCellValueFactory(new PropertyValueFactory<>("username"));
colName.setCellValueFactory(new PropertyValueFactory<>("fullName"));
colEmail.setCellValueFactory(new PropertyValueFactory<>("email"));
colPhone.setCellValueFactory(new PropertyValueFactory<>("phone"));
colStatus.setCellValueFactory(new PropertyValueFactory<>("status"));
colCreated.setCellValueFactory(new PropertyValueFactory<>("createdAt"));
filtered = new FilteredList<>(staffAccounts, a -> true);
tvStaff.setItems(filtered);
txtSearch.textProperty().addListener((obs, o, n) -> applyFilter(n));
if (!UserSession.getInstance().isAdmin()) {
lblError.setText("Access restricted.");
tvStaff.setDisable(true);
return;
}
refresh();
}
@FXML
void btnRefreshClicked(ActionEvent event) {
refresh();
}
private void refresh() {
lblError.setText("");
try {
staffAccounts.setAll(UserDB.getStaffAccounts());
} catch (SQLException e) {
ActivityLogger.getInstance().logException("StaffAccountsController.refresh", e, "Loading staff accounts");
lblError.setText("Could not load staff accounts.");
} catch (RuntimeException e) {
ActivityLogger.getInstance().logException("StaffAccountsController.refresh", e, "Database connection");
lblError.setText("Database is not connected.");
}
}
private void applyFilter(String text) {
String q = text == null ? "" : text.trim().toLowerCase();
if (q.isEmpty()) {
filtered.setPredicate(a -> true);
return;
}
filtered.setPredicate(a ->
safe(a.getUsername()).contains(q)
|| safe(a.getFullName()).contains(q)
|| safe(a.getEmail()).contains(q)
|| safe(a.getPhone()).contains(q)
|| safe(a.getStatus()).contains(q)
);
}
private static String safe(String v) {
return v == null ? "" : v.toLowerCase();
}
}

View File

@@ -13,6 +13,7 @@ import javafx.stage.Stage;
import org.example.petshopdesktop.controllers.dialogcontrollers.SupplierDialogController;
import org.example.petshopdesktop.database.SupplierDB;
import org.example.petshopdesktop.models.Supplier;
import org.example.petshopdesktop.util.ActivityLogger;
import java.io.IOException;
import java.sql.SQLException;
@@ -87,6 +88,14 @@ public class SupplierController {
displayFilteredSupplier(newValue);
});
//EventListener for DELETE key
tvSuppliers.setOnKeyPressed(event -> {
if (event.getCode() == javafx.scene.input.KeyCode.DELETE) {
if (tvSuppliers.getSelectionModel().getSelectedItem() != null) {
btnDeleteClicked(null);
}
}
});
}
@@ -99,6 +108,10 @@ public class SupplierController {
try{
data = SupplierDB.getSuppliers();
} catch (SQLException e) {
ActivityLogger.getInstance().logException(
"SupplierController.displaySupplier",
e,
"Fetching supplier data for table display");
System.out.println("Error while fetching table data: " + e.getMessage());
}
@@ -121,6 +134,10 @@ public class SupplierController {
tvSuppliers.setItems(data);
}
} catch (Exception e) {
ActivityLogger.getInstance().logException(
"SupplierController.displayFilteredSupplier",
e,
"Filtering suppliers with filter: " + filter);
System.out.println("Error while fetching table data: " + e.getMessage());
}
}
@@ -150,6 +167,7 @@ public class SupplierController {
Alert question = new Alert(Alert.AlertType.CONFIRMATION);
question.setHeaderText("Please confirm delete");
question.setContentText("Are you sure you want to delete this supplier?");
question.getDialogPane().lookupButton(ButtonType.OK).requestFocus();
Optional<ButtonType> result = question.showAndWait(); //show alert and wait for response
//if confirmed, start deletion
@@ -161,6 +179,10 @@ public class SupplierController {
numRows = SupplierDB.deleteSupplier(supId);
}
catch (SQLIntegrityConstraintViolationException e){
ActivityLogger.getInstance().logException(
"SupplierController.btnDeleteClicked",
e,
"Deleting supplier (integrity constraint violation) with ID: " + supId);
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setHeaderText("Database Operation Error");
alert.setContentText("Delete failed\n" +
@@ -169,6 +191,10 @@ public class SupplierController {
return;
}
catch (SQLException e) {
ActivityLogger.getInstance().logException(
"SupplierController.btnDeleteClicked",
e,
"Deleting supplier with ID: " + supId);
throw new RuntimeException(e);
}
@@ -222,6 +248,10 @@ public class SupplierController {
try{
scene = new Scene(fxmlLoader.load());
} catch (IOException e) {
ActivityLogger.getInstance().logException(
"SupplierController.openDialog",
e,
"Loading supplier dialog in " + mode + " mode");
throw new RuntimeException(e);
}
SupplierDialogController dialogController = fxmlLoader.getController(); //controller associated with this view