changed petDetailFragment to support new backend

This commit is contained in:
Alex
2026-04-07 02:23:58 -06:00
parent 5cb625a710
commit 003c7ec58a
7 changed files with 269 additions and 4 deletions

View File

@@ -1,6 +1,9 @@
package com.example.petstoremobile.dtos; package com.example.petstoremobile.dtos;
import com.google.gson.annotations.SerializedName;
public class CustomerDTO { public class CustomerDTO {
@SerializedName("id")
private Long customerId; private Long customerId;
private String firstName; private String firstName;
private String lastName; private String lastName;
@@ -12,18 +15,34 @@ public class CustomerDTO {
return customerId; return customerId;
} }
public void setCustomerId(Long customerId) {
this.customerId = customerId;
}
public String getFirstName() { public String getFirstName() {
return firstName; return firstName;
} }
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() { public String getLastName() {
return lastName; return lastName;
} }
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() { public String getEmail() {
return email; return email;
} }
public void setEmail(String email) {
this.email = email;
}
public String getFullName() { public String getFullName() {
return firstName + " " + lastName; return firstName + " " + lastName;
} }
@@ -32,7 +51,15 @@ public class CustomerDTO {
return createdAt; return createdAt;
} }
public void setCreatedAt(String createdAt) {
this.createdAt = createdAt;
}
public String getUpdatedAt() { public String getUpdatedAt() {
return updatedAt; return updatedAt;
} }
}
public void setUpdatedAt(String updatedAt) {
this.updatedAt = updatedAt;
}
}

View File

