Add error dialog for view loading failures and fix btnPurchaseOrdersClicked signature

This commit is contained in:
2026-02-25 09:21:18 -07:00
parent 82bbe79d01
commit ffa45044e4

View File

@@ -5,16 +5,14 @@ import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.scene.Parent; import javafx.scene.Parent;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.stage.Stage; import javafx.stage.Stage;
import org.example.petshopdesktop.auth.UserSession; import org.example.petshopdesktop.auth.UserSession;
import org.example.petshopdesktop.util.ActivityLogger;
/*
Petshop Desktop
Purpose: Main application shell controller, includes navigation and UI level role based access control.
*/
public class MainLayoutController { public class MainLayoutController {
@FXML @FXML
@@ -50,6 +48,9 @@ public class MainLayoutController {
@FXML @FXML
private Button btnSuppliers; private Button btnSuppliers;
@FXML
private Button btnStaffAccounts;
@FXML @FXML
private Label lblUsername; private Label lblUsername;
@@ -102,11 +103,17 @@ public class MainLayoutController {
} }
@FXML @FXML
void btnPurchaseOrdersClicked() { void btnPurchaseOrdersClicked(ActionEvent event) {
loadView("purchase-order-view.fxml"); loadView("purchase-order-view.fxml");
updateButtons(btnPurchaseOrders); updateButtons(btnPurchaseOrders);
} }
@FXML
void btnStaffAccountsClicked(ActionEvent event) {
loadView("staff-accounts-view.fxml");
updateButtons(btnStaffAccounts);
}
@FXML @FXML
void btnServicesClicked(ActionEvent event) { void btnServicesClicked(ActionEvent event) {
loadView("service-view.fxml"); loadView("service-view.fxml");
@@ -117,14 +124,10 @@ public class MainLayoutController {
void btnSuppliersClicked(ActionEvent event) { void btnSuppliersClicked(ActionEvent event) {
loadView("supplier-view.fxml"); loadView("supplier-view.fxml");
updateButtons(btnSuppliers); updateButtons(btnSuppliers);
} }
@FXML @FXML
void btnLogoutClicked(ActionEvent event) { void btnLogoutClicked(ActionEvent event) {
// Logout clears session state before returning to the login view.
UserSession.getInstance().logout(); UserSession.getInstance().logout();
try { try {
FXMLLoader loader = new FXMLLoader( FXMLLoader loader = new FXMLLoader(
@@ -134,6 +137,10 @@ public class MainLayoutController {
stage.setScene(scene); stage.setScene(scene);
stage.setTitle("Pet Shop Manager - Login"); stage.setTitle("Pet Shop Manager - Login");
} catch (Exception e) { } catch (Exception e) {
ActivityLogger.getInstance().logException(
"MainLayoutController.btnLogoutClicked",
e,
"Loading login view after logout");
System.err.println("Error loading login view: " + e.getMessage()); System.err.println("Error loading login view: " + e.getMessage());
e.printStackTrace(); e.printStackTrace();
} }
@@ -141,22 +148,22 @@ public class MainLayoutController {
@FXML @FXML
public void initialize() { public void initialize() {
// RBAC state is applied once during initial layout load.
applyRBAC(); applyRBAC();
// Default landing view after successful authentication.
loadView("pet-view.fxml"); loadView("pet-view.fxml");
updateButtons(btnPets);
} }
private void applyRBAC() { private void applyRBAC() {
UserSession session = UserSession.getInstance(); UserSession session = UserSession.getInstance();
// Session identity is displayed in the header for clarity and auditing. String displayName = session.getEmployeeName();
lblUsername.setText(session.getUsername()); if (displayName == null || displayName.isBlank()) {
lblRole.setText(session.getRole().toString()); displayName = session.getUsername();
}
lblUsername.setText(displayName == null ? "" : displayName);
lblRole.setText("Leon's Petstore");
// UI level RBAC hides admin only navigation entries for non admin users.
// setManaged(false) removes the node from layout calculations to avoid empty spacing.
boolean isAdmin = session.isAdmin(); boolean isAdmin = session.isAdmin();
btnInventory.setVisible(isAdmin); btnInventory.setVisible(isAdmin);
btnInventory.setManaged(isAdmin); btnInventory.setManaged(isAdmin);
@@ -165,47 +172,65 @@ public class MainLayoutController {
btnProductSuppliers.setVisible(isAdmin); btnProductSuppliers.setVisible(isAdmin);
btnProductSuppliers.setManaged(isAdmin); btnProductSuppliers.setManaged(isAdmin);
// Privileged operations should still be enforced within the relevant controllers and database methods. btnPurchaseOrders.setVisible(isAdmin);
btnPurchaseOrders.setManaged(isAdmin);
btnStaffAccounts.setVisible(isAdmin);
btnStaffAccounts.setManaged(isAdmin);
btnSalesHistory.setText(isAdmin ? "Sales History" : "Sales");
} }
/**
* Load a view when a button is clicked on the navigation
* @param fxmlFile the fxmlFile name to be loaded
*/
private void loadView(String fxmlFile) { private void loadView(String fxmlFile) {
try { try {
//Get the location of the fxml for view
FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/example/petshopdesktop/modelviews/" + fxmlFile)); FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/example/petshopdesktop/modelviews/" + fxmlFile));
Parent view = loader.load(); Parent view = loader.load();
//Clear any content that is in the stack pane and add the new view to display
spContentArea.getChildren().clear(); spContentArea.getChildren().clear();
spContentArea.getChildren().add(view); spContentArea.getChildren().add(view);
} catch (Exception e) { } catch (Exception e) {
ActivityLogger.getInstance().logException(
"MainLayoutController.loadView",
e,
"Loading view: " + fxmlFile);
System.err.println("Error loading view: " + fxmlFile); System.err.println("Error loading view: " + fxmlFile);
e.printStackTrace(); e.printStackTrace();
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("View Load Error");
alert.setHeaderText("Failed to load: " + fxmlFile);
alert.setContentText("Error: " + e.getMessage() + "\n\nCheck console for details.");
alert.showAndWait();
} }
} }
/**
* update the button visuals when a button is clicked on the navigation
* @param activeButton the button to be set active
*/
private void updateButtons(Button activeButton) { private void updateButtons(Button activeButton) {
//reset all buttons String base = "-fx-background-color: transparent; -fx-text-fill: #D5DDE6; -fx-cursor: hand; -fx-background-radius: 10; -fx-alignment: CENTER_LEFT;";
Button[] BUTTONS = {btnAdoptions, btnPets, btnAppointments, btnInventory, String active = "-fx-background-color: #FF6B6B; -fx-text-fill: white; -fx-cursor: hand; -fx-background-radius: 10; -fx-alignment: CENTER_LEFT;";
btnSalesHistory, btnServices, btnSuppliers, btnProductSuppliers, btnProducts};
for (Button button : BUTTONS) { Button[] buttons = {
//set all buttons to inactive btnAdoptions,
button.setStyle("-fx-background-color: transparent; " + btnPets,
"-fx-text-fill: #CCCCCC; " + btnAppointments,
"-fx-cursor: hand"); btnInventory,
btnSalesHistory,
btnServices,
btnSuppliers,
btnProductSuppliers,
btnProducts,
btnPurchaseOrders,
btnStaffAccounts
};
for (Button button : buttons) {
if (button != null) {
button.setStyle(base);
}
} }
//set active button if (activeButton != null) {
activeButton.setStyle("-fx-background-color: #FF6B6B; " + activeButton.setStyle(active);
"-fx-text-fill: white; " + }
"-fx-cursor: hand; " +
"-fx-background-radius: 8");
} }
} }