diff --git a/android/app/src/main/java/com/example/petstoremobile/adapters/AdoptionAdapter.java b/android/app/src/main/java/com/example/petstoremobile/adapters/AdoptionAdapter.java index 98e88256..c6bd678c 100644 --- a/android/app/src/main/java/com/example/petstoremobile/adapters/AdoptionAdapter.java +++ b/android/app/src/main/java/com/example/petstoremobile/adapters/AdoptionAdapter.java @@ -1,79 +1,79 @@ package com.example.petstoremobile.adapters; - import android.graphics.Color; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; +import android.view.*; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.example.petstoremobile.R; -import com.example.petstoremobile.models.Adoption; +import com.example.petstoremobile.dtos.AdoptionDTO; import java.util.List; public class AdoptionAdapter extends RecyclerView.Adapter { - private List adoptionList; - private OnAdoptionClickListener adoptionClickListener; + private List adoptionList; + private OnAdoptionClickListener listener; - // Interface for adoption click on recycler view public interface OnAdoptionClickListener { void onAdoptionClick(int position); } - // Constructor - public AdoptionAdapter(List adoptionList, OnAdoptionClickListener adoptionClickListener) { + public AdoptionAdapter(List adoptionList, OnAdoptionClickListener listener) { this.adoptionList = adoptionList; - this.adoptionClickListener = adoptionClickListener; + this.listener = listener; } - // Get the controls of each row in recycler view public static class AdoptionViewHolder extends RecyclerView.ViewHolder { - TextView tvAdopterName, tvPetName, tvAdoptionDate, tvAdoptionStatus; + TextView tvCustomerName, tvPetName, tvDate, tvFee, tvStatus; public AdoptionViewHolder(@NonNull View v) { super(v); - tvAdopterName = v.findViewById(R.id.tvAdopterName); - tvPetName = v.findViewById(R.id.tvAdoptionPetName); - tvAdoptionDate = v.findViewById(R.id.tvAdoptionDate); - tvAdoptionStatus = v.findViewById(R.id.tvAdoptionStatus); + tvCustomerName = v.findViewById(R.id.tvAdoptionCustomerName); + tvPetName = v.findViewById(R.id.tvAdoptionPetName); + tvDate = v.findViewById(R.id.tvAdoptionDate); + tvFee = v.findViewById(R.id.tvAdoptionFee); + tvStatus = v.findViewById(R.id.tvAdoptionStatus); } } - // Create a new row view @NonNull @Override public AdoptionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_adoption, parent, false); + View v = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_adoption, parent, false); return new AdoptionViewHolder(v); } - // Populate the row with adoption data @Override public void onBindViewHolder(@NonNull AdoptionViewHolder holder, int position) { - Adoption adoption = adoptionList.get(position); + AdoptionDTO a = adoptionList.get(position); - holder.tvAdopterName.setText(adoption.getAdopterName()); - holder.tvPetName.setText("Pet: " + adoption.getPetName()); - holder.tvAdoptionDate.setText("Date: " + adoption.getAdoptionDate()); - holder.tvAdoptionStatus.setText(adoption.getStatus()); + holder.tvCustomerName.setText(a.getCustomerName() != null ? a.getCustomerName() : ""); + holder.tvPetName.setText("Pet: " + (a.getPetName() != null ? a.getPetName() : "")); + holder.tvDate.setText("Date: " + (a.getAdoptionDate() != null ? a.getAdoptionDate() : "")); + holder.tvFee.setText(a.getAdoptionFee() != null ? "$" + a.getAdoptionFee() : ""); - // Set the status color depending on adoption status - if (adoption.getStatus().equals("Approved")) { - holder.tvAdoptionStatus.setBackgroundColor(Color.parseColor("#4CAF50")); - } else if (adoption.getStatus().equals("Pending")) { - holder.tvAdoptionStatus.setBackgroundColor(Color.parseColor("#FF9800")); - } else { - holder.tvAdoptionStatus.setBackgroundColor(Color.parseColor("#F44336")); + String status = a.getAdoptionStatus() != null ? a.getAdoptionStatus() : ""; + holder.tvStatus.setText(status); + + switch (status) { + case "Approved": + holder.tvStatus.setBackgroundColor(Color.parseColor("#4CAF50")); + break; + case "Pending": + holder.tvStatus.setBackgroundColor(Color.parseColor("#FF9800")); + break; + case "Rejected": + holder.tvStatus.setBackgroundColor(Color.parseColor("#F44336")); + break; + default: + holder.tvStatus.setBackgroundColor(Color.parseColor("#9E9E9E")); + break; } - // When a row is clicked, open the detail view - holder.itemView.setOnClickListener(v -> adoptionClickListener.onAdoptionClick(position)); + holder.itemView.setOnClickListener(v -> listener.onAdoptionClick(position)); } @Override - public int getItemCount() { - return adoptionList.size(); - } -} + public int getItemCount() { return adoptionList.size(); } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/example/petstoremobile/api/AdoptionApi.java b/android/app/src/main/java/com/example/petstoremobile/api/AdoptionApi.java new file mode 100644 index 00000000..2f704a41 --- /dev/null +++ b/android/app/src/main/java/com/example/petstoremobile/api/AdoptionApi.java @@ -0,0 +1,34 @@ +package com.example.petstoremobile.api; + +import com.example.petstoremobile.dtos.AdoptionDTO; +import com.example.petstoremobile.dtos.PageResponse; + +import retrofit2.Call; +import retrofit2.http.Body; +import retrofit2.http.DELETE; +import retrofit2.http.GET; +import retrofit2.http.POST; +import retrofit2.http.PUT; +import retrofit2.http.Path; +import retrofit2.http.Query; + +public interface AdoptionApi { + + @GET("api/v1/adoptions") + Call> getAllAdoptions( + @Query("page") int page, + @Query("size") int size); + + @GET("api/v1/adoptions/{id}") + Call getAdoptionById(@Path("id") Long id); + + @POST("api/v1/adoptions") + Call createAdoption(@Body AdoptionDTO adoption); + + @PUT("api/v1/adoptions/{id}") + Call updateAdoption(@Path("id") Long id, @Body AdoptionDTO adoption); + + @DELETE("api/v1/adoptions/{id}") + Call deleteAdoption(@Path("id") Long id); +} + diff --git a/android/app/src/main/java/com/example/petstoremobile/dtos/AdoptionDTO.java b/android/app/src/main/java/com/example/petstoremobile/dtos/AdoptionDTO.java new file mode 100644 index 00000000..03758473 --- /dev/null +++ b/android/app/src/main/java/com/example/petstoremobile/dtos/AdoptionDTO.java @@ -0,0 +1,68 @@ +package com.example.petstoremobile.dtos; + +import java.math.BigDecimal; + +public class AdoptionDTO { + private Long adoptionId; + private Long petId; + private String petName; + private Long customerId; + private String customerName; + private String adoptionDate; + private String adoptionStatus; + private BigDecimal adoptionFee; + private String createdAt; + private String updatedAt; + + // Constructor for create/update requests + public AdoptionDTO(Long petId, Long customerId, String adoptionDate, String adoptionStatus) { + this.petId = petId; + this.customerId = customerId; + this.adoptionDate = adoptionDate; + this.adoptionStatus = adoptionStatus; + } + + public Long getAdoptionId() { + return adoptionId; + } + + public Long getPetId() { + return petId; + } + + public String getPetName() { + return petName; + } + + public Long getCustomerId() { + return customerId; + } + + public String getCustomerName() { + return customerName; + } + + public String getAdoptionDate() { + return adoptionDate; + } + + public String getAdoptionStatus() { + return adoptionStatus; + } + + public String getStatus() { + return adoptionStatus; + } + + public BigDecimal getAdoptionFee() { + return adoptionFee; + } + + public String getCreatedAt() { + return createdAt; + } + + public String getUpdatedAt() { + return updatedAt; + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AdoptionFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AdoptionFragment.java index f72d6836..34e7d602 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AdoptionFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AdoptionFragment.java @@ -1,36 +1,33 @@ package com.example.petstoremobile.fragments.listfragments; -// Added search/filter bar to filter adoptions by adopter name or pet name. -// Added pull-to-refresh using SwipeRefreshLayout. - import android.os.Bundle; +import android.text.*; +import android.util.Log; +import android.view.*; +import android.widget.*; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; -import android.text.Editable; -import android.text.TextWatcher; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.EditText; -import android.widget.ImageButton; - import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.AdoptionAdapter; +import com.example.petstoremobile.api.AdoptionApi; +import com.example.petstoremobile.api.RetrofitClient; +import com.example.petstoremobile.dtos.AdoptionDTO; +import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.fragments.ListFragment; import com.example.petstoremobile.fragments.listfragments.detailfragments.AdoptionDetailFragment; -import com.example.petstoremobile.models.Adoption; import com.google.android.material.floatingactionbutton.FloatingActionButton; -import java.util.ArrayList; -import java.util.List; +import java.util.*; +import retrofit2.*; public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdoptionClickListener { - private List adoptionList = new ArrayList<>(); - private List filteredList = new ArrayList<>(); + private List adoptionList = new ArrayList<>(); + private List filteredList = new ArrayList<>(); private AdoptionAdapter adapter; - private SwipeRefreshLayout swipeRefreshLayout; + private AdoptionApi api; + private SwipeRefreshLayout swipeRefresh; private EditText etSearch; private ImageButton hamburger; @@ -39,51 +36,58 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_adoption, container, false); - hamburger = view.findViewById(R.id.btnHamburger); + api = RetrofitClient.getAdoptionApi(requireContext()); + hamburger = view.findViewById(R.id.btnHamburgerAdoption); - loadAdoptionData(); - // Replace with actual API call when backend is ready setupRecyclerView(view); setupSearch(view); setupSwipeRefresh(view); + loadAdoptions(); - FloatingActionButton fabAddAdoption = view.findViewById(R.id.fabAddAdoption); - fabAddAdoption.setOnClickListener(v -> openAdoptionDetails(-1)); + FloatingActionButton fab = view.findViewById(R.id.fabAddAdoption); + fab.setOnClickListener(v -> openDetail(-1)); - //Make the hamburger button open the drawer from listFragment hamburger.setOnClickListener(v -> { - ListFragment listFragment = (ListFragment) getParentFragment(); - //if list fragment is found then use its helper function to open the drawer - if (listFragment != null) { - listFragment.openDrawer(); - } + ListFragment lf = (ListFragment) getParentFragment(); + if (lf != null) lf.openDrawer(); }); return view; } - // Filters adoption list by adopter name or pet name + private void setupRecyclerView(View view) { + RecyclerView rv = view.findViewById(R.id.recyclerViewAdoptions); + adapter = new AdoptionAdapter(filteredList, this); + rv.setLayoutManager(new LinearLayoutManager(getContext())); + rv.setAdapter(adapter); + } + private void setupSearch(View view) { etSearch = view.findViewById(R.id.etSearchAdoption); etSearch.addTextChangedListener(new TextWatcher() { - @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} - @Override public void onTextChanged(CharSequence s, int start, int before, int count) { - filterAdoptions(s.toString()); + public void beforeTextChanged(CharSequence s, int a, int b, int c) {} + public void afterTextChanged(Editable s) {} + public void onTextChanged(CharSequence s, int a, int b, int c) { + filter(s.toString()); } - @Override public void afterTextChanged(Editable s) {} }); } - private void filterAdoptions(String query) { + private void setupSwipeRefresh(View view) { + swipeRefresh = view.findViewById(R.id.swipeRefreshAdoption); + swipeRefresh.setOnRefreshListener(this::loadAdoptions); + } + + private void filter(String query) { filteredList.clear(); if (query.isEmpty()) { filteredList.addAll(adoptionList); } else { String lower = query.toLowerCase(); - for (Adoption a : adoptionList) { - if (a.getAdopterName().toLowerCase().contains(lower) - || a.getPetName().toLowerCase().contains(lower) - || a.getStatus().toLowerCase().contains(lower)) { + for (AdoptionDTO a : adoptionList) { + if ((a.getCustomerName() != null && a.getCustomerName().toLowerCase().contains(lower)) + || (a.getPetName() != null && a.getPetName().toLowerCase().contains(lower)) + || (a.getAdoptionStatus() != null && a.getAdoptionStatus().toLowerCase().contains(lower))) { filteredList.add(a); } } @@ -91,73 +95,47 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop adapter.notifyDataSetChanged(); } - private void setupSwipeRefresh(View view) { - swipeRefreshLayout = view.findViewById(R.id.swipeRefreshAdoption); - swipeRefreshLayout.setOnRefreshListener(() -> { - loadAdoptionData(); // TODO: Replace with actual API call - filterAdoptions(etSearch.getText().toString()); - swipeRefreshLayout.setRefreshing(false); + private void loadAdoptions() { + if (swipeRefresh != null) swipeRefresh.setRefreshing(true); + api.getAllAdoptions(0, 100).enqueue(new Callback>() { + public void onResponse(Call> c, + Response> r) { + if (swipeRefresh != null) swipeRefresh.setRefreshing(false); + if (r.isSuccessful() && r.body() != null) { + adoptionList.clear(); + adoptionList.addAll(r.body().getContent()); + filter(etSearch != null ? etSearch.getText().toString() : ""); + } else { + Toast.makeText(getContext(), "Failed to load adoptions", Toast.LENGTH_SHORT).show(); + Log.e("AdoptionFragment", "Error: " + r.message()); + } + } + public void onFailure(Call> c, Throwable t) { + if (swipeRefresh != null) swipeRefresh.setRefreshing(false); + Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show(); + Log.e("AdoptionFragment", t.getMessage()); + } }); } - private void openAdoptionDetails(int position) { - AdoptionDetailFragment detailFragment = new AdoptionDetailFragment(); + private void openDetail(int position) { + AdoptionDetailFragment detail = new AdoptionDetailFragment(); Bundle args = new Bundle(); - args.putInt("position", position); if (position != -1) { - Adoption adoption = filteredList.get(position); - int realPosition = adoptionList.indexOf(adoption); - args.putInt("position", realPosition); - args.putInt("adoptionId", adoption.getAdoptionId()); - args.putString("adopterName", adoption.getAdopterName()); - args.putString("adopterEmail", adoption.getAdopterEmail()); - args.putString("adopterPhone", adoption.getAdopterPhone()); - args.putString("petName", adoption.getPetName()); - args.putString("adoptionDate", adoption.getAdoptionDate()); - args.putString("status", adoption.getStatus()); + AdoptionDTO a = filteredList.get(position); + args.putLong("adoptionId", a.getAdoptionId()); + args.putLong("petId", a.getPetId() != null ? a.getPetId() : -1); + args.putLong("customerId", a.getCustomerId() != null ? a.getCustomerId() : -1); + args.putString("adoptionDate", a.getAdoptionDate()); + args.putString("adoptionStatus", a.getAdoptionStatus()); } - detailFragment.setArguments(args); - detailFragment.setAdoptionFragment(this); - - ListFragment listFragment = (ListFragment) getParentFragment(); - if (listFragment != null) listFragment.loadFragment(detailFragment); - } - - public void onAdoptionSaved(int position, Adoption adoption) { - if (position == -1) { - adoptionList.add(adoption); - } else { - adoptionList.set(position, adoption); - } - filterAdoptions(etSearch.getText().toString()); - } - - public void onAdoptionDeleted(int position) { - adoptionList.remove(position); - filterAdoptions(etSearch.getText().toString()); + detail.setArguments(args); + ListFragment lf = (ListFragment) getParentFragment(); + if (lf != null) lf.loadFragment(detail); } @Override - public void onAdoptionClick(int position) { - openAdoptionDetails(position); - } - - private void loadAdoptionData() { - adoptionList.clear(); - adoptionList.add(new Adoption(1, "Sarah Connor", "sarah@email.com", "555-1234", "Luna", "2026-03-01", "Approved")); - adoptionList.add(new Adoption(2, "Tom Hardy", "tom@email.com", "555-5678", "Bella", "2026-03-05", "Pending")); - adoptionList.add(new Adoption(3, "Emily Clark", "emily@email.com", "555-9012", "Charlie", "2026-03-07", "Pending")); - adoptionList.add(new Adoption(4, "Mike Ross", "mike@email.com", "555-3456", "Milo", "2026-02-20", "Rejected")); - filteredList.clear(); - filteredList.addAll(adoptionList); - } - - private void setupRecyclerView(View view) { - RecyclerView recyclerView = view.findViewById(R.id.recyclerViewAdoptions); - adapter = new AdoptionAdapter(filteredList, this); - recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); - recyclerView.setAdapter(adapter); - } -} + public void onAdoptionClick(int position) { openDetail(position); } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AdoptionDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AdoptionDetailFragment.java index 32c06996..c32edaf0 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AdoptionDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AdoptionDetailFragment.java @@ -1,172 +1,257 @@ package com.example.petstoremobile.fragments.listfragments.detailfragments; - -// Uses InputValidator for detailed field validation and ActivityLogger to log all changes. - +import android.app.DatePickerDialog; import android.os.Bundle; - +import android.util.Log; +import android.view.*; +import android.widget.*; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; +import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.EditText; -import android.widget.Spinner; -import android.widget.TextView; -import android.widget.Toast; import com.example.petstoremobile.R; +import com.example.petstoremobile.api.*; +import com.example.petstoremobile.dtos.*; import com.example.petstoremobile.fragments.ListFragment; -import com.example.petstoremobile.fragments.listfragments.AdoptionFragment; -import com.example.petstoremobile.models.Adoption; -import com.example.petstoremobile.utils.ActivityLogger; -import com.example.petstoremobile.utils.InputValidator; +import java.util.*; +import retrofit2.*; public class AdoptionDetailFragment extends Fragment { private TextView tvMode, tvAdoptionId; - private EditText etAdopterName, etAdopterEmail, etAdopterPhone, etPetName, etAdoptionDate; - private Spinner spinnerAdoptionStatus; - private Button btnSaveAdoption, btnDeleteAdoption, btnBack; - private int adoptionId; - private int position; - private boolean isEditing = false; - private AdoptionFragment adoptionFragment; + private EditText etAdoptionDate; + private Spinner spinnerPet, spinnerCustomer, spinnerStatus; + private Button btnSave, btnDelete, btnBack; - // Set the adoption fragment as parent so we refer back when save or delete is done - public void setAdoptionFragment(AdoptionFragment fragment) { - this.adoptionFragment = fragment; - } + private long adoptionId = -1; + private boolean isEditing = false; + private long preselectedPetId = -1; + private long preselectedCustomerId = -1; + + private List petList = new ArrayList<>(); + private List customerList = new ArrayList<>(); + + private final String[] STATUSES = {"Pending", "Approved", "Rejected"}; @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_adoption_detail, container, false); - initViews(view); - setupSpinner(); + setupSpinners(); + setupDatePicker(); + loadData(); handleArguments(); - btnBack.setOnClickListener(v -> { - ListFragment listFragment = (ListFragment) getParentFragment(); - if (listFragment != null) listFragment.getChildFragmentManager().popBackStack(); - }); - btnSaveAdoption.setOnClickListener(v -> saveAdoption()); - btnDeleteAdoption.setOnClickListener(v -> deleteAdoption()); - + btnBack.setOnClickListener(v -> navigateBack()); + btnSave.setOnClickListener(v -> saveAdoption()); + btnDelete.setOnClickListener(v -> confirmDelete()); return view; } - // Validates all fields using InputValidator, then saves the adoption record - private void saveAdoption() { - if (!InputValidator.isNotEmpty(etAdopterName, "Adopter Name")) return; - if (!InputValidator.isValidEmail(etAdopterEmail)) return; - if (!InputValidator.isValidPhone(etAdopterPhone)) return; - if (!InputValidator.isNotEmpty(etPetName, "Pet Name")) return; - if (!InputValidator.isValidDate(etAdoptionDate)) return; + private void initViews(View v) { + tvMode = v.findViewById(R.id.tvAdoptionMode); + tvAdoptionId = v.findViewById(R.id.tvAdoptionId); + etAdoptionDate = v.findViewById(R.id.etAdoptionDate); + spinnerPet = v.findViewById(R.id.spinnerAdoptionPet); + spinnerCustomer= v.findViewById(R.id.spinnerAdoptionCustomer); + spinnerStatus = v.findViewById(R.id.spinnerAdoptionStatus); + btnSave = v.findViewById(R.id.btnSaveAdoption); + btnDelete = v.findViewById(R.id.btnDeleteAdoption); + btnBack = v.findViewById(R.id.btnAdoptionBack); + } - String adopterName = etAdopterName.getText().toString().trim(); - String adopterEmail = etAdopterEmail.getText().toString().trim(); - String adopterPhone = etAdopterPhone.getText().toString().trim(); - String petName = etPetName.getText().toString().trim(); - String adoptionDate = etAdoptionDate.getText().toString().trim(); - String status = spinnerAdoptionStatus.getSelectedItem().toString(); + private void setupSpinners() { + spinnerStatus.setAdapter(new ArrayAdapter<>(requireContext(), + android.R.layout.simple_spinner_item, STATUSES)); + } - try { - if (isEditing) { - // TODO: Replace with actual API PUT call when backend is ready - Adoption updated = new Adoption(adoptionId, adopterName, adopterEmail, adopterPhone, petName, adoptionDate, status); - if (adoptionFragment != null) adoptionFragment.onAdoptionSaved(position, updated); - ActivityLogger.logChange(requireContext(), "Adoption", "UPDATED", adoptionId); - Toast.makeText(getContext(), "Adoption record updated.", Toast.LENGTH_SHORT).show(); - } else { - // TODO: Replace with actual API POST call when backend is ready - Adoption newAdoption = new Adoption(0, adopterName, adopterEmail, adopterPhone, petName, adoptionDate, status); - if (adoptionFragment != null) adoptionFragment.onAdoptionSaved(-1, newAdoption); - ActivityLogger.log(requireContext(), "Added new Adoption record for: " + adopterName + " adopting " + petName); - Toast.makeText(getContext(), "Adoption record added.", Toast.LENGTH_SHORT).show(); + private void setupDatePicker() { + etAdoptionDate.setOnClickListener(v -> { + Calendar c = Calendar.getInstance(); + new DatePickerDialog(requireContext(), + (dp, y, m, d) -> etAdoptionDate.setText( + String.format("%04d-%02d-%02d", y, m + 1, d)), + c.get(Calendar.YEAR), + c.get(Calendar.MONTH), + c.get(Calendar.DAY_OF_MONTH)).show(); + }); + } + + private void loadData() { + loadPets(); + loadCustomers(); + } + + private void loadPets() { + RetrofitClient.getPetApi(requireContext()).getAllPets(0, 200) + .enqueue(new Callback>() { + public void onResponse(Call> c, + Response> r) { + if (r.isSuccessful() && r.body() != null) { + petList = r.body().getContent(); + populatePetSpinner(); + } + } + public void onFailure(Call> c, Throwable t) { + Log.e("ADOPTION", "Pet load failed: " + t.getMessage()); + } + }); + } + + private void populatePetSpinner() { + List names = new ArrayList<>(); + names.add("-- Select Pet --"); + for (PetDTO p : petList) names.add(p.getPetName()); + spinnerPet.setAdapter(new ArrayAdapter<>(requireContext(), + android.R.layout.simple_spinner_item, names)); + if (preselectedPetId != -1) { + for (int i = 0; i < petList.size(); i++) { + if (petList.get(i).getPetId().equals(preselectedPetId)) { + spinnerPet.setSelection(i + 1); break; + } } - ListFragment listFragment = (ListFragment) getParentFragment(); - if (listFragment != null) listFragment.getChildFragmentManager().popBackStack(); - } catch (Exception e) { - ActivityLogger.logException(requireContext(), "AdoptionDetailFragment.saveAdoption", e); - Toast.makeText(getContext(), "Error saving adoption record.", Toast.LENGTH_SHORT).show(); } } - // Deletes the adoption record and logs the action - private void deleteAdoption() { - try { - // TODO: Replace with actual API DELETE call when backend is ready - if (adoptionFragment != null) adoptionFragment.onAdoptionDeleted(position); - ActivityLogger.logChange(requireContext(), "Adoption", "DELETED", adoptionId); - Toast.makeText(getContext(), "Adoption record deleted.", Toast.LENGTH_SHORT).show(); - ListFragment listFragment = (ListFragment) getParentFragment(); - if (listFragment != null) listFragment.getChildFragmentManager().popBackStack(); - } catch (Exception e) { - ActivityLogger.logException(requireContext(), "AdoptionDetailFragment.deleteAdoption", e); - Toast.makeText(getContext(), "Error deleting adoption record.", Toast.LENGTH_SHORT).show(); + private void loadCustomers() { + RetrofitClient.getCustomerApi(requireContext()).getAllCustomers(0, 200) + .enqueue(new Callback>() { + public void onResponse(Call> c, + Response> r) { + if (r.isSuccessful() && r.body() != null) { + customerList = r.body().getContent(); + populateCustomerSpinner(); + } + } + public void onFailure(Call> c, Throwable t) { + Log.e("ADOPTION", "Customer load failed: " + t.getMessage()); + } + }); + } + + private void populateCustomerSpinner() { + List names = new ArrayList<>(); + names.add("-- Select Customer --"); + for (CustomerDTO c : customerList) + names.add(c.getFirstName() + " " + c.getLastName()); + spinnerCustomer.setAdapter(new ArrayAdapter<>(requireContext(), + android.R.layout.simple_spinner_item, names)); + if (preselectedCustomerId != -1) { + for (int i = 0; i < customerList.size(); i++) { + if (customerList.get(i).getCustomerId().equals(preselectedCustomerId)) { + spinnerCustomer.setSelection(i + 1); break; + } + } } } private void handleArguments() { - if (getArguments() != null && getArguments().containsKey("adoptionId")) { + Bundle a = getArguments(); + if (a != null && a.containsKey("adoptionId")) { isEditing = true; - adoptionId = getArguments().getInt("adoptionId"); - position = getArguments().getInt("position"); + adoptionId = a.getLong("adoptionId"); + preselectedPetId = a.getLong("petId", -1); + preselectedCustomerId = a.getLong("customerId", -1); + tvMode.setText("Edit Adoption"); tvAdoptionId.setText("ID: " + adoptionId); - etAdopterName.setText(getArguments().getString("adopterName")); - etAdopterEmail.setText(getArguments().getString("adopterEmail")); - etAdopterPhone.setText(getArguments().getString("adopterPhone")); - etPetName.setText(getArguments().getString("petName")); - etAdoptionDate.setText(getArguments().getString("adoptionDate")); - String status = getArguments().getString("status"); - if ("Approved".equals(status)) spinnerAdoptionStatus.setSelection(0); - else if ("Pending".equals(status)) spinnerAdoptionStatus.setSelection(1); - else spinnerAdoptionStatus.setSelection(2); - btnDeleteAdoption.setVisibility(View.VISIBLE); + tvAdoptionId.setVisibility(View.VISIBLE); + etAdoptionDate.setText(a.getString("adoptionDate")); + btnDelete.setVisibility(View.VISIBLE); + + // Pre-fill status + String status = a.getString("adoptionStatus", "Pending"); + for (int i = 0; i < STATUSES.length; i++) { + if (STATUSES[i].equals(status)) { + spinnerStatus.setSelection(i); break; + } + } } else { - isEditing = false; tvMode.setText("Add Adoption"); + btnDelete.setVisibility(View.GONE); tvAdoptionId.setVisibility(View.GONE); - btnDeleteAdoption.setVisibility(View.GONE); - btnSaveAdoption.setText("Add"); } } - private void initViews(View view) { - tvMode = view.findViewById(R.id.tvAdoptionMode); - tvAdoptionId = view.findViewById(R.id.tvAdoptionId); - etAdopterName = view.findViewById(R.id.etAdopterName); - etAdopterEmail = view.findViewById(R.id.etAdopterEmail); - etAdopterPhone = view.findViewById(R.id.etAdopterPhone); - etPetName = view.findViewById(R.id.etAdoptionPetName); - etAdoptionDate = view.findViewById(R.id.etAdoptionDate); - spinnerAdoptionStatus = view.findViewById(R.id.spinnerAdoptionStatus); - btnSaveAdoption = view.findViewById(R.id.btnSaveAdoption); - btnDeleteAdoption = view.findViewById(R.id.btnDeleteAdoption); - btnBack = view.findViewById(R.id.btnAdoptionBack); + private void saveAdoption() { + if (spinnerCustomer.getSelectedItemPosition() == 0) { + Toast.makeText(getContext(), "Select a customer", Toast.LENGTH_SHORT).show(); return; + } + if (spinnerPet.getSelectedItemPosition() == 0) { + Toast.makeText(getContext(), "Select a pet", Toast.LENGTH_SHORT).show(); return; + } + String date = etAdoptionDate.getText().toString().trim(); + if (date.isEmpty()) { + Toast.makeText(getContext(), "Select a date", Toast.LENGTH_SHORT).show(); return; + } + + CustomerDTO customer = customerList.get(spinnerCustomer.getSelectedItemPosition() - 1); + PetDTO pet = petList.get(spinnerPet.getSelectedItemPosition() - 1); + String status = STATUSES[spinnerStatus.getSelectedItemPosition()]; + + AdoptionDTO dto = new AdoptionDTO( + pet.getPetId(), + customer.getCustomerId(), + date, + status + ); + + Log.d("ADOPTION_SAVE", "petId=" + pet.getPetId() + + " customerId=" + customer.getCustomerId() + + " date=" + date + " status=" + status); + + AdoptionApi api = RetrofitClient.getAdoptionApi(requireContext()); + if (isEditing) { + api.updateAdoption(adoptionId, dto).enqueue(simpleCallback("Updated")); + } else { + api.createAdoption(dto).enqueue(simpleCallback("Saved")); + } } - private void setupSpinner() { - ArrayAdapter adapter = new ArrayAdapter(requireContext(), - android.R.layout.simple_spinner_item, - new String[]{"Approved", "Pending", "Rejected"}) { - - //Override the getView method for the spinner to make the text color darker for more readability - @NonNull - @Override - public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { - View view = super.getView(position, convertView, parent); - ((TextView) view).setTextColor(ContextCompat.getColor(requireContext(), R.color.text_dark)); - return view; + private Callback simpleCallback(String msg) { + return new Callback<>() { + public void onResponse(Call c, Response r) { + Log.d("ADOPTION_SAVE", "Response: " + r.code()); + if (r.isSuccessful()) { + Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT).show(); + navigateBack(); + } else { + try { + String err = r.errorBody().string(); + Log.e("ADOPTION_SAVE", "Error: " + err); + Toast.makeText(getContext(), "Error " + r.code(), Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + Log.e("ADOPTION_SAVE", "Failed to read error"); + } + } + } + public void onFailure(Call c, Throwable t) { + Log.e("ADOPTION_SAVE", "Failure: " + t.getMessage()); + Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show(); } }; - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - spinnerAdoptionStatus.setAdapter(adapter); } -} + + private void confirmDelete() { + new AlertDialog.Builder(requireContext()) + .setTitle("Delete Adoption?") + .setPositiveButton("Yes", (d, w) -> + RetrofitClient.getAdoptionApi(requireContext()) + .deleteAdoption(adoptionId) + .enqueue(new Callback() { + public void onResponse(Call c, Response r) { + navigateBack(); + } + public void onFailure(Call c, Throwable t) { + Toast.makeText(getContext(), "Delete failed", + Toast.LENGTH_SHORT).show(); + } + })) + .setNegativeButton("No", null).show(); + } + + private void navigateBack() { + ListFragment lf = (ListFragment) getParentFragment(); + if (lf != null) lf.getChildFragmentManager().popBackStack(); + } +} \ No newline at end of file diff --git a/android/app/src/main/res/layout/fragment_adoption.xml b/android/app/src/main/res/layout/fragment_adoption.xml index 705b6fa2..492b9236 100644 --- a/android/app/src/main/res/layout/fragment_adoption.xml +++ b/android/app/src/main/res/layout/fragment_adoption.xml @@ -12,7 +12,6 @@ android:orientation="vertical"> + android:textColor="@color/white"/> @@ -65,74 +65,37 @@ android:layout_gravity="end" android:layout_marginBottom="8dp"/> + - + android:layout_marginBottom="16dp"/> + - - - - - - - - - + android:layout_marginBottom="16dp"/> + + android:hint="Tap to select date" + android:inputType="none" + android:focusable="false" + android:clickable="true" + android:drawableEnd="@android:drawable/ic_menu_my_calendar" + android:layout_marginBottom="16dp"/> + + android:layout_height="wrap_content" + android:layout_marginBottom="8dp"/> diff --git a/android/app/src/main/res/layout/item_adoption.xml b/android/app/src/main/res/layout/item_adoption.xml index 9cf3cd58..40b03b51 100644 --- a/android/app/src/main/res/layout/item_adoption.xml +++ b/android/app/src/main/res/layout/item_adoption.xml @@ -1,64 +1,78 @@ - - + android:layout_margin="8dp" + app:cardCornerRadius="8dp" + app:cardElevation="2dp"> + android:orientation="vertical" + android:padding="16dp"> - + android:orientation="horizontal"> - + + + + + + + + + + + + + + + - - - - - - - + \ No newline at end of file