added bulk delete for ProductSupplier, appointments, and adoptions

This commit is contained in:
Alex
2026-04-07 16:20:51 -06:00
parent 4ccfe55174
commit c232f193d1
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.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.petstoremobile.databinding.ItemAdoptionBinding;
import com.example.petstoremobile.dtos.AdoptionDTO;
import com.example.petstoremobile.utils.BulkDeleteHandler;
import com.example.petstoremobile.utils.SelectionHelper;
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 OnAdoptionClickListener listener;
private final SelectionHelper selectionHelper;
public interface OnAdoptionClickListener {
void onAdoptionClick(int position);
void onSelectionChanged(int count);
}
public AdoptionAdapter(List<AdoptionDTO> adoptionList, OnAdoptionClickListener listener) {
this.adoptionList = adoptionList;
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 {
@@ -68,9 +94,34 @@ public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.Adopti
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
public int getItemCount() { return adoptionList.size(); }
}
}

View File

@@ -2,26 +2,52 @@ package com.example.petstoremobile.adapters;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.petstoremobile.databinding.ItemAppointmentBinding;
import com.example.petstoremobile.dtos.AppointmentDTO;
import com.example.petstoremobile.utils.BulkDeleteHandler;
import com.example.petstoremobile.utils.SelectionHelper;
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 OnAppointmentClickListener appointmentClickListener;
private final SelectionHelper selectionHelper;
public interface OnAppointmentClickListener {
void onAppointmentClick(int position);
void onSelectionChanged(int count);
}
public AppointmentAdapter(List<AppointmentDTO> appointmentList,
OnAppointmentClickListener appointmentClickListener) {
this.appointmentList = appointmentList;
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 {
@@ -70,11 +96,36 @@ public class AppointmentAdapter extends RecyclerView.Adapter<AppointmentAdapter.
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
public int getItemCount() {
return appointmentList.size();
}
}
}

View File

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

View File

