added bulk delete for ProductSupplier, appointments, and adoptions

This commit is contained in:
Alex
2026-04-07 16:20:51 -06:00
parent 713e919c10
commit 01f5efa991
35 changed files with 695 additions and 261 deletions

View File

@@ -2,25 +2,51 @@ package com.example.petstoremobile.adapters;
import android.graphics.Color; import android.graphics.Color;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.example.petstoremobile.databinding.ItemAdoptionBinding; import com.example.petstoremobile.databinding.ItemAdoptionBinding;
import com.example.petstoremobile.dtos.AdoptionDTO; import com.example.petstoremobile.dtos.AdoptionDTO;
import com.example.petstoremobile.utils.BulkDeleteHandler;
import com.example.petstoremobile.utils.SelectionHelper;
import java.util.List; import java.util.List;
public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.AdoptionViewHolder> { public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.AdoptionViewHolder> implements BulkDeleteHandler.SelectableAdapter {
private List<AdoptionDTO> adoptionList; private List<AdoptionDTO> adoptionList;
private OnAdoptionClickListener listener; private OnAdoptionClickListener listener;
private final SelectionHelper selectionHelper;
public interface OnAdoptionClickListener { public interface OnAdoptionClickListener {
void onAdoptionClick(int position); void onAdoptionClick(int position);
void onSelectionChanged(int count);
} }
public AdoptionAdapter(List<AdoptionDTO> adoptionList, OnAdoptionClickListener listener) { public AdoptionAdapter(List<AdoptionDTO> adoptionList, OnAdoptionClickListener listener) {
this.adoptionList = adoptionList; this.adoptionList = adoptionList;
this.listener = listener; this.listener = listener;
this.selectionHelper = new SelectionHelper(new SelectionHelper.SelectionListener() {
@Override
public void onSelectionChanged(int count) {
listener.onSelectionChanged(count);
}
@Override
public void onSelectionModeToggle(boolean selectionMode) {
notifyDataSetChanged();
}
});
}
@Override
public List<String> getSelectedKeys() {
return selectionHelper.getSelectedKeys();
}
@Override
public void clearSelection() {
selectionHelper.clearSelection();
} }
public static class AdoptionViewHolder extends RecyclerView.ViewHolder { public static class AdoptionViewHolder extends RecyclerView.ViewHolder {
@@ -68,7 +94,32 @@ public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.Adopti
break; break;
} }
holder.itemView.setOnClickListener(v -> listener.onAdoptionClick(position)); String key = String.valueOf(a.getAdoptionId());
// Bulk delete selection mode
if (selectionHelper.isInSelectionMode()) {
binding.cbSelectAdoption.setVisibility(View.VISIBLE);
binding.cbSelectAdoption.setChecked(selectionHelper.isSelected(key));
} else {
binding.cbSelectAdoption.setVisibility(View.GONE);
binding.cbSelectAdoption.setChecked(false);
}
holder.itemView.setOnClickListener(v -> {
if (selectionHelper.isInSelectionMode()) {
selectionHelper.toggleSelection(key);
notifyItemChanged(position);
} else {
listener.onAdoptionClick(position);
}
});
holder.itemView.setOnLongClickListener(v -> {
if (!selectionHelper.isInSelectionMode()) {
selectionHelper.startSelection(key);
}
return true;
});
} }
@Override @Override

View File

@@ -2,26 +2,52 @@ package com.example.petstoremobile.adapters;
import android.graphics.Color; import android.graphics.Color;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.example.petstoremobile.databinding.ItemAppointmentBinding; import com.example.petstoremobile.databinding.ItemAppointmentBinding;
import com.example.petstoremobile.dtos.AppointmentDTO; import com.example.petstoremobile.dtos.AppointmentDTO;
import com.example.petstoremobile.utils.BulkDeleteHandler;
import com.example.petstoremobile.utils.SelectionHelper;
import java.util.List; import java.util.List;
public class AppointmentAdapter extends RecyclerView.Adapter<AppointmentAdapter.AppointmentViewHolder> { public class AppointmentAdapter extends RecyclerView.Adapter<AppointmentAdapter.AppointmentViewHolder> implements BulkDeleteHandler.SelectableAdapter {
private List<AppointmentDTO> appointmentList; private List<AppointmentDTO> appointmentList;
private OnAppointmentClickListener appointmentClickListener; private OnAppointmentClickListener appointmentClickListener;
private final SelectionHelper selectionHelper;
public interface OnAppointmentClickListener { public interface OnAppointmentClickListener {
void onAppointmentClick(int position); void onAppointmentClick(int position);
void onSelectionChanged(int count);
} }
public AppointmentAdapter(List<AppointmentDTO> appointmentList, public AppointmentAdapter(List<AppointmentDTO> appointmentList,
OnAppointmentClickListener appointmentClickListener) { OnAppointmentClickListener appointmentClickListener) {
this.appointmentList = appointmentList; this.appointmentList = appointmentList;
this.appointmentClickListener = appointmentClickListener; this.appointmentClickListener = appointmentClickListener;
this.selectionHelper = new SelectionHelper(new SelectionHelper.SelectionListener() {
@Override
public void onSelectionChanged(int count) {
appointmentClickListener.onSelectionChanged(count);
}
@Override
public void onSelectionModeToggle(boolean selectionMode) {
notifyDataSetChanged();
}
});
}
@Override
public List<String> getSelectedKeys() {
return selectionHelper.getSelectedKeys();
}
@Override
public void clearSelection() {
selectionHelper.clearSelection();
} }
public static class AppointmentViewHolder extends RecyclerView.ViewHolder { public static class AppointmentViewHolder extends RecyclerView.ViewHolder {
@@ -70,7 +96,32 @@ public class AppointmentAdapter extends RecyclerView.Adapter<AppointmentAdapter.
break; break;
} }
holder.itemView.setOnClickListener(v -> appointmentClickListener.onAppointmentClick(position)); String key = String.valueOf(a.getAppointmentId());
// Bulk delete selection mode
if (selectionHelper.isInSelectionMode()) {
binding.cbSelectAppointment.setVisibility(View.VISIBLE);
binding.cbSelectAppointment.setChecked(selectionHelper.isSelected(key));
} else {
binding.cbSelectAppointment.setVisibility(View.GONE);
binding.cbSelectAppointment.setChecked(false);
}
holder.itemView.setOnClickListener(v -> {
if (selectionHelper.isInSelectionMode()) {
selectionHelper.toggleSelection(key);
notifyItemChanged(position);
} else {
appointmentClickListener.onAppointmentClick(position);
}
});
holder.itemView.setOnLongClickListener(v -> {
if (!selectionHelper.isInSelectionMode()) {
selectionHelper.startSelection(key);
}
return true;
});
} }
@Override @Override

View File

