revert adoption fragment

This commit is contained in:
2026-04-07 22:17:15 -06:00
parent 3baef0e1ab
commit b3ff789f1b

View File

@@ -1,142 +1,337 @@
package com.example.petstoremobile.fragments.listfragments; package com.example.petstoremobile.fragments.listfragments;
import android.graphics.Color;
import android.os.Bundle; import android.os.Bundle;
import android.text.*; import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log; import android.util.Log;
import android.view.*; import android.view.LayoutInflater;
import android.widget.*; import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
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.databinding.FragmentAdoptionBinding;
import com.example.petstoremobile.api.RetrofitClient;
import com.example.petstoremobile.dtos.AdoptionDTO; import com.example.petstoremobile.dtos.AdoptionDTO;
import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.dtos.StoreDTO;
import com.example.petstoremobile.fragments.ListFragment; import com.example.petstoremobile.fragments.ListFragment;
import com.example.petstoremobile.fragments.listfragments.detailfragments.AdoptionDetailFragment; import com.example.petstoremobile.utils.BulkDeleteHandler;
import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.example.petstoremobile.utils.Resource;
import java.util.*; import com.example.petstoremobile.utils.SpinnerUtils;
import retrofit2.*; import com.example.petstoremobile.viewmodels.AdoptionViewModel;
import com.example.petstoremobile.utils.EventDecorator;
import com.example.petstoremobile.viewmodels.StoreViewModel;
import com.prolificinteractive.materialcalendarview.CalendarDay;
import com.prolificinteractive.materialcalendarview.CalendarMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import dagger.hilt.android.AndroidEntryPoint;
@AndroidEntryPoint
public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdoptionClickListener { public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdoptionClickListener {
private FragmentAdoptionBinding binding;
private List<AdoptionDTO> adoptionList = new ArrayList<>(); private List<AdoptionDTO> adoptionList = new ArrayList<>();
private List<AdoptionDTO> filteredList = new ArrayList<>(); private List<StoreDTO> storeList = new ArrayList<>();
private AdoptionAdapter adapter; private AdoptionAdapter adapter;
private AdoptionApi api; private AdoptionViewModel adoptionViewModel;
private SwipeRefreshLayout swipeRefresh; private StoreViewModel storeViewModel;
private EditText etSearch; private BulkDeleteHandler bulkDeleteHandler;
private ImageButton hamburger; private CalendarDay selectedCalendarDay;
private boolean isMonthMode = false;
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
/**
* Initializes the fragment and its ViewModels.
*/
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
adoptionViewModel = new ViewModelProvider(this).get(AdoptionViewModel.class);
storeViewModel = new ViewModelProvider(this).get(StoreViewModel.class);
}
/**
* Sets up the fragment's UI components, including RecyclerView, Search, SwipeRefresh, and Calendar.
*/
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentAdoptionBinding.inflate(inflater, container, false);
setupRecyclerView();
setupSearch();
setupStatusFilter();
setupStoreFilter();
setupSwipeRefresh();
setupCalendar();
setupFilterToggle();
setupBulkDelete();
binding.fabAddAdoption.setOnClickListener(v -> openDetail(-1));
binding.btnHamburgerAdoption.setOnClickListener(v -> {
Fragment parent = getParentFragment();
if (parent != null) {
Fragment grandParent = parent.getParentFragment();
if (grandParent instanceof ListFragment) {
((ListFragment) grandParent).openDrawer();
}
}
});
binding.btnToggleCalendarModeAdoption.setOnClickListener(v -> toggleCalendarMode());
return binding.getRoot();
}
private void setupBulkDelete() {
bulkDeleteHandler = new BulkDeleteHandler(
this,
binding.layoutBulkDelete,
binding.tvSelectionCount,
binding.btnBulkDelete,
adapter,
"adoption",
adoptionViewModel::bulkDeleteAdoptions,
this::loadAdoptions
);
}
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public void onDestroyView() {
Bundle savedInstanceState) { super.onDestroyView();
View view = inflater.inflate(R.layout.fragment_adoption, container, false); binding = null;
}
api = RetrofitClient.getAdoptionApi(requireContext()); @Override
hamburger = view.findViewById(R.id.btnHamburgerAdoption); public void onResume() {
super.onResume();
setupRecyclerView(view);
setupSearch(view);
setupSwipeRefresh(view);
loadAdoptions(); loadAdoptions();
loadStoreData();
FloatingActionButton fab = view.findViewById(R.id.fabAddAdoption);
fab.setOnClickListener(v -> openDetail(-1));
hamburger.setOnClickListener(v -> {
ListFragment lf = (ListFragment) getParentFragment();
if (lf != null) lf.openDrawer();
});
return view;
} }
private void setupRecyclerView(View view) { /**
RecyclerView rv = view.findViewById(R.id.recyclerViewAdoptions); * Toggles the calendar display between week and month modes.
adapter = new AdoptionAdapter(filteredList, this); */
rv.setLayoutManager(new LinearLayoutManager(getContext())); private void toggleCalendarMode() {
rv.setAdapter(adapter); isMonthMode = !isMonthMode;
binding.calendarViewAdoption.state().edit()
.setCalendarDisplayMode(isMonthMode ? CalendarMode.MONTHS : CalendarMode.WEEKS)
.commit();
} }
private void setupSearch(View view) { /**
etSearch = view.findViewById(R.id.etSearchAdoption); * Sets up the filter toggle button to show/hide the filter layout.
etSearch.addTextChangedListener(new TextWatcher() { */
public void beforeTextChanged(CharSequence s, int a, int b, int c) {} private void setupFilterToggle() {
public void afterTextChanged(Editable s) {} binding.btnToggleFilterAdoption.setOnClickListener(v -> {
public void onTextChanged(CharSequence s, int a, int b, int c) { if (binding.layoutFilterAdoption.getVisibility() == View.GONE) {
filter(s.toString()); binding.layoutFilterAdoption.setVisibility(View.VISIBLE);
binding.btnToggleFilterAdoption.setImageResource(android.R.drawable.ic_menu_close_clear_cancel);
} else {
binding.layoutFilterAdoption.setVisibility(View.GONE);
binding.btnToggleFilterAdoption.setImageResource(android.R.drawable.ic_menu_search);
// Reset filters when closing
binding.etSearchAdoption.setText("");
binding.spinnerStatusAdoption.setSelection(0);
binding.spinnerStoreAdoption.setSelection(0);
selectedCalendarDay = null;
binding.calendarViewAdoption.clearSelection();
} }
}); });
} }
private void setupSwipeRefresh(View view) { /**
swipeRefresh = view.findViewById(R.id.swipeRefreshAdoption); * Sets up the date selection listener for the calendar.
swipeRefresh.setOnRefreshListener(this::loadAdoptions); */
private void setupCalendar() {
binding.calendarViewAdoption.setOnDateChangedListener((widget, date, selected) -> {
if (selected) {
if (date.equals(selectedCalendarDay)) {
selectedCalendarDay = null;
binding.calendarViewAdoption.clearSelection();
} else {
selectedCalendarDay = date;
}
} else {
selectedCalendarDay = null;
}
loadAdoptions();
});
} }
private void filter(String query) { /**
filteredList.clear(); * Updates the calendar decorators to highlight days with adoptions.
if (query.isEmpty()) { */
filteredList.addAll(adoptionList); private void updateCalendarDecorators() {
} else { HashSet<CalendarDay> datesWithAdoptions = new HashSet<>();
String lower = query.toLowerCase(); for (AdoptionDTO adoption : adoptionList) {
for (AdoptionDTO a : adoptionList) { try {
if ((a.getCustomerName() != null && a.getCustomerName().toLowerCase().contains(lower)) if (adoption.getAdoptionDate() != null) {
|| (a.getPetName() != null && a.getPetName().toLowerCase().contains(lower)) Date date = dateFormat.parse(adoption.getAdoptionDate());
|| (a.getAdoptionStatus() != null && a.getAdoptionStatus().toLowerCase().contains(lower))) { if (date != null) {
filteredList.add(a); Calendar cal = Calendar.getInstance();
cal.setTime(date);
datesWithAdoptions.add(CalendarDay.from(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH)));
}
} }
} catch (ParseException e) {
Log.e("AdoptionFragment", "Error parsing date: " + adoption.getAdoptionDate());
} }
} }
adapter.notifyDataSetChanged(); binding.calendarViewAdoption.removeDecorators();
binding.calendarViewAdoption.addDecorator(new EventDecorator(Color.RED, datesWithAdoptions));
} }
private void loadAdoptions() { /**
if (swipeRefresh != null) swipeRefresh.setRefreshing(true); * Initializes the RecyclerView for displaying adoptions.
api.getAllAdoptions(0, 100, null, null, null, null, null).enqueue(new Callback<PageResponse<AdoptionDTO>>() { */
public void onResponse(Call<PageResponse<AdoptionDTO>> c, private void setupRecyclerView() {
Response<PageResponse<AdoptionDTO>> r) { adapter = new AdoptionAdapter(adoptionList, this);
if (swipeRefresh != null) swipeRefresh.setRefreshing(false); binding.recyclerViewAdoptions.setLayoutManager(new LinearLayoutManager(getContext()));
if (r.isSuccessful() && r.body() != null) { binding.recyclerViewAdoptions.setAdapter(adapter);
adoptionList.clear(); }
adoptionList.addAll(r.body().getContent());
filter(etSearch != null ? etSearch.getText().toString() : ""); /**
} else { * Sets up the search bar for filtering
Toast.makeText(getContext(), "Failed to load adoptions", Toast.LENGTH_SHORT).show(); */
Log.e("AdoptionFragment", "Error: " + r.message()); private void setupSearch() {
} binding.etSearchAdoption.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) {
loadAdoptions();
} }
public void onFailure(Call<PageResponse<AdoptionDTO>> c, Throwable t) { @Override public void afterTextChanged(Editable s) {}
if (swipeRefresh != null) swipeRefresh.setRefreshing(false); });
Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show(); }
Log.e("AdoptionFragment", t.getMessage());
/**
* Configures the status filter spinner.
*/
private void setupStatusFilter() {
String[] statuses = {"All Statuses", "Completed", "Pending", "Cancelled"};
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatusAdoption, statuses, this::loadAdoptions);
}
/**
* Configures the store filter spinner.
*/
private void setupStoreFilter() {
SpinnerUtils.setupFilterSpinner(binding.spinnerStoreAdoption, this::loadAdoptions);
}
/**
* Fetches store data to populate the store filter.
*/
private void loadStoreData() {
storeViewModel.getAllStores(0, 100).observe(getViewLifecycleOwner(), resource -> {
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
storeList = resource.data.getContent();
SpinnerUtils.populateWhiteSpinner(requireContext(), binding.spinnerStoreAdoption, storeList,
StoreDTO::getStoreName, "All Stores", -1L, StoreDTO::getStoreId);
} }
}); });
} }
/**
* Sets up the SwipeRefreshLayout to reload adoption data.
*/
private void setupSwipeRefresh() {
binding.swipeRefreshAdoption.setOnRefreshListener(this::loadAdoptions);
}
/**
* Fetches the adoption list from the server through the ViewModel.
*/
private void loadAdoptions() {
String query = binding.etSearchAdoption.getText().toString().trim();
String status = binding.spinnerStatusAdoption.getSelectedItem() != null ? binding.spinnerStatusAdoption.getSelectedItem().toString() : "All Statuses";
Long storeId = null;
if (binding.spinnerStoreAdoption.getSelectedItemPosition() > 0 && !storeList.isEmpty()) {
storeId = storeList.get(binding.spinnerStoreAdoption.getSelectedItemPosition() - 1).getStoreId();
}
String selectedDateString = null;
if (selectedCalendarDay != null) {
selectedDateString = String.format(Locale.getDefault(), "%04d-%02d-%02d",
selectedCalendarDay.getYear(), selectedCalendarDay.getMonth(), selectedCalendarDay.getDay());
}
if (status.equals("All Statuses")) status = null;
else status = status.toUpperCase();
adoptionViewModel.getAllAdoptions(0, 500, query, status, storeId, selectedDateString, null).observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return;
// Check the status to see if the resource is loaded and display the data
switch (resource.status) {
case LOADING:
// Show loading indicator
binding.swipeRefreshAdoption.setRefreshing(true);
break;
case SUCCESS:
// Hide loading indicator and display data
binding.swipeRefreshAdoption.setRefreshing(false);
if (resource.data != null) {
adoptionList.clear();
adoptionList.addAll(resource.data.getContent());
updateCalendarDecorators();
adapter.notifyDataSetChanged();
}
break;
case ERROR:
// Hide loading indicator and toast error message
binding.swipeRefreshAdoption.setRefreshing(false);
Toast.makeText(getContext(), "Failed to load adoptions: " + resource.message, Toast.LENGTH_SHORT).show();
Log.e("AdoptionFragment", "Error loading adoptions: " + resource.message);
break;
}
});
}
/**
* Navigates to the adoption detail screen for a specific adoption or to create a new one.
*/
private void openDetail(int position) { private void openDetail(int position) {
Bundle args = new Bundle(); Bundle args = new Bundle();
if (position != -1) { if (position != -1) {
AdoptionDTO a = filteredList.get(position); AdoptionDTO a = adoptionList.get(position);
args.putLong("adoptionId", a.getAdoptionId()); 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());
if (a.getEmployeeId() != null)
args.putLong("employeeId", a.getEmployeeId());}
NavHostFragment.findNavController(this).navigate(R.id.nav_adoption_detail, args); NavHostFragment.findNavController(this).navigate(R.id.nav_adoption_detail, args);
} }
/**
* Handles item click in the adoption list.
*/
@Override @Override
public void onAdoptionClick(int position) { openDetail(position); } public void onAdoptionClick(int position) { openDetail(position); }
@Override @Override
public void onSelectionChanged(int count) {} public void onSelectionChanged(int selectedCount) {
if (bulkDeleteHandler != null) {
bulkDeleteHandler.onSelectionChanged(selectedCount);
}
}
} }