@@ -11,18 +11,27 @@ import androidx.navigation.fragment.NavHostFragment;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.example.petstoremobile.R; import com.example.petstoremobile.R;
import com.example.petstoremobile.databinding.FragmentPetDetailBinding; import com.example.petstoremobile.databinding.FragmentPetDetailBinding;
import com.example.petstoremobile.dtos.CustomerDTO;
import com.example.petstoremobile.dtos.PetDTO; import com.example.petstoremobile.dtos.PetDTO;
import com.example.petstoremobile.dtos.StoreDTO;
import com.example.petstoremobile.utils.ActivityLogger; import com.example.petstoremobile.utils.ActivityLogger;
import com.example.petstoremobile.utils.DialogUtils; import com.example.petstoremobile.utils.DialogUtils;
import com.example.petstoremobile.utils.InputValidator; import com.example.petstoremobile.utils.InputValidator;
import com.example.petstoremobile.utils.Resource; import com.example.petstoremobile.utils.Resource;
import com.example.petstoremobile.utils.SpinnerUtils; import com.example.petstoremobile.utils.SpinnerUtils;
import com.example.petstoremobile.viewmodels.CustomerViewModel;
import com.example.petstoremobile.viewmodels.PetViewModel; import com.example.petstoremobile.viewmodels.PetViewModel;
import com.example.petstoremobile.viewmodels.StoreViewModel;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
@@ -38,11 +47,19 @@ public class PetDetailFragment extends Fragment {
private boolean isEditing = false; private boolean isEditing = false;
private PetViewModel viewModel; private PetViewModel viewModel;
private CustomerViewModel customerViewModel;
private StoreViewModel storeViewModel;
private List<CustomerDTO> customerList = new ArrayList<>();
private List<StoreDTO> storeList = new ArrayList<>();
private Long selectedCustomerId = null;
private Long selectedStoreId = null;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(PetViewModel.class); viewModel = new ViewModelProvider(this).get(PetViewModel.class);
customerViewModel = new ViewModelProvider(this).get(CustomerViewModel.class);
storeViewModel = new ViewModelProvider(this).get(StoreViewModel.class);
} }
@Override @Override
@@ -57,6 +74,8 @@ public class PetDetailFragment extends Fragment {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
setupSpinner(); setupSpinner();
loadCustomers();
loadStores();
handleArguments(); handleArguments();
//set button click listeners //set button click listeners
@@ -90,6 +109,36 @@ public class PetDetailFragment extends Fragment {
double price = Double.parseDouble(binding.etPetPrice.getText().toString().trim()); double price = Double.parseDouble(binding.etPetPrice.getText().toString().trim());
String status = binding.spinnerPetStatus.getSelectedItem().toString(); String status = binding.spinnerPetStatus.getSelectedItem().toString();
// Get selected customer
Long customerId = null;
int customerPos = binding.spinnerCustomer.getSelectedItemPosition();
if (customerPos > 0) { // 0 means no customer for pet
customerId = customerList.get(customerPos - 1).getCustomerId();
}
// Get selected store
Long storeId = null;
int storePos = binding.spinnerStore.getSelectedItemPosition();
if (storePos > 0) {
storeId = storeList.get(storePos - 1).getStoreId();
}
// Validation: If status is Available, a store must be selected
if ("Available".equalsIgnoreCase(status)) {
if (!InputValidator.isSpinnerSelected(binding.spinnerStore, "Store")) return;
}
// Validation: If status is Owned, an owner must be selected
if ("Owned".equalsIgnoreCase(status)) {
if (!InputValidator.isSpinnerSelected(binding.spinnerCustomer, "Owner")) return;
}
// Validation: If status is Adopted, an owner and store must be selected
if ("Adopted".equalsIgnoreCase(status)) {
if (!InputValidator.isSpinnerSelected(binding.spinnerCustomer, "Owner")) return;
if (!InputValidator.isSpinnerSelected(binding.spinnerStore, "Store")) return;
}
//create a pet object to send to the API //create a pet object to send to the API
PetDTO petDTO = new PetDTO(); PetDTO petDTO = new PetDTO();
petDTO.setPetName(name); petDTO.setPetName(name);
@@ -98,6 +147,8 @@ public class PetDetailFragment extends Fragment {
petDTO.setPetAge(age); petDTO.setPetAge(age);
petDTO.setPetPrice(price); petDTO.setPetPrice(price);
petDTO.setPetStatus(status); petDTO.setPetStatus(status);
petDTO.setCustomerId(customerId);
petDTO.setStoreId(storeId);
//check if the pet is being edited or added //check if the pet is being edited or added
if (isEditing) { if (isEditing) {
@@ -197,17 +248,118 @@ public class PetDetailFragment extends Fragment {
binding.etPetPrice.setText(String.format(Locale.getDefault(), "%.2f", p.getPetPrice())); binding.etPetPrice.setText(String.format(Locale.getDefault(), "%.2f", p.getPetPrice()));
} }
SpinnerUtils.setSelectionByValue(binding.spinnerPetStatus, p.getPetStatus()); SpinnerUtils.setSelectionByValue(binding.spinnerPetStatus, p.getPetStatus());
selectedCustomerId = p.getCustomerId();
updateCustomerSpinnerSelection();
selectedStoreId = p.getStoreId();
updateStoreSpinnerSelection();
} else if (resource.status == Resource.Status.ERROR) { } else if (resource.status == Resource.Status.ERROR) {
Toast.makeText(getContext(), "Failed to load pet: " + resource.message, Toast.LENGTH_SHORT).show(); Toast.makeText(getContext(), "Failed to load pet: " + resource.message, Toast.LENGTH_SHORT).show();
} }
}); });
} }
/**
* Fetches the list of customers and populates the spinner.
*/
private void loadCustomers() {
customerViewModel.getAllCustomers(0, 1000).observe(getViewLifecycleOwner(), resource -> {
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
customerList = resource.data.getContent();
updateCustomerSpinnerSelection();
}
});
}
/**
* Fetches the list of stores and populates the spinner.
*/
private void loadStores() {
storeViewModel.getAllStores(0, 1000).observe(getViewLifecycleOwner(), resource -> {
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
storeList = resource.data.getContent();
updateStoreSpinnerSelection();
}
});
}
/**
* Updates the customer spinner with the current list and sets the selection if needed.
*/
private void updateCustomerSpinnerSelection() {
SpinnerUtils.populateSpinner(
requireContext(),
binding.spinnerCustomer,
customerList,
CustomerDTO::getFullName,
"No Owner",
selectedCustomerId,
CustomerDTO::getCustomerId
);
}
/**
* Updates the store spinner with the current list and sets the selection if needed.
*/
private void updateStoreSpinnerSelection() {
SpinnerUtils.populateSpinner(
requireContext(),
binding.spinnerStore,
storeList,
StoreDTO::getStoreName,
"None",
selectedStoreId,
StoreDTO::getStoreId
);
}
/** /**
* Initializes the spinner for pet status selection. * Initializes the spinner for pet status selection.
*/ */
private void setupSpinner() { private void setupSpinner() {
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerPetStatus, SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerPetStatus,
new String[]{"Available", "Adopted"}); new String[]{"Available", "Adopted", "Owned"});
binding.spinnerPetStatus.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String status = parent.getItemAtPosition(position).toString();
// Clear any existing error icons when status changes
clearSpinnerError(binding.spinnerCustomer);
clearSpinnerError(binding.spinnerStore);
//Disable the customer spinner if the status is "Available"
if ("Available".equalsIgnoreCase(status)) {
binding.spinnerCustomer.setSelection(0);
binding.spinnerCustomer.setEnabled(false);
} else {
binding.spinnerCustomer.setEnabled(true);
}
//Disable the store spinner if the status is "Owned"
if ("Owned".equalsIgnoreCase(status)) {
binding.spinnerStore.setSelection(0);
binding.spinnerStore.setEnabled(false);
} else {
binding.spinnerStore.setEnabled(true);
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
/**
* Clears error messages from a Spinner's selected view.
*/
private void clearSpinnerError(Spinner spinner) {
View selectedView = spinner.getSelectedView();
if (selectedView instanceof TextView) {
((TextView) selectedView).setError(null);
}
} }
} }

