diff --git a/android/app/src/main/java/com/example/petstoremobile/adapters/AdoptionAdapter.java b/android/app/src/main/java/com/example/petstoremobile/adapters/AdoptionAdapter.java index c6bd678c..6dd8eeb4 100644 --- a/android/app/src/main/java/com/example/petstoremobile/adapters/AdoptionAdapter.java +++ b/android/app/src/main/java/com/example/petstoremobile/adapters/AdoptionAdapter.java @@ -57,7 +57,7 @@ public class AdoptionAdapter extends RecyclerView.Adapter petList = new ArrayList<>(); private List customerList = new ArrayList<>(); + private List storeList = new ArrayList<>(); - private final String[] STATUSES = {"Pending", "Approved", "Rejected"}; + private final String[] STATUSES = {"Pending", "Completed", "Cancelled"}; private AdoptionViewModel adoptionViewModel; private PetViewModel petViewModel; private CustomerViewModel customerViewModel; + private StoreViewModel storeViewModel; @Override public void onCreate(Bundle savedInstanceState) { @@ -51,6 +55,7 @@ public class AdoptionDetailFragment extends Fragment { adoptionViewModel = new ViewModelProvider(this).get(AdoptionViewModel.class); petViewModel = new ViewModelProvider(this).get(PetViewModel.class); customerViewModel = new ViewModelProvider(this).get(CustomerViewModel.class); + storeViewModel = new ViewModelProvider(this).get(StoreViewModel.class); } @Override @@ -107,6 +112,7 @@ public class AdoptionDetailFragment extends Fragment { private void loadSpinnersData() { loadPets(); loadCustomers(); + loadStores(); } /** @@ -152,6 +158,27 @@ public class AdoptionDetailFragment extends Fragment { preselectedCustomerId, CustomerDTO::getCustomerId); } + /** + * Loads the list of stores from the API. + */ + private void loadStores() { + storeViewModel.getAllStores(0, 200).observe(getViewLifecycleOwner(), resource -> { + if (resource.status == Resource.Status.SUCCESS && resource.data != null) { + storeList = resource.data.getContent(); + refreshStoreSpinner(); + } + }); + } + + /** + * Populates the store selection spinner with data. + */ + private void refreshStoreSpinner() { + SpinnerUtils.populateSpinner(requireContext(), binding.spinnerAdoptionStore, storeList, + StoreDTO::getStoreName, "-- Select Store --", + preselectedStoreId, StoreDTO::getStoreId); + } + /** * Handles arguments to determine if the fragment is in edit or add mode. */ @@ -182,11 +209,13 @@ public class AdoptionDetailFragment extends Fragment { AdoptionDTO a = resource.data; preselectedPetId = a.getPetId() != null ? a.getPetId() : -1; preselectedCustomerId = a.getCustomerId() != null ? a.getCustomerId() : -1; + preselectedStoreId = a.getSourceStoreId() != null ? a.getSourceStoreId() : -1; binding.etAdoptionDate.setText(a.getAdoptionDate()); SpinnerUtils.setSelectionByValue(binding.spinnerAdoptionStatus, a.getAdoptionStatus()); refreshPetSpinner(); refreshCustomerSpinner(); + refreshStoreSpinner(); } else if (resource.status == Resource.Status.ERROR) { Toast.makeText(getContext(), "Failed to load adoption: " + resource.message, Toast.LENGTH_SHORT).show(); } @@ -203,6 +232,9 @@ public class AdoptionDetailFragment extends Fragment { if (binding.spinnerAdoptionPet.getSelectedItemPosition() == 0) { Toast.makeText(getContext(), "Select a pet", Toast.LENGTH_SHORT).show(); return; } + if (binding.spinnerAdoptionStore.getSelectedItemPosition() == 0) { + Toast.makeText(getContext(), "Select a store", Toast.LENGTH_SHORT).show(); return; + } String date = binding.etAdoptionDate.getText().toString().trim(); if (date.isEmpty()) { Toast.makeText(getContext(), "Select a date", Toast.LENGTH_SHORT).show(); return; @@ -210,11 +242,13 @@ public class AdoptionDetailFragment extends Fragment { CustomerDTO customer = customerList.get(binding.spinnerAdoptionCustomer.getSelectedItemPosition() - 1); PetDTO pet = petList.get(binding.spinnerAdoptionPet.getSelectedItemPosition() - 1); + StoreDTO store = storeList.get(binding.spinnerAdoptionStore.getSelectedItemPosition() - 1); String status = STATUSES[binding.spinnerAdoptionStatus.getSelectedItemPosition()]; AdoptionDTO dto = new AdoptionDTO( pet.getPetId(), customer.getCustomerId(), + store.getStoreId(), date, status ); diff --git a/android/app/src/main/res/layout/fragment_adoption_detail.xml b/android/app/src/main/res/layout/fragment_adoption_detail.xml index 2f6b153f..0ad14fcf 100644 --- a/android/app/src/main/res/layout/fragment_adoption_detail.xml +++ b/android/app/src/main/res/layout/fragment_adoption_detail.xml @@ -95,6 +95,20 @@ android:layout_height="wrap_content" android:layout_marginBottom="16dp"/> + + + + - \ No newline at end of file + diff --git a/backend/src/main/java/com/petshop/backend/controller/AdoptionController.java b/backend/src/main/java/com/petshop/backend/controller/AdoptionController.java index bcb61db4..0e833dd5 100644 --- a/backend/src/main/java/com/petshop/backend/controller/AdoptionController.java +++ b/backend/src/main/java/com/petshop/backend/controller/AdoptionController.java @@ -33,6 +33,8 @@ public class AdoptionController { @PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')") public ResponseEntity> getAllAdoptions( @RequestParam(required = false) String q, + @RequestParam(required = false) Long customerId, + @RequestParam(required = false) String status, Pageable pageable) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); String role = authentication.getAuthorities().stream() @@ -40,13 +42,13 @@ public class AdoptionController { .map(authority -> authority.getAuthority().replace("ROLE_", "")) .orElse(null); - Long customerId = null; + Long effectiveCustomerId = customerId; if (role != null && role.equals("CUSTOMER")) { User user = AuthenticationHelper.getAuthenticatedUser(userRepository); - customerId = user.getId(); + effectiveCustomerId = user.getId(); } - return ResponseEntity.ok(adoptionService.getAllAdoptions(q, pageable, customerId)); + return ResponseEntity.ok(adoptionService.getAllAdoptions(q, effectiveCustomerId, status, pageable)); } @GetMapping("/{id}") diff --git a/backend/src/main/java/com/petshop/backend/repository/AdoptionRepository.java b/backend/src/main/java/com/petshop/backend/repository/AdoptionRepository.java index 7502ec33..e3c21776 100644 --- a/backend/src/main/java/com/petshop/backend/repository/AdoptionRepository.java +++ b/backend/src/main/java/com/petshop/backend/repository/AdoptionRepository.java @@ -14,22 +14,24 @@ import java.util.Optional; public interface AdoptionRepository extends JpaRepository { @Query("SELECT a FROM Adoption a WHERE " + + "(:q IS NULL OR (" + "LOWER(a.customer.firstName) LIKE LOWER(CONCAT('%', :q, '%')) OR " + "LOWER(a.customer.lastName) LIKE LOWER(CONCAT('%', :q, '%')) OR " + - "LOWER(a.pet.petName) LIKE LOWER(CONCAT('%', :q, '%'))") - Page searchAdoptions(@Param("q") String query, Pageable pageable); - - Page findByCustomerId(Long customerId, Pageable pageable); - - @Query("SELECT a FROM Adoption a WHERE a.customer.id = :customerId AND (" + - "LOWER(a.customer.firstName) LIKE LOWER(CONCAT('%', :q, '%')) OR " + - "LOWER(a.customer.lastName) LIKE LOWER(CONCAT('%', :q, '%')) OR " + - "LOWER(a.pet.petName) LIKE LOWER(CONCAT('%', :q, '%')))") - Page searchAdoptionsByCustomer(@Param("customerId") Long customerId, @Param("q") String query, Pageable pageable); + "LOWER(a.pet.petName) LIKE LOWER(CONCAT('%', :q, '%'))" + + ")) AND " + + "(:customerId IS NULL OR a.customer.id = :customerId) AND " + + "(:status IS NULL OR LOWER(a.adoptionStatus) = LOWER(:status))") + Page searchAdoptions( + @Param("q") String query, + @Param("customerId") Long customerId, + @Param("status") String status, + Pageable pageable); Optional findFirstByPet_IdAndAdoptionStatusOrderByAdoptionDateDesc(Long petId, String adoptionStatus); - boolean existsByPetPetIdAndAdoptionStatusIgnoreCaseAndAdoptionIdNot(Long petId, String adoptionStatus, Long adoptionId); + @Query("SELECT CASE WHEN COUNT(a) > 0 THEN true ELSE false END FROM Adoption a WHERE a.pet.id = :petId AND LOWER(a.adoptionStatus) = LOWER(:adoptionStatus) AND a.adoptionId <> :adoptionId") + boolean existsByPetPetIdAndAdoptionStatusIgnoreCaseAndAdoptionIdNot(@Param("petId") Long petId, @Param("adoptionStatus") String adoptionStatus, @Param("adoptionId") Long adoptionId); - boolean existsByPetPetIdAndAdoptionStatusIgnoreCase(Long petId, String adoptionStatus); + @Query("SELECT CASE WHEN COUNT(a) > 0 THEN true ELSE false END FROM Adoption a WHERE a.pet.id = :petId AND LOWER(a.adoptionStatus) = LOWER(:adoptionStatus)") + boolean existsByPetPetIdAndAdoptionStatusIgnoreCase(@Param("petId") Long petId, @Param("adoptionStatus") String adoptionStatus); } diff --git a/backend/src/main/java/com/petshop/backend/service/AdoptionService.java b/backend/src/main/java/com/petshop/backend/service/AdoptionService.java index 10b3fd02..ec9ef393 100644 --- a/backend/src/main/java/com/petshop/backend/service/AdoptionService.java +++ b/backend/src/main/java/com/petshop/backend/service/AdoptionService.java @@ -38,22 +38,16 @@ public class AdoptionService { this.storeRepository = storeRepository; } - public Page getAllAdoptions(String query, Pageable pageable, Long customerId) { - Page adoptions; + public Page getAllAdoptions(String query, Long customerId, String status, Pageable pageable) { + String normalizedQuery = normalizeFilter(query); + String normalizedStatus = normalizeFilter(status); - if (customerId != null) { - if (query != null && !query.trim().isEmpty()) { - adoptions = adoptionRepository.searchAdoptionsByCustomer(customerId, query, pageable); - } else { - adoptions = adoptionRepository.findByCustomerId(customerId, pageable); - } - } else { - if (query != null && !query.trim().isEmpty()) { - adoptions = adoptionRepository.searchAdoptions(query, pageable); - } else { - adoptions = adoptionRepository.findAll(pageable); - } - } + Page adoptions = adoptionRepository.searchAdoptions( + normalizedQuery, + customerId, + normalizedStatus, + pageable + ); return adoptions.map(this::mapToResponse); } @@ -140,6 +134,14 @@ public class AdoptionService { adoptionRepository.deleteAllById(request.getIds()); } + private String normalizeFilter(String value) { + if (value == null) { + return null; + } + String trimmed = value.trim(); + return trimmed.isEmpty() ? null : trimmed; + } + private AdoptionResponse mapToResponse(Adoption adoption) { StoreLocation sourceStore = adoption.getSourceStore(); return new AdoptionResponse(