bluk delete added for Service and suppliers on andriod
This commit is contained in:
@@ -1,30 +1,65 @@
|
||||
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.ItemServiceBinding;
|
||||
import com.example.petstoremobile.dtos.ServiceDTO;
|
||||
import com.example.petstoremobile.utils.BulkDeleteHandler;
|
||||
import com.example.petstoremobile.utils.SelectionHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ServiceAdapter extends RecyclerView.Adapter<ServiceAdapter.ServiceViewHolder> {
|
||||
/**
|
||||
* Adapter class for displaying a list of services in a RecyclerView.
|
||||
*/
|
||||
public class ServiceAdapter extends RecyclerView.Adapter<ServiceAdapter.ServiceViewHolder> implements BulkDeleteHandler.SelectableAdapter {
|
||||
|
||||
private List<ServiceDTO> serviceList;
|
||||
private OnServiceClickListener serviceClickListener;
|
||||
private final List<ServiceDTO> serviceList;
|
||||
private final OnServiceClickListener clickListener;
|
||||
private final SelectionHelper selectionHelper;
|
||||
|
||||
// Interface for service click on recycler view
|
||||
/**
|
||||
* Interface for handling clicks on service items.
|
||||
*/
|
||||
public interface OnServiceClickListener {
|
||||
void onServiceClick(int position);
|
||||
void onSelectionChanged(int count);
|
||||
}
|
||||
|
||||
//Constructor
|
||||
public ServiceAdapter(List<ServiceDTO> serviceList, OnServiceClickListener serviceClickListener) {
|
||||
this.serviceList = serviceList;
|
||||
this.serviceClickListener = serviceClickListener;
|
||||
public ServiceAdapter(List<ServiceDTO> serviceList, OnServiceClickListener clickListener) {
|
||||
this.serviceList = serviceList;
|
||||
this.clickListener = clickListener;
|
||||
this.selectionHelper = new SelectionHelper(new SelectionHelper.SelectionListener() {
|
||||
@Override
|
||||
public void onSelectionChanged(int count) {
|
||||
clickListener.onSelectionChanged(count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectionModeToggle(boolean selectionMode) {
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Get the controls of each row in recycler view
|
||||
@Override
|
||||
public List<Long> getSelectedIds() {
|
||||
return selectionHelper.getSelectedIds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionHelper.clearSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder class for service items.
|
||||
*/
|
||||
public static class ServiceViewHolder extends RecyclerView.ViewHolder {
|
||||
final ItemServiceBinding binding;
|
||||
|
||||
@@ -50,15 +85,37 @@ public class ServiceAdapter extends RecyclerView.Adapter<ServiceAdapter.ServiceV
|
||||
|
||||
binding.tvServiceName.setText(service.getServiceName());
|
||||
binding.tvServiceDesc.setText(service.getServiceDesc());
|
||||
binding.tvServiceDuration.setText("Duration: " + service.getServiceDuration() + " min");
|
||||
binding.tvServicePrice.setText("$" + String.format("%.2f", service.getServicePrice()));
|
||||
binding.tvServiceDuration.setText(service.getServiceDuration() != null ? service.getServiceDuration() + " mins" : "0 mins");
|
||||
binding.tvServicePrice.setText(service.getServicePrice() != null ? "$" + String.format("%.2f", service.getServicePrice()) : "$0.00");
|
||||
|
||||
//when a row is clicked, open the detail view
|
||||
holder.itemView.setOnClickListener(v -> serviceClickListener.onServiceClick(position));
|
||||
// Bulk delete selection mode
|
||||
if (selectionHelper.isInSelectionMode()) {
|
||||
binding.cbSelectService.setVisibility(View.VISIBLE);
|
||||
binding.cbSelectService.setChecked(selectionHelper.isSelected(service.getServiceId()));
|
||||
} else {
|
||||
binding.cbSelectService.setVisibility(View.GONE);
|
||||
binding.cbSelectService.setChecked(false);
|
||||
}
|
||||
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
if (selectionHelper.isInSelectionMode()) {
|
||||
selectionHelper.toggleSelection(service.getServiceId());
|
||||
notifyItemChanged(position);
|
||||
} else {
|
||||
clickListener.onServiceClick(position);
|
||||
}
|
||||
});
|
||||
|
||||
holder.itemView.setOnLongClickListener(v -> {
|
||||
if (!selectionHelper.isInSelectionMode()) {
|
||||
selectionHelper.startSelection(service.getServiceId());
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return serviceList.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,56 @@
|
||||
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.ItemSupplierBinding;
|
||||
import com.example.petstoremobile.dtos.SupplierDTO;
|
||||
import com.example.petstoremobile.utils.BulkDeleteHandler;
|
||||
import com.example.petstoremobile.utils.SelectionHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SupplierAdapter extends RecyclerView.Adapter<SupplierAdapter.SupplierViewHolder> {
|
||||
public class SupplierAdapter extends RecyclerView.Adapter<SupplierAdapter.SupplierViewHolder> implements BulkDeleteHandler.SelectableAdapter {
|
||||
|
||||
private List<SupplierDTO> supplierList;
|
||||
private OnSupplierClickListener supplierClickListener;
|
||||
private final List<SupplierDTO> supplierList;
|
||||
private final OnSupplierClickListener supplierClickListener;
|
||||
private final SelectionHelper selectionHelper;
|
||||
|
||||
// Interface for supplier click on recycler view
|
||||
public interface OnSupplierClickListener {
|
||||
void onSupplierClick(int position);
|
||||
void onSelectionChanged(int count);
|
||||
}
|
||||
|
||||
//Constructor
|
||||
public SupplierAdapter(List<SupplierDTO> supplierList, OnSupplierClickListener supplierClickListener) {
|
||||
this.supplierList = supplierList;
|
||||
this.supplierClickListener = supplierClickListener;
|
||||
this.selectionHelper = new SelectionHelper(new SelectionHelper.SelectionListener() {
|
||||
@Override
|
||||
public void onSelectionChanged(int count) {
|
||||
supplierClickListener.onSelectionChanged(count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectionModeToggle(boolean selectionMode) {
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getSelectedIds() {
|
||||
return selectionHelper.getSelectedIds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionHelper.clearSelection();
|
||||
}
|
||||
|
||||
// Get the controls of each row in recycler view
|
||||
@@ -53,12 +82,35 @@ public class SupplierAdapter extends RecyclerView.Adapter<SupplierAdapter.Suppli
|
||||
binding.tvSupEmail.setText(supplier.getSupEmail());
|
||||
binding.tvSupPhone.setText(supplier.getSupPhone());
|
||||
|
||||
// Bulk delete selection mode
|
||||
if (selectionHelper.isInSelectionMode()) {
|
||||
binding.cbSelectSupplier.setVisibility(View.VISIBLE);
|
||||
binding.cbSelectSupplier.setChecked(selectionHelper.isSelected(supplier.getSupId()));
|
||||
} else {
|
||||
binding.cbSelectSupplier.setVisibility(View.GONE);
|
||||
binding.cbSelectSupplier.setChecked(false);
|
||||
}
|
||||
|
||||
//when a row is clicked, open the detail view
|
||||
holder.itemView.setOnClickListener(v -> supplierClickListener.onSupplierClick(position));
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
if (selectionHelper.isInSelectionMode()) {
|
||||
selectionHelper.toggleSelection(supplier.getSupId());
|
||||
notifyItemChanged(position);
|
||||
} else {
|
||||
supplierClickListener.onSupplierClick(position);
|
||||
}
|
||||
});
|
||||
|
||||
holder.itemView.setOnLongClickListener(v -> {
|
||||
if (!selectionHelper.isInSelectionMode()) {
|
||||
selectionHelper.startSelection(supplier.getSupId());
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return supplierList.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.ServiceDTO;
|
||||
|
||||
@@ -7,6 +8,7 @@ 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;
|
||||
@@ -38,4 +40,8 @@ public interface ServiceApi {
|
||||
// Delete service
|
||||
@DELETE("api/v1/services/{id}")
|
||||
Call<Void> deleteService(@Path("id") Long id);
|
||||
|
||||
// Bulk delete services
|
||||
@HTTP(method = "DELETE", path = "api/v1/services", hasBody = true)
|
||||
Call<Void> bulkDeleteServices(@Body BulkDeleteRequest request);
|
||||
}
|
||||
|
||||
@@ -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.SupplierDTO;
|
||||
|
||||
@@ -7,6 +8,7 @@ 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;
|
||||
@@ -38,4 +40,8 @@ public interface SupplierApi {
|
||||
// Delete supplier
|
||||
@DELETE("api/v1/suppliers/{id}")
|
||||
Call<Void> deleteSupplier(@Path("id") Long id);
|
||||
|
||||
// Bulk delete suppliers
|
||||
@HTTP(method = "DELETE", path = "api/v1/suppliers", hasBody = true)
|
||||
Call<Void> bulkDeleteSuppliers(@Body BulkDeleteRequest request);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
package com.example.petstoremobile.fragments.listfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
@@ -17,11 +9,21 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.adapters.ServiceAdapter;
|
||||
import com.example.petstoremobile.databinding.FragmentServiceBinding;
|
||||
import com.example.petstoremobile.dtos.ServiceDTO;
|
||||
import com.example.petstoremobile.fragments.ListFragment;
|
||||
import com.example.petstoremobile.utils.BulkDeleteHandler;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.viewmodels.ServiceViewModel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -29,16 +31,28 @@ import java.util.List;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment class for displaying a list of services in a RecyclerView.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class ServiceFragment extends Fragment implements ServiceAdapter.OnServiceClickListener {
|
||||
|
||||
private static final String TAG = "ServiceFragment";
|
||||
private static final int PAGE_SIZE = 20;
|
||||
|
||||
private FragmentServiceBinding binding;
|
||||
private List<ServiceDTO> serviceList = new ArrayList<>();
|
||||
private final List<ServiceDTO> serviceList = new ArrayList<>();
|
||||
private ServiceAdapter adapter;
|
||||
private ServiceViewModel viewModel;
|
||||
private BulkDeleteHandler bulkDeleteHandler;
|
||||
|
||||
// Pagination
|
||||
private int currentPage = 0;
|
||||
private boolean isLastPage = false;
|
||||
private boolean isLoading = false;
|
||||
|
||||
/**
|
||||
* Initializes the fragment and its associated ServiceViewModel.
|
||||
* Initializes the fragment and its associated ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
@@ -58,12 +72,11 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
|
||||
setupSearch();
|
||||
setupSwipeRefresh();
|
||||
setupFilterToggle();
|
||||
loadServiceData();
|
||||
setupBulkDelete();
|
||||
loadServices(true);
|
||||
|
||||
//Add button to opens the add dialog
|
||||
binding.fabAddService.setOnClickListener(v -> openServiceDetails(-1));
|
||||
binding.fabAddService.setOnClickListener(v -> openDetail(null));
|
||||
|
||||
//Make the hamburger button open the drawer from listFragment
|
||||
binding.btnHamburger.setOnClickListener(v -> {
|
||||
Fragment parent = getParentFragment();
|
||||
if (parent != null) {
|
||||
@@ -77,6 +90,19 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
private void setupBulkDelete() {
|
||||
bulkDeleteHandler = new BulkDeleteHandler(
|
||||
this,
|
||||
binding.layoutBulkDelete,
|
||||
binding.tvSelectionCount,
|
||||
binding.btnBulkDelete,
|
||||
adapter,
|
||||
"service",
|
||||
viewModel::bulkDeleteServices,
|
||||
() -> loadServices(true)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
@@ -95,100 +121,121 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
|
||||
binding.layoutFilter.setVisibility(View.GONE);
|
||||
binding.btnToggleFilter.setImageResource(android.R.drawable.ic_menu_search);
|
||||
|
||||
// Reset search when closing
|
||||
// Reset filters when closing
|
||||
binding.etSearchService.setText("");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the search bar for filtering.
|
||||
* Sets up the search bar for filtering.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
binding.etSearchService.addTextChangedListener(new TextWatcher() {
|
||||
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
loadServiceData();
|
||||
loadServices(true);
|
||||
}
|
||||
@Override public void afterTextChanged(Editable s) {}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the SwipeRefreshLayout to allow manual reloading of service data.
|
||||
* Initializes the RecyclerView with a layout manager and adapter.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new ServiceAdapter(serviceList, this);
|
||||
binding.recyclerViewServices.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
binding.recyclerViewServices.setAdapter(adapter);
|
||||
|
||||
binding.recyclerViewServices.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||
if (dy <= 0) return;
|
||||
LinearLayoutManager lm = (LinearLayoutManager) binding.recyclerViewServices.getLayoutManager();
|
||||
if (lm == null) return;
|
||||
int visible = lm.getChildCount();
|
||||
int total = lm.getItemCount();
|
||||
int firstVis = lm.findFirstVisibleItemPosition();
|
||||
if (!isLoading && !isLastPage && (visible + firstVis) >= total - 3) {
|
||||
loadServices(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the SwipeRefreshLayout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshService.setOnRefreshListener(this::loadServiceData);
|
||||
binding.swipeRefreshService.setOnRefreshListener(() -> loadServices(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the service detail screen for editing an existing service or adding a new one.
|
||||
* Fetches a page of services from the API.
|
||||
*/
|
||||
private void openServiceDetails(int position) {
|
||||
//Make a bundle to pass data to the detail fragment
|
||||
Bundle args = new Bundle();
|
||||
private void loadServices(boolean reset) {
|
||||
if (isLoading) return;
|
||||
|
||||
//if editing a service, add the service id to the bundle
|
||||
if (position != -1) {
|
||||
ServiceDTO service = serviceList.get(position);
|
||||
args.putLong("serviceId", service.getServiceId());
|
||||
if (reset) {
|
||||
currentPage = 0;
|
||||
isLastPage = false;
|
||||
}
|
||||
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_service_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles item click in the service list.
|
||||
*/
|
||||
@Override
|
||||
public void onServiceClick(int position) {
|
||||
openServiceDetails(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches all service data from the server through the ViewModel and updates the UI.
|
||||
*/
|
||||
private void loadServiceData() {
|
||||
String query = binding.etSearchService != null ? binding.etSearchService.getText().toString().trim() : "";
|
||||
String query = binding.etSearchService.getText().toString().trim();
|
||||
if (query.isEmpty()) query = null;
|
||||
|
||||
//Load services from the backend with query and default sort
|
||||
viewModel.getAllServices(0, 100, query, "serviceName").observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.getAllServices(currentPage, PAGE_SIZE, query, "serviceName").observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
|
||||
// Check the status to see if the resource is loaded and display the data
|
||||
switch (resource.status) {
|
||||
case LOADING:
|
||||
// Show loading indicator
|
||||
isLoading = true;
|
||||
binding.swipeRefreshService.setRefreshing(true);
|
||||
break;
|
||||
case SUCCESS:
|
||||
// Hide loading indicator and display data
|
||||
isLoading = false;
|
||||
binding.swipeRefreshService.setRefreshing(false);
|
||||
if (resource.data != null) {
|
||||
serviceList.clear();
|
||||
if (reset) serviceList.clear();
|
||||
serviceList.addAll(resource.data.getContent());
|
||||
adapter.notifyDataSetChanged();
|
||||
isLastPage = resource.data.isLast();
|
||||
if (!isLastPage) currentPage++;
|
||||
}
|
||||
break;
|
||||
case ERROR:
|
||||
// Hide loading indicator and toast error message
|
||||
isLoading = false;
|
||||
binding.swipeRefreshService.setRefreshing(false);
|
||||
if (getContext() != null) {
|
||||
Toast.makeText(getContext(), "Failed to load services: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
Log.e("ServiceFragment", "Error loading services: " + resource.message);
|
||||
Log.e(TAG, "Error: " + resource.message);
|
||||
Toast.makeText(getContext(), "Failed to load services: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the RecyclerView with a layout manager and adapter for services.
|
||||
* Navigates to the service detail screen.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new ServiceAdapter(serviceList, this);
|
||||
binding.recyclerViewServices.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
binding.recyclerViewServices.setAdapter(adapter);
|
||||
private void openDetail(ServiceDTO service) {
|
||||
Bundle args = new Bundle();
|
||||
if (service != null) {
|
||||
args.putLong("serviceId", service.getServiceId());
|
||||
}
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_service_detail, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceClick(int position) {
|
||||
if (position >= 0 && position < serviceList.size()) {
|
||||
openDetail(serviceList.get(position));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectionChanged(int count) {
|
||||
if (bulkDeleteHandler != null) {
|
||||
bulkDeleteHandler.onSelectionChanged(count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import com.example.petstoremobile.adapters.SupplierAdapter;
|
||||
import com.example.petstoremobile.databinding.FragmentSupplierBinding;
|
||||
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.viewmodels.SupplierViewModel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -36,6 +38,7 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
|
||||
private List<SupplierDTO> supplierList = new ArrayList<>();
|
||||
private SupplierAdapter adapter;
|
||||
private SupplierViewModel viewModel;
|
||||
private BulkDeleteHandler bulkDeleteHandler;
|
||||
|
||||
/**
|
||||
* Initializes the fragment and its associated SupplierViewModel.
|
||||
@@ -58,6 +61,7 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
|
||||
setupSearch();
|
||||
setupSwipeRefresh();
|
||||
setupFilterToggle();
|
||||
setupBulkDelete();
|
||||
loadSupplierData();
|
||||
|
||||
//Add button to opens the add dialog
|
||||
@@ -77,6 +81,19 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
private void setupBulkDelete() {
|
||||
bulkDeleteHandler = new BulkDeleteHandler(
|
||||
this,
|
||||
binding.layoutBulkDelete,
|
||||
binding.tvSelectionCount,
|
||||
binding.btnBulkDelete,
|
||||
adapter,
|
||||
"supplier",
|
||||
viewModel::bulkDeleteSuppliers,
|
||||
this::loadSupplierData
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
@@ -146,6 +163,13 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
|
||||
openSupplierDetails(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectionChanged(int count) {
|
||||
if (bulkDeleteHandler != null) {
|
||||
bulkDeleteHandler.onSelectionChanged(count);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches all supplier data from the server through the ViewModel and updates the UI.
|
||||
*/
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.example.petstoremobile.repositories;
|
||||
import androidx.lifecycle.LiveData;
|
||||
|
||||
import com.example.petstoremobile.api.ServiceApi;
|
||||
import com.example.petstoremobile.dtos.BulkDeleteRequest;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.ServiceDTO;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
@@ -54,4 +55,11 @@ public class ServiceRepository extends BaseRepository {
|
||||
public LiveData<Resource<Void>> deleteService(Long id) {
|
||||
return executeCall(serviceApi.deleteService(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to delete multiple services.
|
||||
*/
|
||||
public LiveData<Resource<Void>> bulkDeleteServices(BulkDeleteRequest request) {
|
||||
return executeCall(serviceApi.bulkDeleteServices(request));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.example.petstoremobile.repositories;
|
||||
import androidx.lifecycle.LiveData;
|
||||
|
||||
import com.example.petstoremobile.api.SupplierApi;
|
||||
import com.example.petstoremobile.dtos.BulkDeleteRequest;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.SupplierDTO;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
@@ -54,4 +55,11 @@ public class SupplierRepository extends BaseRepository {
|
||||
public LiveData<Resource<Void>> deleteSupplier(Long id) {
|
||||
return executeCall(supplierApi.deleteSupplier(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to delete multiple supplier records.
|
||||
*/
|
||||
public LiveData<Resource<Void>> bulkDeleteSuppliers(BulkDeleteRequest request) {
|
||||
return executeCall(supplierApi.bulkDeleteSuppliers(request));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.ServiceDTO;
|
||||
import com.example.petstoremobile.repositories.ServiceRepository;
|
||||
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 ServiceViewModel extends ViewModel {
|
||||
public LiveData<Resource<Void>> deleteService(Long id) {
|
||||
return repository.deleteService(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes multiple services.
|
||||
*/
|
||||
public LiveData<Resource<Void>> bulkDeleteServices(List<Long> ids) {
|
||||
return repository.bulkDeleteServices(new BulkDeleteRequest(ids));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.SupplierDTO;
|
||||
import com.example.petstoremobile.repositories.SupplierRepository;
|
||||
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 SupplierViewModel extends ViewModel {
|
||||
public LiveData<Resource<Void>> deleteSupplier(Long id) {
|
||||
return repository.deleteSupplier(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes multiple supplier records.
|
||||
*/
|
||||
public LiveData<Resource<Void>> bulkDeleteSuppliers(List<Long> ids) {
|
||||
return repository.bulkDeleteSuppliers(new BulkDeleteRequest(ids));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +106,7 @@
|
||||
|
||||
<!-- Bulk-delete action bar (hidden until long-press) -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutBulkDelete"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
@@ -114,7 +115,8 @@
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp">
|
||||
android:paddingBottom="4dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSelectionCount"
|
||||
@@ -122,8 +124,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="0 selected"
|
||||
android:textColor="@color/white"
|
||||
android:visibility="gone"/>
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnBulkDelete"
|
||||
@@ -131,8 +132,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Delete Selected"
|
||||
android:backgroundTint="@color/accent_coral"
|
||||
android:textColor="@color/white"
|
||||
android:visibility="gone"/>
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -93,6 +93,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/swipeRefreshService"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -93,6 +93,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/swipeRefreshSupplier"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -2,66 +2,83 @@
|
||||
<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/tvServiceName"
|
||||
android:layout_width="match_parent"
|
||||
<CheckBox
|
||||
android:id="@+id/cbSelectService"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="Service"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvServiceDesc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="Description"
|
||||
android:textColor="#888888"
|
||||
android:textSize="13sp" />
|
||||
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/tvServiceDuration"
|
||||
android:layout_width="0dp"
|
||||
android:id="@+id/tvServiceName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Duration"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="Service"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvServiceDesc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="Description"
|
||||
android:textColor="#888888"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvServicePrice"
|
||||
android:layout_width="wrap_content"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="$126.00"
|
||||
android:textColor="@color/accent_coral"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginTop="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvServiceDuration"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Duration"
|
||||
android:textColor="#888888"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvServicePrice"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="$126.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>
|
||||
@@ -2,65 +2,82 @@
|
||||
<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/tvSupCompany"
|
||||
android:layout_width="match_parent"
|
||||
<CheckBox
|
||||
android:id="@+id/cbSelectSupplier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="Company"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSupContactName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="First Last"
|
||||
android:textColor="#888888"
|
||||
android:textSize="13sp" />
|
||||
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/tvSupEmail"
|
||||
android:layout_width="0dp"
|
||||
android:id="@+id/tvSupCompany"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="example@email.com"
|
||||
android:text="Company"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSupContactName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="First Last"
|
||||
android:textColor="#888888"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSupPhone"
|
||||
android:layout_width="wrap_content"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="(123) 123-1234"
|
||||
android:textColor="@color/accent_coral"
|
||||
android:textSize="13sp" />
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginTop="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSupEmail"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="example@email.com"
|
||||
android:textColor="#888888"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSupPhone"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="(123) 123-1234"
|
||||
android:textColor="@color/accent_coral"
|
||||
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>
|
||||
Reference in New Issue
Block a user