Merge pull request #338 from RecentRunner/final

merge final branch
This commit is contained in:
2026-04-20 21:33:19 -06:00
committed by GitHub

View File

@@ -20,6 +20,7 @@ import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
// ViewModel for the pet detail/create screen
@HiltViewModel
public class PetDetailViewModel extends ViewModel {
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<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 selectedStoreId = null;
private String selectedSpecies = null;
private String selectedBreed = null;
private boolean isOriginallyOwnedOrAdopted = false;
private Long originalCustomerId = null;
private boolean isOriginallyOwnedOrAdopted = false; // used to restrict staff edits
private Long originalCustomerId = null; // used to detect owner changes
@Inject
public PetDetailViewModel(PetRepository petRepository, CustomerRepository customerRepository, StoreRepository storeRepository) {
@@ -53,6 +54,7 @@ public class PetDetailViewModel extends ViewModel {
this.storeRepository = storeRepository;
}
// Loads customers, stores, and species dropdowns needed for the form
public void loadInitialFormData() {
observeOnce(customerRepository.getCustomerDropdowns(), resource -> {
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
@@ -78,26 +80,18 @@ public class PetDetailViewModel extends ViewModel {
initMode(id != -1);
}
public long getPetId() {
return petId;
}
public long getPetId() { return petId; }
public boolean isEditing() {
ViewState current = viewState.getValue();
return current != null && current.isEditing;
}
public boolean isOriginallyOwnedOrAdopted() {
return isOriginallyOwnedOrAdopted;
}
public boolean isOriginallyOwnedOrAdopted() { return isOriginallyOwnedOrAdopted; }
public Long getOriginalCustomerId() {
return originalCustomerId;
}
public Long getOriginalCustomerId() { return originalCustomerId; }
public LiveData<ViewState> getViewState() {
return viewState;
}
public LiveData<ViewState> getViewState() { return viewState; }
public void onCustomerSelected(int position) {
List<DropdownDTO> list = customerList.getValue();
@@ -106,7 +100,6 @@ public class PetDetailViewModel extends ViewModel {
} else {
selectedCustomerId = null;
}
updateViewState(state -> state.selectedCustomerId = selectedCustomerId);
}
@@ -114,7 +107,7 @@ public class PetDetailViewModel extends ViewModel {
List<DropdownDTO> list = speciesList.getValue();
if (position > 0 && list != null && position <= list.size()) {
selectedSpecies = list.get(position - 1).getLabel();
loadBreeds(selectedSpecies);
loadBreeds(selectedSpecies); // reload breeds when species changes
} else {
selectedSpecies = null;
breedList.setValue(new ArrayList<>());
@@ -150,17 +143,17 @@ public class PetDetailViewModel extends ViewModel {
} else {
selectedStoreId = null;
}
updateViewState(state -> state.selectedStoreId = selectedStoreId);
}
public void onStatusSelected(String status) {
updateViewState(state -> {
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) {
updateViewState(state -> {
state.isEditing = isEditing;
@@ -177,6 +170,7 @@ public class PetDetailViewModel extends ViewModel {
}
if (!isEditing) {
// Reset all selections for a blank create form
selectedCustomerId = null;
selectedStoreId = null;
selectedSpecies = null;
@@ -202,6 +196,7 @@ public class PetDetailViewModel extends ViewModel {
selectedStoreId = pet.getStoreId();
selectedSpecies = pet.getPetSpecies();
selectedBreed = pet.getPetBreed();
// Track original status to restrict staff from editing owned/adopted pets
isOriginallyOwnedOrAdopted = STATUS_OWNED.equalsIgnoreCase(pet.getPetStatus())
|| STATUS_ADOPTED.equalsIgnoreCase(pet.getPetStatus());
originalCustomerId = pet.getCustomerId();
@@ -217,7 +212,7 @@ public class PetDetailViewModel extends ViewModel {
state.selectedBreed = selectedBreed;
state.selectedStatus = normalizeStatus(pet.getPetStatus());
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);
return petRepository.updatePet(petId, petDTO);
}
return petRepository.createPet(petDTO);
}
public LiveData<Resource<Void>> deletePet() {
return petRepository.deletePet(petId);
}
public LiveData<Resource<Void>> deletePet() { return petRepository.deletePet(petId); }
public LiveData<Resource<Void>> uploadPetImage(okhttp3.MultipartBody.Part image) {
return petRepository.uploadPetImage(petId, image);
}
public LiveData<Resource<Void>> deletePetImage() {
return petRepository.deletePetImage(petId);
}
public LiveData<Resource<Void>> deletePetImage() { return petRepository.deletePetImage(petId); }
public LiveData<List<DropdownDTO>> getCustomerList() {
return customerList;
}
public LiveData<List<DropdownDTO>> getCustomerList() { 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() {
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);
}
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) {
if (STATUS_AVAILABLE.equalsIgnoreCase(state.selectedStatus)) {
state.isCustomerEnabled = false;
@@ -292,10 +267,12 @@ public class PetDetailViewModel extends ViewModel {
return;
}
// Adopted and Pending: both customer and store required
state.isCustomerEnabled = true;
state.isStoreEnabled = true;
}
// Normalizes any status string to one of the four known constants
private String normalizeStatus(String status) {
if (status == null) return STATUS_AVAILABLE;
String normalized = status.trim();
@@ -305,6 +282,7 @@ public class PetDetailViewModel extends ViewModel {
return STATUS_AVAILABLE;
}
// Mutates the current ViewState and re-posts it to trigger UI updates
private void updateViewState(Action<ViewState> action) {
ViewState current = viewState.getValue();
if (current != null) {
@@ -317,6 +295,7 @@ public class PetDetailViewModel extends ViewModel {
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) {
liveData.observeForever(new Observer<Resource<T>>() {
@Override
@@ -329,6 +308,7 @@ public class PetDetailViewModel extends ViewModel {
});
}
// Holds all UI state for the pet detail screen
public static class ViewState {
public boolean isEditing = false;
public boolean isDeleteVisible = false;
@@ -346,4 +326,4 @@ public class PetDetailViewModel extends ViewModel {
public Long selectedCustomerId = null;
public Long selectedStoreId = null;
}
}
}