Azure deployment setup #297
@@ -56,11 +56,14 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
|
||||
setupFilterToggle();
|
||||
setupBulkDelete();
|
||||
observeViewModel();
|
||||
|
||||
|
||||
loadServices(true);
|
||||
|
||||
UIUtils.setupHamburgerMenu(binding.btnHamburger, this);
|
||||
|
||||
binding.fabAddService.setOnClickListener(v ->
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_service_detail));
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@@ -156,4 +159,4 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
|
||||
bulkDeleteHandler.onSelectionChanged(count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,9 +41,7 @@ public class PetDetailFragment extends Fragment {
|
||||
|
||||
private FragmentPetDetailBinding binding;
|
||||
private PetDetailViewModel viewModel;
|
||||
|
||||
private Long selectedCustomerId = null;
|
||||
private Long selectedStoreId = null;
|
||||
private boolean isUpdatingUI = false;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
@@ -65,6 +63,7 @@ public class PetDetailFragment extends Fragment {
|
||||
setupSpinner();
|
||||
observeViewModel();
|
||||
handleArguments();
|
||||
viewModel.loadInitialFormData();
|
||||
|
||||
binding.btnBack.setOnClickListener(v -> navigateBack());
|
||||
binding.btnSavePet.setOnClickListener(v -> savePet());
|
||||
@@ -72,23 +71,18 @@ public class PetDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void observeViewModel() {
|
||||
viewModel.getCustomerList().observe(getViewLifecycleOwner(), list -> updateCustomerSpinnerSelection());
|
||||
viewModel.getStoreList().observe(getViewLifecycleOwner(), list -> updateStoreSpinnerSelection());
|
||||
|
||||
viewModel.loadCustomers().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setCustomerList(resource.data);
|
||||
}
|
||||
viewModel.getViewState().observe(getViewLifecycleOwner(), this::applyViewState);
|
||||
|
||||
viewModel.getCustomerList().observe(getViewLifecycleOwner(), list -> {
|
||||
PetDetailViewModel.ViewState state = viewModel.getViewState().getValue();
|
||||
Long selectedCustomerId = state != null ? state.selectedCustomerId : null;
|
||||
updateCustomerSpinnerSelection(selectedCustomerId);
|
||||
});
|
||||
|
||||
viewModel.loadStores().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setStoreList(resource.data);
|
||||
}
|
||||
viewModel.getStoreList().observe(getViewLifecycleOwner(), list -> {
|
||||
PetDetailViewModel.ViewState state = viewModel.getViewState().getValue();
|
||||
Long selectedStoreId = state != null ? state.selectedStoreId : null;
|
||||
updateStoreSpinnerSelection(selectedStoreId);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -119,12 +113,12 @@ public class PetDetailFragment extends Fragment {
|
||||
String status = binding.spinnerPetStatus.getSelectedItem().toString();
|
||||
|
||||
Long customerId = null;
|
||||
if (binding.spinnerCustomer.getSelectedItemPosition() > 0) {
|
||||
if (binding.spinnerCustomer.getSelectedItemPosition() > 0 && viewModel.getCustomerList().getValue() != null) {
|
||||
customerId = viewModel.getCustomerList().getValue().get(binding.spinnerCustomer.getSelectedItemPosition() - 1).getId();
|
||||
}
|
||||
|
||||
Long storeId = null;
|
||||
if (binding.spinnerStore.getSelectedItemPosition() > 0) {
|
||||
if (binding.spinnerStore.getSelectedItemPosition() > 0 && viewModel.getStoreList().getValue() != null) {
|
||||
storeId = viewModel.getStoreList().getValue().get(binding.spinnerStore.getSelectedItemPosition() - 1).getId();
|
||||
}
|
||||
|
||||
@@ -193,24 +187,12 @@ public class PetDetailFragment extends Fragment {
|
||||
|
||||
private void handleArguments() {
|
||||
if (getArguments() != null && getArguments().containsKey("petId")) {
|
||||
long petId = getArguments().getLong("petId");
|
||||
viewModel.setPetId(petId);
|
||||
binding.tvMode.setText("Edit Pet");
|
||||
binding.tvPetId.setText(DateTimeUtils.formatId(petId));
|
||||
binding.tvPetId.setVisibility(View.VISIBLE);
|
||||
binding.btnDeletePet.setVisibility(View.VISIBLE);
|
||||
|
||||
UIUtils.setViewsEnabled(false, binding.etPetSpecies, binding.etPetBreed);
|
||||
viewModel.setPetId(getArguments().getLong("petId"));
|
||||
loadPetData();
|
||||
} else {
|
||||
viewModel.setPetId(-1);
|
||||
binding.tvMode.setText("Add Pet");
|
||||
binding.tvPetId.setVisibility(View.GONE);
|
||||
binding.btnDeletePet.setVisibility(View.GONE);
|
||||
binding.btnSavePet.setText("Add");
|
||||
|
||||
UIUtils.setViewsEnabled(true, binding.etPetSpecies, binding.etPetBreed);
|
||||
return;
|
||||
}
|
||||
|
||||
viewModel.setPetId(-1);
|
||||
}
|
||||
|
||||
private void loadPetData() {
|
||||
@@ -226,20 +208,13 @@ public class PetDetailFragment extends Fragment {
|
||||
if (p.getPetPrice() != null) {
|
||||
binding.etPetPrice.setText(String.format(Locale.getDefault(), "%.2f", p.getPetPrice()));
|
||||
}
|
||||
SpinnerUtils.setSelectionByValue(binding.spinnerPetStatus, p.getPetStatus());
|
||||
|
||||
selectedCustomerId = p.getCustomerId();
|
||||
updateCustomerSpinnerSelection();
|
||||
|
||||
selectedStoreId = p.getStoreId();
|
||||
updateStoreSpinnerSelection();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Failed to load pet: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateCustomerSpinnerSelection() {
|
||||
private void updateCustomerSpinnerSelection(Long selectedCustomerId) {
|
||||
SpinnerUtils.populateSpinner(
|
||||
requireContext(),
|
||||
binding.spinnerCustomer,
|
||||
@@ -251,7 +226,7 @@ public class PetDetailFragment extends Fragment {
|
||||
);
|
||||
}
|
||||
|
||||
private void updateStoreSpinnerSelection() {
|
||||
private void updateStoreSpinnerSelection(Long selectedStoreId) {
|
||||
SpinnerUtils.populateSpinner(
|
||||
requireContext(),
|
||||
binding.spinnerStore,
|
||||
@@ -264,36 +239,76 @@ public class PetDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void setupSpinner() {
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerPetStatus,
|
||||
new String[]{"Available", "Adopted", "Owned"});
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerPetStatus, new String[]{});
|
||||
|
||||
binding.spinnerPetStatus.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
binding.spinnerCustomer.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
String status = parent.getItemAtPosition(position).toString();
|
||||
|
||||
clearSpinnerError(binding.spinnerCustomer);
|
||||
clearSpinnerError(binding.spinnerStore);
|
||||
|
||||
if ("Available".equalsIgnoreCase(status)) {
|
||||
binding.spinnerCustomer.setSelection(0);
|
||||
UIUtils.setViewsEnabled(false, binding.spinnerCustomer);
|
||||
} else {
|
||||
UIUtils.setViewsEnabled(true, binding.spinnerCustomer);
|
||||
}
|
||||
|
||||
if ("Owned".equalsIgnoreCase(status)) {
|
||||
binding.spinnerStore.setSelection(0);
|
||||
UIUtils.setViewsEnabled(false, binding.spinnerStore);
|
||||
} else {
|
||||
UIUtils.setViewsEnabled(true, binding.spinnerStore);
|
||||
}
|
||||
if (isUpdatingUI) return;
|
||||
viewModel.onCustomerSelected(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
}
|
||||
});
|
||||
|
||||
binding.spinnerStore.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (isUpdatingUI) return;
|
||||
viewModel.onStoreSelected(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
}
|
||||
});
|
||||
|
||||
binding.spinnerPetStatus.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (isUpdatingUI) return;
|
||||
String status = parent.getItemAtPosition(position).toString();
|
||||
clearSpinnerError(binding.spinnerCustomer);
|
||||
clearSpinnerError(binding.spinnerStore);
|
||||
viewModel.onStatusSelected(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void applyViewState(PetDetailViewModel.ViewState state) {
|
||||
isUpdatingUI = true;
|
||||
|
||||
binding.tvMode.setText(state.modeTitle);
|
||||
binding.tvPetId.setText(DateTimeUtils.formatId(viewModel.getPetId()));
|
||||
binding.tvPetId.setVisibility(state.isPetIdVisible ? View.VISIBLE : View.GONE);
|
||||
binding.btnDeletePet.setVisibility(state.isDeleteVisible ? View.VISIBLE : View.GONE);
|
||||
binding.btnSavePet.setText(state.saveButtonText);
|
||||
|
||||
UIUtils.setViewsEnabled(state.isSpeciesEnabled, binding.etPetSpecies);
|
||||
UIUtils.setViewsEnabled(state.isBreedEnabled, binding.etPetBreed);
|
||||
UIUtils.setViewsEnabled(state.isCustomerEnabled, binding.spinnerCustomer);
|
||||
UIUtils.setViewsEnabled(state.isStoreEnabled, binding.spinnerStore);
|
||||
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerPetStatus, state.availableStatuses);
|
||||
SpinnerUtils.setSelectionByValue(binding.spinnerPetStatus, state.selectedStatus);
|
||||
|
||||
updateCustomerSpinnerSelection(state.selectedCustomerId);
|
||||
updateStoreSpinnerSelection(state.selectedStoreId);
|
||||
|
||||
if (!state.isCustomerEnabled && binding.spinnerCustomer.getSelectedItemPosition() != 0) {
|
||||
binding.spinnerCustomer.setSelection(0);
|
||||
}
|
||||
if (!state.isStoreEnabled && binding.spinnerStore.getSelectedItemPosition() != 0) {
|
||||
binding.spinnerStore.setSelection(0);
|
||||
}
|
||||
|
||||
isUpdatingUI = false;
|
||||
}
|
||||
|
||||
private void clearSpinnerError(Spinner spinner) {
|
||||
|
||||
@@ -11,9 +11,9 @@ import androidx.navigation.fragment.NavHostFragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.databinding.FragmentServiceDetailBinding;
|
||||
import com.example.petstoremobile.dtos.ServiceDTO;
|
||||
import com.example.petstoremobile.utils.ActivityLogger;
|
||||
@@ -21,6 +21,7 @@ import com.example.petstoremobile.utils.DateTimeUtils;
|
||||
import com.example.petstoremobile.utils.DialogUtils;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.UIUtils;
|
||||
import com.example.petstoremobile.viewmodels.ServiceDetailViewModel;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
@@ -51,6 +52,7 @@ public class ServiceDetailFragment extends Fragment {
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
observeViewModel();
|
||||
handleArguments();
|
||||
|
||||
binding.btnBack.setOnClickListener(v -> navigateBack());
|
||||
@@ -58,8 +60,12 @@ public class ServiceDetailFragment extends Fragment {
|
||||
binding.btnDeleteService.setOnClickListener(v -> deleteService());
|
||||
}
|
||||
|
||||
private void observeViewModel() {
|
||||
viewModel.getViewState().observe(getViewLifecycleOwner(), this::applyViewState);
|
||||
}
|
||||
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
if (binding != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
@@ -126,34 +132,48 @@ public class ServiceDetailFragment extends Fragment {
|
||||
|
||||
private void handleArguments() {
|
||||
if (getArguments() != null && getArguments().containsKey("serviceId")) {
|
||||
long serviceId = getArguments().getLong("serviceId");
|
||||
viewModel.setServiceId(serviceId);
|
||||
binding.tvMode.setText("Edit Service");
|
||||
binding.tvServiceId.setText(DateTimeUtils.formatId(serviceId));
|
||||
binding.btnDeleteService.setVisibility(View.VISIBLE);
|
||||
viewModel.setServiceId(getArguments().getLong("serviceId"));
|
||||
loadServiceData();
|
||||
} else {
|
||||
viewModel.setServiceId(-1);
|
||||
binding.tvMode.setText("Add Service");
|
||||
binding.tvServiceId.setVisibility(View.GONE);
|
||||
binding.btnDeleteService.setVisibility(View.GONE);
|
||||
binding.btnSaveService.setText("Add");
|
||||
return;
|
||||
}
|
||||
|
||||
viewModel.setServiceId(-1);
|
||||
}
|
||||
|
||||
private void loadServiceData() {
|
||||
viewModel.loadService().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
ServiceDTO s = resource.data;
|
||||
binding.etServiceName.setText(s.getServiceName());
|
||||
binding.etServiceDesc.setText(s.getServiceDesc());
|
||||
binding.etServiceDuration.setText(String.valueOf(s.getServiceDuration()));
|
||||
binding.etServicePrice.setText(String.valueOf(s.getServicePrice()));
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Failed to load service: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void applyViewState(ServiceDetailViewModel.ViewState state) {
|
||||
binding.tvMode.setText(state.modeTitle);
|
||||
binding.tvServiceId.setText(DateTimeUtils.formatId(viewModel.getServiceId()));
|
||||
binding.tvServiceId.setVisibility(state.isServiceIdVisible ? View.VISIBLE : View.GONE);
|
||||
binding.btnDeleteService.setVisibility(state.isDeleteVisible ? View.VISIBLE : View.GONE);
|
||||
binding.btnSaveService.setText(state.saveButtonText);
|
||||
|
||||
UIUtils.setViewsEnabled(state.isFieldsEnabled,
|
||||
binding.etServiceName,
|
||||
binding.etServiceDesc,
|
||||
binding.etServiceDuration,
|
||||
binding.etServicePrice);
|
||||
|
||||
updateIfDifferent(binding.etServiceName, state.serviceName);
|
||||
updateIfDifferent(binding.etServiceDesc, state.serviceDesc);
|
||||
updateIfDifferent(binding.etServiceDuration, state.serviceDuration);
|
||||
updateIfDifferent(binding.etServicePrice, state.servicePrice);
|
||||
}
|
||||
|
||||
private void updateIfDifferent(EditText field, String value) {
|
||||
String current = field.getText() != null ? field.getText().toString() : "";
|
||||
String next = value != null ? value : "";
|
||||
if (!current.equals(next)) {
|
||||
field.setText(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,31 @@ import java.io.InputStream;
|
||||
public class FileUtils {
|
||||
public static File getFileFromUri(Context context, Uri uri) {
|
||||
try {
|
||||
if ("content".equals(uri.getScheme())) {
|
||||
String authority = uri.getAuthority();
|
||||
if (authority != null && authority.equals(context.getPackageName() + ".fileprovider")) {
|
||||
String lastSegment = uri.getLastPathSegment();
|
||||
if (lastSegment != null) {
|
||||
String fileName = lastSegment.contains("/")
|
||||
? lastSegment.substring(lastSegment.lastIndexOf('/') + 1)
|
||||
: lastSegment;
|
||||
File cachedFile = new File(context.getCacheDir(), fileName);
|
||||
if (cachedFile.exists() && cachedFile.length() > 0) {
|
||||
return cachedFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String fileName = getFileName(context, uri);
|
||||
if (fileName == null) fileName = "upload_" + System.currentTimeMillis();
|
||||
|
||||
|
||||
InputStream inputStream = context.getContentResolver().openInputStream(uri);
|
||||
if (inputStream == null) return null;
|
||||
|
||||
File tempFile = new File(context.getCacheDir(), fileName);
|
||||
FileOutputStream outputStream = new FileOutputStream(tempFile);
|
||||
byte[] buffer = new byte[1024];
|
||||
byte[] buffer = new byte[4096];
|
||||
int length;
|
||||
while ((length = inputStream.read(buffer)) > 0) {
|
||||
outputStream.write(buffer, 0, length);
|
||||
@@ -47,4 +65,4 @@ public class FileUtils {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,9 +129,16 @@ public class ImagePickerHelper {
|
||||
* Prepares a temporary file and launches the camera app.
|
||||
*/
|
||||
private void launchCamera() {
|
||||
File photoFile = new File(fragment.requireContext().getCacheDir(), tempFileName);
|
||||
photoUri = FileProvider.getUriForFile(fragment.requireContext(), fragment.requireContext().getPackageName() + ".fileprovider", photoFile);
|
||||
cameraLauncher.launch(photoUri);
|
||||
try {
|
||||
File photoFile = new File(fragment.requireContext().getCacheDir(), tempFileName);
|
||||
if (!photoFile.exists()) photoFile.createNewFile();
|
||||
photoUri = FileProvider.getUriForFile(fragment.requireContext(),
|
||||
fragment.requireContext().getPackageName() + ".fileprovider", photoFile);
|
||||
cameraLauncher.launch(photoUri);
|
||||
} catch (Exception e) {
|
||||
android.widget.Toast.makeText(fragment.requireContext(),
|
||||
"Could not prepare camera", android.widget.Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,4 +164,4 @@ public class ImagePickerHelper {
|
||||
.setNegativeButton("Cancel", null)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,17 +20,22 @@ import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
|
||||
@HiltViewModel
|
||||
public class PetDetailViewModel extends ViewModel {
|
||||
private static final String STATUS_AVAILABLE = "Available";
|
||||
private static final String STATUS_ADOPTED = "Adopted";
|
||||
private static final String STATUS_OWNED = "Owned";
|
||||
|
||||
private final PetRepository petRepository;
|
||||
private final CustomerRepository customerRepository;
|
||||
private final StoreRepository storeRepository;
|
||||
|
||||
private final MutableLiveData<PetDTO> petState = new MutableLiveData<>();
|
||||
private final MutableLiveData<List<DropdownDTO>> customerList = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<List<DropdownDTO>> storeList = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<Boolean> isLoading = new MutableLiveData<>(false);
|
||||
|
||||
private final MutableLiveData<ViewState> viewState = new MutableLiveData<>(new ViewState());
|
||||
|
||||
private long petId = -1;
|
||||
private boolean isEditing = false;
|
||||
private Long selectedCustomerId = null;
|
||||
private Long selectedStoreId = null;
|
||||
|
||||
@Inject
|
||||
public PetDetailViewModel(PetRepository petRepository, CustomerRepository customerRepository, StoreRepository storeRepository) {
|
||||
@@ -39,9 +44,23 @@ public class PetDetailViewModel extends ViewModel {
|
||||
this.storeRepository = storeRepository;
|
||||
}
|
||||
|
||||
public void loadInitialFormData() {
|
||||
customerRepository.getCustomerDropdowns().observeForever(resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
customerList.setValue(resource.data);
|
||||
}
|
||||
});
|
||||
|
||||
storeRepository.getStoreDropdowns().observeForever(resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
storeList.setValue(resource.data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setPetId(long id) {
|
||||
this.petId = id;
|
||||
this.isEditing = id != -1;
|
||||
initMode(id != -1);
|
||||
}
|
||||
|
||||
public long getPetId() {
|
||||
@@ -49,46 +68,108 @@ public class PetDetailViewModel extends ViewModel {
|
||||
}
|
||||
|
||||
public boolean isEditing() {
|
||||
return isEditing;
|
||||
ViewState current = viewState.getValue();
|
||||
return current != null && current.isEditing;
|
||||
}
|
||||
|
||||
public LiveData<ViewState> getViewState() {
|
||||
return viewState;
|
||||
}
|
||||
|
||||
public void onCustomerSelected(int position) {
|
||||
List<DropdownDTO> list = customerList.getValue();
|
||||
if (position > 0 && list != null && position <= list.size()) {
|
||||
selectedCustomerId = list.get(position - 1).getId();
|
||||
} else {
|
||||
selectedCustomerId = null;
|
||||
}
|
||||
|
||||
updateViewState(state -> state.selectedCustomerId = selectedCustomerId);
|
||||
}
|
||||
|
||||
public void onStoreSelected(int position) {
|
||||
List<DropdownDTO> list = storeList.getValue();
|
||||
if (position > 0 && list != null && position <= list.size()) {
|
||||
selectedStoreId = list.get(position - 1).getId();
|
||||
} else {
|
||||
selectedStoreId = null;
|
||||
}
|
||||
|
||||
updateViewState(state -> state.selectedStoreId = selectedStoreId);
|
||||
}
|
||||
|
||||
public void onStatusSelected(String status) {
|
||||
updateViewState(state -> {
|
||||
state.selectedStatus = normalizeStatus(status);
|
||||
applyStatusRules(state, true);
|
||||
});
|
||||
}
|
||||
|
||||
public void initMode(boolean isEditing) {
|
||||
updateViewState(state -> {
|
||||
state.isEditing = isEditing;
|
||||
state.modeTitle = isEditing ? "Edit Pet" : "Add Pet";
|
||||
state.saveButtonText = isEditing ? "Save" : "Add";
|
||||
state.isPetIdVisible = isEditing;
|
||||
state.isDeleteVisible = isEditing;
|
||||
state.isSpeciesEnabled = !isEditing;
|
||||
state.isBreedEnabled = !isEditing;
|
||||
|
||||
if (isEditing) {
|
||||
state.isCustomerEnabled = true;
|
||||
state.isStoreEnabled = true;
|
||||
}
|
||||
|
||||
if (!isEditing) {
|
||||
selectedCustomerId = null;
|
||||
selectedStoreId = null;
|
||||
state.selectedCustomerId = null;
|
||||
state.selectedStoreId = null;
|
||||
state.selectedStatus = STATUS_AVAILABLE;
|
||||
state.isCustomerEnabled = false;
|
||||
state.isStoreEnabled = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public LiveData<Resource<PetDTO>> loadPet() {
|
||||
return petRepository.getPetById(petId);
|
||||
}
|
||||
MutableLiveData<Resource<PetDTO>> result = new MutableLiveData<>();
|
||||
petRepository.getPetById(petId).observeForever(resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
PetDTO pet = resource.data;
|
||||
selectedCustomerId = pet.getCustomerId();
|
||||
selectedStoreId = pet.getStoreId();
|
||||
|
||||
public LiveData<Resource<List<DropdownDTO>>> loadCustomers() {
|
||||
return customerRepository.getCustomerDropdowns();
|
||||
}
|
||||
updateViewState(state -> {
|
||||
state.selectedCustomerId = selectedCustomerId;
|
||||
state.selectedStoreId = selectedStoreId;
|
||||
state.selectedStatus = normalizeStatus(pet.getPetStatus());
|
||||
applyStatusRules(state, false);
|
||||
});
|
||||
}
|
||||
|
||||
public LiveData<Resource<List<DropdownDTO>>> loadStores() {
|
||||
return storeRepository.getStoreDropdowns();
|
||||
result.setValue(resource);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public LiveData<Resource<PetDTO>> savePet(PetDTO petDTO) {
|
||||
if (isEditing) {
|
||||
if (isEditing()) {
|
||||
petDTO.setPetId(petId);
|
||||
return petRepository.updatePet(petId, petDTO);
|
||||
} else {
|
||||
return petRepository.createPet(petDTO);
|
||||
}
|
||||
|
||||
return petRepository.createPet(petDTO);
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> deletePet() {
|
||||
return petRepository.deletePet(petId);
|
||||
}
|
||||
|
||||
public void setCustomerList(List<DropdownDTO> list) {
|
||||
customerList.setValue(list);
|
||||
}
|
||||
|
||||
public LiveData<List<DropdownDTO>> getCustomerList() {
|
||||
return customerList;
|
||||
}
|
||||
|
||||
public void setStoreList(List<DropdownDTO> list) {
|
||||
storeList.setValue(list);
|
||||
}
|
||||
|
||||
public LiveData<List<DropdownDTO>> getStoreList() {
|
||||
return storeList;
|
||||
}
|
||||
@@ -100,4 +181,66 @@ public class PetDetailViewModel extends ViewModel {
|
||||
public void setLoading(boolean loading) {
|
||||
isLoading.setValue(loading);
|
||||
}
|
||||
|
||||
private void applyStatusRules(ViewState state, boolean clearInvalidSelections) {
|
||||
if (STATUS_AVAILABLE.equalsIgnoreCase(state.selectedStatus)) {
|
||||
state.isCustomerEnabled = false;
|
||||
state.isStoreEnabled = true;
|
||||
if (clearInvalidSelections) {
|
||||
selectedCustomerId = null;
|
||||
state.selectedCustomerId = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (STATUS_OWNED.equalsIgnoreCase(state.selectedStatus)) {
|
||||
state.isCustomerEnabled = true;
|
||||
state.isStoreEnabled = false;
|
||||
if (clearInvalidSelections) {
|
||||
selectedStoreId = null;
|
||||
state.selectedStoreId = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
state.isCustomerEnabled = true;
|
||||
state.isStoreEnabled = true;
|
||||
}
|
||||
|
||||
private String normalizeStatus(String status) {
|
||||
if (status == null) return STATUS_AVAILABLE;
|
||||
String normalized = status.trim();
|
||||
if (STATUS_ADOPTED.equalsIgnoreCase(normalized)) return STATUS_ADOPTED;
|
||||
if (STATUS_OWNED.equalsIgnoreCase(normalized)) return STATUS_OWNED;
|
||||
return STATUS_AVAILABLE;
|
||||
}
|
||||
|
||||
private void updateViewState(Action<ViewState> action) {
|
||||
ViewState current = viewState.getValue();
|
||||
if (current != null) {
|
||||
action.run(current);
|
||||
viewState.setValue(current);
|
||||
}
|
||||
}
|
||||
|
||||
private interface Action<T> {
|
||||
void run(T target);
|
||||
}
|
||||
|
||||
|
||||
public static class ViewState {
|
||||
public boolean isEditing = false;
|
||||
public boolean isDeleteVisible = false;
|
||||
public boolean isPetIdVisible = false;
|
||||
public boolean isSpeciesEnabled = true;
|
||||
public boolean isBreedEnabled = true;
|
||||
public boolean isCustomerEnabled = false;
|
||||
public boolean isStoreEnabled = true;
|
||||
public String modeTitle = "Add Pet";
|
||||
public String saveButtonText = "Add";
|
||||
public String[] availableStatuses = new String[]{STATUS_AVAILABLE, STATUS_ADOPTED, STATUS_OWNED};
|
||||
public String selectedStatus = STATUS_AVAILABLE;
|
||||
public Long selectedCustomerId = null;
|
||||
public Long selectedStoreId = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.example.petstoremobile.viewmodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.example.petstoremobile.dtos.ServiceDTO;
|
||||
@@ -14,8 +15,9 @@ import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
@HiltViewModel
|
||||
public class ServiceDetailViewModel extends ViewModel {
|
||||
private final ServiceRepository repository;
|
||||
private final MutableLiveData<ViewState> viewState = new MutableLiveData<>(new ViewState());
|
||||
|
||||
private long serviceId = -1;
|
||||
private boolean isEditing = false;
|
||||
|
||||
@Inject
|
||||
public ServiceDetailViewModel(ServiceRepository repository) {
|
||||
@@ -24,7 +26,7 @@ public class ServiceDetailViewModel extends ViewModel {
|
||||
|
||||
public void setServiceId(long id) {
|
||||
this.serviceId = id;
|
||||
this.isEditing = id != -1;
|
||||
initMode(id != -1);
|
||||
}
|
||||
|
||||
public long getServiceId() {
|
||||
@@ -32,23 +34,88 @@ public class ServiceDetailViewModel extends ViewModel {
|
||||
}
|
||||
|
||||
public boolean isEditing() {
|
||||
return isEditing;
|
||||
ViewState current = viewState.getValue();
|
||||
return current != null && current.isEditing;
|
||||
}
|
||||
|
||||
public LiveData<ViewState> getViewState() {
|
||||
return viewState;
|
||||
}
|
||||
|
||||
public void initMode(boolean isEditing) {
|
||||
updateViewState(state -> {
|
||||
state.isEditing = isEditing;
|
||||
state.modeTitle = isEditing ? "Edit Service" : "Add Service";
|
||||
state.saveButtonText = isEditing ? "Save" : "Add";
|
||||
state.isServiceIdVisible = isEditing;
|
||||
state.isDeleteVisible = isEditing;
|
||||
state.isFieldsEnabled = true;
|
||||
});
|
||||
}
|
||||
|
||||
public LiveData<Resource<ServiceDTO>> loadService() {
|
||||
return repository.getServiceById(serviceId);
|
||||
MutableLiveData<Resource<ServiceDTO>> result = new MutableLiveData<>();
|
||||
repository.getServiceById(serviceId).observeForever(resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
ServiceDTO service = resource.data;
|
||||
updateViewState(state -> {
|
||||
state.serviceName = safeText(service.getServiceName());
|
||||
state.serviceDesc = safeText(service.getServiceDesc());
|
||||
state.serviceDuration = service.getServiceDuration() != null ? String.valueOf(service.getServiceDuration()) : "";
|
||||
state.servicePrice = service.getServicePrice() != null ? String.valueOf(service.getServicePrice()) : "";
|
||||
});
|
||||
}
|
||||
result.setValue(resource);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public LiveData<Resource<ServiceDTO>> saveService(ServiceDTO dto) {
|
||||
if (isEditing) {
|
||||
updateViewState(state -> {
|
||||
state.serviceName = safeText(dto.getServiceName());
|
||||
state.serviceDesc = safeText(dto.getServiceDesc());
|
||||
state.serviceDuration = dto.getServiceDuration() != null ? String.valueOf(dto.getServiceDuration()) : "";
|
||||
state.servicePrice = dto.getServicePrice() != null ? String.valueOf(dto.getServicePrice()) : "";
|
||||
});
|
||||
|
||||
if (isEditing()) {
|
||||
dto.setServiceId(serviceId);
|
||||
return repository.updateService(serviceId, dto);
|
||||
} else {
|
||||
return repository.createService(dto);
|
||||
}
|
||||
|
||||
return repository.createService(dto);
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> deleteService() {
|
||||
return repository.deleteService(serviceId);
|
||||
}
|
||||
|
||||
private String safeText(String value) {
|
||||
return value == null ? "" : value.trim();
|
||||
}
|
||||
|
||||
private void updateViewState(Action<ViewState> action) {
|
||||
ViewState current = viewState.getValue();
|
||||
if (current != null) {
|
||||
action.run(current);
|
||||
viewState.setValue(current);
|
||||
}
|
||||
}
|
||||
|
||||
private interface Action<T> {
|
||||
void run(T target);
|
||||
}
|
||||
|
||||
public static class ViewState {
|
||||
public boolean isEditing = false;
|
||||
public boolean isDeleteVisible = false;
|
||||
public boolean isServiceIdVisible = false;
|
||||
public boolean isFieldsEnabled = true;
|
||||
public String modeTitle = "Add Service";
|
||||
public String saveButtonText = "Add";
|
||||
public String serviceName = "";
|
||||
public String serviceDesc = "";
|
||||
public String serviceDuration = "";
|
||||
public String servicePrice = "";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user