Adoption files

adoption
This commit is contained in:
Nikitha
2026-03-29 16:24:14 -06:00
parent 7e832a139f
commit 55f40572de
8 changed files with 514 additions and 369 deletions

View File

@@ -1,79 +1,79 @@
package com.example.petstoremobile.adapters; package com.example.petstoremobile.adapters;
import android.graphics.Color; import android.graphics.Color;
import android.view.LayoutInflater; import android.view.*;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.example.petstoremobile.R; import com.example.petstoremobile.R;
import com.example.petstoremobile.models.Adoption; import com.example.petstoremobile.dtos.AdoptionDTO;
import java.util.List; import java.util.List;
public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.AdoptionViewHolder> { public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.AdoptionViewHolder> {
private List<Adoption> adoptionList; private List<AdoptionDTO> adoptionList;
private OnAdoptionClickListener adoptionClickListener; private OnAdoptionClickListener listener;
// Interface for adoption click on recycler view
public interface OnAdoptionClickListener { public interface OnAdoptionClickListener {
void onAdoptionClick(int position); void onAdoptionClick(int position);
} }
// Constructor public AdoptionAdapter(List<AdoptionDTO> adoptionList, OnAdoptionClickListener listener) {
public AdoptionAdapter(List<Adoption> adoptionList, OnAdoptionClickListener adoptionClickListener) {
this.adoptionList = adoptionList; 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 { public static class AdoptionViewHolder extends RecyclerView.ViewHolder {
TextView tvAdopterName, tvPetName, tvAdoptionDate, tvAdoptionStatus; TextView tvCustomerName, tvPetName, tvDate, tvFee, tvStatus;
public AdoptionViewHolder(@NonNull View v) { public AdoptionViewHolder(@NonNull View v) {
super(v); super(v);
tvAdopterName = v.findViewById(R.id.tvAdopterName); tvCustomerName = v.findViewById(R.id.tvAdoptionCustomerName);
tvPetName = v.findViewById(R.id.tvAdoptionPetName); tvPetName = v.findViewById(R.id.tvAdoptionPetName);
tvAdoptionDate = v.findViewById(R.id.tvAdoptionDate); tvDate = v.findViewById(R.id.tvAdoptionDate);
tvAdoptionStatus = v.findViewById(R.id.tvAdoptionStatus); tvFee = v.findViewById(R.id.tvAdoptionFee);
tvStatus = v.findViewById(R.id.tvAdoptionStatus);
} }
} }
// Create a new row view
@NonNull @NonNull
@Override @Override
public AdoptionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 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); return new AdoptionViewHolder(v);
} }
// Populate the row with adoption data
@Override @Override
public void onBindViewHolder(@NonNull AdoptionViewHolder holder, int position) { public void onBindViewHolder(@NonNull AdoptionViewHolder holder, int position) {
Adoption adoption = adoptionList.get(position); AdoptionDTO a = adoptionList.get(position);
holder.tvAdopterName.setText(adoption.getAdopterName()); holder.tvCustomerName.setText(a.getCustomerName() != null ? a.getCustomerName() : "");
holder.tvPetName.setText("Pet: " + adoption.getPetName()); holder.tvPetName.setText("Pet: " + (a.getPetName() != null ? a.getPetName() : ""));
holder.tvAdoptionDate.setText("Date: " + adoption.getAdoptionDate()); holder.tvDate.setText("Date: " + (a.getAdoptionDate() != null ? a.getAdoptionDate() : ""));
holder.tvAdoptionStatus.setText(adoption.getStatus()); holder.tvFee.setText(a.getAdoptionFee() != null ? "$" + a.getAdoptionFee() : "");
// Set the status color depending on adoption status String status = a.getAdoptionStatus() != null ? a.getAdoptionStatus() : "";
if (adoption.getStatus().equals("Approved")) { holder.tvStatus.setText(status);
holder.tvAdoptionStatus.setBackgroundColor(Color.parseColor("#4CAF50"));
} else if (adoption.getStatus().equals("Pending")) { switch (status) {
holder.tvAdoptionStatus.setBackgroundColor(Color.parseColor("#FF9800")); case "Approved":
} else { holder.tvStatus.setBackgroundColor(Color.parseColor("#4CAF50"));
holder.tvAdoptionStatus.setBackgroundColor(Color.parseColor("#F44336")); 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 -> listener.onAdoptionClick(position));
holder.itemView.setOnClickListener(v -> adoptionClickListener.onAdoptionClick(position));
} }
@Override @Override
public int getItemCount() { public int getItemCount() { return adoptionList.size(); }
return adoptionList.size();
}
} }

