merge final branch #338
@@ -20,6 +20,7 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel;
|
import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||||
|
|
||||||
|
// ViewModel for the pet detail/create screen
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
public class PetDetailViewModel extends ViewModel {
|
public class PetDetailViewModel extends ViewModel {
|
||||||
private static final String STATUS_AVAILABLE = "Available";
|
private static final String STATUS_AVAILABLE = "Available";
|
||||||
@@ -38,13 +39,13 @@ public class PetDetailViewModel extends ViewModel {
|
|||||||
private final MutableLiveData<Boolean> isLoading = new MutableLiveData<>(false);
|
private final MutableLiveData<Boolean> isLoading = new MutableLiveData<>(false);
|
||||||
private final MutableLiveData<ViewState> viewState = new MutableLiveData<>(new ViewState());
|
private final MutableLiveData<ViewState> viewState = new MutableLiveData<>(new ViewState());
|
||||||
|
|
||||||
private long petId = -1;
|
private long petId = -1; // -1 means creating a new pet
|
||||||
private Long selectedCustomerId = null;
|
private Long selectedCustomerId = null;
|
||||||
private Long selectedStoreId = null;
|
private Long selectedStoreId = null;
|
||||||
private String selectedSpecies = null;
|
private String selectedSpecies = null;
|
||||||
private String selectedBreed = null;
|
private String selectedBreed = null;
|
||||||
private boolean isOriginallyOwnedOrAdopted = false;
|
private boolean isOriginallyOwnedOrAdopted = false; // used to restrict staff edits
|
||||||
private Long originalCustomerId = null;
|
private Long originalCustomerId = null; // used to detect owner changes
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public PetDetailViewModel(PetRepository petRepository, CustomerRepository customerRepository, StoreRepository storeRepository) {
|
public PetDetailViewModel(PetRepository petRepository, CustomerRepository customerRepository, StoreRepository storeRepository) {
|
||||||
@@ -53,6 +54,7 @@ public class PetDetailViewModel extends ViewModel {
|
|||||||
this.storeRepository = storeRepository;
|
this.storeRepository = storeRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Loads customers, stores, and species dropdowns needed for the form
|
||||||
public void loadInitialFormData() {
|
public void loadInitialFormData() {
|
||||||
observeOnce(customerRepository.getCustomerDropdowns(), resource -> {
|
observeOnce(customerRepository.getCustomerDropdowns(), resource -> {
|
||||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||||
@@ -78,26 +80,18 @@ public class PetDetailViewModel extends ViewModel {
|
|||||||
initMode(id != -1);
|
initMode(id != -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getPetId() {
|
public long getPetId() { return petId; }
|
||||||
return petId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEditing() {
|
public boolean isEditing() {
|
||||||
ViewState current = viewState.getValue();
|
ViewState current = viewState.getValue();
|
||||||
return current != null && current.isEditing;
|
return current != null && current.isEditing;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOriginallyOwnedOrAdopted() {
|
public boolean isOriginallyOwnedOrAdopted() { return isOriginallyOwnedOrAdopted; }
|
||||||
return isOriginallyOwnedOrAdopted;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getOriginalCustomerId() {
|
public Long getOriginalCustomerId() { return originalCustomerId; }
|
||||||
return originalCustomerId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LiveData<ViewState> getViewState() {
|
public LiveData<ViewState> getViewState() { return viewState; }
|
||||||
return viewState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onCustomerSelected(int position) {
|
public void onCustomerSelected(int position) {
|
||||||
List<DropdownDTO> list = customerList.getValue();
|
List<DropdownDTO> list = customerList.getValue();
|
||||||
@@ -106,7 +100,6 @@ public class PetDetailViewModel extends ViewModel {
|
|||||||
} else {
|
} else {
|
||||||
selectedCustomerId = null;
|
selectedCustomerId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateViewState(state -> state.selectedCustomerId = selectedCustomerId);
|
updateViewState(state -> state.selectedCustomerId = selectedCustomerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +107,7 @@ public class PetDetailViewModel extends ViewModel {
|
|||||||
List<DropdownDTO> list = speciesList.getValue();
|
List<DropdownDTO> list = speciesList.getValue();
|
||||||
if (position > 0 && list != null && position <= list.size()) {
|
if (position > 0 && list != null && position <= list.size()) {
|
||||||
selectedSpecies = list.get(position - 1).getLabel();
|
selectedSpecies = list.get(position - 1).getLabel();
|
||||||
loadBreeds(selectedSpecies);
|
loadBreeds(selectedSpecies); // reload breeds when species changes
|
||||||
} else {
|
} else {
|
||||||
selectedSpecies = null;
|
selectedSpecies = null;
|
||||||
breedList.setValue(new ArrayList<>());
|
breedList.setValue(new ArrayList<>());
|
||||||
@@ -150,17 +143,17 @@ public class PetDetailViewModel extends ViewModel {
|
|||||||
} else {
|
} else {
|
||||||
selectedStoreId = null;
|
selectedStoreId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateViewState(state -> state.selectedStoreId = selectedStoreId);
|
updateViewState(state -> state.selectedStoreId = selectedStoreId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onStatusSelected(String status) {
|
public void onStatusSelected(String status) {
|
||||||
updateViewState(state -> {
|
updateViewState(state -> {
|
||||||
state.selectedStatus = normalizeStatus(status);
|
state.selectedStatus = normalizeStatus(status);
|
||||||
applyStatusRules(state, true);
|
applyStatusRules(state, true); // clear invalid selections when status changes
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets up initial ViewState based on whether we're editing or creating
|
||||||
public void initMode(boolean isEditing) {
|
public void initMode(boolean isEditing) {
|
||||||
updateViewState(state -> {
|
updateViewState(state -> {
|
||||||
state.isEditing = isEditing;
|
state.isEditing = isEditing;
|
||||||
@@ -177,6 +170,7 @@ public class PetDetailViewModel extends ViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isEditing) {
|
if (!isEditing) {
|
||||||
|
// Reset all selections for a blank create form
|
||||||
selectedCustomerId = null;
|
selectedCustomerId = null;
|
||||||
selectedStoreId = null;
|
selectedStoreId = null;
|
||||||
selectedSpecies = null;
|
selectedSpecies = null;
|
||||||
@@ -202,6 +196,7 @@ public class PetDetailViewModel extends ViewModel {
|
|||||||
selectedStoreId = pet.getStoreId();
|
selectedStoreId = pet.getStoreId();
|
||||||
selectedSpecies = pet.getPetSpecies();
|
selectedSpecies = pet.getPetSpecies();
|
||||||
selectedBreed = pet.getPetBreed();
|
selectedBreed = pet.getPetBreed();
|
||||||
|
// Track original status to restrict staff from editing owned/adopted pets
|
||||||
isOriginallyOwnedOrAdopted = STATUS_OWNED.equalsIgnoreCase(pet.getPetStatus())
|
isOriginallyOwnedOrAdopted = STATUS_OWNED.equalsIgnoreCase(pet.getPetStatus())
|
||||||
|| STATUS_ADOPTED.equalsIgnoreCase(pet.getPetStatus());
|
|| STATUS_ADOPTED.equalsIgnoreCase(pet.getPetStatus());
|
||||||
originalCustomerId = pet.getCustomerId();
|
originalCustomerId = pet.getCustomerId();
|
||||||
@@ -217,7 +212,7 @@ public class PetDetailViewModel extends ViewModel {
|
|||||||
state.selectedBreed = selectedBreed;
|
state.selectedBreed = selectedBreed;
|
||||||
state.selectedStatus = normalizeStatus(pet.getPetStatus());
|
state.selectedStatus = normalizeStatus(pet.getPetStatus());
|
||||||
state.isBreedEnabled = !state.isEditing && (selectedSpecies != null);
|
state.isBreedEnabled = !state.isEditing && (selectedSpecies != null);
|
||||||
applyStatusRules(state, false);
|
applyStatusRules(state, false); // don't clear selections when loading existing data
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,46 +226,26 @@ public class PetDetailViewModel extends ViewModel {
|
|||||||
petDTO.setPetId(petId);
|
petDTO.setPetId(petId);
|
||||||
return petRepository.updatePet(petId, petDTO);
|
return petRepository.updatePet(petId, petDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
return petRepository.createPet(petDTO);
|
return petRepository.createPet(petDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiveData<Resource<Void>> deletePet() {
|
public LiveData<Resource<Void>> deletePet() { return petRepository.deletePet(petId); }
|
||||||
return petRepository.deletePet(petId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LiveData<Resource<Void>> uploadPetImage(okhttp3.MultipartBody.Part image) {
|
public LiveData<Resource<Void>> uploadPetImage(okhttp3.MultipartBody.Part image) {
|
||||||
return petRepository.uploadPetImage(petId, image);
|
return petRepository.uploadPetImage(petId, image);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiveData<Resource<Void>> deletePetImage() {
|
public LiveData<Resource<Void>> deletePetImage() { return petRepository.deletePetImage(petId); }
|
||||||
return petRepository.deletePetImage(petId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LiveData<List<DropdownDTO>> getCustomerList() {
|
public LiveData<List<DropdownDTO>> getCustomerList() { return customerList; }
|
||||||
return customerList;
|
public LiveData<List<DropdownDTO>> getStoreList() { return storeList; }
|
||||||
}
|
public LiveData<List<DropdownDTO>> getSpeciesList() { return speciesList; }
|
||||||
|
public LiveData<List<DropdownDTO>> getBreedList() { return breedList; }
|
||||||
|
public LiveData<Boolean> getIsLoading() { return isLoading; }
|
||||||
|
|
||||||
public LiveData<List<DropdownDTO>> getStoreList() {
|
public void setLoading(boolean loading) { isLoading.setValue(loading); }
|
||||||
return storeList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LiveData<List<DropdownDTO>> getSpeciesList() {
|
|
||||||
return speciesList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LiveData<List<DropdownDTO>> getBreedList() {
|
|
||||||
return breedList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LiveData<Boolean> getIsLoading() {
|
|
||||||
return isLoading;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLoading(boolean loading) {
|
|
||||||
isLoading.setValue(loading);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Enables/disables customer and store spinners based on the selected status
|
||||||
private void applyStatusRules(ViewState state, boolean clearInvalidSelections) {
|
private void applyStatusRules(ViewState state, boolean clearInvalidSelections) {
|
||||||
if (STATUS_AVAILABLE.equalsIgnoreCase(state.selectedStatus)) {
|
if (STATUS_AVAILABLE.equalsIgnoreCase(state.selectedStatus)) {
|
||||||
state.isCustomerEnabled = false;
|
state.isCustomerEnabled = false;
|
||||||
@@ -292,10 +267,12 @@ public class PetDetailViewModel extends ViewModel {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adopted and Pending: both customer and store required
|
||||||
state.isCustomerEnabled = true;
|
state.isCustomerEnabled = true;
|
||||||
state.isStoreEnabled = true;
|
state.isStoreEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Normalizes any status string to one of the four known constants
|
||||||
private String normalizeStatus(String status) {
|
private String normalizeStatus(String status) {
|
||||||
if (status == null) return STATUS_AVAILABLE;
|
if (status == null) return STATUS_AVAILABLE;
|
||||||
String normalized = status.trim();
|
String normalized = status.trim();
|
||||||
@@ -305,6 +282,7 @@ public class PetDetailViewModel extends ViewModel {
|
|||||||
return STATUS_AVAILABLE;
|
return STATUS_AVAILABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mutates the current ViewState and re-posts it to trigger UI updates
|
||||||
private void updateViewState(Action<ViewState> action) {
|
private void updateViewState(Action<ViewState> action) {
|
||||||
ViewState current = viewState.getValue();
|
ViewState current = viewState.getValue();
|
||||||
if (current != null) {
|
if (current != null) {
|
||||||
@@ -317,6 +295,7 @@ public class PetDetailViewModel extends ViewModel {
|
|||||||
void run(T target);
|
void run(T target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Observes a LiveData exactly once, removing itself after the first non-loading result
|
||||||
private <T> void observeOnce(LiveData<Resource<T>> liveData, Observer<Resource<T>> handler) {
|
private <T> void observeOnce(LiveData<Resource<T>> liveData, Observer<Resource<T>> handler) {
|
||||||
liveData.observeForever(new Observer<Resource<T>>() {
|
liveData.observeForever(new Observer<Resource<T>>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -329,6 +308,7 @@ public class PetDetailViewModel extends ViewModel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Holds all UI state for the pet detail screen
|
||||||
public static class ViewState {
|
public static class ViewState {
|
||||||
public boolean isEditing = false;
|
public boolean isEditing = false;
|
||||||
public boolean isDeleteVisible = false;
|
public boolean isDeleteVisible = false;
|
||||||
@@ -346,4 +326,4 @@ public class PetDetailViewModel extends ViewModel {
|
|||||||
public Long selectedCustomerId = null;
|
public Long selectedCustomerId = null;
|
||||||
public Long selectedStoreId = null;
|
public Long selectedStoreId = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user