From 54ff97448d705c0141436c610ca730bde3458fe9 Mon Sep 17 00:00:00 2001 From: Harkamal Randhawa Date: Thu, 9 Apr 2026 12:28:33 -0600 Subject: [PATCH] Refactor user management --- .../api/endpoints/CustomerApi.java | 48 +++++++ .../petshopdesktop/api/endpoints/UserApi.java | 4 + .../controllers/MainLayoutController.java | 4 +- .../controllers/StaffAccountsController.java | 136 ++++++++---------- .../StaffEditDialogController.java | 61 ++++---- .../StaffRegisterDialogController.java | 30 ++-- .../petshopdesktop/main-layout-view.fxml | 2 +- .../modelviews/staff-accounts-view.fxml | 15 +- 8 files changed, 179 insertions(+), 121 deletions(-) create mode 100644 desktop/src/main/java/org/example/petshopdesktop/api/endpoints/CustomerApi.java diff --git a/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/CustomerApi.java b/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/CustomerApi.java new file mode 100644 index 00000000..9f286f6b --- /dev/null +++ b/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/CustomerApi.java @@ -0,0 +1,48 @@ +package org.example.petshopdesktop.api.endpoints; + +import com.fasterxml.jackson.core.type.TypeReference; +import org.example.petshopdesktop.api.ApiClient; +import org.example.petshopdesktop.api.dto.common.PageResponse; +import org.example.petshopdesktop.api.dto.user.UserRequest; +import org.example.petshopdesktop.api.dto.user.UserResponse; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.List; + +public class CustomerApi { + private static final CustomerApi INSTANCE = new CustomerApi(); + private final ApiClient apiClient; + + private CustomerApi() { + this.apiClient = ApiClient.getInstance(); + } + + public static CustomerApi getInstance() { + return INSTANCE; + } + + public List listCustomers(String query) throws Exception { + String path = "/api/v1/customers?page=0&size=1000"; + if (query != null && !query.isEmpty()) { + path += "&q=" + URLEncoder.encode(query, StandardCharsets.UTF_8); + } + String response = apiClient.getRawResponse(path); + PageResponse pageResponse = apiClient.getObjectMapper().readValue( + response, + new TypeReference>() {} + ); + if (pageResponse == null) { + throw new IllegalStateException("Null response from customers endpoint"); + } + return pageResponse.getContent(); + } + + public UserResponse updateCustomer(Long id, UserRequest request) throws Exception { + return apiClient.put("/api/v1/customers/" + id, request, UserResponse.class); + } + + public UserResponse createCustomer(UserRequest request) throws Exception { + return apiClient.post("/api/v1/customers", request, UserResponse.class); + } +} diff --git a/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/UserApi.java b/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/UserApi.java index 315d53a1..e927f03c 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/UserApi.java +++ b/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/UserApi.java @@ -41,4 +41,8 @@ public class UserApi { public UserResponse createUser(UserRequest request) throws Exception { return apiClient.post("/api/v1/users", request, UserResponse.class); } + + public UserResponse updateUser(Long id, UserRequest request) throws Exception { + return apiClient.put("/api/v1/users/" + id, request, UserResponse.class); + } } diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java index 4481bc9c..1b10ce19 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java @@ -370,8 +370,8 @@ public class MainLayoutController { btnPurchaseOrders.setManaged(isAdmin); if (btnStaffAccounts != null) { - btnStaffAccounts.setVisible(isAdmin); - btnStaffAccounts.setManaged(isAdmin); + btnStaffAccounts.setVisible(true); + btnStaffAccounts.setManaged(true); } if (lblAdminSection != null) { diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/StaffAccountsController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/StaffAccountsController.java index bec351a4..9eab0c7b 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/StaffAccountsController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/StaffAccountsController.java @@ -16,14 +16,12 @@ import javafx.scene.control.TextField; import javafx.scene.control.cell.PropertyValueFactory; import javafx.stage.Modality; import javafx.stage.Stage; -import org.example.petshopdesktop.api.dto.employee.EmployeeResponse; -import org.example.petshopdesktop.api.endpoints.EmployeeApi; +import org.example.petshopdesktop.api.dto.user.UserResponse; +import org.example.petshopdesktop.api.endpoints.UserApi; +import org.example.petshopdesktop.api.endpoints.CustomerApi; import org.example.petshopdesktop.auth.UserSession; -import org.example.petshopdesktop.models.StaffAccount; import org.example.petshopdesktop.util.ActivityLogger; -import java.sql.Timestamp; -import java.time.ZoneId; import java.util.List; import java.util.Comparator; import java.util.stream.Collectors; @@ -31,25 +29,28 @@ import java.util.stream.Collectors; public class StaffAccountsController { @FXML - private TableView tvStaff; + private TableView tvStaff; @FXML - private TableColumn colUsername; + private TableColumn colUsername; @FXML - private TableColumn colName; + private TableColumn colName; @FXML - private TableColumn colEmail; + private TableColumn colEmail; @FXML - private TableColumn colPhone; + private TableColumn colPhone; @FXML - private TableColumn colStatus; + private TableColumn colRole; @FXML - private TableColumn colCreated; + private TableColumn colStatus; + + @FXML + private TableColumn colCreated; @FXML private TextField txtSearch; @@ -63,8 +64,8 @@ public class StaffAccountsController { @FXML private Button btnEditAccount; - private final ObservableList staffAccounts = FXCollections.observableArrayList(); - private FilteredList filtered; + private final ObservableList staffAccounts = FXCollections.observableArrayList(); + private FilteredList filtered; @FXML public void initialize() { @@ -72,7 +73,20 @@ public class StaffAccountsController { colName.setCellValueFactory(new PropertyValueFactory<>("fullName")); colEmail.setCellValueFactory(new PropertyValueFactory<>("email")); colPhone.setCellValueFactory(new PropertyValueFactory<>("phone")); - colStatus.setCellValueFactory(new PropertyValueFactory<>("status")); + colRole.setCellValueFactory(new PropertyValueFactory<>("role")); + colStatus.setCellValueFactory(new PropertyValueFactory<>("active")); + colStatus.setCellFactory(column -> new javafx.scene.control.TableCell() { + @Override + protected void updateItem(String item, boolean empty) { + super.updateItem(item, empty); + if (empty || getTableRow() == null || getTableRow().getItem() == null) { + setText(null); + } else { + Boolean active = getTableRow().getItem().getActive(); + setText(active != null && active ? "Active" : "Inactive"); + } + } + }); colCreated.setCellValueFactory(new PropertyValueFactory<>("createdAt")); filtered = new FilteredList<>(staffAccounts, a -> true); @@ -86,16 +100,6 @@ public class StaffAccountsController { } }); - if (!UserSession.getInstance().isAdmin()) { - lblError.setText("Access restricted."); - tvStaff.setDisable(true); - btnCreateAccount.setDisable(true); - if (btnEditAccount != null) { - btnEditAccount.setDisable(true); - } - return; - } - if (btnEditAccount != null) { btnEditAccount.setDisable(true); } @@ -103,6 +107,9 @@ public class StaffAccountsController { refresh(); } + refresh(); + } + @FXML void btnRefreshClicked(ActionEvent event) { refresh(); @@ -130,26 +137,36 @@ public class StaffAccountsController { @FXML void btnEditAccountClicked(ActionEvent event) { lblError.setText(""); - StaffAccount selected = tvStaff.getSelectionModel().getSelectedItem(); + UserResponse selected = tvStaff.getSelectionModel().getSelectedItem(); if (selected == null) { - lblError.setText("Select a staff account to edit."); + lblError.setText("Select a user account to edit."); return; } + + UserSession session = UserSession.getInstance(); + boolean isAdmin = session.isAdmin(); + boolean targetIsAdmin = "ADMIN".equalsIgnoreCase(selected.getRole()); + + if (isAdmin && targetIsAdmin && !selected.getId().equals(session.getUserId())) { + lblError.setText("Admins cannot edit other admin accounts."); + return; + } + try { FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/example/petshopdesktop/dialogviews/staff-edit-dialog-view.fxml")); Stage dialog = new Stage(); dialog.initOwner(tvStaff.getScene().getWindow()); dialog.initModality(Modality.APPLICATION_MODAL); - dialog.setTitle("Edit Staff Account"); + dialog.setTitle("Edit User Account"); dialog.setScene(new Scene(loader.load())); dialog.setResizable(false); var controller = (org.example.petshopdesktop.controllers.dialogcontrollers.StaffEditDialogController) loader.getController(); - controller.setStaffAccount(selected); + controller.setUser(selected); dialog.showAndWait(); refresh(); } catch (Exception e) { - ActivityLogger.getInstance().logException("StaffAccountsController.btnEditAccountClicked", e, "Opening staff edit dialog"); - lblError.setText("Could not open staff account editor."); + ActivityLogger.getInstance().logException("StaffAccountsController.btnEditAccountClicked", e, "Opening user edit dialog"); + lblError.setText("Could not open user account editor."); } } @@ -159,62 +176,35 @@ public class StaffAccountsController { new Thread(() -> { try { - List employees = EmployeeApi.getInstance().listEmployees(null); - List accounts = employees.stream() - .map(this::mapToStaffAccount) - .sorted(Comparator.comparing(StaffAccount::getCreatedAt, Comparator.nullsLast(Comparator.reverseOrder()))) + UserSession session = UserSession.getInstance(); + List users; + if (session.isAdmin()) { + users = UserApi.getInstance().listUsers(null); + } else { + users = CustomerApi.getInstance().listCustomers(null); + } + + List sortedUsers = users.stream() + .sorted(Comparator.comparing(UserResponse::getCreatedAt, Comparator.nullsLast(Comparator.reverseOrder()))) .collect(Collectors.toList()); Platform.runLater(() -> { - staffAccounts.setAll(accounts); + staffAccounts.setAll(sortedUsers); tvStaff.setDisable(false); }); } catch (Exception e) { - ActivityLogger.getInstance().logException("StaffAccountsController.refresh", e, "Loading staff accounts"); + ActivityLogger.getInstance().logException("StaffAccountsController.refresh", e, "Loading user accounts"); Platform.runLater(() -> { String message = e.getMessage(); lblError.setText(message == null || message.isBlank() - ? "Could not load staff accounts." - : "Could not load staff accounts: " + message); + ? "Could not load user accounts." + : "Could not load user accounts: " + message); tvStaff.setDisable(false); }); } }).start(); } - private StaffAccount mapToStaffAccount(EmployeeResponse employee) { - long userId = employee.getUserId() != null ? employee.getUserId() : 0L; - long employeeId = employee.getEmployeeId() != null ? employee.getEmployeeId() : 0L; - String username = employee.getUsername(); - String firstName = employee.getFirstName() != null ? employee.getFirstName() : ""; - String lastName = employee.getLastName() != null ? employee.getLastName() : ""; - if (firstName.isBlank() && lastName.isBlank()) { - String fullName = employee.getFullName() != null ? employee.getFullName() : ""; - String[] names = splitFullName(fullName); - firstName = names[0]; - lastName = names[1]; - } - String email = employee.getEmail() != null ? employee.getEmail() : ""; - String phone = employee.getPhone() != null ? employee.getPhone() : ""; - String role = employee.getRole() != null ? employee.getRole() : "STAFF"; - boolean active = employee.getActive() != null ? employee.getActive() : false; - Timestamp createdAt = employee.getCreatedAt() != null - ? Timestamp.from(employee.getCreatedAt().atZone(ZoneId.systemDefault()).toInstant()) - : null; - - return new StaffAccount(userId, employeeId, username, firstName, lastName, email, phone, role, active, createdAt); - } - - private String[] splitFullName(String fullName) { - if (fullName == null || fullName.trim().isEmpty()) { - return new String[]{"", ""}; - } - String[] parts = fullName.trim().split("\\s+", 2); - String firstName = parts.length > 0 ? parts[0] : ""; - String lastName = parts.length > 1 ? parts[1] : ""; - return new String[]{firstName, lastName}; - } - private void applyFilter(String text) { String q = text == null ? "" : text.trim().toLowerCase(); if (q.isEmpty()) { @@ -227,7 +217,7 @@ public class StaffAccountsController { || safe(a.getFullName()).contains(q) || safe(a.getEmail()).contains(q) || safe(a.getPhone()).contains(q) - || safe(a.getStatus()).contains(q) + || safe(a.getRole()).contains(q) ); } diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/StaffEditDialogController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/StaffEditDialogController.java index 5b8b4d6a..8b88e98f 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/StaffEditDialogController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/StaffEditDialogController.java @@ -9,10 +9,11 @@ import javafx.scene.control.PasswordField; import javafx.scene.control.TextField; import javafx.stage.Stage; import org.example.petshopdesktop.Validator; -import org.example.petshopdesktop.api.dto.employee.EmployeeRequest; -import org.example.petshopdesktop.api.endpoints.EmployeeApi; +import org.example.petshopdesktop.api.dto.user.UserRequest; +import org.example.petshopdesktop.api.dto.user.UserResponse; +import org.example.petshopdesktop.api.endpoints.UserApi; +import org.example.petshopdesktop.api.endpoints.CustomerApi; import org.example.petshopdesktop.auth.UserSession; -import org.example.petshopdesktop.models.StaffAccount; import org.example.petshopdesktop.util.ActivityLogger; public class StaffEditDialogController { @@ -44,22 +45,34 @@ public class StaffEditDialogController { @FXML private Button btnSave; - private StaffAccount staffAccount; + private UserResponse user; - public void setStaffAccount(StaffAccount staffAccount) { - this.staffAccount = staffAccount; - txtFirstName.setText(staffAccount.getFirstName()); - txtLastName.setText(staffAccount.getLastName()); - txtEmail.setText(staffAccount.getEmail()); - txtPhone.setText(staffAccount.getPhone()); - txtUsername.setText(staffAccount.getUsername()); + public void setUser(UserResponse user) { + this.user = user; + String fullName = user.getFullName() == null ? "" : user.getFullName(); + String[] names = splitFullName(fullName); + txtFirstName.setText(names[0]); + txtLastName.setText(names[1]); + txtEmail.setText(user.getEmail()); + txtPhone.setText(user.getPhone()); + txtUsername.setText(user.getUsername()); + } + + private String[] splitFullName(String fullName) { + if (fullName == null || fullName.trim().isEmpty()) { + return new String[]{"", ""}; + } + String[] parts = fullName.trim().split("\\s+", 2); + String firstName = parts.length > 0 ? parts[0] : ""; + String lastName = parts.length > 1 ? parts[1] : ""; + return new String[]{firstName, lastName}; } @FXML void btnSaveClicked(ActionEvent event) { lblError.setText(""); - if (staffAccount == null) { - lblError.setText("No staff account selected."); + if (user == null) { + lblError.setText("No user selected."); return; } @@ -105,26 +118,26 @@ public class StaffEditDialogController { new Thread(() -> { try { - Long storeId = UserSession.getInstance().getStoreId(); - EmployeeRequest request = new EmployeeRequest(); + UserRequest request = new UserRequest(); request.setUsername(username); request.setPassword(password.isEmpty() ? null : password); - request.setFirstName(firstName); - request.setLastName(lastName); request.setFullName(firstName + " " + lastName); request.setEmail(email); request.setPhone(phone); - request.setRole(staffAccount.getRole()); - request.setStaffRole("Staff"); - request.setPrimaryStoreId(storeId); - request.setActive(staffAccount.isActive()); + request.setRole(user.getRole()); + request.setActive(user.getActive()); - EmployeeApi.getInstance().updateEmployee(staffAccount.getEmployeeId(), request); + UserSession session = UserSession.getInstance(); + if (session.isAdmin()) { + UserApi.getInstance().updateUser(user.getId(), request); + } else { + CustomerApi.getInstance().updateCustomer(user.getId(), request); + } Platform.runLater(this::close); } catch (Exception e) { - ActivityLogger.getInstance().logException("StaffEditDialogController.btnSaveClicked", e, "Updating staff account"); - String msg = e.getMessage() == null ? "Could not update staff account." : e.getMessage(); + ActivityLogger.getInstance().logException("StaffEditDialogController.btnSaveClicked", e, "Updating user"); + String msg = e.getMessage() == null ? "Could not update user." : e.getMessage(); Platform.runLater(() -> { lblError.setText(msg); btnSave.setDisable(false); diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/StaffRegisterDialogController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/StaffRegisterDialogController.java index 5be1a27e..03b7a241 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/StaffRegisterDialogController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/StaffRegisterDialogController.java @@ -9,8 +9,9 @@ import javafx.scene.control.Label; import javafx.scene.control.PasswordField; import javafx.scene.control.TextField; import javafx.stage.Stage; -import org.example.petshopdesktop.api.dto.employee.EmployeeRequest; -import org.example.petshopdesktop.api.endpoints.EmployeeApi; +import org.example.petshopdesktop.api.dto.user.UserRequest; +import org.example.petshopdesktop.api.endpoints.UserApi; +import org.example.petshopdesktop.api.endpoints.CustomerApi; import org.example.petshopdesktop.auth.UserSession; import org.example.petshopdesktop.Validator; import org.example.petshopdesktop.util.ActivityLogger; @@ -90,33 +91,34 @@ public class StaffRegisterDialogController { new Thread(() -> { try { - Long storeId = UserSession.getInstance().getStoreId(); - EmployeeRequest request = new EmployeeRequest(); + UserSession session = UserSession.getInstance(); + UserRequest request = new UserRequest(); request.setUsername(username); request.setPassword(password); - request.setFirstName(firstName); - request.setLastName(lastName); request.setFullName(firstName + " " + lastName); request.setEmail(email); request.setPhone(phone); - request.setRole("STAFF"); - request.setStaffRole("Staff"); - request.setPrimaryStoreId(storeId); request.setActive(true); - EmployeeApi.getInstance().createEmployee(request); + if (session.isAdmin()) { + request.setRole("STAFF"); + UserApi.getInstance().createUser(request); + } else { + request.setRole("CUSTOMER"); + CustomerApi.getInstance().createCustomer(request); + } Platform.runLater(() -> { Alert alert = new Alert(Alert.AlertType.INFORMATION); - alert.setTitle("Staff Account"); + alert.setTitle("Account Created"); alert.setHeaderText(null); - alert.setContentText("Staff account created. You can log in now."); + alert.setContentText("Account created successfully."); alert.showAndWait(); close(); }); } catch (Exception e) { - ActivityLogger.getInstance().logException("StaffRegisterDialogController.btnCreateClicked", e, "Creating staff account"); - String msg = e.getMessage() == null ? "Could not create staff account." : e.getMessage(); + ActivityLogger.getInstance().logException("StaffRegisterDialogController.btnCreateClicked", e, "Creating account"); + String msg = e.getMessage() == null ? "Could not create account." : e.getMessage(); Platform.runLater(() -> { if (msg.toLowerCase().contains("duplicate") || msg.toLowerCase().contains("unique")) { lblError.setText("Username already exists."); diff --git a/desktop/src/main/resources/org/example/petshopdesktop/main-layout-view.fxml b/desktop/src/main/resources/org/example/petshopdesktop/main-layout-view.fxml index 4e1cee6f..b7b209c5 100644 --- a/desktop/src/main/resources/org/example/petshopdesktop/main-layout-view.fxml +++ b/desktop/src/main/resources/org/example/petshopdesktop/main-layout-view.fxml @@ -211,7 +211,7 @@ -