@@ -57,8 +57,8 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
}
@Override
public List<Long> getSelectedIds() {
return selectionHelper.getSelectedIds();
public List<String> getSelectedKeys() {
return selectionHelper.getSelectedKeys();
}
@Override
@@ -118,10 +118,12 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
binding.ivPetProfile.setImageResource(R.drawable.placeholder);
}
String key = String.valueOf(pet.getPetId());
// Bulk delete selection mode
if (selectionHelper.isInSelectionMode()) {
binding.cbSelectPet.setVisibility(View.VISIBLE);
binding.cbSelectPet.setChecked(selectionHelper.isSelected(pet.getPetId()));
binding.cbSelectPet.setChecked(selectionHelper.isSelected(key));
} else {
binding.cbSelectPet.setVisibility(View.GONE);
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
holder.itemView.setOnClickListener(v -> {
if (selectionHelper.isInSelectionMode()) {
selectionHelper.toggleSelection(pet.getPetId());
selectionHelper.toggleSelection(key);
notifyItemChanged(position);
} else {
petClickListener.onPetClick(position);
@@ -139,7 +141,7 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
holder.itemView.setOnLongClickListener(v -> {
if (!selectionHelper.isInSelectionMode()) {
selectionHelper.startSelection(pet.getPetId());
selectionHelper.startSelection(key);
}
return true;
});
@@ -149,4 +151,4 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
public int getItemCount() {
return petList.size();
}
}
}

View File

@@ -1,25 +1,54 @@
package com.example.petstoremobile.adapters;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.petstoremobile.databinding.ItemProductSupplierBinding;
import com.example.petstoremobile.dtos.ProductSupplierDTO;
import com.example.petstoremobile.utils.BulkDeleteHandler;
import com.example.petstoremobile.utils.SelectionHelper;
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 OnProductSupplierClickListener listener;
private final List<ProductSupplierDTO> list;
private final OnProductSupplierClickListener listener;
private final SelectionHelper selectionHelper;
public interface OnProductSupplierClickListener {
void onProductSupplierClick(int position);
void onSelectionChanged(int count);
}
public ProductSupplierAdapter(List<ProductSupplierDTO> list, OnProductSupplierClickListener listener) {
this.list = list;
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 {
@@ -46,9 +75,35 @@ public class ProductSupplierAdapter extends RecyclerView.Adapter<ProductSupplier
binding.tvPSProductName.setText(ps.getProductName() != null ? ps.getProductName() : "");
binding.tvPSSupplierName.setText("Supplier: " + (ps.getSupplierName() != null ? ps.getSupplierName() : ""));
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
public int getItemCount() { return list.size(); }
}
}

View File

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

View File

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

View File

@@ -1,12 +1,14 @@
package com.example.petstoremobile.api;
import com.example.petstoremobile.dtos.AdoptionDTO;
import com.example.petstoremobile.dtos.BulkDeleteRequest;
import com.example.petstoremobile.dtos.PageResponse;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.GET;
import retrofit2.http.HTTP;
import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Path;
@@ -30,5 +32,7 @@ public interface AdoptionApi {
@DELETE("api/v1/adoptions/{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;
import com.example.petstoremobile.dtos.AppointmentDTO;
import com.example.petstoremobile.dtos.BulkDeleteRequest;
import com.example.petstoremobile.dtos.PageResponse;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.GET;
import retrofit2.http.HTTP;
import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Path;
@@ -35,4 +37,7 @@ public interface AppointmentApi {
@DELETE("api/v1/appointments/{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;
import com.example.petstoremobile.dtos.BulkDeleteRequest;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.ProductSupplierDTO;
import retrofit2.Call;
@@ -34,4 +35,6 @@ public interface ProductSupplierApi {
Call<Void> deleteProductSupplier(
@Path("productId") Long productId,
@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;
public class BulkDeleteRequest {
private List<Long> ids;
private List<String> ids;
public BulkDeleteRequest() {
}
public BulkDeleteRequest(List<Long> ids) {
public BulkDeleteRequest(List<String> ids) {
this.ids = ids;
}
public List<Long> getIds() {
public List<String> getIds() {
return ids;
}
public void setIds(List<Long> ids) {
public void setIds(List<String> 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.dtos.AdoptionDTO;
import com.example.petstoremobile.fragments.ListFragment;
import com.example.petstoremobile.utils.BulkDeleteHandler;
import com.example.petstoremobile.viewmodels.AdoptionViewModel;
import com.example.petstoremobile.utils.EventDecorator;
import com.example.petstoremobile.utils.Resource;
import com.prolificinteractive.materialcalendarview.CalendarDay;
import com.prolificinteractive.materialcalendarview.CalendarMode;
import com.prolificinteractive.materialcalendarview.MaterialCalendarView;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
@@ -36,6 +37,7 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
private List<AdoptionDTO> filteredList = new ArrayList<>();
private AdoptionAdapter adapter;
private AdoptionViewModel viewModel;
private BulkDeleteHandler bulkDeleteHandler;
private CalendarDay selectedCalendarDay;
private boolean isMonthMode = false;
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
@@ -61,6 +63,7 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
setupSearch();
setupSwipeRefresh();
setupCalendar();
setupBulkDelete();
loadAdoptions();
binding.fabAddAdoption.setOnClickListener(v -> openDetail(-1));
@@ -80,6 +83,19 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
return binding.getRoot();
}
private void setupBulkDelete() {
bulkDeleteHandler = new BulkDeleteHandler(
this,
binding.layoutBulkDelete,
binding.tvSelectionCount,
binding.btnBulkDelete,
adapter,
"adoption",
viewModel::bulkDeleteAdoptions,
this::loadAdoptions
);
}
@Override
public void onDestroyView() {
super.onDestroyView();
@@ -249,4 +265,11 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
*/
@Override
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.StoreDTO;
import com.example.petstoremobile.fragments.ListFragment;
import com.example.petstoremobile.utils.BulkDeleteHandler;
import com.example.petstoremobile.utils.Resource;
import com.example.petstoremobile.utils.SpinnerUtils;
import com.example.petstoremobile.viewmodels.AppointmentViewModel;
@@ -57,6 +58,7 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
private AppointmentViewModel appointmentViewModel;
private StoreViewModel storeViewModel;
private AuthViewModel authViewModel;
private BulkDeleteHandler bulkDeleteHandler;
private CalendarDay selectedCalendarDay;
private boolean isMonthMode = false;
@@ -90,6 +92,7 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
setupCalendar();
setupFilterToggle();
setupMyAppointmentFilter();
setupBulkDelete();
binding.fabAddAppointment.setOnClickListener(v -> openAppointmentDetails(-1));
@@ -110,6 +113,19 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
return binding.getRoot();
}
private void setupBulkDelete() {
bulkDeleteHandler = new BulkDeleteHandler(
this,
binding.layoutBulkDelete,
binding.tvSelectionCount,
binding.btnBulkDelete,
adapter,
"appointment",
appointmentViewModel::bulkDeleteAppointments,
this::loadAppointmentData
);
}
@Override
public void onDestroyView() {
super.onDestroyView();
@@ -303,6 +319,13 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
openAppointmentDetails(position);
}
@Override
public void onSelectionChanged(int count) {
if (bulkDeleteHandler != null) {
bulkDeleteHandler.onSelectionChanged(count);
}
}
/**
* 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.tvSelectionCount,
binding.btnBulkDelete,
new BulkDeleteHandler.SelectableAdapter() {
@Override public List<Long> getSelectedIds() { return adapter.getSelectedIds(); }
@Override public void clearSelection() { adapter.clearSelection(); }
},
adapter,
"inventory item",
viewModel::bulkDeleteInventory,
() -> loadInventory(true)

View File

@@ -99,10 +99,7 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
binding.layoutBulkDelete,
binding.tvSelectionCount,
binding.btnBulkDelete,
new BulkDeleteHandler.SelectableAdapter() {
@Override public List<Long> getSelectedIds() { return adapter.getSelectedIds(); }
@Override public void clearSelection() { adapter.clearSelection(); }
},
adapter,
"pet",
viewModel::bulkDeletePets,
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.SupplierDTO;
import com.example.petstoremobile.fragments.ListFragment;
import com.example.petstoremobile.utils.BulkDeleteHandler;
import com.example.petstoremobile.utils.Resource;
import com.example.petstoremobile.utils.SpinnerUtils;
import com.example.petstoremobile.viewmodels.ProductSupplierViewModel;
@@ -48,6 +49,7 @@ public class ProductSupplierFragment extends Fragment
private ProductSupplierViewModel viewModel;
private ProductViewModel productViewModel;
private SupplierViewModel supplierViewModel;
private BulkDeleteHandler bulkDeleteHandler;
/**
* Initializes the fragment and its associated ViewModels.
@@ -74,6 +76,7 @@ public class ProductSupplierFragment extends Fragment
setupSupplierFilter();
setupSwipeRefresh();
setupFilterToggle();
setupBulkDelete();
binding.fabAddPS.setOnClickListener(v -> openDetail(-1));
@@ -90,6 +93,19 @@ public class ProductSupplierFragment extends Fragment
return binding.getRoot();
}
private void setupBulkDelete() {
bulkDeleteHandler = new BulkDeleteHandler(
this,
binding.layoutBulkDelete,
binding.tvSelectionCount,
binding.btnBulkDelete,
adapter,
"relationship",
viewModel::bulkDeleteProductSuppliers,
this::loadData
);
}
@Override
public void onDestroyView() {
super.onDestroyView();
@@ -265,4 +281,11 @@ public class ProductSupplierFragment extends Fragment
*/
@Override
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.dtos.AdoptionDTO;
import com.example.petstoremobile.dtos.BulkDeleteRequest;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.utils.Resource;
@@ -54,4 +55,11 @@ public class AdoptionRepository extends BaseRepository {
public LiveData<Resource<Void>> deleteAdoption(Long 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.dtos.AppointmentDTO;
import com.example.petstoremobile.dtos.BulkDeleteRequest;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.utils.Resource;
@@ -54,4 +55,11 @@ public class AppointmentRepository extends BaseRepository {
public LiveData<Resource<Void>> deleteAppointment(Long 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 com.example.petstoremobile.api.ProductSupplierApi;
import com.example.petstoremobile.dtos.BulkDeleteRequest;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.ProductSupplierDTO;
import com.example.petstoremobile.utils.Resource;
@@ -54,4 +55,8 @@ public class ProductSupplierRepository extends BaseRepository {
public LiveData<Resource<Void>> deleteProductSupplier(Long productId, Long 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
public class PurchaseOrderRepository extends BaseRepository {
private final PurchaseOrderApi api;
private final PurchaseOrderApi purchaseOrderApi;
@Inject
public PurchaseOrderRepository(PurchaseOrderApi api) {
super("PurchaseOrderRepo");
this.api = api;
public PurchaseOrderRepository(PurchaseOrderApi purchaseOrderApi) {
super("PurchaseOrderRepository");
this.purchaseOrderApi = purchaseOrderApi;
}
/**
* 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) {
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.
*/
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.
* Now supports String keys to accommodate both simple and composite keys.
*/
public class BulkDeleteHandler {
@@ -19,7 +20,7 @@ public class BulkDeleteHandler {
* Interface that adapters must implement to support bulk selection.
*/
public interface SelectableAdapter {
List<Long> getSelectedIds();
List<String> getSelectedKeys();
void clearSelection();
}
@@ -27,7 +28,7 @@ public class BulkDeleteHandler {
* Functional interface for the API call execution.
*/
public interface BulkDeleteOperation {
LiveData<Resource<Void>> execute(List<Long> ids);
LiveData<Resource<Void>> execute(List<String> keys);
}
private final Fragment fragment;
@@ -82,23 +83,23 @@ public class BulkDeleteHandler {
* Shows the confirmation dialog.
*/
private void confirmDelete() {
List<Long> ids = adapter.getSelectedIds();
if (ids.isEmpty()) return;
List<String> keys = adapter.getSelectedKeys();
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.
*/
private void performDelete(List<Long> ids) {
operation.execute(ids).observe(fragment.getViewLifecycleOwner(), resource -> {
private void performDelete(List<String> keys) {
operation.execute(keys).observe(fragment.getViewLifecycleOwner(), resource -> {
if (resource != null && resource.status != Resource.Status.LOADING) {
if (resource.status == Resource.Status.SUCCESS) {
adapter.clearSelection();
hideBar();
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 {
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.
* Uses String keys to support both simple Long IDs and composite keys (e.g., "id1-id2").
*/
public class SelectionHelper {
private final List<Long> selectedIds = new ArrayList<>();
private final List<String> selectedKeys = new ArrayList<>();
private boolean selectionMode = false;
private final SelectionListener listener;
@@ -21,44 +22,45 @@ public class SelectionHelper {
this.listener = listener;
}
public void toggleSelection(Long id) {
if (id == null) return;
public void toggleSelection(String key) {
if (key == null) return;
if (selectedIds.contains(id)) {
selectedIds.remove(id);
if (selectedKeys.contains(key)) {
selectedKeys.remove(key);
} 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;
listener.onSelectionModeToggle(false);
}
}
public void startSelection(Long id) {
public void startSelection(String key) {
if (key == null) return;
selectionMode = true;
selectedIds.add(id);
listener.onSelectionChanged(selectedIds.size());
selectedKeys.add(key);
listener.onSelectionChanged(selectedKeys.size());
listener.onSelectionModeToggle(true);
}
public boolean isSelected(Long id) {
return selectedIds.contains(id);
public boolean isSelected(String key) {
return selectedKeys.contains(key);
}
public boolean isInSelectionMode() {
return selectionMode;
}
public List<Long> getSelectedIds() {
return new ArrayList<>(selectedIds);
public List<String> getSelectedKeys() {
return new ArrayList<>(selectedKeys);
}
public void clearSelection() {
selectedIds.clear();
selectedKeys.clear();
selectionMode = false;
listener.onSelectionChanged(0);
listener.onSelectionModeToggle(false);

View File

@@ -4,10 +4,13 @@ import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.example.petstoremobile.dtos.AdoptionDTO;
import com.example.petstoremobile.dtos.BulkDeleteRequest;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.repositories.AdoptionRepository;
import com.example.petstoremobile.utils.Resource;
import java.util.List;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@@ -55,4 +58,11 @@ public class AdoptionViewModel extends ViewModel {
public LiveData<Resource<Void>> deleteAdoption(Long 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 com.example.petstoremobile.dtos.AppointmentDTO;
import com.example.petstoremobile.dtos.BulkDeleteRequest;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.repositories.AppointmentRepository;
import com.example.petstoremobile.utils.Resource;
import java.util.List;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@@ -55,4 +58,11 @@ public class AppointmentViewModel extends ViewModel {
public LiveData<Resource<Void>> deleteAppointment(Long 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.
*/
public LiveData<Resource<Void>> bulkDeleteInventory(List<Long> ids) {
public LiveData<Resource<Void>> bulkDeleteInventory(List<String> ids) {
return inventoryRepository.bulkDeleteInventory(new BulkDeleteRequest(ids));
}

View File

@@ -63,7 +63,7 @@ public class PetViewModel extends ViewModel {
/**
* 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));
}

View File

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

View File

@@ -62,7 +62,7 @@ public class SupplierViewModel extends ViewModel {
/**
* 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));
}
}

View File

@@ -70,6 +70,37 @@
android:padding="12dp"
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
android:id="@+id/swipeRefreshAdoption"
android:layout_width="match_parent"

View File

@@ -144,6 +144,37 @@
</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
android:id="@+id/calendarView"
android:layout_width="match_parent"

View File

@@ -123,6 +123,37 @@
</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
android:id="@+id/swipeRefreshPS"
android:layout_width="match_parent"

View File

@@ -2,100 +2,117 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:orientation="horizontal"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingTop="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
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
android:orientation="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
android:id="@+id/tvAdoptionCustomerName"
android:layout_width="0dp"
android:id="@+id/tvAdoptionPetName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginTop="4dp"
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
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:text="Pet Name"
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>
<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"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:orientation="horizontal"
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
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:orientation="vertical">
<TextView
android:id="@+id/tvCustomerName"
android:layout_width="0dp"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Customer Name"
android:textColor="#000000"
android:textSize="18sp"
android:textStyle="bold" />
android:orientation="horizontal">
<TextView
android:id="@+id/tvCustomerName"
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
android:id="@+id/tvAppointmentStatus"
android:id="@+id/tvApptPetName"
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" />
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>
<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>

View File

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