View File

@@ -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<PageResponse<AdoptionDTO>> getAllAdoptions(
@Query("page") int page,
@Query("size") int size);
@GET("api/v1/adoptions/{id}")
Call<AdoptionDTO> getAdoptionById(@Path("id") Long id);
@POST("api/v1/adoptions")
Call<AdoptionDTO> createAdoption(@Body AdoptionDTO adoption);
@PUT("api/v1/adoptions/{id}")
Call<AdoptionDTO> updateAdoption(@Path("id") Long id, @Body AdoptionDTO adoption);
@DELETE("api/v1/adoptions/{id}")
Call<Void> deleteAdoption(@Path("id") Long id);
}

View File

@@ -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;
}
}

View File

@@ -1,36 +1,33 @@
package com.example.petstoremobile.fragments.listfragments; 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.os.Bundle;
import android.text.*;
import android.util.Log;
import android.view.*;
import android.widget.*;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; 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.R;
import com.example.petstoremobile.adapters.AdoptionAdapter; 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.ListFragment;
import com.example.petstoremobile.fragments.listfragments.detailfragments.AdoptionDetailFragment; import com.example.petstoremobile.fragments.listfragments.detailfragments.AdoptionDetailFragment;
import com.example.petstoremobile.models.Adoption;
import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.ArrayList; import java.util.*;
import java.util.List; import retrofit2.*;
public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdoptionClickListener { public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdoptionClickListener {
private List<Adoption> adoptionList = new ArrayList<>(); private List<AdoptionDTO> adoptionList = new ArrayList<>();
private List<Adoption> filteredList = new ArrayList<>(); private List<AdoptionDTO> filteredList = new ArrayList<>();
private AdoptionAdapter adapter; private AdoptionAdapter adapter;
private SwipeRefreshLayout swipeRefreshLayout; private AdoptionApi api;
private SwipeRefreshLayout swipeRefresh;
private EditText etSearch; private EditText etSearch;
private ImageButton hamburger; private ImageButton hamburger;
@@ -39,51 +36,58 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
Bundle savedInstanceState) { Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_adoption, container, false); 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); setupRecyclerView(view);
setupSearch(view); setupSearch(view);
setupSwipeRefresh(view); setupSwipeRefresh(view);
loadAdoptions();
FloatingActionButton fabAddAdoption = view.findViewById(R.id.fabAddAdoption); FloatingActionButton fab = view.findViewById(R.id.fabAddAdoption);
fabAddAdoption.setOnClickListener(v -> openAdoptionDetails(-1)); fab.setOnClickListener(v -> openDetail(-1));
//Make the hamburger button open the drawer from listFragment
hamburger.setOnClickListener(v -> { hamburger.setOnClickListener(v -> {
ListFragment listFragment = (ListFragment) getParentFragment(); ListFragment lf = (ListFragment) getParentFragment();
//if list fragment is found then use its helper function to open the drawer if (lf != null) lf.openDrawer();
if (listFragment != null) {
listFragment.openDrawer();
}
}); });
return view; 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) { private void setupSearch(View view) {
etSearch = view.findViewById(R.id.etSearchAdoption); etSearch = view.findViewById(R.id.etSearchAdoption);
etSearch.addTextChangedListener(new TextWatcher() { etSearch.addTextChangedListener(new TextWatcher() {
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} public void beforeTextChanged(CharSequence s, int a, int b, int c) {}
@Override public void onTextChanged(CharSequence s, int start, int before, int count) { public void afterTextChanged(Editable s) {}
filterAdoptions(s.toString()); 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(); filteredList.clear();
if (query.isEmpty()) { if (query.isEmpty()) {
filteredList.addAll(adoptionList); filteredList.addAll(adoptionList);
} else { } else {
String lower = query.toLowerCase(); String lower = query.toLowerCase();
for (Adoption a : adoptionList) { for (AdoptionDTO a : adoptionList) {
if (a.getAdopterName().toLowerCase().contains(lower) if ((a.getCustomerName() != null && a.getCustomerName().toLowerCase().contains(lower))
|| a.getPetName().toLowerCase().contains(lower) || (a.getPetName() != null && a.getPetName().toLowerCase().contains(lower))
|| a.getStatus().toLowerCase().contains(lower)) { || (a.getAdoptionStatus() != null && a.getAdoptionStatus().toLowerCase().contains(lower))) {
filteredList.add(a); filteredList.add(a);
} }
} }
@@ -91,73 +95,47 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
adapter.notifyDataSetChanged(); adapter.notifyDataSetChanged();
} }
private void setupSwipeRefresh(View view) { private void loadAdoptions() {
swipeRefreshLayout = view.findViewById(R.id.swipeRefreshAdoption); if (swipeRefresh != null) swipeRefresh.setRefreshing(true);
swipeRefreshLayout.setOnRefreshListener(() -> { api.getAllAdoptions(0, 100).enqueue(new Callback<PageResponse<AdoptionDTO>>() {
loadAdoptionData(); // TODO: Replace with actual API call public void onResponse(Call<PageResponse<AdoptionDTO>> c,
filterAdoptions(etSearch.getText().toString()); Response<PageResponse<AdoptionDTO>> r) {
swipeRefreshLayout.setRefreshing(false); 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<PageResponse<AdoptionDTO>> 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) { private void openDetail(int position) {
AdoptionDetailFragment detailFragment = new AdoptionDetailFragment(); AdoptionDetailFragment detail = new AdoptionDetailFragment();
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putInt("position", position);
if (position != -1) { if (position != -1) {
Adoption adoption = filteredList.get(position); AdoptionDTO a = filteredList.get(position);
int realPosition = adoptionList.indexOf(adoption); args.putLong("adoptionId", a.getAdoptionId());
args.putInt("position", realPosition); args.putLong("petId", a.getPetId() != null ? a.getPetId() : -1);
args.putInt("adoptionId", adoption.getAdoptionId()); args.putLong("customerId", a.getCustomerId() != null ? a.getCustomerId() : -1);
args.putString("adopterName", adoption.getAdopterName()); args.putString("adoptionDate", a.getAdoptionDate());
args.putString("adopterEmail", adoption.getAdopterEmail()); args.putString("adoptionStatus", a.getAdoptionStatus());
args.putString("adopterPhone", adoption.getAdopterPhone());
args.putString("petName", adoption.getPetName());
args.putString("adoptionDate", adoption.getAdoptionDate());
args.putString("status", adoption.getStatus());
} }
detailFragment.setArguments(args); detail.setArguments(args);
detailFragment.setAdoptionFragment(this); ListFragment lf = (ListFragment) getParentFragment();
if (lf != null) lf.loadFragment(detail);
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());
} }
@Override @Override
public void onAdoptionClick(int position) { public void onAdoptionClick(int position) { openDetail(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);
}
} }

View File

@@ -1,172 +1,257 @@
package com.example.petstoremobile.fragments.listfragments.detailfragments; package com.example.petstoremobile.fragments.listfragments.detailfragments;
import android.app.DatePickerDialog;
// Uses InputValidator for detailed field validation and ActivityLogger to log all changes.
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import android.view.*;
import android.widget.*;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; 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.R;
import com.example.petstoremobile.api.*;
import com.example.petstoremobile.dtos.*;
import com.example.petstoremobile.fragments.ListFragment; import com.example.petstoremobile.fragments.ListFragment;
import com.example.petstoremobile.fragments.listfragments.AdoptionFragment; import java.util.*;
import com.example.petstoremobile.models.Adoption; import retrofit2.*;
import com.example.petstoremobile.utils.ActivityLogger;
import com.example.petstoremobile.utils.InputValidator;
public class AdoptionDetailFragment extends Fragment { public class AdoptionDetailFragment extends Fragment {
private TextView tvMode, tvAdoptionId; private TextView tvMode, tvAdoptionId;
private EditText etAdopterName, etAdopterEmail, etAdopterPhone, etPetName, etAdoptionDate; private EditText etAdoptionDate;
private Spinner spinnerAdoptionStatus; private Spinner spinnerPet, spinnerCustomer, spinnerStatus;
private Button btnSaveAdoption, btnDeleteAdoption, btnBack; private Button btnSave, btnDelete, btnBack;
private int adoptionId;
private int position;
private boolean isEditing = false;
private AdoptionFragment adoptionFragment;
// Set the adoption fragment as parent so we refer back when save or delete is done private long adoptionId = -1;
public void setAdoptionFragment(AdoptionFragment fragment) { private boolean isEditing = false;
this.adoptionFragment = fragment; private long preselectedPetId = -1;
} private long preselectedCustomerId = -1;
private List<PetDTO> petList = new ArrayList<>();
private List<CustomerDTO> customerList = new ArrayList<>();
private final String[] STATUSES = {"Pending", "Approved", "Rejected"};
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_adoption_detail, container, false); View view = inflater.inflate(R.layout.fragment_adoption_detail, container, false);
initViews(view); initViews(view);
setupSpinner(); setupSpinners();
setupDatePicker();
loadData();
handleArguments(); handleArguments();
btnBack.setOnClickListener(v -> { btnBack.setOnClickListener(v -> navigateBack());
ListFragment listFragment = (ListFragment) getParentFragment(); btnSave.setOnClickListener(v -> saveAdoption());
if (listFragment != null) listFragment.getChildFragmentManager().popBackStack(); btnDelete.setOnClickListener(v -> confirmDelete());
});
btnSaveAdoption.setOnClickListener(v -> saveAdoption());
btnDeleteAdoption.setOnClickListener(v -> deleteAdoption());
return view; return view;
} }
// Validates all fields using InputValidator, then saves the adoption record private void initViews(View v) {
private void saveAdoption() { tvMode = v.findViewById(R.id.tvAdoptionMode);
if (!InputValidator.isNotEmpty(etAdopterName, "Adopter Name")) return; tvAdoptionId = v.findViewById(R.id.tvAdoptionId);
if (!InputValidator.isValidEmail(etAdopterEmail)) return; etAdoptionDate = v.findViewById(R.id.etAdoptionDate);
if (!InputValidator.isValidPhone(etAdopterPhone)) return; spinnerPet = v.findViewById(R.id.spinnerAdoptionPet);
if (!InputValidator.isNotEmpty(etPetName, "Pet Name")) return; spinnerCustomer= v.findViewById(R.id.spinnerAdoptionCustomer);
if (!InputValidator.isValidDate(etAdoptionDate)) return; spinnerStatus = v.findViewById(R.id.spinnerAdoptionStatus);
btnSave = v.findViewById(R.id.btnSaveAdoption);
String adopterName = etAdopterName.getText().toString().trim(); btnDelete = v.findViewById(R.id.btnDeleteAdoption);
String adopterEmail = etAdopterEmail.getText().toString().trim(); btnBack = v.findViewById(R.id.btnAdoptionBack);
String adopterPhone = etAdopterPhone.getText().toString().trim(); }
String petName = etPetName.getText().toString().trim();
String adoptionDate = etAdoptionDate.getText().toString().trim(); private void setupSpinners() {
String status = spinnerAdoptionStatus.getSelectedItem().toString(); 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 private void setupDatePicker() {
Adoption updated = new Adoption(adoptionId, adopterName, adopterEmail, adopterPhone, petName, adoptionDate, status); etAdoptionDate.setOnClickListener(v -> {
if (adoptionFragment != null) adoptionFragment.onAdoptionSaved(position, updated); Calendar c = Calendar.getInstance();
ActivityLogger.logChange(requireContext(), "Adoption", "UPDATED", adoptionId); new DatePickerDialog(requireContext(),
Toast.makeText(getContext(), "Adoption record updated.", Toast.LENGTH_SHORT).show(); (dp, y, m, d) -> etAdoptionDate.setText(
} else { String.format("%04d-%02d-%02d", y, m + 1, d)),
// TODO: Replace with actual API POST call when backend is ready c.get(Calendar.YEAR),
Adoption newAdoption = new Adoption(0, adopterName, adopterEmail, adopterPhone, petName, adoptionDate, status); c.get(Calendar.MONTH),
if (adoptionFragment != null) adoptionFragment.onAdoptionSaved(-1, newAdoption); c.get(Calendar.DAY_OF_MONTH)).show();
ActivityLogger.log(requireContext(), "Added new Adoption record for: " + adopterName + " adopting " + petName); });
Toast.makeText(getContext(), "Adoption record added.", Toast.LENGTH_SHORT).show(); }
private void loadData() {
loadPets();
loadCustomers();
}
private void loadPets() {
RetrofitClient.getPetApi(requireContext()).getAllPets(0, 200)
.enqueue(new Callback<PageResponse<PetDTO>>() {
public void onResponse(Call<PageResponse<PetDTO>> c,
Response<PageResponse<PetDTO>> r) {
if (r.isSuccessful() && r.body() != null) {
petList = r.body().getContent();
populatePetSpinner();
}
}
public void onFailure(Call<PageResponse<PetDTO>> c, Throwable t) {
Log.e("ADOPTION", "Pet load failed: " + t.getMessage());
}
});
}
private void populatePetSpinner() {
List<String> 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 loadCustomers() {
private void deleteAdoption() { RetrofitClient.getCustomerApi(requireContext()).getAllCustomers(0, 200)
try { .enqueue(new Callback<PageResponse<CustomerDTO>>() {
// TODO: Replace with actual API DELETE call when backend is ready public void onResponse(Call<PageResponse<CustomerDTO>> c,
if (adoptionFragment != null) adoptionFragment.onAdoptionDeleted(position); Response<PageResponse<CustomerDTO>> r) {
ActivityLogger.logChange(requireContext(), "Adoption", "DELETED", adoptionId); if (r.isSuccessful() && r.body() != null) {
Toast.makeText(getContext(), "Adoption record deleted.", Toast.LENGTH_SHORT).show(); customerList = r.body().getContent();
ListFragment listFragment = (ListFragment) getParentFragment(); populateCustomerSpinner();
if (listFragment != null) listFragment.getChildFragmentManager().popBackStack(); }
} catch (Exception e) { }
ActivityLogger.logException(requireContext(), "AdoptionDetailFragment.deleteAdoption", e); public void onFailure(Call<PageResponse<CustomerDTO>> c, Throwable t) {
Toast.makeText(getContext(), "Error deleting adoption record.", Toast.LENGTH_SHORT).show(); Log.e("ADOPTION", "Customer load failed: " + t.getMessage());
}
});
}
private void populateCustomerSpinner() {
List<String> 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() { private void handleArguments() {
if (getArguments() != null && getArguments().containsKey("adoptionId")) { Bundle a = getArguments();
if (a != null && a.containsKey("adoptionId")) {
isEditing = true; isEditing = true;
adoptionId = getArguments().getInt("adoptionId"); adoptionId = a.getLong("adoptionId");
position = getArguments().getInt("position"); preselectedPetId = a.getLong("petId", -1);
preselectedCustomerId = a.getLong("customerId", -1);
tvMode.setText("Edit Adoption"); tvMode.setText("Edit Adoption");
tvAdoptionId.setText("ID: " + adoptionId); tvAdoptionId.setText("ID: " + adoptionId);
etAdopterName.setText(getArguments().getString("adopterName")); tvAdoptionId.setVisibility(View.VISIBLE);
etAdopterEmail.setText(getArguments().getString("adopterEmail")); etAdoptionDate.setText(a.getString("adoptionDate"));
etAdopterPhone.setText(getArguments().getString("adopterPhone")); btnDelete.setVisibility(View.VISIBLE);
etPetName.setText(getArguments().getString("petName"));
etAdoptionDate.setText(getArguments().getString("adoptionDate")); // Pre-fill status
String status = getArguments().getString("status"); String status = a.getString("adoptionStatus", "Pending");
if ("Approved".equals(status)) spinnerAdoptionStatus.setSelection(0); for (int i = 0; i < STATUSES.length; i++) {
else if ("Pending".equals(status)) spinnerAdoptionStatus.setSelection(1); if (STATUSES[i].equals(status)) {
else spinnerAdoptionStatus.setSelection(2); spinnerStatus.setSelection(i); break;
btnDeleteAdoption.setVisibility(View.VISIBLE); }
}
} else { } else {
isEditing = false;
tvMode.setText("Add Adoption"); tvMode.setText("Add Adoption");
btnDelete.setVisibility(View.GONE);
tvAdoptionId.setVisibility(View.GONE); tvAdoptionId.setVisibility(View.GONE);
btnDeleteAdoption.setVisibility(View.GONE);
btnSaveAdoption.setText("Add");
} }
} }
private void initViews(View view) { private void saveAdoption() {
tvMode = view.findViewById(R.id.tvAdoptionMode); if (spinnerCustomer.getSelectedItemPosition() == 0) {
tvAdoptionId = view.findViewById(R.id.tvAdoptionId); Toast.makeText(getContext(), "Select a customer", Toast.LENGTH_SHORT).show(); return;
etAdopterName = view.findViewById(R.id.etAdopterName); }
etAdopterEmail = view.findViewById(R.id.etAdopterEmail); if (spinnerPet.getSelectedItemPosition() == 0) {
etAdopterPhone = view.findViewById(R.id.etAdopterPhone); Toast.makeText(getContext(), "Select a pet", Toast.LENGTH_SHORT).show(); return;
etPetName = view.findViewById(R.id.etAdoptionPetName); }
etAdoptionDate = view.findViewById(R.id.etAdoptionDate); String date = etAdoptionDate.getText().toString().trim();
spinnerAdoptionStatus = view.findViewById(R.id.spinnerAdoptionStatus); if (date.isEmpty()) {
btnSaveAdoption = view.findViewById(R.id.btnSaveAdoption); Toast.makeText(getContext(), "Select a date", Toast.LENGTH_SHORT).show(); return;
btnDeleteAdoption = view.findViewById(R.id.btnDeleteAdoption);
btnBack = view.findViewById(R.id.btnAdoptionBack);
} }
private void setupSpinner() { CustomerDTO customer = customerList.get(spinnerCustomer.getSelectedItemPosition() - 1);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(requireContext(), PetDTO pet = petList.get(spinnerPet.getSelectedItemPosition() - 1);
android.R.layout.simple_spinner_item, String status = STATUSES[spinnerStatus.getSelectedItemPosition()];
new String[]{"Approved", "Pending", "Rejected"}) {
//Override the getView method for the spinner to make the text color darker for more readability AdoptionDTO dto = new AdoptionDTO(
@NonNull pet.getPetId(),
@Override customer.getCustomerId(),
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { date,
View view = super.getView(position, convertView, parent); status
((TextView) view).setTextColor(ContextCompat.getColor(requireContext(), R.color.text_dark)); );
return view;
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 Callback<AdoptionDTO> simpleCallback(String msg) {
return new Callback<>() {
public void onResponse(Call<AdoptionDTO> c, Response<AdoptionDTO> 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<AdoptionDTO> 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<Void>() {
public void onResponse(Call<Void> c, Response<Void> r) {
navigateBack();
}
public void onFailure(Call<Void> 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();
} }
} }

View File

@@ -12,7 +12,6 @@
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <LinearLayout
android:id="@+id/header"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="56dp" android:layout_height="56dp"
android:background="@color/primary_dark" android:background="@color/primary_dark"
@@ -21,7 +20,7 @@
android:paddingEnd="16dp"> android:paddingEnd="16dp">
<ImageButton <ImageButton
android:id="@+id/btnHamburger" android:id="@+id/btnHamburgerAdoption"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
android:src="@drawable/baseline_menu_36" android:src="@drawable/baseline_menu_36"
@@ -43,7 +42,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="8dp" android:layout_margin="8dp"
android:hint="Search by adopter or pet name..." android:hint="Search by customer or pet..."
android:inputType="text" android:inputType="text"
android:drawableStart="@android:drawable/ic_menu_search" android:drawableStart="@android:drawable/ic_menu_search"
android:drawablePadding="8dp" android:drawablePadding="8dp"

View File

@@ -65,74 +65,37 @@
android:layout_gravity="end" android:layout_gravity="end"
android:layout_marginBottom="8dp"/> android:layout_marginBottom="8dp"/>
<!-- Customer -->
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Adopter Name" android:text="Customer"
android:textColor="@color/text_dark" android:textColor="@color/text_dark"
android:textSize="12sp" android:textSize="12sp"
android:layout_marginBottom="4dp"/> android:layout_marginBottom="4dp"/>
<EditText <Spinner
android:id="@+id/etAdopterName" android:id="@+id/spinnerAdoptionCustomer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="Enter adopter name" android:layout_marginBottom="16dp"/>
android:inputType="text"
android:layout_marginBottom="16dp"
android:textColor="@color/text_dark"/>
<!-- Pet -->
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Adopter Email" android:text="Pet"
android:textColor="@color/text_dark" android:textColor="@color/text_dark"
android:textSize="12sp" android:textSize="12sp"
android:layout_marginBottom="4dp"/> android:layout_marginBottom="4dp"/>
<EditText <Spinner
android:id="@+id/etAdopterEmail" android:id="@+id/spinnerAdoptionPet"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="Enter email address" android:layout_marginBottom="16dp"/>
android:inputType="textEmailAddress"
android:layout_marginBottom="16dp"
android:textColor="@color/text_dark"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Adopter Phone"
android:textColor="@color/text_dark"
android:textSize="12sp"
android:layout_marginBottom="4dp"/>
<EditText
android:id="@+id/etAdopterPhone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter phone number"
android:inputType="phone"
android:layout_marginBottom="16dp"
android:textColor="@color/text_dark"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Pet Name"
android:textColor="@color/text_dark"
android:textSize="12sp"
android:layout_marginBottom="4dp"/>
<EditText
android:id="@+id/etAdoptionPetName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter pet name"
android:inputType="text"
android:layout_marginBottom="16dp"
android:textColor="@color/text_dark"/>
<!-- Adoption Date -->
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -145,11 +108,14 @@
android:id="@+id/etAdoptionDate" android:id="@+id/etAdoptionDate"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="YYYY-MM-DD" android:hint="Tap to select date"
android:inputType="text" android:inputType="none"
android:layout_marginBottom="16dp" android:focusable="false"
android:textColor="@color/text_dark"/> android:clickable="true"
android:drawableEnd="@android:drawable/ic_menu_my_calendar"
android:layout_marginBottom="16dp"/>
<!-- Status -->
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -161,7 +127,8 @@
<Spinner <Spinner
android:id="@+id/spinnerAdoptionStatus" android:id="@+id/spinnerAdoptionStatus"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"
android:layout_marginBottom="8dp"/>
</LinearLayout> </LinearLayout>

View File

@@ -1,64 +1,78 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardCornerRadius="8dp"
app:cardElevation="2dp">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:padding="16dp" android:padding="16dp">
android:background="@android:color/white">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:orientation="horizontal">
<TextView <LinearLayout
android:id="@+id/tvAdopterName"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="Adopter Name" android:orientation="vertical">
android:textColor="#000000"
android:textSize="18sp"
android:textStyle="bold" />
<TextView <TextView
android:id="@+id/tvAdoptionStatus" android:id="@+id/tvAdoptionCustomerName"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="#4CAF50" android:textSize="16sp"
android:paddingStart="8dp" android:textStyle="bold"
android:paddingTop="4dp" android:textColor="@color/text_dark"/>
android:paddingEnd="8dp"
android:paddingBottom="4dp"
android:text="Status"
android:textColor="#FFFFFF"
android:textSize="12sp" />
</LinearLayout>
<TextView <TextView
android:id="@+id/tvAdoptionPetName" android:id="@+id/tvAdoptionPetName"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="4dp" android:textSize="13sp"
android:text="Pet: name" android:textColor="@color/text_light"
android:textColor="#666666" android:layout_marginTop="2dp"/>
android:textSize="14sp" />
<TextView <TextView
android:id="@+id/tvAdoptionDate" android:id="@+id/tvAdoptionDate"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="4dp" android:textSize="12sp"
android:text="Date: " android:textColor="@color/text_light"
android:textColor="#666666" android:layout_marginTop="2dp"/>
android:textSize="14sp" />
<View <TextView
android:layout_width="match_parent" android:id="@+id/tvAdoptionFee"
android:layout_height="1dp" android:layout_width="wrap_content"
android:background="#EEEEEE" android:layout_height="wrap_content"
android:layout_marginTop="12dp"/> android:textSize="12sp"
android:textColor="@color/text_dark"
android:layout_marginTop="2dp"/>
</LinearLayout> </LinearLayout>
<TextView
android:id="@+id/tvAdoptionStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:textSize="12sp"
android:textColor="@color/white"
android:layout_gravity="center_vertical"/>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>