From 819109893c61e85a981118aa1da26b263c3e516a Mon Sep 17 00:00:00 2001 From: Alex <78383757+Lextical@users.noreply.github.com> Date: Mon, 13 Apr 2026 21:40:34 -0600 Subject: [PATCH] added dropdowns for breed desktop --- .../api/endpoints/DropdownApi.java | 9 +++ .../AdoptionDialogController.java | 47 +++++++++++++- .../AppointmentDialogController.java | 56 ++++++++++++++-- .../InventoryDialogController.java | 2 +- .../PetDialogController.java | 65 +++++++++++++++---- .../StaffRegisterDialogController.java | 4 ++ .../dialogviews/pet-dialog-view.fxml | 8 +-- 7 files changed, 166 insertions(+), 25 deletions(-) diff --git a/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/DropdownApi.java b/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/DropdownApi.java index 127d75db..75c0c297 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/DropdownApi.java +++ b/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/DropdownApi.java @@ -42,6 +42,15 @@ public class DropdownApi { return apiClient.getObjectMapper().readValue(response, new TypeReference>() {}); } + public List getPetBreeds(String species) throws Exception { + String encoded = java.net.URLEncoder.encode(species, java.nio.charset.StandardCharsets.UTF_8); + String response = apiClient.getRawResponse("/api/v1/dropdowns/pet-breeds?species=" + encoded); + if (response == null || response.isEmpty()) { + throw new IllegalStateException("Empty response from pet breeds endpoint"); + } + return apiClient.getObjectMapper().readValue(response, new TypeReference>() {}); + } + public List getProducts() throws Exception { String response = apiClient.getRawResponse("/api/v1/dropdowns/products"); if (response == null || response.isEmpty()) { diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AdoptionDialogController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AdoptionDialogController.java index 21a89e31..399f1ce6 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AdoptionDialogController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AdoptionDialogController.java @@ -62,7 +62,7 @@ public class AdoptionDialogController { private boolean suppressPaymentDialog = false; private ObservableList statusList = FXCollections.observableArrayList( - "Pending", "Completed", "Cancelled" + "Pending", "Completed", "Missed", "Cancelled" ); @FXML @@ -282,6 +282,7 @@ public class AdoptionDialogController { } suppressPaymentDialog = true; + cbAdoptionStatus.setItems(statusList); for (String status : cbAdoptionStatus.getItems()) { if (status.equals(adoption.getAdoptionStatus())) { cbAdoptionStatus.getSelectionModel().select(status); @@ -289,13 +290,57 @@ public class AdoptionDialogController { } } suppressPaymentDialog = false; + applyEditModeLock(); } } + private void applyEditModeLock() { + String status = cbAdoptionStatus.getValue(); + + if ("Cancelled".equalsIgnoreCase(status)) { + cbPet.setDisable(true); + cbCustomer.setDisable(true); + cbEmployee.setDisable(true); + dpAdoptionDate.setDisable(true); + cbAdoptionStatus.setDisable(true); + cbAdoptionStatus.setItems(FXCollections.observableArrayList("Cancelled")); + btnSave.setDisable(true); + return; + } + + LocalDate adoptionDate = dpAdoptionDate.getValue(); + boolean isPast = adoptionDate != null && adoptionDate.isBefore(LocalDate.now()); + + cbPet.setDisable(true); + cbCustomer.setDisable(true); + dpAdoptionDate.setDisable(false); + cbEmployee.setDisable(false); + cbAdoptionStatus.setDisable(false); + + suppressPaymentDialog = true; + if (isPast) { + cbAdoptionStatus.setItems(FXCollections.observableArrayList("Completed", "Missed")); + dpAdoptionDate.setDisable(true); + } else { + cbAdoptionStatus.setItems(FXCollections.observableArrayList("Pending", "Cancelled")); + } + if (!cbAdoptionStatus.getItems().contains(cbAdoptionStatus.getValue())) { + cbAdoptionStatus.getSelectionModel().selectFirst(); + } + suppressPaymentDialog = false; + } + public void setMode(String mode) { this.mode = mode; lblMode.setText(mode + " Adoption"); lblAdoptionId.setVisible(mode.equals("Edit")); + if (mode.equals("Add")) { + suppressPaymentDialog = true; + cbAdoptionStatus.setItems(FXCollections.observableArrayList("Pending")); + cbAdoptionStatus.setValue("Pending"); + cbAdoptionStatus.setDisable(true); + suppressPaymentDialog = false; + } } private void applySelectedPet() { diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AppointmentDialogController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AppointmentDialogController.java index 91490c8f..c3980df5 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AppointmentDialogController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AppointmentDialogController.java @@ -43,24 +43,26 @@ public class AppointmentDialogController { @FXML private Label lblAppointmentId; @FXML private Label lblMode; - private String mode = null; + private String mode = null; private AppointmentDTO selectedAppointment = null; private Long pendingPetSelectionId = null; - - private ObservableList statusList = - FXCollections.observableArrayList( - "Booked", "Completed", "Missed", "Cancelled" - ); + private boolean isOriginallyCancel = false; + private boolean isOriginallyCompletedOrMissed = false; public void setMode(String mode) { this.mode = mode; lblMode.setText(mode + " Appointment"); lblAppointmentId.setVisible(!mode.equals("Add")); + if (mode.equals("Add")) { + cbAppointmentStatus.setItems(FXCollections.observableArrayList("Booked")); + cbAppointmentStatus.setValue("Booked"); + cbAppointmentStatus.setDisable(true); + } } @FXML public void initialize() { - cbAppointmentStatus.setItems(statusList); + cbAppointmentStatus.setItems(FXCollections.observableArrayList("Booked", "Completed", "Missed", "Cancelled")); cbPet.setDisable(true); cbEmployee.setPromptText("Select an employee"); cbPet.setPromptText("Select a customer first"); @@ -228,6 +230,46 @@ public class AppointmentDialogController { applySelectedService(); applySelectedCustomer(); applySelectedEmployee(); + applyEditModeLock(); + } + + private void applyEditModeLock() { + String status = cbAppointmentStatus.getValue(); + isOriginallyCancel = "Cancelled".equalsIgnoreCase(status); + isOriginallyCompletedOrMissed = "Completed".equalsIgnoreCase(status) || "Missed".equalsIgnoreCase(status); + + if (isOriginallyCancel) { + cbService.setDisable(true); + cbCustomer.setDisable(true); + cbPet.setDisable(true); + cbEmployee.setDisable(true); + cbHour.setDisable(true); + cbMinute.setDisable(true); + dpAppointmentDate.setDisable(true); + cbAppointmentStatus.setDisable(true); + cbAppointmentStatus.setItems(FXCollections.observableArrayList("Cancelled")); + btnSave.setDisable(true); + } else if (isOriginallyCompletedOrMissed) { + cbService.setDisable(true); + cbCustomer.setDisable(true); + cbPet.setDisable(true); + cbEmployee.setDisable(true); + cbHour.setDisable(true); + cbMinute.setDisable(true); + dpAppointmentDate.setDisable(true); + cbAppointmentStatus.setDisable(false); + cbAppointmentStatus.setItems(FXCollections.observableArrayList("Completed", "Missed")); + } else { + cbService.setDisable(true); + cbCustomer.setDisable(true); + cbPet.setDisable(true); + cbEmployee.setDisable(false); + cbHour.setDisable(false); + cbMinute.setDisable(false); + dpAppointmentDate.setDisable(false); + cbAppointmentStatus.setDisable(false); + cbAppointmentStatus.setItems(FXCollections.observableArrayList("Booked", "Cancelled")); + } } private void buttonSaveClicked(MouseEvent e) { diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/InventoryDialogController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/InventoryDialogController.java index 499b5ae8..54ff00f5 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/InventoryDialogController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/InventoryDialogController.java @@ -121,7 +121,7 @@ public class InventoryDialogController { //Validate inputs errorMsg += Validator.isPresent(txtQuantity.getText(), "Quantity"); errorMsg += Validator.isLessThanVarChars(txtQuantity.getText(), "Quantity", 11); - errorMsg += Validator.isNonNegativeInteger(txtQuantity.getText(), "Quantity"); + errorMsg += Validator.isPositiveInteger(txtQuantity.getText(), "Quantity"); //Operation only occurs if there are no errors if (errorMsg.isEmpty()) { diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/PetDialogController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/PetDialogController.java index 16805d34..ea4e853a 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/PetDialogController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/PetDialogController.java @@ -28,6 +28,7 @@ import java.math.BigDecimal; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; public class PetDialogController { @@ -41,6 +42,8 @@ public class PetDialogController { @FXML private Button btnChangeImage; @FXML private Button btnRemoveImage; @FXML private ComboBox cbPetStatus; + @FXML private ComboBox cbPetSpecies; + @FXML private ComboBox cbPetBreed; @FXML private ComboBox cbCustomer; @FXML private ComboBox cbStore; @FXML private VBox vbCustomerField; @@ -51,10 +54,8 @@ public class PetDialogController { @FXML private Label lblImageStatus; @FXML private ImageView imgPetPreview; @FXML private TextField txtPetAge; - @FXML private TextField txtPetBreed; @FXML private TextField txtPetName; @FXML private TextField txtPetPrice; - @FXML private ComboBox cbPetSpecies; private String mode = null; private File selectedImageFile; @@ -65,6 +66,7 @@ public class PetDialogController { private Long pendingStoreId = null; private Long originalCustomerId = null; private boolean isOriginallyOwnedOrAdopted = false; + private String pendingBreedValue = null; private final ObservableList statusList = FXCollections.observableArrayList( STATUS_AVAILABLE, STATUS_ADOPTED, STATUS_OWNED, STATUS_PENDING @@ -73,6 +75,7 @@ public class PetDialogController { @FXML void initialize() { cbPetStatus.setItems(statusList); + cbPetBreed.setDisable(true); cbCustomer.setCellFactory(param -> new ListCell<>() { @Override protected void updateItem(DropdownOption o, boolean empty) { @@ -106,6 +109,18 @@ public class PetDialogController { loadCustomers(); loadStores(); + cbPetSpecies.valueProperty().addListener((obs, oldVal, newVal) -> { + if (newVal != null && !newVal.isBlank()) { + if (!cbPetSpecies.isDisabled()) cbPetBreed.setDisable(false); + loadBreeds(newVal); + } else { + cbPetBreed.setItems(FXCollections.observableArrayList()); + cbPetBreed.setValue(null); + cbPetBreed.setPromptText("Select Species first"); + if (!cbPetSpecies.isDisabled()) cbPetBreed.setDisable(true); + } + }); + cbPetStatus.valueProperty().addListener((obs, oldVal, newVal) -> { if (newVal != null) applyStatusRules(newVal, true); }); @@ -129,6 +144,29 @@ public class PetDialogController { refreshImagePreview(); } + private void loadBreeds(String species) { + cbPetBreed.setPromptText("Loading breeds..."); + new Thread(() -> { + try { + List options = DropdownApi.getInstance().getPetBreeds(species); + List breeds = options.stream().map(DropdownOption::getLabel).collect(Collectors.toList()); + Platform.runLater(() -> { + cbPetBreed.setItems(FXCollections.observableArrayList(breeds)); + cbPetBreed.setPromptText("Select Breed"); + if (pendingBreedValue != null) { + cbPetBreed.setValue(pendingBreedValue); + pendingBreedValue = null; + } + }); + } catch (Exception e) { + Platform.runLater(() -> { + ActivityLogger.getInstance().logException("PetDialogController.loadBreeds", e, "Loading breeds for species: " + species); + cbPetBreed.setPromptText("Unable to load breeds"); + }); + } + }).start(); + } + private void applyStatusRules(String status, boolean clearInvalidSelections) { if (STATUS_AVAILABLE.equalsIgnoreCase(status)) { vbCustomerField.setDisable(true); @@ -151,9 +189,10 @@ public class PetDialogController { errorMsg += Validator.isPresent(txtPetName.getText(), "Pet Name"); errorMsg += Validator.isPresent(txtPetAge.getText(), "Age"); - errorMsg += Validator.isPresent(txtPetBreed.getText(), "Breed"); String speciesValue = cbPetSpecies.getValue() != null ? cbPetSpecies.getValue().trim() : ""; if (speciesValue.isEmpty()) errorMsg += "Species is required\n"; + String breedValue = cbPetBreed.getValue() != null ? cbPetBreed.getValue().trim() : ""; + if (breedValue.isEmpty()) errorMsg += "Breed is required\n"; if (cbPetStatus.getSelectionModel().getSelectedItem() == null) errorMsg += "Status is required\n"; errorMsg += Validator.isPresent(txtPetPrice.getText(), "Price"); @@ -171,7 +210,7 @@ public class PetDialogController { errorMsg += Validator.isLessThanVarChars(txtPetName.getText(), "Pet Name", 50); errorMsg += Validator.isLessThanVarChars(speciesValue, "Species", 50); - errorMsg += Validator.isLessThanVarChars(txtPetBreed.getText(), "Breed", 50); + errorMsg += Validator.isLessThanVarChars(breedValue, "Breed", 50); errorMsg += Validator.isLessThanVarChars(txtPetPrice.getText(), "Price", 12); errorMsg += Validator.isLessThanVarChars(txtPetAge.getText(), "Age", 11); errorMsg += Validator.isNonNegativeDouble(txtPetPrice.getText(), "Price"); @@ -224,7 +263,7 @@ public class PetDialogController { PetRequest request = new PetRequest(); request.setPetName(txtPetName.getText()); request.setPetSpecies(cbPetSpecies.getValue() != null ? cbPetSpecies.getValue().trim() : ""); - request.setPetBreed(txtPetBreed.getText()); + request.setPetBreed(cbPetBreed.getValue() != null ? cbPetBreed.getValue().trim() : ""); request.setPetStatus(cbPetStatus.getValue()); if (txtPetPrice.getText() != null && !txtPetPrice.getText().isBlank()) { @@ -251,9 +290,7 @@ public class PetDialogController { new Thread(() -> { try { List options = DropdownApi.getInstance().getPetSpecies(); - List species = options.stream() - .map(DropdownOption::getLabel) - .collect(java.util.stream.Collectors.toList()); + List species = options.stream().map(DropdownOption::getLabel).collect(Collectors.toList()); Platform.runLater(() -> { String current = cbPetSpecies.getValue(); cbPetSpecies.setItems(FXCollections.observableArrayList(species)); @@ -339,8 +376,6 @@ public class PetDialogController { lblPetId.setText("ID: " + pet.getPetId()); txtPetName.setText(pet.getPetName()); - cbPetSpecies.setValue(pet.getPetSpecies()); - txtPetBreed.setText(pet.getPetBreed()); txtPetAge.setText(String.valueOf(pet.getPetAge())); txtPetPrice.setText(String.valueOf(pet.getPetPrice())); currentImageUrl = pet.getImageUrl(); @@ -355,6 +390,9 @@ public class PetDialogController { isOriginallyOwnedOrAdopted = STATUS_OWNED.equalsIgnoreCase(pet.getPetStatus()) || STATUS_ADOPTED.equalsIgnoreCase(pet.getPetStatus()); + pendingBreedValue = pet.getPetBreed(); + cbPetSpecies.setValue(pet.getPetSpecies()); + for (String status : cbPetStatus.getItems()) { if (status.equals(pet.getPetStatus())) { cbPetStatus.getSelectionModel().select(status); @@ -368,7 +406,7 @@ public class PetDialogController { private void applyEditModeLock() { cbPetSpecies.setDisable(true); - txtPetBreed.setDisable(true); + cbPetBreed.setDisable(true); boolean isStaff = !UserSession.getInstance().isAdmin(); if (isStaff && isOriginallyOwnedOrAdopted) { @@ -388,7 +426,10 @@ public class PetDialogController { selectedImageFile = null; removeImageRequested = false; cbPetSpecies.setDisable(false); - txtPetBreed.setDisable(false); + cbPetBreed.setDisable(true); + cbPetBreed.setItems(FXCollections.observableArrayList()); + cbPetBreed.setValue(null); + cbPetBreed.setPromptText("Select Species first"); cbPetStatus.setDisable(false); cbPetStatus.getSelectionModel().select(STATUS_AVAILABLE); applyStatusRules(STATUS_AVAILABLE, 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 03b7a241..b50d1789 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 @@ -82,6 +82,10 @@ public class StaffRegisterDialogController { lblError.setText("Password is required."); return; } + if (password.length() < 6) { + lblError.setText("Password must be at least 6 characters."); + return; + } if (!password.equals(confirm)) { lblError.setText("Passwords do not match."); return; diff --git a/desktop/src/main/resources/org/example/petshopdesktop/dialogviews/pet-dialog-view.fxml b/desktop/src/main/resources/org/example/petshopdesktop/dialogviews/pet-dialog-view.fxml index 6f8bf1e0..cb92bec5 100644 --- a/desktop/src/main/resources/org/example/petshopdesktop/dialogviews/pet-dialog-view.fxml +++ b/desktop/src/main/resources/org/example/petshopdesktop/dialogviews/pet-dialog-view.fxml @@ -91,7 +91,7 @@ - + @@ -105,11 +105,11 @@ - + - + - +