From edf5bb7a7e07a97da94252ecbcd916f8575807d7 Mon Sep 17 00:00:00 2001 From: Nikitha Date: Thu, 19 Feb 2026 19:38:14 -0700 Subject: [PATCH 01/13] Appoinment,service,Purchase fxml --- .../dialogviews/appointment-dialog-view.fxml | 176 ++++++++++++++++++ .../dialogviews/service-dialog-view.fxml | 147 +++++++++++++++ .../modelviews/appointment-view.fxml | 8 +- .../modelviews/purchaseorder-view.fxml | 82 ++++++++ .../modelviews/service-view.fxml | 8 +- 5 files changed, 413 insertions(+), 8 deletions(-) create mode 100644 src/main/resources/org/example/petshopdesktop/dialogviews/appointment-dialog-view.fxml create mode 100644 src/main/resources/org/example/petshopdesktop/dialogviews/service-dialog-view.fxml create mode 100644 src/main/resources/org/example/petshopdesktop/modelviews/purchaseorder-view.fxml diff --git a/src/main/resources/org/example/petshopdesktop/dialogviews/appointment-dialog-view.fxml b/src/main/resources/org/example/petshopdesktop/dialogviews/appointment-dialog-view.fxml new file mode 100644 index 00000000..7ff155b2 --- /dev/null +++ b/src/main/resources/org/example/petshopdesktop/dialogviews/appointment-dialog-view.fxml @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/org/example/petshopdesktop/dialogviews/service-dialog-view.fxml b/src/main/resources/org/example/petshopdesktop/dialogviews/service-dialog-view.fxml new file mode 100644 index 00000000..ccd4c24f --- /dev/null +++ b/src/main/resources/org/example/petshopdesktop/dialogviews/service-dialog-view.fxml @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/org/example/petshopdesktop/modelviews/appointment-view.fxml b/src/main/resources/org/example/petshopdesktop/modelviews/appointment-view.fxml index ec7e6cce..4bfd3029 100644 --- a/src/main/resources/org/example/petshopdesktop/modelviews/appointment-view.fxml +++ b/src/main/resources/org/example/petshopdesktop/modelviews/appointment-view.fxml @@ -66,15 +66,15 @@ - - - + + + - + diff --git a/src/main/resources/org/example/petshopdesktop/modelviews/purchaseorder-view.fxml b/src/main/resources/org/example/petshopdesktop/modelviews/purchaseorder-view.fxml new file mode 100644 index 00000000..fdac860e --- /dev/null +++ b/src/main/resources/org/example/petshopdesktop/modelviews/purchaseorder-view.fxml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/org/example/petshopdesktop/modelviews/service-view.fxml b/src/main/resources/org/example/petshopdesktop/modelviews/service-view.fxml index c3398855..2adac139 100644 --- a/src/main/resources/org/example/petshopdesktop/modelviews/service-view.fxml +++ b/src/main/resources/org/example/petshopdesktop/modelviews/service-view.fxml @@ -66,13 +66,13 @@ - - - + + + - + From a12af3f0ee9dee7c041883e9f3a21dd1d321a285 Mon Sep 17 00:00:00 2001 From: Harkamal Randhawa Date: Sat, 21 Feb 2026 13:27:50 -0700 Subject: [PATCH 02/13] Implement RBAC with login screen for admin and staff roles - Add Role enum (ADMIN, STAFF) and UserSession singleton - Add User model and UserDB with SHA2-256 authentication - Add login screen (login-view.fxml + LoginController) - Update main-layout-view.fxml: dynamic username/role labels, logout button - Update MainLayoutController: hide Inventory/Suppliers/Product-Suppliers for STAFF, show username/role, handle logout - Update PetShopApplication to start with login screen and initialize users table - Update module-info.java to open/export auth package --- src/main/java/module-info.java | 2 + .../petshopdesktop/PetShopApplication.java | 10 ++- .../org/example/petshopdesktop/auth/Role.java | 6 ++ .../petshopdesktop/auth/UserSession.java | 44 ++++++++++ .../controllers/LoginController.java | 67 ++++++++++++++++ .../controllers/MainLayoutController.java | 45 +++++++++++ .../petshopdesktop/database/UserDB.java | 71 ++++++++++++++++ .../example/petshopdesktop/models/User.java | 27 +++++++ .../example/petshopdesktop/login-view.fxml | 80 +++++++++++++++++++ .../petshopdesktop/main-layout-view.fxml | 15 +++- 10 files changed, 363 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/example/petshopdesktop/auth/Role.java create mode 100644 src/main/java/org/example/petshopdesktop/auth/UserSession.java create mode 100644 src/main/java/org/example/petshopdesktop/controllers/LoginController.java create mode 100644 src/main/java/org/example/petshopdesktop/database/UserDB.java create mode 100644 src/main/java/org/example/petshopdesktop/models/User.java create mode 100644 src/main/resources/org/example/petshopdesktop/login-view.fxml diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 7459e876..d1f07dd9 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -8,7 +8,9 @@ module org.example.petshopdesktop { opens org.example.petshopdesktop to javafx.fxml; opens org.example.petshopdesktop.controllers.dialogcontrollers to javafx.fxml; opens org.example.petshopdesktop.controllers to javafx.fxml; + opens org.example.petshopdesktop.auth to javafx.fxml; exports org.example.petshopdesktop; exports org.example.petshopdesktop.controllers; + exports org.example.petshopdesktop.auth; } \ No newline at end of file diff --git a/src/main/java/org/example/petshopdesktop/PetShopApplication.java b/src/main/java/org/example/petshopdesktop/PetShopApplication.java index 8a8fcd65..06def37b 100644 --- a/src/main/java/org/example/petshopdesktop/PetShopApplication.java +++ b/src/main/java/org/example/petshopdesktop/PetShopApplication.java @@ -4,15 +4,21 @@ import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Scene; import javafx.stage.Stage; +import org.example.petshopdesktop.database.UserDB; import java.io.IOException; public class PetShopApplication extends Application { @Override public void start(Stage stage) throws IOException { - FXMLLoader fxmlLoader = new FXMLLoader(PetShopApplication.class.getResource("main-layout-view.fxml")); + try { + UserDB.initializeTable(); + } catch (Exception e) { + System.err.println("Warning: could not initialize users table: " + e.getMessage()); + } + FXMLLoader fxmlLoader = new FXMLLoader(PetShopApplication.class.getResource("login-view.fxml")); Scene scene = new Scene(fxmlLoader.load()); - stage.setTitle("Pet Shop Manager"); + stage.setTitle("Pet Shop Manager - Login"); stage.setScene(scene); stage.show(); } diff --git a/src/main/java/org/example/petshopdesktop/auth/Role.java b/src/main/java/org/example/petshopdesktop/auth/Role.java new file mode 100644 index 00000000..64f38459 --- /dev/null +++ b/src/main/java/org/example/petshopdesktop/auth/Role.java @@ -0,0 +1,6 @@ +package org.example.petshopdesktop.auth; + +public enum Role { + ADMIN, + STAFF +} diff --git a/src/main/java/org/example/petshopdesktop/auth/UserSession.java b/src/main/java/org/example/petshopdesktop/auth/UserSession.java new file mode 100644 index 00000000..748c9546 --- /dev/null +++ b/src/main/java/org/example/petshopdesktop/auth/UserSession.java @@ -0,0 +1,44 @@ +package org.example.petshopdesktop.auth; + +public class UserSession { + + private static UserSession instance; + + private String username; + private Role role; + + private UserSession() {} + + public static UserSession getInstance() { + if (instance == null) { + instance = new UserSession(); + } + return instance; + } + + public void login(String username, Role role) { + this.username = username; + this.role = role; + } + + public void logout() { + this.username = null; + this.role = null; + } + + public String getUsername() { + return username; + } + + public Role getRole() { + return role; + } + + public boolean isAdmin() { + return Role.ADMIN.equals(role); + } + + public boolean isLoggedIn() { + return username != null && role != null; + } +} diff --git a/src/main/java/org/example/petshopdesktop/controllers/LoginController.java b/src/main/java/org/example/petshopdesktop/controllers/LoginController.java new file mode 100644 index 00000000..43bb3334 --- /dev/null +++ b/src/main/java/org/example/petshopdesktop/controllers/LoginController.java @@ -0,0 +1,67 @@ +package org.example.petshopdesktop.controllers; + +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.scene.control.PasswordField; +import javafx.scene.control.TextField; +import javafx.stage.Stage; +import org.example.petshopdesktop.auth.UserSession; +import org.example.petshopdesktop.database.UserDB; +import org.example.petshopdesktop.models.User; + +import java.sql.SQLException; + +public class LoginController { + + @FXML + private TextField txtUsername; + + @FXML + private PasswordField txtPassword; + + @FXML + private Label lblError; + + @FXML + void btnLoginClicked(ActionEvent event) { + String username = txtUsername.getText().trim(); + String password = txtPassword.getText(); + + if (username.isEmpty() || password.isEmpty()) { + lblError.setText("Please enter username and password."); + return; + } + + try { + User user = UserDB.authenticate(username, password); + if (user == null) { + lblError.setText("Invalid username or password."); + txtPassword.clear(); + return; + } + + UserSession.getInstance().login(user.getUsername(), user.getRole()); + openMainLayout(); + + } catch (SQLException e) { + lblError.setText("Database error: " + e.getMessage()); + } + } + + private void openMainLayout() { + try { + FXMLLoader loader = new FXMLLoader( + getClass().getResource("/org/example/petshopdesktop/main-layout-view.fxml")); + Scene scene = new Scene(loader.load()); + Stage stage = (Stage) txtUsername.getScene().getWindow(); + stage.setScene(scene); + stage.setTitle("Pet Shop Manager"); + } catch (Exception e) { + lblError.setText("Error loading application: " + e.getMessage()); + e.printStackTrace(); + } + } +} diff --git a/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java b/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java index 431d38d4..3584a0cc 100644 --- a/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java +++ b/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java @@ -4,8 +4,12 @@ import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; +import javafx.scene.Scene; 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; public class MainLayoutController { @@ -18,6 +22,9 @@ public class MainLayoutController { @FXML private Button btnInventory; + @FXML + private Button btnLogout; + @FXML private Button btnPets; @@ -36,6 +43,12 @@ public class MainLayoutController { @FXML private Button btnSuppliers; + @FXML + private Label lblUsername; + + @FXML + private Label lblRole; + @FXML private StackPane spContentArea; @@ -93,11 +106,43 @@ public class MainLayoutController { updateButtons(btnSuppliers); } + @FXML + void btnLogoutClicked(ActionEvent event) { + UserSession.getInstance().logout(); + try { + FXMLLoader loader = new FXMLLoader( + getClass().getResource("/org/example/petshopdesktop/login-view.fxml")); + Scene scene = new Scene(loader.load()); + Stage stage = (Stage) btnLogout.getScene().getWindow(); + stage.setScene(scene); + stage.setTitle("Pet Shop Manager - Login"); + } catch (Exception e) { + System.err.println("Error loading login view: " + e.getMessage()); + e.printStackTrace(); + } + } + @FXML public void initialize() { + applyRBAC(); loadView("pet-view.fxml"); } + private void applyRBAC() { + UserSession session = UserSession.getInstance(); + + lblUsername.setText(session.getUsername()); + lblRole.setText(session.getRole().toString()); + + boolean isAdmin = session.isAdmin(); + btnInventory.setVisible(isAdmin); + btnInventory.setManaged(isAdmin); + btnSuppliers.setVisible(isAdmin); + btnSuppliers.setManaged(isAdmin); + btnProductSuppliers.setVisible(isAdmin); + btnProductSuppliers.setManaged(isAdmin); + } + /** * Load a view when a button is clicked on the navigation * @param fxmlFile the fxmlFile name to be loaded diff --git a/src/main/java/org/example/petshopdesktop/database/UserDB.java b/src/main/java/org/example/petshopdesktop/database/UserDB.java new file mode 100644 index 00000000..8909d211 --- /dev/null +++ b/src/main/java/org/example/petshopdesktop/database/UserDB.java @@ -0,0 +1,71 @@ +package org.example.petshopdesktop.database; + +import org.example.petshopdesktop.auth.Role; +import org.example.petshopdesktop.models.User; + +import java.sql.*; + +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)"; + + try (Connection conn = ConnectionDB.getConnection(); + PreparedStatement ps = conn.prepareStatement(sql)) { + + ps.setString(1, username); + ps.setString(2, password); + + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + int userId = rs.getInt("user_id"); + String uname = rs.getString("username"); + Role role = Role.valueOf(rs.getString("role").toUpperCase()); + return new User(userId, uname, 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, + username VARCHAR(100) NOT NULL UNIQUE, + password_hash CHAR(64) NOT NULL, + role ENUM('ADMIN','STAFF') NOT NULL + ) + """; + + 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); + } + } +} diff --git a/src/main/java/org/example/petshopdesktop/models/User.java b/src/main/java/org/example/petshopdesktop/models/User.java new file mode 100644 index 00000000..02089be4 --- /dev/null +++ b/src/main/java/org/example/petshopdesktop/models/User.java @@ -0,0 +1,27 @@ +package org.example.petshopdesktop.models; + +import org.example.petshopdesktop.auth.Role; + +public class User { + private int userId; + private String username; + private Role role; + + public User(int userId, String username, Role role) { + this.userId = userId; + this.username = username; + this.role = role; + } + + public int getUserId() { + return userId; + } + + public String getUsername() { + return username; + } + + 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 new file mode 100644 index 00000000..65199c95 --- /dev/null +++ b/src/main/resources/org/example/petshopdesktop/login-view.fxml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/org/example/petshopdesktop/main-layout-view.fxml b/src/main/resources/org/example/petshopdesktop/main-layout-view.fxml index 9579dff6..983d07a9 100644 --- a/src/main/resources/org/example/petshopdesktop/main-layout-view.fxml +++ b/src/main/resources/org/example/petshopdesktop/main-layout-view.fxml @@ -16,12 +16,12 @@ - From bbf06456f5b12c4a35b02c88681116e0957fd002 Mon Sep 17 00:00:00 2001 From: Harkamal Randhawa Date: Sun, 22 Feb 2026 17:39:42 -0700 Subject: [PATCH 03/13] comments --- .../org/example/petshopdesktop/auth/Role.java | 7 +++++++ .../example/petshopdesktop/auth/UserSession.java | 15 +++++++++++++++ .../controllers/LoginController.java | 9 +++++++++ .../controllers/MainLayoutController.java | 13 +++++++++++++ .../example/petshopdesktop/database/UserDB.java | 9 +++++++++ 5 files changed, 53 insertions(+) diff --git a/src/main/java/org/example/petshopdesktop/auth/Role.java b/src/main/java/org/example/petshopdesktop/auth/Role.java index 64f38459..e631ddf7 100644 --- a/src/main/java/org/example/petshopdesktop/auth/Role.java +++ b/src/main/java/org/example/petshopdesktop/auth/Role.java @@ -1,6 +1,13 @@ package org.example.petshopdesktop.auth; +/* +Petshop Desktop +Purpose: Application role definitions used by session state and role based access control. +*/ public enum Role { + // Administrative access, includes system management screens. ADMIN, + + // Staff access, limited to day to day operational screens. STAFF } diff --git a/src/main/java/org/example/petshopdesktop/auth/UserSession.java b/src/main/java/org/example/petshopdesktop/auth/UserSession.java index 748c9546..2cc4538a 100644 --- a/src/main/java/org/example/petshopdesktop/auth/UserSession.java +++ b/src/main/java/org/example/petshopdesktop/auth/UserSession.java @@ -1,14 +1,24 @@ package org.example.petshopdesktop.auth; +/* +Petshop Desktop +Purpose: In memory session state for the authenticated user. +Notes: Session is process local and cleared on logout or application restart. +*/ public class UserSession { + // Singleton instance used to share session state across controllers. private static UserSession instance; + // Current authenticated username, null when logged out. private String username; + + // Current authenticated role, null when logged out. private Role role; private UserSession() {} + // Lazily initialised singleton accessor. public static UserSession getInstance() { if (instance == null) { instance = new UserSession(); @@ -16,11 +26,13 @@ public class UserSession { return instance; } + // Stores identity and role for the active session. public void login(String username, Role role) { this.username = username; this.role = role; } + // Clears session state and returns the application to an unauthenticated state. public void logout() { this.username = null; this.role = null; @@ -34,10 +46,13 @@ public class UserSession { return role; } + // Convenience check for administrative privileges. + // Role.ADMIN.equals(role) remains safe when role is null. public boolean isAdmin() { return Role.ADMIN.equals(role); } + // Session is considered active only when both username and role are set. public boolean isLoggedIn() { return username != null && role != null; } diff --git a/src/main/java/org/example/petshopdesktop/controllers/LoginController.java b/src/main/java/org/example/petshopdesktop/controllers/LoginController.java index 43bb3334..440c5686 100644 --- a/src/main/java/org/example/petshopdesktop/controllers/LoginController.java +++ b/src/main/java/org/example/petshopdesktop/controllers/LoginController.java @@ -14,6 +14,10 @@ import org.example.petshopdesktop.models.User; import java.sql.SQLException; +/* +Petshop Desktop +Purpose: Authentication controller responsible for validating credentials and initialising the user session. +*/ public class LoginController { @FXML @@ -27,15 +31,18 @@ public class LoginController { @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."); @@ -43,6 +50,7 @@ public class LoginController { return; } + // Session state is stored in memory for use by controllers and UI RBAC. UserSession.getInstance().login(user.getUsername(), user.getRole()); openMainLayout(); @@ -53,6 +61,7 @@ public class LoginController { 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()); diff --git a/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java b/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java index 3584a0cc..a910432e 100644 --- a/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java +++ b/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java @@ -11,6 +11,10 @@ import javafx.scene.layout.StackPane; import javafx.stage.Stage; import org.example.petshopdesktop.auth.UserSession; +/* +Petshop Desktop +Purpose: Main application shell controller, includes navigation and UI level role based access control. +*/ public class MainLayoutController { @FXML @@ -108,6 +112,7 @@ public class MainLayoutController { @FXML void btnLogoutClicked(ActionEvent event) { + // Logout clears session state before returning to the login view. UserSession.getInstance().logout(); try { FXMLLoader loader = new FXMLLoader( @@ -124,16 +129,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"); } 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()); + // 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); @@ -141,6 +152,8 @@ public class MainLayoutController { btnSuppliers.setManaged(isAdmin); btnProductSuppliers.setVisible(isAdmin); btnProductSuppliers.setManaged(isAdmin); + + // Privileged operations should still be enforced within the relevant controllers and database methods. } /** diff --git a/src/main/java/org/example/petshopdesktop/database/UserDB.java b/src/main/java/org/example/petshopdesktop/database/UserDB.java index 8909d211..3b5f78b4 100644 --- a/src/main/java/org/example/petshopdesktop/database/UserDB.java +++ b/src/main/java/org/example/petshopdesktop/database/UserDB.java @@ -5,6 +5,10 @@ import org.example.petshopdesktop.models.User; import java.sql.*; +/* +Petshop Desktop +Purpose: User authentication and role lookup against the users table. +*/ public class UserDB { /** @@ -29,7 +33,11 @@ public class UserDB { if (rs.next()) { int userId = rs.getInt("user_id"); String uname = rs.getString("username"); + + // Role values are stored in the database as strings and normalised to match the enum. + // Table constraints limit role values, Role.valueOf is expected to be safe under normal operation. Role role = Role.valueOf(rs.getString("role").toUpperCase()); + return new User(userId, uname, role); } } @@ -51,6 +59,7 @@ public class UserDB { ) """; + // Default accounts support initial development and testing, credentials should be rotated or removed for deployment. String seedAdmin = """ INSERT IGNORE INTO users (username, password_hash, role) VALUES ('admin', SHA2('admin123', 256), 'ADMIN') From 1f784f72d166153b1ca5a69c0765058641ab29c3 Mon Sep 17 00:00:00 2001 From: Nikitha Date: Sun, 22 Feb 2026 18:21:22 -0700 Subject: [PATCH 04/13] appionmeent,service --- .../petshopdesktop/DTOs/ServiceDTO.java | 110 ++++++++++++++ .../controllers/AppointmentController.java | 77 +++++++++- .../controllers/ServiceController.java | 135 +++++++++++++----- .../AppointmentDialogController.java | 111 ++++++++++++++ .../ServiceDialogController.java | 98 +++++++++++++ .../database/AppointmentDB.java | 73 ++++++++++ .../petshopdesktop/database/ServiceDB.java | 120 ++++++++++++++++ .../petshopdesktop/models/Appointment.java | 40 ++++++ .../petshopdesktop/models/Service.java | 98 +++++++++++++ .../dialogviews/appointment-dialog-view.fxml | 11 +- 10 files changed, 835 insertions(+), 38 deletions(-) create mode 100644 src/main/java/org/example/petshopdesktop/DTOs/ServiceDTO.java create mode 100644 src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AppointmentDialogController.java create mode 100644 src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/ServiceDialogController.java create mode 100644 src/main/java/org/example/petshopdesktop/database/AppointmentDB.java create mode 100644 src/main/java/org/example/petshopdesktop/database/ServiceDB.java create mode 100644 src/main/java/org/example/petshopdesktop/models/Appointment.java create mode 100644 src/main/java/org/example/petshopdesktop/models/Service.java diff --git a/src/main/java/org/example/petshopdesktop/DTOs/ServiceDTO.java b/src/main/java/org/example/petshopdesktop/DTOs/ServiceDTO.java new file mode 100644 index 00000000..c876cd3f --- /dev/null +++ b/src/main/java/org/example/petshopdesktop/DTOs/ServiceDTO.java @@ -0,0 +1,110 @@ +package org.example.petshopdesktop.DTOs; + +import javafx.beans.property.SimpleDoubleProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleStringProperty; +import org.example.petshopdesktop.models.Service; + +/** + * The class for ServiceDTO, all service data stored here + */ +public class ServiceDTO { + + private SimpleIntegerProperty serviceId; + private SimpleStringProperty serviceName; + private SimpleStringProperty serviceDesc; + private SimpleIntegerProperty serviceDuration; + private SimpleDoubleProperty servicePrice; + + // constructor + public ServiceDTO(int serviceId, + String serviceName, + String serviceDesc, + int serviceDuration, + double servicePrice) { + + this.serviceId = new SimpleIntegerProperty(serviceId); + this.serviceName = new SimpleStringProperty(serviceName); + this.serviceDesc = new SimpleStringProperty(serviceDesc); + this.serviceDuration = new SimpleIntegerProperty(serviceDuration); + this.servicePrice = new SimpleDoubleProperty(servicePrice); + } + + // getters & setters + + public int getServiceId() { + return serviceId.get(); + } + + public SimpleIntegerProperty serviceIdProperty() { + return serviceId; + } + + public void setServiceId(int serviceId) { + this.serviceId.set(serviceId); + } + + public String getServiceName() { + return serviceName.get(); + } + + public SimpleStringProperty serviceNameProperty() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName.set(serviceName); + } + + public String getServiceDesc() { + return serviceDesc.get(); + } + + public SimpleStringProperty serviceDescProperty() { + return serviceDesc; + } + + public void setServiceDesc(String serviceDesc) { + this.serviceDesc.set(serviceDesc); + } + + public int getServiceDuration() { + return serviceDuration.get(); + } + + public SimpleIntegerProperty serviceDurationProperty() { + return serviceDuration; + } + + public void setServiceDuration(int serviceDuration) { + this.serviceDuration.set(serviceDuration); + } + + public double getServicePrice() { + return servicePrice.get(); + } + + public SimpleDoubleProperty servicePriceProperty() { + return servicePrice; + } + + public void setServicePrice(double servicePrice) { + this.servicePrice.set(servicePrice); + } + + /** + * Converts DTO into Service model (for edit/delete) + */ + public Service toService() { + + Service service = new Service( + getServiceId(), + getServiceName(), + getServiceDesc(), + getServiceDuration(), + getServicePrice() + ); + + return service; + } +} \ No newline at end of file diff --git a/src/main/java/org/example/petshopdesktop/controllers/AppointmentController.java b/src/main/java/org/example/petshopdesktop/controllers/AppointmentController.java index 9eb6dfce..3d5f6d13 100644 --- a/src/main/java/org/example/petshopdesktop/controllers/AppointmentController.java +++ b/src/main/java/org/example/petshopdesktop/controllers/AppointmentController.java @@ -6,9 +6,39 @@ import javafx.scene.control.Button; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextField; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +import java.io.IOException; +import org.example.petshopdesktop.database.AppointmentDB; +import org.example.petshopdesktop.models.Appointment; + public class AppointmentController { + + private ObservableList appointments = + FXCollections.observableArrayList(); + + @FXML + + + public void initialize() { + loadAppointments(); + } + + + + private void loadAppointments() { + appointments.setAll(AppointmentDB.getAllAppointments()); + tvAppointments.setItems(appointments); + } + @FXML private Button btnAdd; @@ -40,7 +70,7 @@ public class AppointmentController { private TableColumn colServiceName; @FXML - private TableView tvAppointments; + private TableView tvAppointments; @FXML private TextField txtSearch; @@ -48,16 +78,61 @@ public class AppointmentController { @FXML void btnAddClicked(ActionEvent event) { + try { + FXMLLoader loader = new FXMLLoader( + getClass().getResource("/appointment-dialog-view.fxml") + ); + + Stage stage = new Stage(); + stage.setScene(new Scene(loader.load())); + stage.showAndWait(); + + loadAppointments(); + + } catch (Exception e) { + e.printStackTrace(); + } } @FXML void btnDeleteClicked(ActionEvent event) { + Appointment selected = tvAppointments.getSelectionModel().getSelectedItem(); + + if (selected == null) return; + + try { + AppointmentDB.deleteAppointment( + selected.getAppointmentId() + ); + loadAppointments(); + } catch (Exception e) { + e.printStackTrace(); + } } @FXML void btnEditClicked(ActionEvent event) { + Appointment selected = tvAppointments.getSelectionModel().getSelectedItem(); + + if (selected == null) return; + + try { + FXMLLoader loader = new FXMLLoader( + getClass().getResource("/appointment-dialog-view.fxml") + ); + + Stage stage = new Stage(); + stage.setScene(new Scene(loader.load())); + stage.showAndWait(); + + loadAppointments(); + + } catch (Exception e) { + e.printStackTrace(); + } } } + diff --git a/src/main/java/org/example/petshopdesktop/controllers/ServiceController.java b/src/main/java/org/example/petshopdesktop/controllers/ServiceController.java index 65f73c7d..d0c361f7 100644 --- a/src/main/java/org/example/petshopdesktop/controllers/ServiceController.java +++ b/src/main/java/org/example/petshopdesktop/controllers/ServiceController.java @@ -2,56 +2,123 @@ package org.example.petshopdesktop.controllers; import javafx.event.ActionEvent; import javafx.fxml.FXML; -import javafx.scene.control.Button; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; -import javafx.scene.control.TextField; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.*; +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; public class ServiceController { - @FXML - private Button btnAdd; + @FXML private Button btnAdd; + @FXML private Button btnDelete; + @FXML private Button btnEdit; + + @FXML private TableColumn colServiceId; + @FXML private TableColumn colServiceName; + @FXML private TableColumn colServiceDesc; + @FXML private TableColumn colServiceDuration; + @FXML private TableColumn colServicePrice; + + @FXML private TableView tvServices; + + @FXML private TextField txtSearch; @FXML - private Button btnDelete; + public void initialize() { - @FXML - private Button btnEdit; + colServiceId.setCellValueFactory(new PropertyValueFactory<>("serviceId")); + colServiceName.setCellValueFactory(new PropertyValueFactory<>("serviceName")); + colServiceDesc.setCellValueFactory(new PropertyValueFactory<>("serviceDesc")); + colServiceDuration.setCellValueFactory(new PropertyValueFactory<>("serviceDuration")); + colServicePrice.setCellValueFactory(new PropertyValueFactory<>("servicePrice")); - @FXML - private TableColumn colServiceDesc; + loadServices(); + } - @FXML - private TableColumn colServiceDuration; + private void loadServices() { + try { + tvServices.setItems(ServiceDB.getServices()); + } catch (Exception e) { + showAlert("Database Error", "Unable to load services."); + e.printStackTrace(); + } + } - @FXML - private TableColumn colServiceId; - - @FXML - private TableColumn colServiceName; - - @FXML - private TableColumn colServicePrice; - - @FXML - private TableView tvServices; - - @FXML - private TextField txtSearch; @FXML void btnAddClicked(ActionEvent event) { - - } - - @FXML - void btnDeleteClicked(ActionEvent event) { - + openDialog(null); + loadServices(); } @FXML void btnEditClicked(ActionEvent event) { + Service selected = tvServices.getSelectionModel().getSelectedItem(); + + if (selected == null) { + showAlert("Select Service", "Please select a service to edit."); + return; + } + + openDialog(selected); + loadServices(); } -} + @FXML + void btnDeleteClicked(ActionEvent event) { + + Service selected = tvServices.getSelectionModel().getSelectedItem(); + + if (selected == null) { + showAlert("Select Service", "Please select a service to delete."); + return; + } + + try { + ServiceDB.deleteService(selected.getServiceId()); + loadServices(); + } catch (Exception e) { + showAlert("Database Error", "Unable to delete service."); + e.printStackTrace(); + } + } + + private void openDialog(Service service) { + + try { + FXMLLoader loader = new FXMLLoader( + getClass().getResource( + "/org.example.petshopdesktop/dialogviews/ServiceDialogView.fxml" + ) + ); + + Parent root = loader.load(); + + // IMPORTANT: specify controller type + ServiceDialogController controller = + loader.getController(); + + controller.setService(service); + + Stage stage = new Stage(); + stage.setScene(new Scene(root)); + stage.showAndWait(); + + } catch (Exception e) { + e.printStackTrace(); + } + } + private void showAlert(String title, String msg) { + Alert alert = new Alert(Alert.AlertType.INFORMATION); + alert.setTitle(title); + alert.setHeaderText(null); + alert.setContentText(msg); + alert.showAndWait(); + } +} \ No newline at end of file diff --git a/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AppointmentDialogController.java b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AppointmentDialogController.java new file mode 100644 index 00000000..af607180 --- /dev/null +++ b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AppointmentDialogController.java @@ -0,0 +1,111 @@ +package org.example.petshopdesktop.controllers.dialogcontrollers; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.control.Button; +import javafx.scene.control.ComboBox; +import javafx.scene.control.DatePicker; +import javafx.scene.control.Label; +import javafx.scene.input.MouseEvent; +import javafx.stage.Stage; + +import org.example.petshopdesktop.database.AppointmentDB; +import org.example.petshopdesktop.models.Appointment; + +import java.sql.Time; + +public class AppointmentDialogController { + + @FXML + private Button btnCancel; + + @FXML + private Button btnSave; + + @FXML + private ComboBox cbAppointmentStatus; + + @FXML + private ComboBox