Merge branch 'AttachmentsToChat'
This commit is contained in:
@@ -32,6 +32,9 @@ public class ForgotPasswordActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityForgotPasswordBinding binding;
|
||||
|
||||
/**
|
||||
* Set the content view for forget password page
|
||||
*/
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
EdgeToEdge.enable(this);
|
||||
@@ -54,6 +57,10 @@ public class ForgotPasswordActivity extends AppCompatActivity {
|
||||
binding.btnBackToLogin.setOnClickListener(v -> finish());
|
||||
}
|
||||
|
||||
/**
|
||||
* A function to send a reset link to the given email address.
|
||||
* Calls the forgotPassword endpoint. To send the reset link
|
||||
* */
|
||||
private void sendResetLink(String email) {
|
||||
binding.btnSubmit.setEnabled(false);
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ public class HomeActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
setIntent(intent); // Set the new intent so fragments can access updated extras
|
||||
setIntent(intent);
|
||||
handleIntent(intent);
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ public class HomeActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests POST_NOTIFICATIONS permission from the user if running on Android 13 and above.
|
||||
* Requests for notification permission from the user if running on Android 13 and above.
|
||||
*/
|
||||
private void requestNotificationPermission() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
|
||||
@@ -98,7 +98,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the login process using the AuthViewModel and handles the authentication response.
|
||||
* Perform login process using the AuthViewModel and handles the authentication response.
|
||||
*/
|
||||
private void performLogin(String username, String password) {
|
||||
viewModel.login(username, password).observe(this, resource -> {
|
||||
@@ -112,6 +112,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
case SUCCESS:
|
||||
if (resource.data != null) {
|
||||
String role = resource.data.getRole();
|
||||
//Check if role is staff/admin or customer
|
||||
if ("CUSTOMER".equalsIgnoreCase(role)) {
|
||||
UIUtils.setViewsEnabled(true, binding.btnLogin);
|
||||
binding.tvLoginStatus.setText("Customers are not allowed to log in");
|
||||
@@ -132,7 +133,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the logged-in user's profile information to save their ID before navigating to the home screen.
|
||||
* Retrieves the user's profile information to save their ID before navigating to the home screen.
|
||||
*/
|
||||
private void fetchUserIdAndNavigate() {
|
||||
viewModel.getMe().observe(this, resource -> {
|
||||
|
||||
@@ -25,10 +25,16 @@ public class ActivityLogAdapter extends RecyclerView.Adapter<ActivityLogAdapter.
|
||||
|
||||
private final List<ActivityLogDTO> items;
|
||||
|
||||
/**
|
||||
* Constructor for the ActivityLogAdapter.
|
||||
*/
|
||||
public ActivityLogAdapter(List<ActivityLogDTO> items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for an activity log item and creates a new ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -37,6 +43,9 @@ public class ActivityLogAdapter extends RecyclerView.Adapter<ActivityLogAdapter.
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the data from ActivityLogDTO to the items in the ViewHolder.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
ActivityLogDTO log = items.get(position);
|
||||
@@ -71,9 +80,15 @@ public class ActivityLogAdapter extends RecyclerView.Adapter<ActivityLogAdapter.
|
||||
holder.tvTimestamp.setText(formatTimestamp(log.getLogTimestamp()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of items in the list.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() { return items.size(); }
|
||||
|
||||
/**
|
||||
* Formats the timestamp string from the API to a readable date/time format.
|
||||
*/
|
||||
private String formatTimestamp(String raw) {
|
||||
if (raw == null) return "";
|
||||
try {
|
||||
@@ -85,6 +100,9 @@ public class ActivityLogAdapter extends RecyclerView.Adapter<ActivityLogAdapter.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the Role string to be consistent
|
||||
*/
|
||||
private String formatRole(String role) {
|
||||
if (role == null) return "";
|
||||
switch (role.toUpperCase(Locale.ROOT)) {
|
||||
@@ -95,6 +113,9 @@ public class ActivityLogAdapter extends RecyclerView.Adapter<ActivityLogAdapter.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first non-null and non-blank string from the provided arguments.
|
||||
*/
|
||||
private String firstNonBlank(String... values) {
|
||||
for (String v : values) {
|
||||
if (v != null && !v.isBlank()) return v;
|
||||
@@ -102,9 +123,15 @@ public class ActivityLogAdapter extends RecyclerView.Adapter<ActivityLogAdapter.
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder class that holds references to the UI components for an activity log item.
|
||||
*/
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvActivity, tvTechnical, tvUser, tvMeta, tvTimestamp;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder by finding the views within the item layout.
|
||||
*/
|
||||
public ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
tvActivity = itemView.findViewById(R.id.tvLogActivity);
|
||||
|
||||
@@ -23,6 +23,9 @@ public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.Adopti
|
||||
void onSelectionChanged(int count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for AdoptionAdapter.
|
||||
*/
|
||||
public AdoptionAdapter(List<AdoptionDTO> adoptionList, OnAdoptionClickListener listener) {
|
||||
this.adoptionList = adoptionList;
|
||||
this.listener = listener;
|
||||
@@ -39,11 +42,17 @@ public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.Adopti
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of IDs for the currently selected adoption items.
|
||||
*/
|
||||
@Override
|
||||
public List<String> getSelectedKeys() {
|
||||
return selectionHelper.getSelectedKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the selection state, deselecting all items.
|
||||
*/
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionHelper.clearSelection();
|
||||
@@ -58,6 +67,9 @@ public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.Adopti
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for an adoption item and creates the ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public AdoptionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -65,6 +77,9 @@ public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.Adopti
|
||||
return new AdoptionViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds adoption data to the UI components and handles click/long-click logic.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull AdoptionViewHolder holder, int position) {
|
||||
AdoptionDTO a = adoptionList.get(position);
|
||||
@@ -123,6 +138,9 @@ public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.Adopti
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of adoption items in the list.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() { return adoptionList.size(); }
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@ public class AppointmentAdapter extends RecyclerView.Adapter<AppointmentAdapter.
|
||||
void onSelectionChanged(int count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for AppointmentAdapter.
|
||||
*/
|
||||
public AppointmentAdapter(List<AppointmentDTO> appointmentList,
|
||||
OnAppointmentClickListener appointmentClickListener) {
|
||||
this.appointmentList = appointmentList;
|
||||
@@ -40,25 +43,40 @@ public class AppointmentAdapter extends RecyclerView.Adapter<AppointmentAdapter.
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of IDs for the currently selected appointment items.
|
||||
*/
|
||||
@Override
|
||||
public List<String> getSelectedKeys() {
|
||||
return selectionHelper.getSelectedKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the selection state, deselecting all items.
|
||||
*/
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionHelper.clearSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder class that holds references to the UI components for an appointment item.
|
||||
*/
|
||||
public static class AppointmentViewHolder extends RecyclerView.ViewHolder {
|
||||
private final ItemAppointmentBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder by finding the views within the item layout.
|
||||
*/
|
||||
public AppointmentViewHolder(@NonNull ItemAppointmentBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for an appointment item and creates the ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public AppointmentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -66,6 +84,9 @@ public class AppointmentAdapter extends RecyclerView.Adapter<AppointmentAdapter.
|
||||
return new AppointmentViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds appointment data to the UI components and handles click/long-click logic.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull AppointmentViewHolder holder, int position) {
|
||||
AppointmentDTO a = appointmentList.get(position);
|
||||
@@ -124,6 +145,9 @@ public class AppointmentAdapter extends RecyclerView.Adapter<AppointmentAdapter.
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of appointment items in the list.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return appointmentList.size();
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.example.petstoremobile.R;
|
||||
import java.util.List;
|
||||
|
||||
// A class that overrides the arrayAdapter so the text color changes based on theme
|
||||
// Used to make spinners have black text on white background no matter the theme
|
||||
public class BlackTextArrayAdapter<T> extends ArrayAdapter<T> {
|
||||
public BlackTextArrayAdapter(@NonNull Context context, int resource, @NonNull T[] objects) {
|
||||
super(context, resource, objects);
|
||||
|
||||
@@ -20,11 +20,17 @@ public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ChatViewHolder
|
||||
void onChatClick(Chat chat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for ChatAdapter.
|
||||
*/
|
||||
public ChatAdapter(List<Chat> chatList, OnChatClickListener listener) {
|
||||
this.chatList = chatList;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for a chat item and creates the ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public ChatViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -32,6 +38,9 @@ public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ChatViewHolder
|
||||
return new ChatViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds chat data to the UI components for a specific conversation.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ChatViewHolder holder, int position) {
|
||||
Chat chat = chatList.get(position);
|
||||
@@ -40,14 +49,23 @@ public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ChatViewHolder
|
||||
holder.itemView.setOnClickListener(v -> listener.onChatClick(chat));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of chat items in the list.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return chatList.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder class that holds references to the UI components for a chat item.
|
||||
*/
|
||||
public static class ChatViewHolder extends RecyclerView.ViewHolder {
|
||||
final ItemChatBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with the chat item's view binding.
|
||||
*/
|
||||
public ChatViewHolder(@NonNull ItemChatBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
|
||||
@@ -30,11 +30,17 @@ public class CouponAdapter extends RecyclerView.Adapter<CouponAdapter.ViewHolder
|
||||
void onSelectionChanged(int count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for CouponAdapter.
|
||||
*/
|
||||
public CouponAdapter(List<CouponDTO> coupons, OnCouponClickListener listener) {
|
||||
this.coupons = coupons;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for a coupon item and creates the ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -42,6 +48,9 @@ public class CouponAdapter extends RecyclerView.Adapter<CouponAdapter.ViewHolder
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds coupon data to the UI components and handles interaction logic.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
CouponDTO coupon = coupons.get(position);
|
||||
@@ -95,6 +104,9 @@ public class CouponAdapter extends RecyclerView.Adapter<CouponAdapter.ViewHolder
|
||||
holder.cbSelectCoupon.setOnClickListener(v -> toggleSelection(coupon.getCouponId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the selection state of a specific coupon by its ID.
|
||||
*/
|
||||
private void toggleSelection(Long id) {
|
||||
if (selectedIds.contains(id)) {
|
||||
selectedIds.remove(id);
|
||||
@@ -105,6 +117,9 @@ public class CouponAdapter extends RecyclerView.Adapter<CouponAdapter.ViewHolder
|
||||
listener.onSelectionChanged(selectedIds.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables bulk selection mode.
|
||||
*/
|
||||
public void setSelectionMode(boolean selectionMode) {
|
||||
this.selectionMode = selectionMode;
|
||||
if (!selectionMode) selectedIds.clear();
|
||||
@@ -112,10 +127,16 @@ public class CouponAdapter extends RecyclerView.Adapter<CouponAdapter.ViewHolder
|
||||
listener.onSelectionChanged(selectedIds.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of IDs for the currently selected coupons.
|
||||
*/
|
||||
public Set<Long> getSelectedIds() {
|
||||
return selectedIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of coupons in the list.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return coupons.size();
|
||||
@@ -125,6 +146,9 @@ public class CouponAdapter extends RecyclerView.Adapter<CouponAdapter.ViewHolder
|
||||
TextView tvCouponCode, tvCouponDiscount, tvCouponMinOrder, tvCouponExpiry, tvCouponStatus;
|
||||
CheckBox cbSelectCoupon;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder by finding the views within the item layout.
|
||||
*/
|
||||
public ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
tvCouponCode = itemView.findViewById(R.id.tvCouponCode);
|
||||
|
||||
@@ -24,23 +24,42 @@ public class CustomerAdapter extends RecyclerView.Adapter<CustomerAdapter.Custom
|
||||
void onCustomerClick(int position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for CustomerAdapter.
|
||||
*/
|
||||
public CustomerAdapter(List<CustomerDTO> list, OnCustomerClickListener listener) {
|
||||
this.list = list;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base URL for fetching customer profile images.
|
||||
*/
|
||||
public void setBaseUrl(String baseUrl) { this.baseUrl = baseUrl; }
|
||||
|
||||
/**
|
||||
* Sets the authentication token for fetching images.
|
||||
*/
|
||||
public void setToken(String token) { this.token = token; }
|
||||
|
||||
/**
|
||||
* ViewHolder class for customer items.
|
||||
*/
|
||||
public static class CustomerViewHolder extends RecyclerView.ViewHolder {
|
||||
final ItemCustomerBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with view binding.
|
||||
*/
|
||||
public CustomerViewHolder(@NonNull ItemCustomerBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for a customer item and creates the ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public CustomerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -49,6 +68,9 @@ public class CustomerAdapter extends RecyclerView.Adapter<CustomerAdapter.Custom
|
||||
return new CustomerViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds customer data to the UI components.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull CustomerViewHolder holder, int position) {
|
||||
CustomerDTO c = list.get(position);
|
||||
@@ -75,6 +97,9 @@ public class CustomerAdapter extends RecyclerView.Adapter<CustomerAdapter.Custom
|
||||
holder.itemView.setOnClickListener(v -> listener.onCustomerClick(position));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of customers in the list.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() { return list.size(); }
|
||||
}
|
||||
|
||||
@@ -25,28 +25,46 @@ public class EmployeeAdapter extends RecyclerView.Adapter<EmployeeAdapter.Employ
|
||||
void onEmployeeClick(int position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for EmployeeAdapter.
|
||||
*/
|
||||
public EmployeeAdapter(List<EmployeeDTO> list, OnEmployeeClickListener listener) {
|
||||
this.list = list;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base URL for fetching employee profile images.
|
||||
*/
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication token for fetching images.
|
||||
*/
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder class for employee items.
|
||||
*/
|
||||
public static class EmployeeViewHolder extends RecyclerView.ViewHolder {
|
||||
private final ItemEmployeeBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with view binding.
|
||||
*/
|
||||
public EmployeeViewHolder(@NonNull ItemEmployeeBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for an employee item and creates the ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public EmployeeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -55,6 +73,9 @@ public class EmployeeAdapter extends RecyclerView.Adapter<EmployeeAdapter.Employ
|
||||
return new EmployeeViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds employee data to the UI components.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull EmployeeViewHolder holder, int position) {
|
||||
EmployeeDTO e = list.get(position);
|
||||
@@ -90,6 +111,9 @@ public class EmployeeAdapter extends RecyclerView.Adapter<EmployeeAdapter.Employ
|
||||
holder.itemView.setOnClickListener(v -> listener.onEmployeeClick(position));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of employees in the list.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() { return list.size(); }
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@ public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.Inve
|
||||
void onSelectionChanged(int selectedCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for InventoryAdapter.
|
||||
*/
|
||||
public InventoryAdapter(List<InventoryDTO> inventoryList, OnInventoryClickListener clickListener) {
|
||||
this.inventoryList = inventoryList;
|
||||
this.clickListener = clickListener;
|
||||
@@ -43,25 +46,40 @@ public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.Inve
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of IDs for the currently selected inventory items.
|
||||
*/
|
||||
@Override
|
||||
public List<String> getSelectedKeys() {
|
||||
return selectionHelper.getSelectedKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the selection state, deselecting all items.
|
||||
*/
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionHelper.clearSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder class that holds references to the UI components for an inventory item.
|
||||
*/
|
||||
public static class InventoryViewHolder extends RecyclerView.ViewHolder {
|
||||
final ItemInventoryBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with view binding.
|
||||
*/
|
||||
public InventoryViewHolder(@NonNull ItemInventoryBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for an inventory item and creates the ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public InventoryViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -69,6 +87,9 @@ public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.Inve
|
||||
return new InventoryViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds inventory data to the UI components.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull InventoryViewHolder holder, int position) {
|
||||
InventoryDTO inv = inventoryList.get(position);
|
||||
@@ -84,7 +105,6 @@ public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.Inve
|
||||
int qty = inv.getQuantity() != null ? inv.getQuantity() : 0;
|
||||
binding.tvQuantity.setText("Stock: " + qty);
|
||||
|
||||
// Low stock = red, normal = green (like desktop reorder concept)
|
||||
if (qty <= 5) {
|
||||
binding.tvQuantity.setTextColor(Color.parseColor("#F44336"));
|
||||
} else {
|
||||
@@ -119,6 +139,9 @@ public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.Inve
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of inventory items in the list.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return inventoryList.size();
|
||||
|
||||
@@ -37,40 +37,64 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
private String baseUrl;
|
||||
private OnAttachmentClickListener attachmentClickListener;
|
||||
|
||||
/**
|
||||
* Constructor for MessageAdapter.
|
||||
*/
|
||||
public MessageAdapter(List<Message> messages, Long currentUserId) {
|
||||
this.messages = messages;
|
||||
this.currentUserId = currentUserId;
|
||||
setHasStableIds(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ID for each message.
|
||||
*/
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
Message m = messages.get(position);
|
||||
return m.getId() != null ? m.getId() : position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current user's ID and refreshes the list.
|
||||
*/
|
||||
public void setCurrentUserId(Long id) {
|
||||
this.currentUserId = id;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the staff ID to identify staff messages in the UI.
|
||||
*/
|
||||
public void setStaffId(Long id) {
|
||||
this.staffId = id;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication token.
|
||||
*/
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base API URL.
|
||||
*/
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a listener for clicks on message attachments.
|
||||
*/
|
||||
public void setOnAttachmentClickListener(OnAttachmentClickListener listener) {
|
||||
this.attachmentClickListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a message is 'sent' or 'received' based on the sender's ID.
|
||||
*/
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
Message m = messages.get(position);
|
||||
@@ -80,6 +104,9 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
return TYPE_RECEIVED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the chat layout for a message.
|
||||
*/
|
||||
@NonNull @Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
LayoutInflater inf = LayoutInflater.from(parent.getContext());
|
||||
@@ -92,6 +119,9 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds message data to the appropriate ViewHolder.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||
Message m = messages.get(position);
|
||||
@@ -99,14 +129,26 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
if (holder instanceof ReceivedHolder) ((ReceivedHolder) holder).bind(m, token, baseUrl, attachmentClickListener, staffId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of messages.
|
||||
*/
|
||||
@Override public int getItemCount() { return messages.size(); }
|
||||
|
||||
/**
|
||||
* ViewHolder for messages sent by the user.
|
||||
*/
|
||||
static class SentHolder extends RecyclerView.ViewHolder {
|
||||
final ItemMessageSentBinding binding;
|
||||
/**
|
||||
* Initializes the SentHolder with view binding.
|
||||
*/
|
||||
SentHolder(ItemMessageSentBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
/**
|
||||
* Binds sent message data to the bubble UI.
|
||||
*/
|
||||
void bind(Message m, String token, String baseUrl, OnAttachmentClickListener listener) {
|
||||
binding.tvSenderName.setText("You");
|
||||
binding.tvTimestamp.setText(formatTimestamp(m.getTimestamp()));
|
||||
@@ -128,12 +170,21 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder for messages received from others.
|
||||
*/
|
||||
static class ReceivedHolder extends RecyclerView.ViewHolder {
|
||||
final ItemMessageReceivedBinding binding;
|
||||
/**
|
||||
* Initializes the ReceivedHolder with view binding.
|
||||
*/
|
||||
ReceivedHolder(ItemMessageReceivedBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
/**
|
||||
* Binds received message data to the bubble UI.
|
||||
*/
|
||||
void bind(Message m, String token, String baseUrl, OnAttachmentClickListener listener, Long staffId) {
|
||||
binding.tvSenderName.setText(resolveSenderName(m, staffId));
|
||||
binding.tvTimestamp.setText(formatTimestamp(m.getTimestamp()));
|
||||
@@ -155,6 +206,9 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the display name of the sender.
|
||||
*/
|
||||
private static String resolveSenderName(Message m, Long staffId) {
|
||||
if ("BOT".equalsIgnoreCase(m.getSenderRole())) {
|
||||
return (m.getSenderDisplayName() != null && !m.getSenderDisplayName().isEmpty())
|
||||
@@ -166,6 +220,9 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
return "Customer";
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the timestamp string into readable format.
|
||||
*/
|
||||
private static String formatTimestamp(String timestamp) {
|
||||
if (timestamp == null || timestamp.isEmpty()) return "";
|
||||
try {
|
||||
@@ -178,8 +235,11 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logic for displaying an attachment in the chat.
|
||||
*/
|
||||
private static void displayAttachment(Message m, ImageView iv, TextView tvName, String token, String baseUrl) {
|
||||
// Check if there's an attachment by looking at name or mime type
|
||||
// Check if there's an attachment
|
||||
if (m.getAttachmentName() != null || m.getAttachmentMimeType() != null) {
|
||||
// Construct the download URL using the message ID
|
||||
String url;
|
||||
@@ -187,7 +247,7 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
String cleanBase = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl;
|
||||
url = cleanBase + "/api/v1/chat/messages/" + m.getId() + "/attachment";
|
||||
} else {
|
||||
url = m.getAttachmentUrl(); // Fallback
|
||||
url = m.getAttachmentUrl();
|
||||
}
|
||||
|
||||
if (url == null) {
|
||||
@@ -208,7 +268,7 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
.build());
|
||||
}
|
||||
|
||||
// Use a signature to prevent Glide from showing stale cached images for the same URL/ID
|
||||
// Use a signature to prevent Glide from showing cached images instead of loading new ones
|
||||
String signatureKey = (m.getTimestamp() != null ? m.getTimestamp() : "") + m.getId();
|
||||
|
||||
Glide.with(iv.getContext()).clear(iv);
|
||||
|
||||
@@ -31,7 +31,9 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
|
||||
void onSelectionChanged(int selectedCount);
|
||||
}
|
||||
|
||||
//Constructor
|
||||
/**
|
||||
* Constructor for PetAdapter.
|
||||
*/
|
||||
public PetAdapter(List<PetDTO> petList, OnPetClickListener petClickListener) {
|
||||
this.petList = petList;
|
||||
this.petClickListener = petClickListener;
|
||||
@@ -48,35 +50,54 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base URL for fetching pet images from the server.
|
||||
*/
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication token
|
||||
*/
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of IDs for the currently selected pet items.
|
||||
*/
|
||||
@Override
|
||||
public List<String> getSelectedKeys() {
|
||||
return selectionHelper.getSelectedKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the selection state, deselecting all items.
|
||||
*/
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionHelper.clearSelection();
|
||||
}
|
||||
|
||||
// Get the controls of each row in recycler view
|
||||
/**
|
||||
* ViewHolder class that holds references to the UI components for a pet item.
|
||||
*/
|
||||
public static class PetViewHolder extends RecyclerView.ViewHolder {
|
||||
private final ItemPetBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with view binding.
|
||||
*/
|
||||
public PetViewHolder(@NonNull ItemPetBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new row view
|
||||
/**
|
||||
* Inflates the layout for a pet item and creates the ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public PetViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -84,7 +105,9 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
|
||||
return new PetViewHolder(binding);
|
||||
}
|
||||
|
||||
//populate the row with pet data
|
||||
/**
|
||||
* Binds pet data to the UI components.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull PetViewHolder holder, int position) {
|
||||
PetDTO pet = petList.get(position);
|
||||
@@ -103,7 +126,7 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
|
||||
|
||||
binding.tvPetStatus.setText(pet.getPetStatus());
|
||||
|
||||
//Set the status color depending on availability. If available, green, If Pending, yellow, otherwise red
|
||||
//Set the status color depending on availability
|
||||
if (pet.getPetStatus() != null) {
|
||||
switch (pet.getPetStatus()) {
|
||||
case "Available":
|
||||
@@ -157,6 +180,9 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of pet items in the list.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return petList.size();
|
||||
|
||||
@@ -22,28 +22,46 @@ public class ProductAdapter extends RecyclerView.Adapter<ProductAdapter.ProductV
|
||||
void onProductClick(int position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for ProductAdapter.
|
||||
*/
|
||||
public ProductAdapter(List<ProductDTO> productList, OnProductClickListener listener) {
|
||||
this.productList = productList;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base URL for fetching product images.
|
||||
*/
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication token
|
||||
*/
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder class for product items.
|
||||
*/
|
||||
public static class ProductViewHolder extends RecyclerView.ViewHolder {
|
||||
final ItemProductBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with view binding.
|
||||
*/
|
||||
public ProductViewHolder(@NonNull ItemProductBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for a product item and creates the ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public ProductViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -51,6 +69,9 @@ public class ProductAdapter extends RecyclerView.Adapter<ProductAdapter.ProductV
|
||||
return new ProductViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds product data to the UI components.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ProductViewHolder holder, int position) {
|
||||
ProductDTO p = productList.get(position);
|
||||
@@ -72,6 +93,9 @@ public class ProductAdapter extends RecyclerView.Adapter<ProductAdapter.ProductV
|
||||
holder.itemView.setOnClickListener(v -> listener.onProductClick(position));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of product items in the list.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() { return productList.size(); }
|
||||
}
|
||||
@@ -25,6 +25,9 @@ public class ProductSupplierAdapter extends RecyclerView.Adapter<ProductSupplier
|
||||
void onSelectionChanged(int count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for ProductSupplierAdapter.
|
||||
*/
|
||||
public ProductSupplierAdapter(List<ProductSupplierDTO> list, OnProductSupplierClickListener listener) {
|
||||
this.list = list;
|
||||
this.listener = listener;
|
||||
@@ -41,25 +44,40 @@ public class ProductSupplierAdapter extends RecyclerView.Adapter<ProductSupplier
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of selected keys for bulk deletion.
|
||||
*/
|
||||
@Override
|
||||
public List<String> getSelectedKeys() {
|
||||
return selectionHelper.getSelectedKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all selected items and exits selection mode.
|
||||
*/
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionHelper.clearSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder for Product-Supplier relationship items.
|
||||
*/
|
||||
public static class PSViewHolder extends RecyclerView.ViewHolder {
|
||||
final ItemProductSupplierBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with view binding.
|
||||
*/
|
||||
public PSViewHolder(@NonNull ItemProductSupplierBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for a Product-Supplier item.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public PSViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -67,6 +85,9 @@ public class ProductSupplierAdapter extends RecyclerView.Adapter<ProductSupplier
|
||||
return new PSViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds product-supplier data to the UI components.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull PSViewHolder holder, int position) {
|
||||
ProductSupplierDTO ps = list.get(position);
|
||||
|
||||
@@ -17,20 +17,32 @@ public class PurchaseOrderAdapter extends RecyclerView.Adapter<PurchaseOrderAdap
|
||||
void onPurchaseOrderClick(int position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for PurchaseOrderAdapter.
|
||||
*/
|
||||
public PurchaseOrderAdapter(List<PurchaseOrderDTO> list, OnPurchaseOrderClickListener listener) {
|
||||
this.list = list;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder for Purchase Order items.
|
||||
*/
|
||||
public static class POViewHolder extends RecyclerView.ViewHolder {
|
||||
final ItemPurchaseOrderBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with view binding.
|
||||
*/
|
||||
public POViewHolder(@NonNull ItemPurchaseOrderBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for a Purchase Order item.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public POViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -38,6 +50,9 @@ public class PurchaseOrderAdapter extends RecyclerView.Adapter<PurchaseOrderAdap
|
||||
return new POViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds purchase order data to the UI components.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull POViewHolder holder, int position) {
|
||||
PurchaseOrderDTO po = list.get(position);
|
||||
|
||||
@@ -20,20 +20,32 @@ public class SaleAdapter extends RecyclerView.Adapter<SaleAdapter.SaleViewHolder
|
||||
void onSaleClick(int position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for SaleAdapter.
|
||||
*/
|
||||
public SaleAdapter(List<SaleDTO> saleList, OnSaleClickListener listener) {
|
||||
this.saleList = saleList;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder for Sale record items.
|
||||
*/
|
||||
public static class SaleViewHolder extends RecyclerView.ViewHolder {
|
||||
final ItemSaleBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with view binding.
|
||||
*/
|
||||
public SaleViewHolder(@NonNull ItemSaleBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for a Sale record item.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public SaleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -41,6 +53,9 @@ public class SaleAdapter extends RecyclerView.Adapter<SaleAdapter.SaleViewHolder
|
||||
return new SaleViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds sale transaction data to the UI components.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull SaleViewHolder holder, int position) {
|
||||
SaleDTO s = saleList.get(position);
|
||||
|
||||
@@ -31,6 +31,9 @@ public class ServiceAdapter extends RecyclerView.Adapter<ServiceAdapter.ServiceV
|
||||
void onSelectionChanged(int count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for ServiceAdapter.
|
||||
*/
|
||||
public ServiceAdapter(List<ServiceDTO> serviceList, OnServiceClickListener clickListener) {
|
||||
this.serviceList = serviceList;
|
||||
this.clickListener = clickListener;
|
||||
@@ -47,29 +50,40 @@ public class ServiceAdapter extends RecyclerView.Adapter<ServiceAdapter.ServiceV
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of selected keys for bulk deletion.
|
||||
*/
|
||||
@Override
|
||||
public List<String> getSelectedKeys() {
|
||||
return selectionHelper.getSelectedKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all selected items and exits selection mode.
|
||||
*/
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionHelper.clearSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder class for service items.
|
||||
* ViewHolder for Service items.
|
||||
*/
|
||||
public static class ServiceViewHolder extends RecyclerView.ViewHolder {
|
||||
final ItemServiceBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with view binding.
|
||||
*/
|
||||
public ServiceViewHolder(@NonNull ItemServiceBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new row view
|
||||
/**
|
||||
* Inflates the layout for a Service item.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public ServiceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -77,7 +91,9 @@ public class ServiceAdapter extends RecyclerView.Adapter<ServiceAdapter.ServiceV
|
||||
return new ServiceViewHolder(binding);
|
||||
}
|
||||
|
||||
//populate the row with service data
|
||||
/**
|
||||
* Binds service data to the UI components.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ServiceViewHolder holder, int position) {
|
||||
ServiceDTO service = serviceList.get(position);
|
||||
|
||||
@@ -26,7 +26,9 @@ public class SupplierAdapter extends RecyclerView.Adapter<SupplierAdapter.Suppli
|
||||
void onSelectionChanged(int count);
|
||||
}
|
||||
|
||||
//Constructor
|
||||
/**
|
||||
* Constructor for SupplierAdapter.
|
||||
*/
|
||||
public SupplierAdapter(List<SupplierDTO> supplierList, OnSupplierClickListener supplierClickListener) {
|
||||
this.supplierList = supplierList;
|
||||
this.supplierClickListener = supplierClickListener;
|
||||
@@ -43,27 +45,40 @@ public class SupplierAdapter extends RecyclerView.Adapter<SupplierAdapter.Suppli
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of selected keys for bulk deletion.
|
||||
*/
|
||||
@Override
|
||||
public List<String> getSelectedKeys() {
|
||||
return selectionHelper.getSelectedKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all selected items and exits selection mode.
|
||||
*/
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionHelper.clearSelection();
|
||||
}
|
||||
|
||||
// Get the controls of each row in recycler view
|
||||
/**
|
||||
* ViewHolder for Supplier items.
|
||||
*/
|
||||
public static class SupplierViewHolder extends RecyclerView.ViewHolder {
|
||||
final ItemSupplierBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with view binding.
|
||||
*/
|
||||
public SupplierViewHolder(@NonNull ItemSupplierBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new row view
|
||||
/**
|
||||
* Inflates the layout for a Supplier item.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public SupplierViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -71,7 +86,9 @@ public class SupplierAdapter extends RecyclerView.Adapter<SupplierAdapter.Suppli
|
||||
return new SupplierViewHolder(binding);
|
||||
}
|
||||
|
||||
//populate the row with supplier data
|
||||
/**
|
||||
* Binds supplier data to the UI components.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull SupplierViewHolder holder, int position) {
|
||||
SupplierDTO supplier = supplierList.get(position);
|
||||
|
||||
@@ -12,9 +12,8 @@ import androidx.core.content.ContextCompat;
|
||||
import com.example.petstoremobile.R;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A class that overrides the arrayAdapter so the text color is white and background is transparent.
|
||||
*/
|
||||
// A class that overrides the arrayAdapter so the text color changes based on theme
|
||||
// Used to make spinners have white text on dark background no matter the theme
|
||||
public class WhiteTextArrayAdapter<T> extends ArrayAdapter<T> {
|
||||
public WhiteTextArrayAdapter(@NonNull Context context, int resource, @NonNull T[] objects) {
|
||||
super(context, resource, objects);
|
||||
|
||||
@@ -8,8 +8,10 @@ import retrofit2.Call;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls to get activity logs
|
||||
public interface ActivityLogApi {
|
||||
|
||||
// Get activity logs with filters
|
||||
@GET("api/v1/activity-logs")
|
||||
Call<List<ActivityLogDTO>> getActivityLogs(
|
||||
@Query("limit") int limit,
|
||||
|
||||
@@ -14,8 +14,10 @@ import retrofit2.http.PUT;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls for adoptions
|
||||
public interface AdoptionApi {
|
||||
|
||||
// Get all adoptions with filters
|
||||
@GET("api/v1/adoptions")
|
||||
Call<PageResponse<AdoptionDTO>> getAllAdoptions(
|
||||
@Query("page") int page,
|
||||
@@ -27,18 +29,23 @@ public interface AdoptionApi {
|
||||
@Query("employeeId") Long employeeId,
|
||||
@Query("sort") String sort);
|
||||
|
||||
// Get adoption by id
|
||||
@GET("api/v1/adoptions/{id}")
|
||||
Call<AdoptionDTO> getAdoptionById(@Path("id") Long id);
|
||||
|
||||
// Create adoption
|
||||
@POST("api/v1/adoptions")
|
||||
Call<AdoptionDTO> createAdoption(@Body AdoptionDTO adoption);
|
||||
|
||||
// Update adoption
|
||||
@PUT("api/v1/adoptions/{id}")
|
||||
Call<AdoptionDTO> updateAdoption(@Path("id") Long id, @Body AdoptionDTO adoption);
|
||||
|
||||
// Delete adoption
|
||||
@DELETE("api/v1/adoptions/{id}")
|
||||
Call<Void> deleteAdoption(@Path("id") Long id);
|
||||
|
||||
// Bulk delete adoptions
|
||||
@HTTP(method = "DELETE", path = "api/v1/adoptions", hasBody = true)
|
||||
Call<Void> bulkDeleteAdoptions(@Body BulkDeleteRequest request);
|
||||
}
|
||||
|
||||
@@ -14,8 +14,10 @@ import retrofit2.http.PUT;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls for appointments
|
||||
public interface AppointmentApi {
|
||||
|
||||
// Get all appointments with filters
|
||||
@GET("api/v1/appointments")
|
||||
Call<PageResponse<AppointmentDTO>> getAllAppointments(
|
||||
@Query("page") int page,
|
||||
@@ -27,18 +29,23 @@ public interface AppointmentApi {
|
||||
@Query("employeeId") Long employeeId,
|
||||
@Query("sort") String sort);
|
||||
|
||||
// Get appointment by id
|
||||
@GET("api/v1/appointments/{id}")
|
||||
Call<AppointmentDTO> getAppointmentById(@Path("id") Long id);
|
||||
|
||||
// Create appointment
|
||||
@POST("api/v1/appointments")
|
||||
Call<AppointmentDTO> createAppointment(@Body AppointmentDTO appointment);
|
||||
|
||||
// Update appointment
|
||||
@PUT("api/v1/appointments/{id}")
|
||||
Call<AppointmentDTO> updateAppointment(@Path("id") Long id, @Body AppointmentDTO appointment);
|
||||
|
||||
// Delete appointment
|
||||
@DELETE("api/v1/appointments/{id}")
|
||||
Call<Void> deleteAppointment(@Path("id") Long id);
|
||||
|
||||
// Bulk delete appointments
|
||||
@HTTP(method = "DELETE", path = "api/v1/appointments", hasBody = true)
|
||||
Call<Void> bulkDeleteAppointments(@Body BulkDeleteRequest request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,10 @@ import retrofit2.Call;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls for categories
|
||||
public interface CategoryApi {
|
||||
|
||||
// Get all categories with pagination
|
||||
@GET("api/v1/categories")
|
||||
Call<PageResponse<CategoryDTO>> getAllCategories(
|
||||
@Query("page") int page,
|
||||
|
||||
@@ -12,15 +12,18 @@ import retrofit2.http.GET;
|
||||
import retrofit2.http.PUT;
|
||||
import retrofit2.http.Path;
|
||||
|
||||
//api calls to get conversations
|
||||
// api calls for chat conversations
|
||||
public interface ChatApi {
|
||||
|
||||
// Get all conversations
|
||||
@GET("api/v1/chat/conversations")
|
||||
Call<List<ConversationDTO>> getAllConversations();
|
||||
|
||||
// Get conversation by id
|
||||
@GET("api/v1/chat/conversations/{conversationId}")
|
||||
Call<ConversationDTO> getConversationById(@Path("conversationId") Long conversationId);
|
||||
|
||||
// Update conversation status
|
||||
@PUT("api/v1/chat/conversations/{conversationId}")
|
||||
Call<ConversationDTO> updateConversationStatus(@Path("conversationId") Long conversationId, @Body UpdateConversationStatusRequest request);
|
||||
|
||||
|
||||
@@ -15,23 +15,30 @@ import retrofit2.http.PUT;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls for customers
|
||||
public interface CustomerApi {
|
||||
|
||||
// Get all customers with pagination
|
||||
@GET("api/v1/customers")
|
||||
Call<PageResponse<CustomerDTO>> getAllCustomers(@Query("page") int page, @Query("size") int size);
|
||||
|
||||
// Get customer by id
|
||||
@GET("api/v1/customers/{customerId}")
|
||||
Call<CustomerDTO> getCustomerById(@Path("customerId") Long customerId);
|
||||
|
||||
// Update customer
|
||||
@PUT("api/v1/customers/{customerId}")
|
||||
Call<CustomerDTO> updateCustomer(@Path("customerId") Long customerId, @Body CustomerDTO customer);
|
||||
|
||||
// Delete customer
|
||||
@DELETE("api/v1/customers/{customerId}")
|
||||
Call<Void> deleteCustomer(@Path("customerId") Long customerId);
|
||||
|
||||
// Register customer
|
||||
@POST("api/v1/auth/register")
|
||||
Call<CustomerDTO> registerCustomer(@Body CustomerDTO customer);
|
||||
|
||||
// Get customer dropdowns
|
||||
@GET("api/v1/dropdowns/customers")
|
||||
Call<List<DropdownDTO>> getCustomerDropdowns();
|
||||
}
|
||||
|
||||
@@ -5,28 +5,28 @@ import com.example.petstoremobile.dtos.PageResponse;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.*;
|
||||
|
||||
// api calls for employees
|
||||
public interface EmployeeApi {
|
||||
|
||||
// Get all employees with pagination
|
||||
@GET("api/v1/employees")
|
||||
Call<PageResponse<EmployeeDTO>> getAllEmployees(
|
||||
@Query("page") int page,
|
||||
@Query("size") int size);
|
||||
|
||||
// Get employee by id
|
||||
@GET("api/v1/employees/{id}")
|
||||
Call<EmployeeDTO> getEmployeeById(@Path("id") Long id);
|
||||
|
||||
// Create employee
|
||||
@POST("api/v1/employees")
|
||||
Call<EmployeeDTO> createEmployee(@Body EmployeeDTO employee);
|
||||
|
||||
// Update employee
|
||||
@PUT("api/v1/employees/{id}")
|
||||
Call<EmployeeDTO> updateEmployee(@Path("id") Long id, @Body EmployeeDTO employee);
|
||||
|
||||
// Delete employee
|
||||
@DELETE("api/v1/employees/{id}")
|
||||
Call<Void> deleteEmployee(@Path("id") Long id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -14,8 +14,10 @@ import retrofit2.http.PUT;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls for inventory
|
||||
public interface InventoryApi {
|
||||
|
||||
// Get all inventory with filters
|
||||
@GET("api/v1/inventory")
|
||||
Call<PageResponse<InventoryDTO>> getAllInventory(
|
||||
@Query("page") int page,
|
||||
@@ -24,23 +26,23 @@ public interface InventoryApi {
|
||||
@Query("storeId") Long storeId,
|
||||
@Query("sort") String sort);
|
||||
|
||||
// GET /api/v1/inventory/{id}
|
||||
// Get inventory by id
|
||||
@GET("api/v1/inventory/{id}")
|
||||
Call<InventoryDTO> getInventoryById(@Path("id") Long id);
|
||||
|
||||
// POST /api/v1/inventory
|
||||
// Create inventory
|
||||
@POST("api/v1/inventory")
|
||||
Call<InventoryDTO> createInventory(@Body InventoryDTO request);
|
||||
|
||||
// PUT /api/v1/inventory/{id}
|
||||
// Update inventory
|
||||
@PUT("api/v1/inventory/{id}")
|
||||
Call<InventoryDTO> updateInventory(@Path("id") Long id, @Body InventoryDTO request);
|
||||
|
||||
// DELETE /api/v1/inventory/{id}
|
||||
// Delete inventory
|
||||
@DELETE("api/v1/inventory/{id}")
|
||||
Call<Void> deleteInventory(@Path("id") Long id);
|
||||
|
||||
// DELETE /api/v1/inventory (bulk delete)
|
||||
// Bulk delete inventory
|
||||
@HTTP(method = "DELETE", path = "api/v1/inventory", hasBody = true)
|
||||
Call<Void> bulkDeleteInventory(@Body BulkDeleteRequest request);
|
||||
}
|
||||
|
||||
@@ -15,15 +15,18 @@ import retrofit2.http.Part;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Streaming;
|
||||
|
||||
//api calls to get and send messages
|
||||
// api calls for messages
|
||||
public interface MessageApi {
|
||||
|
||||
// Get messages for a conversation
|
||||
@GET("api/v1/chat/conversations/{id}/messages")
|
||||
Call<List<MessageDTO>> getMessages(@Path("id") Long conversationId);
|
||||
|
||||
// Send a message
|
||||
@POST("api/v1/chat/conversations/{id}/messages")
|
||||
Call<MessageDTO> sendMessage(@Path("id") Long conversationId, @Body SendMessageRequest request);
|
||||
|
||||
// Send a message with attachment
|
||||
@Multipart
|
||||
@POST("api/v1/chat/conversations/{id}/attachments")
|
||||
Call<MessageDTO> sendMessageWithAttachment(
|
||||
@@ -32,6 +35,7 @@ public interface MessageApi {
|
||||
@Part MultipartBody.Part file
|
||||
);
|
||||
|
||||
// Download attachment
|
||||
@GET("api/v1/chat/messages/{id}/attachment")
|
||||
@Streaming
|
||||
Call<ResponseBody> downloadAttachment(@Path("id") Long messageId);
|
||||
|
||||
@@ -20,7 +20,7 @@ import retrofit2.http.Part;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
//api calls to CRUD pets
|
||||
// api calls to CRUD pets
|
||||
public interface PetApi {
|
||||
// endpoint for downloading the pet's image file
|
||||
String PET_IMAGE_PATH = "api/v1/pets/%d/image";
|
||||
@@ -38,18 +38,23 @@ public interface PetApi {
|
||||
@Query("sort") String sort
|
||||
);
|
||||
|
||||
// Get pets by customer id
|
||||
@GET("api/v1/dropdowns/customers/{customerId}/pets")
|
||||
Call<List<DropdownDTO>> getCustomerPets(@Path("customerId") Long customerId);
|
||||
|
||||
// Get adoption pets
|
||||
@GET("api/v1/dropdowns/adoption-pets")
|
||||
Call<List<DropdownDTO>> getAdoptionPets(@Query("storeId") Long storeId);
|
||||
|
||||
// Get pet dropdowns
|
||||
@GET("api/v1/dropdowns/pets")
|
||||
Call<List<DropdownDTO>> getPetDropdowns();
|
||||
|
||||
// Get pet species dropdowns
|
||||
@GET("api/v1/dropdowns/pet-species")
|
||||
Call<List<DropdownDTO>> getPetSpeciesDropdowns();
|
||||
|
||||
// Get pet breeds dropdowns
|
||||
@GET("api/v1/dropdowns/pet-breeds")
|
||||
Call<List<DropdownDTO>> getPetBreedsDropdowns(@Query("species") String species);
|
||||
|
||||
@@ -81,5 +86,4 @@ public interface PetApi {
|
||||
// Delete pet image
|
||||
@DELETE("api/v1/pets/{id}/image")
|
||||
Call<Void> deletePetImage(@Path("id") Long id);
|
||||
|
||||
}
|
||||
|
||||
@@ -9,9 +9,12 @@ import retrofit2.http.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
// api calls for products
|
||||
public interface ProductApi {
|
||||
// endpoint for downloading the product's image file
|
||||
String PRODUCT_IMAGE_PATH = "api/v1/products/%d/image";
|
||||
|
||||
// Get all products with filters
|
||||
@GET("api/v1/products")
|
||||
Call<PageResponse<ProductDTO>> getAllProducts(
|
||||
@Query("q") String query,
|
||||
@@ -20,28 +23,36 @@ public interface ProductApi {
|
||||
@Query("size") int size,
|
||||
@Query("sort") String sort);
|
||||
|
||||
// Get product by id
|
||||
@GET("api/v1/products/{id}")
|
||||
Call<ProductDTO> getProductById(@Path("id") Long id);
|
||||
|
||||
// Create product
|
||||
@POST("api/v1/products")
|
||||
Call<ProductDTO> createProduct(@Body ProductDTO product);
|
||||
|
||||
// Update product
|
||||
@PUT("api/v1/products/{id}")
|
||||
Call<ProductDTO> updateProduct(@Path("id") Long id, @Body ProductDTO product);
|
||||
|
||||
// Delete product
|
||||
@DELETE("api/v1/products/{id}")
|
||||
Call<Void> deleteProduct(@Path("id") Long id);
|
||||
|
||||
// Upload product image
|
||||
@Multipart
|
||||
@POST("api/v1/products/{id}/image")
|
||||
Call<Void> uploadProductImage(@Path("id") Long id, @Part MultipartBody.Part image);
|
||||
|
||||
// Delete product image
|
||||
@DELETE("api/v1/products/{id}/image")
|
||||
Call<Void> deleteProductImage(@Path("id") Long id);
|
||||
|
||||
// Get product dropdowns
|
||||
@GET("api/v1/dropdowns/products")
|
||||
Call<List<DropdownDTO>> getProductDropdowns();
|
||||
|
||||
// Get category dropdowns
|
||||
@GET("api/v1/dropdowns/categories")
|
||||
Call<List<DropdownDTO>> getCategoryDropdowns();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,10 @@ import com.example.petstoremobile.dtos.ProductSupplierDTO;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.*;
|
||||
|
||||
// api calls for product-supplier relationships
|
||||
public interface ProductSupplierApi {
|
||||
|
||||
// Get all product-suppliers with filters
|
||||
@GET("api/v1/product-suppliers")
|
||||
Call<PageResponse<ProductSupplierDTO>> getAllProductSuppliers(
|
||||
@Query("page") int page,
|
||||
@@ -17,24 +19,30 @@ public interface ProductSupplierApi {
|
||||
@Query("supplierId") Long supplierId,
|
||||
@Query("sort") String sort);
|
||||
|
||||
// Get product-supplier by composite id
|
||||
@GET("api/v1/product-suppliers/{productId}/{supplierId}")
|
||||
Call<ProductSupplierDTO> getProductSupplierById(
|
||||
@Path("productId") Long productId,
|
||||
@Path("supplierId") Long supplierId);
|
||||
|
||||
// Create product-supplier
|
||||
@POST("api/v1/product-suppliers")
|
||||
Call<ProductSupplierDTO> createProductSupplier(@Body ProductSupplierDTO dto);
|
||||
|
||||
// Update product-supplier
|
||||
@PUT("api/v1/product-suppliers/{productId}/{supplierId}")
|
||||
Call<ProductSupplierDTO> updateProductSupplier(
|
||||
@Path("productId") Long productId,
|
||||
@Path("supplierId") Long supplierId,
|
||||
@Body ProductSupplierDTO dto);
|
||||
|
||||
// Delete product-supplier
|
||||
@DELETE("api/v1/product-suppliers/{productId}/{supplierId}")
|
||||
Call<Void> deleteProductSupplier(
|
||||
@Path("productId") Long productId,
|
||||
@Path("supplierId") Long supplierId);
|
||||
|
||||
// Bulk delete product-suppliers
|
||||
@HTTP(method = "DELETE", path = "api/v1/product-suppliers", hasBody = true)
|
||||
Call<Void> bulkDeleteProductSuppliers(@Body BulkDeleteRequest request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,10 @@ import retrofit2.http.GET;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls for purchase orders
|
||||
public interface PurchaseOrderApi {
|
||||
|
||||
// Get all purchase orders with filters
|
||||
@GET("api/v1/purchase-orders")
|
||||
Call<PageResponse<PurchaseOrderDTO>> getAllPurchaseOrders(
|
||||
@Query("page") int page,
|
||||
@@ -17,6 +19,7 @@ public interface PurchaseOrderApi {
|
||||
@Query("storeId") Long storeId,
|
||||
@Query("sort") String sort);
|
||||
|
||||
// Get purchase order by id
|
||||
@GET("api/v1/purchase-orders/{id}")
|
||||
Call<PurchaseOrderDTO> getPurchaseOrderById(@Path("id") Long id);
|
||||
}
|
||||
@@ -6,20 +6,26 @@ import retrofit2.http.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
// api calls for refunds
|
||||
public interface RefundApi {
|
||||
|
||||
// Get all refunds
|
||||
@GET("api/v1/refunds")
|
||||
Call<List<RefundDTO>> getAllRefunds();
|
||||
|
||||
// Get refund by id
|
||||
@GET("api/v1/refunds/{id}")
|
||||
Call<RefundDTO> getRefundById(@Path("id") Long id);
|
||||
|
||||
// Create refund
|
||||
@POST("api/v1/refunds")
|
||||
Call<RefundDTO> createRefund(@Body RefundDTO refund);
|
||||
|
||||
// Update refund
|
||||
@PUT("api/v1/refunds/{id}")
|
||||
Call<RefundDTO> updateRefund(@Path("id") Long id, @Body RefundDTO refund);
|
||||
|
||||
// Delete refund
|
||||
@DELETE("api/v1/refunds/{id}")
|
||||
Call<Void> deleteRefund(@Path("id") Long id);
|
||||
}
|
||||
@@ -10,8 +10,10 @@ import retrofit2.http.POST;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls for sales
|
||||
public interface SaleApi {
|
||||
|
||||
// Get all sales with filters
|
||||
@GET("api/v1/sales")
|
||||
Call<PageResponse<SaleDTO>> getAllSales(
|
||||
@Query("page") int page,
|
||||
@@ -23,9 +25,11 @@ public interface SaleApi {
|
||||
@Query("customerId") Long customerId,
|
||||
@Query("sort") String sort);
|
||||
|
||||
// Get sale by id
|
||||
@GET("api/v1/sales/{id}")
|
||||
Call<SaleDTO> getSaleById(@Path("id") Long id);
|
||||
|
||||
// Create sale
|
||||
@POST("api/v1/sales")
|
||||
Call<SaleDTO> createSale(@Body SaleDTO sale);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import retrofit2.http.PUT;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
//api calls to CRUD services
|
||||
// api calls to CRUD services
|
||||
public interface ServiceApi {
|
||||
// Get all services
|
||||
@GET("api/v1/services")
|
||||
|
||||
@@ -11,16 +11,20 @@ import retrofit2.http.GET;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls for stores
|
||||
public interface StoreApi {
|
||||
|
||||
// Get all stores with pagination
|
||||
@GET("api/v1/stores")
|
||||
Call<PageResponse<StoreDTO>> getAllStores(
|
||||
@Query("page") int page,
|
||||
@Query("size") int size);
|
||||
|
||||
// Get store dropdowns
|
||||
@GET("api/v1/dropdowns/stores")
|
||||
Call<List<DropdownDTO>> getStoreDropdowns();
|
||||
|
||||
// Get employees of a specific store
|
||||
@GET("api/v1/dropdowns/stores/{storeId}/employees")
|
||||
Call<List<DropdownDTO>> getStoreEmployees(@Path("storeId") Long storeId);
|
||||
}
|
||||
|
||||
@@ -7,9 +7,12 @@ import retrofit2.Call;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls for users
|
||||
public interface UserApi {
|
||||
// endpoint for downloading the user's avatar file
|
||||
String AVATAR_PATH = "api/v1/users/%d/avatar/file";
|
||||
|
||||
// Get all users with filters
|
||||
@GET("api/v1/users")
|
||||
Call<PageResponse<UserDTO>> getUsers(@Query("role") String role, @Query("page") int page, @Query("size") int size);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import javax.inject.Singleton;
|
||||
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext;
|
||||
|
||||
//Used to save and retrieve login data
|
||||
@Singleton
|
||||
public class TokenManager {
|
||||
public static final String ACTION_FORCE_LOGOUT = "com.example.petstoremobile.ACTION_FORCE_LOGOUT";
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for activity logs.
|
||||
*/
|
||||
public class ActivityLogDTO {
|
||||
private Long logId;
|
||||
private String activity;
|
||||
|
||||
@@ -2,6 +2,9 @@ package com.example.petstoremobile.dtos;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for pet adoptions.
|
||||
*/
|
||||
public class AdoptionDTO {
|
||||
|
||||
private Long adoptionId;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for appointments.
|
||||
*/
|
||||
public class AppointmentDTO {
|
||||
|
||||
private Long appointmentId;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
//Used to send login data to the backend
|
||||
/**
|
||||
* Data Transfer Object for authentication credentials.
|
||||
*/
|
||||
public class AuthDTO {
|
||||
public static class LoginRequest {
|
||||
private String username;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Response containing the URL of a newly uploaded avatar.
|
||||
*/
|
||||
public class AvatarUploadResponse {
|
||||
private String avatarUrl;
|
||||
private String message;
|
||||
|
||||
@@ -2,6 +2,9 @@ package com.example.petstoremobile.dtos;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Request body for deleting multiple records at once.
|
||||
*/
|
||||
public class BulkDeleteRequest {
|
||||
private List<Long> ids;
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for product categories.
|
||||
*/
|
||||
public class CategoryDTO {
|
||||
private Long categoryId;
|
||||
private String categoryName;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for chat conversations.
|
||||
*/
|
||||
public class ConversationDTO {
|
||||
private Long id;
|
||||
private Long customerId;
|
||||
|
||||
@@ -2,6 +2,9 @@ package com.example.petstoremobile.dtos;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for coupons.
|
||||
*/
|
||||
public class CouponDTO {
|
||||
private Long couponId;
|
||||
private String couponCode;
|
||||
|
||||
@@ -2,6 +2,9 @@ package com.example.petstoremobile.dtos;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for customers.
|
||||
*/
|
||||
public class CustomerDTO {
|
||||
@SerializedName("id")
|
||||
private Long customerId;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for simple dropdown selection lists.
|
||||
*/
|
||||
public class DropdownDTO {
|
||||
private Long id;
|
||||
private String label;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for employees.
|
||||
*/
|
||||
public class EmployeeDTO {
|
||||
|
||||
private Long id;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
//Used to get messages of any errors from the backend
|
||||
|
||||
/**
|
||||
* Used to get messages of any errors from the backend.
|
||||
*/
|
||||
public class ErrorResponse {
|
||||
private String message;
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for inventory stock.
|
||||
*/
|
||||
public class InventoryDTO {
|
||||
// Response fields (from backend InventoryResponse)
|
||||
private Long inventoryId;
|
||||
|
||||
@@ -2,6 +2,9 @@ package com.example.petstoremobile.dtos;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for chat messages.
|
||||
*/
|
||||
public class MessageDTO {
|
||||
|
||||
@SerializedName("id")
|
||||
|
||||
@@ -2,7 +2,9 @@ package com.example.petstoremobile.dtos;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
//Used to get data from the API
|
||||
/**
|
||||
* Generic response wrapper for paginated API results.
|
||||
*/
|
||||
public class PageResponse<T> {
|
||||
private List<T> content;
|
||||
private int totalPages;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object representing a pet.
|
||||
*/
|
||||
public class PetDTO {
|
||||
private Long petId;
|
||||
private String petName;
|
||||
|
||||
@@ -2,6 +2,9 @@ package com.example.petstoremobile.dtos;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for products.
|
||||
*/
|
||||
public class ProductDTO {
|
||||
private Long prodId;
|
||||
private String prodName;
|
||||
|
||||
@@ -2,6 +2,9 @@ package com.example.petstoremobile.dtos;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for mapping products to suppliers.
|
||||
*/
|
||||
public class ProductSupplierDTO {
|
||||
private Long productId;
|
||||
private String productName;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for purchase orders.
|
||||
*/
|
||||
public class PurchaseOrderDTO {
|
||||
private Long purchaseOrderId;
|
||||
private Long supId;
|
||||
|
||||
@@ -2,6 +2,9 @@ package com.example.petstoremobile.dtos;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for refund processing.
|
||||
*/
|
||||
public class RefundDTO {
|
||||
// Response fields
|
||||
private Long id;
|
||||
|
||||
@@ -3,6 +3,9 @@ package com.example.petstoremobile.dtos;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for sales transactions.
|
||||
*/
|
||||
public class SaleDTO {
|
||||
// Response fields
|
||||
private Long saleId;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Request body for sending a new chat message.
|
||||
*/
|
||||
public class SendMessageRequest {
|
||||
|
||||
private String content;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for services.
|
||||
*/
|
||||
public class ServiceDTO {
|
||||
private Long serviceId;
|
||||
private String serviceName;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for store information.
|
||||
*/
|
||||
public class StoreDTO {
|
||||
private Long storeId;
|
||||
private String storeName;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for suppliers.
|
||||
*/
|
||||
public class SupplierDTO {
|
||||
private Long supId;
|
||||
private String supCompany;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Request body for updating chat conversation status.
|
||||
*/
|
||||
public class UpdateConversationStatusRequest {
|
||||
private String status;
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for user account details.
|
||||
*/
|
||||
public class UserDTO {
|
||||
private Long id;
|
||||
private String username;
|
||||
|
||||
@@ -65,6 +65,9 @@ import okhttp3.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.ResponseBody;
|
||||
|
||||
/**
|
||||
* Fragment for handling customer support chat.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickListener, StompChatManager.MessageListener,
|
||||
StompChatManager.ConversationListener, StompChatManager.ConnectionListener {
|
||||
@@ -90,6 +93,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
private StompChatManager stompChatManager;
|
||||
private ActivityResultLauncher<Intent> attachmentLauncher;
|
||||
|
||||
/**
|
||||
* Initializes the view model and attachment launcher.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -105,6 +111,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and sets up UI event listeners.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
ViewGroup container, Bundle savedInstanceState) {
|
||||
@@ -137,6 +146,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the logic to open and close the chat drawer.
|
||||
*/
|
||||
private void setupDrawerToggles() {
|
||||
binding.headerActiveChats.setOnClickListener(v -> {
|
||||
if (binding.rvActiveChats.getVisibility() == View.VISIBLE) {
|
||||
@@ -159,6 +171,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the adapters and layout managers for chat lists and message history.
|
||||
*/
|
||||
private void setupRecyclerViews() {
|
||||
activeChatAdapter = new ChatAdapter(activeChatList, this);
|
||||
binding.rvActiveChats.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
@@ -187,6 +202,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
setConversationActive(false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a full-screen image preview for message attachments.
|
||||
*/
|
||||
private void showFullScreenImage(Message message) {
|
||||
if (baseUrl == null || message.getId() == null) return;
|
||||
|
||||
@@ -208,6 +226,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the download process for a message attachment.
|
||||
*/
|
||||
private void downloadFile(Message message) {
|
||||
if (message.getId() == null) return;
|
||||
|
||||
@@ -227,6 +248,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the downloaded file body to the device's downloads folder.
|
||||
*/
|
||||
private void saveFileToDownloads(ResponseBody body, String fileName, String mimeType) {
|
||||
android.os.Handler mainHandler = new android.os.Handler(android.os.Looper.getMainLooper());
|
||||
new Thread(() -> {
|
||||
@@ -270,6 +294,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes LiveData from the ViewModel to update chat lists and messages.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getActiveChats().observe(getViewLifecycleOwner(), list -> {
|
||||
activeChatList.clear();
|
||||
@@ -300,12 +327,18 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
viewModel.getIsLoading().observe(getViewLifecycleOwner(), this::setLoading);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the visibility of the progress bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the chat header and input state if the active conversation changes.
|
||||
*/
|
||||
private void updateTitleAndStateIfActive(List<Chat> list) {
|
||||
if (activeConversationId != null) {
|
||||
for (Chat chat : list) {
|
||||
@@ -318,6 +351,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads initial chat data and establishes WebSocket connection.
|
||||
*/
|
||||
private void loadInitialData() {
|
||||
String token = tokenManager.getToken();
|
||||
Long currentUserId = tokenManager.getUserId();
|
||||
@@ -353,6 +389,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicks on a chat from the drawer to switch the active conversation.
|
||||
*/
|
||||
@Override
|
||||
public void onChatClick(Chat chat) {
|
||||
activeConversationId = Long.parseLong(chat.getChatId());
|
||||
@@ -368,6 +407,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
viewModel.loadMessageHistory(activeConversationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the active chat conversation.
|
||||
*/
|
||||
private void closeChat() {
|
||||
if (activeConversationId == null) return;
|
||||
|
||||
@@ -392,6 +434,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a text message to the active conversation.
|
||||
*/
|
||||
private void sendMessage() {
|
||||
if (activeConversationId == null) return;
|
||||
String text = binding.etMessage.getText().toString().trim();
|
||||
@@ -408,12 +453,18 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a file picker to select an attachment.
|
||||
*/
|
||||
private void selectAttachment() {
|
||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
intent.setType("*/*");
|
||||
attachmentLauncher.launch(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a preview of the selected attachment.
|
||||
*/
|
||||
private void showAttachmentPreview(Uri uri) {
|
||||
pendingAttachmentUri = uri;
|
||||
binding.layoutAttachmentPreview.setVisibility(View.VISIBLE);
|
||||
@@ -427,11 +478,17 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the currently selected attachment from the preview.
|
||||
*/
|
||||
private void removeAttachment() {
|
||||
pendingAttachmentUri = null;
|
||||
binding.layoutAttachmentPreview.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message with a file attachment.
|
||||
*/
|
||||
private void sendWithAttachment(Uri uri) {
|
||||
if (activeConversationId == null) return;
|
||||
|
||||
@@ -468,6 +525,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when a new message is received through the WebSocket.
|
||||
*/
|
||||
@Override
|
||||
public void onMessageReceived(MessageDTO dto) {
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
@@ -478,6 +538,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when a conversation's status or last message is updated.
|
||||
*/
|
||||
@Override
|
||||
public void onConversationUpdated(ConversationDTO dto) {
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
@@ -489,6 +552,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the WebSocket connection is successfully opened.
|
||||
*/
|
||||
@Override
|
||||
public void onSocketOpened() {
|
||||
if (!isAdded()) return;
|
||||
@@ -498,12 +564,18 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the WebSocket connection is closed.
|
||||
*/
|
||||
@Override
|
||||
public void onSocketClosed() {
|
||||
if (!isAdded()) return;
|
||||
requireActivity().runOnUiThread(viewModel::loadConversations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when a WebSocket error occurs.
|
||||
*/
|
||||
@Override
|
||||
public void onSocketError() {
|
||||
if (!isAdded()) return;
|
||||
@@ -513,6 +585,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls the message list to the most recent message.
|
||||
*/
|
||||
private void scrollToBottom() {
|
||||
if (!messageList.isEmpty()) {
|
||||
binding.rvMessages.post(() ->
|
||||
@@ -520,6 +595,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the UI state based on whether a conversation is active and its status.
|
||||
*/
|
||||
private void setConversationActive(boolean active, String status) {
|
||||
boolean isClosed = "CLOSED".equalsIgnoreCase(status);
|
||||
UIUtils.setViewsEnabled(active && !isClosed, binding.btnSend, binding.etMessage, binding.btnAttach);
|
||||
@@ -541,6 +619,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up resources and disconnects the WebSocket when the view is destroyed.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -23,7 +23,9 @@ import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
//The Fragment for the displaying the list of entities to be viewed
|
||||
/**
|
||||
* Fragment that serves as a container for various list-based screens, providing a navigation drawer.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class ListFragment extends Fragment {
|
||||
|
||||
@@ -97,6 +99,9 @@ public class ListFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding when the view is destroyed.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -173,12 +173,18 @@ public class ProfileFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the visibility of the progress bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding when the view is destroyed.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -34,6 +34,9 @@ import java.util.List;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for viewing application activity logs with various filtering options.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class ActivityLogFragment extends Fragment {
|
||||
private FragmentActivityLogBinding binding;
|
||||
@@ -46,6 +49,9 @@ public class ActivityLogFragment extends Fragment {
|
||||
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Inflates the layout, checks for admin access, and initializes ViewModel and UI components.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -69,12 +75,18 @@ public class ActivityLogFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers initial data loading after the view is created.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
viewModel.loadInitialData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new ActivityLogAdapter(logList);
|
||||
binding.recyclerViewActivityLog.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
@@ -97,6 +109,9 @@ public class ActivityLogFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up filters for logs, including search, role, store, and date range.
|
||||
*/
|
||||
private void setupFilters() {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter,
|
||||
binding.etSearchLog, binding.spinnerRoleFilter, binding.spinnerStoreFilter);
|
||||
@@ -123,6 +138,9 @@ public class ActivityLogFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a date picker dialog and updates the selected start or end date.
|
||||
*/
|
||||
private void showDatePicker(boolean isStart) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
new DatePickerDialog(requireContext(), (view, year, month, day) -> {
|
||||
@@ -141,12 +159,18 @@ public class ActivityLogFragment extends Fragment {
|
||||
}, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles store selection from the filter spinner.
|
||||
*/
|
||||
private void onStoreSelected() {
|
||||
int pos = binding.spinnerStoreFilter.getSelectedItemPosition();
|
||||
Long storeId = (pos > 0 && !storeList.isEmpty()) ? storeList.get(pos - 1).getId() : null;
|
||||
viewModel.setStoreFilter(storeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for log list updates, store options, and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getLogs().observe(getViewLifecycleOwner(), list -> {
|
||||
logList.clear();
|
||||
@@ -164,6 +188,9 @@ public class ActivityLogFragment extends Fragment {
|
||||
binding.swipeRefreshActivityLog.setRefreshing(loading));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -44,6 +44,9 @@ import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a list of adoptions with a calendar view and filtering.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdoptionClickListener {
|
||||
|
||||
@@ -58,12 +61,18 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Initializes the ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(AdoptionListViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and sets up UI components, calendar, and observers.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -88,6 +97,9 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for adoption list, stores, and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getAdoptions().observe(getViewLifecycleOwner(), list -> {
|
||||
adoptionList.clear();
|
||||
@@ -106,6 +118,9 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the bulk delete handler for multiple adoption record deletion.
|
||||
*/
|
||||
private void setupBulkDelete() {
|
||||
bulkDeleteHandler = new BulkDeleteHandler(
|
||||
this,
|
||||
@@ -119,6 +134,9 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads adoption data and stores when the fragment resumes.
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@@ -126,6 +144,9 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
if (!isStaff()) viewModel.loadStores();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles between month and week display modes for the calendar.
|
||||
*/
|
||||
private void toggleCalendarMode() {
|
||||
isMonthMode = !isMonthMode;
|
||||
binding.calendarViewAdoption.state().edit()
|
||||
@@ -133,6 +154,9 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the filter visibility toggle, considering user roles.
|
||||
*/
|
||||
private void setupFilterToggle() {
|
||||
if (isStaff()) {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilterAdoption, binding.layoutFilterAdoption,
|
||||
@@ -144,10 +168,16 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the currently logged-in user has the STAFF role.
|
||||
*/
|
||||
private boolean isStaff() {
|
||||
return "STAFF".equalsIgnoreCase(tokenManager.getRole());
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the calendar view for date-based filtering.
|
||||
*/
|
||||
private void setupCalendar() {
|
||||
binding.calendarViewAdoption.setOnDateChangedListener((widget, date, selected) -> {
|
||||
if (selected) {
|
||||
@@ -164,6 +194,9 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates calendar decorators to highlight dates with adoptions.
|
||||
*/
|
||||
private void updateCalendarDecorators() {
|
||||
HashSet<CalendarDay> datesWithAdoptions = new HashSet<>();
|
||||
for (AdoptionDTO adoption : adoptionList) {
|
||||
@@ -184,6 +217,9 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
binding.calendarViewAdoption.addDecorator(new EventDecorator(Color.RED, datesWithAdoptions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new AdoptionAdapter(adoptionList, this);
|
||||
binding.recyclerViewAdoptions.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
@@ -206,23 +242,38 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search input listener.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchAdoption, () -> loadAdoptions(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the status filter spinner.
|
||||
*/
|
||||
private void setupStatusFilter() {
|
||||
String[] statuses = {"All Statuses", "Completed", "Pending", "Missed", "Cancelled"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatusAdoption, statuses, () -> loadAdoptions(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the store filter spinner.
|
||||
*/
|
||||
private void setupStoreFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerStoreAdoption, () -> loadAdoptions(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshAdoption.setOnRefreshListener(() -> loadAdoptions(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads adoption data based on current filters, search query, and selected date.
|
||||
*/
|
||||
private void loadAdoptions(boolean reset) {
|
||||
String query = binding.etSearchAdoption.getText().toString().trim();
|
||||
String status = binding.spinnerStatusAdoption.getSelectedItem() != null ? binding.spinnerStatusAdoption.getSelectedItem().toString() : "All Statuses";
|
||||
@@ -250,6 +301,9 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
viewModel.loadAdoptions(reset, query, status, storeId, selectedDateString, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the adoption detail screen.
|
||||
*/
|
||||
private void openDetail(int position) {
|
||||
Bundle args = new Bundle();
|
||||
if (position != -1) {
|
||||
@@ -259,9 +313,15 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_adoption_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles adoption item clicks by opening details.
|
||||
*/
|
||||
@Override
|
||||
public void onAdoptionClick(int position) { openDetail(position); }
|
||||
|
||||
/**
|
||||
* Forwards selection changes to the bulk delete handler.
|
||||
*/
|
||||
@Override
|
||||
public void onSelectionChanged(int selectedCount) {
|
||||
if (bulkDeleteHandler != null) {
|
||||
@@ -269,6 +329,9 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -18,6 +18,9 @@ import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Fragment for displaying business analytics, including revenue, transactions, and product performance.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class AnalyticsFragment extends Fragment {
|
||||
|
||||
@@ -31,6 +34,9 @@ public class AnalyticsFragment extends Fragment {
|
||||
private static final String[] TOP_N_OPTIONS = {"5", "10", "15", "20"};
|
||||
private static final int[] TOP_N_VALUES = { 5, 10, 15, 20 };
|
||||
|
||||
/**
|
||||
* Inflates the layout, initializes ViewModel, and sets up UI components and filters.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -51,6 +57,9 @@ public class AnalyticsFragment extends Fragment {
|
||||
private static final int COLOR_SELECTED = 0xFF4ECDC4;
|
||||
private static final int COLOR_UNSELECTED = 0xFFCBD5E1;
|
||||
|
||||
/**
|
||||
* Configures the view mode toggle buttons (My Analytics vs Store Analytics).
|
||||
*/
|
||||
private void setupViewModeToggle() {
|
||||
updateViewModeButtonStyles(viewModel.getViewMode());
|
||||
|
||||
@@ -67,6 +76,9 @@ public class AnalyticsFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the styles of the view mode buttons based on the selected mode.
|
||||
*/
|
||||
private void updateViewModeButtonStyles(String mode) {
|
||||
binding.btnMyAnalytics.setBackgroundTintList(
|
||||
android.content.res.ColorStateList.valueOf(mode.equals("mine") ? COLOR_SELECTED : COLOR_UNSELECTED));
|
||||
@@ -74,6 +86,9 @@ public class AnalyticsFragment extends Fragment {
|
||||
android.content.res.ColorStateList.valueOf(mode.equals("store") ? COLOR_SELECTED : COLOR_UNSELECTED));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the visibility of the store filter based on the user's role and selected view mode.
|
||||
*/
|
||||
private void updateStoreFilterVisibility(String mode) {
|
||||
boolean isAdmin = "ADMIN".equalsIgnoreCase(tokenManager.getRole());
|
||||
int vis = (isAdmin && mode.equals("store")) ? View.VISIBLE : View.GONE;
|
||||
@@ -81,8 +96,10 @@ public class AnalyticsFragment extends Fragment {
|
||||
binding.spinnerFilterStore.setVisibility(vis);
|
||||
}
|
||||
|
||||
// Filter Panel
|
||||
|
||||
/**
|
||||
* Configures the filter panel, including date pickers, presets, and action buttons.
|
||||
*/
|
||||
private void setupFilterPanel() {
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerTopN, TOP_N_OPTIONS);
|
||||
|
||||
@@ -111,12 +128,18 @@ public class AnalyticsFragment extends Fragment {
|
||||
binding.btnFilterReset.setOnClickListener(v -> resetFilters());
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the visibility of the filter content section.
|
||||
*/
|
||||
private void toggleFilters() {
|
||||
filtersExpanded = !filtersExpanded;
|
||||
binding.llFilterContent.setVisibility(filtersExpanded ? View.VISIBLE : View.GONE);
|
||||
binding.tvFilterToggleIcon.setText(filtersExpanded ? "▲" : "▼");
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a date range preset to the filter fields.
|
||||
*/
|
||||
private void applyPreset(int startOffset, int endOffset) {
|
||||
binding.etFilterStartDate.setText(getDateString(startOffset));
|
||||
binding.etFilterEndDate.setText(getDateString(endOffset));
|
||||
@@ -124,6 +147,9 @@ public class AnalyticsFragment extends Fragment {
|
||||
applyFiltersFromUI();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads filter values from the UI and applies them to the ViewModel.
|
||||
*/
|
||||
private void applyFiltersFromUI() {
|
||||
AnalyticsViewModel.FilterState filter = new AnalyticsViewModel.FilterState();
|
||||
filter.startDate = binding.etFilterStartDate.getText().toString().trim();
|
||||
@@ -142,6 +168,9 @@ public class AnalyticsFragment extends Fragment {
|
||||
viewModel.applyFilter(filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all filters to their default values.
|
||||
*/
|
||||
private void resetFilters() {
|
||||
binding.etFilterStartDate.setText("");
|
||||
binding.etFilterEndDate.setText("");
|
||||
@@ -152,6 +181,9 @@ public class AnalyticsFragment extends Fragment {
|
||||
viewModel.resetFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the text summary of the currently selected date range.
|
||||
*/
|
||||
private void updateFilterSummary() {
|
||||
String start = binding.etFilterStartDate.getText().toString().trim();
|
||||
String end = binding.etFilterEndDate.getText().toString().trim();
|
||||
@@ -166,10 +198,16 @@ public class AnalyticsFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a date string into a shorter version for display.
|
||||
*/
|
||||
private String shortDate(String date) {
|
||||
return (date != null && date.length() >= 10) ? date.substring(5) : date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a formatted date string for a given day.
|
||||
*/
|
||||
private String getDateString(int offsetDays) {
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.add(Calendar.DAY_OF_YEAR, offsetDays);
|
||||
@@ -177,8 +215,10 @@ public class AnalyticsFragment extends Fragment {
|
||||
c.get(Calendar.YEAR), c.get(Calendar.MONTH) + 1, c.get(Calendar.DAY_OF_MONTH));
|
||||
}
|
||||
|
||||
// ViewModel Observation
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for analytics data, loading status, errors, and filter options.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getAnalyticsData().observe(getViewLifecycleOwner(), this::computeAndDisplay);
|
||||
|
||||
@@ -216,14 +256,19 @@ public class AnalyticsFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
// Display
|
||||
|
||||
/**
|
||||
* Computes and displays analytics data in summary cards and bar charts.
|
||||
*/
|
||||
private void computeAndDisplay(AnalyticsViewModel.AnalyticsData data) {
|
||||
if (data == null) return;
|
||||
|
||||
@@ -312,8 +357,10 @@ public class AnalyticsFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
// Chart Helpers
|
||||
|
||||
/**
|
||||
* Dynamically adds a bar chart row to a given layout container.
|
||||
*/
|
||||
private void addBarRow(LinearLayout parent, String label, String value, float ratio, String color) {
|
||||
if (getContext() == null) return;
|
||||
LinearLayout row = new LinearLayout(getContext());
|
||||
@@ -359,6 +406,9 @@ public class AnalyticsFragment extends Fragment {
|
||||
parent.addView(row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an empty message row to a given layout container.
|
||||
*/
|
||||
private void addEmptyRow(LinearLayout parent, String message) {
|
||||
if (getContext() == null) return;
|
||||
TextView tv = new TextView(getContext());
|
||||
@@ -368,6 +418,9 @@ public class AnalyticsFragment extends Fragment {
|
||||
parent.addView(tv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an error message and updates UI to reflect the error state.
|
||||
*/
|
||||
private void showError(String msg) {
|
||||
if (getContext() == null || binding == null) return;
|
||||
binding.tvTotalRevenue.setText("Error");
|
||||
|
||||
@@ -45,6 +45,9 @@ import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a list of appointments with calendar integration and filtering.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class AppointmentFragment extends Fragment implements AppointmentAdapter.OnAppointmentClickListener {
|
||||
|
||||
@@ -63,6 +66,9 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
private Long currentUserId = null;
|
||||
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
|
||||
|
||||
/**
|
||||
* Initializes ViewModels for appointment and authentication data.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -70,6 +76,9 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
authViewModel = new ViewModelProvider(this).get(AuthViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and sets up UI components, calendar, and observers.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -97,6 +106,9 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for appointment list, stores, and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getAppointments().observe(getViewLifecycleOwner(), list -> {
|
||||
appointmentList.clear();
|
||||
@@ -115,6 +127,9 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the bulk delete handler for multiple appointment deletion.
|
||||
*/
|
||||
private void setupBulkDelete() {
|
||||
bulkDeleteHandler = new BulkDeleteHandler(
|
||||
this,
|
||||
@@ -128,6 +143,9 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads appointment data and stores when the fragment resumes.
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@@ -135,6 +153,9 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
if (!isStaff()) viewModel.loadStores();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles between month and week display modes for the calendar.
|
||||
*/
|
||||
private void toggleCalendarMode() {
|
||||
isMonthMode = !isMonthMode;
|
||||
binding.calendarView.state().edit()
|
||||
@@ -142,12 +163,18 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the "My Appointments" filter button.
|
||||
*/
|
||||
private void setupMyAppointmentFilter() {
|
||||
binding.btnMyAppointments.setOnClickListener(v -> {
|
||||
loadAppointmentData(true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads information about the currently logged-in user.
|
||||
*/
|
||||
private void loadCurrentUserInfo() {
|
||||
authViewModel.getMe().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
@@ -156,6 +183,9 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the filter visibility toggle.
|
||||
*/
|
||||
private void setupFilterToggle() {
|
||||
if (isStaff()) {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchAppointment, binding.spinnerStatus);
|
||||
@@ -166,6 +196,9 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the calendar view for date-based filtering.
|
||||
*/
|
||||
private void setupCalendar() {
|
||||
binding.calendarView.setOnDateChangedListener((widget, date, selected) -> {
|
||||
if (selected) {
|
||||
@@ -182,6 +215,9 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates calendar decorators to highlight dates with appointments.
|
||||
*/
|
||||
private void updateCalendarDecorators() {
|
||||
HashSet<CalendarDay> datesWithAppointments = new HashSet<>();
|
||||
for (AppointmentDTO appointment : appointmentList) {
|
||||
@@ -200,23 +236,38 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
binding.calendarView.addDecorator(new EventDecorator(Color.RED, datesWithAppointments));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search input listener.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchAppointment, () -> loadAppointmentData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the status filter spinner.
|
||||
*/
|
||||
private void setupStatusFilter() {
|
||||
String[] statuses = {"All Statuses", "Booked", "Completed", "Cancelled", "Missed"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatus, statuses, () -> loadAppointmentData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the store filter spinner.
|
||||
*/
|
||||
private void setupStoreFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadAppointmentData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshAppointment.setOnRefreshListener(() -> loadAppointmentData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the appointment detail screen.
|
||||
*/
|
||||
private void openAppointmentDetails(int position) {
|
||||
Bundle args = new Bundle();
|
||||
if (position != -1) {
|
||||
@@ -226,11 +277,17 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_appointment_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles appointment item clicks by opening details.
|
||||
*/
|
||||
@Override
|
||||
public void onAppointmentClick(int position) {
|
||||
openAppointmentDetails(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards selection changes to the bulk delete handler.
|
||||
*/
|
||||
@Override
|
||||
public void onSelectionChanged(int count) {
|
||||
if (bulkDeleteHandler != null) {
|
||||
@@ -238,10 +295,16 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the currently logged-in user has the STAFF role.
|
||||
*/
|
||||
private boolean isStaff() {
|
||||
return "STAFF".equalsIgnoreCase(tokenManager.getRole());
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads appointment data based on current filters, search query, and selected date.
|
||||
*/
|
||||
private void loadAppointmentData(boolean reset) {
|
||||
String query = binding.etSearchAppointment.getText().toString().trim();
|
||||
String status = binding.spinnerStatus.getSelectedItem() != null ? binding.spinnerStatus.getSelectedItem().toString() : "All Statuses";
|
||||
@@ -274,6 +337,9 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
viewModel.loadAppointments(reset, query, status, storeId, selectedDateString, employeeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new AppointmentAdapter(appointmentList, this);
|
||||
binding.recyclerViewAppointments.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
@@ -296,6 +362,9 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -27,6 +27,9 @@ import java.util.List;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a list of coupons.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class CouponFragment extends Fragment implements CouponAdapter.OnCouponClickListener {
|
||||
private FragmentCouponBinding binding;
|
||||
@@ -34,6 +37,9 @@ public class CouponFragment extends Fragment implements CouponAdapter.OnCouponCl
|
||||
private CouponAdapter adapter;
|
||||
private final List<CouponDTO> couponList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Inflates the layout, initializes ViewModel, and sets up UI components.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -59,6 +65,9 @@ public class CouponFragment extends Fragment implements CouponAdapter.OnCouponCl
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for coupon list updates and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getCoupons().observe(getViewLifecycleOwner(), list -> {
|
||||
couponList.clear();
|
||||
@@ -71,6 +80,9 @@ public class CouponFragment extends Fragment implements CouponAdapter.OnCouponCl
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new CouponAdapter(couponList, this);
|
||||
binding.recyclerViewCoupon.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
@@ -93,24 +105,39 @@ public class CouponFragment extends Fragment implements CouponAdapter.OnCouponCl
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the status filter spinner.
|
||||
*/
|
||||
private void setupStatusFilter() {
|
||||
String[] statuses = {"All Statuses", "Active", "Inactive"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatusCoupon, statuses, () -> applyFilters(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the discount type filter spinner.
|
||||
*/
|
||||
private void setupTypeFilter() {
|
||||
String[] types = {"All Types", "FIXED", "PERCENT"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerTypeCoupon, types, () -> applyFilters(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search input listener.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchCoupon, () -> applyFilters(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshCoupon.setOnRefreshListener(() -> applyFilters(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies filters and loads the coupon list.
|
||||
*/
|
||||
private void applyFilters(boolean reset) {
|
||||
String statusStr = binding.spinnerStatusCoupon.getSelectedItem() != null ?
|
||||
binding.spinnerStatusCoupon.getSelectedItem().toString() : "All Statuses";
|
||||
@@ -125,22 +152,34 @@ public class CouponFragment extends Fragment implements CouponAdapter.OnCouponCl
|
||||
viewModel.loadCoupons(reset, active, discountType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the coupon detail screen.
|
||||
*/
|
||||
private void openDetail(long id) {
|
||||
Bundle args = new Bundle();
|
||||
args.putLong("couponId", id);
|
||||
Navigation.findNavController(requireView()).navigate(R.id.couponDetailFragment, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles coupon item clicks by opening details.
|
||||
*/
|
||||
@Override
|
||||
public void onCouponClick(CouponDTO coupon) {
|
||||
openDetail(coupon.getCouponId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the bulk delete button based on selection count.
|
||||
*/
|
||||
@Override
|
||||
public void onSelectionChanged(int count) {
|
||||
binding.btnBulkDeleteCoupons.setVisibility(count > 0 ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog for deleting multiple coupons.
|
||||
*/
|
||||
private void confirmBulkDelete() {
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle("Confirm Bulk Delete")
|
||||
|
||||
@@ -22,6 +22,9 @@ import java.util.*;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a list of customers.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class CustomerFragment extends Fragment implements CustomerAdapter.OnCustomerClickListener {
|
||||
|
||||
@@ -33,6 +36,9 @@ public class CustomerFragment extends Fragment implements CustomerAdapter.OnCust
|
||||
@Inject @Named("baseUrl") String baseUrl;
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Inflates the layout, initializes ViewModel, and sets up UI components and filters.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -56,6 +62,9 @@ public class CustomerFragment extends Fragment implements CustomerAdapter.OnCust
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for customer list updates and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getFilteredCustomers().observe(getViewLifecycleOwner(), list -> {
|
||||
customerList.clear();
|
||||
@@ -68,6 +77,9 @@ public class CustomerFragment extends Fragment implements CustomerAdapter.OnCust
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new CustomerAdapter(customerList, this);
|
||||
adapter.setBaseUrl(baseUrl);
|
||||
@@ -92,15 +104,24 @@ public class CustomerFragment extends Fragment implements CustomerAdapter.OnCust
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the status filter spinner.
|
||||
*/
|
||||
private void setupStatusFilter() {
|
||||
String[] statuses = {"All Statuses", "Active", "Inactive"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatusCustomer, statuses, this::applyFilters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search input listener.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchCustomer, this::applyFilters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies filters and triggers data reloading or filtering in ViewModel.
|
||||
*/
|
||||
private void applyFilters() {
|
||||
String query = binding.etSearchCustomer.getText().toString().trim();
|
||||
String status = binding.spinnerStatusCustomer.getSelectedItem() != null ?
|
||||
@@ -108,10 +129,16 @@ public class CustomerFragment extends Fragment implements CustomerAdapter.OnCust
|
||||
viewModel.filter(query, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshCustomer.setOnRefreshListener(() -> viewModel.loadCustomers(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the customer detail screen.
|
||||
*/
|
||||
private void openDetail(int position) {
|
||||
Bundle args = new Bundle();
|
||||
if (position != -1) {
|
||||
@@ -129,11 +156,17 @@ public class CustomerFragment extends Fragment implements CustomerAdapter.OnCust
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_customer_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles customer item clicks by opening details.
|
||||
*/
|
||||
@Override
|
||||
public void onCustomerClick(int position) {
|
||||
openDetail(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -32,6 +32,9 @@ import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing inventory items.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class InventoryFragment extends Fragment implements InventoryAdapter.OnInventoryClickListener {
|
||||
|
||||
@@ -43,12 +46,18 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Initializes the ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(InventoryListViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and sets up UI components, filters, and observers.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -71,6 +80,9 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for inventory list updates, store list, and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getInventory().observe(getViewLifecycleOwner(), list -> {
|
||||
inventoryList.clear();
|
||||
@@ -88,6 +100,9 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the bulk delete handler for multiple inventory item deletion.
|
||||
*/
|
||||
private void setupBulkDelete() {
|
||||
bulkDeleteHandler = new BulkDeleteHandler(
|
||||
this,
|
||||
@@ -101,18 +116,27 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads store data if necessary when the fragment resumes.
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (!isStaff()) viewModel.loadStores();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the filter visibility toggle, considering user roles.
|
||||
*/
|
||||
private void setupFilterToggle() {
|
||||
if (isStaff()) {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchInventory);
|
||||
@@ -122,18 +146,30 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the currently logged-in user has the STAFF role.
|
||||
*/
|
||||
private boolean isStaff() {
|
||||
return "STAFF".equalsIgnoreCase(tokenManager.getRole());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search input listener.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchInventory, () -> loadInventory(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the store filter spinner.
|
||||
*/
|
||||
private void setupStoreFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadInventory(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new InventoryAdapter(inventoryList, this);
|
||||
binding.recyclerViewInventory.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
@@ -156,10 +192,16 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshInventory.setOnRefreshListener(() -> loadInventory(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads inventory data based on current search query and store filter.
|
||||
*/
|
||||
private void loadInventory(boolean reset) {
|
||||
String query = binding.etSearchInventory != null ? binding.etSearchInventory.getText().toString().trim() : "";
|
||||
if (query.isEmpty()) query = null;
|
||||
@@ -178,6 +220,9 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
viewModel.loadInventory(reset, query, storeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the inventory detail screen.
|
||||
*/
|
||||
private void openDetail(InventoryDTO inv) {
|
||||
Bundle args = new Bundle();
|
||||
if (inv != null) {
|
||||
@@ -186,6 +231,9 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_inventory_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles inventory item clicks by opening details.
|
||||
*/
|
||||
@Override
|
||||
public void onInventoryClick(int position) {
|
||||
if (position >= 0 && position < inventoryList.size()) {
|
||||
@@ -193,6 +241,9 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards selection changes to the bulk delete handler.
|
||||
*/
|
||||
@Override
|
||||
public void onSelectionChanged(int selectedCount) {
|
||||
if (bulkDeleteHandler != null) {
|
||||
|
||||
@@ -33,6 +33,9 @@ import javax.inject.Named;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a list of pets.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class PetFragment extends Fragment implements PetAdapter.OnPetClickListener {
|
||||
private FragmentPetBinding binding;
|
||||
@@ -44,12 +47,18 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
@Inject @Named("baseUrl") String baseUrl;
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Initializes the view model.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(PetListViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and initializes UI components and filters.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -72,6 +81,9 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes LiveData from the ViewModel to update the list and filter options.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getPets().observe(getViewLifecycleOwner(), list -> {
|
||||
petList.clear();
|
||||
@@ -94,6 +106,9 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the handler for bulk deletion of pets.
|
||||
*/
|
||||
private void setupBulkDelete() {
|
||||
bulkDeleteHandler = new BulkDeleteHandler(
|
||||
this,
|
||||
@@ -107,6 +122,9 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes pet data and filters when the fragment is resumed.
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@@ -115,6 +133,9 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
if (!isStaff()) viewModel.loadStores();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the visibility of filters based on the user's role.
|
||||
*/
|
||||
private void setupFilterToggle() {
|
||||
if (isStaff()) {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchPet,
|
||||
@@ -126,32 +147,53 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current user has the 'STAFF' role.
|
||||
*/
|
||||
private boolean isStaff() {
|
||||
return "STAFF".equalsIgnoreCase(tokenManager.getRole());
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches search functionality to the search input field.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchPet, () -> loadPetData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the status filter spinner.
|
||||
*/
|
||||
private void setupStatusFilter() {
|
||||
String[] statuses = {"All Statuses", "Available", "Adopted", "Owned", "Pending"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatus, statuses, () -> loadPetData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the species filter spinner.
|
||||
*/
|
||||
private void setupSpeciesFilter() {
|
||||
String[] initial = {"All Species"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerSpecies, initial, () -> loadPetData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the store filter spinner.
|
||||
*/
|
||||
private void setupStoreFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadPetData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshPet.setOnRefreshListener(() -> loadPetData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers loading of pet data from the backend with current filters.
|
||||
*/
|
||||
private void loadPetData(boolean reset) {
|
||||
String query = binding.etSearchPet.getText().toString().trim();
|
||||
String status = binding.spinnerStatus.getSelectedItem() != null ? binding.spinnerStatus.getSelectedItem().toString() : "All Statuses";
|
||||
@@ -171,6 +213,9 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
viewModel.loadPets(reset, query, status, species, storeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new PetAdapter(petList, this);
|
||||
adapter.setBaseUrl(baseUrl);
|
||||
@@ -195,6 +240,9 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the profile view of a specific pet.
|
||||
*/
|
||||
private void openPetProfile(int position) {
|
||||
Bundle args = new Bundle();
|
||||
PetDTO pet = petList.get(position);
|
||||
@@ -202,15 +250,24 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_pet_profile, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the screen for adding a new pet.
|
||||
*/
|
||||
private void openPetDetails() {
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_pet_detail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicks on individual pets in the list.
|
||||
*/
|
||||
@Override
|
||||
public void onPetClick(int position) {
|
||||
openPetProfile(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the bulk delete handler when item selection changes.
|
||||
*/
|
||||
@Override
|
||||
public void onSelectionChanged(int selectedCount) {
|
||||
if (bulkDeleteHandler != null) {
|
||||
@@ -218,6 +275,9 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding when the view is destroyed.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -31,6 +31,9 @@ import javax.inject.Named;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a list of products.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class ProductFragment extends Fragment implements ProductAdapter.OnProductClickListener {
|
||||
|
||||
@@ -41,12 +44,18 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
|
||||
|
||||
@Inject @Named("baseUrl") String baseUrl;
|
||||
|
||||
/**
|
||||
* Initializes the ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(ProductListViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and sets up UI components.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -66,6 +75,9 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for product list, categories, and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getProducts().observe(getViewLifecycleOwner(), list -> {
|
||||
productList.clear();
|
||||
@@ -83,6 +95,9 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads product data and categories when the fragment resumes.
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@@ -90,23 +105,38 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
|
||||
viewModel.loadCategories();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the filter visibility toggle.
|
||||
*/
|
||||
private void setupFilterToggle() {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter,
|
||||
binding.etSearchProduct, binding.spinnerCategory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search input listener.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchProduct, () -> loadProductData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the category filter spinner.
|
||||
*/
|
||||
private void setupCategoryFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerCategory, () -> loadProductData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshProduct.setOnRefreshListener(() -> loadProductData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads product data based on current filters and search query.
|
||||
*/
|
||||
private void loadProductData(boolean reset) {
|
||||
String query = binding.etSearchProduct.getText().toString().trim();
|
||||
if (query.isEmpty()) query = null;
|
||||
@@ -120,6 +150,9 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
|
||||
viewModel.loadProducts(reset, query, categoryId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new ProductAdapter(productList, this);
|
||||
adapter.setBaseUrl(baseUrl);
|
||||
@@ -143,6 +176,9 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the product detail screen.
|
||||
*/
|
||||
private void openProductDetails(int position) {
|
||||
Bundle args = new Bundle();
|
||||
if (position != -1) {
|
||||
@@ -152,11 +188,17 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_product_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles product item clicks by opening details.
|
||||
*/
|
||||
@Override
|
||||
public void onProductClick(int position) {
|
||||
openProductDetails(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -29,6 +29,9 @@ import java.util.List;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing the relationships between products and suppliers.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class ProductSupplierFragment extends Fragment
|
||||
implements ProductSupplierAdapter.OnProductSupplierClickListener {
|
||||
@@ -40,12 +43,18 @@ public class ProductSupplierFragment extends Fragment
|
||||
private ProductSupplierListViewModel viewModel;
|
||||
private BulkDeleteHandler bulkDeleteHandler;
|
||||
|
||||
/**
|
||||
* Initializes the ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(ProductSupplierListViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and sets up UI components, filters, and observers.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -67,6 +76,9 @@ public class ProductSupplierFragment extends Fragment
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for product-supplier list, products, suppliers, and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getProductSuppliers().observe(getViewLifecycleOwner(), list -> {
|
||||
psList.clear();
|
||||
@@ -89,6 +101,9 @@ public class ProductSupplierFragment extends Fragment
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the bulk delete handler for multiple product-supplier relationship deletion.
|
||||
*/
|
||||
private void setupBulkDelete() {
|
||||
bulkDeleteHandler = new BulkDeleteHandler(
|
||||
this,
|
||||
@@ -102,6 +117,9 @@ public class ProductSupplierFragment extends Fragment
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads data and filter options when the fragment resumes.
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@@ -109,17 +127,26 @@ public class ProductSupplierFragment extends Fragment
|
||||
viewModel.loadFilterData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the filter visibility toggle.
|
||||
*/
|
||||
private void setupFilterToggle() {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchPS,
|
||||
binding.spinnerProduct, binding.spinnerSupplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new ProductSupplierAdapter(psList, this);
|
||||
binding.recyclerViewPS.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
@@ -142,22 +169,37 @@ public class ProductSupplierFragment extends Fragment
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search input listener.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchPS, () -> loadData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the product filter spinner.
|
||||
*/
|
||||
private void setupProductFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerProduct, () -> loadData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the supplier filter spinner.
|
||||
*/
|
||||
private void setupSupplierFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerSupplier, () -> loadData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshPS.setOnRefreshListener(() -> loadData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads product-supplier data based on current search query and filters.
|
||||
*/
|
||||
private void loadData(boolean reset) {
|
||||
String query = binding.etSearchPS.getText().toString().trim();
|
||||
if (query.isEmpty()) query = null;
|
||||
@@ -177,6 +219,9 @@ public class ProductSupplierFragment extends Fragment
|
||||
viewModel.loadProductSuppliers(reset, query, productId, supplierId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the product-supplier detail screen.
|
||||
*/
|
||||
private void openDetail(int position) {
|
||||
Bundle args = new Bundle();
|
||||
if (position != -1) {
|
||||
@@ -187,9 +232,15 @@ public class ProductSupplierFragment extends Fragment
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_product_supplier_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles product-supplier item clicks by opening details.
|
||||
*/
|
||||
@Override
|
||||
public void onProductSupplierClick(int position) { openDetail(position); }
|
||||
|
||||
/**
|
||||
* Forwards selection changes to the bulk delete handler.
|
||||
*/
|
||||
@Override
|
||||
public void onSelectionChanged(int count) {
|
||||
if (bulkDeleteHandler != null) {
|
||||
|
||||
@@ -30,6 +30,9 @@ import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a list of purchase orders.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class PurchaseOrderFragment extends Fragment
|
||||
implements PurchaseOrderAdapter.OnPurchaseOrderClickListener {
|
||||
@@ -41,12 +44,18 @@ public class PurchaseOrderFragment extends Fragment
|
||||
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Initializes the ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(PurchaseOrderListViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and sets up UI components, filters, and observers.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -64,6 +73,9 @@ public class PurchaseOrderFragment extends Fragment
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for purchase order list, stores, and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getPurchaseOrders().observe(getViewLifecycleOwner(), list -> {
|
||||
poList.clear();
|
||||
@@ -81,6 +93,9 @@ public class PurchaseOrderFragment extends Fragment
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads data and stores if necessary when the fragment resumes.
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@@ -88,6 +103,9 @@ public class PurchaseOrderFragment extends Fragment
|
||||
if (!isStaff()) viewModel.loadStores();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the filter visibility toggle
|
||||
*/
|
||||
private void setupFilterToggle() {
|
||||
if (isStaff()) {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchPO);
|
||||
@@ -97,18 +115,30 @@ public class PurchaseOrderFragment extends Fragment
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the currently logged-in user has the STAFF role.
|
||||
*/
|
||||
private boolean isStaff() {
|
||||
return "STAFF".equalsIgnoreCase(tokenManager.getRole());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search input listener.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchPO, () -> loadData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the store filter spinner.
|
||||
*/
|
||||
private void setupStoreFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new PurchaseOrderAdapter(poList, this);
|
||||
binding.recyclerViewPO.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
@@ -131,10 +161,16 @@ public class PurchaseOrderFragment extends Fragment
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshPO.setOnRefreshListener(() -> loadData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads purchase order data based on current search query and store filter.
|
||||
*/
|
||||
private void loadData(boolean reset) {
|
||||
String query = binding.etSearchPO != null ? binding.etSearchPO.getText().toString().trim() : "";
|
||||
if (query.isEmpty()) query = null;
|
||||
@@ -153,6 +189,9 @@ public class PurchaseOrderFragment extends Fragment
|
||||
viewModel.loadPurchaseOrders(reset, query, storeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the purchase order detail screen.
|
||||
*/
|
||||
private void openDetail(int position) {
|
||||
Bundle args = new Bundle();
|
||||
PurchaseOrderDTO po = poList.get(position);
|
||||
@@ -160,11 +199,17 @@ public class PurchaseOrderFragment extends Fragment
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_purchase_order_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles purchase order item clicks by opening details.
|
||||
*/
|
||||
@Override
|
||||
public void onPurchaseOrderClick(int position) {
|
||||
openDetail(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -31,6 +31,9 @@ import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a list of sales.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickListener {
|
||||
|
||||
@@ -41,6 +44,9 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Inflates the layout for this fragment.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -48,6 +54,9 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes UI components and observers after the view is created.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
@@ -79,6 +88,9 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_refund));
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes LiveData from the ViewModel to update the list and filter options.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getSales().observe(getViewLifecycleOwner(), list -> {
|
||||
saleList.clear();
|
||||
@@ -101,6 +113,9 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes lookup data when the fragment is resumed.
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@@ -108,6 +123,9 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
viewModel.loadCustomers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the visibility of filters.
|
||||
*/
|
||||
private void setupFilterToggle() {
|
||||
if (isStaff()) {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchSale,
|
||||
@@ -119,32 +137,53 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current user has the 'STAFF' role.
|
||||
*/
|
||||
private boolean isStaff() {
|
||||
return "STAFF".equalsIgnoreCase(tokenManager.getRole());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current user has the 'ADMIN' role.
|
||||
*/
|
||||
private boolean isAdmin() {
|
||||
return "ADMIN".equalsIgnoreCase(tokenManager.getRole());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the store filter spinner.
|
||||
*/
|
||||
private void setupStoreFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadSales(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the payment method filter spinner.
|
||||
*/
|
||||
private void setupPaymentMethodFilter() {
|
||||
String[] paymentMethods = {"All Payments", "Cash", "Card"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerPaymentMethod, paymentMethods, () -> loadSales(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the refund status filter spinner.
|
||||
*/
|
||||
private void setupRefundStatusFilter() {
|
||||
String[] refundStatuses = {"All Status", "Sale", "Refund"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerRefundStatus, refundStatuses, () -> loadSales(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the customer filter spinner.
|
||||
*/
|
||||
private void setupCustomerFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerCustomer, () -> loadSales(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new SaleAdapter(saleList, this);
|
||||
binding.recyclerViewSales.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
@@ -168,14 +207,23 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches search functionality to the search input field.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchSale, () -> loadSales(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshSale.setOnRefreshListener(() -> loadSales(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers loading of sale data from the backend with current filters.
|
||||
*/
|
||||
private void loadSales(boolean reset) {
|
||||
String query = binding.etSearchSale != null ? binding.etSearchSale.getText().toString().trim() : "";
|
||||
if (query.isEmpty()) query = null;
|
||||
@@ -210,6 +258,9 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
viewModel.loadSales(reset, query, paymentMethod, storeId, isRefund, customerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicks on individual sales in the list.
|
||||
*/
|
||||
@Override
|
||||
public void onSaleClick(int position) {
|
||||
if (position < 0 || position >= saleList.size()) return;
|
||||
@@ -225,6 +276,9 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_sale_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding when the view is destroyed.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -39,12 +39,18 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
|
||||
private ServiceListViewModel viewModel;
|
||||
private BulkDeleteHandler bulkDeleteHandler;
|
||||
|
||||
/**
|
||||
* Initializes the ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(ServiceListViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and sets up UI components and observers.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -67,6 +73,9 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for service list updates and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getServices().observe(getViewLifecycleOwner(), list -> {
|
||||
serviceList.clear();
|
||||
@@ -79,6 +88,9 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the bulk delete handler for multiple service deletion.
|
||||
*/
|
||||
private void setupBulkDelete() {
|
||||
bulkDeleteHandler = new BulkDeleteHandler(
|
||||
this,
|
||||
@@ -92,20 +104,32 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the filter visibility toggle.
|
||||
*/
|
||||
private void setupFilterToggle() {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search input listener.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchService, () -> loadServices(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new ServiceAdapter(serviceList, this);
|
||||
binding.recyclerViewServices.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
@@ -128,16 +152,25 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshService.setOnRefreshListener(() -> loadServices(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads service data based on current filters and search query.
|
||||
*/
|
||||
private void loadServices(boolean reset) {
|
||||
String query = binding.etSearchService.getText().toString().trim();
|
||||
if (query.isEmpty()) query = null;
|
||||
viewModel.loadServices(reset, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the service detail screen.
|
||||
*/
|
||||
private void openDetail(ServiceDTO service) {
|
||||
Bundle args = new Bundle();
|
||||
if (service != null) {
|
||||
@@ -146,6 +179,9 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_service_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles service item clicks by opening details.
|
||||
*/
|
||||
@Override
|
||||
public void onServiceClick(int position) {
|
||||
if (position >= 0 && position < serviceList.size()) {
|
||||
@@ -153,6 +189,9 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards selection changes to the bulk delete handler.
|
||||
*/
|
||||
@Override
|
||||
public void onSelectionChanged(int count) {
|
||||
if (bulkDeleteHandler != null) {
|
||||
|
||||
@@ -24,6 +24,9 @@ import java.util.*;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a list of staff members.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmployeeClickListener {
|
||||
|
||||
@@ -35,6 +38,9 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
|
||||
@Inject @Named("baseUrl") String baseUrl;
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Inflates the layout and initializes UI components, filters, and observers.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -60,6 +66,9 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes LiveData from the ViewModel to update the list and filter options.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getFilteredEmployees().observe(getViewLifecycleOwner(), list -> {
|
||||
staffList.clear();
|
||||
@@ -77,6 +86,9 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new EmployeeAdapter(staffList, this);
|
||||
adapter.setBaseUrl(baseUrl);
|
||||
@@ -101,19 +113,31 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the status filter spinner.
|
||||
*/
|
||||
private void setupStatusFilter() {
|
||||
String[] statuses = {"All Statuses", "Active", "Inactive"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatusStaff, statuses, this::applyFilters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the store filter spinner.
|
||||
*/
|
||||
private void setupStoreFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerStoreStaff, this::applyFilters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches search functionality to the search input field.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchStaff, this::applyFilters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the selected filters and triggers a data reload from the view model.
|
||||
*/
|
||||
private void applyFilters() {
|
||||
String query = binding.etSearchStaff.getText().toString().trim();
|
||||
String status = binding.spinnerStatusStaff.getSelectedItem() != null ?
|
||||
@@ -128,10 +152,16 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
|
||||
viewModel.filter(query, storeId, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshStaff.setOnRefreshListener(() -> viewModel.loadStaff(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the staff detail screen for adding or editing an employee.
|
||||
*/
|
||||
private void openDetail(int position) {
|
||||
Bundle args = new Bundle();
|
||||
if (position != -1) {
|
||||
@@ -149,11 +179,17 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_staff_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicks on individual staff members in the list.
|
||||
*/
|
||||
@Override
|
||||
public void onEmployeeClick(int position) {
|
||||
openDetail(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding when the view is destroyed.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -27,6 +27,9 @@ import java.util.List;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a list of suppliers.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupplierClickListener {
|
||||
|
||||
@@ -36,12 +39,18 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
|
||||
private SupplierListViewModel viewModel;
|
||||
private BulkDeleteHandler bulkDeleteHandler;
|
||||
|
||||
/**
|
||||
* Initializes the ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(SupplierListViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and sets up UI components, bulk delete, and observers.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -63,6 +72,9 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for supplier list updates and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getSuppliers().observe(getViewLifecycleOwner(), list -> {
|
||||
supplierList.clear();
|
||||
@@ -75,6 +87,9 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the bulk delete handler for multiple supplier deletion.
|
||||
*/
|
||||
private void setupBulkDelete() {
|
||||
bulkDeleteHandler = new BulkDeleteHandler(
|
||||
this,
|
||||
@@ -88,24 +103,39 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the filter visibility toggle.
|
||||
*/
|
||||
private void setupFilterToggle() {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchSupplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search input listener.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchSupplier, () -> loadSupplierData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshSupplier.setOnRefreshListener(() -> loadSupplierData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the supplier detail screen.
|
||||
*/
|
||||
private void openSupplierDetails(int position) {
|
||||
Bundle args = new Bundle();
|
||||
if (position != -1) {
|
||||
@@ -115,11 +145,17 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_supplier_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles supplier item clicks by opening details.
|
||||
*/
|
||||
@Override
|
||||
public void onSupplierClick(int position) {
|
||||
openSupplierDetails(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards selection changes to the bulk delete handler.
|
||||
*/
|
||||
@Override
|
||||
public void onSelectionChanged(int count) {
|
||||
if (bulkDeleteHandler != null) {
|
||||
@@ -127,12 +163,18 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads supplier data based on current search query.
|
||||
*/
|
||||
private void loadSupplierData(boolean reset) {
|
||||
String query = binding.etSearchSupplier != null ? binding.etSearchSupplier.getText().toString().trim() : "";
|
||||
if (query.isEmpty()) query = null;
|
||||
viewModel.loadSuppliers(reset, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new SupplierAdapter(supplierList, this);
|
||||
binding.recyclerViewSuppliers.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
|
||||
@@ -39,12 +39,18 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Initializes the fragment and its ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(AdoptionDetailViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for the fragment
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -52,6 +58,9 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up UI components, observers, and loads initial data after the view is created.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
@@ -68,6 +77,9 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
binding.btnDeleteAdoption.setOnClickListener(v -> confirmDelete());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up observers for ViewModel to update the UI dynamically.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getViewState().observe(getViewLifecycleOwner(), this::applyViewState);
|
||||
|
||||
@@ -113,18 +125,27 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the loading bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the spinners with data and selection listeners.
|
||||
*/
|
||||
private void setupSpinners() {
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerAdoptionStatus, new String[]{});
|
||||
|
||||
@@ -141,11 +162,17 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
SpinnerUtils.setOnIndexSelectedListener(binding.spinnerAdoptionStatus, p -> notifyDateStatusChange());
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the date picker for the adoption date field.
|
||||
*/
|
||||
private void setupDatePicker() {
|
||||
binding.etAdoptionDate.setOnClickListener(v ->
|
||||
UIUtils.showDatePicker(requireContext(), binding.etAdoptionDate, this::notifyDateStatusChange));
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the ViewModel when the date or status changes to update available options.
|
||||
*/
|
||||
private void notifyDateStatusChange() {
|
||||
if (isUpdatingUI) return;
|
||||
String date = binding.etAdoptionDate.getText().toString();
|
||||
@@ -154,6 +181,9 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
viewModel.onDateChanged(date, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses fragment arguments to determine if we are editing an existing record.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.containsKey("adoptionId")) {
|
||||
@@ -164,6 +194,9 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
viewModel.setAdoptionId(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the adoption data from the backend.
|
||||
*/
|
||||
private void loadAdoptionData() {
|
||||
viewModel.loadAdoption().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
@@ -174,6 +207,10 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the current ViewState to the UI elements.
|
||||
* This handles enabling/disabling views and setting text/selections based on state.
|
||||
*/
|
||||
private void applyViewState(AdoptionDetailViewModel.ViewState state) {
|
||||
isUpdatingUI = true;
|
||||
|
||||
@@ -224,10 +261,16 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
isUpdatingUI = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the currently logged-in user has the STAFF role.
|
||||
*/
|
||||
private boolean isStaff() {
|
||||
return "STAFF".equalsIgnoreCase(tokenManager.getRole());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input and saves the adoption record.
|
||||
*/
|
||||
private void saveAdoption() {
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerAdoptionCustomer, "Customer")) return;
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerAdoptionPet, "Pet")) return;
|
||||
@@ -275,6 +318,9 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog before deleting the adoption record.
|
||||
*/
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Adoption Record", () ->
|
||||
viewModel.deleteAdoption().observe(getViewLifecycleOwner(), resource -> {
|
||||
@@ -289,6 +335,9 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous screen.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
@@ -47,18 +47,27 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Initializes the fragment and its ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(AppointmentDetailViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for the fragment
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
binding = FragmentAppointmentDetailBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up UI components, observers, and loads initial data after the view is created.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
@@ -73,12 +82,18 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
binding.btnDeleteAppointment.setOnClickListener(v -> confirmDelete());
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the spinners with data and selection listeners.
|
||||
*/
|
||||
private void setupSpinners() {
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerAppointmentStatus, new String[]{});
|
||||
|
||||
@@ -108,11 +123,17 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
SpinnerUtils.setOnIndexSelectedListener(binding.spinnerAppointmentStatus, p -> notifyDateTimeStatusChange());
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the date picker for the appointment date field.
|
||||
*/
|
||||
private void setupDatePicker() {
|
||||
binding.etAppointmentDate.setOnClickListener(v ->
|
||||
UIUtils.showDatePicker(requireContext(), binding.etAppointmentDate, this::notifyDateTimeStatusChange));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up observers for ViewModel to update the UI dynamically.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getViewState().observe(getViewLifecycleOwner(), this::applyViewState);
|
||||
|
||||
@@ -147,12 +168,19 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the loading bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the current ViewState to the UI elements.
|
||||
* This handles enabling/disabling views and setting text/selections based on state.
|
||||
*/
|
||||
private void applyViewState(AppointmentDetailViewModel.ViewState state) {
|
||||
isUpdatingUI = true;
|
||||
|
||||
@@ -201,10 +229,16 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
isUpdatingUI = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the currently logged-in user has the STAFF role.
|
||||
*/
|
||||
private boolean isStaff() {
|
||||
return "STAFF".equalsIgnoreCase(tokenManager.getRole());
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the ViewModel when the date, time, or status changes to update available options.
|
||||
*/
|
||||
private void notifyDateTimeStatusChange() {
|
||||
if (isUpdatingUI) return;
|
||||
|
||||
@@ -215,6 +249,9 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
viewModel.onDateOrTimeChanged(date, time, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses fragment arguments to determine if we are editing an existing record.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.containsKey("appointmentId")) {
|
||||
@@ -225,6 +262,9 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the appointment data from the backend.
|
||||
*/
|
||||
private void loadAppointmentData() {
|
||||
viewModel.loadAppointment().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
@@ -244,6 +284,9 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input and saves the appointment record.
|
||||
*/
|
||||
private void saveAppointment() {
|
||||
if (!validateRequiredFields()) return;
|
||||
|
||||
@@ -275,6 +318,9 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that all required fields are selected or filled.
|
||||
*/
|
||||
private boolean validateRequiredFields() {
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerCustomer, "Customer")) return false;
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerStore, "Store")) return false;
|
||||
@@ -284,15 +330,24 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the selected hour and minute from spinners into a time string.
|
||||
*/
|
||||
private String buildTimeString() {
|
||||
return DateTimeUtils.formatTime(HOURS[binding.spinnerHour.getSelectedItemPosition()], MINUTES[binding.spinnerMinute.getSelectedItemPosition()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles errors that occur during the save process.
|
||||
*/
|
||||
private void handleSaveError(String errorMessage) {
|
||||
if (errorMessage != null && errorMessage.toLowerCase().contains("not available")) showNoAvailabilityDialog();
|
||||
else Toast.makeText(getContext(), errorMessage != null ? errorMessage : "Error saving", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a dialog when the selected time slot is not available.
|
||||
*/
|
||||
private void showNoAvailabilityDialog() {
|
||||
new androidx.appcompat.app.AlertDialog.Builder(requireContext())
|
||||
.setTitle("No Availability")
|
||||
@@ -301,6 +356,9 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
.setNegativeButton("Cancel Booking", (d, w) -> navigateBack()).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog before deleting the appointment record.
|
||||
*/
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Appointment", () ->
|
||||
viewModel.deleteAppointment().observe(getViewLifecycleOwner(), resource -> {
|
||||
@@ -310,10 +368,16 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous screen.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a time string and sets the hour and minute spinners accordingly.
|
||||
*/
|
||||
private void parseAndSetTimeSpinners(String time) {
|
||||
int[] parsedTime = DateTimeUtils.parseTimeString(time);
|
||||
if (parsedTime == null) return;
|
||||
|
||||
@@ -29,12 +29,18 @@ import java.util.Locale;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and editing coupon details.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class CouponDetailFragment extends Fragment {
|
||||
private FragmentCouponDetailBinding binding;
|
||||
private CouponDetailViewModel viewModel;
|
||||
private long couponId = -1;
|
||||
|
||||
/**
|
||||
* Inflates the layout, initializes ViewModel, and sets up UI components.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -74,10 +80,16 @@ public class CouponDetailFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the discount type spinner with options.
|
||||
*/
|
||||
private void setupDiscountTypeSpinner() {
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerDiscountTypeDetail, new String[]{"FIXED", "PERCENT"});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a date picker for an EditText field.
|
||||
*/
|
||||
private void setupDatePicker(android.widget.EditText editText, android.widget.EditText dependOn, Runnable onDateSet) {
|
||||
editText.setFocusable(false);
|
||||
editText.setClickable(true);
|
||||
@@ -91,6 +103,9 @@ public class CouponDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads coupon details from the backend and populates the UI.
|
||||
*/
|
||||
private void loadCouponDetails() {
|
||||
binding.tvCouponId.setText(DateTimeUtils.formatId(couponId));
|
||||
binding.tvCouponId.setVisibility(View.VISIBLE);
|
||||
@@ -111,6 +126,9 @@ public class CouponDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input and saves the coupon record.
|
||||
*/
|
||||
private void saveCoupon() {
|
||||
if (!InputValidator.isNotEmpty(binding.etCouponCodeDetail, "Coupon Code")) return;
|
||||
if (!InputValidator.isGreaterThanZero(binding.etDiscountValueDetail, "Discount Value")) return;
|
||||
@@ -156,6 +174,9 @@ public class CouponDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog before deleting the coupon.
|
||||
*/
|
||||
private void confirmDelete() {
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle("Delete Coupon")
|
||||
|
||||
@@ -21,6 +21,9 @@ import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and editing customer details.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class CustomerDetailFragment extends Fragment {
|
||||
|
||||
@@ -31,6 +34,9 @@ public class CustomerDetailFragment extends Fragment {
|
||||
|
||||
private final String[] STATUSES = {"Active", "Inactive"};
|
||||
|
||||
/**
|
||||
* Inflates the layout, initializes ViewModel, and sets up UI components.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -49,10 +55,16 @@ public class CustomerDetailFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the spinners with data.
|
||||
*/
|
||||
private void setupSpinners() {
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerCustomerStatus, STATUSES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses fragment arguments to determine if we are editing an existing record.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.getBoolean("isEditing", false)) {
|
||||
@@ -86,6 +98,9 @@ public class CustomerDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads customer data from the backend.
|
||||
*/
|
||||
private void loadCustomerData(long id) {
|
||||
viewModel.loadCustomer(id).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null) {
|
||||
@@ -105,12 +120,18 @@ public class CustomerDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the loading bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input and saves the customer record.
|
||||
*/
|
||||
private void save() {
|
||||
if (!InputValidator.isNotEmpty(binding.etCustomerUsername, "Username")) return;
|
||||
|
||||
@@ -160,6 +181,9 @@ public class CustomerDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog before deleting the customer.
|
||||
*/
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Customer", () ->
|
||||
viewModel.deleteCustomer().observe(getViewLifecycleOwner(), resource -> {
|
||||
@@ -175,10 +199,16 @@ public class CustomerDetailFragment extends Fragment {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous screen.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -37,12 +37,18 @@ public class InventoryDetailFragment extends Fragment {
|
||||
private long preselectedStoreId = -1;
|
||||
private long preselectedProductId = -1;
|
||||
|
||||
/**
|
||||
* Initializes the fragment and its ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(InventoryDetailViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for the fragment.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -50,6 +56,9 @@ public class InventoryDetailFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up UI components, observers, and loads initial data after the view is created.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
@@ -63,23 +72,35 @@ public class InventoryDetailFragment extends Fragment {
|
||||
binding.btnDeleteInventory.setOnClickListener(v -> confirmDelete());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up observers for ViewModel to update the UI dynamically.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getStoreList().observe(getViewLifecycleOwner(), list -> refreshStoreSpinner());
|
||||
viewModel.getProductList().observe(getViewLifecycleOwner(), list -> refreshProductSpinner());
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the loading bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads initial data for the store and product spinners.
|
||||
*/
|
||||
private void loadSpinnersData() {
|
||||
viewModel.loadStores().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
@@ -97,18 +118,27 @@ public class InventoryDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the store spinner with the current data.
|
||||
*/
|
||||
private void refreshStoreSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerInventoryStore, viewModel.getStoreList().getValue(),
|
||||
DropdownDTO::getLabel, "-- Select Store --",
|
||||
preselectedStoreId, DropdownDTO::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the product spinner with the current data.
|
||||
*/
|
||||
private void refreshProductSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerInventoryProduct, viewModel.getProductList().getValue(),
|
||||
DropdownDTO::getLabel, "-- Select Product --",
|
||||
preselectedProductId, DropdownDTO::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses fragment arguments to determine if we are editing an existing record.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle args = getArguments();
|
||||
if (args != null && args.containsKey("inventoryId")) {
|
||||
@@ -131,6 +161,9 @@ public class InventoryDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the inventory data from the backend.
|
||||
*/
|
||||
private void loadInventoryData() {
|
||||
viewModel.loadInventory().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
@@ -149,6 +182,9 @@ public class InventoryDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input and saves the inventory record.
|
||||
*/
|
||||
private void saveInventory() {
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerInventoryStore, "Store")) return;
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerInventoryProduct, "Product")) return;
|
||||
@@ -176,10 +212,16 @@ public class InventoryDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog before deleting the inventory record.
|
||||
*/
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Inventory Item", this::deleteInventory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the ViewModel to delete the inventory record.
|
||||
*/
|
||||
private void deleteInventory() {
|
||||
setButtonsEnabled(false);
|
||||
viewModel.deleteInventory().observe(getViewLifecycleOwner(), resource -> {
|
||||
@@ -197,10 +239,16 @@ public class InventoryDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous screen.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the action buttons.
|
||||
*/
|
||||
private void setButtonsEnabled(boolean enabled) {
|
||||
UIUtils.setViewsEnabled(enabled, binding.btnSaveInventory, binding.btnDeleteInventory, binding.btnInventoryBack);
|
||||
}
|
||||
|
||||
@@ -58,6 +58,9 @@ public class ProductDetailFragment extends Fragment {
|
||||
@Inject @Named("baseUrl") String baseUrl;
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Initializes the fragment, its ViewModel, and the ImagePickerHelper.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -88,6 +91,9 @@ public class ProductDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for the fragment.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -95,6 +101,9 @@ public class ProductDetailFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up UI components, observers, and handles arguments after the view is created.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
@@ -108,6 +117,9 @@ public class ProductDetailFragment extends Fragment {
|
||||
binding.ivProductImage.setOnClickListener(v -> imagePickerHelper.showImagePickerDialog("Select Product Image", hasImage));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up observers for the ViewModel and loads product categories.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getCategoryList().observe(getViewLifecycleOwner(), list -> updateCategorySpinner());
|
||||
|
||||
@@ -120,24 +132,36 @@ public class ProductDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the loading bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the category spinner with available categories.
|
||||
*/
|
||||
private void updateCategorySpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerProductCategory, viewModel.getCategoryList().getValue(),
|
||||
DropdownDTO::getLabel, "-- Select Category --",
|
||||
preselectedCategoryId, DropdownDTO::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses fragment arguments to determine if we are editing an existing record.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.containsKey("prodId")) {
|
||||
@@ -158,6 +182,9 @@ public class ProductDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the product details from the backend.
|
||||
*/
|
||||
private void loadProductData() {
|
||||
viewModel.loadProduct().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
@@ -175,6 +202,9 @@ public class ProductDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the product image from the server.
|
||||
*/
|
||||
private void loadProductImage() {
|
||||
String imageUrl = baseUrl + String.format(Locale.US, ProductApi.PRODUCT_IMAGE_PATH, viewModel.getProdId());
|
||||
String token = tokenManager.getToken();
|
||||
@@ -192,6 +222,9 @@ public class ProductDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs image-related actions (removal or upload) after a successful product save.
|
||||
*/
|
||||
private void performPendingImageActions(String successMsg) {
|
||||
if (isImageRemoved) {
|
||||
viewModel.deleteProductImage().observe(getViewLifecycleOwner(), resource -> {
|
||||
@@ -214,6 +247,9 @@ public class ProductDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the selected product image to the server.
|
||||
*/
|
||||
private void uploadProductImageAndNavigate(Uri uri, String successMsg) {
|
||||
File file = FileUtils.getFileFromUri(requireContext(), uri);
|
||||
if (file == null) {
|
||||
@@ -239,6 +275,9 @@ public class ProductDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input and saves the product record.
|
||||
*/
|
||||
private void saveProduct() {
|
||||
if (!InputValidator.isNotEmpty(binding.etProductName, "Product Name")) return;
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerProductCategory, "Category")) return;
|
||||
@@ -267,6 +306,9 @@ public class ProductDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog before deleting the product.
|
||||
*/
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Product", () ->
|
||||
viewModel.deleteProduct().observe(getViewLifecycleOwner(), resource -> {
|
||||
@@ -280,6 +322,9 @@ public class ProductDetailFragment extends Fragment {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous screen.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
@@ -35,12 +35,18 @@ public class ProductSupplierDetailFragment extends Fragment {
|
||||
private long preselectedProductId = -1;
|
||||
private long preselectedSupplierId = -1;
|
||||
|
||||
/**
|
||||
* Initializes the view model.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(ProductSupplierDetailViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for this fragment.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -48,6 +54,9 @@ public class ProductSupplierDetailFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the UI components and observers after the view is created.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
@@ -60,23 +69,35 @@ public class ProductSupplierDetailFragment extends Fragment {
|
||||
binding.btnDeletePS.setOnClickListener(v -> confirmDelete());
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes LiveData from the ViewModel to update the UI.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getProductList().observe(getViewLifecycleOwner(), list -> refreshProductSpinner());
|
||||
viewModel.getSupplierList().observe(getViewLifecycleOwner(), list -> refreshSupplierSpinner());
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the visibility of the progress bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding when the view is destroyed.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads lookup data for products and suppliers.
|
||||
*/
|
||||
private void loadSpinnersData() {
|
||||
viewModel.loadProducts().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
@@ -94,18 +115,27 @@ public class ProductSupplierDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the product spinner with data from the view model.
|
||||
*/
|
||||
private void refreshProductSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerPSProduct, viewModel.getProductList().getValue(),
|
||||
ProductDTO::getProdName, "-- Select Product --",
|
||||
preselectedProductId, ProductDTO::getProdId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the supplier spinner with data from the view model.
|
||||
*/
|
||||
private void refreshSupplierSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerPSSupplier, viewModel.getSupplierList().getValue(),
|
||||
SupplierDTO::getSupCompany, "-- Select Supplier --",
|
||||
preselectedSupplierId, SupplierDTO::getSupId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles fragment arguments to determine mode (new vs edit).
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.containsKey("productId") && a.containsKey("supplierId")) {
|
||||
@@ -132,6 +162,9 @@ public class ProductSupplierDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and saves the product-supplier relationship.
|
||||
*/
|
||||
private void save() {
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerPSProduct, "Product")) return;
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerPSSupplier, "Supplier")) return;
|
||||
@@ -155,6 +188,9 @@ public class ProductSupplierDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a confirmation dialog before deleting the relationship.
|
||||
*/
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Product Supplier Relationship", () ->
|
||||
viewModel.deleteProductSupplier().observe(getViewLifecycleOwner(), resource -> {
|
||||
@@ -169,6 +205,9 @@ public class ProductSupplierDetailFragment extends Fragment {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous fragment.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@ public class PurchaseOrderDetailFragment extends Fragment {
|
||||
private PurchaseOrderDetailViewModel viewModel;
|
||||
private long purchaseOrderId;
|
||||
|
||||
/**
|
||||
* Initializes the view model.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -57,6 +60,9 @@ public class PurchaseOrderDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles fragment arguments to retrieve the purchase order ID.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.containsKey("purchaseOrderId")) {
|
||||
@@ -65,12 +71,18 @@ public class PurchaseOrderDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the visibility of the progress bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the details of the specified purchase order.
|
||||
*/
|
||||
private void loadPurchaseOrderData() {
|
||||
viewModel.loadPurchaseOrder(purchaseOrderId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
@@ -87,6 +99,9 @@ public class PurchaseOrderDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding when the view is destroyed.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -21,6 +21,9 @@ import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Fragment for processing refunds for existing sales.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class RefundFragment extends Fragment {
|
||||
|
||||
@@ -29,6 +32,9 @@ public class RefundFragment extends Fragment {
|
||||
|
||||
private final String[] PAYMENT_METHODS = {"Cash", "Card"};
|
||||
|
||||
/**
|
||||
* Initializes the fragment's UI and view model.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -51,25 +57,37 @@ public class RefundFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the payment method spinner.
|
||||
*/
|
||||
private void setupSpinner() {
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerRefundPayment, PAYMENT_METHODS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes LiveData from the ViewModel to update the UI.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getAvailableItems().observe(getViewLifecycleOwner(), items -> renderOriginalItems());
|
||||
viewModel.getRefundCart().observe(getViewLifecycleOwner(), cart -> {
|
||||
renderRefundCart();
|
||||
updateRefundTotal();
|
||||
renderOriginalItems(); // Re-render to reflect quantities in cart
|
||||
renderOriginalItems();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the visibility of the progress bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the list of all sales to allow looking up the sale to refund.
|
||||
*/
|
||||
private void loadAllSales() {
|
||||
viewModel.loadAllSales().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
@@ -84,6 +102,9 @@ public class RefundFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the details of a specific sale for refund processing.
|
||||
*/
|
||||
private void loadSale() {
|
||||
String idStr = binding.etRefundSaleId.getText().toString().trim();
|
||||
if (idStr.isEmpty()) {
|
||||
@@ -145,6 +166,9 @@ public class RefundFragment extends Fragment {
|
||||
binding.btnProcessRefund.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the items from the original sale that are available for refund.
|
||||
*/
|
||||
private void renderOriginalItems() {
|
||||
binding.llOriginalItems.removeAllViews();
|
||||
List<RefundViewModel.RefundItem> available = viewModel.getAvailableItems().getValue();
|
||||
@@ -173,6 +197,9 @@ public class RefundFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the items selected for the refund.
|
||||
*/
|
||||
private void renderRefundCart() {
|
||||
binding.llRefundItems.removeAllViews();
|
||||
List<RefundViewModel.RefundItem> cart = viewModel.getRefundCart().getValue();
|
||||
@@ -200,6 +227,9 @@ public class RefundFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a header row to the layout representing a table.
|
||||
*/
|
||||
private void addTableHeader(LinearLayout parent) {
|
||||
if (getContext() == null) return;
|
||||
LinearLayout header = new LinearLayout(getContext());
|
||||
@@ -220,6 +250,9 @@ public class RefundFragment extends Fragment {
|
||||
parent.addView(header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a UI row for an item in the original list or refund cart.
|
||||
*/
|
||||
private LinearLayout buildItemRow(String name, int qty, BigDecimal unitPrice,
|
||||
boolean isAdd, Runnable action) {
|
||||
if (getContext() == null) return new LinearLayout(getContext());
|
||||
@@ -267,6 +300,9 @@ public class RefundFragment extends Fragment {
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a dialog to select the quantity of an item to refund.
|
||||
*/
|
||||
private void showQuantityDialog(RefundViewModel.RefundItem item, int available) {
|
||||
EditText input = new EditText(getContext());
|
||||
input.setInputType(android.text.InputType.TYPE_CLASS_NUMBER);
|
||||
@@ -301,11 +337,17 @@ public class RefundFragment extends Fragment {
|
||||
.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the total refund amount display.
|
||||
*/
|
||||
private void updateRefundTotal() {
|
||||
BigDecimal total = viewModel.calculateRefundTotal();
|
||||
binding.tvRefundTotal.setText("Refund Total: $" + total.setScale(2, RoundingMode.HALF_UP));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and prepares the refund for processing.
|
||||
*/
|
||||
private void processRefund() {
|
||||
if (viewModel.getCurrentSale() == null) {
|
||||
Toast.makeText(getContext(), "Load a sale first", Toast.LENGTH_SHORT).show();
|
||||
@@ -325,6 +367,9 @@ public class RefundFragment extends Fragment {
|
||||
() -> submitRefund(payment));
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits the refund request to the backend.
|
||||
*/
|
||||
private void submitRefund(String payment) {
|
||||
viewModel.submitRefund(payment).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null) {
|
||||
@@ -339,10 +384,16 @@ public class RefundFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous fragment.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding when the view is destroyed.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -26,6 +26,9 @@ import dagger.hilt.android.AndroidEntryPoint;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Fragment for viewing or creating sale details.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class SaleDetailFragment extends Fragment {
|
||||
|
||||
@@ -36,6 +39,9 @@ public class SaleDetailFragment extends Fragment {
|
||||
|
||||
private final String[] PAYMENT_METHODS = { "Cash", "Card"};
|
||||
|
||||
/**
|
||||
* Initializes the fragment's UI and view model.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -65,14 +71,23 @@ public class SaleDetailFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current user has the 'STAFF' role.
|
||||
*/
|
||||
private boolean isStaff() {
|
||||
return "STAFF".equalsIgnoreCase(tokenManager.getRole());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current user has the 'ADMIN' role.
|
||||
*/
|
||||
private boolean isAdmin() {
|
||||
return "ADMIN".equalsIgnoreCase(tokenManager.getRole());
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes LiveData from the ViewModel to update the UI.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getStoreList().observe(getViewLifecycleOwner(), list -> {
|
||||
Long primaryStoreId = tokenManager.getPrimaryStoreId();
|
||||
@@ -115,6 +130,9 @@ public class SaleDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles fragment arguments to determine mode (new sale vs view existing).
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.containsKey("saleId")) {
|
||||
@@ -159,12 +177,18 @@ public class SaleDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the visibility of the progress bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads lookup data for stores, customers, and products.
|
||||
*/
|
||||
private void loadData() {
|
||||
viewModel.loadStores().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
@@ -183,6 +207,9 @@ public class SaleDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the details of an existing sale.
|
||||
*/
|
||||
private void loadSaleDetails() {
|
||||
viewModel.loadSaleDetails().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
@@ -238,6 +265,9 @@ public class SaleDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the coupon application logic.
|
||||
*/
|
||||
private void setupCoupon() {
|
||||
binding.btnApplyCoupon.setOnClickListener(v -> {
|
||||
String code = binding.etCouponCode.getText().toString().trim();
|
||||
@@ -281,6 +311,9 @@ public class SaleDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the UI to reflect an applied coupon.
|
||||
*/
|
||||
private void applyAppliedCouponUI(CouponDTO coupon) {
|
||||
String info;
|
||||
if ("PERCENTAGE".equalsIgnoreCase(coupon.getDiscountType())) {
|
||||
@@ -296,12 +329,18 @@ public class SaleDetailFragment extends Fragment {
|
||||
binding.etCouponCode.setEnabled(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an error message related to coupon validation.
|
||||
*/
|
||||
private void showCouponError(String message) {
|
||||
binding.tvCouponInfo.setText(message);
|
||||
binding.tvCouponInfo.setTextColor(0xFFE53935);
|
||||
binding.tvCouponInfo.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the logic for adding items to the sale cart.
|
||||
*/
|
||||
private void setupAddItem() {
|
||||
binding.btnAddItem.setOnClickListener(v -> {
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerSaleProduct, "Product")) return;
|
||||
@@ -335,6 +374,9 @@ public class SaleDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the list of items currently in the cart.
|
||||
*/
|
||||
private void renderCartItems() {
|
||||
binding.llSaleItems.removeAllViews();
|
||||
List<SaleDTO.SaleItemDTO> items = viewModel.getCartItems().getValue();
|
||||
@@ -355,6 +397,9 @@ public class SaleDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a row representing a sale item to the layout.
|
||||
*/
|
||||
private void addItemRow(String name, int qty, BigDecimal price, Long prodId) {
|
||||
if (getContext() == null) return;
|
||||
LinearLayout row = new LinearLayout(getContext());
|
||||
@@ -394,6 +439,9 @@ public class SaleDetailFragment extends Fragment {
|
||||
binding.llSaleItems.addView(row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the subtotal, discounts, and total amount display.
|
||||
*/
|
||||
private void updateTotal() {
|
||||
BigDecimal subtotal = viewModel.calculateSubtotal();
|
||||
BigDecimal couponDiscount = viewModel.calculateCouponDiscount();
|
||||
@@ -429,6 +477,9 @@ public class SaleDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and saves the new sale.
|
||||
*/
|
||||
private void saveSale() {
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerSaleStore, "Store")) return;
|
||||
|
||||
@@ -465,6 +516,9 @@ public class SaleDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a confirmation dialog before proceeding to the refund screen.
|
||||
*/
|
||||
private void showRefundDialog() {
|
||||
DialogUtils.showConfirmDialog(requireContext(), "Process Refund",
|
||||
"Are you sure you want to process a refund for this sale?", () -> {
|
||||
@@ -474,10 +528,16 @@ public class SaleDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous fragment.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding when the view is destroyed.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -35,12 +35,18 @@ public class ServiceDetailFragment extends Fragment {
|
||||
private FragmentServiceDetailBinding binding;
|
||||
private ServiceDetailViewModel viewModel;
|
||||
|
||||
/**
|
||||
* Initializes the fragment and its ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(ServiceDetailViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for the fragment.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -48,6 +54,9 @@ public class ServiceDetailFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up UI components, observers, and handles arguments after the view is created.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
@@ -60,22 +69,34 @@ public class ServiceDetailFragment extends Fragment {
|
||||
binding.btnDeleteService.setOnClickListener(v -> deleteService());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up observers for ViewModel to update the UI dynamically.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getViewState().observe(getViewLifecycleOwner(), this::applyViewState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the loading bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input and saves the service record.
|
||||
*/
|
||||
private void saveService() {
|
||||
if (!InputValidator.isNotEmpty(binding.etServiceName, "Service Name")) return;
|
||||
if (!InputValidator.isNotEmpty(binding.etServiceDesc, "Description")) return;
|
||||
@@ -111,6 +132,9 @@ public class ServiceDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog before deleting the service record.
|
||||
*/
|
||||
private void deleteService() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Service", () ->
|
||||
viewModel.deleteService().observe(getViewLifecycleOwner(), resource -> {
|
||||
@@ -126,10 +150,16 @@ public class ServiceDetailFragment extends Fragment {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous screen.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses fragment arguments to determine if we are editing an existing record.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
if (getArguments() != null && getArguments().containsKey("serviceId")) {
|
||||
viewModel.setServiceId(getArguments().getLong("serviceId"));
|
||||
@@ -140,6 +170,9 @@ public class ServiceDetailFragment extends Fragment {
|
||||
viewModel.setServiceId(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the service details from the backend.
|
||||
*/
|
||||
private void loadServiceData() {
|
||||
viewModel.loadService().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
@@ -150,6 +183,9 @@ public class ServiceDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the current ViewState to the UI elements.
|
||||
*/
|
||||
private void applyViewState(ServiceDetailViewModel.ViewState state) {
|
||||
binding.tvMode.setText(state.modeTitle);
|
||||
binding.tvServiceId.setText(DateTimeUtils.formatId(viewModel.getServiceId()));
|
||||
@@ -169,6 +205,9 @@ public class ServiceDetailFragment extends Fragment {
|
||||
updateIfDifferent(binding.etServicePrice, state.servicePrice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an EditText field only if the new value is different from the current one.
|
||||
*/
|
||||
private void updateIfDifferent(EditText field, String value) {
|
||||
String current = field.getText() != null ? field.getText().toString() : "";
|
||||
String next = value != null ? value : "";
|
||||
|
||||
@@ -22,6 +22,9 @@ import java.util.List;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and editing staff details.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class StaffDetailFragment extends Fragment {
|
||||
|
||||
@@ -32,6 +35,9 @@ public class StaffDetailFragment extends Fragment {
|
||||
|
||||
private long preselectedStoreId = -1;
|
||||
|
||||
/**
|
||||
* Inflates the layout, initializes ViewModel, and sets up UI components.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -52,14 +58,23 @@ public class StaffDetailFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up observers for the ViewModel to refresh the store spinner.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getStoreList().observe(getViewLifecycleOwner(), list -> refreshStoreSpinner());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the status spinner with options.
|
||||
*/
|
||||
private void setupSpinners() {
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerStaffStatus, STATUSES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the list of stores from the backend.
|
||||
*/
|
||||
private void loadStores() {
|
||||
viewModel.loadStores().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
@@ -68,6 +83,9 @@ public class StaffDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the store spinner with available stores.
|
||||
*/
|
||||
private void refreshStoreSpinner() {
|
||||
List<DropdownDTO> list = viewModel.getStoreList().getValue();
|
||||
if (list == null) return;
|
||||
@@ -76,6 +94,9 @@ public class StaffDetailFragment extends Fragment {
|
||||
preselectedStoreId, DropdownDTO::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses fragment arguments to determine if we are editing an existing record.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.getBoolean("isEditing", false)) {
|
||||
@@ -96,6 +117,9 @@ public class StaffDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads staff member details from the backend.
|
||||
*/
|
||||
private void loadEmployeeData(long id) {
|
||||
viewModel.loadEmployee(id).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null) {
|
||||
@@ -117,12 +141,18 @@ public class StaffDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the loading bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input and saves the staff record.
|
||||
*/
|
||||
private void save() {
|
||||
if (!InputValidator.isNotEmpty(binding.etStaffUsername, "Username")) return;
|
||||
|
||||
@@ -198,6 +228,9 @@ public class StaffDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog before deleting the staff account.
|
||||
*/
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Staff Account", () ->
|
||||
viewModel.deleteEmployee().observe(getViewLifecycleOwner(), resource -> {
|
||||
@@ -213,10 +246,16 @@ public class StaffDetailFragment extends Fragment {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous screen.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -35,12 +35,18 @@ public class SupplierDetailFragment extends Fragment {
|
||||
private FragmentSupplierDetailBinding binding;
|
||||
private SupplierDetailViewModel viewModel;
|
||||
|
||||
/**
|
||||
* Initializes the view model.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(SupplierDetailViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for this fragment.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -48,6 +54,9 @@ public class SupplierDetailFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the UI components and observers after the view is created.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
@@ -61,22 +70,34 @@ public class SupplierDetailFragment extends Fragment {
|
||||
binding.btnDeleteSupplier.setOnClickListener(v -> deleteSupplier());
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes LiveData from the ViewModel to update the UI.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getViewState().observe(getViewLifecycleOwner(), this::applyViewState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the visibility of the progress bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding when the view is destroyed.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and saves the supplier information.
|
||||
*/
|
||||
private void saveSupplier() {
|
||||
if (!InputValidator.isNotEmpty(binding.etSupCompany, "Company Name")) return;
|
||||
if (!InputValidator.isNotEmpty(binding.etSupContactFirstName, "First Name")) return;
|
||||
@@ -115,6 +136,9 @@ public class SupplierDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the current supplier after confirmation.
|
||||
*/
|
||||
private void deleteSupplier() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Supplier", () ->
|
||||
viewModel.deleteSupplier().observe(getViewLifecycleOwner(), resource -> {
|
||||
@@ -130,10 +154,16 @@ public class SupplierDetailFragment extends Fragment {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous fragment.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles fragment arguments to determine mode (new vs edit).
|
||||
*/
|
||||
private void handleArguments() {
|
||||
if (getArguments() != null && getArguments().containsKey("supId")) {
|
||||
viewModel.setSupId(getArguments().getLong("supId"));
|
||||
@@ -144,6 +174,9 @@ public class SupplierDetailFragment extends Fragment {
|
||||
viewModel.setSupId(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the details of the specified supplier.
|
||||
*/
|
||||
private void loadSupplierData() {
|
||||
viewModel.loadSupplier().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
@@ -154,6 +187,9 @@ public class SupplierDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the current view state to the UI components.
|
||||
*/
|
||||
private void applyViewState(SupplierDetailViewModel.ViewState state) {
|
||||
binding.tvMode.setText(state.modeTitle);
|
||||
binding.tvSupId.setText(DateTimeUtils.formatId(viewModel.getSupId()));
|
||||
@@ -175,6 +211,9 @@ public class SupplierDetailFragment extends Fragment {
|
||||
updateIfDifferent(binding.etSupPhone, state.supPhone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an EditText field only if the new value is different from the current one.
|
||||
*/
|
||||
private void updateIfDifferent(EditText field, String value) {
|
||||
String current = field.getText() != null ? field.getText().toString() : "";
|
||||
String next = value != null ? value : "";
|
||||
|
||||
@@ -36,6 +36,9 @@ import okhttp3.MediaType;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a pet's profile.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class PetProfileFragment extends Fragment {
|
||||
|
||||
@@ -49,6 +52,9 @@ public class PetProfileFragment extends Fragment {
|
||||
private PetProfileViewModel viewModel;
|
||||
private ImagePickerHelper imagePickerHelper;
|
||||
|
||||
/**
|
||||
* Initializes the ViewModel and image picker helper.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -67,6 +73,9 @@ public class PetProfileFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout, handles arguments, and sets up click listeners.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -95,18 +104,27 @@ public class PetProfileFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the loading progress bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads pet data from the ViewModel and updates the UI.
|
||||
*/
|
||||
private void loadPetData() {
|
||||
viewModel.getPetById(petId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
@@ -153,6 +171,9 @@ public class PetProfileFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the pet's image using Glide with authentication.
|
||||
*/
|
||||
private void loadPetImage(int petId) {
|
||||
String imageUrl = baseUrl + String.format(Locale.US, PetApi.PET_IMAGE_PATH, petId);
|
||||
String token = tokenManager.getToken();
|
||||
@@ -170,6 +191,9 @@ public class PetProfileFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads a new image for the pet.
|
||||
*/
|
||||
private void uploadPetImage(Uri uri) {
|
||||
try {
|
||||
File file = FileUtils.getFileFromUri(requireContext(), uri);
|
||||
@@ -193,6 +217,9 @@ public class PetProfileFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the pet's current image.
|
||||
*/
|
||||
private void deletePetImage() {
|
||||
viewModel.deletePetImage(petId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user