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.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
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 {
@FXML
@@ -50,6 +48,9 @@ public class MainLayoutController {
@FXML
private Button btnSuppliers;
@FXML
private Button btnStaffAccounts;
@FXML
private Label lblUsername;
@@ -102,11 +103,17 @@ public class MainLayoutController {
}
@FXML
void btnPurchaseOrdersClicked() {
void btnPurchaseOrdersClicked(ActionEvent event) {
loadView("purchase-order-view.fxml");
updateButtons(btnPurchaseOrders);
}
@FXML
void btnStaffAccountsClicked(ActionEvent event) {
loadView("staff-accounts-view.fxml");
updateButtons(btnStaffAccounts);
}
@FXML
void btnServicesClicked(ActionEvent event) {
loadView("service-view.fxml");
@@ -117,14 +124,10 @@ public class MainLayoutController {
void btnSuppliersClicked(ActionEvent event) {
loadView("supplier-view.fxml");
updateButtons(btnSuppliers);
}
@FXML
void btnLogoutClicked(ActionEvent event) {
// Logout clears session state before returning to the login view.
UserSession.getInstance().logout();
try {
FXMLLoader loader = new FXMLLoader(
@@ -134,6 +137,10 @@ public class MainLayoutController {
stage.setScene(scene);
stage.setTitle("Pet Shop Manager - Login");
} catch (Exception e) {
ActivityLogger.getInstance().logException(
"MainLayoutController.btnLogoutClicked",
e,
"Loading login view after logout");
System.err.println("Error loading login view: " + e.getMessage());
e.printStackTrace();
}
@@ -141,22 +148,22 @@ public class MainLayoutController {
@FXML
public void initialize() {
// RBAC state is applied once during initial layout load.
applyRBAC();
// Default landing view after successful authentication.
loadView("pet-view.fxml");
updateButtons(btnPets);
}
private void applyRBAC() {
UserSession session = UserSession.getInstance();
// Session identity is displayed in the header for clarity and auditing.
lblUsername.setText(session.getUsername());
lblRole.setText(session.getRole().toString());
String displayName = session.getEmployeeName();
if (displayName == null || displayName.isBlank()) {
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();
btnInventory.setVisible(isAdmin);
btnInventory.setManaged(isAdmin);
@@ -165,47 +172,65 @@ public class MainLayoutController {
btnProductSuppliers.setVisible(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) {
try {
//Get the location of the fxml for view
FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/example/petshopdesktop/modelviews/" + fxmlFile));
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().add(view);
} catch (Exception e) {
ActivityLogger.getInstance().logException(
"MainLayoutController.loadView",
e,
"Loading view: " + fxmlFile);
System.err.println("Error loading view: " + fxmlFile);
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) {
//reset all buttons
Button[] BUTTONS = {btnAdoptions, btnPets, btnAppointments, btnInventory,
btnSalesHistory, btnServices, btnSuppliers, btnProductSuppliers, btnProducts};
for (Button button : BUTTONS) {
//set all buttons to inactive
button.setStyle("-fx-background-color: transparent; " +
"-fx-text-fill: #CCCCCC; " +
"-fx-cursor: hand");
String base = "-fx-background-color: transparent; -fx-text-fill: #D5DDE6; -fx-cursor: hand; -fx-background-radius: 10; -fx-alignment: CENTER_LEFT;";
String active = "-fx-background-color: #FF6B6B; -fx-text-fill: white; -fx-cursor: hand; -fx-background-radius: 10; -fx-alignment: CENTER_LEFT;";
Button[] buttons = {
btnAdoptions,
btnPets,
btnAppointments,
btnInventory,
btnSalesHistory,
btnServices,
btnSuppliers,
btnProductSuppliers,
btnProducts,
btnPurchaseOrders,
btnStaffAccounts
};
for (Button button : buttons) {
if (button != null) {
button.setStyle(base);
}
}
//set active button
activeButton.setStyle("-fx-background-color: #FF6B6B; " +
"-fx-text-fill: white; " +
"-fx-cursor: hand; " +
"-fx-background-radius: 8");
if (activeButton != null) {
activeButton.setStyle(active);
}
}
}