bluk delete added for Service and suppliers on andriod

This commit is contained in:
Alex
2026-04-07 15:24:25 -06:00
parent 93b4ad8c50
commit 713e919c10
15 changed files with 488 additions and 164 deletions

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

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.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);
}

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.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);
}

View File

@@ -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);
}
}
}

View File

@@ -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.
*/

View File

@@ -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));
}
}

View File

@@ -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));
}
}

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.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));
}
}

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.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));
}
}

View File

@@ -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>

View File

@@ -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"

View File

@@ -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"

View File

@@ -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>

View File

@@ -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>