diff --git a/desktop/src/main/java/org/example/petshopdesktop/api/dto/pet/PetRequest.java b/desktop/src/main/java/org/example/petshopdesktop/api/dto/pet/PetRequest.java index be71c214..4bb56185 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/api/dto/pet/PetRequest.java +++ b/desktop/src/main/java/org/example/petshopdesktop/api/dto/pet/PetRequest.java @@ -9,6 +9,8 @@ public class PetRequest { private Integer petAge; private String petStatus; private BigDecimal petPrice; + private Long customerId; + private Long storeId; public PetRequest() { } @@ -60,4 +62,20 @@ public class PetRequest { public void setPetPrice(BigDecimal petPrice) { this.petPrice = petPrice; } + + public Long getCustomerId() { + return customerId; + } + + public void setCustomerId(Long customerId) { + this.customerId = customerId; + } + + public Long getStoreId() { + return storeId; + } + + public void setStoreId(Long storeId) { + this.storeId = storeId; + } } diff --git a/desktop/src/main/java/org/example/petshopdesktop/api/dto/pet/PetResponse.java b/desktop/src/main/java/org/example/petshopdesktop/api/dto/pet/PetResponse.java index b1155214..f0961a18 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/api/dto/pet/PetResponse.java +++ b/desktop/src/main/java/org/example/petshopdesktop/api/dto/pet/PetResponse.java @@ -14,6 +14,10 @@ public class PetResponse { private String imageUrl; private LocalDateTime createdAt; private LocalDateTime updatedAt; + private Long customerId; + private String customerName; + private Long storeId; + private String storeName; public PetResponse() { } @@ -97,4 +101,36 @@ public class PetResponse { public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; } + + public Long getCustomerId() { + return customerId; + } + + public void setCustomerId(Long customerId) { + this.customerId = customerId; + } + + public String getCustomerName() { + return customerName; + } + + public void setCustomerName(String customerName) { + this.customerName = customerName; + } + + public Long getStoreId() { + return storeId; + } + + public void setStoreId(Long storeId) { + this.storeId = storeId; + } + + public String getStoreName() { + return storeName; + } + + public void setStoreName(String storeName) { + this.storeName = storeName; + } } diff --git a/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/PetApi.java b/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/PetApi.java index 92fa28c9..5f69e025 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/PetApi.java +++ b/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/PetApi.java @@ -24,7 +24,7 @@ public class PetApi { return INSTANCE; } - public List listPets(String query, String species, String status) throws Exception { + public List listPets(String query, String species, String status, Long storeId) throws Exception { String path = "/api/v1/pets?page=0&size=1000"; if (query != null && !query.isEmpty()) { path += "&q=" + URLEncoder.encode(query, StandardCharsets.UTF_8); @@ -35,6 +35,9 @@ public class PetApi { if (status != null && !status.isEmpty()) { path += "&status=" + URLEncoder.encode(status, StandardCharsets.UTF_8); } + if (storeId != null) { + path += "&storeId=" + storeId; + } String response = apiClient.getRawResponse(path); PageResponse pageResponse = apiClient.getObjectMapper().readValue( response, @@ -46,8 +49,12 @@ public class PetApi { return pageResponse.getContent(); } + public List listPets(String query, String species, String status) throws Exception { + return listPets(query, species, status, null); + } + public List listPets(String query) throws Exception { - return listPets(query, null, null); + return listPets(query, null, null, null); } public PetResponse createPet(PetRequest request) throws Exception { diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/PetController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/PetController.java index bd76c9ce..005eab1b 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/PetController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/PetController.java @@ -63,6 +63,12 @@ public class PetController { @FXML private TableColumn colPetStatus; + @FXML + private TableColumn colCustomerName; + + @FXML + private TableColumn colStoreName; + @FXML private TableView tvPets; @@ -156,11 +162,13 @@ public class PetController { colPetAge.setCellValueFactory(new PropertyValueFactory("petAge")); colPetStatus.setCellValueFactory(new PropertyValueFactory("petStatus")); colPetPrice.setCellValueFactory(new PropertyValueFactory("petPrice")); + colCustomerName.setCellValueFactory(new PropertyValueFactory("customerName")); + colStoreName.setCellValueFactory(new PropertyValueFactory("storeName")); configureImageColumn(colPetImage); loadSpeciesFilter(); - cbStatusFilter.setItems(FXCollections.observableArrayList("All Statuses", "Available", "Adopted", "Pending")); + cbStatusFilter.setItems(FXCollections.observableArrayList("All Statuses", "Available", "Adopted", "Owned", "Pending")); cbStatusFilter.getSelectionModel().selectFirst(); displayPets(); @@ -316,7 +324,7 @@ public class PetController { } private Pet mapToPet(PetResponse response) { - return new Pet( + Pet pet = new Pet( response.getPetId().intValue(), response.getPetName(), response.getPetSpecies(), @@ -326,6 +334,11 @@ public class PetController { response.getPetPrice().doubleValue(), response.getImageUrl() ); + pet.setCustomerName(response.getCustomerName()); + pet.setStoreName(response.getStoreName()); + pet.setCustomerId(response.getCustomerId() != null ? response.getCustomerId() : 0L); + pet.setStoreId(response.getStoreId() != null ? response.getStoreId() : 0L); + return pet; } private void configureImageColumn(TableColumn column) { 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 78ad836a..afd2f549 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 @@ -1,5 +1,6 @@ package org.example.petshopdesktop.controllers.dialogcontrollers; +import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.EventHandler; @@ -10,8 +11,10 @@ import javafx.scene.image.ImageView; import javafx.scene.input.MouseEvent; import javafx.stage.Stage; import org.example.petshopdesktop.Validator; +import org.example.petshopdesktop.api.dto.common.DropdownOption; import org.example.petshopdesktop.api.dto.pet.PetRequest; import org.example.petshopdesktop.api.dto.pet.PetResponse; +import org.example.petshopdesktop.api.endpoints.DropdownApi; import org.example.petshopdesktop.api.endpoints.PetApi; import org.example.petshopdesktop.models.Pet; import org.example.petshopdesktop.util.ActivityLogger; @@ -20,6 +23,7 @@ import org.example.petshopdesktop.util.FilePickerSupport; import java.io.File; import java.math.BigDecimal; +import java.util.List; public class PetDialogController { @@ -38,6 +42,12 @@ public class PetDialogController { @FXML private ComboBox cbPetStatus; + @FXML + private ComboBox cbCustomer; + + @FXML + private ComboBox cbStore; + @FXML private Label lblMode; @@ -70,16 +80,54 @@ public class PetDialogController { private String currentImageUrl; private boolean removeImageRequested; + private Long pendingCustomerId = null; + private Long pendingStoreId = null; + private ObservableList statusList = FXCollections.observableArrayList( - "Available", "Adopted" + "Available", "Adopted", "Owned" ); @FXML void initialize() { - cbPetStatus.setItems(statusList); //set status combobox + cbPetStatus.setItems(statusList); + + cbCustomer.setCellFactory(param -> new ListCell<>() { + @Override protected void updateItem(DropdownOption o, boolean empty) { + super.updateItem(o, empty); + setText(empty || o == null ? null : o.getLabel()); + } + }); + cbCustomer.setButtonCell(new ListCell<>() { + @Override protected void updateItem(DropdownOption o, boolean empty) { + super.updateItem(o, empty); + setText(empty || o == null ? null : o.getLabel()); + } + }); + + cbStore.setCellFactory(param -> new ListCell<>() { + @Override protected void updateItem(DropdownOption o, boolean empty) { + super.updateItem(o, empty); + setText(empty || o == null ? null : o.getLabel()); + } + }); + cbStore.setButtonCell(new ListCell<>() { + @Override protected void updateItem(DropdownOption o, boolean empty) { + super.updateItem(o, empty); + setText(empty || o == null ? null : o.getLabel()); + } + }); + + cbCustomer.setVisible(false); + cbStore.setVisible(false); + + cbPetStatus.valueProperty().addListener((obs, oldVal, newVal) -> { + boolean isOwned = "Owned".equalsIgnoreCase(newVal); + boolean isAvailable = "Available".equalsIgnoreCase(newVal) || "Unadopted".equalsIgnoreCase(newVal); + cbCustomer.setVisible(isOwned); + cbStore.setVisible(isAvailable); + }); - //Set up mouse handlers for buttons btnSave.setOnMouseClicked(new EventHandler() { @Override public void handle(MouseEvent mouseEvent) { @@ -97,6 +145,9 @@ public class PetDialogController { btnChangeImage.setOnMouseClicked(mouseEvent -> handleChangeImage()); btnRemoveImage.setOnMouseClicked(mouseEvent -> handleRemoveImage()); refreshImagePreview(); + + loadCustomers(); + loadStores(); } private void buttonSaveClicked(MouseEvent mouseEvent) { @@ -111,6 +162,10 @@ public class PetDialogController { if (cbPetStatus.getSelectionModel().getSelectedItem() == null){ errorMsg += "Status is required"; } + String selectedStatus = cbPetStatus.getValue(); + if ("Owned".equalsIgnoreCase(selectedStatus) && cbCustomer.getValue() == null) { + errorMsg += "Customer is required for Owned status\n"; + } //Check validation (length size) errorMsg += Validator.isLessThanVarChars(txtPetName.getText(), "Pet Name", 50); @@ -184,9 +239,81 @@ public class PetDialogController { } request.setPetAge(age); + String status = cbPetStatus.getValue(); + if ("Owned".equalsIgnoreCase(status) && cbCustomer.getValue() != null) { + request.setCustomerId(cbCustomer.getValue().getId()); + } + if (("Available".equalsIgnoreCase(status) || "Unadopted".equalsIgnoreCase(status)) && cbStore.getValue() != null) { + request.setStoreId(cbStore.getValue().getId()); + } + return request; } + private void loadCustomers() { + new Thread(() -> { + try { + List customers = DropdownApi.getInstance().getCustomers(); + Platform.runLater(() -> { + cbCustomer.setItems(FXCollections.observableArrayList(customers)); + applySelectedCustomer(); + }); + } catch (Exception e) { + Platform.runLater(() -> { + ActivityLogger.getInstance().logException( + "PetDialogController.loadCustomers", e, "Loading customers"); + cbCustomer.setDisable(true); + cbCustomer.setPromptText("Unable to load customers"); + }); + } + }).start(); + } + + private void loadStores() { + new Thread(() -> { + try { + List stores = DropdownApi.getInstance().getStores(); + Platform.runLater(() -> { + cbStore.setItems(FXCollections.observableArrayList(stores)); + applySelectedStore(); + }); + } catch (Exception e) { + Platform.runLater(() -> { + ActivityLogger.getInstance().logException( + "PetDialogController.loadStores", e, "Loading stores"); + cbStore.setDisable(true); + cbStore.setPromptText("Unable to load stores"); + }); + } + }).start(); + } + + private void applySelectedCustomer() { + if (pendingCustomerId == null) return; + DropdownOption selected = findOptionById(cbCustomer.getItems(), pendingCustomerId); + if (selected != null) { + cbCustomer.setValue(selected); + pendingCustomerId = null; + } + } + + private void applySelectedStore() { + if (pendingStoreId == null) return; + DropdownOption selected = findOptionById(cbStore.getItems(), pendingStoreId); + if (selected != null) { + cbStore.setValue(selected); + pendingStoreId = null; + } + } + + private DropdownOption findOptionById(List options, Long id) { + if (id == null || options == null) return null; + for (DropdownOption option : options) { + if (option.getId() != null && option.getId().equals(id)) return option; + } + return null; + } + private void closeStage(MouseEvent mouseEvent) { Node node = (Node) mouseEvent.getSource(); Stage stage = (Stage) node.getScene().getWindow(); @@ -206,14 +333,14 @@ public class PetDialogController { removeImageRequested = false; refreshImagePreview(); - //get the right combobox selection + pendingCustomerId = pet.getCustomerId() > 0 ? pet.getCustomerId() : null; + pendingStoreId = pet.getStoreId() > 0 ? pet.getStoreId() : null; + for (String status : cbPetStatus.getItems()) { if(status.equals(pet.getPetStatus())){ cbPetStatus.getSelectionModel().select(status); } } - - } } diff --git a/desktop/src/main/java/org/example/petshopdesktop/models/Pet.java b/desktop/src/main/java/org/example/petshopdesktop/models/Pet.java index fc1723c5..bc7e1a5c 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/models/Pet.java +++ b/desktop/src/main/java/org/example/petshopdesktop/models/Pet.java @@ -13,6 +13,10 @@ public class Pet { private SimpleStringProperty petStatus; private SimpleDoubleProperty petPrice; private SimpleStringProperty imageUrl; + private SimpleStringProperty customerName = new SimpleStringProperty(""); + private SimpleStringProperty storeName = new SimpleStringProperty(""); + private long customerId = 0L; + private long storeId = 0L; public Pet(int petId, String petName, String petSpecies, String petBreed, int petAge, String petStatus, double petPrice, String imageUrl) { this.petId = new SimpleIntegerProperty(petId); @@ -120,4 +124,44 @@ public class Pet { public SimpleStringProperty imageUrlProperty() { return imageUrl; } + + public String getCustomerName() { + return customerName.get(); + } + + public void setCustomerName(String customerName) { + this.customerName.set(customerName != null ? customerName : ""); + } + + public SimpleStringProperty customerNameProperty() { + return customerName; + } + + public String getStoreName() { + return storeName.get(); + } + + public void setStoreName(String storeName) { + this.storeName.set(storeName != null ? storeName : ""); + } + + public SimpleStringProperty storeNameProperty() { + return storeName; + } + + public long getCustomerId() { + return customerId; + } + + public void setCustomerId(long customerId) { + this.customerId = customerId; + } + + public long getStoreId() { + return storeId; + } + + public void setStoreId(long storeId) { + this.storeId = storeId; + } } 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 2f5bd110..f25e113f 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 @@ -13,7 +13,7 @@ - + @@ -153,6 +153,34 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/desktop/src/main/resources/org/example/petshopdesktop/modelviews/pet-view.fxml b/desktop/src/main/resources/org/example/petshopdesktop/modelviews/pet-view.fxml index c8e77504..d599e010 100644 --- a/desktop/src/main/resources/org/example/petshopdesktop/modelviews/pet-view.fxml +++ b/desktop/src/main/resources/org/example/petshopdesktop/modelviews/pet-view.fxml @@ -79,6 +79,8 @@ + +