making appointment userfrendly part1 andriod
This commit is contained in:
@@ -31,6 +31,7 @@ public interface PetApi {
|
||||
@Query("status") String status,
|
||||
@Query("species") String species,
|
||||
@Query("storeId") Long storeId,
|
||||
@Query("customerId") Long customerId,
|
||||
@Query("sort") String sort
|
||||
);
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.DropdownDTO;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.StoreDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
public interface StoreApi {
|
||||
@@ -13,4 +17,7 @@ public interface StoreApi {
|
||||
Call<PageResponse<StoreDTO>> getAllStores(
|
||||
@Query("page") int page,
|
||||
@Query("size") int size);
|
||||
|
||||
@GET("api/v1/dropdowns/stores/{storeId}/employees")
|
||||
Call<List<DropdownDTO>> getStoreEmployees(@Path("storeId") Long storeId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
public class DropdownDTO {
|
||||
private Long id;
|
||||
private String label;
|
||||
|
||||
public DropdownDTO() {}
|
||||
|
||||
public DropdownDTO(Long id, String label) {
|
||||
this.id = id;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
}
|
||||
@@ -133,24 +133,6 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
private void setupFilterToggle() {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilterAdoption, binding.layoutFilterAdoption,
|
||||
binding.etSearchAdoption, binding.spinnerStatusAdoption, binding.spinnerStoreAdoption);
|
||||
|
||||
binding.btnToggleFilterAdoption.setOnClickListener(v -> {
|
||||
boolean isVisible = binding.layoutFilterAdoption.getVisibility() == View.VISIBLE;
|
||||
binding.layoutFilterAdoption.setVisibility(isVisible ? View.GONE : View.VISIBLE);
|
||||
|
||||
binding.btnToggleFilterAdoption.setImageResource(isVisible ?
|
||||
android.R.drawable.ic_menu_search :
|
||||
android.R.drawable.ic_menu_close_clear_cancel);
|
||||
|
||||
if (isVisible) {
|
||||
binding.etSearchAdoption.setText("");
|
||||
binding.spinnerStatusAdoption.setSelection(0);
|
||||
binding.spinnerStoreAdoption.setSelection(0);
|
||||
selectedCalendarDay = null;
|
||||
binding.calendarViewAdoption.clearSelection();
|
||||
loadAdoptions();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -163,26 +163,6 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
private void setupFilterToggle() {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchAppointment,
|
||||
binding.spinnerStatus, binding.spinnerStore);
|
||||
|
||||
// Add additional reset logic for elements specific to this fragment
|
||||
binding.btnToggleFilter.setOnClickListener(v -> {
|
||||
boolean isVisible = binding.layoutFilter.getVisibility() == View.VISIBLE;
|
||||
binding.layoutFilter.setVisibility(isVisible ? View.GONE : View.VISIBLE);
|
||||
|
||||
binding.btnToggleFilter.setImageResource(isVisible ?
|
||||
android.R.drawable.ic_menu_search :
|
||||
android.R.drawable.ic_menu_close_clear_cancel);
|
||||
|
||||
if (isVisible) {
|
||||
binding.etSearchAppointment.setText("");
|
||||
binding.spinnerStatus.setSelection(0);
|
||||
binding.spinnerStore.setSelection(0);
|
||||
binding.btnMyAppointments.setChecked(false);
|
||||
selectedCalendarDay = null;
|
||||
binding.calendarView.clearSelection();
|
||||
loadAppointmentData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -215,7 +215,7 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
if (status.equals("All Statuses")) status = null;
|
||||
if (species.equals("All Species")) species = null;
|
||||
|
||||
viewModel.getAllPets(0, 100, query, status, species, storeId, "petName").observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.getAllPets(0, 100, query, status, species, storeId, null, "petName").observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
|
||||
switch (resource.status) {
|
||||
|
||||
@@ -126,7 +126,7 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
* Loads the list of pets from the API.
|
||||
*/
|
||||
private void loadPets() {
|
||||
petViewModel.getAllPets(0, 200, null, null, null, null, "petName").observe(getViewLifecycleOwner(), resource -> {
|
||||
petViewModel.getAllPets(0, 200, null, null, null, null, null, "petName").observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
petList = resource.data.getContent();
|
||||
refreshPetSpinner();
|
||||
|
||||
@@ -47,7 +47,7 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
private List<ServiceDTO> serviceList = new ArrayList<>();
|
||||
private List<CustomerDTO> customerList = new ArrayList<>();
|
||||
private List<StoreDTO> storeList = new ArrayList<>();
|
||||
private List<UserDTO> staffList = new ArrayList<>();
|
||||
private List<DropdownDTO> staffList = new ArrayList<>();
|
||||
|
||||
private final Integer[] HOURS = {9,10,11,12,13,14,15,16,17};
|
||||
private final Integer[] MINUTES = {0,15,30,45};
|
||||
@@ -107,6 +107,40 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
hours[i] = String.format("%02d:00", HOURS[i]);
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerHour, hours);
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerMinute, new String[]{"00","15","30","45"});
|
||||
|
||||
// Listener to load pets based on selected customer
|
||||
binding.spinnerCustomer.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (position > 0 && position <= customerList.size()) {
|
||||
CustomerDTO selectedCustomer = customerList.get(position - 1);
|
||||
loadPets(selectedCustomer.getCustomerId());
|
||||
} else {
|
||||
petList.clear();
|
||||
refreshPetSpinner();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {}
|
||||
});
|
||||
|
||||
// Listener to load staff based on selected store
|
||||
binding.spinnerStore.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (position > 0 && position <= storeList.size()) {
|
||||
StoreDTO selectedStore = storeList.get(position - 1);
|
||||
loadStaff(selectedStore.getStoreId());
|
||||
} else {
|
||||
staffList.clear();
|
||||
refreshStaffSpinner();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,18 +163,16 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
* Fetches all required data for spinners from the backend.
|
||||
*/
|
||||
private void loadSpinnersData() {
|
||||
loadPets();
|
||||
loadServices();
|
||||
loadCustomers();
|
||||
loadStores();
|
||||
loadStaff();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the list of pets from the ViewModel.
|
||||
* Loads the list of pets from the ViewModel, filtered by customerId.
|
||||
*/
|
||||
private void loadPets() {
|
||||
petViewModel.getAllPets(0, 200, null, null, null, null, "petName").observe(getViewLifecycleOwner(), resource -> {
|
||||
private void loadPets(Long customerId) {
|
||||
petViewModel.getAllPets(0, 200, null, null, null, null, customerId, "petName").observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
petList = resource.data.getContent();
|
||||
refreshPetSpinner();
|
||||
@@ -222,12 +254,12 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the list of staff from the API.
|
||||
* Loads the list of staff for a specific store.
|
||||
*/
|
||||
private void loadStaff() {
|
||||
userViewModel.getUsers("STAFF", 0, 100).observe(getViewLifecycleOwner(), resource -> {
|
||||
private void loadStaff(Long storeId) {
|
||||
storeViewModel.getStoreEmployees(storeId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
staffList = resource.data.getContent();
|
||||
staffList = resource.data;
|
||||
refreshStaffSpinner();
|
||||
}
|
||||
});
|
||||
@@ -238,8 +270,8 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
*/
|
||||
private void refreshStaffSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerStaff, staffList,
|
||||
UserDTO::getFullName, "-- Select Staff --",
|
||||
preselectedStaffId, UserDTO::getId);
|
||||
DropdownDTO::getLabel, "-- Select Staff --",
|
||||
preselectedStaffId, DropdownDTO::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -254,11 +286,42 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
binding.tvAppointmentId.setText("ID: " + appointmentId);
|
||||
binding.tvAppointmentId.setVisibility(View.VISIBLE);
|
||||
binding.btnDeleteAppointment.setVisibility(View.VISIBLE);
|
||||
|
||||
// Disable and fade fields in edit mode
|
||||
binding.spinnerCustomer.setEnabled(false);
|
||||
binding.spinnerStore.setEnabled(false);
|
||||
binding.spinnerPet.setEnabled(false);
|
||||
binding.spinnerService.setEnabled(false);
|
||||
binding.spinnerCustomer.setAlpha(0.5f);
|
||||
binding.spinnerStore.setAlpha(0.5f);
|
||||
binding.spinnerPet.setAlpha(0.5f);
|
||||
binding.spinnerService.setAlpha(0.5f);
|
||||
|
||||
binding.tvLabelCustomer.setAlpha(0.5f);
|
||||
binding.tvLabelStore.setAlpha(0.5f);
|
||||
binding.tvLabelPet.setAlpha(0.5f);
|
||||
binding.tvLabelService.setAlpha(0.5f);
|
||||
|
||||
loadAppointmentData();
|
||||
} else {
|
||||
binding.tvApptMode.setText("Add Appointment");
|
||||
binding.btnDeleteAppointment.setVisibility(View.GONE);
|
||||
binding.tvAppointmentId.setVisibility(View.GONE);
|
||||
|
||||
// enable fields in add mode
|
||||
binding.spinnerCustomer.setEnabled(true);
|
||||
binding.spinnerStore.setEnabled(true);
|
||||
binding.spinnerPet.setEnabled(true);
|
||||
binding.spinnerService.setEnabled(true);
|
||||
binding.spinnerCustomer.setAlpha(1.0f);
|
||||
binding.spinnerStore.setAlpha(1.0f);
|
||||
binding.spinnerPet.setAlpha(1.0f);
|
||||
binding.spinnerService.setAlpha(1.0f);
|
||||
|
||||
binding.tvLabelCustomer.setAlpha(1.0f);
|
||||
binding.tvLabelStore.setAlpha(1.0f);
|
||||
binding.tvLabelPet.setAlpha(1.0f);
|
||||
binding.tvLabelService.setAlpha(1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,8 @@ public class PetRepository extends BaseRepository {
|
||||
/**
|
||||
* Retrieves a paginated list of pets from the API with optional filters.
|
||||
*/
|
||||
public LiveData<Resource<PageResponse<PetDTO>>> getAllPets(int page, int size, String query, String status, String species, Long storeId, String sort) {
|
||||
return executeCall(petApi.getAllPets(page, size, query, status, species, storeId, sort));
|
||||
public LiveData<Resource<PageResponse<PetDTO>>> getAllPets(int page, int size, String query, String status, String species, Long storeId, Long customerId, String sort) {
|
||||
return executeCall(petApi.getAllPets(page, size, query, status, species, storeId, customerId, sort));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,10 +3,13 @@ package com.example.petstoremobile.repositories;
|
||||
import androidx.lifecycle.LiveData;
|
||||
|
||||
import com.example.petstoremobile.api.StoreApi;
|
||||
import com.example.petstoremobile.dtos.DropdownDTO;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.StoreDTO;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@@ -26,4 +29,11 @@ public class StoreRepository extends BaseRepository {
|
||||
public LiveData<Resource<PageResponse<StoreDTO>>> getAllStores(int page, int size) {
|
||||
return executeCall(storeApi.getAllStores(page, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of employees for a specific store from the dropdowns API.
|
||||
*/
|
||||
public LiveData<Resource<List<DropdownDTO>>> getStoreEmployees(Long storeId) {
|
||||
return executeCall(storeApi.getStoreEmployees(storeId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public class UIUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a toggle for a filter layout, including icon changes and field resets.
|
||||
* Sets up a toggle for a filter layout, including icon changes.
|
||||
*/
|
||||
public static void setupFilterToggle(ImageButton btnToggle, View layoutFilter, EditText etSearch, Spinner... spinners) {
|
||||
btnToggle.setOnClickListener(v -> {
|
||||
@@ -36,13 +36,6 @@ public class UIUtils {
|
||||
btnToggle.setImageResource(isVisible ?
|
||||
android.R.drawable.ic_menu_search :
|
||||
android.R.drawable.ic_menu_close_clear_cancel);
|
||||
|
||||
if (isVisible) {
|
||||
if (etSearch != null) etSearch.setText("");
|
||||
for (Spinner spinner : spinners) {
|
||||
if (spinner != null) spinner.setSelection(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ public class PetViewModel extends ViewModel {
|
||||
/**
|
||||
* Fetches a paginated list of pets with filters.
|
||||
*/
|
||||
public LiveData<Resource<PageResponse<PetDTO>>> getAllPets(int page, int size, String query, String status, String species, Long storeId, String sort) {
|
||||
return repository.getAllPets(page, size, query, status, species, storeId, sort);
|
||||
public LiveData<Resource<PageResponse<PetDTO>>> getAllPets(int page, int size, String query, String status, String species, Long storeId, Long customerId, String sort) {
|
||||
return repository.getAllPets(page, size, query, status, species, storeId, customerId, sort);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,11 +3,14 @@ package com.example.petstoremobile.viewmodels;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.example.petstoremobile.dtos.DropdownDTO;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.StoreDTO;
|
||||
import com.example.petstoremobile.repositories.StoreRepository;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
@@ -27,4 +30,11 @@ public class StoreViewModel extends ViewModel {
|
||||
public LiveData<Resource<PageResponse<StoreDTO>>> getAllStores(int page, int size) {
|
||||
return repository.getAllStores(page, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a list of employees for a specific store.
|
||||
*/
|
||||
public LiveData<Resource<List<DropdownDTO>>> getStoreEmployees(Long storeId) {
|
||||
return repository.getStoreEmployees(storeId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
|
||||
<!-- Customer -->
|
||||
<TextView
|
||||
android:id="@+id/tvLabelCustomer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Customer"
|
||||
@@ -84,6 +85,7 @@
|
||||
|
||||
<!-- Store -->
|
||||
<TextView
|
||||
android:id="@+id/tvLabelStore"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Store"
|
||||
@@ -100,7 +102,8 @@
|
||||
<!-- Pet -->
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:id="@+id/tvLabelPet"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Pet"
|
||||
android:textColor="@color/text_dark"
|
||||
@@ -117,6 +120,7 @@
|
||||
<!-- Service -->
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvLabelService"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Service"
|
||||
|
||||
@@ -28,8 +28,9 @@ public class PetController {
|
||||
@RequestParam(required = false) String species,
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(required = false) Long storeId,
|
||||
@RequestParam(required = false) Long customerId,
|
||||
Pageable pageable) {
|
||||
return ResponseEntity.ok(petService.getAllPets(q, species, status, storeId, pageable));
|
||||
return ResponseEntity.ok(petService.getAllPets(q, species, status, storeId, customerId, pageable));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
|
||||
@@ -30,8 +30,9 @@ public interface PetRepository extends JpaRepository<Pet, Long> {
|
||||
"(:q IS NULL OR LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(COALESCE(p.petBreed, '')) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
|
||||
"(:species IS NULL OR LOWER(p.petSpecies) = LOWER(:species)) AND " +
|
||||
"(:status IS NULL OR LOWER(p.petStatus) = LOWER(:status)) AND " +
|
||||
"(:storeId IS NULL OR p.store.storeId = :storeId)")
|
||||
Page<Pet> searchPets(@Param("q") String query, @Param("species") String species, @Param("status") String status, @Param("storeId") Long storeId, Pageable pageable);
|
||||
"(:storeId IS NULL OR p.store.storeId = :storeId) AND " +
|
||||
"(:customerId IS NULL OR p.owner.id = :customerId)")
|
||||
Page<Pet> searchPets(@Param("q") String query, @Param("species") String species, @Param("status") String status, @Param("storeId") Long storeId, @Param("customerId") Long customerId, Pageable pageable);
|
||||
|
||||
@Query("SELECT p FROM Pet p WHERE LOWER(p.petStatus) = 'available' AND " +
|
||||
"(:q IS NULL OR LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(COALESCE(p.petBreed, '')) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
|
||||
|
||||
@@ -48,7 +48,7 @@ public class PetService {
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Page<PetResponse> getAllPets(String query, String species, String status, Long storeId, Pageable pageable) {
|
||||
public Page<PetResponse> getAllPets(String query, String species, String status, Long storeId, Long customerId, Pageable pageable) {
|
||||
String normalizedQuery = normalizeFilter(query);
|
||||
String normalizedSpecies = normalizeFilter(species);
|
||||
String normalizedStatus = normalizeFilter(status);
|
||||
@@ -61,7 +61,7 @@ public class PetService {
|
||||
}
|
||||
pets = petRepository.searchPublicPets(normalizedQuery, normalizedSpecies, storeId, pageable);
|
||||
} else if (viewer.role() == User.Role.STAFF || viewer.role() == User.Role.ADMIN) {
|
||||
pets = petRepository.searchPets(normalizedQuery, normalizedSpecies, normalizedStatus, storeId, pageable);
|
||||
pets = petRepository.searchPets(normalizedQuery, normalizedSpecies, normalizedStatus, storeId, customerId, pageable);
|
||||
} else if (viewer.role() == User.Role.CUSTOMER) {
|
||||
if (!isAllowedCustomerStatus(normalizedStatus)) {
|
||||
return new PageImpl<>(java.util.List.of(), pageable, 0);
|
||||
|
||||
Reference in New Issue
Block a user