From 26f9f8c0d89cd768ca00937afa8d67242428e9ae Mon Sep 17 00:00:00 2001 From: Harkamal Randhawa Date: Tue, 7 Apr 2026 22:41:32 -0600 Subject: [PATCH 1/9] fix image error responses --- .../petshop/backend/exception/GlobalExceptionHandler.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/com/petshop/backend/exception/GlobalExceptionHandler.java b/backend/src/main/java/com/petshop/backend/exception/GlobalExceptionHandler.java index 81163287..1f9434a5 100644 --- a/backend/src/main/java/com/petshop/backend/exception/GlobalExceptionHandler.java +++ b/backend/src/main/java/com/petshop/backend/exception/GlobalExceptionHandler.java @@ -5,6 +5,7 @@ import jakarta.servlet.http.HttpServletRequest; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.data.core.PropertyReferenceException; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.validation.FieldError; import org.springframework.web.HttpRequestMethodNotSupportedException; @@ -118,7 +119,9 @@ public class GlobalExceptionHandler { request.getRequestURI(), LocalDateTime.now() ); - return ResponseEntity.status(status).body(error); + return ResponseEntity.status(status) + .contentType(MediaType.APPLICATION_JSON) + .body(error); } private String buildDetails(Exception ex) { From ffef9243ddc06ac82f09ad248529779956d9c693 Mon Sep 17 00:00:00 2001 From: augmentedpotato Date: Tue, 7 Apr 2026 22:44:50 -0600 Subject: [PATCH 2/9] Fixed(?) being unable to create appointments on today's date --- web/app/appointments/page.js | 1 - 1 file changed, 1 deletion(-) diff --git a/web/app/appointments/page.js b/web/app/appointments/page.js index f8e4ec2d..cd3746a6 100644 --- a/web/app/appointments/page.js +++ b/web/app/appointments/page.js @@ -392,7 +392,6 @@ function AppointmentsPage() { function getMinDate() { const d = new Date(); - d.setDate(d.getDate() + 1); return d.toISOString().split("T")[0]; } From 83477904be16423329385bf342d6867032acbc34 Mon Sep 17 00:00:00 2001 From: augmentedpotato Date: Tue, 7 Apr 2026 23:23:05 -0600 Subject: [PATCH 3/9] Feature parity with admins and users (also a minor backend change) --- .../controller/AppointmentController.java | 4 ++-- web/app/appointments/page.js | 18 ++++-------------- web/app/profile/page.js | 4 ++-- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/backend/src/main/java/com/petshop/backend/controller/AppointmentController.java b/backend/src/main/java/com/petshop/backend/controller/AppointmentController.java index 5e64afd9..4c9d402b 100644 --- a/backend/src/main/java/com/petshop/backend/controller/AppointmentController.java +++ b/backend/src/main/java/com/petshop/backend/controller/AppointmentController.java @@ -50,7 +50,7 @@ public class AppointmentController { .orElse(null); Long effectiveCustomerId = customerId; - if (role != null && role.equals("CUSTOMER")) { + if (role != null && (role.equals("CUSTOMER") || role.equals("ADMIN"))) { User user = AuthenticationHelper.getAuthenticatedUser(userRepository); effectiveCustomerId = user.getId(); } @@ -88,7 +88,7 @@ public class AppointmentController { .map(authority -> authority.getAuthority().replace("ROLE_", "")) .orElse(null); - if (role != null && role.equals("CUSTOMER")) { + if (role != null && (role.equals("CUSTOMER") || role.equals("ADMIN"))) { User user = AuthenticationHelper.getAuthenticatedUser(userRepository); if (!request.getCustomerId().equals(user.getId())) { throw new org.springframework.security.access.AccessDeniedException("You can only create appointments for yourself"); diff --git a/web/app/appointments/page.js b/web/app/appointments/page.js index cd3746a6..ce90e858 100644 --- a/web/app/appointments/page.js +++ b/web/app/appointments/page.js @@ -224,7 +224,7 @@ function AppointmentsPage() { const [appointments, setAppointments] = useState([]); const [loadingAppointments, setLoadingAppointments] = useState(false); - const canBookAppointments = user?.role === "CUSTOMER"; +const canBookAppointments = user?.role === "CUSTOMER" || user?.role === "ADMIN"; useEffect(() => { if (!authLoading && !user) { @@ -410,12 +410,6 @@ function AppointmentsPage() { return; } - if (!user?.customerId) { - setError("Customer account not found. Please contact support."); - - return; - } - if (selectedPetIds.length === 0) { setError(isAdoptionService ? "Please select a pet to adopt." : "Please select at least one pet."); @@ -426,7 +420,7 @@ function AppointmentsPage() { try { const body = { - customerId: user.customerId, + customerId: user.customerId || user.id, storeId: Number(storeId), serviceId: Number(serviceId), employeeId: employeeId ? Number(employeeId) : undefined, @@ -435,12 +429,8 @@ function AppointmentsPage() { appointmentStatus: "Booked", }; - if (isCustomerPetService) { - body.customerPetIds = selectedPetIds; - } - - else { - body.petIds = selectedPetIds; + if (selectedPetIds.length > 0) { + body.petId = selectedPetIds[0]; } const res = await fetch(`${API_BASE}/api/v1/appointments`, { diff --git a/web/app/profile/page.js b/web/app/profile/page.js index ff79521e..cf2f0d2d 100644 --- a/web/app/profile/page.js +++ b/web/app/profile/page.js @@ -108,7 +108,7 @@ export default function ProfilePage() { }, [clearPetImageObjectUrls]); useEffect(() => { - if (user?.role === "CUSTOMER") { +if (user?.role === "CUSTOMER" || user?.role === "ADMIN") { loadPets(); } }, [user, loadPets]); @@ -419,7 +419,7 @@ export default function ProfilePage() { - {user.role === "CUSTOMER" && ( +{(user.role === "CUSTOMER" || user.role === "ADMIN") && (

My Pets

From 2469c07fef415681827b2adcb69c267e4dd51d8d Mon Sep 17 00:00:00 2001 From: augmentedpotato Date: Tue, 7 Apr 2026 23:53:48 -0600 Subject: [PATCH 4/9] Can now add pets in the appointments page. --- web/app/appointments/page.js | 136 ++++++++++++++++++++++++++++++++--- web/app/globals.css | 38 ++++++++++ 2 files changed, 164 insertions(+), 10 deletions(-) diff --git a/web/app/appointments/page.js b/web/app/appointments/page.js index ce90e858..b4dd5cee 100644 --- a/web/app/appointments/page.js +++ b/web/app/appointments/page.js @@ -195,6 +195,100 @@ function DatePicker({ value, minDate, onChange }) { ); } +function AddPetModal({ token, onClose, onAdded }) { + const [petName, setPetName] = useState(""); + const [species, setSpecies] = useState(""); + const [breed, setBreed] = useState(""); + const [submitting, setSubmitting] = useState(false); + const [petError, setPetError] = useState(null); + + async function handleSubmit(e) { + e.preventDefault(); + setPetError(null); + setSubmitting(true); + + try { + const res = await fetch(`${API_BASE}/api/v1/my-pets`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ petName, species, breed: breed || null }), + }); + + if (!res.ok) { + const data = await res.json().catch(() => null); + throw new Error(data?.message || `Request failed (${res.status})`); + } + + onAdded(); + onClose(); + } + + catch (err) { + setPetError(err.message); + } + + finally { + setSubmitting(false); + } + } + + return ( +
+
e.stopPropagation()}> +

