revert adoption fragment
This commit is contained in:
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user