View File

@@ -130,6 +130,13 @@ public class PetProfileFragment extends Fragment {
} else { } else {
binding.tvPetPrice.setText("$0.00"); binding.tvPetPrice.setText("$0.00");
} }
// Display owner name if available, otherwise show No Owner
if (pet.getCustomerName() != null && !pet.getCustomerName().isEmpty()) {
binding.tvPetOwner.setText(pet.getCustomerName());
} else {
binding.tvPetOwner.setText("No Owner");
}
} else if (resource.status == Resource.Status.ERROR) { } else if (resource.status == Resource.Status.ERROR) {
Toast.makeText(getContext(), "Failed to load pet data: " + resource.message, Toast.LENGTH_SHORT).show(); Toast.makeText(getContext(), "Failed to load pet data: " + resource.message, Toast.LENGTH_SHORT).show();
} }

View File

@@ -1,6 +1,9 @@
package com.example.petstoremobile.utils; package com.example.petstoremobile.utils;
import android.view.View;
import android.widget.EditText; import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
public class InputValidator { public class InputValidator {
@@ -94,4 +97,21 @@ public class InputValidator {
} }
return true; return true;
} }
/**
* Checks if a selection has been made in a Spinner.
* Assumes position 0 is a placeholder like "None" or "Select".
*/
public static boolean isSpinnerSelected(Spinner spinner, String fieldName) {
if (spinner.getSelectedItemPosition() <= 0) {
View selectedView = spinner.getSelectedView();
if (selectedView instanceof TextView) {
TextView tv = (TextView) selectedView;
tv.setError(fieldName + " is required");
spinner.requestFocus();
}
return false;
}
return true;
}
} }

View File

@@ -8,6 +8,7 @@ import com.example.petstoremobile.adapters.BlackTextArrayAdapter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
/** /**
@@ -36,7 +37,8 @@ public class SpinnerUtils {
if (preselectedId != null && preselectedId != -1) { if (preselectedId != null && preselectedId != -1) {
int offset = (defaultText != null) ? 1 : 0; int offset = (defaultText != null) ? 1 : 0;
for (int i = 0; i < data.size(); i++) { for (int i = 0; i < data.size(); i++) {
if (idExtractor.apply(data.get(i)).equals(preselectedId)) { Long currentId = idExtractor.apply(data.get(i));
if (Objects.equals(currentId, preselectedId)) {
spinner.setSelection(i + offset); spinner.setSelection(i + offset);
break; break;
} }

View File

@@ -161,6 +161,34 @@
<Spinner <Spinner
android:id="@+id/spinnerPetStatus" android:id="@+id/spinnerPetStatus"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Owner"
android:textColor="@color/text_dark"
android:textSize="12sp"
android:layout_marginBottom="4dp"/>
<Spinner
android:id="@+id/spinnerCustomer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Store"
android:textColor="@color/text_dark"
android:textSize="12sp"
android:layout_marginBottom="4dp"/>
<Spinner
android:id="@+id/spinnerStore"
android:layout_width="match_parent"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"/>
</LinearLayout> </LinearLayout>

View File

@@ -221,6 +221,35 @@
</LinearLayout> </LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
android:background="@color/white"
android:layout_marginTop="16dp"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="OWNER"
android:textSize="11sp"
android:textColor="#888888"
android:textAllCaps="true"
android:layout_marginBottom="4dp"/>
<TextView
android:id="@+id/tvPetOwner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="No Owner"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="@color/text_dark"/>
</LinearLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
@@ -241,4 +270,4 @@
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>