Add a New Pet

+ {petError &&
{petError}
} +
+ + + +
+ + +
+
+
+
+ ); +} + function AppointmentsPage() { const { user, token, loading: authLoading } = useAuth(); const router = useRouter(); @@ -224,6 +318,8 @@ function AppointmentsPage() { const [appointments, setAppointments] = useState([]); const [loadingAppointments, setLoadingAppointments] = useState(false); + const [showAddPetModal, setShowAddPetModal] = useState(false); + const canBookAppointments = user?.role === "CUSTOMER" || user?.role === "ADMIN"; useEffect(() => { @@ -234,6 +330,16 @@ const canBookAppointments = user?.role === "CUSTOMER" || user?.role === "ADMIN"; }, [authLoading, user, router, preselectedPetId]); + const loadCustomerPets = useCallback(() => { + if (!token || !canBookAppointments) return; + fetch(`${API_BASE}/api/v1/my-pets`, { + headers: { Authorization: `Bearer ${token}` }, + }) + .then((r) => r.json()) + .then((data) => setCustomerPets(Array.isArray(data) ? data : [])) + .catch(() => {}); + }, [token, canBookAppointments]); + useEffect(() => { if (!token) { return; @@ -256,15 +362,8 @@ const canBookAppointments = user?.role === "CUSTOMER" || user?.role === "ADMIN"; .then((data) => setAllPets(data.content ?? [])) .catch(() => {}); - if (canBookAppointments) { - fetch(`${API_BASE}/api/v1/my-pets`, { - headers: { Authorization: `Bearer ${token}` }, - }) - .then((r) => r.json()) - .then((data) => setCustomerPets(Array.isArray(data) ? data : [])) - .catch(() => {}); - } - }, [token, canBookAppointments]); + loadCustomerPets(); + }, [token, loadCustomerPets]); useEffect(() => { if (didPreselectRef.current) { @@ -482,10 +581,18 @@ const canBookAppointments = user?.role === "CUSTOMER" || user?.role === "ADMIN"; const petSectionLabel = isAdoptionService ? "Select a Pet to Adopt" : "Select Pet(s)"; const noPetsMessage = isAdoptionService ? "No pets are currently available for adoption." - : "No pets found. Please add your pets in your profile before booking."; + : "No pets found on your profile."; return (
+ {showAddPetModal && ( + setShowAddPetModal(false)} + onAdded={loadCustomerPets} + /> + )} +

Schedule an Appointment

Book a service for your pet or schedule a pet adoption visit

@@ -589,6 +696,15 @@ const canBookAppointments = user?.role === "CUSTOMER" || user?.role === "ADMIN"; {serviceId && (
{petSectionLabel} + {isCustomerPetService && ( + + )} {petsToShow.length === 0 ? (

{noPetsMessage}

) : isAdoptionService ? ( diff --git a/web/app/globals.css b/web/app/globals.css index 8571bfeb..43ec07e6 100644 --- a/web/app/globals.css +++ b/web/app/globals.css @@ -1351,6 +1351,44 @@ body { accent-color: orange; } +.appt-add-pet-btn { + display: inline-block; + margin-top: 0.5rem; + padding: 0.4rem 0.85rem; + background: none; + border: 1.5px solid orange; + border-radius: 6px; + color: orange; + font-size: 0.875rem; + font-weight: 600; + cursor: pointer; + transition: background 0.15s, color 0.15s; +} + +.appt-add-pet-btn:hover { + background: orange; + color: white; +} + +.appt-modal-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.45); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +} + +.appt-modal { + background: white; + border-radius: 12px; + padding: 2rem; + width: 100%; + max-width: 420px; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18); +} + .appt-link { color: orange; font-weight: 600; From f2932d80c8d3dd13b7bd91e6a7d1b5aee773ff62 Mon Sep 17 00:00:00 2001 From: Harkamal Randhawa Date: Wed, 8 Apr 2026 07:55:55 -0600 Subject: [PATCH 5/9] fix desktop forms --- .../controllers/AdoptionController.java | 5 +- .../controllers/AppointmentController.java | 9 ++- .../controllers/InventoryController.java | 5 +- .../controllers/PetController.java | 5 +- .../controllers/ProductController.java | 5 +- .../ProductSupplierController.java | 5 +- .../controllers/SaleController.java | 2 +- .../controllers/ServiceController.java | 7 ++- .../controllers/SupplierController.java | 19 +++--- .../PetDialogController.java | 47 +++++++++++--- .../ProductSupplierDialogController.java | 62 ++++++++++++++----- .../dialogviews/pet-dialog-view.fxml | 4 +- 12 files changed, 127 insertions(+), 48 deletions(-) diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/AdoptionController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/AdoptionController.java index 4de4d212..7b5b6a5f 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/AdoptionController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/AdoptionController.java @@ -83,8 +83,9 @@ public class AdoptionController { tvAdoptions.getSelectionModel().selectedItemProperty().addListener( (observable, oldValue, newValue) -> { - btnEdit.setDisable(false); - btnDelete.setDisable(false); + boolean hasSelection = newValue != null; + btnEdit.setDisable(!hasSelection); + btnDelete.setDisable(!hasSelection); }); txtSearch.textProperty().addListener((observable, oldValue, newValue) -> { diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/AppointmentController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/AppointmentController.java index 039e4b4f..ecc44bc6 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/AppointmentController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/AppointmentController.java @@ -47,7 +47,8 @@ public class AppointmentController { @FXML public void initialize(){ - + btnEdit.setDisable(true); + btnDelete.setDisable(true); tvAppointments.getSelectionModel().setSelectionMode(javafx.scene.control.SelectionMode.MULTIPLE); colAppointmentId.setCellValueFactory(new PropertyValueFactory<>("appointmentId")); @@ -66,6 +67,12 @@ public class AppointmentController { txtSearch.textProperty().addListener((obs, o, n) -> applyFilter(n)); } + tvAppointments.getSelectionModel().selectedItemProperty().addListener((obs, oldValue, newValue) -> { + boolean hasSelection = newValue != null; + btnEdit.setDisable(!hasSelection); + btnDelete.setDisable(!hasSelection); + }); + tvAppointments.setOnKeyPressed(event -> { if (event.getCode() == javafx.scene.input.KeyCode.DELETE) { if (tvAppointments.getSelectionModel().getSelectedItem() != null) { diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/InventoryController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/InventoryController.java index 06b2afa9..1153fe04 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/InventoryController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/InventoryController.java @@ -73,8 +73,9 @@ public class InventoryController { tvInventory.getSelectionModel().selectedItemProperty().addListener( (observable, oldValue, newValue) -> { - btnEdit.setDisable(false); - btnDelete.setDisable(false); + boolean hasSelection = newValue != null; + btnEdit.setDisable(!hasSelection); + btnDelete.setDisable(!hasSelection); }); txtSearch.textProperty().addListener((observable, oldValue, newValue) -> { 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 005eab1b..e12c13c8 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/PetController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/PetController.java @@ -175,8 +175,9 @@ public class PetController { tvPets.getSelectionModel().selectedItemProperty().addListener( (observable, oldValue, newValue) -> { - btnEdit.setDisable(false); - btnDelete.setDisable(false); + boolean hasSelection = newValue != null; + btnEdit.setDisable(!hasSelection); + btnDelete.setDisable(!hasSelection); }); txtSearch.textProperty().addListener((observable, oldValue, newValue) -> { diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/ProductController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/ProductController.java index 84cfb8ce..61702f3b 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/ProductController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/ProductController.java @@ -99,8 +99,9 @@ public class ProductController { //EventListener to Enable buttons when a row is selected tvProducts.getSelectionModel().selectedItemProperty().addListener( (observable, oldValue, newValue) -> { - btnEdit.setDisable(false); - btnDelete.setDisable(false); + boolean hasSelection = newValue != null; + btnEdit.setDisable(!hasSelection); + btnDelete.setDisable(!hasSelection); } ); diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/ProductSupplierController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/ProductSupplierController.java index 63bba3ae..1e9b9a95 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/ProductSupplierController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/ProductSupplierController.java @@ -80,8 +80,9 @@ public class ProductSupplierController { //EventListener to Enable buttons when a row is selected tvProductSuppliers.getSelectionModel().selectedItemProperty().addListener( (observable, oldValue, newValue) -> { - btnEdit.setDisable(false); - btnDelete.setDisable(false); + boolean hasSelection = newValue != null; + btnEdit.setDisable(!hasSelection); + btnDelete.setDisable(!hasSelection); } ); diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/SaleController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/SaleController.java index 22c96dbd..3d6e9388 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/SaleController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/SaleController.java @@ -186,7 +186,7 @@ public class SaleController { spQuantity.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 999, 1)); spQuantity.setEditable(true); - cbPaymentMethod.setItems(FXCollections.observableArrayList("Cash", "Card")); + cbPaymentMethod.setItems(FXCollections.observableArrayList("Cash", "Card", "Debit")); cbPaymentMethod.getSelectionModel().selectFirst(); updateCartTotal(); diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/ServiceController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/ServiceController.java index d0a3049e..8500677a 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/ServiceController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/ServiceController.java @@ -57,8 +57,9 @@ public class ServiceController { tvServices.getSelectionModel().selectedItemProperty().addListener( (observable, oldValue, newValue) -> { - btnEdit.setDisable(false); - btnDelete.setDisable(false); + boolean hasSelection = newValue != null; + btnEdit.setDisable(!hasSelection); + btnDelete.setDisable(!hasSelection); } ); @@ -230,4 +231,4 @@ public class ServiceController { response.getServicePrice().doubleValue() ); } -} \ No newline at end of file +} diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/SupplierController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/SupplierController.java index 4df26941..28248657 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/SupplierController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/SupplierController.java @@ -82,8 +82,9 @@ public class SupplierController { //EventListener to Enable buttons when a row is selected tvSuppliers.getSelectionModel().selectedItemProperty().addListener( (observable, oldValue, newValue) -> { - btnEdit.setDisable(false); - btnDelete.setDisable(false); + boolean hasSelection = newValue != null; + btnEdit.setDisable(!hasSelection); + btnDelete.setDisable(!hasSelection); } ); @@ -285,19 +286,21 @@ public class SupplierController { } private Supplier mapToSupplier(SupplierResponse response) { - String contactPerson = response.getSupContactFirstName() + " " + response.getSupContactLastName() != null ? response.getSupContactFirstName() + " " + response.getSupContactLastName() : ""; + String firstName = response.getSupContactFirstName() != null ? response.getSupContactFirstName().trim() : ""; + String lastName = response.getSupContactLastName() != null ? response.getSupContactLastName().trim() : ""; + String contactPerson = (firstName + " " + lastName).trim(); String[] nameParts = contactPerson.split(" ", 2); - String firstName = nameParts.length > 0 ? nameParts[0] : ""; - String lastName = nameParts.length > 1 ? nameParts[1] : ""; + String mappedFirstName = nameParts.length > 0 ? nameParts[0] : ""; + String mappedLastName = nameParts.length > 1 ? nameParts[1] : ""; return new Supplier( response.getSupId().intValue(), response.getSupCompany(), - firstName, - lastName, + mappedFirstName, + mappedLastName, response.getSupEmail(), response.getSupPhone() ); } -} \ No newline at end of file +} 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 afd2f549..5e4835e2 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 @@ -9,6 +9,7 @@ import javafx.scene.Node; import javafx.scene.control.*; import javafx.scene.image.ImageView; import javafx.scene.input.MouseEvent; +import javafx.scene.layout.VBox; import javafx.stage.Stage; import org.example.petshopdesktop.Validator; import org.example.petshopdesktop.api.dto.common.DropdownOption; @@ -48,6 +49,12 @@ public class PetDialogController { @FXML private ComboBox cbStore; + @FXML + private VBox vbCustomerField; + + @FXML + private VBox vbStoreField; + @FXML private Label lblMode; @@ -84,7 +91,7 @@ public class PetDialogController { private Long pendingStoreId = null; private ObservableList statusList = FXCollections.observableArrayList( - "Available", "Adopted", "Owned" + "Available", "Adopted", "Owned", "Pending" ); @FXML @@ -118,14 +125,11 @@ public class PetDialogController { } }); - cbCustomer.setVisible(false); - cbStore.setVisible(false); + setFieldVisibility(vbCustomerField, false); + setFieldVisibility(vbStoreField, 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); + updateStatusFieldVisibility(newVal); }); btnSave.setOnMouseClicked(new EventHandler() { @@ -166,6 +170,9 @@ public class PetDialogController { if ("Owned".equalsIgnoreCase(selectedStatus) && cbCustomer.getValue() == null) { errorMsg += "Customer is required for Owned status\n"; } + if (requiresStore(selectedStatus) && cbStore.getValue() == null) { + errorMsg += "Store is required for " + selectedStatus + " status\n"; + } //Check validation (length size) errorMsg += Validator.isLessThanVarChars(txtPetName.getText(), "Pet Name", 50); @@ -243,7 +250,7 @@ public class PetDialogController { if ("Owned".equalsIgnoreCase(status) && cbCustomer.getValue() != null) { request.setCustomerId(cbCustomer.getValue().getId()); } - if (("Available".equalsIgnoreCase(status) || "Unadopted".equalsIgnoreCase(status)) && cbStore.getValue() != null) { + if (requiresStore(status) && cbStore.getValue() != null) { request.setStoreId(cbStore.getValue().getId()); } @@ -339,8 +346,10 @@ public class PetDialogController { for (String status : cbPetStatus.getItems()) { if(status.equals(pet.getPetStatus())){ cbPetStatus.getSelectionModel().select(status); + break; } } + updateStatusFieldVisibility(cbPetStatus.getValue()); } } @@ -358,6 +367,7 @@ public class PetDialogController { lblPetId.setVisible(true); refreshImagePreview(); } + updateStatusFieldVisibility(cbPetStatus.getValue()); } private void handleChangeImage() { @@ -421,4 +431,25 @@ public class PetDialogController { btnRemoveImage.setDisable(true); } + private void updateStatusFieldVisibility(String status) { + boolean owned = "Owned".equalsIgnoreCase(status); + boolean storeBased = requiresStore(status); + setFieldVisibility(vbCustomerField, owned); + setFieldVisibility(vbStoreField, storeBased); + } + + private boolean requiresStore(String status) { + return "Available".equalsIgnoreCase(status) + || "Pending".equalsIgnoreCase(status) + || "Unadopted".equalsIgnoreCase(status); + } + + private void setFieldVisibility(VBox field, boolean visible) { + if (field == null) { + return; + } + field.setVisible(visible); + field.setManaged(visible); + } + } diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/ProductSupplierDialogController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/ProductSupplierDialogController.java index 2348b43b..acff3799 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/ProductSupplierDialogController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/ProductSupplierDialogController.java @@ -19,6 +19,7 @@ import org.example.petshopdesktop.api.endpoints.ProductSupplierApi; import org.example.petshopdesktop.util.ActivityLogger; import java.math.BigDecimal; +import java.util.List; public class ProductSupplierDialogController { @@ -46,6 +47,8 @@ public class ProductSupplierDialogController { private String mode = null; private int selectedSupId = -1; private int selectedProdId = -1; + private Long pendingSupplierId = null; + private Long pendingProductId = null; /** * add event listeners to buttons and set up combobox @@ -120,9 +123,11 @@ public class ProductSupplierDialogController { Platform.runLater(() -> { if (suppliers != null) { cbSupplier.setItems(FXCollections.observableArrayList(suppliers)); + applyPendingSupplierSelection(); } if (products != null) { cbProduct.setItems(FXCollections.observableArrayList(products)); + applyPendingProductSelection(); } }); } catch (Exception e) { @@ -220,21 +225,14 @@ public class ProductSupplierDialogController { * @param productSupplier */ public void displayProductSupplierDetails(ProductSupplierDTO productSupplier){ - if(productSupplier != null){ - txtCost.setText(productSupplier.getCost() + ""); - } - - for (DropdownOption product : cbProduct.getItems()) { - if(product.getId() == productSupplier.getProdId()){ - cbProduct.getSelectionModel().select(product); - } - } - - for (DropdownOption supplier : cbSupplier.getItems()) { - if (supplier.getId() == productSupplier.getSupId()) { - cbSupplier.getSelectionModel().select(supplier); - } + if (productSupplier == null) { + return; } + txtCost.setText(productSupplier.getCost() + ""); + pendingProductId = (long) productSupplier.getProdId(); + pendingSupplierId = (long) productSupplier.getSupId(); + applyPendingProductSelection(); + applyPendingSupplierSelection(); } /** @@ -253,7 +251,7 @@ public class ProductSupplierDialogController { */ public void setMode(String mode) { this.mode = mode; - lblMode.setText(mode + " Product"); + lblMode.setText(mode + " Product-Supplier"); lblProductSupplierId.setVisible(false); } @@ -267,4 +265,38 @@ public class ProductSupplierDialogController { this.selectedProdId = prodId; } + private void applyPendingProductSelection() { + if (pendingProductId == null) { + return; + } + DropdownOption product = findOptionById(cbProduct.getItems(), pendingProductId); + if (product != null) { + cbProduct.getSelectionModel().select(product); + pendingProductId = null; + } + } + + private void applyPendingSupplierSelection() { + if (pendingSupplierId == null) { + return; + } + DropdownOption supplier = findOptionById(cbSupplier.getItems(), pendingSupplierId); + if (supplier != null) { + cbSupplier.getSelectionModel().select(supplier); + pendingSupplierId = null; + } + } + + private DropdownOption findOptionById(List options, Long id) { + if (options == null || id == null) { + return null; + } + for (DropdownOption option : options) { + if (option.getId() != null && option.getId().equals(id)) { + return option; + } + } + return null; + } + } 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 f25e113f..9130513a 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 @@ -153,7 +153,7 @@ - + - +