@@ -44,8 +44,8 @@ public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.Inve
} }
@Override @Override
public List<Long> getSelectedIds() { public List<String> getSelectedKeys() {
return selectionHelper.getSelectedIds(); return selectionHelper.getSelectedKeys();
} }
@Override @Override
@@ -91,10 +91,12 @@ public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.Inve
binding.tvQuantity.setTextColor(Color.parseColor("#4CAF50")); binding.tvQuantity.setTextColor(Color.parseColor("#4CAF50"));
} }
String key = String.valueOf(inv.getInventoryId());
// Bulk delete selection mode // Bulk delete selection mode
if (selectionHelper.isInSelectionMode()) { if (selectionHelper.isInSelectionMode()) {
binding.cbSelectInventory.setVisibility(View.VISIBLE); binding.cbSelectInventory.setVisibility(View.VISIBLE);
binding.cbSelectInventory.setChecked(selectionHelper.isSelected(inv.getInventoryId())); binding.cbSelectInventory.setChecked(selectionHelper.isSelected(key));
} else { } else {
binding.cbSelectInventory.setVisibility(View.GONE); binding.cbSelectInventory.setVisibility(View.GONE);
binding.cbSelectInventory.setChecked(false); binding.cbSelectInventory.setChecked(false);
@@ -102,7 +104,7 @@ public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.Inve
holder.itemView.setOnClickListener(v -> { holder.itemView.setOnClickListener(v -> {
if (selectionHelper.isInSelectionMode()) { if (selectionHelper.isInSelectionMode()) {
selectionHelper.toggleSelection(inv.getInventoryId()); selectionHelper.toggleSelection(key);
notifyItemChanged(position); notifyItemChanged(position);
} else { } else {
clickListener.onInventoryClick(holder.getAdapterPosition()); clickListener.onInventoryClick(holder.getAdapterPosition());
@@ -111,7 +113,7 @@ public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.Inve
holder.itemView.setOnLongClickListener(v -> { holder.itemView.setOnLongClickListener(v -> {
if (!selectionHelper.isInSelectionMode()) { if (!selectionHelper.isInSelectionMode()) {
selectionHelper.startSelection(inv.getInventoryId()); selectionHelper.startSelection(key);
} }
return true; return true;
}); });

View File

@@ -57,8 +57,8 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
} }
@Override @Override
public List<Long> getSelectedIds() { public List<String> getSelectedKeys() {
return selectionHelper.getSelectedIds(); return selectionHelper.getSelectedKeys();
} }
@Override @Override
@@ -118,10 +118,12 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
binding.ivPetProfile.setImageResource(R.drawable.placeholder); binding.ivPetProfile.setImageResource(R.drawable.placeholder);
} }
String key = String.valueOf(pet.getPetId());
// Bulk delete selection mode // Bulk delete selection mode
if (selectionHelper.isInSelectionMode()) { if (selectionHelper.isInSelectionMode()) {
binding.cbSelectPet.setVisibility(View.VISIBLE); binding.cbSelectPet.setVisibility(View.VISIBLE);
binding.cbSelectPet.setChecked(selectionHelper.isSelected(pet.getPetId())); binding.cbSelectPet.setChecked(selectionHelper.isSelected(key));
} else { } else {
binding.cbSelectPet.setVisibility(View.GONE); binding.cbSelectPet.setVisibility(View.GONE);
binding.cbSelectPet.setChecked(false); binding.cbSelectPet.setChecked(false);
@@ -130,7 +132,7 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
//when a row is clicked, open the detail view //when a row is clicked, open the detail view
holder.itemView.setOnClickListener(v -> { holder.itemView.setOnClickListener(v -> {
if (selectionHelper.isInSelectionMode()) { if (selectionHelper.isInSelectionMode()) {
selectionHelper.toggleSelection(pet.getPetId()); selectionHelper.toggleSelection(key);
notifyItemChanged(position); notifyItemChanged(position);
} else { } else {
petClickListener.onPetClick(position); petClickListener.onPetClick(position);
@@ -139,7 +141,7 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
holder.itemView.setOnLongClickListener(v -> { holder.itemView.setOnLongClickListener(v -> {
if (!selectionHelper.isInSelectionMode()) { if (!selectionHelper.isInSelectionMode()) {
selectionHelper.startSelection(pet.getPetId()); selectionHelper.startSelection(key);
} }
return true; return true;
}); });

View File

@@ -1,25 +1,54 @@
package com.example.petstoremobile.adapters; package com.example.petstoremobile.adapters;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.example.petstoremobile.databinding.ItemProductSupplierBinding; import com.example.petstoremobile.databinding.ItemProductSupplierBinding;
import com.example.petstoremobile.dtos.ProductSupplierDTO; import com.example.petstoremobile.dtos.ProductSupplierDTO;
import com.example.petstoremobile.utils.BulkDeleteHandler;
import com.example.petstoremobile.utils.SelectionHelper;
import java.util.List; import java.util.List;
public class ProductSupplierAdapter extends RecyclerView.Adapter<ProductSupplierAdapter.PSViewHolder> { public class ProductSupplierAdapter extends RecyclerView.Adapter<ProductSupplierAdapter.PSViewHolder> implements BulkDeleteHandler.SelectableAdapter {
private List<ProductSupplierDTO> list; private final List<ProductSupplierDTO> list;
private OnProductSupplierClickListener listener; private final OnProductSupplierClickListener listener;
private final SelectionHelper selectionHelper;
public interface OnProductSupplierClickListener { public interface OnProductSupplierClickListener {
void onProductSupplierClick(int position); void onProductSupplierClick(int position);
void onSelectionChanged(int count);
} }
public ProductSupplierAdapter(List<ProductSupplierDTO> list, OnProductSupplierClickListener listener) { public ProductSupplierAdapter(List<ProductSupplierDTO> list, OnProductSupplierClickListener listener) {
this.list = list; this.list = list;
this.listener = listener; this.listener = listener;
this.selectionHelper = new SelectionHelper(new SelectionHelper.SelectionListener() {
@Override
public void onSelectionChanged(int count) {
listener.onSelectionChanged(count);
}
@Override
public void onSelectionModeToggle(boolean selectionMode) {
notifyDataSetChanged();
}
});
}
@Override
public List<String> getSelectedKeys() {
return selectionHelper.getSelectedKeys();
}
@Override
public void clearSelection() {
selectionHelper.clearSelection();
} }
public static class PSViewHolder extends RecyclerView.ViewHolder { public static class PSViewHolder extends RecyclerView.ViewHolder {
@@ -46,7 +75,33 @@ public class ProductSupplierAdapter extends RecyclerView.Adapter<ProductSupplier
binding.tvPSProductName.setText(ps.getProductName() != null ? ps.getProductName() : ""); binding.tvPSProductName.setText(ps.getProductName() != null ? ps.getProductName() : "");
binding.tvPSSupplierName.setText("Supplier: " + (ps.getSupplierName() != null ? ps.getSupplierName() : "")); binding.tvPSSupplierName.setText("Supplier: " + (ps.getSupplierName() != null ? ps.getSupplierName() : ""));
binding.tvPSCost.setText(ps.getCost() != null ? "Cost: $" + ps.getCost() : ""); binding.tvPSCost.setText(ps.getCost() != null ? "Cost: $" + ps.getCost() : "");
holder.itemView.setOnClickListener(v -> listener.onProductSupplierClick(position));
String key = ps.getProductId() + "-" + ps.getSupplierId();
// Bulk delete selection mode
if (selectionHelper.isInSelectionMode()) {
binding.cbSelectProductSupplier.setVisibility(View.VISIBLE);
binding.cbSelectProductSupplier.setChecked(selectionHelper.isSelected(key));
} else {
binding.cbSelectProductSupplier.setVisibility(View.GONE);
binding.cbSelectProductSupplier.setChecked(false);
}
holder.itemView.setOnClickListener(v -> {
if (selectionHelper.isInSelectionMode()) {
selectionHelper.toggleSelection(key);
notifyItemChanged(position);
} else {
listener.onProductSupplierClick(position);
}
});
holder.itemView.setOnLongClickListener(v -> {
if (!selectionHelper.isInSelectionMode()) {
selectionHelper.startSelection(key);
}
return true;
});
} }
@Override @Override

View File

@@ -48,8 +48,8 @@ public class ServiceAdapter extends RecyclerView.Adapter<ServiceAdapter.ServiceV
} }
@Override @Override
public List<Long> getSelectedIds() { public List<String> getSelectedKeys() {
return selectionHelper.getSelectedIds(); return selectionHelper.getSelectedKeys();
} }
@Override @Override
@@ -88,10 +88,12 @@ public class ServiceAdapter extends RecyclerView.Adapter<ServiceAdapter.ServiceV
binding.tvServiceDuration.setText(service.getServiceDuration() != null ? service.getServiceDuration() + " mins" : "0 mins"); binding.tvServiceDuration.setText(service.getServiceDuration() != null ? service.getServiceDuration() + " mins" : "0 mins");
binding.tvServicePrice.setText(service.getServicePrice() != null ? "$" + String.format("%.2f", service.getServicePrice()) : "$0.00"); binding.tvServicePrice.setText(service.getServicePrice() != null ? "$" + String.format("%.2f", service.getServicePrice()) : "$0.00");
String key = String.valueOf(service.getServiceId());
// Bulk delete selection mode // Bulk delete selection mode
if (selectionHelper.isInSelectionMode()) { if (selectionHelper.isInSelectionMode()) {
binding.cbSelectService.setVisibility(View.VISIBLE); binding.cbSelectService.setVisibility(View.VISIBLE);
binding.cbSelectService.setChecked(selectionHelper.isSelected(service.getServiceId())); binding.cbSelectService.setChecked(selectionHelper.isSelected(key));
} else { } else {
binding.cbSelectService.setVisibility(View.GONE); binding.cbSelectService.setVisibility(View.GONE);
binding.cbSelectService.setChecked(false); binding.cbSelectService.setChecked(false);
@@ -99,7 +101,7 @@ public class ServiceAdapter extends RecyclerView.Adapter<ServiceAdapter.ServiceV
holder.itemView.setOnClickListener(v -> { holder.itemView.setOnClickListener(v -> {
if (selectionHelper.isInSelectionMode()) { if (selectionHelper.isInSelectionMode()) {
selectionHelper.toggleSelection(service.getServiceId()); selectionHelper.toggleSelection(key);
notifyItemChanged(position); notifyItemChanged(position);
} else { } else {
clickListener.onServiceClick(position); clickListener.onServiceClick(position);
@@ -108,7 +110,7 @@ public class ServiceAdapter extends RecyclerView.Adapter<ServiceAdapter.ServiceV
holder.itemView.setOnLongClickListener(v -> { holder.itemView.setOnLongClickListener(v -> {
if (!selectionHelper.isInSelectionMode()) { if (!selectionHelper.isInSelectionMode()) {
selectionHelper.startSelection(service.getServiceId()); selectionHelper.startSelection(key);
} }
return true; return true;
}); });

View File

@@ -44,8 +44,8 @@ public class SupplierAdapter extends RecyclerView.Adapter<SupplierAdapter.Suppli
} }
@Override @Override
public List<Long> getSelectedIds() { public List<String> getSelectedKeys() {
return selectionHelper.getSelectedIds(); return selectionHelper.getSelectedKeys();
} }
@Override @Override
@@ -82,10 +82,12 @@ public class SupplierAdapter extends RecyclerView.Adapter<SupplierAdapter.Suppli
binding.tvSupEmail.setText(supplier.getSupEmail()); binding.tvSupEmail.setText(supplier.getSupEmail());
binding.tvSupPhone.setText(supplier.getSupPhone()); binding.tvSupPhone.setText(supplier.getSupPhone());
String key = String.valueOf(supplier.getSupId());
// Bulk delete selection mode // Bulk delete selection mode
if (selectionHelper.isInSelectionMode()) { if (selectionHelper.isInSelectionMode()) {
binding.cbSelectSupplier.setVisibility(View.VISIBLE); binding.cbSelectSupplier.setVisibility(View.VISIBLE);
binding.cbSelectSupplier.setChecked(selectionHelper.isSelected(supplier.getSupId())); binding.cbSelectSupplier.setChecked(selectionHelper.isSelected(key));
} else { } else {
binding.cbSelectSupplier.setVisibility(View.GONE); binding.cbSelectSupplier.setVisibility(View.GONE);
binding.cbSelectSupplier.setChecked(false); binding.cbSelectSupplier.setChecked(false);
@@ -94,7 +96,7 @@ public class SupplierAdapter extends RecyclerView.Adapter<SupplierAdapter.Suppli
//when a row is clicked, open the detail view //when a row is clicked, open the detail view
holder.itemView.setOnClickListener(v -> { holder.itemView.setOnClickListener(v -> {
if (selectionHelper.isInSelectionMode()) { if (selectionHelper.isInSelectionMode()) {
selectionHelper.toggleSelection(supplier.getSupId()); selectionHelper.toggleSelection(key);
notifyItemChanged(position); notifyItemChanged(position);
} else { } else {
supplierClickListener.onSupplierClick(position); supplierClickListener.onSupplierClick(position);
@@ -103,7 +105,7 @@ public class SupplierAdapter extends RecyclerView.Adapter<SupplierAdapter.Suppli
holder.itemView.setOnLongClickListener(v -> { holder.itemView.setOnLongClickListener(v -> {
if (!selectionHelper.isInSelectionMode()) { if (!selectionHelper.isInSelectionMode()) {
selectionHelper.startSelection(supplier.getSupId()); selectionHelper.startSelection(key);
} }
return true; return true;
}); });

View File

@@ -1,12 +1,14 @@
package com.example.petstoremobile.api; package com.example.petstoremobile.api;
import com.example.petstoremobile.dtos.AdoptionDTO; import com.example.petstoremobile.dtos.AdoptionDTO;
import com.example.petstoremobile.dtos.BulkDeleteRequest;
import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.dtos.PageResponse;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.Body; import retrofit2.http.Body;
import retrofit2.http.DELETE; import retrofit2.http.DELETE;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.HTTP;
import retrofit2.http.POST; import retrofit2.http.POST;
import retrofit2.http.PUT; import retrofit2.http.PUT;
import retrofit2.http.Path; import retrofit2.http.Path;
@@ -30,5 +32,7 @@ public interface AdoptionApi {
@DELETE("api/v1/adoptions/{id}") @DELETE("api/v1/adoptions/{id}")
Call<Void> deleteAdoption(@Path("id") Long id); Call<Void> deleteAdoption(@Path("id") Long id);
}
@HTTP(method = "DELETE", path = "api/v1/adoptions", hasBody = true)
Call<Void> bulkDeleteAdoptions(@Body BulkDeleteRequest request);
}

View File

@@ -1,12 +1,14 @@
package com.example.petstoremobile.api; package com.example.petstoremobile.api;
import com.example.petstoremobile.dtos.AppointmentDTO; import com.example.petstoremobile.dtos.AppointmentDTO;
import com.example.petstoremobile.dtos.BulkDeleteRequest;
import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.dtos.PageResponse;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.Body; import retrofit2.http.Body;
import retrofit2.http.DELETE; import retrofit2.http.DELETE;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.HTTP;
import retrofit2.http.POST; import retrofit2.http.POST;
import retrofit2.http.PUT; import retrofit2.http.PUT;
import retrofit2.http.Path; import retrofit2.http.Path;
@@ -35,4 +37,7 @@ public interface AppointmentApi {
@DELETE("api/v1/appointments/{id}") @DELETE("api/v1/appointments/{id}")
Call<Void> deleteAppointment(@Path("id") Long id); Call<Void> deleteAppointment(@Path("id") Long id);
@HTTP(method = "DELETE", path = "api/v1/appointments", hasBody = true)
Call<Void> bulkDeleteAppointments(@Body BulkDeleteRequest request);
} }

View File

@@ -1,5 +1,6 @@
package com.example.petstoremobile.api; package com.example.petstoremobile.api;
import com.example.petstoremobile.dtos.BulkDeleteRequest;
import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.ProductSupplierDTO; import com.example.petstoremobile.dtos.ProductSupplierDTO;
import retrofit2.Call; import retrofit2.Call;
@@ -34,4 +35,6 @@ public interface ProductSupplierApi {
Call<Void> deleteProductSupplier( Call<Void> deleteProductSupplier(
@Path("productId") Long productId, @Path("productId") Long productId,
@Path("supplierId") Long supplierId); @Path("supplierId") Long supplierId);
@HTTP(method = "DELETE", path = "api/v1/product-suppliers", hasBody = true)
Call<Void> bulkDeleteProductSuppliers(@Body BulkDeleteRequest request);
} }

View File

@@ -3,20 +3,20 @@ package com.example.petstoremobile.dtos;
import java.util.List; import java.util.List;
public class BulkDeleteRequest { public class BulkDeleteRequest {
private List<Long> ids; private List<String> ids;
public BulkDeleteRequest() { public BulkDeleteRequest() {
} }
public BulkDeleteRequest(List<Long> ids) { public BulkDeleteRequest(List<String> ids) {
this.ids = ids; this.ids = ids;
} }
public List<Long> getIds() { public List<String> getIds() {
return ids; return ids;
} }
public void setIds(List<Long> ids) { public void setIds(List<String> ids) {
this.ids = ids; this.ids = ids;
} }
} }

View File

@@ -17,11 +17,12 @@ import com.example.petstoremobile.adapters.AdoptionAdapter;
import com.example.petstoremobile.databinding.FragmentAdoptionBinding; import com.example.petstoremobile.databinding.FragmentAdoptionBinding;
import com.example.petstoremobile.dtos.AdoptionDTO; import com.example.petstoremobile.dtos.AdoptionDTO;
import com.example.petstoremobile.fragments.ListFragment; import com.example.petstoremobile.fragments.ListFragment;
import com.example.petstoremobile.utils.BulkDeleteHandler;
import com.example.petstoremobile.viewmodels.AdoptionViewModel; import com.example.petstoremobile.viewmodels.AdoptionViewModel;
import com.example.petstoremobile.utils.EventDecorator; import com.example.petstoremobile.utils.EventDecorator;
import com.example.petstoremobile.utils.Resource;
import com.prolificinteractive.materialcalendarview.CalendarDay; import com.prolificinteractive.materialcalendarview.CalendarDay;
import com.prolificinteractive.materialcalendarview.CalendarMode; import com.prolificinteractive.materialcalendarview.CalendarMode;
import com.prolificinteractive.materialcalendarview.MaterialCalendarView;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
@@ -36,6 +37,7 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
private List<AdoptionDTO> filteredList = new ArrayList<>(); private List<AdoptionDTO> filteredList = new ArrayList<>();
private AdoptionAdapter adapter; private AdoptionAdapter adapter;
private AdoptionViewModel viewModel; private AdoptionViewModel viewModel;
private BulkDeleteHandler bulkDeleteHandler;
private CalendarDay selectedCalendarDay; private CalendarDay selectedCalendarDay;
private boolean isMonthMode = false; private boolean isMonthMode = false;
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
@@ -61,6 +63,7 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
setupSearch(); setupSearch();
setupSwipeRefresh(); setupSwipeRefresh();
setupCalendar(); setupCalendar();
setupBulkDelete();
loadAdoptions(); loadAdoptions();
binding.fabAddAdoption.setOnClickListener(v -> openDetail(-1)); binding.fabAddAdoption.setOnClickListener(v -> openDetail(-1));
@@ -80,6 +83,19 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
return binding.getRoot(); return binding.getRoot();
} }
private void setupBulkDelete() {
bulkDeleteHandler = new BulkDeleteHandler(
this,
binding.layoutBulkDelete,
binding.tvSelectionCount,
binding.btnBulkDelete,
adapter,
"adoption",
viewModel::bulkDeleteAdoptions,
this::loadAdoptions
);
}
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
@@ -249,4 +265,11 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
*/ */
@Override @Override
public void onAdoptionClick(int position) { openDetail(position); } public void onAdoptionClick(int position) { openDetail(position); }
@Override
public void onSelectionChanged(int selectedCount) {
if (bulkDeleteHandler != null) {
bulkDeleteHandler.onSelectionChanged(selectedCount);
}
}
} }

View File

@@ -26,6 +26,7 @@ import com.example.petstoremobile.databinding.FragmentAppointmentBinding;
import com.example.petstoremobile.dtos.AppointmentDTO; import com.example.petstoremobile.dtos.AppointmentDTO;
import com.example.petstoremobile.dtos.StoreDTO; import com.example.petstoremobile.dtos.StoreDTO;
import com.example.petstoremobile.fragments.ListFragment; import com.example.petstoremobile.fragments.ListFragment;
import com.example.petstoremobile.utils.BulkDeleteHandler;
import com.example.petstoremobile.utils.Resource; import com.example.petstoremobile.utils.Resource;
import com.example.petstoremobile.utils.SpinnerUtils; import com.example.petstoremobile.utils.SpinnerUtils;
import com.example.petstoremobile.viewmodels.AppointmentViewModel; import com.example.petstoremobile.viewmodels.AppointmentViewModel;
@@ -57,6 +58,7 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
private AppointmentViewModel appointmentViewModel; private AppointmentViewModel appointmentViewModel;
private StoreViewModel storeViewModel; private StoreViewModel storeViewModel;
private AuthViewModel authViewModel; private AuthViewModel authViewModel;
private BulkDeleteHandler bulkDeleteHandler;
private CalendarDay selectedCalendarDay; private CalendarDay selectedCalendarDay;
private boolean isMonthMode = false; private boolean isMonthMode = false;
@@ -90,6 +92,7 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
setupCalendar(); setupCalendar();
setupFilterToggle(); setupFilterToggle();
setupMyAppointmentFilter(); setupMyAppointmentFilter();
setupBulkDelete();
binding.fabAddAppointment.setOnClickListener(v -> openAppointmentDetails(-1)); binding.fabAddAppointment.setOnClickListener(v -> openAppointmentDetails(-1));
@@ -110,6 +113,19 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
return binding.getRoot(); return binding.getRoot();
} }
private void setupBulkDelete() {
bulkDeleteHandler = new BulkDeleteHandler(
this,
binding.layoutBulkDelete,
binding.tvSelectionCount,
binding.btnBulkDelete,
adapter,
"appointment",
appointmentViewModel::bulkDeleteAppointments,
this::loadAppointmentData
);
}
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
@@ -303,6 +319,13 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
openAppointmentDetails(position); openAppointmentDetails(position);
} }
@Override
public void onSelectionChanged(int count) {
if (bulkDeleteHandler != null) {
bulkDeleteHandler.onSelectionChanged(count);
}
}
/** /**
* Fetches appointment data from the server with all active filters. * Fetches appointment data from the server with all active filters.
*/ */

View File

@@ -99,10 +99,7 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
binding.layoutBulkDelete, binding.layoutBulkDelete,
binding.tvSelectionCount, binding.tvSelectionCount,
binding.btnBulkDelete, binding.btnBulkDelete,
new BulkDeleteHandler.SelectableAdapter() { adapter,
@Override public List<Long> getSelectedIds() { return adapter.getSelectedIds(); }
@Override public void clearSelection() { adapter.clearSelection(); }
},
"inventory item", "inventory item",
viewModel::bulkDeleteInventory, viewModel::bulkDeleteInventory,
() -> loadInventory(true) () -> loadInventory(true)

View File

@@ -99,10 +99,7 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
binding.layoutBulkDelete, binding.layoutBulkDelete,
binding.tvSelectionCount, binding.tvSelectionCount,
binding.btnBulkDelete, binding.btnBulkDelete,
new BulkDeleteHandler.SelectableAdapter() { adapter,
@Override public List<Long> getSelectedIds() { return adapter.getSelectedIds(); }
@Override public void clearSelection() { adapter.clearSelection(); }
},
"pet", "pet",
viewModel::bulkDeletePets, viewModel::bulkDeletePets,
this::loadPetData this::loadPetData

View File

@@ -24,6 +24,7 @@ import com.example.petstoremobile.dtos.ProductDTO;
import com.example.petstoremobile.dtos.ProductSupplierDTO; import com.example.petstoremobile.dtos.ProductSupplierDTO;
import com.example.petstoremobile.dtos.SupplierDTO; import com.example.petstoremobile.dtos.SupplierDTO;
import com.example.petstoremobile.fragments.ListFragment; import com.example.petstoremobile.fragments.ListFragment;
import com.example.petstoremobile.utils.BulkDeleteHandler;
import com.example.petstoremobile.utils.Resource; import com.example.petstoremobile.utils.Resource;
import com.example.petstoremobile.utils.SpinnerUtils; import com.example.petstoremobile.utils.SpinnerUtils;
import com.example.petstoremobile.viewmodels.ProductSupplierViewModel; import com.example.petstoremobile.viewmodels.ProductSupplierViewModel;
@@ -48,6 +49,7 @@ public class ProductSupplierFragment extends Fragment
private ProductSupplierViewModel viewModel; private ProductSupplierViewModel viewModel;
private ProductViewModel productViewModel; private ProductViewModel productViewModel;
private SupplierViewModel supplierViewModel; private SupplierViewModel supplierViewModel;
private BulkDeleteHandler bulkDeleteHandler;
/** /**
* Initializes the fragment and its associated ViewModels. * Initializes the fragment and its associated ViewModels.
@@ -74,6 +76,7 @@ public class ProductSupplierFragment extends Fragment
setupSupplierFilter(); setupSupplierFilter();
setupSwipeRefresh(); setupSwipeRefresh();
setupFilterToggle(); setupFilterToggle();
setupBulkDelete();
binding.fabAddPS.setOnClickListener(v -> openDetail(-1)); binding.fabAddPS.setOnClickListener(v -> openDetail(-1));
@@ -90,6 +93,19 @@ public class ProductSupplierFragment extends Fragment
return binding.getRoot(); return binding.getRoot();
} }
private void setupBulkDelete() {
bulkDeleteHandler = new BulkDeleteHandler(
this,
binding.layoutBulkDelete,
binding.tvSelectionCount,
binding.btnBulkDelete,
adapter,
"relationship",
viewModel::bulkDeleteProductSuppliers,
this::loadData
);
}
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
@@ -265,4 +281,11 @@ public class ProductSupplierFragment extends Fragment
*/ */
@Override @Override
public void onProductSupplierClick(int position) { openDetail(position); } public void onProductSupplierClick(int position) { openDetail(position); }
@Override
public void onSelectionChanged(int count) {
if (bulkDeleteHandler != null) {
bulkDeleteHandler.onSelectionChanged(count);
}
}
} }

View File

@@ -4,6 +4,7 @@ import androidx.lifecycle.LiveData;
import com.example.petstoremobile.api.AdoptionApi; import com.example.petstoremobile.api.AdoptionApi;
import com.example.petstoremobile.dtos.AdoptionDTO; import com.example.petstoremobile.dtos.AdoptionDTO;
import com.example.petstoremobile.dtos.BulkDeleteRequest;
import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.utils.Resource; import com.example.petstoremobile.utils.Resource;
@@ -54,4 +55,11 @@ public class AdoptionRepository extends BaseRepository {
public LiveData<Resource<Void>> deleteAdoption(Long id) { public LiveData<Resource<Void>> deleteAdoption(Long id) {
return executeCall(adoptionApi.deleteAdoption(id)); return executeCall(adoptionApi.deleteAdoption(id));
} }
/**
* Sends a request to the API to delete multiple adoption records.
*/
public LiveData<Resource<Void>> bulkDeleteAdoptions(BulkDeleteRequest request) {
return executeCall(adoptionApi.bulkDeleteAdoptions(request));
}
} }

View File

@@ -4,6 +4,7 @@ import androidx.lifecycle.LiveData;
import com.example.petstoremobile.api.AppointmentApi; import com.example.petstoremobile.api.AppointmentApi;
import com.example.petstoremobile.dtos.AppointmentDTO; import com.example.petstoremobile.dtos.AppointmentDTO;
import com.example.petstoremobile.dtos.BulkDeleteRequest;
import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.utils.Resource; import com.example.petstoremobile.utils.Resource;
@@ -54,4 +55,11 @@ public class AppointmentRepository extends BaseRepository {
public LiveData<Resource<Void>> deleteAppointment(Long id) { public LiveData<Resource<Void>> deleteAppointment(Long id) {
return executeCall(appointmentApi.deleteAppointment(id)); return executeCall(appointmentApi.deleteAppointment(id));
} }
/**
* Sends a request to the API to delete multiple appointment records.
*/
public LiveData<Resource<Void>> bulkDeleteAppointments(BulkDeleteRequest request) {
return executeCall(appointmentApi.bulkDeleteAppointments(request));
}
} }

View File

@@ -3,6 +3,7 @@ package com.example.petstoremobile.repositories;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import com.example.petstoremobile.api.ProductSupplierApi; import com.example.petstoremobile.api.ProductSupplierApi;
import com.example.petstoremobile.dtos.BulkDeleteRequest;
import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.ProductSupplierDTO; import com.example.petstoremobile.dtos.ProductSupplierDTO;
import com.example.petstoremobile.utils.Resource; import com.example.petstoremobile.utils.Resource;
@@ -54,4 +55,8 @@ public class ProductSupplierRepository extends BaseRepository {
public LiveData<Resource<Void>> deleteProductSupplier(Long productId, Long supplierId) { public LiveData<Resource<Void>> deleteProductSupplier(Long productId, Long supplierId) {
return executeCall(api.deleteProductSupplier(productId, supplierId)); return executeCall(api.deleteProductSupplier(productId, supplierId));
} }
public LiveData<Resource<Void>> bulkDeleteProductSuppliers(BulkDeleteRequest request) {
return executeCall(api.bulkDeleteProductSuppliers(request));
}
} }

View File

@@ -12,25 +12,25 @@ import javax.inject.Singleton;
@Singleton @Singleton
public class PurchaseOrderRepository extends BaseRepository { public class PurchaseOrderRepository extends BaseRepository {
private final PurchaseOrderApi api; private final PurchaseOrderApi purchaseOrderApi;
@Inject @Inject
public PurchaseOrderRepository(PurchaseOrderApi api) { public PurchaseOrderRepository(PurchaseOrderApi purchaseOrderApi) {
super("PurchaseOrderRepo"); super("PurchaseOrderRepository");
this.api = api; this.purchaseOrderApi = purchaseOrderApi;
} }
/** /**
* Retrieves a paginated list of all purchase orders from the API. * Retrieves a paginated list of all purchase orders from the API.
*/ */
public LiveData<Resource<PageResponse<PurchaseOrderDTO>>> getAllPurchaseOrders(int page, int size, String query, Long storeId, String sort) { public LiveData<Resource<PageResponse<PurchaseOrderDTO>>> getAllPurchaseOrders(int page, int size, String query, Long storeId, String sort) {
return executeCall(api.getAllPurchaseOrders(page, size, query, storeId, sort)); return executeCall(purchaseOrderApi.getAllPurchaseOrders(page, size, query, storeId, sort));
} }
/** /**
* Retrieves a specific purchase order by its ID from the API. * Retrieves a specific purchase order by its ID from the API.
*/ */
public LiveData<Resource<PurchaseOrderDTO>> getPurchaseOrderById(Long id) { public LiveData<Resource<PurchaseOrderDTO>> getPurchaseOrderById(Long id) {
return executeCall(api.getPurchaseOrderById(id)); return executeCall(purchaseOrderApi.getPurchaseOrderById(id));
} }
} }

View File

@@ -12,6 +12,7 @@ import java.util.List;
/** /**
* A helper class to handle the UI and logic for bulk deletion across different fragments. * A helper class to handle the UI and logic for bulk deletion across different fragments.
* Now supports String keys to accommodate both simple and composite keys.
*/ */
public class BulkDeleteHandler { public class BulkDeleteHandler {
@@ -19,7 +20,7 @@ public class BulkDeleteHandler {
* Interface that adapters must implement to support bulk selection. * Interface that adapters must implement to support bulk selection.
*/ */
public interface SelectableAdapter { public interface SelectableAdapter {
List<Long> getSelectedIds(); List<String> getSelectedKeys();
void clearSelection(); void clearSelection();
} }
@@ -27,7 +28,7 @@ public class BulkDeleteHandler {
* Functional interface for the API call execution. * Functional interface for the API call execution.
*/ */
public interface BulkDeleteOperation { public interface BulkDeleteOperation {
LiveData<Resource<Void>> execute(List<Long> ids); LiveData<Resource<Void>> execute(List<String> keys);
} }
private final Fragment fragment; private final Fragment fragment;
@@ -82,23 +83,23 @@ public class BulkDeleteHandler {
* Shows the confirmation dialog. * Shows the confirmation dialog.
*/ */
private void confirmDelete() { private void confirmDelete() {
List<Long> ids = adapter.getSelectedIds(); List<String> keys = adapter.getSelectedKeys();
if (ids.isEmpty()) return; if (keys.isEmpty()) return;
DialogUtils.showBulkDeleteConfirmDialog(fragment.requireContext(), ids.size(), () -> performDelete(ids)); DialogUtils.showBulkDeleteConfirmDialog(fragment.requireContext(), keys.size(), () -> performDelete(keys));
} }
/** /**
* Executes the deletion via the provided operation. * Executes the deletion via the provided operation.
*/ */
private void performDelete(List<Long> ids) { private void performDelete(List<String> keys) {
operation.execute(ids).observe(fragment.getViewLifecycleOwner(), resource -> { operation.execute(keys).observe(fragment.getViewLifecycleOwner(), resource -> {
if (resource != null && resource.status != Resource.Status.LOADING) { if (resource != null && resource.status != Resource.Status.LOADING) {
if (resource.status == Resource.Status.SUCCESS) { if (resource.status == Resource.Status.SUCCESS) {
adapter.clearSelection(); adapter.clearSelection();
hideBar(); hideBar();
onSuccess.run(); onSuccess.run();
Toast.makeText(fragment.getContext(), ids.size() + " " + itemName + "(s) deleted", Toast.LENGTH_SHORT).show(); Toast.makeText(fragment.getContext(), keys.size() + " " + itemName + "(s) deleted", Toast.LENGTH_SHORT).show();
} else { } else {
Toast.makeText(fragment.getContext(), "Delete failed: " + resource.message, Toast.LENGTH_SHORT).show(); Toast.makeText(fragment.getContext(), "Delete failed: " + resource.message, Toast.LENGTH_SHORT).show();
} }

View File

@@ -5,10 +5,11 @@ import java.util.List;
/** /**
* Helper class to manage selection state in Adapters for bulk operations. * Helper class to manage selection state in Adapters for bulk operations.
* Uses String keys to support both simple Long IDs and composite keys (e.g., "id1-id2").
*/ */
public class SelectionHelper { public class SelectionHelper {
private final List<Long> selectedIds = new ArrayList<>(); private final List<String> selectedKeys = new ArrayList<>();
private boolean selectionMode = false; private boolean selectionMode = false;
private final SelectionListener listener; private final SelectionListener listener;
@@ -21,44 +22,45 @@ public class SelectionHelper {
this.listener = listener; this.listener = listener;
} }
public void toggleSelection(Long id) { public void toggleSelection(String key) {
if (id == null) return; if (key == null) return;
if (selectedIds.contains(id)) { if (selectedKeys.contains(key)) {
selectedIds.remove(id); selectedKeys.remove(key);
} else { } else {
selectedIds.add(id); selectedKeys.add(key);
} }
listener.onSelectionChanged(selectedIds.size()); listener.onSelectionChanged(selectedKeys.size());
if (selectedIds.isEmpty() && selectionMode) { if (selectedKeys.isEmpty() && selectionMode) {
selectionMode = false; selectionMode = false;
listener.onSelectionModeToggle(false); listener.onSelectionModeToggle(false);
} }
} }
public void startSelection(Long id) { public void startSelection(String key) {
if (key == null) return;
selectionMode = true; selectionMode = true;
selectedIds.add(id); selectedKeys.add(key);
listener.onSelectionChanged(selectedIds.size()); listener.onSelectionChanged(selectedKeys.size());
listener.onSelectionModeToggle(true); listener.onSelectionModeToggle(true);
} }
public boolean isSelected(Long id) { public boolean isSelected(String key) {
return selectedIds.contains(id); return selectedKeys.contains(key);
} }
public boolean isInSelectionMode() { public boolean isInSelectionMode() {
return selectionMode; return selectionMode;
} }
public List<Long> getSelectedIds() { public List<String> getSelectedKeys() {
return new ArrayList<>(selectedIds); return new ArrayList<>(selectedKeys);
} }
public void clearSelection() { public void clearSelection() {
selectedIds.clear(); selectedKeys.clear();
selectionMode = false; selectionMode = false;
listener.onSelectionChanged(0); listener.onSelectionChanged(0);
listener.onSelectionModeToggle(false); listener.onSelectionModeToggle(false);

View File

@@ -4,10 +4,13 @@ import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModel;
import com.example.petstoremobile.dtos.AdoptionDTO; import com.example.petstoremobile.dtos.AdoptionDTO;
import com.example.petstoremobile.dtos.BulkDeleteRequest;
import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.repositories.AdoptionRepository; import com.example.petstoremobile.repositories.AdoptionRepository;
import com.example.petstoremobile.utils.Resource; import com.example.petstoremobile.utils.Resource;
import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel; import dagger.hilt.android.lifecycle.HiltViewModel;
@@ -55,4 +58,11 @@ public class AdoptionViewModel extends ViewModel {
public LiveData<Resource<Void>> deleteAdoption(Long id) { public LiveData<Resource<Void>> deleteAdoption(Long id) {
return repository.deleteAdoption(id); return repository.deleteAdoption(id);
} }
/**
* Deletes multiple adoption records.
*/
public LiveData<Resource<Void>> bulkDeleteAdoptions(List<String> ids) {
return repository.bulkDeleteAdoptions(new BulkDeleteRequest(ids));
}
} }

View File

@@ -4,10 +4,13 @@ import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModel;
import com.example.petstoremobile.dtos.AppointmentDTO; import com.example.petstoremobile.dtos.AppointmentDTO;
import com.example.petstoremobile.dtos.BulkDeleteRequest;
import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.repositories.AppointmentRepository; import com.example.petstoremobile.repositories.AppointmentRepository;
import com.example.petstoremobile.utils.Resource; import com.example.petstoremobile.utils.Resource;
import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel; import dagger.hilt.android.lifecycle.HiltViewModel;
@@ -55,4 +58,11 @@ public class AppointmentViewModel extends ViewModel {
public LiveData<Resource<Void>> deleteAppointment(Long id) { public LiveData<Resource<Void>> deleteAppointment(Long id) {
return repository.deleteAppointment(id); return repository.deleteAppointment(id);
} }
/**
* Deletes multiple appointment records.
*/
public LiveData<Resource<Void>> bulkDeleteAppointments(List<String> ids) {
return repository.bulkDeleteAppointments(new BulkDeleteRequest(ids));
}
} }

View File

@@ -70,7 +70,7 @@ public class InventoryViewModel extends ViewModel {
/** /**
* Deletes multiple inventory records in a single request. * Deletes multiple inventory records in a single request.
*/ */
public LiveData<Resource<Void>> bulkDeleteInventory(List<Long> ids) { public LiveData<Resource<Void>> bulkDeleteInventory(List<String> ids) {
return inventoryRepository.bulkDeleteInventory(new BulkDeleteRequest(ids)); return inventoryRepository.bulkDeleteInventory(new BulkDeleteRequest(ids));
} }

View File

@@ -63,7 +63,7 @@ public class PetViewModel extends ViewModel {
/** /**
* Deletes multiple pet records. * Deletes multiple pet records.
*/ */
public LiveData<Resource<Void>> bulkDeletePets(List<Long> ids) { public LiveData<Resource<Void>> bulkDeletePets(List<String> ids) {
return repository.bulkDeletePets(new BulkDeleteRequest(ids)); return repository.bulkDeletePets(new BulkDeleteRequest(ids));
} }

View File

@@ -3,11 +3,14 @@ package com.example.petstoremobile.viewmodels;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModel;
import com.example.petstoremobile.dtos.BulkDeleteRequest;
import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.ProductSupplierDTO; import com.example.petstoremobile.dtos.ProductSupplierDTO;
import com.example.petstoremobile.repositories.ProductSupplierRepository; import com.example.petstoremobile.repositories.ProductSupplierRepository;
import com.example.petstoremobile.utils.Resource; import com.example.petstoremobile.utils.Resource;
import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel; import dagger.hilt.android.lifecycle.HiltViewModel;
@@ -48,4 +51,8 @@ public class ProductSupplierViewModel extends ViewModel {
public LiveData<Resource<Void>> deleteProductSupplier(Long productId, Long supplierId) { public LiveData<Resource<Void>> deleteProductSupplier(Long productId, Long supplierId) {
return repository.deleteProductSupplier(productId, supplierId); return repository.deleteProductSupplier(productId, supplierId);
} }
public LiveData<Resource<Void>> bulkDeleteProductSuppliers(List<String> ids) {
return repository.bulkDeleteProductSuppliers(new BulkDeleteRequest(ids));
}
} }

View File

@@ -62,7 +62,7 @@ public class ServiceViewModel extends ViewModel {
/** /**
* Deletes multiple services. * Deletes multiple services.
*/ */
public LiveData<Resource<Void>> bulkDeleteServices(List<Long> ids) { public LiveData<Resource<Void>> bulkDeleteServices(List<String> ids) {
return repository.bulkDeleteServices(new BulkDeleteRequest(ids)); return repository.bulkDeleteServices(new BulkDeleteRequest(ids));
} }
} }

View File

@@ -62,7 +62,7 @@ public class SupplierViewModel extends ViewModel {
/** /**
* Deletes multiple supplier records. * Deletes multiple supplier records.
*/ */
public LiveData<Resource<Void>> bulkDeleteSuppliers(List<Long> ids) { public LiveData<Resource<Void>> bulkDeleteSuppliers(List<String> ids) {
return repository.bulkDeleteSuppliers(new BulkDeleteRequest(ids)); return repository.bulkDeleteSuppliers(new BulkDeleteRequest(ids));
} }
} }

View File

@@ -70,6 +70,37 @@
android:padding="12dp" android:padding="12dp"
android:textColor="@color/text_dark"/> android:textColor="@color/text_dark"/>
<LinearLayout
android:id="@+id/layoutBulkDelete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:background="@color/primary_medium"
android:paddingStart="16dp"
android:paddingEnd="8dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:visibility="gone">
<TextView
android:id="@+id/tvSelectionCount"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="0 selected"
android:textColor="@color/white"/>
<Button
android:id="@+id/btnBulkDelete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Delete Selected"
android:backgroundTint="@color/accent_coral"
android:textColor="@color/white"/>
</LinearLayout>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshAdoption" android:id="@+id/swipeRefreshAdoption"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -144,6 +144,37 @@
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/layoutBulkDelete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:background="@color/primary_medium"
android:paddingStart="16dp"
android:paddingEnd="8dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:visibility="gone">
<TextView
android:id="@+id/tvSelectionCount"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="0 selected"
android:textColor="@color/white"/>
<Button
android:id="@+id/btnBulkDelete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Delete Selected"
android:backgroundTint="@color/accent_coral"
android:textColor="@color/white"/>
</LinearLayout>
<com.prolificinteractive.materialcalendarview.MaterialCalendarView <com.prolificinteractive.materialcalendarview.MaterialCalendarView
android:id="@+id/calendarView" android:id="@+id/calendarView"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -123,6 +123,37 @@
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/layoutBulkDelete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:background="@color/primary_medium"
android:paddingStart="16dp"
android:paddingEnd="8dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:visibility="gone">
<TextView
android:id="@+id/tvSelectionCount"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="0 selected"
android:textColor="@color/white"/>
<Button
android:id="@+id/btnBulkDelete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Delete Selected"
android:backgroundTint="@color/accent_coral"
android:textColor="@color/white"/>
</LinearLayout>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshPS" android:id="@+id/swipeRefreshPS"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -2,100 +2,117 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
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="horizontal"
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingEnd="16dp" android:paddingEnd="16dp"
android:paddingTop="16dp" android:paddingTop="16dp"
android:paddingBottom="16dp" android:paddingBottom="16dp"
android:background="@color/white"> android:background="@color/white"
android:gravity="center_vertical">
<CheckBox
android:id="@+id/cbSelectAdoption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:visibility="gone"
android:clickable="false"
android:focusable="false"/>
<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="vertical">
android:gravity="center_vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<TextView
android:id="@+id/tvAdoptionCustomerName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:maxLines="1"
android:text="Customer Name"
android:textColor="@color/text_dark"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvAdoptionStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="8dp"
android:paddingTop="3dp"
android:paddingEnd="8dp"
android:paddingBottom="3dp"
android:text="Status"
android:textAllCaps="true"
android:textColor="@color/white"
android:textSize="11sp" />
</LinearLayout>
<TextView <TextView
android:id="@+id/tvAdoptionCustomerName" android:id="@+id/tvAdoptionPetName"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_marginTop="4dp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:text="Customer Name" android:text="Pet Name"
android:textColor="@color/text_dark"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvAdoptionStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="8dp"
android:paddingTop="3dp"
android:paddingEnd="8dp"
android:paddingBottom="3dp"
android:text="Status"
android:textAllCaps="true"
android:textColor="@color/white"
android:textSize="11sp" />
</LinearLayout>
<TextView
android:id="@+id/tvAdoptionPetName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:maxLines="1"
android:text="Pet Name"
android:textColor="#888888"
android:textSize="14sp" />
<TextView
android:id="@+id/tvAdoptionStaffName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:ellipsize="end"
android:maxLines="1"
android:text="Staff: "
android:textColor="#888888"
android:textSize="13sp"
android:textStyle="italic" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="8dp">
<TextView
android:id="@+id/tvAdoptionFee"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="$0.00"
android:textColor="@color/accent_coral"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvAdoptionDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Date"
android:textColor="#888888" android:textColor="#888888"
android:textSize="13sp" /> android:textSize="14sp" />
<TextView
android:id="@+id/tvAdoptionStaffName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:ellipsize="end"
android:maxLines="1"
android:text="Staff: "
android:textColor="#888888"
android:textSize="13sp"
android:textStyle="italic" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="8dp">
<TextView
android:id="@+id/tvAdoptionFee"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="$0.00"
android:textColor="@color/accent_coral"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvAdoptionDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Date"
android:textColor="#888888"
android:textSize="13sp" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#F0F0F0"
android:layout_marginTop="12dp"/>
</LinearLayout> </LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#F0F0F0"
android:layout_marginTop="12dp"/>
</LinearLayout> </LinearLayout>

View File

@@ -1,83 +1,99 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
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="horizontal"
android:padding="16dp" android:padding="16dp"
android:background="@android:color/white"> android:background="@android:color/white"
android:gravity="center_vertical">
<CheckBox
android:id="@+id/cbSelectAppointment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:visibility="gone"
android:clickable="false"
android:focusable="false"/>
<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="vertical">
<TextView <LinearLayout
android:id="@+id/tvCustomerName" android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:orientation="horizontal">
android:text="Customer Name"
android:textColor="#000000" <TextView
android:textSize="18sp" android:id="@+id/tvCustomerName"
android:textStyle="bold" /> android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Customer Name"
android:textColor="#000000"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvAppointmentStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#4CAF50"
android:paddingStart="8dp"
android:paddingTop="4dp"
android:paddingEnd="8dp"
android:paddingBottom="4dp"
android:text="Status"
android:textColor="#FFFFFF"
android:textSize="12sp" />
</LinearLayout>
<TextView <TextView
android:id="@+id/tvAppointmentStatus" android:id="@+id/tvApptPetName"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="#4CAF50" android:layout_marginTop="4dp"
android:paddingStart="8dp" android:text="Pet: name"
android:paddingTop="4dp" android:textColor="#666666"
android:paddingEnd="8dp" android:textSize="14sp" />
android:paddingBottom="4dp"
android:text="Status" <TextView
android:textColor="#FFFFFF" android:id="@+id/tvServiceType"
android:textSize="12sp" /> android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:text="Service type"
android:textColor="#666666"
android:textSize="14sp" />
<TextView
android:id="@+id/tvStaffName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:text="Staff: name"
android:textColor="#666666"
android:textSize="14sp" />
<TextView
android:id="@+id/tvDateTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="Date and time"
android:textColor="#2196F3"
android:textSize="14sp"
android:textStyle="bold" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#EEEEEE"
android:layout_marginTop="12dp"/>
</LinearLayout> </LinearLayout>
<TextView
android:id="@+id/tvApptPetName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="Pet: name"
android:textColor="#666666"
android:textSize="14sp" />
<TextView
android:id="@+id/tvServiceType"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:text="Service type"
android:textColor="#666666"
android:textSize="14sp" />
<TextView
android:id="@+id/tvStaffName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:text="Staff: name"
android:textColor="#666666"
android:textSize="14sp" />
<TextView
android:id="@+id/tvDateTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="Date and time"
android:textColor="#2196F3"
android:textSize="14sp"
android:textStyle="bold" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#EEEEEE"
android:layout_marginTop="12dp"/>
</LinearLayout> </LinearLayout>

View File

@@ -2,58 +2,75 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
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="horizontal"
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingEnd="16dp" android:paddingEnd="16dp"
android:paddingTop="16dp" android:paddingTop="16dp"
android:paddingBottom="16dp" android:paddingBottom="16dp"
android:background="@color/white"> android:background="@color/white"
android:gravity="center_vertical">
<TextView <CheckBox
android:id="@+id/tvPSProductName" android:id="@+id/cbSelectProductSupplier"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:text="Product Name"
android:textColor="@color/text_dark"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvPSSupplierName"
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:layout_marginEnd="12dp"
android:ellipsize="end" android:visibility="gone"
android:maxLines="1" android:clickable="false"
android:text="Supplier Name" android:focusable="false"/>
android:textColor="#888888"
android:textSize="14sp" />
<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="vertical">
android:gravity="center_vertical"
android:layout_marginTop="8dp">
<TextView <TextView
android:id="@+id/tvPSCost" android:id="@+id/tvPSProductName"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:ellipsize="end"
android:text="$0.00" android:maxLines="1"
android:textColor="@color/accent_coral" android:text="Product Name"
android:textSize="16sp" android:textColor="@color/text_dark"
android:textSize="18sp"
android:textStyle="bold" /> android:textStyle="bold" />
<TextView
android:id="@+id/tvPSSupplierName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:maxLines="1"
android:text="Supplier Name"
android:textColor="#888888"
android:textSize="14sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="8dp">
<TextView
android:id="@+id/tvPSCost"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="$0.00"
android:textColor="@color/accent_coral"
android:textSize="16sp"
android:textStyle="bold" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#F0F0F0"
android:layout_marginTop="12dp"/>
</LinearLayout> </LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#F0F0F0"
android:layout_marginTop="12dp"/>
</LinearLayout> </LinearLayout>