Merge branch 'morefiles'
This commit is contained in:
@@ -1,79 +1,128 @@
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.view.*;
|
||||
import android.widget.TextView;
|
||||
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.R;
|
||||
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 {
|
||||
TextView tvCustomerName, tvPetName, tvDate, tvFee, tvStatus;
|
||||
final ItemAdoptionBinding binding;
|
||||
|
||||
public AdoptionViewHolder(@NonNull View v) {
|
||||
super(v);
|
||||
tvCustomerName = v.findViewById(R.id.tvAdoptionCustomerName);
|
||||
tvPetName = v.findViewById(R.id.tvAdoptionPetName);
|
||||
tvDate = v.findViewById(R.id.tvAdoptionDate);
|
||||
tvFee = v.findViewById(R.id.tvAdoptionFee);
|
||||
tvStatus = v.findViewById(R.id.tvAdoptionStatus);
|
||||
public AdoptionViewHolder(@NonNull ItemAdoptionBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AdoptionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_adoption, parent, false);
|
||||
return new AdoptionViewHolder(v);
|
||||
ItemAdoptionBinding binding = ItemAdoptionBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new AdoptionViewHolder(binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull AdoptionViewHolder holder, int position) {
|
||||
AdoptionDTO a = adoptionList.get(position);
|
||||
ItemAdoptionBinding binding = holder.binding;
|
||||
|
||||
binding.tvAdoptionCustomerName.setText(a.getCustomerName() != null ? a.getCustomerName() : "");
|
||||
binding.tvAdoptionPetName.setText("Pet: " + (a.getPetName() != null ? a.getPetName() : ""));
|
||||
binding.tvAdoptionStaffName.setText("Staff: " + (a.getEmployeeName() != null ? a.getEmployeeName() : "N/A"));
|
||||
binding.tvAdoptionDate.setText("Date: " + (a.getAdoptionDate() != null ? a.getAdoptionDate() : ""));
|
||||
binding.tvAdoptionFee.setText(a.getAdoptionFee() != null ? "$" + a.getAdoptionFee() : "");
|
||||
|
||||
holder.tvCustomerName.setText(a.getCustomerName() != null ? a.getCustomerName() : "");
|
||||
holder.tvPetName.setText("Pet: " + (a.getPetName() != null ? a.getPetName() : ""));
|
||||
holder.tvDate.setText("Date: " + (a.getAdoptionDate() != null ? a.getAdoptionDate() : ""));
|
||||
holder.tvFee.setText(a.getAdoptionFee() != null ? "$" + a.getAdoptionFee() : "");
|
||||
|
||||
String status = a.getAdoptionStatus() != null ? a.getAdoptionStatus() : "";
|
||||
holder.tvStatus.setText(status);
|
||||
binding.tvAdoptionStatus.setText(status);
|
||||
|
||||
switch (status) {
|
||||
case "Completed":
|
||||
holder.tvStatus.setBackgroundColor(Color.parseColor("#4CAF50"));
|
||||
binding.tvAdoptionStatus.setBackgroundColor(Color.parseColor("#4CAF50"));
|
||||
break;
|
||||
case "Pending":
|
||||
holder.tvStatus.setBackgroundColor(Color.parseColor("#FF9800"));
|
||||
binding.tvAdoptionStatus.setBackgroundColor(Color.parseColor("#FF9800"));
|
||||
break;
|
||||
case "Rejected":
|
||||
holder.tvStatus.setBackgroundColor(Color.parseColor("#F44336"));
|
||||
case "Cancelled":
|
||||
binding.tvAdoptionStatus.setBackgroundColor(Color.parseColor("#F44336"));
|
||||
break;
|
||||
default:
|
||||
holder.tvStatus.setBackgroundColor(Color.parseColor("#9E9E9E"));
|
||||
binding.tvAdoptionStatus.setBackgroundColor(Color.parseColor("#9E9E9E"));
|
||||
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(); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,77 +4,124 @@ import android.graphics.Color;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.example.petstoremobile.R;
|
||||
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 {
|
||||
TextView tvCustomerName, tvPetName, tvServiceType, tvDateTime, tvAppointmentStatus;
|
||||
private final ItemAppointmentBinding binding;
|
||||
|
||||
public AppointmentViewHolder(@NonNull View v) {
|
||||
super(v);
|
||||
tvCustomerName = v.findViewById(R.id.tvCustomerName);
|
||||
tvPetName = v.findViewById(R.id.tvApptPetName);
|
||||
tvServiceType = v.findViewById(R.id.tvServiceType);
|
||||
tvDateTime = v.findViewById(R.id.tvDateTime);
|
||||
tvAppointmentStatus = v.findViewById(R.id.tvAppointmentStatus);
|
||||
public AppointmentViewHolder(@NonNull ItemAppointmentBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AppointmentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_appointment, parent, false);
|
||||
return new AppointmentViewHolder(v);
|
||||
ItemAppointmentBinding binding = ItemAppointmentBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new AppointmentViewHolder(binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull AppointmentViewHolder holder, int position) {
|
||||
AppointmentDTO a = appointmentList.get(position);
|
||||
ItemAppointmentBinding binding = holder.binding;
|
||||
|
||||
holder.tvCustomerName.setText(a.getCustomerName() != null ? a.getCustomerName() : "");
|
||||
holder.tvPetName.setText("Pet: " + (a.getPetName() != null ? a.getPetName() : ""));
|
||||
holder.tvServiceType.setText(a.getServiceType() != null ? a.getServiceType() : "");
|
||||
holder.tvDateTime.setText((a.getAppointmentDate() != null ? a.getAppointmentDate() : "") +
|
||||
binding.tvCustomerName.setText(a.getCustomerName() != null ? a.getCustomerName() : "");
|
||||
binding.tvApptPetName.setText("Pet: " + (a.getPetName() != null ? a.getPetName() : ""));
|
||||
binding.tvServiceType.setText(a.getServiceType() != null ? a.getServiceType() : "");
|
||||
binding.tvStaffName.setText("Staff: " + (a.getEmployeeName() != null ? a.getEmployeeName() : "Unassigned"));
|
||||
binding.tvDateTime.setText((a.getAppointmentDate() != null ? a.getAppointmentDate() : "") +
|
||||
" at " + (a.getAppointmentTime() != null ? a.getAppointmentTime() : ""));
|
||||
|
||||
String status = a.getStatus() != null ? a.getStatus() : "";
|
||||
holder.tvAppointmentStatus.setText(status);
|
||||
binding.tvAppointmentStatus.setText(status);
|
||||
|
||||
switch (status.toUpperCase()) {
|
||||
case "BOOKED":
|
||||
holder.tvAppointmentStatus.setBackgroundColor(Color.parseColor("#2196F3")); // blue
|
||||
binding.tvAppointmentStatus.setBackgroundColor(Color.parseColor("#2196F3")); // blue
|
||||
break;
|
||||
case "COMPLETED":
|
||||
holder.tvAppointmentStatus.setBackgroundColor(Color.parseColor("#4CAF50")); // green
|
||||
binding.tvAppointmentStatus.setBackgroundColor(Color.parseColor("#4CAF50")); // green
|
||||
break;
|
||||
case "CANCELLED":
|
||||
holder.tvAppointmentStatus.setBackgroundColor(Color.parseColor("#F44336")); // red
|
||||
binding.tvAppointmentStatus.setBackgroundColor(Color.parseColor("#F44336")); // red
|
||||
break;
|
||||
default:
|
||||
holder.tvAppointmentStatus.setBackgroundColor(Color.parseColor("#9E9E9E")); // gray
|
||||
binding.tvAppointmentStatus.setBackgroundColor(Color.parseColor("#9E9E9E")); // gray
|
||||
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
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.databinding.ItemChatBinding;
|
||||
import com.example.petstoremobile.models.Chat;
|
||||
|
||||
import java.util.List;
|
||||
@@ -30,15 +28,15 @@ public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ChatViewHolder
|
||||
@NonNull
|
||||
@Override
|
||||
public ChatViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_chat, parent, false);
|
||||
return new ChatViewHolder(view);
|
||||
ItemChatBinding binding = ItemChatBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new ChatViewHolder(binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ChatViewHolder holder, int position) {
|
||||
Chat chat = chatList.get(position);
|
||||
holder.tvCustomerName.setText(chat.getCustomerName());
|
||||
holder.tvLastMessage.setText(chat.getLastMessage());
|
||||
holder.binding.tvCustomerName.setText(chat.getCustomerName());
|
||||
holder.binding.tvLastMessage.setText(chat.getLastMessage());
|
||||
holder.itemView.setOnClickListener(v -> listener.onChatClick(chat));
|
||||
}
|
||||
|
||||
@@ -48,12 +46,11 @@ public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ChatViewHolder
|
||||
}
|
||||
|
||||
public static class ChatViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvCustomerName, tvLastMessage;
|
||||
final ItemChatBinding binding;
|
||||
|
||||
public ChatViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
tvCustomerName = itemView.findViewById(R.id.tvCustomerName);
|
||||
tvLastMessage = itemView.findViewById(R.id.tvLastMessage);
|
||||
public ChatViewHolder(@NonNull ItemChatBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.view.*;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.dtos.EmployeeDTO;
|
||||
import java.util.List;
|
||||
|
||||
public class EmployeeAdapter extends RecyclerView.Adapter<EmployeeAdapter.EmployeeViewHolder> {
|
||||
|
||||
private List<EmployeeDTO> list;
|
||||
private OnEmployeeClickListener listener;
|
||||
|
||||
public interface OnEmployeeClickListener {
|
||||
void onEmployeeClick(int position);
|
||||
}
|
||||
|
||||
public EmployeeAdapter(List<EmployeeDTO> list, OnEmployeeClickListener listener) {
|
||||
this.list = list;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public static class EmployeeViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvFullName, tvUsername, tvEmail, tvPhone, tvRole, tvStatus;
|
||||
|
||||
public EmployeeViewHolder(@NonNull View v) {
|
||||
super(v);
|
||||
tvFullName = v.findViewById(R.id.tvEmployeeFullName);
|
||||
tvUsername = v.findViewById(R.id.tvEmployeeUsername);
|
||||
tvEmail = v.findViewById(R.id.tvEmployeeEmail);
|
||||
tvPhone = v.findViewById(R.id.tvEmployeePhone);
|
||||
tvRole = v.findViewById(R.id.tvEmployeeRole);
|
||||
tvStatus = v.findViewById(R.id.tvEmployeeStatus);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public EmployeeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_employee, parent, false);
|
||||
return new EmployeeViewHolder(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull EmployeeViewHolder holder, int position) {
|
||||
EmployeeDTO e = list.get(position);
|
||||
|
||||
holder.tvFullName.setText(e.getFullName() != null ? e.getFullName() : "");
|
||||
holder.tvUsername.setText("@" + (e.getUsername() != null ? e.getUsername() : ""));
|
||||
holder.tvEmail.setText(e.getEmail() != null ? e.getEmail() : "");
|
||||
holder.tvPhone.setText(e.getPhone() != null ? e.getPhone() : "");
|
||||
|
||||
// Role badge
|
||||
String role = e.getRole() != null ? e.getRole() : "";
|
||||
holder.tvRole.setText(role);
|
||||
holder.tvRole.setBackgroundColor(
|
||||
"ADMIN".equals(role) ? Color.parseColor("#1a759f") : Color.parseColor("#577590"));
|
||||
|
||||
// Status badge
|
||||
boolean active = Boolean.TRUE.equals(e.getActive());
|
||||
holder.tvStatus.setText(active ? "Active" : "Inactive");
|
||||
holder.tvStatus.setBackgroundColor(
|
||||
active ? Color.parseColor("#4CAF50") : Color.parseColor("#F44336"));
|
||||
|
||||
holder.itemView.setOnClickListener(v -> listener.onEmployeeClick(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() { return list.size(); }
|
||||
}
|
||||
@@ -4,24 +4,22 @@ import android.graphics.Color;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.databinding.ItemInventoryBinding;
|
||||
import com.example.petstoremobile.dtos.InventoryDTO;
|
||||
import com.example.petstoremobile.utils.BulkDeleteHandler;
|
||||
import com.example.petstoremobile.utils.SelectionHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.InventoryViewHolder> {
|
||||
public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.InventoryViewHolder> implements BulkDeleteHandler.SelectableAdapter {
|
||||
|
||||
private final List<InventoryDTO> inventoryList;
|
||||
private final OnInventoryClickListener clickListener;
|
||||
private final List<Long> selectedIds = new ArrayList<>();
|
||||
private boolean selectionMode = false;
|
||||
private final SelectionHelper selectionHelper;
|
||||
|
||||
public interface OnInventoryClickListener {
|
||||
void onInventoryClick(int position);
|
||||
@@ -32,119 +30,97 @@ public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.Inve
|
||||
public InventoryAdapter(List<InventoryDTO> inventoryList, OnInventoryClickListener clickListener) {
|
||||
this.inventoryList = inventoryList;
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSelectedKeys() {
|
||||
return selectionHelper.getSelectedKeys();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionHelper.clearSelection();
|
||||
}
|
||||
|
||||
public static class InventoryViewHolder extends RecyclerView.ViewHolder {
|
||||
// Matches desktop table columns: Inventory ID, Product ID, Product Name,
|
||||
// Quantity
|
||||
TextView tvInventoryId, tvProdId, tvProductName, tvQuantity;
|
||||
CheckBox checkBox;
|
||||
final ItemInventoryBinding binding;
|
||||
|
||||
public InventoryViewHolder(@NonNull View v) {
|
||||
super(v);
|
||||
tvInventoryId = v.findViewById(R.id.tvInventoryId);
|
||||
tvProdId = v.findViewById(R.id.tvProdId);
|
||||
tvProductName = v.findViewById(R.id.tvProductName);
|
||||
tvQuantity = v.findViewById(R.id.tvQuantity);
|
||||
checkBox = v.findViewById(R.id.cbSelectInventory);
|
||||
public InventoryViewHolder(@NonNull ItemInventoryBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public InventoryViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_inventory, parent, false);
|
||||
return new InventoryViewHolder(v);
|
||||
ItemInventoryBinding binding = ItemInventoryBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new InventoryViewHolder(binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull InventoryViewHolder holder, int position) {
|
||||
InventoryDTO inv = inventoryList.get(position);
|
||||
|
||||
// Column: Inventory ID
|
||||
String invIdStr = inv.getInventoryId() != null ? String.valueOf(inv.getInventoryId()) : "—";
|
||||
holder.tvInventoryId.setText("Inv ID: " + invIdStr);
|
||||
|
||||
// Column: Product ID
|
||||
String prodIdStr = inv.getProdId() != null ? String.valueOf(inv.getProdId()) : "—";
|
||||
holder.tvProdId.setText("Prod ID: " + prodIdStr);
|
||||
ItemInventoryBinding binding = holder.binding;
|
||||
|
||||
// Column: Product Name
|
||||
holder.tvProductName.setText(inv.getProductName() != null ? inv.getProductName() : "—");
|
||||
binding.tvProductName.setText(inv.getProductName() != null ? inv.getProductName() : "—");
|
||||
|
||||
// Column: Store Name
|
||||
binding.tvInventoryStore.setText("Store: " + (inv.getStoreName() != null ? inv.getStoreName() : "—"));
|
||||
|
||||
// Column: Quantity
|
||||
int qty = inv.getQuantity() != null ? inv.getQuantity() : 0;
|
||||
holder.tvQuantity.setText(String.valueOf(qty));
|
||||
binding.tvQuantity.setText("Stock: " + qty);
|
||||
|
||||
// Low stock = red, normal = green (like desktop reorder concept)
|
||||
if (qty <= 5) {
|
||||
holder.tvQuantity.setTextColor(Color.parseColor("#F44336"));
|
||||
binding.tvQuantity.setTextColor(Color.parseColor("#F44336"));
|
||||
} else {
|
||||
holder.tvQuantity.setTextColor(Color.parseColor("#4CAF50"));
|
||||
binding.tvQuantity.setTextColor(Color.parseColor("#4CAF50"));
|
||||
}
|
||||
|
||||
String key = String.valueOf(inv.getInventoryId());
|
||||
|
||||
// Bulk delete selection mode
|
||||
if (selectionMode) {
|
||||
holder.checkBox.setVisibility(View.VISIBLE);
|
||||
holder.checkBox.setChecked(inv.getInventoryId() != null
|
||||
&& selectedIds.contains(inv.getInventoryId()));
|
||||
if (selectionHelper.isInSelectionMode()) {
|
||||
binding.cbSelectInventory.setVisibility(View.VISIBLE);
|
||||
binding.cbSelectInventory.setChecked(selectionHelper.isSelected(key));
|
||||
} else {
|
||||
holder.checkBox.setVisibility(View.GONE);
|
||||
holder.checkBox.setChecked(false);
|
||||
binding.cbSelectInventory.setVisibility(View.GONE);
|
||||
binding.cbSelectInventory.setChecked(false);
|
||||
}
|
||||
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
if (selectionMode) {
|
||||
toggleSelection(inv.getInventoryId(), holder.checkBox);
|
||||
if (selectionHelper.isInSelectionMode()) {
|
||||
selectionHelper.toggleSelection(key);
|
||||
notifyItemChanged(position);
|
||||
} else {
|
||||
clickListener.onInventoryClick(holder.getAdapterPosition());
|
||||
}
|
||||
});
|
||||
|
||||
holder.itemView.setOnLongClickListener(v -> {
|
||||
if (!selectionMode) {
|
||||
selectionMode = true;
|
||||
toggleSelection(inv.getInventoryId(), holder.checkBox);
|
||||
notifyDataSetChanged();
|
||||
if (!selectionHelper.isInSelectionMode()) {
|
||||
selectionHelper.startSelection(key);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void toggleSelection(Long id, CheckBox checkBox) {
|
||||
if (id == null)
|
||||
return;
|
||||
if (selectedIds.contains(id)) {
|
||||
selectedIds.remove(id);
|
||||
checkBox.setChecked(false);
|
||||
} else {
|
||||
selectedIds.add(id);
|
||||
checkBox.setChecked(true);
|
||||
}
|
||||
clickListener.onSelectionChanged(selectedIds.size());
|
||||
if (selectedIds.isEmpty()) {
|
||||
selectionMode = false;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Long> getSelectedIds() {
|
||||
return new ArrayList<>(selectedIds);
|
||||
}
|
||||
|
||||
public void clearSelection() {
|
||||
selectedIds.clear();
|
||||
selectionMode = false;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public boolean isInSelectionMode() {
|
||||
return selectionMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return inventoryList.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.load.model.GlideUrl;
|
||||
import com.bumptech.glide.load.model.LazyHeaders;
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.databinding.ItemMessageReceivedBinding;
|
||||
import com.example.petstoremobile.databinding.ItemMessageSentBinding;
|
||||
import com.example.petstoremobile.models.Message;
|
||||
import java.util.List;
|
||||
|
||||
@@ -51,11 +53,11 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
LayoutInflater inf = LayoutInflater.from(parent.getContext());
|
||||
if (viewType == TYPE_SENT) {
|
||||
View v = inf.inflate(R.layout.item_message_sent, parent, false);
|
||||
return new SentHolder(v);
|
||||
ItemMessageSentBinding binding = ItemMessageSentBinding.inflate(inf, parent, false);
|
||||
return new SentHolder(binding);
|
||||
} else {
|
||||
View v = inf.inflate(R.layout.item_message_received, parent, false);
|
||||
return new ReceivedHolder(v);
|
||||
ItemMessageReceivedBinding binding = ItemMessageReceivedBinding.inflate(inf, parent, false);
|
||||
return new ReceivedHolder(binding);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,32 +71,26 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
@Override public int getItemCount() { return messages.size(); }
|
||||
|
||||
static class SentHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvMessage, tvAttachmentName;
|
||||
ImageView ivAttachment;
|
||||
SentHolder(View v) {
|
||||
super(v);
|
||||
tvMessage = v.findViewById(R.id.tvMessageContent);
|
||||
tvAttachmentName = v.findViewById(R.id.tvAttachmentName);
|
||||
ivAttachment = v.findViewById(R.id.ivAttachment);
|
||||
final ItemMessageSentBinding binding;
|
||||
SentHolder(ItemMessageSentBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
void bind(Message m, String token) {
|
||||
tvMessage.setText(m.getContent());
|
||||
displayAttachment(m, ivAttachment, tvAttachmentName, token);
|
||||
binding.tvMessageContent.setText(m.getContent());
|
||||
displayAttachment(m, binding.ivAttachment, binding.tvAttachmentName, token);
|
||||
}
|
||||
}
|
||||
|
||||
static class ReceivedHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvMessage, tvAttachmentName;
|
||||
ImageView ivAttachment;
|
||||
ReceivedHolder(View v) {
|
||||
super(v);
|
||||
tvMessage = v.findViewById(R.id.tvMessageContent);
|
||||
tvAttachmentName = v.findViewById(R.id.tvAttachmentName);
|
||||
ivAttachment = v.findViewById(R.id.ivAttachment);
|
||||
final ItemMessageReceivedBinding binding;
|
||||
ReceivedHolder(ItemMessageReceivedBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
void bind(Message m, String token) {
|
||||
tvMessage.setText(m.getContent());
|
||||
displayAttachment(m, ivAttachment, tvAttachmentName, token);
|
||||
binding.tvMessageContent.setText(m.getContent());
|
||||
displayAttachment(m, binding.ivAttachment, binding.tvAttachmentName, token);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,33 +4,48 @@ import android.graphics.Color;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.api.PetApi;
|
||||
import com.example.petstoremobile.databinding.ItemPetBinding;
|
||||
import com.example.petstoremobile.dtos.PetDTO;
|
||||
import com.example.petstoremobile.utils.BulkDeleteHandler;
|
||||
import com.example.petstoremobile.utils.GlideUtils;
|
||||
import com.example.petstoremobile.utils.SelectionHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> {
|
||||
public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> implements BulkDeleteHandler.SelectableAdapter {
|
||||
|
||||
private List<PetDTO> petList;
|
||||
private OnPetClickListener petClickListener;
|
||||
private String baseUrl;
|
||||
private String token;
|
||||
private final SelectionHelper selectionHelper;
|
||||
|
||||
// Interface for pet click on recycler view
|
||||
public interface OnPetClickListener {
|
||||
void onPetClick(int position);
|
||||
void onSelectionChanged(int selectedCount);
|
||||
}
|
||||
|
||||
//Constructor
|
||||
public PetAdapter(List<PetDTO> petList, OnPetClickListener petClickListener) {
|
||||
this.petList = petList;
|
||||
this.petClickListener = petClickListener;
|
||||
this.selectionHelper = new SelectionHelper(new SelectionHelper.SelectionListener() {
|
||||
@Override
|
||||
public void onSelectionChanged(int count) {
|
||||
petClickListener.onSelectionChanged(count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectionModeToggle(boolean selectionMode) {
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
@@ -41,19 +56,23 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSelectedKeys() {
|
||||
return selectionHelper.getSelectedKeys();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionHelper.clearSelection();
|
||||
}
|
||||
|
||||
// Get the controls of each row in recycler view
|
||||
public static class PetViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvPetName, tvPetSpeciesBreed, tvPetAge, tvPetPrice, tvPetStatus;
|
||||
ImageView ivPetProfile;
|
||||
private final ItemPetBinding binding;
|
||||
|
||||
public PetViewHolder(@NonNull View v) {
|
||||
super(v);
|
||||
tvPetName = v.findViewById(R.id.tvPetName);
|
||||
tvPetSpeciesBreed = v.findViewById(R.id.tvPetSpeciesBreed);
|
||||
tvPetAge = v.findViewById(R.id.tvPetAge);
|
||||
tvPetPrice = v.findViewById(R.id.tvPetPrice);
|
||||
tvPetStatus = v.findViewById(R.id.tvPetStatus);
|
||||
ivPetProfile = v.findViewById(R.id.ivPetProfile);
|
||||
public PetViewHolder(@NonNull ItemPetBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,49 +80,75 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> {
|
||||
@NonNull
|
||||
@Override
|
||||
public PetViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_pet, parent, false);
|
||||
return new PetViewHolder(v);
|
||||
ItemPetBinding binding = ItemPetBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new PetViewHolder(binding);
|
||||
}
|
||||
|
||||
//populate the row with pet data
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull PetViewHolder holder, int position) {
|
||||
PetDTO pet = petList.get(position);
|
||||
ItemPetBinding binding = holder.binding;
|
||||
|
||||
holder.tvPetName.setText(pet.getPetName());
|
||||
holder.tvPetSpeciesBreed.setText(pet.getPetSpecies() + " - " + pet.getPetBreed());
|
||||
holder.tvPetAge.setText("Age: " + pet.getPetAge() + " yr(s)");
|
||||
binding.tvPetName.setText(pet.getPetName());
|
||||
binding.tvPetSpeciesBreed.setText(pet.getPetSpecies() + " - " + pet.getPetBreed());
|
||||
binding.tvPetAge.setText("Age: " + pet.getPetAge() + " yr(s)");
|
||||
|
||||
Double price = pet.getPetPrice();
|
||||
if (price != null) {
|
||||
holder.tvPetPrice.setText("$" + String.format("%.2f", price));
|
||||
binding.tvPetPrice.setText("$" + String.format("%.2f", price));
|
||||
} else {
|
||||
holder.tvPetPrice.setText("$0.00");
|
||||
binding.tvPetPrice.setText("$0.00");
|
||||
}
|
||||
|
||||
holder.tvPetStatus.setText(pet.getPetStatus());
|
||||
binding.tvPetStatus.setText(pet.getPetStatus());
|
||||
|
||||
//Set the status color depending on availability. If available, green, otherwise red
|
||||
if (pet.getPetStatus() != null && pet.getPetStatus().equals("Available")) {
|
||||
holder.tvPetStatus.setBackgroundColor(Color.parseColor("#4CAF50"));
|
||||
binding.tvPetStatus.setBackgroundColor(Color.parseColor("#4CAF50"));
|
||||
} else {
|
||||
holder.tvPetStatus.setBackgroundColor(Color.parseColor("#F44336"));
|
||||
binding.tvPetStatus.setBackgroundColor(Color.parseColor("#F44336"));
|
||||
}
|
||||
|
||||
// Load pet image using Glide
|
||||
if (baseUrl != null) {
|
||||
String imageUrl = baseUrl + String.format(PetApi.PET_IMAGE_PATH, pet.getPetId());
|
||||
GlideUtils.loadImageWithTokenCircle(holder.itemView.getContext(), holder.ivPetProfile, imageUrl, token, R.drawable.placeholder);
|
||||
GlideUtils.loadImageWithTokenCircle(holder.itemView.getContext(), binding.ivPetProfile, imageUrl, token, R.drawable.placeholder);
|
||||
} else {
|
||||
holder.ivPetProfile.setImageResource(R.drawable.placeholder);
|
||||
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(key));
|
||||
} else {
|
||||
binding.cbSelectPet.setVisibility(View.GONE);
|
||||
binding.cbSelectPet.setChecked(false);
|
||||
}
|
||||
|
||||
//when a row is clicked, open the detail view
|
||||
holder.itemView.setOnClickListener(v -> petClickListener.onPetClick(position));
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
if (selectionHelper.isInSelectionMode()) {
|
||||
selectionHelper.toggleSelection(key);
|
||||
notifyItemChanged(position);
|
||||
} else {
|
||||
petClickListener.onPetClick(position);
|
||||
}
|
||||
});
|
||||
|
||||
holder.itemView.setOnLongClickListener(v -> {
|
||||
if (!selectionHelper.isInSelectionMode()) {
|
||||
selectionHelper.startSelection(key);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return petList.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.view.*;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.api.ProductApi;
|
||||
import com.example.petstoremobile.databinding.ItemProductBinding;
|
||||
import com.example.petstoremobile.dtos.ProductDTO;
|
||||
import com.example.petstoremobile.utils.GlideUtils;
|
||||
import java.util.List;
|
||||
@@ -37,41 +36,37 @@ public class ProductAdapter extends RecyclerView.Adapter<ProductAdapter.ProductV
|
||||
}
|
||||
|
||||
public static class ProductViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvName, tvCategory, tvDesc, tvPrice;
|
||||
ImageView ivProductImage;
|
||||
final ItemProductBinding binding;
|
||||
|
||||
public ProductViewHolder(@NonNull View v) {
|
||||
super(v);
|
||||
tvName = v.findViewById(R.id.tvProductName);
|
||||
tvCategory = v.findViewById(R.id.tvProductCategory);
|
||||
tvDesc = v.findViewById(R.id.tvProductDesc);
|
||||
tvPrice = v.findViewById(R.id.tvProductPrice);
|
||||
ivProductImage = v.findViewById(R.id.ivProductImage);
|
||||
public ProductViewHolder(@NonNull ItemProductBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ProductViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_product, parent, false);
|
||||
return new ProductViewHolder(v);
|
||||
ItemProductBinding binding = ItemProductBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new ProductViewHolder(binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ProductViewHolder holder, int position) {
|
||||
ProductDTO p = productList.get(position);
|
||||
holder.tvName.setText(p.getProdName() != null ? p.getProdName() : "");
|
||||
holder.tvCategory.setText("Category: " + (p.getCategoryName() != null ? p.getCategoryName() : ""));
|
||||
holder.tvDesc.setText(p.getProdDesc() != null ? p.getProdDesc() : "");
|
||||
holder.tvPrice.setText(p.getProdPrice() != null ? "$" + p.getProdPrice() : "");
|
||||
ItemProductBinding binding = holder.binding;
|
||||
|
||||
binding.tvProductName.setText(p.getProdName() != null ? p.getProdName() : "");
|
||||
binding.tvProductCategory.setText("Category: " + (p.getCategoryName() != null ? p.getCategoryName() : ""));
|
||||
binding.tvProductDesc.setText(p.getProdDesc() != null ? p.getProdDesc() : "");
|
||||
binding.tvProductPrice.setText(p.getProdPrice() != null ? "$" + p.getProdPrice() : "");
|
||||
|
||||
// Load product image using Glide
|
||||
if (baseUrl != null) {
|
||||
String imageUrl = baseUrl + String.format(ProductApi.PRODUCT_IMAGE_PATH, p.getProdId());
|
||||
GlideUtils.loadImageWithTokenCircle(holder.itemView.getContext(), holder.ivProductImage, imageUrl, token, R.drawable.placeholder);
|
||||
GlideUtils.loadImageWithTokenCircle(holder.itemView.getContext(), binding.ivProductImage, imageUrl, token, R.drawable.placeholder);
|
||||
} else {
|
||||
holder.ivProductImage.setImageResource(R.drawable.placeholder);
|
||||
binding.ivProductImage.setImageResource(R.drawable.placeholder);
|
||||
}
|
||||
|
||||
holder.itemView.setOnClickListener(v -> listener.onProductClick(position));
|
||||
|
||||
@@ -1,55 +1,109 @@
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.view.*;
|
||||
import android.widget.TextView;
|
||||
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.R;
|
||||
|
||||
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 {
|
||||
TextView tvProductName, tvSupplierName, tvCost;
|
||||
final ItemProductSupplierBinding binding;
|
||||
|
||||
public PSViewHolder(@NonNull View v) {
|
||||
super(v);
|
||||
tvProductName = v.findViewById(R.id.tvPSProductName);
|
||||
tvSupplierName = v.findViewById(R.id.tvPSSupplierName);
|
||||
tvCost = v.findViewById(R.id.tvPSCost);
|
||||
public PSViewHolder(@NonNull ItemProductSupplierBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PSViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_product_supplier, parent, false);
|
||||
return new PSViewHolder(v);
|
||||
ItemProductSupplierBinding binding = ItemProductSupplierBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new PSViewHolder(binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull PSViewHolder holder, int position) {
|
||||
ProductSupplierDTO ps = list.get(position);
|
||||
holder.tvProductName.setText(ps.getProductName() != null ? ps.getProductName() : "");
|
||||
holder.tvSupplierName.setText("Supplier: " + (ps.getSupplierName() != null ? ps.getSupplierName() : ""));
|
||||
holder.tvCost.setText(ps.getCost() != null ? "Cost: $" + ps.getCost() : "");
|
||||
holder.itemView.setOnClickListener(v -> listener.onProductSupplierClick(position));
|
||||
ItemProductSupplierBinding binding = holder.binding;
|
||||
|
||||
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() : "");
|
||||
|
||||
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(); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.view.*;
|
||||
import android.widget.TextView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.databinding.ItemPurchaseOrderBinding;
|
||||
import com.example.petstoremobile.dtos.PurchaseOrderDTO;
|
||||
import java.util.List;
|
||||
|
||||
@@ -24,47 +24,49 @@ public class PurchaseOrderAdapter extends RecyclerView.Adapter<PurchaseOrderAdap
|
||||
}
|
||||
|
||||
public static class POViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvId, tvSupplier, tvDate, tvStatus;
|
||||
final ItemPurchaseOrderBinding binding;
|
||||
|
||||
public POViewHolder(@NonNull View v) {
|
||||
super(v);
|
||||
tvId = v.findViewById(R.id.tvPOId);
|
||||
tvSupplier = v.findViewById(R.id.tvPOSupplier);
|
||||
tvDate = v.findViewById(R.id.tvPODate);
|
||||
tvStatus = v.findViewById(R.id.tvPOStatus);
|
||||
public POViewHolder(@NonNull ItemPurchaseOrderBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public POViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_purchase_order, parent, false);
|
||||
return new POViewHolder(v);
|
||||
ItemPurchaseOrderBinding binding = ItemPurchaseOrderBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new POViewHolder(binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull POViewHolder holder, int position) {
|
||||
PurchaseOrderDTO po = list.get(position);
|
||||
holder.tvId.setText("PO #" + (po.getPurchaseOrderId() != null ? po.getPurchaseOrderId() : ""));
|
||||
holder.tvSupplier.setText("Supplier: " + (po.getSupplierName() != null ? po.getSupplierName() : ""));
|
||||
holder.tvDate.setText("Date: " + (po.getOrderDate() != null ? po.getOrderDate() : ""));
|
||||
ItemPurchaseOrderBinding binding = holder.binding;
|
||||
|
||||
binding.tvPOId.setText("PO #" + (po.getPurchaseOrderId() != null ? po.getPurchaseOrderId() : ""));
|
||||
binding.tvPOSupplier.setText("Supplier: " + (po.getSupplierName() != null ? po.getSupplierName() : ""));
|
||||
binding.tvPOStore.setText("Store: " + (po.getStoreName() != null ? po.getStoreName() : ""));
|
||||
binding.tvPODate.setText("Date: " + (po.getOrderDate() != null ? po.getOrderDate() : ""));
|
||||
|
||||
String status = po.getStatus() != null ? po.getStatus() : "";
|
||||
holder.tvStatus.setText(status);
|
||||
binding.tvPOStatus.setText(status);
|
||||
|
||||
switch (status) {
|
||||
case "Completed":
|
||||
holder.tvStatus.setBackgroundColor(Color.parseColor("#4CAF50"));
|
||||
switch (status.toUpperCase()) {
|
||||
case "RECEIVED":
|
||||
binding.tvPOStatus.setBackgroundColor(Color.parseColor("#4CAF50"));
|
||||
break;
|
||||
case "Pending":
|
||||
holder.tvStatus.setBackgroundColor(Color.parseColor("#FF9800"));
|
||||
case "PLACED":
|
||||
binding.tvPOStatus.setBackgroundColor(Color.parseColor("#2196F3"));
|
||||
break;
|
||||
case "Cancelled":
|
||||
holder.tvStatus.setBackgroundColor(Color.parseColor("#F44336"));
|
||||
case "PENDING":
|
||||
binding.tvPOStatus.setBackgroundColor(Color.parseColor("#FF9800"));
|
||||
break;
|
||||
case "CANCELLED":
|
||||
binding.tvPOStatus.setBackgroundColor(Color.parseColor("#F44336"));
|
||||
break;
|
||||
default:
|
||||
holder.tvStatus.setBackgroundColor(Color.parseColor("#9E9E9E"));
|
||||
binding.tvPOStatus.setBackgroundColor(Color.parseColor("#9E9E9E"));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,71 +4,63 @@ import android.graphics.Color;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.models.Sale;
|
||||
import com.example.petstoremobile.databinding.ItemSaleBinding;
|
||||
import com.example.petstoremobile.dtos.SaleDTO;
|
||||
import java.util.List;
|
||||
|
||||
public class SaleAdapter extends RecyclerView.Adapter<SaleAdapter.SaleViewHolder> {
|
||||
|
||||
private List<Sale> saleList;
|
||||
private OnSaleClickListener saleClickListener;
|
||||
private List<SaleDTO> saleList;
|
||||
private OnSaleClickListener listener;
|
||||
|
||||
public interface OnSaleClickListener {
|
||||
void onSaleClick(int position);
|
||||
}
|
||||
|
||||
public SaleAdapter(List<Sale> saleList, OnSaleClickListener saleClickListener) {
|
||||
public SaleAdapter(List<SaleDTO> saleList, OnSaleClickListener listener) {
|
||||
this.saleList = saleList;
|
||||
this.saleClickListener = saleClickListener;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public static class SaleViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvSaleId, tvItemName, tvEmployeeName, tvSaleDate, tvTotal, tvPaymentMethod, tvRefundBadge;
|
||||
final ItemSaleBinding binding;
|
||||
|
||||
public SaleViewHolder(@NonNull View v) {
|
||||
super(v);
|
||||
tvSaleId = v.findViewById(R.id.tvSaleId);
|
||||
tvItemName = v.findViewById(R.id.tvSaleItemName);
|
||||
tvEmployeeName = v.findViewById(R.id.tvSaleEmployee);
|
||||
tvSaleDate = v.findViewById(R.id.tvSaleDate);
|
||||
tvTotal = v.findViewById(R.id.tvSaleTotal);
|
||||
tvPaymentMethod = v.findViewById(R.id.tvSalePayment);
|
||||
tvRefundBadge = v.findViewById(R.id.tvRefundBadge);
|
||||
public SaleViewHolder(@NonNull ItemSaleBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public SaleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_sale, parent, false);
|
||||
return new SaleViewHolder(v);
|
||||
ItemSaleBinding binding = ItemSaleBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new SaleViewHolder(binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull SaleViewHolder holder, int position) {
|
||||
Sale sale = saleList.get(position);
|
||||
SaleDTO s = saleList.get(position);
|
||||
ItemSaleBinding binding = holder.binding;
|
||||
|
||||
holder.tvSaleId.setText("ID: " + sale.getSaleId());
|
||||
holder.tvItemName.setText(sale.getItemName());
|
||||
holder.tvEmployeeName.setText("By: " + sale.getEmployeeName());
|
||||
holder.tvSaleDate.setText(sale.getSaleDate());
|
||||
holder.tvTotal.setText("$" + String.format("%.2f", sale.getTotal()));
|
||||
holder.tvPaymentMethod.setText(sale.getPaymentMethod());
|
||||
binding.tvSaleId.setText("Sale #" + (s.getSaleId() != null ? s.getSaleId() : ""));
|
||||
binding.tvSaleEmployee.setText("By: " + (s.getEmployeeName() != null ? s.getEmployeeName() : ""));
|
||||
binding.tvSaleDate.setText(s.getSaleDate() != null ? s.getSaleDate().substring(0, Math.min(10, s.getSaleDate().length())) : "");
|
||||
binding.tvSalePayment.setText(s.getPaymentMethod() != null ? s.getPaymentMethod() : "");
|
||||
binding.tvSaleTotal.setText(s.getTotalAmount() != null ? "$" + s.getTotalAmount() : "");
|
||||
|
||||
// Show refund badge if it's a refund
|
||||
if (sale.isRefund()) {
|
||||
holder.tvRefundBadge.setVisibility(View.VISIBLE);
|
||||
holder.tvRefundBadge.setBackgroundColor(Color.parseColor("#F44336"));
|
||||
holder.tvTotal.setTextColor(Color.parseColor("#F44336"));
|
||||
if (Boolean.TRUE.equals(s.getIsRefund())) {
|
||||
binding.tvSaleRefundBadge.setVisibility(View.VISIBLE);
|
||||
binding.tvSaleRefundBadge.setBackgroundColor(Color.parseColor("#F44336"));
|
||||
binding.tvSaleTotal.setTextColor(Color.parseColor("#F44336"));
|
||||
} else {
|
||||
holder.tvRefundBadge.setVisibility(View.GONE);
|
||||
holder.tvTotal.setTextColor(Color.parseColor("#4CAF50"));
|
||||
binding.tvSaleRefundBadge.setVisibility(View.GONE);
|
||||
binding.tvSaleTotal.setTextColor(Color.parseColor("#4CAF50"));
|
||||
}
|
||||
|
||||
holder.itemView.setOnClickListener(v -> saleClickListener.onSaleClick(position));
|
||||
holder.itemView.setOnClickListener(v -> listener.onSaleClick(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,39 +3,69 @@ package com.example.petstoremobile.adapters;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.example.petstoremobile.R;
|
||||
|
||||
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<String> getSelectedKeys() {
|
||||
return selectionHelper.getSelectedKeys();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionHelper.clearSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder class for service items.
|
||||
*/
|
||||
public static class ServiceViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvServiceName, tvServiceDesc, tvServiceDuration, tvServicePrice;
|
||||
final ItemServiceBinding binding;
|
||||
|
||||
public ServiceViewHolder(@NonNull View v) {
|
||||
super(v);
|
||||
tvServiceName = v.findViewById(R.id.tvServiceName);
|
||||
tvServiceDesc = v.findViewById(R.id.tvServiceDesc);
|
||||
tvServiceDuration = v.findViewById(R.id.tvServiceDuration);
|
||||
tvServicePrice = v.findViewById(R.id.tvServicePrice);
|
||||
public ServiceViewHolder(@NonNull ItemServiceBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,26 +73,51 @@ public class ServiceAdapter extends RecyclerView.Adapter<ServiceAdapter.ServiceV
|
||||
@NonNull
|
||||
@Override
|
||||
public ServiceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_service, parent, false);
|
||||
return new ServiceViewHolder(v);
|
||||
ItemServiceBinding binding = ItemServiceBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new ServiceViewHolder(binding);
|
||||
}
|
||||
|
||||
//populate the row with service data
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ServiceViewHolder holder, int position) {
|
||||
ServiceDTO service = serviceList.get(position);
|
||||
ItemServiceBinding binding = holder.binding;
|
||||
|
||||
holder.tvServiceName.setText(service.getServiceName());
|
||||
holder.tvServiceDesc.setText(service.getServiceDesc());
|
||||
holder.tvServiceDuration.setText("Duration: " + service.getServiceDuration() + " min");
|
||||
holder.tvServicePrice.setText("$" + String.format("%.2f", service.getServicePrice()));
|
||||
binding.tvServiceName.setText(service.getServiceName());
|
||||
binding.tvServiceDesc.setText(service.getServiceDesc());
|
||||
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));
|
||||
String key = String.valueOf(service.getServiceId());
|
||||
|
||||
// Bulk delete selection mode
|
||||
if (selectionHelper.isInSelectionMode()) {
|
||||
binding.cbSelectService.setVisibility(View.VISIBLE);
|
||||
binding.cbSelectService.setChecked(selectionHelper.isSelected(key));
|
||||
} else {
|
||||
binding.cbSelectService.setVisibility(View.GONE);
|
||||
binding.cbSelectService.setChecked(false);
|
||||
}
|
||||
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
if (selectionHelper.isInSelectionMode()) {
|
||||
selectionHelper.toggleSelection(key);
|
||||
notifyItemChanged(position);
|
||||
} else {
|
||||
clickListener.onServiceClick(position);
|
||||
}
|
||||
});
|
||||
|
||||
holder.itemView.setOnLongClickListener(v -> {
|
||||
if (!selectionHelper.isInSelectionMode()) {
|
||||
selectionHelper.startSelection(key);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return serviceList.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,39 +3,63 @@ package com.example.petstoremobile.adapters;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.example.petstoremobile.R;
|
||||
|
||||
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<String> getSelectedKeys() {
|
||||
return selectionHelper.getSelectedKeys();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionHelper.clearSelection();
|
||||
}
|
||||
|
||||
// Get the controls of each row in recycler view
|
||||
public static class SupplierViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvSupCompany, tvSupContactName, tvSupEmail, tvSupPhone;
|
||||
final ItemSupplierBinding binding;
|
||||
|
||||
public SupplierViewHolder(@NonNull View v) {
|
||||
super(v);
|
||||
tvSupCompany = v.findViewById(R.id.tvSupCompany);
|
||||
tvSupContactName = v.findViewById(R.id.tvSupContactName);
|
||||
tvSupEmail = v.findViewById(R.id.tvSupEmail);
|
||||
tvSupPhone = v.findViewById(R.id.tvSupPhone);
|
||||
public SupplierViewHolder(@NonNull ItemSupplierBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,26 +67,52 @@ public class SupplierAdapter extends RecyclerView.Adapter<SupplierAdapter.Suppli
|
||||
@NonNull
|
||||
@Override
|
||||
public SupplierViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_supplier, parent, false);
|
||||
return new SupplierViewHolder(v);
|
||||
ItemSupplierBinding binding = ItemSupplierBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new SupplierViewHolder(binding);
|
||||
}
|
||||
|
||||
//populate the row with supplier data
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull SupplierViewHolder holder, int position) {
|
||||
SupplierDTO supplier = supplierList.get(position);
|
||||
ItemSupplierBinding binding = holder.binding;
|
||||
|
||||
holder.tvSupCompany.setText(supplier.getSupCompany());
|
||||
holder.tvSupContactName.setText(supplier.getSupContactFirstName() + " " + supplier.getSupContactLastName());
|
||||
holder.tvSupEmail.setText(supplier.getSupEmail());
|
||||
holder.tvSupPhone.setText(supplier.getSupPhone());
|
||||
binding.tvSupCompany.setText(supplier.getSupCompany());
|
||||
binding.tvSupContactName.setText(supplier.getSupContactFirstName() + " " + supplier.getSupContactLastName());
|
||||
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(key));
|
||||
} 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(key);
|
||||
notifyItemChanged(position);
|
||||
} else {
|
||||
supplierClickListener.onSupplierClick(position);
|
||||
}
|
||||
});
|
||||
|
||||
holder.itemView.setOnLongClickListener(v -> {
|
||||
if (!selectionHelper.isInSelectionMode()) {
|
||||
selectionHelper.startSelection(key);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return supplierList.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -17,7 +19,12 @@ public interface AdoptionApi {
|
||||
@GET("api/v1/adoptions")
|
||||
Call<PageResponse<AdoptionDTO>> getAllAdoptions(
|
||||
@Query("page") int page,
|
||||
@Query("size") int size);
|
||||
@Query("size") int size,
|
||||
@Query("q") String query,
|
||||
@Query("status") String status,
|
||||
@Query("storeId") Long storeId,
|
||||
@Query("date") String date,
|
||||
@Query("employeeId") Long employeeId);
|
||||
|
||||
@GET("api/v1/adoptions/{id}")
|
||||
Call<AdoptionDTO> getAdoptionById(@Path("id") Long id);
|
||||
@@ -30,5 +37,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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.EmployeeDTO;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.*;
|
||||
|
||||
public interface EmployeeApi {
|
||||
|
||||
@GET("api/v1/employees")
|
||||
Call<PageResponse<EmployeeDTO>> getAllEmployees(
|
||||
@Query("page") int page,
|
||||
@Query("size") int size);
|
||||
|
||||
@GET("api/v1/employees/{id}")
|
||||
Call<EmployeeDTO> getEmployeeById(@Path("id") Long id);
|
||||
|
||||
@POST("api/v1/employees")
|
||||
Call<EmployeeDTO> createEmployee(@Body EmployeeDTO employee);
|
||||
|
||||
@PUT("api/v1/employees/{id}")
|
||||
Call<EmployeeDTO> updateEmployee(@Path("id") Long id, @Body EmployeeDTO employee);
|
||||
|
||||
@DELETE("api/v1/employees/{id}")
|
||||
Call<Void> deleteEmployee(@Path("id") Long id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@ package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.BulkDeleteRequest;
|
||||
import com.example.petstoremobile.dtos.InventoryDTO;
|
||||
import com.example.petstoremobile.dtos.InventoryRequest;
|
||||
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,17 +30,17 @@ public interface InventoryApi {
|
||||
|
||||
// POST /api/v1/inventory
|
||||
@POST("api/v1/inventory")
|
||||
Call<InventoryDTO> createInventory(@Body InventoryRequest request);
|
||||
Call<InventoryDTO> createInventory(@Body InventoryDTO request);
|
||||
|
||||
// PUT /api/v1/inventory/{id}
|
||||
@PUT("api/v1/inventory/{id}")
|
||||
Call<InventoryDTO> updateInventory(@Path("id") Long id, @Body InventoryRequest request);
|
||||
Call<InventoryDTO> updateInventory(@Path("id") Long id, @Body InventoryDTO request);
|
||||
|
||||
// DELETE /api/v1/inventory/{id}
|
||||
@DELETE("api/v1/inventory/{id}")
|
||||
Call<Void> deleteInventory(@Path("id") Long id);
|
||||
|
||||
// DELETE /api/v1/inventory (bulk delete)
|
||||
@DELETE("api/v1/inventory")
|
||||
@HTTP(method = "DELETE", path = "api/v1/inventory", hasBody = true)
|
||||
Call<Void> bulkDeleteInventory(@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.PetDTO;
|
||||
|
||||
@@ -8,6 +9,7 @@ import retrofit2.Call;
|
||||
import retrofit2.http.Body;
|
||||
import retrofit2.http.DELETE;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.HTTP;
|
||||
import retrofit2.http.Multipart;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.PUT;
|
||||
@@ -48,6 +50,10 @@ public interface PetApi {
|
||||
@DELETE("api/v1/pets/{id}")
|
||||
Call<Void> deletePet(@Path("id") Long id);
|
||||
|
||||
// Bulk delete pets
|
||||
@HTTP(method = "DELETE", path = "api/v1/pets", hasBody = true)
|
||||
Call<Void> bulkDeletePets(@Body BulkDeleteRequest request);
|
||||
|
||||
// Upload pet image
|
||||
@Multipart
|
||||
@POST("api/v1/pets/{id}/image")
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.RefundDTO;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface RefundApi {
|
||||
|
||||
@GET("api/v1/refunds")
|
||||
Call<List<RefundDTO>> getAllRefunds();
|
||||
|
||||
@GET("api/v1/refunds/{id}")
|
||||
Call<RefundDTO> getRefundById(@Path("id") Long id);
|
||||
|
||||
@POST("api/v1/refunds")
|
||||
Call<RefundDTO> createRefund(@Body RefundDTO refund);
|
||||
|
||||
@PUT("api/v1/refunds/{id}")
|
||||
Call<RefundDTO> updateRefund(@Path("id") Long id, @Body RefundDTO refund);
|
||||
|
||||
@DELETE("api/v1/refunds/{id}")
|
||||
Call<Void> deleteRefund(@Path("id") Long id);
|
||||
}
|
||||
@@ -132,4 +132,12 @@ public class RetrofitClient {
|
||||
return getClient(context).create(CategoryApi.class);
|
||||
}
|
||||
|
||||
public static RefundApi getRefundApi(Context context) {
|
||||
return getClient(context).create(RefundApi.class);
|
||||
}
|
||||
|
||||
public static EmployeeApi getEmployeeApi(Context context) {
|
||||
return getClient(context).create(EmployeeApi.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -179,4 +179,16 @@ public class NetworkModule {
|
||||
public static UserApi provideUserApi(Retrofit retrofit) {
|
||||
return retrofit.create(UserApi.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public static EmployeeApi provideEmployeeApi(Retrofit retrofit) {
|
||||
return retrofit.create(EmployeeApi.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public static RefundApi provideRefundApi(Retrofit retrofit) {
|
||||
return retrofit.create(RefundApi.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.example.petstoremobile.dtos;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class AdoptionDTO {
|
||||
|
||||
private Long adoptionId;
|
||||
private Long petId;
|
||||
private String petName;
|
||||
@@ -18,80 +19,58 @@ public class AdoptionDTO {
|
||||
private String createdAt;
|
||||
private String updatedAt;
|
||||
|
||||
public AdoptionDTO(Long petId, Long customerId, Long sourceStoreId, String adoptionDate, String adoptionStatus) {
|
||||
this.petId = petId;
|
||||
this.customerId = customerId;
|
||||
this.sourceStoreId = sourceStoreId;
|
||||
this.adoptionDate = adoptionDate;
|
||||
this.adoptionStatus = adoptionStatus;
|
||||
}
|
||||
public AdoptionDTO() {}
|
||||
|
||||
public AdoptionDTO(Long petId, Long customerId, Long employeeId, Long sourceStoreId, String adoptionDate, String adoptionStatus) {
|
||||
public AdoptionDTO(Long petId, Long customerId, Long employeeId, Long sourceStoreId,
|
||||
String adoptionDate, String adoptionStatus, BigDecimal adoptionFee) {
|
||||
this.petId = petId;
|
||||
this.customerId = customerId;
|
||||
this.employeeId = employeeId;
|
||||
this.sourceStoreId = sourceStoreId;
|
||||
this.adoptionDate = adoptionDate;
|
||||
this.adoptionStatus = adoptionStatus;
|
||||
this.adoptionFee = adoptionFee;
|
||||
}
|
||||
|
||||
public Long getAdoptionId() {
|
||||
return adoptionId;
|
||||
}
|
||||
public Long getAdoptionId() { return adoptionId; }
|
||||
public void setAdoptionId(Long adoptionId) { this.adoptionId = adoptionId; }
|
||||
|
||||
public Long getPetId() {
|
||||
return petId;
|
||||
}
|
||||
public Long getPetId() { return petId; }
|
||||
public void setPetId(Long petId) { this.petId = petId; }
|
||||
|
||||
public String getPetName() {
|
||||
return petName;
|
||||
}
|
||||
public String getPetName() { return petName; }
|
||||
public void setPetName(String petName) { this.petName = petName; }
|
||||
|
||||
public Long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
public Long getCustomerId() { return customerId; }
|
||||
public void setCustomerId(Long customerId) { this.customerId = customerId; }
|
||||
|
||||
public String getCustomerName() {
|
||||
return customerName;
|
||||
}
|
||||
public String getCustomerName() { return customerName; }
|
||||
public void setCustomerName(String customerName) { this.customerName = customerName; }
|
||||
|
||||
public Long getEmployeeId() {
|
||||
return employeeId;
|
||||
}
|
||||
public Long getEmployeeId() { return employeeId; }
|
||||
public void setEmployeeId(Long employeeId) { this.employeeId = employeeId; }
|
||||
|
||||
public String getEmployeeName() {
|
||||
return employeeName;
|
||||
}
|
||||
public String getEmployeeName() { return employeeName; }
|
||||
public void setEmployeeName(String employeeName) { this.employeeName = employeeName; }
|
||||
|
||||
public Long getSourceStoreId() {
|
||||
return sourceStoreId;
|
||||
}
|
||||
public Long getSourceStoreId() { return sourceStoreId; }
|
||||
public void setSourceStoreId(Long sourceStoreId) { this.sourceStoreId = sourceStoreId; }
|
||||
|
||||
public String getSourceStoreName() {
|
||||
return sourceStoreName;
|
||||
}
|
||||
public String getSourceStoreName() { return sourceStoreName; }
|
||||
public void setSourceStoreName(String sourceStoreName) { this.sourceStoreName = sourceStoreName; }
|
||||
|
||||
public String getAdoptionDate() {
|
||||
return adoptionDate;
|
||||
}
|
||||
public String getAdoptionDate() { return adoptionDate; }
|
||||
public void setAdoptionDate(String adoptionDate) { this.adoptionDate = adoptionDate; }
|
||||
|
||||
public String getAdoptionStatus() {
|
||||
return adoptionStatus;
|
||||
}
|
||||
public String getAdoptionStatus() { return adoptionStatus; }
|
||||
public void setAdoptionStatus(String adoptionStatus) { this.adoptionStatus = adoptionStatus; }
|
||||
|
||||
public String getStatus() {
|
||||
return adoptionStatus;
|
||||
}
|
||||
public BigDecimal getAdoptionFee() { return adoptionFee; }
|
||||
public void setAdoptionFee(BigDecimal adoptionFee) { this.adoptionFee = adoptionFee; }
|
||||
|
||||
public BigDecimal getAdoptionFee() {
|
||||
return adoptionFee;
|
||||
}
|
||||
public String getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(String createdAt) { this.createdAt = createdAt; }
|
||||
|
||||
public String getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public String getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
public String getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(String updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
public class AppointmentDTO {
|
||||
|
||||
|
||||
private Long appointmentId;
|
||||
private Long customerId;
|
||||
private String customerName;
|
||||
@@ -38,83 +38,43 @@ public class AppointmentDTO {
|
||||
this.petId = petId;
|
||||
}
|
||||
|
||||
public Long getAppointmentId() {
|
||||
return appointmentId;
|
||||
}
|
||||
public Long getAppointmentId() { return appointmentId; }
|
||||
|
||||
public Long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
public Long getCustomerId() { return customerId; }
|
||||
|
||||
public String getCustomerName() {
|
||||
return customerName;
|
||||
}
|
||||
public String getCustomerName() { return customerName; }
|
||||
|
||||
public Long getStoreId() {
|
||||
return storeId;
|
||||
}
|
||||
public Long getStoreId() { return storeId; }
|
||||
|
||||
public String getStoreName() {
|
||||
return storeName;
|
||||
}
|
||||
public String getStoreName() { return storeName; }
|
||||
|
||||
public Long getServiceId() {
|
||||
return serviceId;
|
||||
}
|
||||
public Long getServiceId() { return serviceId; }
|
||||
|
||||
public String getServiceName() {
|
||||
return serviceName;
|
||||
}
|
||||
public String getServiceName() { return serviceName; }
|
||||
|
||||
public Long getEmployeeId() {
|
||||
return employeeId;
|
||||
}
|
||||
public Long getEmployeeId() { return employeeId; }
|
||||
|
||||
public String getEmployeeName() {
|
||||
return employeeName;
|
||||
}
|
||||
public String getEmployeeName() { return employeeName; }
|
||||
|
||||
public String getAppointmentDate() {
|
||||
return appointmentDate;
|
||||
}
|
||||
public String getAppointmentDate() { return appointmentDate; }
|
||||
|
||||
public String getAppointmentTime() {
|
||||
return appointmentTime;
|
||||
}
|
||||
public String getAppointmentTime() { return appointmentTime; }
|
||||
|
||||
public String getAppointmentStatus() {
|
||||
return appointmentStatus;
|
||||
}
|
||||
public String getAppointmentStatus() { return appointmentStatus; }
|
||||
|
||||
public String getPetName() {
|
||||
return petName;
|
||||
}
|
||||
public String getPetName() { return petName; }
|
||||
|
||||
public Long getPetId() {
|
||||
return petId;
|
||||
}
|
||||
public Long getPetId() { return petId; }
|
||||
|
||||
public String getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
public String getCreatedAt() { return createdAt; }
|
||||
|
||||
public String getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
public String getUpdatedAt() { return updatedAt; }
|
||||
|
||||
public Long getPetID() {
|
||||
return petId;
|
||||
}
|
||||
public Long getPetID() { return petId; }
|
||||
|
||||
public String getServiceType() {
|
||||
return serviceName;
|
||||
}
|
||||
public String getServiceType() { return serviceName; }
|
||||
|
||||
public Long getServiceID() {
|
||||
return serviceId;
|
||||
}
|
||||
public Long getServiceID() { return serviceId; }
|
||||
|
||||
public String getStatus() {
|
||||
return appointmentStatus;
|
||||
}
|
||||
public String getStatus() { return appointmentStatus; }
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
public class EmployeeDTO {
|
||||
|
||||
private long EmployeeId;
|
||||
private Long userId;
|
||||
private String username;
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private String fullName;
|
||||
private String email;
|
||||
private String phone;
|
||||
private String role;
|
||||
private Boolean active;
|
||||
private String createAt;
|
||||
private String updatedAt;
|
||||
|
||||
|
||||
// Constructor for create and update the employee
|
||||
|
||||
|
||||
public EmployeeDTO(String username, String password, String firstName, String lastName,
|
||||
String email, String phone, String role, boolean active) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
this.email = email;
|
||||
this.phone = phone;
|
||||
this.role = role;
|
||||
this.active = active;
|
||||
}
|
||||
// password field for request only
|
||||
private String password;
|
||||
|
||||
|
||||
public long getEmployeeId() {
|
||||
|
||||
return EmployeeId;
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
|
||||
return userId;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
|
||||
return email;
|
||||
}
|
||||
public String getPhone() {
|
||||
|
||||
return phone;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
|
||||
return role;
|
||||
}
|
||||
|
||||
public Boolean getActive() {
|
||||
|
||||
return active;
|
||||
}
|
||||
|
||||
public String getCreateAt() {
|
||||
|
||||
return createAt;
|
||||
}
|
||||
|
||||
public String getUpdatedAt() {
|
||||
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
|
||||
return password;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -6,6 +6,8 @@ public class InventoryDTO {
|
||||
private Long prodId;
|
||||
private String productName;
|
||||
private String categoryName;
|
||||
private Long storeId;
|
||||
private String storeName;
|
||||
private Integer quantity;
|
||||
private String createdAt;
|
||||
private String updatedAt;
|
||||
@@ -14,8 +16,9 @@ public class InventoryDTO {
|
||||
}
|
||||
|
||||
// Constructor for create/update requests (matches InventoryRequest)
|
||||
public InventoryDTO(Long prodId, Integer quantity) {
|
||||
public InventoryDTO(Long prodId, Long storeId, Integer quantity) {
|
||||
this.prodId = prodId;
|
||||
this.storeId = storeId;
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
@@ -51,6 +54,22 @@ public class InventoryDTO {
|
||||
this.categoryName = categoryName;
|
||||
}
|
||||
|
||||
public Long getStoreId() {
|
||||
return storeId;
|
||||
}
|
||||
|
||||
public void setStoreId(Long storeId) {
|
||||
this.storeId = storeId;
|
||||
}
|
||||
|
||||
public String getStoreName() {
|
||||
return storeName;
|
||||
}
|
||||
|
||||
public void setStoreName(String storeName) {
|
||||
this.storeName = storeName;
|
||||
}
|
||||
|
||||
public Integer getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
public class InventoryRequest {
|
||||
private Long prodId;
|
||||
private Integer quantity;
|
||||
|
||||
public InventoryRequest() {
|
||||
}
|
||||
|
||||
public InventoryRequest(Long prodId, Integer quantity) {
|
||||
this.prodId = prodId;
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
public Long getProdId() {
|
||||
return prodId;
|
||||
}
|
||||
|
||||
public void setProdId(Long prodId) {
|
||||
this.prodId = prodId;
|
||||
}
|
||||
|
||||
public Integer getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public void setQuantity(Integer quantity) {
|
||||
this.quantity = quantity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ public class PurchaseOrderDTO {
|
||||
private Long purchaseOrderId;
|
||||
private Long supId;
|
||||
private String supplierName;
|
||||
private Long storeId;
|
||||
private String storeName;
|
||||
private String orderDate;
|
||||
private String status;
|
||||
private String createdAt;
|
||||
@@ -21,6 +23,14 @@ public class PurchaseOrderDTO {
|
||||
return supplierName;
|
||||
}
|
||||
|
||||
public Long getStoreId() {
|
||||
return storeId;
|
||||
}
|
||||
|
||||
public String getStoreName() {
|
||||
return storeName;
|
||||
}
|
||||
|
||||
public String getOrderDate() {
|
||||
return orderDate;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class RefundDTO {
|
||||
// Response fields
|
||||
private Long id;
|
||||
private Long saleId;
|
||||
private Long customerId;
|
||||
private BigDecimal amount;
|
||||
private String reason;
|
||||
private String status;
|
||||
private String createdAt;
|
||||
private String updatedAt;
|
||||
|
||||
// Constructor for create request
|
||||
public RefundDTO(Long saleId, String reason) {
|
||||
this.saleId = saleId;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
// Constructor for update request
|
||||
public RefundDTO(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Long getSaleId() {
|
||||
return saleId;
|
||||
}
|
||||
|
||||
public Long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public String getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public String getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
}
|
||||
@@ -1,82 +1,121 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
public class SaleDTO {
|
||||
|
||||
// Response fields
|
||||
private Long saleId;
|
||||
|
||||
private Long productId;
|
||||
private String productName;
|
||||
|
||||
private Integer quantity;
|
||||
private Double price;
|
||||
private Double totalAmount;
|
||||
|
||||
private String saleDate;
|
||||
private String customerName;
|
||||
private Long employeeId;
|
||||
private String employeeName;
|
||||
private Long storeId;
|
||||
private String storeName;
|
||||
private BigDecimal totalAmount;
|
||||
private String paymentMethod;
|
||||
private Boolean isRefund;
|
||||
private Long originalSaleId;
|
||||
private List<SaleItemDTO> items;
|
||||
private String createdAt;
|
||||
|
||||
public SaleDTO() {}
|
||||
// Request fields
|
||||
private Long customerId;
|
||||
|
||||
// Constructor for create request
|
||||
public SaleDTO(Long storeId, String paymentMethod, List<SaleItemDTO> items,
|
||||
Boolean isRefund, Long originalSaleId, Long customerId) {
|
||||
this.storeId = storeId;
|
||||
this.paymentMethod = paymentMethod;
|
||||
this.items = items;
|
||||
this.isRefund = isRefund;
|
||||
this.originalSaleId = originalSaleId;
|
||||
this.customerId = customerId;
|
||||
}
|
||||
|
||||
public Long getSaleId() {
|
||||
return saleId;
|
||||
}
|
||||
|
||||
public void setSaleId(Long saleId) {
|
||||
this.saleId = saleId;
|
||||
}
|
||||
|
||||
public Long getProductId() {
|
||||
return productId;
|
||||
}
|
||||
|
||||
public void setProductId(Long productId) {
|
||||
this.productId = productId;
|
||||
}
|
||||
|
||||
public String getProductName() {
|
||||
return productName;
|
||||
}
|
||||
|
||||
public void setProductName(String productName) {
|
||||
this.productName = productName;
|
||||
}
|
||||
|
||||
public Integer getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public void setQuantity(Integer quantity) {
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
public Double getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public void setPrice(Double price) {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public Double getTotalAmount() {
|
||||
return totalAmount;
|
||||
}
|
||||
|
||||
public void setTotalAmount(Double totalAmount) {
|
||||
this.totalAmount = totalAmount;
|
||||
}
|
||||
|
||||
public String getSaleDate() {
|
||||
return saleDate;
|
||||
}
|
||||
|
||||
public void setSaleDate(String saleDate) {
|
||||
this.saleDate = saleDate;
|
||||
public Long getEmployeeId() {
|
||||
return employeeId;
|
||||
}
|
||||
|
||||
public String getCustomerName() {
|
||||
return customerName;
|
||||
public String getEmployeeName() {
|
||||
return employeeName;
|
||||
}
|
||||
|
||||
public void setCustomerName(String customerName) {
|
||||
this.customerName = customerName;
|
||||
public Long getStoreId() {
|
||||
return storeId;
|
||||
}
|
||||
|
||||
public String getStoreName() {
|
||||
return storeName;
|
||||
}
|
||||
|
||||
public BigDecimal getTotalAmount() {
|
||||
return totalAmount;
|
||||
}
|
||||
|
||||
public String getPaymentMethod() {
|
||||
return paymentMethod;
|
||||
}
|
||||
|
||||
public Boolean getIsRefund() {
|
||||
return isRefund;
|
||||
}
|
||||
|
||||
public Long getOriginalSaleId() {
|
||||
return originalSaleId;
|
||||
}
|
||||
|
||||
public List<SaleItemDTO> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public String getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public Long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
// Nested SaleItemDTO
|
||||
public static class SaleItemDTO {
|
||||
private Long saleItemId;
|
||||
private Long prodId;
|
||||
private String productName;
|
||||
private Integer quantity;
|
||||
private BigDecimal unitPrice;
|
||||
|
||||
// Constructor for request
|
||||
public SaleItemDTO(Long prodId, Integer quantity) {
|
||||
this.prodId = prodId;
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
public Long getSaleItemId() {
|
||||
return saleItemId;
|
||||
}
|
||||
|
||||
public Long getProdId() {
|
||||
return prodId;
|
||||
}
|
||||
|
||||
public String getProductName() {
|
||||
return productName;
|
||||
}
|
||||
|
||||
public Integer getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public BigDecimal getUnitPrice() {
|
||||
return unitPrice;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,13 @@ public class ListFragment extends Fragment {
|
||||
binding.drawerInventory.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// Only show for ADMIN
|
||||
if ("ADMIN".equalsIgnoreCase(role)) {
|
||||
binding.drawerStaff.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.drawerStaff.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
//add Listeners to the drawer so user won't be able to interact with the innerContainer (the list fragments)
|
||||
//while the drawer is open
|
||||
binding.drawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
|
||||
@@ -83,6 +90,8 @@ public class ListFragment extends Fragment {
|
||||
binding.drawerProductSupplier.setOnClickListener(v -> navigateTo(R.id.nav_product_supplier));
|
||||
binding.drawerPurchaseOrderView.setOnClickListener(v -> navigateTo(R.id.nav_purchase_order));
|
||||
binding.drawerSale.setOnClickListener(v -> navigateTo(R.id.nav_sale));
|
||||
binding.drawerStaff.setOnClickListener(v -> navigateTo(R.id.nav_staff));
|
||||
binding.drawerAnalytics.setOnClickListener(v -> navigateTo(R.id.nav_analytics));
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@@ -1,252 +1,142 @@
|
||||
package com.example.petstoremobile.fragments.listfragments;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.text.*;
|
||||
import android.util.Log;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
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 androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.adapters.AdoptionAdapter;
|
||||
import com.example.petstoremobile.databinding.FragmentAdoptionBinding;
|
||||
import com.example.petstoremobile.api.AdoptionApi;
|
||||
import com.example.petstoremobile.api.RetrofitClient;
|
||||
import com.example.petstoremobile.dtos.AdoptionDTO;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.fragments.ListFragment;
|
||||
import com.example.petstoremobile.viewmodels.AdoptionViewModel;
|
||||
import com.example.petstoremobile.utils.EventDecorator;
|
||||
import com.prolificinteractive.materialcalendarview.CalendarDay;
|
||||
import com.prolificinteractive.materialcalendarview.CalendarMode;
|
||||
import com.prolificinteractive.materialcalendarview.MaterialCalendarView;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import com.example.petstoremobile.fragments.listfragments.detailfragments.AdoptionDetailFragment;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import java.util.*;
|
||||
import retrofit2.*;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
@AndroidEntryPoint
|
||||
public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdoptionClickListener {
|
||||
|
||||
private FragmentAdoptionBinding binding;
|
||||
private List<AdoptionDTO> adoptionList = new ArrayList<>();
|
||||
private List<AdoptionDTO> filteredList = new ArrayList<>();
|
||||
private AdoptionAdapter adapter;
|
||||
private AdoptionViewModel viewModel;
|
||||
private CalendarDay selectedCalendarDay;
|
||||
private boolean isMonthMode = false;
|
||||
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
|
||||
private AdoptionApi api;
|
||||
private SwipeRefreshLayout swipeRefresh;
|
||||
private EditText etSearch;
|
||||
private ImageButton hamburger;
|
||||
|
||||
/**
|
||||
* Initializes the fragment and its ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(AdoptionViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the fragment's UI components, including RecyclerView, Search, SwipeRefresh, and Calendar.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
binding = FragmentAdoptionBinding.inflate(inflater, container, false);
|
||||
View view = inflater.inflate(R.layout.fragment_adoption, container, false);
|
||||
|
||||
setupRecyclerView();
|
||||
setupSearch();
|
||||
setupSwipeRefresh();
|
||||
setupCalendar();
|
||||
api = RetrofitClient.getAdoptionApi(requireContext());
|
||||
hamburger = view.findViewById(R.id.btnHamburgerAdoption);
|
||||
|
||||
setupRecyclerView(view);
|
||||
setupSearch(view);
|
||||
setupSwipeRefresh(view);
|
||||
loadAdoptions();
|
||||
|
||||
binding.fabAddAdoption.setOnClickListener(v -> openDetail(-1));
|
||||
FloatingActionButton fab = view.findViewById(R.id.fabAddAdoption);
|
||||
fab.setOnClickListener(v -> openDetail(-1));
|
||||
|
||||
binding.btnHamburgerAdoption.setOnClickListener(v -> {
|
||||
Fragment parent = getParentFragment();
|
||||
if (parent != null) {
|
||||
Fragment grandParent = parent.getParentFragment();
|
||||
if (grandParent instanceof ListFragment) {
|
||||
((ListFragment) grandParent).openDrawer();
|
||||
}
|
||||
}
|
||||
hamburger.setOnClickListener(v -> {
|
||||
ListFragment lf = (ListFragment) getParentFragment();
|
||||
if (lf != null) lf.openDrawer();
|
||||
});
|
||||
|
||||
binding.btnToggleCalendarModeAdoption.setOnClickListener(v -> toggleCalendarMode());
|
||||
|
||||
return binding.getRoot();
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the calendar display between week and month modes.
|
||||
*/
|
||||
private void toggleCalendarMode() {
|
||||
isMonthMode = !isMonthMode;
|
||||
binding.calendarViewAdoption.state().edit()
|
||||
.setCalendarDisplayMode(isMonthMode ? CalendarMode.MONTHS : CalendarMode.WEEKS)
|
||||
.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the date selection listener for the calendar.
|
||||
*/
|
||||
private void setupCalendar() {
|
||||
binding.calendarViewAdoption.setOnDateChangedListener((widget, date, selected) -> {
|
||||
if (selected) {
|
||||
if (date.equals(selectedCalendarDay)) {
|
||||
selectedCalendarDay = null;
|
||||
binding.calendarViewAdoption.clearSelection();
|
||||
} else {
|
||||
selectedCalendarDay = date;
|
||||
}
|
||||
} else {
|
||||
selectedCalendarDay = null;
|
||||
}
|
||||
filter(binding.etSearchAdoption.getText().toString());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the calendar decorators to highlight days with adoptions.
|
||||
*/
|
||||
private void updateCalendarDecorators() {
|
||||
HashSet<CalendarDay> datesWithAdoptions = new HashSet<>();
|
||||
for (AdoptionDTO adoption : adoptionList) {
|
||||
try {
|
||||
if (adoption.getAdoptionDate() != null) {
|
||||
Date date = dateFormat.parse(adoption.getAdoptionDate());
|
||||
if (date != null) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
datesWithAdoptions.add(CalendarDay.from(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH)));
|
||||
}
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
Log.e("AdoptionFragment", "Error parsing date: " + adoption.getAdoptionDate());
|
||||
}
|
||||
}
|
||||
binding.calendarViewAdoption.removeDecorators();
|
||||
binding.calendarViewAdoption.addDecorator(new EventDecorator(Color.RED, datesWithAdoptions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the RecyclerView for displaying adoptions.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
private void setupRecyclerView(View view) {
|
||||
RecyclerView rv = view.findViewById(R.id.recyclerViewAdoptions);
|
||||
adapter = new AdoptionAdapter(filteredList, this);
|
||||
binding.recyclerViewAdoptions.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
binding.recyclerViewAdoptions.setAdapter(adapter);
|
||||
rv.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
rv.setAdapter(adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search bar for filtering
|
||||
*/
|
||||
private void setupSearch() {
|
||||
binding.etSearchAdoption.addTextChangedListener(new android.text.TextWatcher() {
|
||||
private void setupSearch(View view) {
|
||||
etSearch = view.findViewById(R.id.etSearchAdoption);
|
||||
etSearch.addTextChangedListener(new TextWatcher() {
|
||||
public void beforeTextChanged(CharSequence s, int a, int b, int c) {}
|
||||
public void afterTextChanged(android.text.Editable s) {}
|
||||
public void afterTextChanged(Editable s) {}
|
||||
public void onTextChanged(CharSequence s, int a, int b, int c) {
|
||||
filter(s.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the SwipeRefreshLayout to reload adoption data.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshAdoption.setOnRefreshListener(this::loadAdoptions);
|
||||
private void setupSwipeRefresh(View view) {
|
||||
swipeRefresh = view.findViewById(R.id.swipeRefreshAdoption);
|
||||
swipeRefresh.setOnRefreshListener(this::loadAdoptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the adoption list based on search query and selected calendar date.
|
||||
*/
|
||||
private void filter(String query) {
|
||||
filteredList.clear();
|
||||
String lowerQuery = query.toLowerCase();
|
||||
|
||||
String selectedDateString = null;
|
||||
if (selectedCalendarDay != null) {
|
||||
selectedDateString = String.format(Locale.getDefault(), "%04d-%02d-%02d",
|
||||
selectedCalendarDay.getYear(), selectedCalendarDay.getMonth(), selectedCalendarDay.getDay());
|
||||
}
|
||||
|
||||
for (AdoptionDTO a : adoptionList) {
|
||||
boolean matchesSearch = query.isEmpty() ||
|
||||
(a.getCustomerName() != null && a.getCustomerName().toLowerCase().contains(lowerQuery)) ||
|
||||
(a.getPetName() != null && a.getPetName().toLowerCase().contains(lowerQuery)) ||
|
||||
(a.getAdoptionStatus() != null && a.getAdoptionStatus().toLowerCase().contains(lowerQuery));
|
||||
|
||||
boolean matchesDate = (selectedDateString == null) ||
|
||||
(a.getAdoptionDate() != null && a.getAdoptionDate().startsWith(selectedDateString));
|
||||
|
||||
if (matchesSearch && matchesDate) {
|
||||
filteredList.add(a);
|
||||
if (query.isEmpty()) {
|
||||
filteredList.addAll(adoptionList);
|
||||
} else {
|
||||
String lower = query.toLowerCase();
|
||||
for (AdoptionDTO a : adoptionList) {
|
||||
if ((a.getCustomerName() != null && a.getCustomerName().toLowerCase().contains(lower))
|
||||
|| (a.getPetName() != null && a.getPetName().toLowerCase().contains(lower))
|
||||
|| (a.getAdoptionStatus() != null && a.getAdoptionStatus().toLowerCase().contains(lower))) {
|
||||
filteredList.add(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the adoption list from the server through the ViewModel.
|
||||
*/
|
||||
private void loadAdoptions() {
|
||||
//Load all adoptions from the backend using viewModel
|
||||
viewModel.getAllAdoptions(0, 500).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
|
||||
// Check the status to see if the resource is loaded and display the data
|
||||
switch (resource.status) {
|
||||
case LOADING:
|
||||
// Show loading indicator
|
||||
binding.swipeRefreshAdoption.setRefreshing(true);
|
||||
break;
|
||||
case SUCCESS:
|
||||
// Hide loading indicator and display data
|
||||
binding.swipeRefreshAdoption.setRefreshing(false);
|
||||
if (resource.data != null) {
|
||||
adoptionList.clear();
|
||||
adoptionList.addAll(resource.data.getContent());
|
||||
updateCalendarDecorators();
|
||||
filter(binding.etSearchAdoption != null ? binding.etSearchAdoption.getText().toString() : "");
|
||||
}
|
||||
break;
|
||||
case ERROR:
|
||||
// Hide loading indicator and toast error message
|
||||
binding.swipeRefreshAdoption.setRefreshing(false);
|
||||
Toast.makeText(getContext(), "Failed to load adoptions: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
Log.e("AdoptionFragment", "Error loading adoptions: " + resource.message);
|
||||
break;
|
||||
if (swipeRefresh != null) swipeRefresh.setRefreshing(true);
|
||||
api.getAllAdoptions(0, 100, null, null, null, null, null).enqueue(new Callback<PageResponse<AdoptionDTO>>() {
|
||||
public void onResponse(Call<PageResponse<AdoptionDTO>> c,
|
||||
Response<PageResponse<AdoptionDTO>> r) {
|
||||
if (swipeRefresh != null) swipeRefresh.setRefreshing(false);
|
||||
if (r.isSuccessful() && r.body() != null) {
|
||||
adoptionList.clear();
|
||||
adoptionList.addAll(r.body().getContent());
|
||||
filter(etSearch != null ? etSearch.getText().toString() : "");
|
||||
} else {
|
||||
Toast.makeText(getContext(), "Failed to load adoptions", Toast.LENGTH_SHORT).show();
|
||||
Log.e("AdoptionFragment", "Error: " + r.message());
|
||||
}
|
||||
}
|
||||
public void onFailure(Call<PageResponse<AdoptionDTO>> c, Throwable t) {
|
||||
if (swipeRefresh != null) swipeRefresh.setRefreshing(false);
|
||||
Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show();
|
||||
Log.e("AdoptionFragment", t.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the adoption detail screen for a specific adoption or to create a new one.
|
||||
*/
|
||||
private void openDetail(int position) {
|
||||
Bundle args = new Bundle();
|
||||
|
||||
if (position != -1) {
|
||||
AdoptionDTO a = filteredList.get(position);
|
||||
args.putLong("adoptionId", a.getAdoptionId());
|
||||
}
|
||||
|
||||
args.putLong("petId", a.getPetId() != null ? a.getPetId() : -1);
|
||||
args.putLong("customerId", a.getCustomerId() != null ? a.getCustomerId() : -1);
|
||||
args.putString("adoptionDate", a.getAdoptionDate());
|
||||
args.putString("adoptionStatus", a.getAdoptionStatus());
|
||||
if (a.getEmployeeId() != null)
|
||||
args.putLong("employeeId", a.getEmployeeId());}
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_adoption_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles item click in the adoption list.
|
||||
*/
|
||||
@Override
|
||||
public void onAdoptionClick(int position) { openDetail(position); }
|
||||
|
||||
@Override
|
||||
public void onSelectionChanged(int count) {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,334 @@
|
||||
package com.example.petstoremobile.fragments.listfragments;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.api.RetrofitClient;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.SaleDTO;
|
||||
import com.example.petstoremobile.fragments.ListFragment;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.*;
|
||||
import retrofit2.*;
|
||||
|
||||
public class AnalyticsFragment extends Fragment {
|
||||
|
||||
private TextView tvTotalRevenue, tvTotalTransactions, tvAvgTransaction, tvTotalItems;
|
||||
private LinearLayout llTopRevenue, llTopQuantity, llPaymentMethods, llEmployeePerformance, llDailyRevenue;
|
||||
private Button btnRefresh;
|
||||
private ImageButton hamburger;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_analytics, container, false);
|
||||
|
||||
initViews(view);
|
||||
loadAnalytics();
|
||||
|
||||
btnRefresh.setOnClickListener(v -> loadAnalytics());
|
||||
hamburger.setOnClickListener(v -> openDrawer());
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void openDrawer() {
|
||||
Fragment parent = getParentFragment();
|
||||
if (parent != null) {
|
||||
Fragment grandParent = parent.getParentFragment();
|
||||
if (grandParent instanceof ListFragment) {
|
||||
((ListFragment) grandParent).openDrawer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initViews(View v) {
|
||||
tvTotalRevenue = v.findViewById(R.id.tvTotalRevenue);
|
||||
tvTotalTransactions = v.findViewById(R.id.tvTotalTransactions);
|
||||
tvAvgTransaction = v.findViewById(R.id.tvAvgTransaction);
|
||||
tvTotalItems = v.findViewById(R.id.tvTotalItems);
|
||||
llTopRevenue = v.findViewById(R.id.llTopRevenue);
|
||||
llTopQuantity = v.findViewById(R.id.llTopQuantity);
|
||||
llPaymentMethods = v.findViewById(R.id.llPaymentMethods);
|
||||
llEmployeePerformance = v.findViewById(R.id.llEmployeePerformance);
|
||||
llDailyRevenue = v.findViewById(R.id.llDailyRevenue);
|
||||
btnRefresh = v.findViewById(R.id.btnRefreshAnalytics);
|
||||
hamburger = v.findViewById(R.id.btnHamburgerAnalytics);
|
||||
}
|
||||
|
||||
private void loadAnalytics() {
|
||||
// Clear all sections
|
||||
llTopRevenue.removeAllViews();
|
||||
llTopQuantity.removeAllViews();
|
||||
llPaymentMethods.removeAllViews();
|
||||
llEmployeePerformance.removeAllViews();
|
||||
llDailyRevenue.removeAllViews();
|
||||
|
||||
// Show loading
|
||||
tvTotalRevenue.setText("Loading...");
|
||||
tvTotalTransactions.setText("...");
|
||||
tvAvgTransaction.setText("...");
|
||||
tvTotalItems.setText("...");
|
||||
|
||||
RetrofitClient.getSaleApi(requireContext()).getAllSales(0, 1000)
|
||||
.enqueue(new Callback<PageResponse<SaleDTO>>() {
|
||||
public void onResponse(Call<PageResponse<SaleDTO>> c,
|
||||
Response<PageResponse<SaleDTO>> r) {
|
||||
if (r.isSuccessful() && r.body() != null) {
|
||||
computeAndDisplay(r.body().getContent());
|
||||
} else {
|
||||
showError("Failed to load sales data");
|
||||
}
|
||||
}
|
||||
|
||||
public void onFailure(Call<PageResponse<SaleDTO>> c, Throwable t) {
|
||||
Log.e("Analytics", t.getMessage());
|
||||
showError("Network error");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void computeAndDisplay(List<SaleDTO> sales) {
|
||||
// Filter out refunds for most metrics
|
||||
List<SaleDTO> regularSales = new ArrayList<>();
|
||||
for (SaleDTO s : sales) {
|
||||
if (!Boolean.TRUE.equals(s.getIsRefund()))
|
||||
regularSales.add(s);
|
||||
}
|
||||
|
||||
// ── Summary ──────────────────────────────────────────
|
||||
BigDecimal totalRevenue = BigDecimal.ZERO;
|
||||
int totalItems = 0;
|
||||
|
||||
for (SaleDTO s : regularSales) {
|
||||
if (s.getTotalAmount() != null)
|
||||
totalRevenue = totalRevenue.add(s.getTotalAmount());
|
||||
if (s.getItems() != null) {
|
||||
for (SaleDTO.SaleItemDTO item : s.getItems()) {
|
||||
if (item.getQuantity() != null)
|
||||
totalItems += Math.abs(item.getQuantity());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int totalTx = regularSales.size();
|
||||
BigDecimal avgTx = totalTx > 0
|
||||
? totalRevenue.divide(BigDecimal.valueOf(totalTx), 2, RoundingMode.HALF_UP)
|
||||
: BigDecimal.ZERO;
|
||||
|
||||
tvTotalRevenue.setText("$" + totalRevenue.setScale(2, RoundingMode.HALF_UP));
|
||||
tvTotalTransactions.setText(String.valueOf(totalTx));
|
||||
tvAvgTransaction.setText("$" + avgTx);
|
||||
tvTotalItems.setText(String.valueOf(totalItems));
|
||||
|
||||
// ── Top Products by Revenue ───────────────────────────
|
||||
Map<String, BigDecimal> revenueByProduct = new LinkedHashMap<>();
|
||||
Map<String, Integer> quantityByProduct = new LinkedHashMap<>();
|
||||
|
||||
for (SaleDTO s : regularSales) {
|
||||
if (s.getItems() != null) {
|
||||
for (SaleDTO.SaleItemDTO item : s.getItems()) {
|
||||
String name = item.getProductName() != null ? item.getProductName() : "Unknown";
|
||||
int qty = item.getQuantity() != null ? Math.abs(item.getQuantity()) : 0;
|
||||
BigDecimal lineTotal = item.getUnitPrice() != null
|
||||
? item.getUnitPrice().multiply(BigDecimal.valueOf(qty))
|
||||
: BigDecimal.ZERO;
|
||||
|
||||
revenueByProduct.merge(name, lineTotal, BigDecimal::add);
|
||||
quantityByProduct.merge(name, qty, Integer::sum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by revenue desc, take top 5
|
||||
List<Map.Entry<String, BigDecimal>> topRevenue = new ArrayList<>(revenueByProduct.entrySet());
|
||||
topRevenue.sort((a, b) -> b.getValue().compareTo(a.getValue()));
|
||||
BigDecimal maxRevenue = topRevenue.isEmpty() ? BigDecimal.ONE : topRevenue.get(0).getValue();
|
||||
|
||||
llTopRevenue.removeAllViews();
|
||||
for (int i = 0; i < Math.min(5, topRevenue.size()); i++) {
|
||||
Map.Entry<String, BigDecimal> e = topRevenue.get(i);
|
||||
addBarRow(llTopRevenue, e.getKey(), "$" + e.getValue().setScale(2, RoundingMode.HALF_UP),
|
||||
e.getValue().floatValue() / maxRevenue.floatValue(), "#ff6b35");
|
||||
}
|
||||
if (topRevenue.isEmpty())
|
||||
addEmptyRow(llTopRevenue, "No data");
|
||||
|
||||
// Sort by quantity desc, take top 5
|
||||
List<Map.Entry<String, Integer>> topQuantity = new ArrayList<>(quantityByProduct.entrySet());
|
||||
topQuantity.sort((a, b) -> b.getValue() - a.getValue());
|
||||
int maxQty = topQuantity.isEmpty() ? 1 : topQuantity.get(0).getValue();
|
||||
|
||||
llTopQuantity.removeAllViews();
|
||||
for (int i = 0; i < Math.min(5, topQuantity.size()); i++) {
|
||||
Map.Entry<String, Integer> e = topQuantity.get(i);
|
||||
addBarRow(llTopQuantity, e.getKey(), e.getValue() + " units",
|
||||
(float) e.getValue() / maxQty, "#4ecdc4");
|
||||
}
|
||||
if (topQuantity.isEmpty())
|
||||
addEmptyRow(llTopQuantity, "No data");
|
||||
|
||||
// ── Payment Methods ───────────────────────────────────
|
||||
Map<String, Integer> paymentCount = new LinkedHashMap<>();
|
||||
for (SaleDTO s : regularSales) {
|
||||
String method = s.getPaymentMethod() != null ? s.getPaymentMethod() : "Unknown";
|
||||
paymentCount.merge(method, 1, Integer::sum);
|
||||
}
|
||||
|
||||
int maxPayment = paymentCount.values().stream().max(Integer::compare).orElse(1);
|
||||
String[] paymentColors = { "#1a759f", "#ff9f1c", "#577590", "#90be6d" };
|
||||
int ci = 0;
|
||||
llPaymentMethods.removeAllViews();
|
||||
for (Map.Entry<String, Integer> e : paymentCount.entrySet()) {
|
||||
addBarRow(llPaymentMethods, e.getKey(),
|
||||
e.getValue() + " transactions",
|
||||
(float) e.getValue() / maxPayment,
|
||||
paymentColors[ci % paymentColors.length]);
|
||||
ci++;
|
||||
}
|
||||
if (paymentCount.isEmpty())
|
||||
addEmptyRow(llPaymentMethods, "No data");
|
||||
|
||||
// ── Employee Performance ──────────────────────────────
|
||||
Map<String, BigDecimal> employeeRevenue = new LinkedHashMap<>();
|
||||
for (SaleDTO s : regularSales) {
|
||||
String emp = s.getEmployeeName() != null ? s.getEmployeeName() : "Unknown";
|
||||
if (s.getTotalAmount() != null)
|
||||
employeeRevenue.merge(emp, s.getTotalAmount(), BigDecimal::add);
|
||||
}
|
||||
|
||||
List<Map.Entry<String, BigDecimal>> empList = new ArrayList<>(employeeRevenue.entrySet());
|
||||
empList.sort((a, b) -> b.getValue().compareTo(a.getValue()));
|
||||
BigDecimal maxEmp = empList.isEmpty() ? BigDecimal.ONE : empList.get(0).getValue();
|
||||
|
||||
llEmployeePerformance.removeAllViews();
|
||||
for (Map.Entry<String, BigDecimal> e : empList) {
|
||||
addBarRow(llEmployeePerformance, e.getKey(),
|
||||
"$" + e.getValue().setScale(2, RoundingMode.HALF_UP),
|
||||
e.getValue().floatValue() / maxEmp.floatValue(),
|
||||
"#1a759f");
|
||||
}
|
||||
if (empList.isEmpty())
|
||||
addEmptyRow(llEmployeePerformance, "No data");
|
||||
|
||||
// ── Daily Revenue (last 7 days) ───────────────────────
|
||||
Map<String, BigDecimal> dailyRevenue = new TreeMap<>();
|
||||
|
||||
// Initialize last 7 days
|
||||
Calendar cal = Calendar.getInstance();
|
||||
for (int i = 6; i >= 0; i--) {
|
||||
Calendar day = Calendar.getInstance();
|
||||
day.add(Calendar.DAY_OF_YEAR, -i);
|
||||
String key = String.format("%04d-%02d-%02d",
|
||||
day.get(Calendar.YEAR),
|
||||
day.get(Calendar.MONTH) + 1,
|
||||
day.get(Calendar.DAY_OF_MONTH));
|
||||
dailyRevenue.put(key, BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
for (SaleDTO s : regularSales) {
|
||||
if (s.getSaleDate() != null && s.getTotalAmount() != null) {
|
||||
String date = s.getSaleDate().length() >= 10
|
||||
? s.getSaleDate().substring(0, 10)
|
||||
: s.getSaleDate();
|
||||
if (dailyRevenue.containsKey(date)) {
|
||||
dailyRevenue.merge(date, s.getTotalAmount(), BigDecimal::add);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BigDecimal maxDaily = dailyRevenue.values().stream()
|
||||
.max(BigDecimal::compareTo).orElse(BigDecimal.ONE);
|
||||
if (maxDaily.compareTo(BigDecimal.ZERO) == 0)
|
||||
maxDaily = BigDecimal.ONE;
|
||||
|
||||
llDailyRevenue.removeAllViews();
|
||||
for (Map.Entry<String, BigDecimal> e : dailyRevenue.entrySet()) {
|
||||
// Show just MM-DD
|
||||
String label = e.getKey().length() >= 10
|
||||
? e.getKey().substring(5)
|
||||
: e.getKey();
|
||||
addBarRow(llDailyRevenue, label,
|
||||
"$" + e.getValue().setScale(2, RoundingMode.HALF_UP),
|
||||
e.getValue().floatValue() / maxDaily.floatValue(),
|
||||
"#ff6b35");
|
||||
}
|
||||
}
|
||||
|
||||
// Adds a horizontal bar row with label, value and a proportional bar
|
||||
private void addBarRow(LinearLayout parent, String label, String value, float ratio, String color) {
|
||||
LinearLayout row = new LinearLayout(getContext());
|
||||
row.setOrientation(LinearLayout.VERTICAL);
|
||||
row.setPadding(0, 6, 0, 6);
|
||||
|
||||
// Label + value row
|
||||
LinearLayout labelRow = new LinearLayout(getContext());
|
||||
labelRow.setOrientation(LinearLayout.HORIZONTAL);
|
||||
|
||||
TextView tvLabel = new TextView(getContext());
|
||||
tvLabel.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f));
|
||||
tvLabel.setText(label);
|
||||
tvLabel.setTextColor(Color.parseColor("#444441"));
|
||||
tvLabel.setTextSize(13f);
|
||||
|
||||
TextView tvValue = new TextView(getContext());
|
||||
tvValue.setText(value);
|
||||
tvValue.setTextColor(Color.parseColor("#444441"));
|
||||
tvValue.setTextSize(13f);
|
||||
tvValue.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_END);
|
||||
|
||||
labelRow.addView(tvLabel);
|
||||
labelRow.addView(tvValue);
|
||||
|
||||
// Bar background
|
||||
LinearLayout barBg = new LinearLayout(getContext());
|
||||
LinearLayout.LayoutParams bgParams = new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT, 12);
|
||||
bgParams.setMargins(0, 4, 0, 0);
|
||||
barBg.setLayoutParams(bgParams);
|
||||
barBg.setBackgroundColor(Color.parseColor("#EEEEEE"));
|
||||
|
||||
// Bar fill
|
||||
View barFill = new View(getContext());
|
||||
int fillWidth = (int) (ratio * 100);
|
||||
LinearLayout.LayoutParams fillParams = new LinearLayout.LayoutParams(
|
||||
0, LinearLayout.LayoutParams.MATCH_PARENT, ratio);
|
||||
barFill.setLayoutParams(fillParams);
|
||||
barFill.setBackgroundColor(Color.parseColor(color));
|
||||
barBg.addView(barFill);
|
||||
|
||||
// Empty space
|
||||
View spacer = new View(getContext());
|
||||
spacer.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
0, LinearLayout.LayoutParams.MATCH_PARENT, 1f - ratio));
|
||||
barBg.addView(spacer);
|
||||
|
||||
row.addView(labelRow);
|
||||
row.addView(barBg);
|
||||
parent.addView(row);
|
||||
}
|
||||
|
||||
private void addEmptyRow(LinearLayout parent, String message) {
|
||||
TextView tv = new TextView(getContext());
|
||||
tv.setText(message);
|
||||
tv.setTextColor(Color.parseColor("#888780"));
|
||||
tv.setTextSize(13f);
|
||||
parent.addView(tv);
|
||||
}
|
||||
|
||||
private void showError(String msg) {
|
||||
if (getContext() == null)
|
||||
return;
|
||||
tvTotalRevenue.setText("Error");
|
||||
tvTotalTransactions.setText("—");
|
||||
tvAvgTransaction.setText("—");
|
||||
tvTotalItems.setText("—");
|
||||
Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
@@ -16,16 +16,15 @@ import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.adapters.AppointmentAdapter;
|
||||
import com.example.petstoremobile.adapters.WhiteTextArrayAdapter;
|
||||
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 +56,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 +90,7 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
setupCalendar();
|
||||
setupFilterToggle();
|
||||
setupMyAppointmentFilter();
|
||||
setupBulkDelete();
|
||||
|
||||
binding.fabAddAppointment.setOnClickListener(v -> openAppointmentDetails(-1));
|
||||
|
||||
@@ -110,6 +111,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();
|
||||
@@ -237,30 +251,14 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
*/
|
||||
private void setupStatusFilter() {
|
||||
String[] statuses = {"All Statuses", "Booked", "Completed", "Cancelled", "Missed"};
|
||||
WhiteTextArrayAdapter<String> adapter = new WhiteTextArrayAdapter<>(requireContext(), android.R.layout.simple_spinner_item, statuses);
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
binding.spinnerStatus.setAdapter(adapter);
|
||||
|
||||
binding.spinnerStatus.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
loadAppointmentData();
|
||||
}
|
||||
@Override public void onNothingSelected(AdapterView<?> parent) {}
|
||||
});
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatus, statuses, this::loadAppointmentData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the store filter spinner.
|
||||
*/
|
||||
private void setupStoreFilter() {
|
||||
binding.spinnerStore.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
loadAppointmentData();
|
||||
}
|
||||
@Override public void onNothingSelected(AdapterView<?> parent) {}
|
||||
});
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerStore, this::loadAppointmentData);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,6 +301,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.
|
||||
*/
|
||||
@@ -366,4 +371,4 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
binding.recyclerViewAppointments.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
binding.recyclerViewAppointments.setAdapter(adapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -24,6 +23,7 @@ import com.example.petstoremobile.databinding.FragmentInventoryBinding;
|
||||
import com.example.petstoremobile.dtos.InventoryDTO;
|
||||
import com.example.petstoremobile.dtos.StoreDTO;
|
||||
import com.example.petstoremobile.fragments.ListFragment;
|
||||
import com.example.petstoremobile.utils.BulkDeleteHandler;
|
||||
import com.example.petstoremobile.viewmodels.InventoryViewModel;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.SpinnerUtils;
|
||||
@@ -44,6 +44,7 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
private List<StoreDTO> storeList = new ArrayList<>();
|
||||
private InventoryAdapter adapter;
|
||||
private InventoryViewModel viewModel;
|
||||
private BulkDeleteHandler bulkDeleteHandler;
|
||||
|
||||
// Pagination
|
||||
private int currentPage = 0;
|
||||
@@ -72,6 +73,7 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
setupStoreFilter();
|
||||
setupSwipeRefresh();
|
||||
setupFilterToggle();
|
||||
setupBulkDelete();
|
||||
loadInventory(true);
|
||||
loadStoreData();
|
||||
|
||||
@@ -87,11 +89,22 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
}
|
||||
});
|
||||
|
||||
binding.btnBulkDelete.setOnClickListener(v -> confirmBulkDelete());
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
private void setupBulkDelete() {
|
||||
bulkDeleteHandler = new BulkDeleteHandler(
|
||||
this,
|
||||
binding.layoutBulkDelete,
|
||||
binding.tvSelectionCount,
|
||||
binding.btnBulkDelete,
|
||||
adapter,
|
||||
"inventory item",
|
||||
viewModel::bulkDeleteInventory,
|
||||
() -> loadInventory(true)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
@@ -134,13 +147,7 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
* Configures the store filter spinner.
|
||||
*/
|
||||
private void setupStoreFilter() {
|
||||
binding.spinnerStore.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
loadInventory(true);
|
||||
}
|
||||
@Override public void onNothingSelected(AdapterView<?> parent) {}
|
||||
});
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadInventory(true));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,50 +250,6 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog before performing a bulk deletion of selected items.
|
||||
*/
|
||||
private void confirmBulkDelete() {
|
||||
List<Long> ids = adapter.getSelectedIds();
|
||||
if (ids.isEmpty())
|
||||
return;
|
||||
|
||||
new androidx.appcompat.app.AlertDialog.Builder(requireContext())
|
||||
.setTitle("Delete " + ids.size() + " item(s)?")
|
||||
.setMessage("This cannot be undone.")
|
||||
.setPositiveButton("Delete", (d, w) -> bulkDelete(ids))
|
||||
.setNegativeButton("Cancel", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the bulk deletion of inventory items through the ViewModel.
|
||||
*/
|
||||
private void bulkDelete(List<Long> ids) {
|
||||
viewModel.bulkDeleteInventory(ids).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status != Resource.Status.LOADING) {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
adapter.clearSelection();
|
||||
hideBulkDeleteBar();
|
||||
loadInventory(true);
|
||||
Toast.makeText(getContext(), ids.size() + " item(s) deleted", Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(getContext(), "Delete failed: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the bulk deletion UI bar.
|
||||
*/
|
||||
private void hideBulkDeleteBar() {
|
||||
if (binding != null) {
|
||||
binding.btnBulkDelete.setVisibility(View.GONE);
|
||||
binding.tvSelectionCount.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the inventory detail screen for a specific item or to add a new one.
|
||||
*/
|
||||
@@ -322,12 +285,8 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
*/
|
||||
@Override
|
||||
public void onSelectionChanged(int selectedCount) {
|
||||
if (selectedCount > 0) {
|
||||
binding.btnBulkDelete.setVisibility(View.VISIBLE);
|
||||
binding.tvSelectionCount.setVisibility(View.VISIBLE);
|
||||
binding.tvSelectionCount.setText(selectedCount + " selected");
|
||||
} else {
|
||||
hideBulkDeleteBar();
|
||||
if (bulkDeleteHandler != null) {
|
||||
bulkDeleteHandler.onSelectionChanged(selectedCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,15 @@ import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.adapters.PetAdapter;
|
||||
import com.example.petstoremobile.adapters.WhiteTextArrayAdapter;
|
||||
import com.example.petstoremobile.databinding.FragmentPetBinding;
|
||||
import com.example.petstoremobile.dtos.PetDTO;
|
||||
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.PetViewModel;
|
||||
@@ -46,6 +45,7 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
private PetAdapter adapter;
|
||||
private PetViewModel viewModel;
|
||||
private StoreViewModel storeViewModel;
|
||||
private BulkDeleteHandler bulkDeleteHandler;
|
||||
|
||||
@Inject @Named("baseUrl") String baseUrl;
|
||||
|
||||
@@ -74,6 +74,7 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
setupStoreFilter();
|
||||
setupSwipeRefresh();
|
||||
setupFilterToggle();
|
||||
setupBulkDelete();
|
||||
|
||||
binding.fabAddPet.setOnClickListener(v -> openPetDetails());
|
||||
|
||||
@@ -90,6 +91,19 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
private void setupBulkDelete() {
|
||||
bulkDeleteHandler = new BulkDeleteHandler(
|
||||
this,
|
||||
binding.layoutBulkDelete,
|
||||
binding.tvSelectionCount,
|
||||
binding.btnBulkDelete,
|
||||
adapter,
|
||||
"pet",
|
||||
viewModel::bulkDeletePets,
|
||||
this::loadPetData
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
@@ -145,17 +159,7 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
*/
|
||||
private void setupStatusFilter() {
|
||||
String[] statuses = {"All Statuses", "Available", "Adopted", "Owned"};
|
||||
WhiteTextArrayAdapter<String> adapter = new WhiteTextArrayAdapter<>(requireContext(), android.R.layout.simple_spinner_item, statuses);
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
binding.spinnerStatus.setAdapter(adapter);
|
||||
|
||||
binding.spinnerStatus.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
loadPetData();
|
||||
}
|
||||
@Override public void onNothingSelected(AdapterView<?> parent) {}
|
||||
});
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatus, statuses, this::loadPetData);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,30 +167,14 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
*/
|
||||
private void setupSpeciesFilter() {
|
||||
String[] species = {"All Species", "Dog", "Cat", "Bird", "Rabbit", "Fish", "Hamster"};
|
||||
WhiteTextArrayAdapter<String> adapter = new WhiteTextArrayAdapter<>(requireContext(), android.R.layout.simple_spinner_item, species);
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
binding.spinnerSpecies.setAdapter(adapter);
|
||||
|
||||
binding.spinnerSpecies.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
loadPetData();
|
||||
}
|
||||
@Override public void onNothingSelected(AdapterView<?> parent) {}
|
||||
});
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerSpecies, species, this::loadPetData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the store filter spinner.
|
||||
*/
|
||||
private void setupStoreFilter() {
|
||||
binding.spinnerStore.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
loadPetData();
|
||||
}
|
||||
@Override public void onNothingSelected(AdapterView<?> parent) {}
|
||||
});
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerStore, this::loadPetData);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -231,6 +219,13 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
openPetProfile(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectionChanged(int selectedCount) {
|
||||
if (bulkDeleteHandler != null) {
|
||||
bulkDeleteHandler.onSelectionChanged(selectedCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches pet data from the server with all active filters.
|
||||
*/
|
||||
|
||||
@@ -15,7 +15,6 @@ import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.example.petstoremobile.R;
|
||||
@@ -137,13 +136,7 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
|
||||
* Configures the category filter spinner.
|
||||
*/
|
||||
private void setupCategoryFilter() {
|
||||
binding.spinnerCategory.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
loadProductData();
|
||||
}
|
||||
@Override public void onNothingSelected(AdapterView<?> parent) {}
|
||||
});
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerCategory, this::loadProductData);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,7 +166,7 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
|
||||
Bundle args = new Bundle();
|
||||
if (position != -1) {
|
||||
ProductDTO product = productList.get(position);
|
||||
args.putLong("productId", product.getProdId());
|
||||
args.putLong("prodId", product.getProdId());
|
||||
}
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_product_detail, args);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -24,6 +23,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 +48,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 +75,7 @@ public class ProductSupplierFragment extends Fragment
|
||||
setupSupplierFilter();
|
||||
setupSwipeRefresh();
|
||||
setupFilterToggle();
|
||||
setupBulkDelete();
|
||||
|
||||
binding.fabAddPS.setOnClickListener(v -> openDetail(-1));
|
||||
|
||||
@@ -90,6 +92,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();
|
||||
@@ -152,26 +167,14 @@ public class ProductSupplierFragment extends Fragment
|
||||
* Configures the product filter spinner.
|
||||
*/
|
||||
private void setupProductFilter() {
|
||||
binding.spinnerProduct.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
loadData();
|
||||
}
|
||||
@Override public void onNothingSelected(AdapterView<?> parent) {}
|
||||
});
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerProduct, this::loadData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the supplier filter spinner.
|
||||
*/
|
||||
private void setupSupplierFilter() {
|
||||
binding.spinnerSupplier.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
loadData();
|
||||
}
|
||||
@Override public void onNothingSelected(AdapterView<?> parent) {}
|
||||
});
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerSupplier, this::loadData);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -265,4 +268,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -133,13 +132,7 @@ public class PurchaseOrderFragment extends Fragment
|
||||
* Configures the store filter spinner.
|
||||
*/
|
||||
private void setupStoreFilter() {
|
||||
binding.spinnerStore.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
loadData();
|
||||
}
|
||||
@Override public void onNothingSelected(AdapterView<?> parent) {}
|
||||
});
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerStore, this::loadData);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,9 @@ 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;
|
||||
@@ -13,38 +15,43 @@ import android.view.ViewGroup;
|
||||
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.adapters.SaleAdapter;
|
||||
import com.example.petstoremobile.api.SaleApi;
|
||||
import com.example.petstoremobile.databinding.FragmentSaleBinding;
|
||||
import com.example.petstoremobile.dtos.SaleDTO;
|
||||
import com.example.petstoremobile.fragments.ListFragment;
|
||||
import com.example.petstoremobile.models.Sale;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.viewmodels.SaleViewModel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
@AndroidEntryPoint
|
||||
public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickListener {
|
||||
|
||||
private FragmentSaleBinding binding;
|
||||
private List<Sale> saleList = new ArrayList<>();
|
||||
private List<Sale> filteredList = new ArrayList<>();
|
||||
private List<SaleDTO> saleList = new ArrayList<>();
|
||||
private List<SaleDTO> filteredList = new ArrayList<>();
|
||||
private SaleAdapter adapter;
|
||||
|
||||
@Inject SaleApi api;
|
||||
private SaleViewModel saleViewModel;
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
binding = FragmentSaleBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
saleViewModel = new ViewModelProvider(this).get(SaleViewModel.class);
|
||||
|
||||
setupRecyclerView();
|
||||
loadSaleData();
|
||||
setupSearch();
|
||||
setupSwipeRefresh();
|
||||
loadSales();
|
||||
|
||||
// Make the hamburger button open the drawer from listFragment
|
||||
binding.btnHamburger.setOnClickListener(v -> {
|
||||
Fragment parent = getParentFragment();
|
||||
if (parent != null) {
|
||||
@@ -55,7 +62,11 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
}
|
||||
});
|
||||
|
||||
return binding.getRoot();
|
||||
binding.fabAddSale.setOnClickListener(v ->
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_sale_detail));
|
||||
|
||||
binding.btnOpenRefund.setOnClickListener(v ->
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_refund));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,19 +75,20 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
binding = null;
|
||||
}
|
||||
|
||||
private void setupRecyclerView() {
|
||||
adapter = new SaleAdapter(filteredList, this);
|
||||
binding.recyclerViewSales.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
binding.recyclerViewSales.setAdapter(adapter);
|
||||
}
|
||||
|
||||
private void setupSearch() {
|
||||
binding.etSearchSale.addTextChangedListener(new TextWatcher() {
|
||||
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
@Override public void afterTextChanged(Editable s) {}
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
filterSales(s.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -86,60 +98,46 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
filteredList.addAll(saleList);
|
||||
} else {
|
||||
String lower = query.toLowerCase();
|
||||
for (Sale s : saleList) {
|
||||
if (s.getItemName().toLowerCase().contains(lower)
|
||||
|| s.getEmployeeName().toLowerCase().contains(lower)
|
||||
|| s.getSaleDate().toLowerCase().contains(lower)
|
||||
|| s.getPaymentMethod().toLowerCase().contains(lower)
|
||||
|| String.valueOf(s.getSaleId()).contains(lower)) {
|
||||
for (SaleDTO s : saleList) {
|
||||
if ((s.getEmployeeName() != null && s.getEmployeeName().toLowerCase().contains(lower))
|
||||
|| (s.getSaleDate() != null && s.getSaleDate().toLowerCase().contains(lower))
|
||||
|| (s.getPaymentMethod() != null && s.getPaymentMethod().toLowerCase().contains(lower))
|
||||
|| (s.getSaleId() != null && String.valueOf(s.getSaleId()).contains(lower))) {
|
||||
filteredList.add(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
adapter.notifyDataSetChanged();
|
||||
if (adapter != null) adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshSale.setOnRefreshListener(() -> {
|
||||
loadSaleData();
|
||||
binding.swipeRefreshSale.setRefreshing(false);
|
||||
loadSales();
|
||||
});
|
||||
}
|
||||
|
||||
private void loadSales() {
|
||||
saleViewModel.getAllSales(0, 200).observe(getViewLifecycleOwner(), resource -> {
|
||||
binding.swipeRefreshSale.setRefreshing(false);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
saleList.clear();
|
||||
saleList.addAll(resource.data.getContent());
|
||||
filterSales(binding.etSearchSale.getText() != null
|
||||
? binding.etSearchSale.getText().toString() : "");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// When a sale row is clicked, open the refund screen for that sale
|
||||
@Override
|
||||
public void onSaleClick(int position) {
|
||||
Sale sale = filteredList.get(position);
|
||||
SaleDTO sale = filteredList.get(position);
|
||||
Bundle args = new Bundle();
|
||||
args.putInt("saleId", sale.getSaleId());
|
||||
args.putString("saleDate", sale.getSaleDate());
|
||||
args.putString("employeeName", sale.getEmployeeName());
|
||||
args.putDouble("total", sale.getTotal());
|
||||
args.putString("paymentMethod", sale.getPaymentMethod());
|
||||
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_refund_detail, args);
|
||||
}
|
||||
|
||||
public void reloadSales() {
|
||||
loadSaleData();
|
||||
}
|
||||
|
||||
// TODO: Replace with actual API call - GET v1/sales
|
||||
private void loadSaleData() {
|
||||
saleList.clear();
|
||||
saleList.add(new Sale(1, "2026-03-01", "John Smith", "Premium Dog Food", 2, 45.99, 91.98, "Card", false));
|
||||
saleList.add(new Sale(2, "2026-03-02", "Jane Doe", "Cat Toy Bundle", 1, 19.99, 19.99, "Cash", false));
|
||||
saleList.add(new Sale(3, "2026-03-03", "John Smith", "Pet Shampoo", 3, 12.99, 38.97, "Card", false));
|
||||
saleList.add(new Sale(4, "2026-03-04", "Jane Doe", "Dog Bed - Large", 1, 89.99, 89.99, "Cash", true));
|
||||
filteredList.clear();
|
||||
filteredList.addAll(saleList);
|
||||
if (adapter != null)
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void setupRecyclerView() {
|
||||
adapter = new SaleAdapter(filteredList, this);
|
||||
binding.recyclerViewSales.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
binding.recyclerViewSales.setAdapter(adapter);
|
||||
if (sale.getSaleId() != null) {
|
||||
args.putLong("saleId", sale.getSaleId());
|
||||
}
|
||||
if (sale.getIsRefund() != null) {
|
||||
args.putBoolean("isRefund", sale.getIsRefund());
|
||||
}
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_sale_detail, args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
package com.example.petstoremobile.fragments.listfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.text.*;
|
||||
import android.util.Log;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.adapters.EmployeeAdapter;
|
||||
import com.example.petstoremobile.api.RetrofitClient;
|
||||
import com.example.petstoremobile.dtos.EmployeeDTO;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.fragments.ListFragment;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import java.util.*;
|
||||
import retrofit2.*;
|
||||
|
||||
public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmployeeClickListener {
|
||||
|
||||
private List<EmployeeDTO> employeeList = new ArrayList<>();
|
||||
private List<EmployeeDTO> filteredList = new ArrayList<>();
|
||||
private EmployeeAdapter adapter;
|
||||
private SwipeRefreshLayout swipeRefresh;
|
||||
private EditText etSearch;
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_staff, container, false);
|
||||
|
||||
setupRecyclerView(view);
|
||||
setupSearch(view);
|
||||
setupSwipeRefresh(view);
|
||||
loadStaff();
|
||||
|
||||
FloatingActionButton fab = view.findViewById(R.id.fabAddStaff);
|
||||
fab.setOnClickListener(v -> openDetail(-1));
|
||||
|
||||
ImageButton hamburger = view.findViewById(R.id.btnHamburgerStaff);
|
||||
hamburger.setOnClickListener(v -> openDrawer());
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void openDrawer() {
|
||||
Fragment parent = getParentFragment();
|
||||
if (parent != null) {
|
||||
Fragment grandParent = parent.getParentFragment();
|
||||
if (grandParent instanceof ListFragment) {
|
||||
((ListFragment) grandParent).openDrawer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setupRecyclerView(View view) {
|
||||
RecyclerView rv = view.findViewById(R.id.recyclerViewStaff);
|
||||
adapter = new EmployeeAdapter(filteredList, this);
|
||||
rv.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
rv.setAdapter(adapter);
|
||||
}
|
||||
|
||||
private void setupSearch(View view) {
|
||||
etSearch = view.findViewById(R.id.etSearchStaff);
|
||||
etSearch.addTextChangedListener(new TextWatcher() {
|
||||
public void beforeTextChanged(CharSequence s, int a, int b, int c) {}
|
||||
public void afterTextChanged(Editable s) {}
|
||||
public void onTextChanged(CharSequence s, int a, int b, int c) {
|
||||
filter(s.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupSwipeRefresh(View view) {
|
||||
swipeRefresh = view.findViewById(R.id.swipeRefreshStaff);
|
||||
swipeRefresh.setOnRefreshListener(this::loadStaff);
|
||||
}
|
||||
|
||||
private void filter(String query) {
|
||||
filteredList.clear();
|
||||
if (query.isEmpty()) {
|
||||
filteredList.addAll(employeeList);
|
||||
} else {
|
||||
String lower = query.toLowerCase();
|
||||
for (EmployeeDTO e : employeeList) {
|
||||
if ((e.getFullName() != null && e.getFullName().toLowerCase().contains(lower))
|
||||
|| (e.getUsername() != null && e.getUsername().toLowerCase().contains(lower))
|
||||
|| (e.getEmail() != null && e.getEmail().toLowerCase().contains(lower))
|
||||
|| (e.getPhone() != null && e.getPhone().toLowerCase().contains(lower))) {
|
||||
filteredList.add(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void loadStaff() {
|
||||
if (swipeRefresh != null) swipeRefresh.setRefreshing(true);
|
||||
RetrofitClient.getEmployeeApi(requireContext()).getAllEmployees(0, 100)
|
||||
.enqueue(new Callback<PageResponse<EmployeeDTO>>() {
|
||||
public void onResponse(Call<PageResponse<EmployeeDTO>> c,
|
||||
Response<PageResponse<EmployeeDTO>> r) {
|
||||
if (swipeRefresh != null) swipeRefresh.setRefreshing(false);
|
||||
if (r.isSuccessful() && r.body() != null) {
|
||||
employeeList.clear();
|
||||
employeeList.addAll(r.body().getContent());
|
||||
filter(etSearch != null ? etSearch.getText().toString() : "");
|
||||
} else {
|
||||
Toast.makeText(getContext(), "Failed to load staff",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
public void onFailure(Call<PageResponse<EmployeeDTO>> c, Throwable t) {
|
||||
if (swipeRefresh != null) swipeRefresh.setRefreshing(false);
|
||||
Log.e("StaffFragment", t.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void openDetail(int position) {
|
||||
Bundle args = new Bundle();
|
||||
if (position != -1) {
|
||||
EmployeeDTO e = filteredList.get(position);
|
||||
args.putLong("employeeId", e.getEmployeeId());
|
||||
args.putString("username", e.getUsername() != null ? e.getUsername() : "");
|
||||
args.putString("firstName", e.getFirstName() != null ? e.getFirstName() : "");
|
||||
args.putString("lastName", e.getLastName() != null ? e.getLastName() : "");
|
||||
args.putString("email", e.getEmail() != null ? e.getEmail() : "");
|
||||
args.putString("phone", e.getPhone() != null ? e.getPhone() : "");
|
||||
args.putString("role", e.getRole() != null ? e.getRole() : "STAFF");
|
||||
args.putBoolean("active", Boolean.TRUE.equals(e.getActive()));
|
||||
args.putBoolean("isEditing", true);
|
||||
}
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_staff_detail, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEmployeeClick(int position) {
|
||||
openDetail(position);
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -19,7 +19,9 @@ import com.example.petstoremobile.viewmodels.AdoptionViewModel;
|
||||
import com.example.petstoremobile.viewmodels.CustomerViewModel;
|
||||
import com.example.petstoremobile.viewmodels.PetViewModel;
|
||||
import com.example.petstoremobile.viewmodels.StoreViewModel;
|
||||
import com.example.petstoremobile.viewmodels.UserViewModel;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
@@ -37,10 +39,12 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
private long preselectedPetId = -1;
|
||||
private long preselectedCustomerId = -1;
|
||||
private long preselectedStoreId = -1;
|
||||
private long preselectedEmployeeId = -1;
|
||||
|
||||
private List<PetDTO> petList = new ArrayList<>();
|
||||
private List<CustomerDTO> customerList = new ArrayList<>();
|
||||
private List<StoreDTO> storeList = new ArrayList<>();
|
||||
private List<UserDTO> employeeList = new ArrayList<>();
|
||||
|
||||
private final String[] STATUSES = {"Pending", "Completed", "Cancelled"};
|
||||
|
||||
@@ -48,6 +52,7 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
private PetViewModel petViewModel;
|
||||
private CustomerViewModel customerViewModel;
|
||||
private StoreViewModel storeViewModel;
|
||||
private UserViewModel userViewModel;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
@@ -56,6 +61,7 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
petViewModel = new ViewModelProvider(this).get(PetViewModel.class);
|
||||
customerViewModel = new ViewModelProvider(this).get(CustomerViewModel.class);
|
||||
storeViewModel = new ViewModelProvider(this).get(StoreViewModel.class);
|
||||
userViewModel = new ViewModelProvider(this).get(UserViewModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -113,6 +119,7 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
loadPets();
|
||||
loadCustomers();
|
||||
loadStores();
|
||||
loadEmployees();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,6 +186,27 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
preselectedStoreId, StoreDTO::getStoreId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the list of employees from the API.
|
||||
*/
|
||||
private void loadEmployees() {
|
||||
userViewModel.getUsers("STAFF", 0, 100).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
employeeList = resource.data.getContent();
|
||||
refreshEmployeeSpinner();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the employee selection spinner with data.
|
||||
*/
|
||||
private void refreshEmployeeSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerAdoptionEmployee, employeeList,
|
||||
UserDTO::getFullName, "-- Select Staff --",
|
||||
preselectedEmployeeId, UserDTO::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles arguments to determine if the fragment is in edit or add mode.
|
||||
*/
|
||||
@@ -210,12 +238,16 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
preselectedPetId = a.getPetId() != null ? a.getPetId() : -1;
|
||||
preselectedCustomerId = a.getCustomerId() != null ? a.getCustomerId() : -1;
|
||||
preselectedStoreId = a.getSourceStoreId() != null ? a.getSourceStoreId() : -1;
|
||||
preselectedEmployeeId = a.getEmployeeId() != null ? a.getEmployeeId() : -1;
|
||||
|
||||
binding.etAdoptionDate.setText(a.getAdoptionDate());
|
||||
binding.etAdoptionFee.setText(a.getAdoptionFee() != null ? a.getAdoptionFee().toString() : "");
|
||||
SpinnerUtils.setSelectionByValue(binding.spinnerAdoptionStatus, a.getAdoptionStatus());
|
||||
|
||||
refreshPetSpinner();
|
||||
refreshCustomerSpinner();
|
||||
refreshStoreSpinner();
|
||||
refreshEmployeeSpinner();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Failed to load adoption: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
@@ -240,17 +272,36 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
Toast.makeText(getContext(), "Select a date", Toast.LENGTH_SHORT).show(); return;
|
||||
}
|
||||
|
||||
BigDecimal fee = BigDecimal.ZERO;
|
||||
String feeStr = binding.etAdoptionFee.getText().toString().trim();
|
||||
if (!feeStr.isEmpty()) {
|
||||
try {
|
||||
fee = new BigDecimal(feeStr);
|
||||
} catch (NumberFormatException e) {
|
||||
Toast.makeText(getContext(), "Invalid fee format", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CustomerDTO customer = customerList.get(binding.spinnerAdoptionCustomer.getSelectedItemPosition() - 1);
|
||||
PetDTO pet = petList.get(binding.spinnerAdoptionPet.getSelectedItemPosition() - 1);
|
||||
StoreDTO store = storeList.get(binding.spinnerAdoptionStore.getSelectedItemPosition() - 1);
|
||||
|
||||
Long employeeId = null;
|
||||
if (binding.spinnerAdoptionEmployee.getSelectedItemPosition() > 0) {
|
||||
employeeId = employeeList.get(binding.spinnerAdoptionEmployee.getSelectedItemPosition() - 1).getId();
|
||||
}
|
||||
|
||||
String status = STATUSES[binding.spinnerAdoptionStatus.getSelectedItemPosition()];
|
||||
|
||||
AdoptionDTO dto = new AdoptionDTO(
|
||||
pet.getPetId(),
|
||||
customer.getCustomerId(),
|
||||
employeeId,
|
||||
store.getStoreId(),
|
||||
date,
|
||||
status
|
||||
status,
|
||||
fee
|
||||
);
|
||||
|
||||
if (isEditing) {
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -18,15 +13,16 @@ import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
|
||||
import com.example.petstoremobile.adapters.BlackTextArrayAdapter;
|
||||
import com.example.petstoremobile.databinding.FragmentInventoryDetailBinding;
|
||||
import com.example.petstoremobile.dtos.InventoryDTO;
|
||||
import com.example.petstoremobile.dtos.InventoryRequest;
|
||||
import com.example.petstoremobile.dtos.ProductDTO;
|
||||
import com.example.petstoremobile.dtos.StoreDTO;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.SpinnerUtils;
|
||||
import com.example.petstoremobile.viewmodels.InventoryViewModel;
|
||||
import com.example.petstoremobile.viewmodels.ProductViewModel;
|
||||
import com.example.petstoremobile.viewmodels.StoreViewModel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -43,20 +39,15 @@ public class InventoryDetailFragment extends Fragment {
|
||||
|
||||
private InventoryViewModel inventoryViewModel;
|
||||
private ProductViewModel productViewModel;
|
||||
private StoreViewModel storeViewModel;
|
||||
|
||||
private boolean isEditing = false;
|
||||
private long inventoryId = -1;
|
||||
private long preselectedStoreId = -1;
|
||||
private long preselectedProductId = -1;
|
||||
|
||||
// The product selected from the dropdown
|
||||
private ProductDTO selectedProduct = null;
|
||||
|
||||
// For debouncing product search
|
||||
private final Handler searchHandler = new Handler(Looper.getMainLooper());
|
||||
private Runnable searchRunnable;
|
||||
|
||||
// Dropdown list
|
||||
private final List<ProductDTO> productSuggestions = new ArrayList<>();
|
||||
private ArrayAdapter<String> dropdownAdapter;
|
||||
private List<StoreDTO> storeList = new ArrayList<>();
|
||||
private List<ProductDTO> productList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Initializes the view models.
|
||||
@@ -66,6 +57,7 @@ public class InventoryDetailFragment extends Fragment {
|
||||
super.onCreate(savedInstanceState);
|
||||
inventoryViewModel = new ViewModelProvider(this).get(InventoryViewModel.class);
|
||||
productViewModel = new ViewModelProvider(this).get(ProductViewModel.class);
|
||||
storeViewModel = new ViewModelProvider(this).get(StoreViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,94 +77,64 @@ public class InventoryDetailFragment extends Fragment {
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
setupProductSearch();
|
||||
loadSpinnersData();
|
||||
handleArguments();
|
||||
|
||||
binding.btnInventoryBack.setOnClickListener(v -> navigateBack());
|
||||
binding.btnSaveInventory.setOnClickListener(v -> saveInventory());
|
||||
binding.btnDeleteInventory.setOnClickListener(v -> confirmDelete());
|
||||
|
||||
// Setup dropdown adapter
|
||||
dropdownAdapter = new BlackTextArrayAdapter<>(requireContext(),
|
||||
android.R.layout.simple_dropdown_item_1line, new ArrayList<>());
|
||||
binding.etProductSearch.setAdapter(dropdownAdapter);
|
||||
binding.etProductSearch.setThreshold(1); // start showing after 1 character
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
if (searchRunnable != null) {
|
||||
searchHandler.removeCallbacks(searchRunnable);
|
||||
}
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the product search dropdown.
|
||||
* Fetches required data for spinners from the backend.
|
||||
*/
|
||||
private void setupProductSearch() {
|
||||
binding.etProductSearch.addTextChangedListener(new TextWatcher() {
|
||||
@Override public void beforeTextChanged(CharSequence s, int i, int i1, int i2) {
|
||||
}
|
||||
private void loadSpinnersData() {
|
||||
loadStores();
|
||||
loadProducts();
|
||||
}
|
||||
|
||||
@Override public void afterTextChanged(Editable s) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
// Clear selected product when user is typing again
|
||||
selectedProduct = null;
|
||||
binding.tvProductInfo.setVisibility(View.GONE);
|
||||
|
||||
if (searchRunnable != null)
|
||||
searchHandler.removeCallbacks(searchRunnable);
|
||||
String query = s.toString().trim();
|
||||
if (query.isEmpty())
|
||||
return;
|
||||
|
||||
searchRunnable = () -> searchProducts(query);
|
||||
searchHandler.postDelayed(searchRunnable, 400);
|
||||
}
|
||||
});
|
||||
|
||||
// When user picks an item from the dropdown
|
||||
binding.etProductSearch.setOnItemClickListener((parent, view, position, id) -> {
|
||||
if (position < productSuggestions.size()) {
|
||||
selectedProduct = productSuggestions.get(position);
|
||||
// Show product details below the search box
|
||||
binding.tvProductInfo.setText(
|
||||
"ID: " + selectedProduct.getProdId()
|
||||
+ " • " + selectedProduct.getCategoryName());
|
||||
binding.tvProductInfo.setVisibility(View.VISIBLE);
|
||||
/**
|
||||
* Loads the list of stores for the spinner.
|
||||
*/
|
||||
private void loadStores() {
|
||||
storeViewModel.getAllStores(0, 100).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
storeList = resource.data.getContent();
|
||||
refreshStoreSpinner();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void refreshStoreSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerInventoryStore, storeList,
|
||||
StoreDTO::getStoreName, "-- Select Store --",
|
||||
preselectedStoreId, StoreDTO::getStoreId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for products matching the query from the backend.
|
||||
* Loads the list of products for the spinner.
|
||||
*/
|
||||
private void searchProducts(String query) {
|
||||
if (getView() == null) return;
|
||||
productViewModel.getAllProducts(query, null, 0, 20, "prodName").observe(getViewLifecycleOwner(), resource -> {
|
||||
private void loadProducts() {
|
||||
productViewModel.getAllProducts(null, null, 0, 500, "prodName").observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
productSuggestions.clear();
|
||||
productSuggestions.addAll(resource.data.getContent());
|
||||
|
||||
// Build display strings: "Product Name (ID: X)"
|
||||
List<String> names = new ArrayList<>();
|
||||
for (ProductDTO p : productSuggestions) {
|
||||
names.add(p.getProdName() + " (ID: " + p.getProdId() + ")");
|
||||
}
|
||||
|
||||
dropdownAdapter.clear();
|
||||
dropdownAdapter.addAll(names);
|
||||
dropdownAdapter.notifyDataSetChanged();
|
||||
binding.etProductSearch.showDropDown();
|
||||
productList = resource.data.getContent();
|
||||
refreshProductSpinner();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void refreshProductSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerInventoryProduct, productList,
|
||||
ProductDTO::getProdName, "-- Select Product --",
|
||||
preselectedProductId, ProductDTO::getProdId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles fragment arguments to determine if we are in edit or add mode.
|
||||
*/
|
||||
@@ -193,7 +155,6 @@ public class InventoryDetailFragment extends Fragment {
|
||||
isEditing = false;
|
||||
binding.tvInventoryMode.setText("Add Inventory");
|
||||
binding.tvInventoryId.setVisibility(View.GONE);
|
||||
binding.tvProductInfo.setVisibility(View.GONE);
|
||||
binding.btnDeleteInventory.setVisibility(View.GONE);
|
||||
binding.btnSaveInventory.setText("Add");
|
||||
}
|
||||
@@ -207,20 +168,12 @@ public class InventoryDetailFragment extends Fragment {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
InventoryDTO inv = resource.data;
|
||||
binding.etProductSearch.setText(inv.getProductName());
|
||||
binding.etQuantity.setText(String.valueOf(inv.getQuantity()));
|
||||
|
||||
if (inv.getProdId() != null) {
|
||||
binding.tvProductInfo.setText(
|
||||
"ID: " + inv.getProdId()
|
||||
+ " • " + inv.getCategoryName());
|
||||
binding.tvProductInfo.setVisibility(View.VISIBLE);
|
||||
|
||||
selectedProduct = new ProductDTO();
|
||||
selectedProduct.setProdId(inv.getProdId());
|
||||
selectedProduct.setProdName(inv.getProductName());
|
||||
selectedProduct.setCategoryName(inv.getCategoryName());
|
||||
}
|
||||
preselectedStoreId = inv.getStoreId() != null ? inv.getStoreId() : -1;
|
||||
preselectedProductId = inv.getProdId() != null ? inv.getProdId() : -1;
|
||||
|
||||
refreshStoreSpinner();
|
||||
refreshProductSpinner();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Failed to load inventory: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
@@ -231,9 +184,12 @@ public class InventoryDetailFragment extends Fragment {
|
||||
* Validates input and saves the current inventory item details to the backend.
|
||||
*/
|
||||
private void saveInventory() {
|
||||
if (selectedProduct == null) {
|
||||
binding.etProductSearch.setError("Please select a product from the list");
|
||||
binding.etProductSearch.requestFocus();
|
||||
if (binding.spinnerInventoryStore.getSelectedItemPosition() == 0) {
|
||||
Toast.makeText(getContext(), "Please select a store", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
if (binding.spinnerInventoryProduct.getSelectedItemPosition() == 0) {
|
||||
Toast.makeText(getContext(), "Please select a product", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -243,8 +199,10 @@ public class InventoryDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
int quantity = Integer.parseInt(binding.etQuantity.getText().toString().trim());
|
||||
StoreDTO store = storeList.get(binding.spinnerInventoryStore.getSelectedItemPosition() - 1);
|
||||
ProductDTO product = productList.get(binding.spinnerInventoryProduct.getSelectedItemPosition() - 1);
|
||||
|
||||
InventoryRequest request = new InventoryRequest(selectedProduct.getProdId(), quantity);
|
||||
InventoryDTO request = new InventoryDTO(product.getProdId(), store.getStoreId(), quantity);
|
||||
setButtonsEnabled(false);
|
||||
|
||||
if (isEditing) {
|
||||
|
||||
@@ -73,19 +73,27 @@ public class PurchaseOrderDetailFragment extends Fragment {
|
||||
PurchaseOrderDTO po = resource.data;
|
||||
binding.tvPODetailId.setText("PO #" + po.getPurchaseOrderId());
|
||||
binding.tvPODetailSupplier.setText(po.getSupplierName());
|
||||
binding.tvPODetailStore.setText(po.getStoreName() != null ? po.getStoreName() : "N/A");
|
||||
binding.tvPODetailDate.setText(po.getOrderDate());
|
||||
|
||||
String status = po.getStatus() != null ? po.getStatus() : "";
|
||||
binding.tvPODetailStatus.setText(status);
|
||||
switch (status) {
|
||||
case "Completed":
|
||||
binding.tvPODetailStatus.setTextColor(Color.parseColor("#4CAF50")); break;
|
||||
case "Pending":
|
||||
binding.tvPODetailStatus.setTextColor(Color.parseColor("#FF9800")); break;
|
||||
case "Cancelled":
|
||||
binding.tvPODetailStatus.setTextColor(Color.parseColor("#F44336")); break;
|
||||
switch (status.toUpperCase()) {
|
||||
case "RECEIVED":
|
||||
binding.tvPODetailStatus.setTextColor(Color.parseColor("#4CAF50"));
|
||||
break;
|
||||
case "PLACED":
|
||||
binding.tvPODetailStatus.setTextColor(Color.parseColor("#2196F3"));
|
||||
break;
|
||||
case "PENDING":
|
||||
binding.tvPODetailStatus.setTextColor(Color.parseColor("#FF9800"));
|
||||
break;
|
||||
case "CANCELLED":
|
||||
binding.tvPODetailStatus.setTextColor(Color.parseColor("#F44336"));
|
||||
break;
|
||||
default:
|
||||
binding.tvPODetailStatus.setTextColor(Color.parseColor("#9E9E9E")); break;
|
||||
binding.tvPODetailStatus.setTextColor(Color.parseColor("#9E9E9E"));
|
||||
break;
|
||||
}
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Failed to load order: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.example.petstoremobile.api.SaleApi;
|
||||
import com.example.petstoremobile.databinding.FragmentRefundDetailBinding;
|
||||
import com.example.petstoremobile.fragments.listfragments.SaleFragment;
|
||||
import com.example.petstoremobile.utils.ActivityLogger;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.example.petstoremobile.utils.SpinnerUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
@AndroidEntryPoint
|
||||
public class RefundDetailFragment extends Fragment {
|
||||
|
||||
private FragmentRefundDetailBinding binding;
|
||||
private int saleId;
|
||||
private SaleFragment saleFragment;
|
||||
|
||||
@Inject SaleApi saleApi;
|
||||
|
||||
public void setSaleFragment(SaleFragment fragment) {
|
||||
this.saleFragment = fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
binding = FragmentRefundDetailBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
setupSpinner();
|
||||
handleArguments();
|
||||
|
||||
binding.btnRefundBack.setOnClickListener(v -> goBack());
|
||||
binding.btnLoadSale.setOnClickListener(v -> loadSaleDetails());
|
||||
binding.btnProcessRefund.setOnClickListener(v -> processRefund());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
private void loadSaleDetails() {
|
||||
String idText = binding.etRefundSaleId.getText().toString().trim();
|
||||
if (idText.isEmpty()) {
|
||||
Toast.makeText(getContext(), "Enter a Sale ID", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int id = Integer.parseInt(idText);
|
||||
// TODO: Replace with actual API call - GET v1/sales/{id}
|
||||
// For now show placeholder info
|
||||
binding.tvSaleInfo.setText("Sale ID: " + id + " loaded. Enter reason and payment method to process refund.");
|
||||
binding.tvSaleInfo.setTextColor(getResources().getColor(android.R.color.holo_green_dark));
|
||||
} catch (NumberFormatException e) {
|
||||
Toast.makeText(getContext(), "Invalid Sale ID", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void processRefund() {
|
||||
if (!InputValidator.isNotEmpty(binding.etRefundSaleId, "Sale ID"))
|
||||
return;
|
||||
if (!InputValidator.isNotEmpty(binding.etRefundReason, "Refund Reason"))
|
||||
return;
|
||||
|
||||
String idText = binding.etRefundSaleId.getText().toString().trim();
|
||||
String reason = binding.etRefundReason.getText().toString().trim();
|
||||
|
||||
try {
|
||||
int id = Integer.parseInt(idText);
|
||||
// TODO: Replace with actual API call - POST v1/refunds
|
||||
ActivityLogger.log(requireContext(), "Processed refund for Sale ID: " + id + " - Reason: " + reason);
|
||||
Toast.makeText(getContext(), "Refund processed for Sale ID: " + id, Toast.LENGTH_SHORT).show();
|
||||
if (saleFragment != null)
|
||||
saleFragment.reloadSales();
|
||||
goBack();
|
||||
} catch (NumberFormatException e) {
|
||||
Toast.makeText(getContext(), "Invalid Sale ID", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleArguments() {
|
||||
if (getArguments() != null && getArguments().containsKey("saleId")) {
|
||||
saleId = getArguments().getInt("saleId");
|
||||
binding.etRefundSaleId.setText(String.valueOf(saleId));
|
||||
String info = "Sale Date: " + getArguments().getString("saleDate")
|
||||
+ " | Employee: " + getArguments().getString("employeeName")
|
||||
+ " | Total: $" + String.format("%.2f", getArguments().getDouble("total"))
|
||||
+ " | Payment: " + getArguments().getString("paymentMethod");
|
||||
binding.tvSaleInfo.setText(info);
|
||||
binding.tvSaleInfo.setTextColor(getResources().getColor(android.R.color.holo_green_dark));
|
||||
|
||||
// Pre-select payment method
|
||||
SpinnerUtils.setSelectionByValue(binding.spinnerRefundPayment, getArguments().getString("paymentMethod"));
|
||||
}
|
||||
}
|
||||
|
||||
private void goBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
private void setupSpinner() {
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerRefundPayment,
|
||||
new String[] { "Cash", "Card", "Debit" });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,510 @@
|
||||
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.api.RetrofitClient;
|
||||
import com.example.petstoremobile.dtos.SaleDTO;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.*;
|
||||
import retrofit2.*;
|
||||
|
||||
public class RefundFragment extends Fragment {
|
||||
|
||||
private EditText etSaleId;
|
||||
private Button btnLoadSale, btnProcessRefund, btnBack;
|
||||
private TextView tvSaleInfo, tvRefundTotal;
|
||||
private LinearLayout llOriginalItems, llRefundItems;
|
||||
private LinearLayout cardOriginalItems, cardRefundItems, cardPayment;
|
||||
private Spinner spinnerPayment;
|
||||
|
||||
private SaleDTO currentSale;
|
||||
private List<SaleDTO> allSales = new ArrayList<>();
|
||||
|
||||
// Items available to refund (after accounting for previous refunds)
|
||||
private List<RefundItem> availableItems = new ArrayList<>();
|
||||
// Items user has added to refund cart
|
||||
private List<RefundItem> refundCart = new ArrayList<>();
|
||||
|
||||
private final String[] PAYMENT_METHODS = {"Cash", "Card", "Debit"};
|
||||
|
||||
// Inner class to track refund items
|
||||
static class RefundItem {
|
||||
long prodId;
|
||||
String productName;
|
||||
int quantity;
|
||||
BigDecimal unitPrice;
|
||||
|
||||
RefundItem(long prodId, String productName, int quantity, BigDecimal unitPrice) {
|
||||
this.prodId = prodId;
|
||||
this.productName = productName;
|
||||
this.quantity = quantity;
|
||||
this.unitPrice = unitPrice;
|
||||
}
|
||||
|
||||
BigDecimal getTotal() {
|
||||
return unitPrice != null
|
||||
? unitPrice.multiply(BigDecimal.valueOf(quantity))
|
||||
: BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_refund, container, false);
|
||||
initViews(view);
|
||||
setupSpinner();
|
||||
loadAllSales();
|
||||
|
||||
// Pre-fill sale ID if passed from SaleFragment
|
||||
Bundle args = getArguments();
|
||||
if (args != null && args.containsKey("saleId")) {
|
||||
long saleId = args.getLong("saleId");
|
||||
etSaleId.setText(String.valueOf(saleId));
|
||||
// Auto-load after sales are fetched
|
||||
}
|
||||
|
||||
btnLoadSale.setOnClickListener(v -> loadSale());
|
||||
btnProcessRefund.setOnClickListener(v -> processRefund());
|
||||
btnBack.setOnClickListener(v -> navigateBack());
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void initViews(View v) {
|
||||
etSaleId = v.findViewById(R.id.etRefundSaleId);
|
||||
btnLoadSale = v.findViewById(R.id.btnLoadSale);
|
||||
btnProcessRefund= v.findViewById(R.id.btnProcessRefund);
|
||||
btnBack = v.findViewById(R.id.btnRefundBack);
|
||||
tvSaleInfo = v.findViewById(R.id.tvSaleInfo);
|
||||
tvRefundTotal = v.findViewById(R.id.tvRefundTotal);
|
||||
llOriginalItems = v.findViewById(R.id.llOriginalItems);
|
||||
llRefundItems = v.findViewById(R.id.llRefundItems);
|
||||
cardOriginalItems = v.findViewById(R.id.cardOriginalItems);
|
||||
cardRefundItems = v.findViewById(R.id.cardRefundItems);
|
||||
cardPayment = v.findViewById(R.id.cardPayment);
|
||||
spinnerPayment = v.findViewById(R.id.spinnerRefundPayment);
|
||||
}
|
||||
|
||||
private void setupSpinner() {
|
||||
spinnerPayment.setAdapter(new ArrayAdapter<>(requireContext(),
|
||||
android.R.layout.simple_spinner_item, PAYMENT_METHODS));
|
||||
}
|
||||
|
||||
private void loadAllSales() {
|
||||
RetrofitClient.getSaleApi(requireContext()).getAllSales(0, 1000)
|
||||
.enqueue(new Callback<PageResponse<SaleDTO>>() {
|
||||
public void onResponse(Call<PageResponse<SaleDTO>> c,
|
||||
Response<PageResponse<SaleDTO>> r) {
|
||||
if (r.isSuccessful() && r.body() != null) {
|
||||
allSales = r.body().getContent();
|
||||
// Auto-load if saleId was pre-filled
|
||||
Bundle args = getArguments();
|
||||
if (args != null && args.containsKey("saleId")) {
|
||||
loadSale();
|
||||
}
|
||||
}
|
||||
}
|
||||
public void onFailure(Call<PageResponse<SaleDTO>> c, Throwable t) {
|
||||
Log.e("Refund", "Failed to load sales: " + t.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void loadSale() {
|
||||
String idStr = etSaleId.getText().toString().trim();
|
||||
if (idStr.isEmpty()) {
|
||||
Toast.makeText(getContext(), "Enter a Sale ID", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
long saleId;
|
||||
try { saleId = Long.parseLong(idStr); }
|
||||
catch (Exception e) {
|
||||
Toast.makeText(getContext(), "Invalid Sale ID", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
// Find sale in loaded list
|
||||
SaleDTO found = null;
|
||||
for (SaleDTO s : allSales) {
|
||||
if (s.getSaleId() != null && s.getSaleId() == saleId) {
|
||||
found = s; break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found == null) {
|
||||
Toast.makeText(getContext(), "Sale #" + saleId + " not found", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Boolean.TRUE.equals(found.getIsRefund())) {
|
||||
Toast.makeText(getContext(), "Select an original sale, not a refund record",
|
||||
Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
currentSale = found;
|
||||
|
||||
// Show sale info
|
||||
tvSaleInfo.setVisibility(View.VISIBLE);
|
||||
tvSaleInfo.setText("Sale #" + currentSale.getSaleId()
|
||||
+ " | " + (currentSale.getSaleDate() != null
|
||||
? currentSale.getSaleDate().substring(0, 10) : "")
|
||||
+ " | Employee: " + (currentSale.getEmployeeName() != null
|
||||
? currentSale.getEmployeeName() : "")
|
||||
+ " | Total: $" + currentSale.getTotalAmount()
|
||||
+ " | Payment: " + currentSale.getPaymentMethod());
|
||||
|
||||
// Pre-select payment method
|
||||
if (currentSale.getPaymentMethod() != null) {
|
||||
for (int i = 0; i < PAYMENT_METHODS.length; i++) {
|
||||
if (PAYMENT_METHODS[i].equalsIgnoreCase(currentSale.getPaymentMethod())) {
|
||||
spinnerPayment.setSelection(i); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build refundable items accounting for previous refunds
|
||||
buildRefundableItems();
|
||||
|
||||
if (availableItems.isEmpty()) {
|
||||
Toast.makeText(getContext(),
|
||||
"This sale has no remaining refundable items", Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset refund cart
|
||||
refundCart.clear();
|
||||
|
||||
// Show cards
|
||||
cardOriginalItems.setVisibility(View.VISIBLE);
|
||||
cardRefundItems.setVisibility(View.VISIBLE);
|
||||
cardPayment.setVisibility(View.VISIBLE);
|
||||
btnProcessRefund.setVisibility(View.VISIBLE);
|
||||
|
||||
renderOriginalItems();
|
||||
renderRefundCart();
|
||||
updateRefundTotal();
|
||||
}
|
||||
|
||||
private void buildRefundableItems() {
|
||||
availableItems.clear();
|
||||
if (currentSale.getItems() == null) return;
|
||||
|
||||
// Find all previous refunds for this sale
|
||||
Map<Long, Integer> alreadyRefunded = new HashMap<>();
|
||||
for (SaleDTO s : allSales) {
|
||||
if (Boolean.TRUE.equals(s.getIsRefund())
|
||||
&& currentSale.getSaleId().equals(s.getOriginalSaleId())
|
||||
&& s.getItems() != null) {
|
||||
for (SaleDTO.SaleItemDTO item : s.getItems()) {
|
||||
if (item.getProdId() != null && item.getQuantity() != null) {
|
||||
alreadyRefunded.merge(item.getProdId(),
|
||||
Math.abs(item.getQuantity()), Integer::sum);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build available items
|
||||
for (SaleDTO.SaleItemDTO item : currentSale.getItems()) {
|
||||
if (item.getProdId() == null || item.getQuantity() == null) continue;
|
||||
int refunded = alreadyRefunded.getOrDefault(item.getProdId(), 0);
|
||||
int remaining = item.getQuantity() - refunded;
|
||||
if (remaining > 0) {
|
||||
availableItems.add(new RefundItem(
|
||||
item.getProdId(),
|
||||
item.getProductName() != null ? item.getProductName() : "Unknown",
|
||||
remaining,
|
||||
item.getUnitPrice()
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void renderOriginalItems() {
|
||||
llOriginalItems.removeAllViews();
|
||||
|
||||
// Header
|
||||
addTableHeader(llOriginalItems);
|
||||
|
||||
for (RefundItem item : availableItems) {
|
||||
// Calculate pending in cart
|
||||
int pendingQty = 0;
|
||||
for (RefundItem r : refundCart) {
|
||||
if (r.prodId == item.prodId) { pendingQty = r.quantity; break; }
|
||||
}
|
||||
int displayQty = item.quantity - pendingQty;
|
||||
if (displayQty <= 0) continue;
|
||||
|
||||
LinearLayout row = buildItemRow(
|
||||
item.productName,
|
||||
displayQty,
|
||||
item.unitPrice,
|
||||
true, // show add button
|
||||
() -> showQuantityDialog(item)
|
||||
);
|
||||
llOriginalItems.addView(row);
|
||||
}
|
||||
}
|
||||
|
||||
private void renderRefundCart() {
|
||||
llRefundItems.removeAllViews();
|
||||
|
||||
if (refundCart.isEmpty()) {
|
||||
TextView empty = new TextView(getContext());
|
||||
empty.setText("No items added to refund yet");
|
||||
empty.setTextColor(0xFF888780);
|
||||
empty.setTextSize(13f);
|
||||
llRefundItems.addView(empty);
|
||||
return;
|
||||
}
|
||||
|
||||
addTableHeader(llRefundItems);
|
||||
|
||||
for (RefundItem item : refundCart) {
|
||||
LinearLayout row = buildItemRow(
|
||||
item.productName,
|
||||
item.quantity,
|
||||
item.unitPrice,
|
||||
false, // show remove button
|
||||
() -> {
|
||||
refundCart.remove(item);
|
||||
renderOriginalItems();
|
||||
renderRefundCart();
|
||||
updateRefundTotal();
|
||||
}
|
||||
);
|
||||
llRefundItems.addView(row);
|
||||
}
|
||||
}
|
||||
|
||||
private void addTableHeader(LinearLayout parent) {
|
||||
LinearLayout header = new LinearLayout(getContext());
|
||||
header.setOrientation(LinearLayout.HORIZONTAL);
|
||||
header.setPadding(0, 0, 0, 8);
|
||||
|
||||
String[] cols = {"Product", "Qty", "Unit Price", ""};
|
||||
float[] weights = {2f, 1f, 1f, 0.8f};
|
||||
for (int i = 0; i < cols.length; i++) {
|
||||
TextView tv = new TextView(getContext());
|
||||
tv.setLayoutParams(new LinearLayout.LayoutParams(0,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT, weights[i]));
|
||||
tv.setText(cols[i]);
|
||||
tv.setTextColor(0xFF888780);
|
||||
tv.setTextSize(11f);
|
||||
header.addView(tv);
|
||||
}
|
||||
parent.addView(header);
|
||||
}
|
||||
|
||||
private LinearLayout buildItemRow(String name, int qty, BigDecimal unitPrice,
|
||||
boolean isAdd, Runnable action) {
|
||||
LinearLayout row = new LinearLayout(getContext());
|
||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
row.setGravity(android.view.Gravity.CENTER_VERTICAL);
|
||||
row.setPadding(0, 8, 0, 8);
|
||||
|
||||
TextView tvName = new TextView(getContext());
|
||||
tvName.setLayoutParams(new LinearLayout.LayoutParams(0,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT, 2f));
|
||||
tvName.setText(name);
|
||||
tvName.setTextSize(13f);
|
||||
tvName.setTextColor(0xFF3d3d3a);
|
||||
|
||||
TextView tvQty = new TextView(getContext());
|
||||
tvQty.setLayoutParams(new LinearLayout.LayoutParams(0,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT, 1f));
|
||||
tvQty.setText(String.valueOf(qty));
|
||||
tvQty.setTextSize(13f);
|
||||
tvQty.setTextColor(0xFF3d3d3a);
|
||||
|
||||
TextView tvPrice = new TextView(getContext());
|
||||
tvPrice.setLayoutParams(new LinearLayout.LayoutParams(0,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT, 1f));
|
||||
tvPrice.setText(unitPrice != null ? "$" + unitPrice : "");
|
||||
tvPrice.setTextSize(13f);
|
||||
tvPrice.setTextColor(0xFF3d3d3a);
|
||||
|
||||
Button btn = new Button(getContext());
|
||||
LinearLayout.LayoutParams btnParams = new LinearLayout.LayoutParams(0,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT, 0.8f);
|
||||
btn.setLayoutParams(btnParams);
|
||||
btn.setText(isAdd ? "Add" : "Remove");
|
||||
btn.setTextSize(11f);
|
||||
btn.setBackgroundColor(isAdd ? 0xFF1a759f : 0xFFE24B4A);
|
||||
btn.setTextColor(0xFFFFFFFF);
|
||||
btn.setPadding(4, 4, 4, 4);
|
||||
btn.setOnClickListener(v -> action.run());
|
||||
|
||||
row.addView(tvName);
|
||||
row.addView(tvQty);
|
||||
row.addView(tvPrice);
|
||||
row.addView(btn);
|
||||
return row;
|
||||
}
|
||||
|
||||
private void showQuantityDialog(RefundItem item) {
|
||||
// Calculate how many are already in cart
|
||||
int inCart = 0;
|
||||
for (RefundItem r : refundCart) {
|
||||
if (r.prodId == item.prodId) { inCart = r.quantity; break; }
|
||||
}
|
||||
int available = item.quantity - inCart;
|
||||
if (available <= 0) {
|
||||
Toast.makeText(getContext(), "All units already added to refund",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
// Build dialog
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(requireContext());
|
||||
builder.setTitle("Refund Quantity");
|
||||
builder.setMessage("Product: " + item.productName
|
||||
+ "\nAvailable: " + available);
|
||||
|
||||
EditText input = new EditText(getContext());
|
||||
input.setInputType(android.text.InputType.TYPE_CLASS_NUMBER);
|
||||
input.setText(String.valueOf(available));
|
||||
input.setSelectAllOnFocus(true);
|
||||
builder.setView(input);
|
||||
|
||||
builder.setPositiveButton("Add to Refund", (d, w) -> {
|
||||
String val = input.getText().toString().trim();
|
||||
if (val.isEmpty()) return;
|
||||
int qty;
|
||||
try { qty = Integer.parseInt(val); }
|
||||
catch (Exception e) {
|
||||
Toast.makeText(getContext(), "Invalid quantity", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
if (qty <= 0) {
|
||||
Toast.makeText(getContext(), "Quantity must be at least 1",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
if (qty > available) {
|
||||
Toast.makeText(getContext(), "Cannot exceed " + available,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
// Add or merge into cart
|
||||
boolean merged = false;
|
||||
for (int i = 0; i < refundCart.size(); i++) {
|
||||
if (refundCart.get(i).prodId == item.prodId) {
|
||||
RefundItem existing = refundCart.get(i);
|
||||
refundCart.set(i, new RefundItem(existing.prodId,
|
||||
existing.productName,
|
||||
existing.quantity + qty,
|
||||
existing.unitPrice));
|
||||
merged = true; break;
|
||||
}
|
||||
}
|
||||
if (!merged) {
|
||||
refundCart.add(new RefundItem(item.prodId, item.productName,
|
||||
qty, item.unitPrice));
|
||||
}
|
||||
|
||||
renderOriginalItems();
|
||||
renderRefundCart();
|
||||
updateRefundTotal();
|
||||
});
|
||||
|
||||
builder.setNegativeButton("Cancel", null);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void updateRefundTotal() {
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
for (RefundItem item : refundCart) total = total.add(item.getTotal());
|
||||
tvRefundTotal.setText("Refund Total: $" + total.setScale(2, RoundingMode.HALF_UP));
|
||||
}
|
||||
|
||||
private void processRefund() {
|
||||
if (currentSale == null) {
|
||||
Toast.makeText(getContext(), "Load a sale first", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
if (refundCart.isEmpty()) {
|
||||
Toast.makeText(getContext(), "Add at least one item to refund",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
String payment = PAYMENT_METHODS[spinnerPayment.getSelectedItemPosition()];
|
||||
|
||||
// Confirm dialog
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
for (RefundItem item : refundCart) total = total.add(item.getTotal());
|
||||
final BigDecimal finalTotal = total;
|
||||
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle("Confirm Refund")
|
||||
.setMessage("Process refund for Sale #" + currentSale.getSaleId()
|
||||
+ "?\nRefund amount: $" + finalTotal.setScale(2, RoundingMode.HALF_UP))
|
||||
.setPositiveButton("Yes", (d, w) -> submitRefund(payment))
|
||||
.setNegativeButton("No", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void submitRefund(String payment) {
|
||||
// Build sale items list
|
||||
List<SaleDTO.SaleItemDTO> items = new ArrayList<>();
|
||||
for (RefundItem item : refundCart) {
|
||||
// Backend expects negative quantity for refunds
|
||||
items.add(new SaleDTO.SaleItemDTO(item.prodId, -item.quantity));
|
||||
}
|
||||
|
||||
SaleDTO dto = new SaleDTO(
|
||||
currentSale.getStoreId(),
|
||||
payment,
|
||||
items,
|
||||
true, // isRefund = true
|
||||
currentSale.getSaleId(), // originalSaleId
|
||||
null // no customer needed
|
||||
);
|
||||
|
||||
Log.d("REFUND", "Submitting refund for saleId=" + currentSale.getSaleId()
|
||||
+ " items=" + items.size());
|
||||
|
||||
RetrofitClient.getSaleApi(requireContext()).createSale(dto)
|
||||
.enqueue(new Callback<SaleDTO>() {
|
||||
public void onResponse(Call<SaleDTO> c, Response<SaleDTO> r) {
|
||||
if (r.isSuccessful() && r.body() != null) {
|
||||
Toast.makeText(getContext(),
|
||||
"Refund #" + r.body().getSaleId() + " processed successfully!",
|
||||
Toast.LENGTH_LONG).show();
|
||||
navigateBack();
|
||||
} else {
|
||||
try {
|
||||
String err = r.errorBody().string();
|
||||
Log.e("REFUND", "Error: " + err);
|
||||
Toast.makeText(getContext(), "Error: " + err,
|
||||
Toast.LENGTH_LONG).show();
|
||||
} catch (Exception e) {
|
||||
Log.e("REFUND", "Failed to read error");
|
||||
}
|
||||
}
|
||||
}
|
||||
public void onFailure(Call<SaleDTO> c, Throwable t) {
|
||||
Log.e("REFUND", "Failure: " + t.getMessage());
|
||||
Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.api.*;
|
||||
import com.example.petstoremobile.dtos.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import retrofit2.*;
|
||||
|
||||
public class SaleDetailFragment extends Fragment {
|
||||
|
||||
private TextView tvMode, tvSaleDetailId, tvTotal;
|
||||
private Spinner spinnerStore, spinnerCustomer, spinnerPayment, spinnerProduct;
|
||||
private EditText etQuantity;
|
||||
private Button btnAddItem, btnSave, btnBack, btnRefund;
|
||||
private LinearLayout llItems;
|
||||
|
||||
private boolean viewOnly = false;
|
||||
private long saleId = -1;
|
||||
|
||||
private List<StoreDTO> storeList = new ArrayList<>();
|
||||
private List<CustomerDTO> customerList = new ArrayList<>();
|
||||
private List<ProductDTO> productList = new ArrayList<>();
|
||||
private List<SaleDTO.SaleItemDTO> cartItems = new ArrayList<>();
|
||||
|
||||
private final String[] PAYMENT_METHODS = { "Cash", "Credit Card", "Debit Card", "Online" };
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_sale_detail, container, false);
|
||||
initViews(view);
|
||||
handleArguments();
|
||||
|
||||
if (!viewOnly) {
|
||||
loadData();
|
||||
setupAddItem();
|
||||
}
|
||||
|
||||
btnBack.setOnClickListener(v -> navigateBack());
|
||||
btnSave.setOnClickListener(v -> saveSale());
|
||||
btnRefund.setOnClickListener(v -> showRefundDialog());
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void initViews(View v) {
|
||||
tvMode = v.findViewById(R.id.tvSaleMode);
|
||||
tvSaleDetailId = v.findViewById(R.id.tvSaleDetailId);
|
||||
tvTotal = v.findViewById(R.id.tvSaleDetailTotal);
|
||||
spinnerStore = v.findViewById(R.id.spinnerSaleStore);
|
||||
spinnerCustomer = v.findViewById(R.id.spinnerSaleCustomer);
|
||||
spinnerPayment = v.findViewById(R.id.spinnerPaymentMethod);
|
||||
spinnerProduct = v.findViewById(R.id.spinnerSaleProduct);
|
||||
etQuantity = v.findViewById(R.id.etSaleQuantity);
|
||||
btnAddItem = v.findViewById(R.id.btnAddItem);
|
||||
btnSave = v.findViewById(R.id.btnSaveSale);
|
||||
btnBack = v.findViewById(R.id.btnSaleBack);
|
||||
btnRefund = v.findViewById(R.id.btnRefundSale);
|
||||
llItems = v.findViewById(R.id.llSaleItems);
|
||||
|
||||
spinnerPayment.setAdapter(new ArrayAdapter<>(requireContext(),
|
||||
android.R.layout.simple_spinner_item, PAYMENT_METHODS));
|
||||
}
|
||||
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.containsKey("saleId")) {
|
||||
saleId = a.getLong("saleId");
|
||||
viewOnly = a.getBoolean("viewOnly", false);
|
||||
tvMode.setText("Sale #" + saleId);
|
||||
tvSaleDetailId.setText("ID: " + saleId);
|
||||
|
||||
// Show refund button for existing non-refund sales
|
||||
if (!a.getBoolean("isRefund", false)) {
|
||||
btnRefund.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
// Hide save and input controls for view only
|
||||
if (viewOnly) {
|
||||
btnSave.setVisibility(View.GONE);
|
||||
spinnerStore.setEnabled(false);
|
||||
spinnerCustomer.setEnabled(false);
|
||||
spinnerPayment.setEnabled(false);
|
||||
spinnerProduct.setEnabled(false);
|
||||
etQuantity.setEnabled(false);
|
||||
btnAddItem.setEnabled(false);
|
||||
}
|
||||
|
||||
// Load sale details
|
||||
loadSaleDetails();
|
||||
} else {
|
||||
tvMode.setText("New Sale");
|
||||
tvSaleDetailId.setVisibility(View.GONE);
|
||||
btnRefund.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadData() {
|
||||
loadStores();
|
||||
loadCustomers();
|
||||
loadProducts();
|
||||
}
|
||||
|
||||
private void loadStores() {
|
||||
// Hardcoded since store endpoint is admin only
|
||||
storeList = new ArrayList<>();
|
||||
storeList.add(new StoreDTO(1L, "Downtown Branch"));
|
||||
List<String> names = new ArrayList<>();
|
||||
names.add("-- Select Store --");
|
||||
names.add("Downtown Branch");
|
||||
spinnerStore.setAdapter(new ArrayAdapter<>(requireContext(),
|
||||
android.R.layout.simple_spinner_item, names));
|
||||
}
|
||||
|
||||
private void loadCustomers() {
|
||||
RetrofitClient.getCustomerApi(requireContext()).getAllCustomers(0, 200)
|
||||
.enqueue(new Callback<PageResponse<CustomerDTO>>() {
|
||||
public void onResponse(Call<PageResponse<CustomerDTO>> c,
|
||||
Response<PageResponse<CustomerDTO>> r) {
|
||||
if (r.isSuccessful() && r.body() != null) {
|
||||
customerList = r.body().getContent();
|
||||
List<String> names = new ArrayList<>();
|
||||
names.add("-- No Customer --");
|
||||
for (CustomerDTO cu : customerList)
|
||||
names.add(cu.getFirstName() + " " + cu.getLastName());
|
||||
spinnerCustomer.setAdapter(new ArrayAdapter<>(requireContext(),
|
||||
android.R.layout.simple_spinner_item, names));
|
||||
}
|
||||
}
|
||||
|
||||
public void onFailure(Call<PageResponse<CustomerDTO>> c, Throwable t) {
|
||||
Log.e("SaleDetail", "Customer load failed: " + t.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void loadProducts() {
|
||||
RetrofitClient.getProductApi(requireContext()).getAllProducts(null, null, 0, 200, null)
|
||||
.enqueue(new Callback<PageResponse<ProductDTO>>() {
|
||||
public void onResponse(Call<PageResponse<ProductDTO>> c,
|
||||
Response<PageResponse<ProductDTO>> r) {
|
||||
if (r.isSuccessful() && r.body() != null) {
|
||||
productList = r.body().getContent();
|
||||
List<String> names = new ArrayList<>();
|
||||
names.add("-- Select Product --");
|
||||
for (ProductDTO p : productList)
|
||||
names.add(p.getProdName());
|
||||
spinnerProduct.setAdapter(new ArrayAdapter<>(requireContext(),
|
||||
android.R.layout.simple_spinner_item, names));
|
||||
}
|
||||
}
|
||||
|
||||
public void onFailure(Call<PageResponse<ProductDTO>> c, Throwable t) {
|
||||
Log.e("SaleDetail", "Product load failed: " + t.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void loadSaleDetails() {
|
||||
RetrofitClient.getSaleApi(requireContext()).getSaleById(saleId)
|
||||
.enqueue(new Callback<SaleDTO>() {
|
||||
public void onResponse(Call<SaleDTO> c, Response<SaleDTO> r) {
|
||||
if (r.isSuccessful() && r.body() != null) {
|
||||
SaleDTO sale = r.body();
|
||||
tvTotal.setText("Total: $" + sale.getTotalAmount());
|
||||
// Display items
|
||||
if (sale.getItems() != null) {
|
||||
llItems.removeAllViews();
|
||||
for (SaleDTO.SaleItemDTO item : sale.getItems()) {
|
||||
addItemRow(item.getProductName(),
|
||||
Math.abs(item.getQuantity()),
|
||||
item.getUnitPrice());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onFailure(Call<SaleDTO> c, Throwable t) {
|
||||
Log.e("SaleDetail", "Load failed: " + t.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupAddItem() {
|
||||
btnAddItem.setOnClickListener(v -> {
|
||||
if (spinnerProduct.getSelectedItemPosition() == 0) {
|
||||
Toast.makeText(getContext(), "Select a product", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
String qtyStr = etQuantity.getText().toString().trim();
|
||||
if (qtyStr.isEmpty()) {
|
||||
etQuantity.setError("Enter quantity");
|
||||
return;
|
||||
}
|
||||
int qty;
|
||||
try {
|
||||
qty = Integer.parseInt(qtyStr);
|
||||
} catch (Exception e) {
|
||||
etQuantity.setError("Invalid quantity");
|
||||
return;
|
||||
}
|
||||
|
||||
ProductDTO product = productList.get(spinnerProduct.getSelectedItemPosition() - 1);
|
||||
|
||||
// Check if product already in cart
|
||||
for (SaleDTO.SaleItemDTO existing : cartItems) {
|
||||
if (existing.getProdId().equals(product.getProdId())) {
|
||||
Toast.makeText(getContext(), "Product already added", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SaleDTO.SaleItemDTO item = new SaleDTO.SaleItemDTO(product.getProdId(), qty);
|
||||
cartItems.add(item);
|
||||
addItemRow(product.getProdName(), qty, product.getProdPrice());
|
||||
updateTotal();
|
||||
etQuantity.setText("");
|
||||
});
|
||||
}
|
||||
|
||||
private void addItemRow(String name, int qty, BigDecimal price) {
|
||||
LinearLayout row = new LinearLayout(getContext());
|
||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
row.setPadding(0, 8, 0, 8);
|
||||
|
||||
TextView tvName = new TextView(getContext());
|
||||
tvName.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
0, LinearLayout.LayoutParams.WRAP_CONTENT, 2f));
|
||||
tvName.setText(name);
|
||||
|
||||
TextView tvQty = new TextView(getContext());
|
||||
tvQty.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f));
|
||||
tvQty.setText("x" + qty);
|
||||
|
||||
TextView tvPrice = new TextView(getContext());
|
||||
tvPrice.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f));
|
||||
tvPrice.setText(price != null ? "$" + price : "");
|
||||
|
||||
row.addView(tvName);
|
||||
row.addView(tvQty);
|
||||
row.addView(tvPrice);
|
||||
llItems.addView(row);
|
||||
}
|
||||
|
||||
private void updateTotal() {
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
int productIdx = 0;
|
||||
for (SaleDTO.SaleItemDTO item : cartItems) {
|
||||
if (productIdx < productList.size()) {
|
||||
for (ProductDTO p : productList) {
|
||||
if (p.getProdId().equals(item.getProdId()) && p.getProdPrice() != null) {
|
||||
total = total.add(p.getProdPrice()
|
||||
.multiply(BigDecimal.valueOf(item.getQuantity())));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tvTotal.setText("Total: $" + total);
|
||||
}
|
||||
|
||||
private void saveSale() {
|
||||
if (spinnerStore.getSelectedItemPosition() == 0) {
|
||||
Toast.makeText(getContext(), "Select a store", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
if (cartItems.isEmpty()) {
|
||||
Toast.makeText(getContext(), "Add at least one item", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
StoreDTO store = storeList.get(0); // only one store
|
||||
String payment = PAYMENT_METHODS[spinnerPayment.getSelectedItemPosition()];
|
||||
|
||||
// Optional customer
|
||||
Long customerId = null;
|
||||
if (spinnerCustomer.getSelectedItemPosition() > 0) {
|
||||
customerId = customerList.get(spinnerCustomer.getSelectedItemPosition() - 1)
|
||||
.getCustomerId();
|
||||
}
|
||||
|
||||
SaleDTO dto = new SaleDTO(
|
||||
store.getStoreId(),
|
||||
payment,
|
||||
cartItems,
|
||||
false,
|
||||
null,
|
||||
customerId);
|
||||
|
||||
Log.d("SALE_SAVE", "storeId=" + store.getStoreId()
|
||||
+ " payment=" + payment
|
||||
+ " items=" + cartItems.size()
|
||||
+ " customerId=" + customerId);
|
||||
|
||||
RetrofitClient.getSaleApi(requireContext()).createSale(dto)
|
||||
.enqueue(new Callback<SaleDTO>() {
|
||||
public void onResponse(Call<SaleDTO> c, Response<SaleDTO> r) {
|
||||
if (r.isSuccessful()) {
|
||||
Toast.makeText(getContext(), "Sale saved!", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else {
|
||||
try {
|
||||
String err = r.errorBody().string();
|
||||
Log.e("SALE_SAVE", "Error: " + err);
|
||||
Toast.makeText(getContext(), "Error " + r.code() + ": " + err,
|
||||
Toast.LENGTH_LONG).show();
|
||||
} catch (Exception e) {
|
||||
Log.e("SALE_SAVE", "Failed to read error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onFailure(Call<SaleDTO> c, Throwable t) {
|
||||
Log.e("SALE_SAVE", "Failure: " + t.getMessage());
|
||||
Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showRefundDialog() {
|
||||
Bundle args = new Bundle();
|
||||
args.putLong("saleId", saleId);
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_refund, args);
|
||||
}
|
||||
|
||||
private void submitRefund() {
|
||||
RefundDTO refundDTO = new RefundDTO(saleId, "Refund requested from mobile app");
|
||||
RetrofitClient.getRefundApi(requireContext()).createRefund(refundDTO)
|
||||
.enqueue(new Callback<RefundDTO>() {
|
||||
public void onResponse(Call<RefundDTO> c, Response<RefundDTO> r) {
|
||||
if (r.isSuccessful()) {
|
||||
Toast.makeText(getContext(), "Refund request submitted!",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
btnRefund.setVisibility(View.GONE);
|
||||
} else {
|
||||
try {
|
||||
String err = r.errorBody().string();
|
||||
Log.e("REFUND", "Error: " + err);
|
||||
Toast.makeText(getContext(), "Error: " + err,
|
||||
Toast.LENGTH_LONG).show();
|
||||
} catch (Exception e) {
|
||||
Log.e("REFUND", "Failed to read error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onFailure(Call<RefundDTO> c, Throwable t) {
|
||||
Log.e("REFUND", "Failure: " + t.getMessage());
|
||||
Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.api.EmployeeApi;
|
||||
import com.example.petstoremobile.api.RetrofitClient;
|
||||
import com.example.petstoremobile.dtos.EmployeeDTO;
|
||||
import retrofit2.*;
|
||||
|
||||
public class StaffDetailFragment extends Fragment {
|
||||
|
||||
private TextView tvMode, tvStaffId;
|
||||
private EditText etUsername, etPassword, etFirstName, etLastName, etEmail, etPhone;
|
||||
private Spinner spinnerRole, spinnerStatus;
|
||||
private Button btnSave, btnDelete, btnBack;
|
||||
|
||||
private long employeeId = -1;
|
||||
private boolean isEditing = false;
|
||||
|
||||
private final String[] ROLES = {"STAFF", "ADMIN"};
|
||||
private final String[] STATUSES = {"Active", "Inactive"};
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_staff_detail, container, false);
|
||||
initViews(view);
|
||||
setupSpinners();
|
||||
handleArguments();
|
||||
|
||||
btnBack.setOnClickListener(v -> navigateBack());
|
||||
btnSave.setOnClickListener(v -> save());
|
||||
btnDelete.setOnClickListener(v -> confirmDelete());
|
||||
return view;
|
||||
}
|
||||
|
||||
private void initViews(View v) {
|
||||
tvMode = v.findViewById(R.id.tvStaffMode);
|
||||
tvStaffId = v.findViewById(R.id.tvStaffId);
|
||||
etUsername = v.findViewById(R.id.etStaffUsername);
|
||||
etPassword = v.findViewById(R.id.etStaffPassword);
|
||||
etFirstName = v.findViewById(R.id.etStaffFirstName);
|
||||
etLastName = v.findViewById(R.id.etStaffLastName);
|
||||
etEmail = v.findViewById(R.id.etStaffEmail);
|
||||
etPhone = v.findViewById(R.id.etStaffPhone);
|
||||
spinnerRole = v.findViewById(R.id.spinnerStaffRole);
|
||||
spinnerStatus = v.findViewById(R.id.spinnerStaffStatus);
|
||||
btnSave = v.findViewById(R.id.btnSaveStaff);
|
||||
btnDelete = v.findViewById(R.id.btnDeleteStaff);
|
||||
btnBack = v.findViewById(R.id.btnStaffBack);
|
||||
}
|
||||
|
||||
private void setupSpinners() {
|
||||
spinnerRole.setAdapter(new ArrayAdapter<>(requireContext(),
|
||||
android.R.layout.simple_spinner_item, ROLES));
|
||||
spinnerStatus.setAdapter(new ArrayAdapter<>(requireContext(),
|
||||
android.R.layout.simple_spinner_item, STATUSES));
|
||||
}
|
||||
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.getBoolean("isEditing", false)) {
|
||||
isEditing = true;
|
||||
employeeId = a.getLong("employeeId", -1);
|
||||
|
||||
tvMode.setText("Edit Staff Account");
|
||||
tvStaffId.setText("ID: " + employeeId);
|
||||
tvStaffId.setVisibility(View.VISIBLE);
|
||||
etUsername.setText(a.getString("username", ""));
|
||||
etFirstName.setText(a.getString("firstName", ""));
|
||||
etLastName.setText(a.getString("lastName", ""));
|
||||
etEmail.setText(a.getString("email", "")); // ← was showing fullName
|
||||
etPhone.setText(a.getString("phone", ""));
|
||||
btnDelete.setVisibility(View.VISIBLE);
|
||||
|
||||
// Pre-fill role
|
||||
String role = a.getString("role", "STAFF");
|
||||
for (int i = 0; i < ROLES.length; i++) {
|
||||
if (ROLES[i].equals(role)) {
|
||||
spinnerRole.setSelection(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-fill status
|
||||
boolean active = a.getBoolean("active", true);
|
||||
spinnerStatus.setSelection(active ? 0 : 1);
|
||||
|
||||
} else {
|
||||
isEditing = false;
|
||||
employeeId = -1;
|
||||
tvMode.setText("Add Staff Account");
|
||||
btnDelete.setVisibility(View.GONE);
|
||||
tvStaffId.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void save() {
|
||||
String username = etUsername.getText() != null ? etUsername.getText().toString().trim() : "";
|
||||
String password = etPassword.getText() != null ? etPassword.getText().toString().trim() : "";
|
||||
String firstName = etFirstName.getText() != null ? etFirstName.getText().toString().trim() : "";
|
||||
String lastName = etLastName.getText() != null ? etLastName.getText().toString().trim() : "";
|
||||
String email = etEmail.getText() != null ? etEmail.getText().toString().trim() : "";
|
||||
String phone = etPhone.getText() != null ? etPhone.getText().toString().trim() : "";
|
||||
String role = ROLES[spinnerRole.getSelectedItemPosition()];
|
||||
boolean active = spinnerStatus.getSelectedItemPosition() == 0;
|
||||
|
||||
// Validation
|
||||
if (username.isEmpty()) { etUsername.setError("Required"); return; }
|
||||
if (!isEditing && password.isEmpty()) {
|
||||
etPassword.setError("Required for new account"); return;
|
||||
}
|
||||
if (!isEditing && password.length() < 6) {
|
||||
etPassword.setError("At least 6 characters"); return;
|
||||
}
|
||||
if (firstName.isEmpty()) { etFirstName.setError("Required"); return; }
|
||||
if (lastName.isEmpty()) { etLastName.setError("Required"); return; }
|
||||
if (email.isEmpty()) { etEmail.setError("Required"); return; }
|
||||
if (phone.isEmpty()) { etPhone.setError("Required"); return; }
|
||||
|
||||
EmployeeDTO dto = new EmployeeDTO(
|
||||
username,
|
||||
password.isEmpty() ? null : password,
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
phone,
|
||||
role,
|
||||
active
|
||||
);
|
||||
|
||||
Log.d("STAFF_SAVE", "isEditing=" + isEditing
|
||||
+ " employeeId=" + employeeId
|
||||
+ " username=" + username);
|
||||
|
||||
EmployeeApi api = RetrofitClient.getEmployeeApi(requireContext());
|
||||
if (isEditing && employeeId > 0) {
|
||||
api.updateEmployee(employeeId, dto).enqueue(simpleCallback("Updated successfully"));
|
||||
} else {
|
||||
api.createEmployee(dto).enqueue(simpleCallback("Staff account created"));
|
||||
}
|
||||
}
|
||||
|
||||
private Callback<EmployeeDTO> simpleCallback(String msg) {
|
||||
return new Callback<>() {
|
||||
public void onResponse(Call<EmployeeDTO> c, Response<EmployeeDTO> r) {
|
||||
Log.d("STAFF_SAVE", "Response: " + r.code());
|
||||
if (r.isSuccessful()) {
|
||||
Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else {
|
||||
try {
|
||||
String err = r.errorBody().string();
|
||||
Log.e("STAFF_SAVE", "Error: " + err);
|
||||
Toast.makeText(getContext(), "Error " + r.code() + ": " + err,
|
||||
Toast.LENGTH_LONG).show();
|
||||
} catch (Exception e) {
|
||||
Log.e("STAFF_SAVE", "Failed to read error");
|
||||
}
|
||||
}
|
||||
}
|
||||
public void onFailure(Call<EmployeeDTO> c, Throwable t) {
|
||||
Log.e("STAFF_SAVE", "Failure: " + t.getMessage());
|
||||
Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void confirmDelete() {
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle("Delete Staff Account?")
|
||||
.setMessage("This will permanently delete this staff account.")
|
||||
.setPositiveButton("Yes", (d, w) ->
|
||||
RetrofitClient.getEmployeeApi(requireContext())
|
||||
.deleteEmployee(employeeId)
|
||||
.enqueue(new Callback<Void>() {
|
||||
public void onResponse(Call<Void> c, Response<Void> r) {
|
||||
navigateBack();
|
||||
}
|
||||
public void onFailure(Call<Void> c, Throwable t) {
|
||||
Toast.makeText(getContext(), "Delete failed",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}))
|
||||
.setNegativeButton("No", null).show();
|
||||
}
|
||||
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -23,8 +24,8 @@ public class AdoptionRepository extends BaseRepository {
|
||||
/**
|
||||
* Retrieves a paginated list of all adoptions from the API.
|
||||
*/
|
||||
public LiveData<Resource<PageResponse<AdoptionDTO>>> getAllAdoptions(int page, int size) {
|
||||
return executeCall(adoptionApi.getAllAdoptions(page, size));
|
||||
public LiveData<Resource<PageResponse<AdoptionDTO>>> getAllAdoptions(int page, int size, String query, String status, Long storeId, String date, Long employeeId) {
|
||||
return executeCall(adoptionApi.getAllAdoptions(page, size, query, status, storeId, date, employeeId));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.example.petstoremobile.repositories;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
|
||||
import com.example.petstoremobile.api.EmployeeApi;
|
||||
import com.example.petstoremobile.dtos.EmployeeDTO;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class EmployeeRepository extends BaseRepository {
|
||||
private final EmployeeApi employeeApi;
|
||||
|
||||
@Inject
|
||||
public EmployeeRepository(EmployeeApi employeeApi) {
|
||||
super("EmployeeRepository");
|
||||
this.employeeApi = employeeApi;
|
||||
}
|
||||
|
||||
public LiveData<Resource<PageResponse<EmployeeDTO>>> getAllEmployees(int page, int size) {
|
||||
return executeCall(employeeApi.getAllEmployees(page, size));
|
||||
}
|
||||
|
||||
public LiveData<Resource<EmployeeDTO>> getEmployeeById(Long id) {
|
||||
return executeCall(employeeApi.getEmployeeById(id));
|
||||
}
|
||||
|
||||
public LiveData<Resource<EmployeeDTO>> createEmployee(EmployeeDTO dto) {
|
||||
return executeCall(employeeApi.createEmployee(dto));
|
||||
}
|
||||
|
||||
public LiveData<Resource<EmployeeDTO>> updateEmployee(Long id, EmployeeDTO dto) {
|
||||
return executeCall(employeeApi.updateEmployee(id, dto));
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> deleteEmployee(Long id) {
|
||||
return executeCall(employeeApi.deleteEmployee(id));
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import androidx.lifecycle.LiveData;
|
||||
import com.example.petstoremobile.api.InventoryApi;
|
||||
import com.example.petstoremobile.dtos.BulkDeleteRequest;
|
||||
import com.example.petstoremobile.dtos.InventoryDTO;
|
||||
import com.example.petstoremobile.dtos.InventoryRequest;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
@@ -39,11 +38,11 @@ public class InventoryRepository extends BaseRepository {
|
||||
/**
|
||||
* Sends a request to the API to create a new inventory record.
|
||||
*/
|
||||
public LiveData<Resource<InventoryDTO>> createInventory(InventoryRequest request) {
|
||||
public LiveData<Resource<InventoryDTO>> createInventory(InventoryDTO request) {
|
||||
return executeCall(inventoryApi.createInventory(request));
|
||||
}
|
||||
|
||||
public LiveData<Resource<InventoryDTO>> updateInventory(Long id, InventoryRequest request) {
|
||||
public LiveData<Resource<InventoryDTO>> updateInventory(Long id, InventoryDTO request) {
|
||||
return executeCall(inventoryApi.updateInventory(id, request));
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.example.petstoremobile.repositories;
|
||||
import androidx.lifecycle.LiveData;
|
||||
|
||||
import com.example.petstoremobile.api.PetApi;
|
||||
import com.example.petstoremobile.dtos.BulkDeleteRequest;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.PetDTO;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
@@ -57,6 +58,13 @@ public class PetRepository extends BaseRepository {
|
||||
return executeCall(petApi.deletePet(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to delete multiple pet records.
|
||||
*/
|
||||
public LiveData<Resource<Void>> bulkDeletePets(BulkDeleteRequest request) {
|
||||
return executeCall(petApi.bulkDeletePets(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads an image file for a specific pet via the API.
|
||||
*/
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.example.petstoremobile.repositories;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
|
||||
import com.example.petstoremobile.api.SaleApi;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.SaleDTO;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class SaleRepository extends BaseRepository {
|
||||
private final SaleApi saleApi;
|
||||
|
||||
@Inject
|
||||
public SaleRepository(SaleApi saleApi) {
|
||||
super("SaleRepository");
|
||||
this.saleApi = saleApi;
|
||||
}
|
||||
|
||||
public LiveData<Resource<PageResponse<SaleDTO>>> getAllSales(int page, int size) {
|
||||
return executeCall(saleApi.getAllSales(page, size));
|
||||
}
|
||||
|
||||
public LiveData<Resource<SaleDTO>> getSaleById(Long id) {
|
||||
return executeCall(saleApi.getSaleById(id));
|
||||
}
|
||||
|
||||
public LiveData<Resource<SaleDTO>> createSale(SaleDTO sale) {
|
||||
return executeCall(saleApi.createSale(sale));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.example.petstoremobile.utils;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.LiveData;
|
||||
|
||||
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 {
|
||||
|
||||
/**
|
||||
* Interface that adapters must implement to support bulk selection.
|
||||
*/
|
||||
public interface SelectableAdapter {
|
||||
List<String> getSelectedKeys();
|
||||
void clearSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Functional interface for the API call execution.
|
||||
*/
|
||||
public interface BulkDeleteOperation {
|
||||
LiveData<Resource<Void>> execute(List<String> keys);
|
||||
}
|
||||
|
||||
private final Fragment fragment;
|
||||
private final View layoutBar;
|
||||
private final TextView tvCount;
|
||||
private final SelectableAdapter adapter;
|
||||
private final BulkDeleteOperation operation;
|
||||
private final Runnable onSuccess;
|
||||
private final String itemName;
|
||||
|
||||
public BulkDeleteHandler(Fragment fragment,
|
||||
View layoutBar,
|
||||
TextView tvCount,
|
||||
Button btnDelete,
|
||||
SelectableAdapter adapter,
|
||||
String itemName,
|
||||
BulkDeleteOperation operation,
|
||||
Runnable onSuccess) {
|
||||
this.fragment = fragment;
|
||||
this.layoutBar = layoutBar;
|
||||
this.tvCount = tvCount;
|
||||
this.adapter = adapter;
|
||||
this.operation = operation;
|
||||
this.onSuccess = onSuccess;
|
||||
this.itemName = itemName;
|
||||
|
||||
btnDelete.setOnClickListener(v -> confirmDelete());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the UI when the selection count changes.
|
||||
*/
|
||||
public void onSelectionChanged(int selectedCount) {
|
||||
if (selectedCount > 0) {
|
||||
layoutBar.setVisibility(View.VISIBLE);
|
||||
tvCount.setText(selectedCount + " selected");
|
||||
} else {
|
||||
hideBar();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the bulk delete bar and resets state.
|
||||
*/
|
||||
public void hideBar() {
|
||||
if (layoutBar != null) {
|
||||
layoutBar.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the confirmation dialog.
|
||||
*/
|
||||
private void confirmDelete() {
|
||||
List<String> keys = adapter.getSelectedKeys();
|
||||
if (keys.isEmpty()) return;
|
||||
|
||||
DialogUtils.showBulkDeleteConfirmDialog(fragment.requireContext(), keys.size(), () -> performDelete(keys));
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the deletion via the provided operation.
|
||||
*/
|
||||
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(), keys.size() + " " + itemName + "(s) deleted", Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(fragment.getContext(), "Delete failed: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,18 @@ public class DialogUtils {
|
||||
showConfirmDialog(context, "Delete " + itemName + "?", "Are you sure you want to delete this " + itemName.toLowerCase() + "? This action cannot be undone.", callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a confirmation dialog with specific "Delete" and "Cancel" buttons.
|
||||
*/
|
||||
public static void showBulkDeleteConfirmDialog(Context context, int count, DialogCallback callback) {
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle("Delete " + count + " item(s)?")
|
||||
.setMessage("This cannot be undone.")
|
||||
.setPositiveButton("Delete", (dialog, which) -> callback.onConfirm())
|
||||
.setNegativeButton("Cancel", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a simple information or error dialog with an "OK" button.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.example.petstoremobile.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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<String> selectedKeys = new ArrayList<>();
|
||||
private boolean selectionMode = false;
|
||||
private final SelectionListener listener;
|
||||
|
||||
public interface SelectionListener {
|
||||
void onSelectionChanged(int count);
|
||||
void onSelectionModeToggle(boolean selectionMode);
|
||||
}
|
||||
|
||||
public SelectionHelper(SelectionListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void toggleSelection(String key) {
|
||||
if (key == null) return;
|
||||
|
||||
if (selectedKeys.contains(key)) {
|
||||
selectedKeys.remove(key);
|
||||
} else {
|
||||
selectedKeys.add(key);
|
||||
}
|
||||
|
||||
listener.onSelectionChanged(selectedKeys.size());
|
||||
|
||||
if (selectedKeys.isEmpty() && selectionMode) {
|
||||
selectionMode = false;
|
||||
listener.onSelectionModeToggle(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void startSelection(String key) {
|
||||
if (key == null) return;
|
||||
selectionMode = true;
|
||||
selectedKeys.add(key);
|
||||
listener.onSelectionChanged(selectedKeys.size());
|
||||
listener.onSelectionModeToggle(true);
|
||||
}
|
||||
|
||||
public boolean isSelected(String key) {
|
||||
return selectedKeys.contains(key);
|
||||
}
|
||||
|
||||
public boolean isInSelectionMode() {
|
||||
return selectionMode;
|
||||
}
|
||||
|
||||
public List<String> getSelectedKeys() {
|
||||
return new ArrayList<>(selectedKeys);
|
||||
}
|
||||
|
||||
public void clearSelection() {
|
||||
selectedKeys.clear();
|
||||
selectionMode = false;
|
||||
listener.onSelectionChanged(0);
|
||||
listener.onSelectionModeToggle(false);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.example.petstoremobile.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Spinner;
|
||||
|
||||
@@ -70,6 +72,30 @@ public class SpinnerUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a simple string spinner for filtering with a callback.
|
||||
*/
|
||||
public static void setupStringFilterSpinner(Context context, Spinner spinner, String[] items, Runnable onSelectionChanged) {
|
||||
WhiteTextArrayAdapter<String> adapter = new WhiteTextArrayAdapter<>(context,
|
||||
android.R.layout.simple_spinner_item, items);
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
spinner.setAdapter(adapter);
|
||||
setupFilterSpinner(spinner, onSelectionChanged);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches an item selected listener to a spinner that triggers a callback.
|
||||
*/
|
||||
public static void setupFilterSpinner(Spinner spinner, Runnable onSelectionChanged) {
|
||||
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
onSelectionChanged.run();
|
||||
}
|
||||
@Override public void onNothingSelected(AdapterView<?> parent) {}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selection of a spinner based on a string value.
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
@@ -22,10 +25,10 @@ public class AdoptionViewModel extends ViewModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a paginated list of all adoptions.
|
||||
* Fetches a paginated list of all adoptions with filters.
|
||||
*/
|
||||
public LiveData<Resource<PageResponse<AdoptionDTO>>> getAllAdoptions(int page, int size) {
|
||||
return repository.getAllAdoptions(page, size);
|
||||
public LiveData<Resource<PageResponse<AdoptionDTO>>> getAllAdoptions(int page, int size, String query, String status, Long storeId, String date, Long employeeId) {
|
||||
return repository.getAllAdoptions(page, size, query, status, storeId, date, employeeId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.example.petstoremobile.viewmodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.example.petstoremobile.dtos.EmployeeDTO;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.repositories.EmployeeRepository;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
|
||||
@HiltViewModel
|
||||
public class EmployeeViewModel extends ViewModel {
|
||||
private final EmployeeRepository employeeRepository;
|
||||
|
||||
@Inject
|
||||
public EmployeeViewModel(EmployeeRepository employeeRepository) {
|
||||
this.employeeRepository = employeeRepository;
|
||||
}
|
||||
|
||||
public LiveData<Resource<PageResponse<EmployeeDTO>>> getAllEmployees(int page, int size) {
|
||||
return employeeRepository.getAllEmployees(page, size);
|
||||
}
|
||||
|
||||
public LiveData<Resource<EmployeeDTO>> getEmployeeById(Long id) {
|
||||
return employeeRepository.getEmployeeById(id);
|
||||
}
|
||||
|
||||
public LiveData<Resource<EmployeeDTO>> createEmployee(EmployeeDTO dto) {
|
||||
return employeeRepository.createEmployee(dto);
|
||||
}
|
||||
|
||||
public LiveData<Resource<EmployeeDTO>> updateEmployee(Long id, EmployeeDTO dto) {
|
||||
return employeeRepository.updateEmployee(id, dto);
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> deleteEmployee(Long id) {
|
||||
return employeeRepository.deleteEmployee(id);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import androidx.lifecycle.ViewModel;
|
||||
import com.example.petstoremobile.dtos.BulkDeleteRequest;
|
||||
import com.example.petstoremobile.dtos.CategoryDTO;
|
||||
import com.example.petstoremobile.dtos.InventoryDTO;
|
||||
import com.example.petstoremobile.dtos.InventoryRequest;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.StoreDTO;
|
||||
import com.example.petstoremobile.repositories.CategoryRepository;
|
||||
@@ -50,14 +49,14 @@ public class InventoryViewModel extends ViewModel {
|
||||
/**
|
||||
* Creates a new inventory record.
|
||||
*/
|
||||
public LiveData<Resource<InventoryDTO>> createInventory(InventoryRequest request) {
|
||||
public LiveData<Resource<InventoryDTO>> createInventory(InventoryDTO request) {
|
||||
return inventoryRepository.createInventory(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing inventory record by ID.
|
||||
*/
|
||||
public LiveData<Resource<InventoryDTO>> updateInventory(Long id, InventoryRequest request) {
|
||||
public LiveData<Resource<InventoryDTO>> updateInventory(Long id, InventoryDTO request) {
|
||||
return inventoryRepository.updateInventory(id, request);
|
||||
}
|
||||
|
||||
@@ -71,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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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.PetDTO;
|
||||
import com.example.petstoremobile.repositories.PetRepository;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
@@ -57,6 +60,13 @@ public class PetViewModel extends ViewModel {
|
||||
return repository.deletePet(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes multiple pet records.
|
||||
*/
|
||||
public LiveData<Resource<Void>> bulkDeletePets(List<String> ids) {
|
||||
return repository.bulkDeletePets(new BulkDeleteRequest(ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads an image for a specific pet.
|
||||
*/
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.example.petstoremobile.viewmodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.SaleDTO;
|
||||
import com.example.petstoremobile.repositories.SaleRepository;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
|
||||
@HiltViewModel
|
||||
public class SaleViewModel extends ViewModel {
|
||||
private final SaleRepository saleRepository;
|
||||
|
||||
@Inject
|
||||
public SaleViewModel(SaleRepository saleRepository) {
|
||||
this.saleRepository = saleRepository;
|
||||
}
|
||||
|
||||
public LiveData<Resource<PageResponse<SaleDTO>>> getAllSales(int page, int size) {
|
||||
return saleRepository.getAllSales(page, size);
|
||||
}
|
||||
|
||||
public LiveData<Resource<SaleDTO>> getSaleById(Long id) {
|
||||
return saleRepository.getSaleById(id);
|
||||
}
|
||||
|
||||
public LiveData<Resource<SaleDTO>> createSale(SaleDTO sale) {
|
||||
return saleRepository.createSale(sale);
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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<String> ids) {
|
||||
return repository.bulkDeleteSuppliers(new BulkDeleteRequest(ids));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:background="@color/primary_dark"
|
||||
@@ -34,7 +35,8 @@
|
||||
android:text="Adoptions"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"/>
|
||||
android:textStyle="bold"
|
||||
android:layout_marginStart="8dp"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnToggleCalendarModeAdoption"
|
||||
@@ -45,6 +47,120 @@
|
||||
app:tint="@color/white"
|
||||
android:contentDescription="Toggle Calendar Mode"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnToggleFilterAdoption"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:src="@android:drawable/ic_menu_search"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
app:tint="@color/white"
|
||||
android:contentDescription="Toggle filter"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutFilterAdoption"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:visibility="gone"
|
||||
android:background="@color/primary_dark"
|
||||
android:elevation="4dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="44dp"
|
||||
android:background="@drawable/bg_search_bar"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="18dp"
|
||||
android:src="@android:drawable/ic_menu_search"
|
||||
android:alpha="0.6"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etSearchAdoption"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:hint="Search by customer or pet..."
|
||||
android:inputType="text"
|
||||
android:background="@android:color/transparent"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textColorHint="#99000000"
|
||||
android:textSize="14sp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="8dp">
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinnerStatusAdoption"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="44dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_spinner"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="8dp"/>
|
||||
|
||||
<View
|
||||
android:layout_width="8dp"
|
||||
android:layout_height="0dp"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinnerStoreAdoption"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="44dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_spinner"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="8dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutBulkDelete"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:background="@color/primary_medium"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSelectionCount"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="0 selected"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnBulkDelete"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Delete Selected"
|
||||
android:backgroundTint="@color/accent_coral"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.prolificinteractive.materialcalendarview.MaterialCalendarView
|
||||
@@ -57,19 +173,6 @@
|
||||
app:mcv_calendarMode="week"
|
||||
app:mcv_tileHeight="40dp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etSearchAdoption"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:hint="Search by customer or pet..."
|
||||
android:inputType="text"
|
||||
android:drawableStart="@android:drawable/ic_menu_search"
|
||||
android:drawablePadding="8dp"
|
||||
android:background="@android:color/white"
|
||||
android:padding="12dp"
|
||||
android:textColor="@color/text_dark"/>
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshAdoption"
|
||||
android:layout_width="match_parent"
|
||||
@@ -96,4 +199,4 @@
|
||||
app:srcCompat="@android:drawable/ic_input_add"
|
||||
app:tint="@color/white"/>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
@@ -95,6 +95,21 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Employee -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Handled By (Staff)"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinnerAdoptionEmployee"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -129,6 +144,23 @@
|
||||
android:drawableEnd="@android:drawable/ic_menu_my_calendar"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Adoption Fee -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Adoption Fee"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etAdoptionFee"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="0.00"
|
||||
android:inputType="numberDecimal"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Status -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
318
android/app/src/main/res/layout/fragment_analytics.xml
Normal file
318
android/app/src/main/res/layout/fragment_analytics.xml
Normal file
@@ -0,0 +1,318 @@
|
||||
<?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="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/background_grey">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:background="@color/primary_dark"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnHamburgerAnalytics"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:src="@drawable/baseline_menu_36"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="Open menu"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Analytics"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnRefreshAnalytics"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Refresh"
|
||||
android:backgroundTint="@color/accent_coral"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- Summary Cards Row 1 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="8dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/rounded_card"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Total Revenue"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="11sp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTotalRevenue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="$0.00"
|
||||
android:textColor="@color/accent_coral"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginTop="4dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="4dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/rounded_card"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Transactions"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="11sp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTotalTransactions"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="0"
|
||||
android:textColor="@color/primary_dark"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginTop="4dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Summary Cards Row 2 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/rounded_card"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Avg Transaction"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="11sp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAvgTransaction"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="$0.00"
|
||||
android:textColor="@color/primary_dark"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginTop="4dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="4dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/rounded_card"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Items Sold"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="11sp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTotalItems"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="0"
|
||||
android:textColor="@color/primary_dark"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginTop="4dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Top Products by Revenue -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/rounded_card"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Top Products by Revenue"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llTopRevenue"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Top Products by Quantity -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/rounded_card"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Top Products by Quantity"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llTopQuantity"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Payment Methods -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/rounded_card"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Payment Methods"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llPaymentMethods"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Employee Performance -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/rounded_card"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Employee Performance"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llEmployeePerformance"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Daily Revenue -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/rounded_card"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Daily Revenue (Last 7 Days)"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llDailyRevenue"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -67,7 +67,23 @@
|
||||
android:layout_marginBottom="12dp"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<!-- Product search label -->
|
||||
<!-- Store selection label -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Store"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<!-- Store Spinner -->
|
||||
<Spinner
|
||||
android:id="@+id/spinnerInventoryStore"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Product selection label -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -76,25 +92,12 @@
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<!-- AutoComplete search box -->
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/etProductSearch"
|
||||
<!-- Product Spinner -->
|
||||
<Spinner
|
||||
android:id="@+id/spinnerInventoryProduct"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Search product name…"
|
||||
android:inputType="text"
|
||||
android:completionThreshold="1"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<!-- Selected product info (ID + category) shown after picking -->
|
||||
<TextView
|
||||
android:id="@+id/tvProductInfo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#888888"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:visibility="gone"/>
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Quantity label -->
|
||||
<TextView
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Staff Portal"
|
||||
android:text="pet shop"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="13sp"/>
|
||||
|
||||
@@ -216,6 +216,41 @@
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerAnalytics"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Analytics"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerStaff"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:visibility="gone">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Staff Accounts"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerPurchaseOrderView"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -132,6 +132,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/swipeRefreshPet"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -70,6 +70,22 @@
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Store"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="12sp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvPODetailStore"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
222
android/app/src/main/res/layout/fragment_refund.xml
Normal file
222
android/app/src/main/res/layout/fragment_refund.xml
Normal file
@@ -0,0 +1,222 @@
|
||||
<?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="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/background_grey">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:background="@color/primary_dark"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Process Refund"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- Load Sale Card -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/rounded_card"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Load Original Sale"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etRefundSaleId"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="Enter Sale ID"
|
||||
android:inputType="number"
|
||||
android:layout_marginEnd="8dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnLoadSale"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Load"
|
||||
android:backgroundTint="@color/primary_medium"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSaleInfo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Original Sale Items Card -->
|
||||
<LinearLayout
|
||||
android:id="@+id/cardOriginalItems"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/rounded_card"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Original Sale Items"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llOriginalItems"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Refund Items Card -->
|
||||
<LinearLayout
|
||||
android:id="@+id/cardRefundItems"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/rounded_card"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Items to Refund"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llRefundItems"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvRefundTotal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Refund Total: $0.00"
|
||||
android:textColor="@color/accent_coral"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="12dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Payment Method Card -->
|
||||
<LinearLayout
|
||||
android:id="@+id/cardPayment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/rounded_card"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Refund Payment Method"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinnerRefundPayment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
<!-- Bottom Buttons -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="@color/white"
|
||||
android:padding="16dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnRefundBack"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="Back"
|
||||
android:backgroundTint="@color/primary_medium"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnProcessRefund"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="Process Refund"
|
||||
android:backgroundTint="@color/accent_coral"
|
||||
android:textColor="@color/white"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -43,7 +43,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:hint="Search by item, employee or date..."
|
||||
android:hint="Search by employee or store..."
|
||||
android:inputType="text"
|
||||
android:drawableStart="@android:drawable/ic_menu_search"
|
||||
android:drawablePadding="8dp"
|
||||
@@ -51,6 +51,14 @@
|
||||
android:padding="12dp"
|
||||
android:textColor="@color/text_dark"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnOpenRefund"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Refund"
|
||||
android:backgroundTint="@color/accent_coral"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshSale"
|
||||
android:layout_width="match_parent"
|
||||
@@ -66,4 +74,16 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fabAddSale"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="16dp"
|
||||
android:backgroundTint="@color/accent_coral"
|
||||
android:contentDescription="Add Sale"
|
||||
app:srcCompat="@android:drawable/ic_input_add"
|
||||
app:tint="@color/white"/>
|
||||
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
221
android/app/src/main/res/layout/fragment_sale_detail.xml
Normal file
221
android/app/src/main/res/layout/fragment_sale_detail.xml
Normal file
@@ -0,0 +1,221 @@
|
||||
<?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="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/background_grey">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:background="@color/primary_dark"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSaleMode"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="New Sale"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnRefundSale"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Refund"
|
||||
android:backgroundTint="@color/accent_coral"
|
||||
android:textColor="@color/white"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<!-- Sale Info Card -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/rounded_card"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSaleDetailId"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="11sp"
|
||||
android:textStyle="italic"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<!-- Store -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Store"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinnerSaleStore"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Customer (optional) -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Customer (Optional)"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinnerSaleCustomer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Payment Method -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Payment Method"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinnerPaymentMethod"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Items Card -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/rounded_card"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Items"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
|
||||
<!-- Item row -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="8dp">
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinnerSaleProduct"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
android:layout_marginEnd="8dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etSaleQuantity"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="Qty"
|
||||
android:inputType="number"
|
||||
android:layout_marginEnd="8dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnAddItem"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Add"
|
||||
android:backgroundTint="@color/primary_medium"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Items list container -->
|
||||
<LinearLayout
|
||||
android:id="@+id/llSaleItems"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
<!-- Total -->
|
||||
<TextView
|
||||
android:id="@+id/tvSaleDetailTotal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Total: $0.00"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/accent_coral"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="12dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="@color/white"
|
||||
android:padding="16dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnSaleBack"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="Back"
|
||||
android:backgroundTint="@color/primary_medium"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnSaveSale"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="Save"
|
||||
android:backgroundTint="@color/accent_coral"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</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"
|
||||
|
||||
82
android/app/src/main/res/layout/fragment_staff.xml
Normal file
82
android/app/src/main/res/layout/fragment_staff.xml
Normal file
@@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/background_grey">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:background="@color/primary_dark"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnHamburgerStaff"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:src="@drawable/baseline_menu_36"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="Open menu"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Staff Accounts"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etSearchStaff"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:hint="Search by name, username or email..."
|
||||
android:inputType="text"
|
||||
android:drawableStart="@android:drawable/ic_menu_search"
|
||||
android:drawablePadding="8dp"
|
||||
android:background="@android:color/white"
|
||||
android:padding="12dp"
|
||||
android:textColor="@color/text_dark"/>
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshStaff"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerViewStaff"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="8dp"/>
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fabAddStaff"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="16dp"
|
||||
android:backgroundTint="@color/accent_coral"
|
||||
android:contentDescription="Add Staff"
|
||||
app:srcCompat="@android:drawable/ic_input_add"
|
||||
app:tint="@color/white"/>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
|
||||
233
android/app/src/main/res/layout/fragment_staff_detail.xml
Normal file
233
android/app/src/main/res/layout/fragment_staff_detail.xml
Normal file
@@ -0,0 +1,233 @@
|
||||
<?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="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/background_grey">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:background="@color/primary_dark"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvStaffMode"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Add Staff Account"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnDeleteStaff"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/accent_coral"
|
||||
android:text="Delete"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/rounded_card"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvStaffId"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="11sp"
|
||||
android:textStyle="italic"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<!-- Username -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Username"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etStaffUsername"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Enter username"
|
||||
android:inputType="text"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Password -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Password"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etStaffPassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Enter password (leave blank to keep current)"
|
||||
android:inputType="textPassword"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- First Name -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="First Name"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etStaffFirstName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Enter first name"
|
||||
android:inputType="textPersonName"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Last Name -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Last Name"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etStaffLastName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Enter last name"
|
||||
android:inputType="textPersonName"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Email -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Email"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etStaffEmail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Enter email"
|
||||
android:inputType="textEmailAddress"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Phone -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Phone"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etStaffPhone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Enter phone number"
|
||||
android:inputType="phone"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Role -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Role"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinnerStaffRole"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Status -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Status"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinnerStaffStatus"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="@color/white"
|
||||
android:padding="16dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnStaffBack"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="Back"
|
||||
android:backgroundTint="@color/primary_medium"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnSaveStaff"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="Save"
|
||||
android:backgroundTint="@color/accent_coral"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</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/swipeRefreshSupplier"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -2,88 +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">
|
||||
|
||||
<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/tvAdoptionPetName"
|
||||
<CheckBox
|
||||
android:id="@+id/cbSelectAdoption"
|
||||
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" />
|
||||
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/tvAdoptionFee"
|
||||
android:layout_width="0dp"
|
||||
<LinearLayout
|
||||
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:textStyle="bold" />
|
||||
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/tvAdoptionDate"
|
||||
android:id="@+id/tvAdoptionPetName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Date"
|
||||
android:layout_marginTop="4dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
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>
|
||||
|
||||
@@ -1,74 +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/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>
|
||||
|
||||
99
android/app/src/main/res/layout/item_employee.xml
Normal file
99
android/app/src/main/res/layout/item_employee.xml
Normal file
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.cardview.widget.CardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvEmployeeFullName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/text_dark"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvEmployeeUsername"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="13sp"
|
||||
android:textColor="@color/text_light"
|
||||
android:layout_marginTop="2dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvEmployeeEmail"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/text_light"
|
||||
android:layout_marginTop="2dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvEmployeePhone"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/text_light"
|
||||
android:layout_marginTop="2dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="end"
|
||||
android:layout_gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvEmployeeRole"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:textSize="11sp"
|
||||
android:textColor="@color/white"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvEmployeeStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:textSize="11sp"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
@@ -47,7 +47,7 @@
|
||||
android:id="@+id/tvQuantity"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="0"
|
||||
android:text="Stock: 0"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
@@ -55,31 +55,14 @@
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvInventoryId"
|
||||
android:id="@+id/tvInventoryStore"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="Inv ID: —"
|
||||
android:layout_marginTop="2dp"
|
||||
android:text="Store: —"
|
||||
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/tvProdId"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Prod ID: —"
|
||||
android:textColor="#888888"
|
||||
android:textSize="13sp" />
|
||||
|
||||
</LinearLayout>
|
||||
android:textSize="14sp"
|
||||
android:textStyle="italic" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -3,116 +3,133 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
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/cbSelectPet"
|
||||
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">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/ivPetProfile"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/placeholder"
|
||||
app:shapeAppearanceOverlay="@style/CircleImageView"
|
||||
app:strokeWidth="2dp"
|
||||
app:strokeColor="#BDBDBD"
|
||||
android:padding="2dp"
|
||||
android:contentDescription="@string/pet_profile_image_desc" />
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/ivPetProfile"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/placeholder"
|
||||
app:shapeAppearanceOverlay="@style/CircleImageView"
|
||||
app:strokeWidth="2dp"
|
||||
app:strokeColor="#BDBDBD"
|
||||
android:padding="2dp"
|
||||
android:contentDescription="@string/pet_profile_image_desc" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
android:layout_weight="1"
|
||||
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/tvPetName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="Pet Name"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvPetStatus"
|
||||
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/tvPetName"
|
||||
android:layout_width="0dp"
|
||||
android:id="@+id/tvPetSpeciesBreed"
|
||||
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="Pet Name"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvPetStatus"
|
||||
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/tvPetSpeciesBreed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="Breed"
|
||||
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/tvPetPrice"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="$126.00"
|
||||
android:textColor="@color/accent_coral"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvPetAge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="1"
|
||||
android:text="Breed"
|
||||
android:textColor="#888888"
|
||||
android:textSize="13sp" />
|
||||
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/tvPetPrice"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="$126.00"
|
||||
android:textColor="@color/accent_coral"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvPetAge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="1"
|
||||
android:textColor="#888888"
|
||||
android:textSize="13sp" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</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,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>
|
||||
@@ -53,6 +53,17 @@
|
||||
android:textColor="#888888"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvPOStore"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="Store Name"
|
||||
android:textColor="#888888"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user