Merge branch 'AttachmentsToChat'

This commit is contained in:
Alex
2026-04-19 20:19:57 -06:00
160 changed files with 3098 additions and 81 deletions

View File

@@ -32,6 +32,9 @@ public class ForgotPasswordActivity extends AppCompatActivity {
private ActivityForgotPasswordBinding binding; private ActivityForgotPasswordBinding binding;
/**
* Set the content view for forget password page
*/
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
EdgeToEdge.enable(this); EdgeToEdge.enable(this);
@@ -54,6 +57,10 @@ public class ForgotPasswordActivity extends AppCompatActivity {
binding.btnBackToLogin.setOnClickListener(v -> finish()); 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) { private void sendResetLink(String email) {
binding.btnSubmit.setEnabled(false); binding.btnSubmit.setEnabled(false);

View File

@@ -104,7 +104,7 @@ public class HomeActivity extends AppCompatActivity {
@Override @Override
protected void onNewIntent(Intent intent) { protected void onNewIntent(Intent intent) {
super.onNewIntent(intent); super.onNewIntent(intent);
setIntent(intent); // Set the new intent so fragments can access updated extras setIntent(intent);
handleIntent(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() { private void requestNotificationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {

View File

@@ -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) { private void performLogin(String username, String password) {
viewModel.login(username, password).observe(this, resource -> { viewModel.login(username, password).observe(this, resource -> {
@@ -112,6 +112,7 @@ public class MainActivity extends AppCompatActivity {
case SUCCESS: case SUCCESS:
if (resource.data != null) { if (resource.data != null) {
String role = resource.data.getRole(); String role = resource.data.getRole();
//Check if role is staff/admin or customer
if ("CUSTOMER".equalsIgnoreCase(role)) { if ("CUSTOMER".equalsIgnoreCase(role)) {
UIUtils.setViewsEnabled(true, binding.btnLogin); UIUtils.setViewsEnabled(true, binding.btnLogin);
binding.tvLoginStatus.setText("Customers are not allowed to log in"); 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() { private void fetchUserIdAndNavigate() {
viewModel.getMe().observe(this, resource -> { viewModel.getMe().observe(this, resource -> {

View File

@@ -25,10 +25,16 @@ public class ActivityLogAdapter extends RecyclerView.Adapter<ActivityLogAdapter.
private final List<ActivityLogDTO> items; private final List<ActivityLogDTO> items;
/**
* Constructor for the ActivityLogAdapter.
*/
public ActivityLogAdapter(List<ActivityLogDTO> items) { public ActivityLogAdapter(List<ActivityLogDTO> items) {
this.items = items; this.items = items;
} }
/**
* Inflates the layout for an activity log item and creates a new ViewHolder.
*/
@NonNull @NonNull
@Override @Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -37,6 +43,9 @@ public class ActivityLogAdapter extends RecyclerView.Adapter<ActivityLogAdapter.
return new ViewHolder(view); return new ViewHolder(view);
} }
/**
* Binds the data from ActivityLogDTO to the items in the ViewHolder.
*/
@Override @Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) { public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
ActivityLogDTO log = items.get(position); ActivityLogDTO log = items.get(position);
@@ -71,9 +80,15 @@ public class ActivityLogAdapter extends RecyclerView.Adapter<ActivityLogAdapter.
holder.tvTimestamp.setText(formatTimestamp(log.getLogTimestamp())); holder.tvTimestamp.setText(formatTimestamp(log.getLogTimestamp()));
} }
/**
* Returns the total number of items in the list.
*/
@Override @Override
public int getItemCount() { return items.size(); } public int getItemCount() { return items.size(); }
/**
* Formats the timestamp string from the API to a readable date/time format.
*/
private String formatTimestamp(String raw) { private String formatTimestamp(String raw) {
if (raw == null) return ""; if (raw == null) return "";
try { try {
@@ -85,6 +100,9 @@ public class ActivityLogAdapter extends RecyclerView.Adapter<ActivityLogAdapter.
} }
} }
/**
* Format the Role string to be consistent
*/
private String formatRole(String role) { private String formatRole(String role) {
if (role == null) return ""; if (role == null) return "";
switch (role.toUpperCase(Locale.ROOT)) { 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) { private String firstNonBlank(String... values) {
for (String v : values) { for (String v : values) {
if (v != null && !v.isBlank()) return v; if (v != null && !v.isBlank()) return v;
@@ -102,9 +123,15 @@ public class ActivityLogAdapter extends RecyclerView.Adapter<ActivityLogAdapter.
return ""; return "";
} }
/**
* ViewHolder class that holds references to the UI components for an activity log item.
*/
public static class ViewHolder extends RecyclerView.ViewHolder { public static class ViewHolder extends RecyclerView.ViewHolder {
TextView tvActivity, tvTechnical, tvUser, tvMeta, tvTimestamp; TextView tvActivity, tvTechnical, tvUser, tvMeta, tvTimestamp;
/**
* Initializes the ViewHolder by finding the views within the item layout.
*/
public ViewHolder(@NonNull View itemView) { public ViewHolder(@NonNull View itemView) {
super(itemView); super(itemView);
tvActivity = itemView.findViewById(R.id.tvLogActivity); tvActivity = itemView.findViewById(R.id.tvLogActivity);

View File

@@ -23,6 +23,9 @@ public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.Adopti
void onSelectionChanged(int count); void onSelectionChanged(int count);
} }
/**
* Constructor for AdoptionAdapter.
*/
public AdoptionAdapter(List<AdoptionDTO> adoptionList, OnAdoptionClickListener listener) { public AdoptionAdapter(List<AdoptionDTO> adoptionList, OnAdoptionClickListener listener) {
this.adoptionList = adoptionList; this.adoptionList = adoptionList;
this.listener = listener; 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 @Override
public List<String> getSelectedKeys() { public List<String> getSelectedKeys() {
return selectionHelper.getSelectedKeys(); return selectionHelper.getSelectedKeys();
} }
/**
* Resets the selection state, deselecting all items.
*/
@Override @Override
public void clearSelection() { public void clearSelection() {
selectionHelper.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 @NonNull
@Override @Override
public AdoptionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public AdoptionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -65,6 +77,9 @@ public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.Adopti
return new AdoptionViewHolder(binding); return new AdoptionViewHolder(binding);
} }
/**
* Binds adoption data to the UI components and handles click/long-click logic.
*/
@Override @Override
public void onBindViewHolder(@NonNull AdoptionViewHolder holder, int position) { public void onBindViewHolder(@NonNull AdoptionViewHolder holder, int position) {
AdoptionDTO a = adoptionList.get(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 @Override
public int getItemCount() { return adoptionList.size(); } public int getItemCount() { return adoptionList.size(); }
} }

View File

@@ -23,6 +23,9 @@ public class AppointmentAdapter extends RecyclerView.Adapter<AppointmentAdapter.
void onSelectionChanged(int count); void onSelectionChanged(int count);
} }
/**
* Constructor for AppointmentAdapter.
*/
public AppointmentAdapter(List<AppointmentDTO> appointmentList, public AppointmentAdapter(List<AppointmentDTO> appointmentList,
OnAppointmentClickListener appointmentClickListener) { OnAppointmentClickListener appointmentClickListener) {
this.appointmentList = appointmentList; 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 @Override
public List<String> getSelectedKeys() { public List<String> getSelectedKeys() {
return selectionHelper.getSelectedKeys(); return selectionHelper.getSelectedKeys();
} }
/**
* Resets the selection state, deselecting all items.
*/
@Override @Override
public void clearSelection() { public void clearSelection() {
selectionHelper.clearSelection(); selectionHelper.clearSelection();
} }
/**
* ViewHolder class that holds references to the UI components for an appointment item.
*/
public static class AppointmentViewHolder extends RecyclerView.ViewHolder { public static class AppointmentViewHolder extends RecyclerView.ViewHolder {
private final ItemAppointmentBinding binding; private final ItemAppointmentBinding binding;
/**
* Initializes the ViewHolder by finding the views within the item layout.
*/
public AppointmentViewHolder(@NonNull ItemAppointmentBinding binding) { public AppointmentViewHolder(@NonNull ItemAppointmentBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
} }
} }
/**
* Inflates the layout for an appointment item and creates the ViewHolder.
*/
@NonNull @NonNull
@Override @Override
public AppointmentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public AppointmentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -66,6 +84,9 @@ public class AppointmentAdapter extends RecyclerView.Adapter<AppointmentAdapter.
return new AppointmentViewHolder(binding); return new AppointmentViewHolder(binding);
} }
/**
* Binds appointment data to the UI components and handles click/long-click logic.
*/
@Override @Override
public void onBindViewHolder(@NonNull AppointmentViewHolder holder, int position) { public void onBindViewHolder(@NonNull AppointmentViewHolder holder, int position) {
AppointmentDTO a = appointmentList.get(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 @Override
public int getItemCount() { public int getItemCount() {
return appointmentList.size(); return appointmentList.size();

View File

@@ -12,6 +12,7 @@ import com.example.petstoremobile.R;
import java.util.List; import java.util.List;
// A class that overrides the arrayAdapter so the text color changes based on theme // 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 class BlackTextArrayAdapter<T> extends ArrayAdapter<T> {
public BlackTextArrayAdapter(@NonNull Context context, int resource, @NonNull T[] objects) { public BlackTextArrayAdapter(@NonNull Context context, int resource, @NonNull T[] objects) {
super(context, resource, objects); super(context, resource, objects);

View File

@@ -20,11 +20,17 @@ public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ChatViewHolder
void onChatClick(Chat chat); void onChatClick(Chat chat);
} }
/**
* Constructor for ChatAdapter.
*/
public ChatAdapter(List<Chat> chatList, OnChatClickListener listener) { public ChatAdapter(List<Chat> chatList, OnChatClickListener listener) {
this.chatList = chatList; this.chatList = chatList;
this.listener = listener; this.listener = listener;
} }
/**
* Inflates the layout for a chat item and creates the ViewHolder.
*/
@NonNull @NonNull
@Override @Override
public ChatViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public ChatViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -32,6 +38,9 @@ public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ChatViewHolder
return new ChatViewHolder(binding); return new ChatViewHolder(binding);
} }
/**
* Binds chat data to the UI components for a specific conversation.
*/
@Override @Override
public void onBindViewHolder(@NonNull ChatViewHolder holder, int position) { public void onBindViewHolder(@NonNull ChatViewHolder holder, int position) {
Chat chat = chatList.get(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)); holder.itemView.setOnClickListener(v -> listener.onChatClick(chat));
} }
/**
* Returns the total number of chat items in the list.
*/
@Override @Override
public int getItemCount() { public int getItemCount() {
return chatList.size(); return chatList.size();
} }
/**
* ViewHolder class that holds references to the UI components for a chat item.
*/
public static class ChatViewHolder extends RecyclerView.ViewHolder { public static class ChatViewHolder extends RecyclerView.ViewHolder {
final ItemChatBinding binding; final ItemChatBinding binding;
/**
* Initializes the ViewHolder with the chat item's view binding.
*/
public ChatViewHolder(@NonNull ItemChatBinding binding) { public ChatViewHolder(@NonNull ItemChatBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;

View File

@@ -30,11 +30,17 @@ public class CouponAdapter extends RecyclerView.Adapter<CouponAdapter.ViewHolder
void onSelectionChanged(int count); void onSelectionChanged(int count);
} }
/**
* Constructor for CouponAdapter.
*/
public CouponAdapter(List<CouponDTO> coupons, OnCouponClickListener listener) { public CouponAdapter(List<CouponDTO> coupons, OnCouponClickListener listener) {
this.coupons = coupons; this.coupons = coupons;
this.listener = listener; this.listener = listener;
} }
/**
* Inflates the layout for a coupon item and creates the ViewHolder.
*/
@NonNull @NonNull
@Override @Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -42,6 +48,9 @@ public class CouponAdapter extends RecyclerView.Adapter<CouponAdapter.ViewHolder
return new ViewHolder(view); return new ViewHolder(view);
} }
/**
* Binds coupon data to the UI components and handles interaction logic.
*/
@Override @Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) { public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
CouponDTO coupon = coupons.get(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())); holder.cbSelectCoupon.setOnClickListener(v -> toggleSelection(coupon.getCouponId()));
} }
/**
* Toggles the selection state of a specific coupon by its ID.
*/
private void toggleSelection(Long id) { private void toggleSelection(Long id) {
if (selectedIds.contains(id)) { if (selectedIds.contains(id)) {
selectedIds.remove(id); selectedIds.remove(id);
@@ -105,6 +117,9 @@ public class CouponAdapter extends RecyclerView.Adapter<CouponAdapter.ViewHolder
listener.onSelectionChanged(selectedIds.size()); listener.onSelectionChanged(selectedIds.size());
} }
/**
* Enables or disables bulk selection mode.
*/
public void setSelectionMode(boolean selectionMode) { public void setSelectionMode(boolean selectionMode) {
this.selectionMode = selectionMode; this.selectionMode = selectionMode;
if (!selectionMode) selectedIds.clear(); if (!selectionMode) selectedIds.clear();
@@ -112,10 +127,16 @@ public class CouponAdapter extends RecyclerView.Adapter<CouponAdapter.ViewHolder
listener.onSelectionChanged(selectedIds.size()); listener.onSelectionChanged(selectedIds.size());
} }
/**
* Returns the set of IDs for the currently selected coupons.
*/
public Set<Long> getSelectedIds() { public Set<Long> getSelectedIds() {
return selectedIds; return selectedIds;
} }
/**
* Returns the total number of coupons in the list.
*/
@Override @Override
public int getItemCount() { public int getItemCount() {
return coupons.size(); return coupons.size();
@@ -125,6 +146,9 @@ public class CouponAdapter extends RecyclerView.Adapter<CouponAdapter.ViewHolder
TextView tvCouponCode, tvCouponDiscount, tvCouponMinOrder, tvCouponExpiry, tvCouponStatus; TextView tvCouponCode, tvCouponDiscount, tvCouponMinOrder, tvCouponExpiry, tvCouponStatus;
CheckBox cbSelectCoupon; CheckBox cbSelectCoupon;
/**
* Initializes the ViewHolder by finding the views within the item layout.
*/
public ViewHolder(@NonNull View itemView) { public ViewHolder(@NonNull View itemView) {
super(itemView); super(itemView);
tvCouponCode = itemView.findViewById(R.id.tvCouponCode); tvCouponCode = itemView.findViewById(R.id.tvCouponCode);

View File

@@ -24,23 +24,42 @@ public class CustomerAdapter extends RecyclerView.Adapter<CustomerAdapter.Custom
void onCustomerClick(int position); void onCustomerClick(int position);
} }
/**
* Constructor for CustomerAdapter.
*/
public CustomerAdapter(List<CustomerDTO> list, OnCustomerClickListener listener) { public CustomerAdapter(List<CustomerDTO> list, OnCustomerClickListener listener) {
this.list = list; this.list = list;
this.listener = listener; this.listener = listener;
} }
/**
* Sets the base URL for fetching customer profile images.
*/
public void setBaseUrl(String baseUrl) { this.baseUrl = baseUrl; } public void setBaseUrl(String baseUrl) { this.baseUrl = baseUrl; }
/**
* Sets the authentication token for fetching images.
*/
public void setToken(String token) { this.token = token; } public void setToken(String token) { this.token = token; }
/**
* ViewHolder class for customer items.
*/
public static class CustomerViewHolder extends RecyclerView.ViewHolder { public static class CustomerViewHolder extends RecyclerView.ViewHolder {
final ItemCustomerBinding binding; final ItemCustomerBinding binding;
/**
* Initializes the ViewHolder with view binding.
*/
public CustomerViewHolder(@NonNull ItemCustomerBinding binding) { public CustomerViewHolder(@NonNull ItemCustomerBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
} }
} }
/**
* Inflates the layout for a customer item and creates the ViewHolder.
*/
@NonNull @NonNull
@Override @Override
public CustomerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public CustomerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -49,6 +68,9 @@ public class CustomerAdapter extends RecyclerView.Adapter<CustomerAdapter.Custom
return new CustomerViewHolder(binding); return new CustomerViewHolder(binding);
} }
/**
* Binds customer data to the UI components.
*/
@Override @Override
public void onBindViewHolder(@NonNull CustomerViewHolder holder, int position) { public void onBindViewHolder(@NonNull CustomerViewHolder holder, int position) {
CustomerDTO c = list.get(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)); holder.itemView.setOnClickListener(v -> listener.onCustomerClick(position));
} }
/**
* Returns the total number of customers in the list.
*/
@Override @Override
public int getItemCount() { return list.size(); } public int getItemCount() { return list.size(); }
} }

View File

@@ -25,28 +25,46 @@ public class EmployeeAdapter extends RecyclerView.Adapter<EmployeeAdapter.Employ
void onEmployeeClick(int position); void onEmployeeClick(int position);
} }
/**
* Constructor for EmployeeAdapter.
*/
public EmployeeAdapter(List<EmployeeDTO> list, OnEmployeeClickListener listener) { public EmployeeAdapter(List<EmployeeDTO> list, OnEmployeeClickListener listener) {
this.list = list; this.list = list;
this.listener = listener; this.listener = listener;
} }
/**
* Sets the base URL for fetching employee profile images.
*/
public void setBaseUrl(String baseUrl) { public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl; this.baseUrl = baseUrl;
} }
/**
* Sets the authentication token for fetching images.
*/
public void setToken(String token) { public void setToken(String token) {
this.token = token; this.token = token;
} }
/**
* ViewHolder class for employee items.
*/
public static class EmployeeViewHolder extends RecyclerView.ViewHolder { public static class EmployeeViewHolder extends RecyclerView.ViewHolder {
private final ItemEmployeeBinding binding; private final ItemEmployeeBinding binding;
/**
* Initializes the ViewHolder with view binding.
*/
public EmployeeViewHolder(@NonNull ItemEmployeeBinding binding) { public EmployeeViewHolder(@NonNull ItemEmployeeBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
} }
} }
/**
* Inflates the layout for an employee item and creates the ViewHolder.
*/
@NonNull @NonNull
@Override @Override
public EmployeeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public EmployeeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -55,6 +73,9 @@ public class EmployeeAdapter extends RecyclerView.Adapter<EmployeeAdapter.Employ
return new EmployeeViewHolder(binding); return new EmployeeViewHolder(binding);
} }
/**
* Binds employee data to the UI components.
*/
@Override @Override
public void onBindViewHolder(@NonNull EmployeeViewHolder holder, int position) { public void onBindViewHolder(@NonNull EmployeeViewHolder holder, int position) {
EmployeeDTO e = list.get(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)); holder.itemView.setOnClickListener(v -> listener.onEmployeeClick(position));
} }
/**
* Returns the total number of employees in the list.
*/
@Override @Override
public int getItemCount() { return list.size(); } public int getItemCount() { return list.size(); }
} }

View File

@@ -27,6 +27,9 @@ public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.Inve
void onSelectionChanged(int selectedCount); void onSelectionChanged(int selectedCount);
} }
/**
* Constructor for InventoryAdapter.
*/
public InventoryAdapter(List<InventoryDTO> inventoryList, OnInventoryClickListener clickListener) { public InventoryAdapter(List<InventoryDTO> inventoryList, OnInventoryClickListener clickListener) {
this.inventoryList = inventoryList; this.inventoryList = inventoryList;
this.clickListener = clickListener; 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 @Override
public List<String> getSelectedKeys() { public List<String> getSelectedKeys() {
return selectionHelper.getSelectedKeys(); return selectionHelper.getSelectedKeys();
} }
/**
* Resets the selection state, deselecting all items.
*/
@Override @Override
public void clearSelection() { public void clearSelection() {
selectionHelper.clearSelection(); selectionHelper.clearSelection();
} }
/**
* ViewHolder class that holds references to the UI components for an inventory item.
*/
public static class InventoryViewHolder extends RecyclerView.ViewHolder { public static class InventoryViewHolder extends RecyclerView.ViewHolder {
final ItemInventoryBinding binding; final ItemInventoryBinding binding;
/**
* Initializes the ViewHolder with view binding.
*/
public InventoryViewHolder(@NonNull ItemInventoryBinding binding) { public InventoryViewHolder(@NonNull ItemInventoryBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
} }
} }
/**
* Inflates the layout for an inventory item and creates the ViewHolder.
*/
@NonNull @NonNull
@Override @Override
public InventoryViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public InventoryViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -69,6 +87,9 @@ public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.Inve
return new InventoryViewHolder(binding); return new InventoryViewHolder(binding);
} }
/**
* Binds inventory data to the UI components.
*/
@Override @Override
public void onBindViewHolder(@NonNull InventoryViewHolder holder, int position) { public void onBindViewHolder(@NonNull InventoryViewHolder holder, int position) {
InventoryDTO inv = inventoryList.get(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; int qty = inv.getQuantity() != null ? inv.getQuantity() : 0;
binding.tvQuantity.setText("Stock: " + qty); binding.tvQuantity.setText("Stock: " + qty);
// Low stock = red, normal = green (like desktop reorder concept)
if (qty <= 5) { if (qty <= 5) {
binding.tvQuantity.setTextColor(Color.parseColor("#F44336")); binding.tvQuantity.setTextColor(Color.parseColor("#F44336"));
} else { } else {
@@ -119,6 +139,9 @@ public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.Inve
}); });
} }
/**
* Returns the total number of inventory items in the list.
*/
@Override @Override
public int getItemCount() { public int getItemCount() {
return inventoryList.size(); return inventoryList.size();

View File

@@ -37,40 +37,64 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
private String baseUrl; private String baseUrl;
private OnAttachmentClickListener attachmentClickListener; private OnAttachmentClickListener attachmentClickListener;
/**
* Constructor for MessageAdapter.
*/
public MessageAdapter(List<Message> messages, Long currentUserId) { public MessageAdapter(List<Message> messages, Long currentUserId) {
this.messages = messages; this.messages = messages;
this.currentUserId = currentUserId; this.currentUserId = currentUserId;
setHasStableIds(true); setHasStableIds(true);
} }
/**
* Returns an ID for each message.
*/
@Override @Override
public long getItemId(int position) { public long getItemId(int position) {
Message m = messages.get(position); Message m = messages.get(position);
return m.getId() != null ? m.getId() : position; return m.getId() != null ? m.getId() : position;
} }
/**
* Updates the current user's ID and refreshes the list.
*/
public void setCurrentUserId(Long id) { public void setCurrentUserId(Long id) {
this.currentUserId = id; this.currentUserId = id;
notifyDataSetChanged(); notifyDataSetChanged();
} }
/**
* Updates the staff ID to identify staff messages in the UI.
*/
public void setStaffId(Long id) { public void setStaffId(Long id) {
this.staffId = id; this.staffId = id;
notifyDataSetChanged(); notifyDataSetChanged();
} }
/**
* Sets the authentication token.
*/
public void setToken(String token) { public void setToken(String token) {
this.token = token; this.token = token;
} }
/**
* Sets the base API URL.
*/
public void setBaseUrl(String baseUrl) { public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl; this.baseUrl = baseUrl;
} }
/**
* Sets a listener for clicks on message attachments.
*/
public void setOnAttachmentClickListener(OnAttachmentClickListener listener) { public void setOnAttachmentClickListener(OnAttachmentClickListener listener) {
this.attachmentClickListener = listener; this.attachmentClickListener = listener;
} }
/**
* Determines if a message is 'sent' or 'received' based on the sender's ID.
*/
@Override @Override
public int getItemViewType(int position) { public int getItemViewType(int position) {
Message m = messages.get(position); Message m = messages.get(position);
@@ -80,6 +104,9 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
return TYPE_RECEIVED; return TYPE_RECEIVED;
} }
/**
* Inflates the chat layout for a message.
*/
@NonNull @Override @NonNull @Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inf = LayoutInflater.from(parent.getContext()); 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 @Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
Message m = messages.get(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); 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(); } @Override public int getItemCount() { return messages.size(); }
/**
* ViewHolder for messages sent by the user.
*/
static class SentHolder extends RecyclerView.ViewHolder { static class SentHolder extends RecyclerView.ViewHolder {
final ItemMessageSentBinding binding; final ItemMessageSentBinding binding;
/**
* Initializes the SentHolder with view binding.
*/
SentHolder(ItemMessageSentBinding binding) { SentHolder(ItemMessageSentBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
} }
/**
* Binds sent message data to the bubble UI.
*/
void bind(Message m, String token, String baseUrl, OnAttachmentClickListener listener) { void bind(Message m, String token, String baseUrl, OnAttachmentClickListener listener) {
binding.tvSenderName.setText("You"); binding.tvSenderName.setText("You");
binding.tvTimestamp.setText(formatTimestamp(m.getTimestamp())); 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 { static class ReceivedHolder extends RecyclerView.ViewHolder {
final ItemMessageReceivedBinding binding; final ItemMessageReceivedBinding binding;
/**
* Initializes the ReceivedHolder with view binding.
*/
ReceivedHolder(ItemMessageReceivedBinding binding) { ReceivedHolder(ItemMessageReceivedBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
} }
/**
* Binds received message data to the bubble UI.
*/
void bind(Message m, String token, String baseUrl, OnAttachmentClickListener listener, Long staffId) { void bind(Message m, String token, String baseUrl, OnAttachmentClickListener listener, Long staffId) {
binding.tvSenderName.setText(resolveSenderName(m, staffId)); binding.tvSenderName.setText(resolveSenderName(m, staffId));
binding.tvTimestamp.setText(formatTimestamp(m.getTimestamp())); 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) { private static String resolveSenderName(Message m, Long staffId) {
if ("BOT".equalsIgnoreCase(m.getSenderRole())) { if ("BOT".equalsIgnoreCase(m.getSenderRole())) {
return (m.getSenderDisplayName() != null && !m.getSenderDisplayName().isEmpty()) return (m.getSenderDisplayName() != null && !m.getSenderDisplayName().isEmpty())
@@ -166,6 +220,9 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
return "Customer"; return "Customer";
} }
/**
* Formats the timestamp string into readable format.
*/
private static String formatTimestamp(String timestamp) { private static String formatTimestamp(String timestamp) {
if (timestamp == null || timestamp.isEmpty()) return ""; if (timestamp == null || timestamp.isEmpty()) return "";
try { 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) { 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) { if (m.getAttachmentName() != null || m.getAttachmentMimeType() != null) {
// Construct the download URL using the message ID // Construct the download URL using the message ID
String url; 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; String cleanBase = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl;
url = cleanBase + "/api/v1/chat/messages/" + m.getId() + "/attachment"; url = cleanBase + "/api/v1/chat/messages/" + m.getId() + "/attachment";
} else { } else {
url = m.getAttachmentUrl(); // Fallback url = m.getAttachmentUrl();
} }
if (url == null) { if (url == null) {
@@ -208,7 +268,7 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
.build()); .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(); String signatureKey = (m.getTimestamp() != null ? m.getTimestamp() : "") + m.getId();
Glide.with(iv.getContext()).clear(iv); Glide.with(iv.getContext()).clear(iv);

View File

@@ -31,7 +31,9 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
void onSelectionChanged(int selectedCount); void onSelectionChanged(int selectedCount);
} }
//Constructor /**
* Constructor for PetAdapter.
*/
public PetAdapter(List<PetDTO> petList, OnPetClickListener petClickListener) { public PetAdapter(List<PetDTO> petList, OnPetClickListener petClickListener) {
this.petList = petList; this.petList = petList;
this.petClickListener = petClickListener; 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) { public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl; this.baseUrl = baseUrl;
} }
/**
* Sets the authentication token
*/
public void setToken(String token) { public void setToken(String token) {
this.token = token; this.token = token;
} }
/**
* Returns a list of IDs for the currently selected pet items.
*/
@Override @Override
public List<String> getSelectedKeys() { public List<String> getSelectedKeys() {
return selectionHelper.getSelectedKeys(); return selectionHelper.getSelectedKeys();
} }
/**
* Resets the selection state, deselecting all items.
*/
@Override @Override
public void clearSelection() { public void clearSelection() {
selectionHelper.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 { public static class PetViewHolder extends RecyclerView.ViewHolder {
private final ItemPetBinding binding; private final ItemPetBinding binding;
/**
* Initializes the ViewHolder with view binding.
*/
public PetViewHolder(@NonNull ItemPetBinding binding) { public PetViewHolder(@NonNull ItemPetBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
} }
} }
// Create a new row view /**
* Inflates the layout for a pet item and creates the ViewHolder.
*/
@NonNull @NonNull
@Override @Override
public PetViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 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); return new PetViewHolder(binding);
} }
//populate the row with pet data /**
* Binds pet data to the UI components.
*/
@Override @Override
public void onBindViewHolder(@NonNull PetViewHolder holder, int position) { public void onBindViewHolder(@NonNull PetViewHolder holder, int position) {
PetDTO pet = petList.get(position); PetDTO pet = petList.get(position);
@@ -103,7 +126,7 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
binding.tvPetStatus.setText(pet.getPetStatus()); 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) { if (pet.getPetStatus() != null) {
switch (pet.getPetStatus()) { switch (pet.getPetStatus()) {
case "Available": 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 @Override
public int getItemCount() { public int getItemCount() {
return petList.size(); return petList.size();

View File

@@ -22,28 +22,46 @@ public class ProductAdapter extends RecyclerView.Adapter<ProductAdapter.ProductV
void onProductClick(int position); void onProductClick(int position);
} }
/**
* Constructor for ProductAdapter.
*/
public ProductAdapter(List<ProductDTO> productList, OnProductClickListener listener) { public ProductAdapter(List<ProductDTO> productList, OnProductClickListener listener) {
this.productList = productList; this.productList = productList;
this.listener = listener; this.listener = listener;
} }
/**
* Sets the base URL for fetching product images.
*/
public void setBaseUrl(String baseUrl) { public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl; this.baseUrl = baseUrl;
} }
/**
* Sets the authentication token
*/
public void setToken(String token) { public void setToken(String token) {
this.token = token; this.token = token;
} }
/**
* ViewHolder class for product items.
*/
public static class ProductViewHolder extends RecyclerView.ViewHolder { public static class ProductViewHolder extends RecyclerView.ViewHolder {
final ItemProductBinding binding; final ItemProductBinding binding;
/**
* Initializes the ViewHolder with view binding.
*/
public ProductViewHolder(@NonNull ItemProductBinding binding) { public ProductViewHolder(@NonNull ItemProductBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
} }
} }
/**
* Inflates the layout for a product item and creates the ViewHolder.
*/
@NonNull @NonNull
@Override @Override
public ProductViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public ProductViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -51,6 +69,9 @@ public class ProductAdapter extends RecyclerView.Adapter<ProductAdapter.ProductV
return new ProductViewHolder(binding); return new ProductViewHolder(binding);
} }
/**
* Binds product data to the UI components.
*/
@Override @Override
public void onBindViewHolder(@NonNull ProductViewHolder holder, int position) { public void onBindViewHolder(@NonNull ProductViewHolder holder, int position) {
ProductDTO p = productList.get(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)); holder.itemView.setOnClickListener(v -> listener.onProductClick(position));
} }
/**
* Returns the total number of product items in the list.
*/
@Override @Override
public int getItemCount() { return productList.size(); } public int getItemCount() { return productList.size(); }
} }

View File

@@ -25,6 +25,9 @@ public class ProductSupplierAdapter extends RecyclerView.Adapter<ProductSupplier
void onSelectionChanged(int count); void onSelectionChanged(int count);
} }
/**
* Constructor for ProductSupplierAdapter.
*/
public ProductSupplierAdapter(List<ProductSupplierDTO> list, OnProductSupplierClickListener listener) { public ProductSupplierAdapter(List<ProductSupplierDTO> list, OnProductSupplierClickListener listener) {
this.list = list; this.list = list;
this.listener = listener; this.listener = listener;
@@ -41,25 +44,40 @@ public class ProductSupplierAdapter extends RecyclerView.Adapter<ProductSupplier
}); });
} }
/**
* Returns the list of selected keys for bulk deletion.
*/
@Override @Override
public List<String> getSelectedKeys() { public List<String> getSelectedKeys() {
return selectionHelper.getSelectedKeys(); return selectionHelper.getSelectedKeys();
} }
/**
* Clears all selected items and exits selection mode.
*/
@Override @Override
public void clearSelection() { public void clearSelection() {
selectionHelper.clearSelection(); selectionHelper.clearSelection();
} }
/**
* ViewHolder for Product-Supplier relationship items.
*/
public static class PSViewHolder extends RecyclerView.ViewHolder { public static class PSViewHolder extends RecyclerView.ViewHolder {
final ItemProductSupplierBinding binding; final ItemProductSupplierBinding binding;
/**
* Initializes the ViewHolder with view binding.
*/
public PSViewHolder(@NonNull ItemProductSupplierBinding binding) { public PSViewHolder(@NonNull ItemProductSupplierBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
} }
} }
/**
* Inflates the layout for a Product-Supplier item.
*/
@NonNull @NonNull
@Override @Override
public PSViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public PSViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -67,6 +85,9 @@ public class ProductSupplierAdapter extends RecyclerView.Adapter<ProductSupplier
return new PSViewHolder(binding); return new PSViewHolder(binding);
} }
/**
* Binds product-supplier data to the UI components.
*/
@Override @Override
public void onBindViewHolder(@NonNull PSViewHolder holder, int position) { public void onBindViewHolder(@NonNull PSViewHolder holder, int position) {
ProductSupplierDTO ps = list.get(position); ProductSupplierDTO ps = list.get(position);

View File

@@ -17,20 +17,32 @@ public class PurchaseOrderAdapter extends RecyclerView.Adapter<PurchaseOrderAdap
void onPurchaseOrderClick(int position); void onPurchaseOrderClick(int position);
} }
/**
* Constructor for PurchaseOrderAdapter.
*/
public PurchaseOrderAdapter(List<PurchaseOrderDTO> list, OnPurchaseOrderClickListener listener) { public PurchaseOrderAdapter(List<PurchaseOrderDTO> list, OnPurchaseOrderClickListener listener) {
this.list = list; this.list = list;
this.listener = listener; this.listener = listener;
} }
/**
* ViewHolder for Purchase Order items.
*/
public static class POViewHolder extends RecyclerView.ViewHolder { public static class POViewHolder extends RecyclerView.ViewHolder {
final ItemPurchaseOrderBinding binding; final ItemPurchaseOrderBinding binding;
/**
* Initializes the ViewHolder with view binding.
*/
public POViewHolder(@NonNull ItemPurchaseOrderBinding binding) { public POViewHolder(@NonNull ItemPurchaseOrderBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
} }
} }
/**
* Inflates the layout for a Purchase Order item.
*/
@NonNull @NonNull
@Override @Override
public POViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public POViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -38,6 +50,9 @@ public class PurchaseOrderAdapter extends RecyclerView.Adapter<PurchaseOrderAdap
return new POViewHolder(binding); return new POViewHolder(binding);
} }
/**
* Binds purchase order data to the UI components.
*/
@Override @Override
public void onBindViewHolder(@NonNull POViewHolder holder, int position) { public void onBindViewHolder(@NonNull POViewHolder holder, int position) {
PurchaseOrderDTO po = list.get(position); PurchaseOrderDTO po = list.get(position);

View File

@@ -20,20 +20,32 @@ public class SaleAdapter extends RecyclerView.Adapter<SaleAdapter.SaleViewHolder
void onSaleClick(int position); void onSaleClick(int position);
} }
/**
* Constructor for SaleAdapter.
*/
public SaleAdapter(List<SaleDTO> saleList, OnSaleClickListener listener) { public SaleAdapter(List<SaleDTO> saleList, OnSaleClickListener listener) {
this.saleList = saleList; this.saleList = saleList;
this.listener = listener; this.listener = listener;
} }
/**
* ViewHolder for Sale record items.
*/
public static class SaleViewHolder extends RecyclerView.ViewHolder { public static class SaleViewHolder extends RecyclerView.ViewHolder {
final ItemSaleBinding binding; final ItemSaleBinding binding;
/**
* Initializes the ViewHolder with view binding.
*/
public SaleViewHolder(@NonNull ItemSaleBinding binding) { public SaleViewHolder(@NonNull ItemSaleBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
} }
} }
/**
* Inflates the layout for a Sale record item.
*/
@NonNull @NonNull
@Override @Override
public SaleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public SaleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -41,6 +53,9 @@ public class SaleAdapter extends RecyclerView.Adapter<SaleAdapter.SaleViewHolder
return new SaleViewHolder(binding); return new SaleViewHolder(binding);
} }
/**
* Binds sale transaction data to the UI components.
*/
@Override @Override
public void onBindViewHolder(@NonNull SaleViewHolder holder, int position) { public void onBindViewHolder(@NonNull SaleViewHolder holder, int position) {
SaleDTO s = saleList.get(position); SaleDTO s = saleList.get(position);

View File

@@ -31,6 +31,9 @@ public class ServiceAdapter extends RecyclerView.Adapter<ServiceAdapter.ServiceV
void onSelectionChanged(int count); void onSelectionChanged(int count);
} }
/**
* Constructor for ServiceAdapter.
*/
public ServiceAdapter(List<ServiceDTO> serviceList, OnServiceClickListener clickListener) { public ServiceAdapter(List<ServiceDTO> serviceList, OnServiceClickListener clickListener) {
this.serviceList = serviceList; this.serviceList = serviceList;
this.clickListener = clickListener; 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 @Override
public List<String> getSelectedKeys() { public List<String> getSelectedKeys() {
return selectionHelper.getSelectedKeys(); return selectionHelper.getSelectedKeys();
} }
/**
* Clears all selected items and exits selection mode.
*/
@Override @Override
public void clearSelection() { public void clearSelection() {
selectionHelper.clearSelection(); selectionHelper.clearSelection();
} }
/** /**
* ViewHolder class for service items. * ViewHolder for Service items.
*/ */
public static class ServiceViewHolder extends RecyclerView.ViewHolder { public static class ServiceViewHolder extends RecyclerView.ViewHolder {
final ItemServiceBinding binding; final ItemServiceBinding binding;
/**
* Initializes the ViewHolder with view binding.
*/
public ServiceViewHolder(@NonNull ItemServiceBinding binding) { public ServiceViewHolder(@NonNull ItemServiceBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
} }
} }
// Create a new row view /**
* Inflates the layout for a Service item.
*/
@NonNull @NonNull
@Override @Override
public ServiceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public ServiceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -77,7 +91,9 @@ public class ServiceAdapter extends RecyclerView.Adapter<ServiceAdapter.ServiceV
return new ServiceViewHolder(binding); return new ServiceViewHolder(binding);
} }
//populate the row with service data /**
* Binds service data to the UI components.
*/
@Override @Override
public void onBindViewHolder(@NonNull ServiceViewHolder holder, int position) { public void onBindViewHolder(@NonNull ServiceViewHolder holder, int position) {
ServiceDTO service = serviceList.get(position); ServiceDTO service = serviceList.get(position);

View File

@@ -26,7 +26,9 @@ public class SupplierAdapter extends RecyclerView.Adapter<SupplierAdapter.Suppli
void onSelectionChanged(int count); void onSelectionChanged(int count);
} }
//Constructor /**
* Constructor for SupplierAdapter.
*/
public SupplierAdapter(List<SupplierDTO> supplierList, OnSupplierClickListener supplierClickListener) { public SupplierAdapter(List<SupplierDTO> supplierList, OnSupplierClickListener supplierClickListener) {
this.supplierList = supplierList; this.supplierList = supplierList;
this.supplierClickListener = supplierClickListener; 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 @Override
public List<String> getSelectedKeys() { public List<String> getSelectedKeys() {
return selectionHelper.getSelectedKeys(); return selectionHelper.getSelectedKeys();
} }
/**
* Clears all selected items and exits selection mode.
*/
@Override @Override
public void clearSelection() { public void clearSelection() {
selectionHelper.clearSelection(); selectionHelper.clearSelection();
} }
// Get the controls of each row in recycler view /**
* ViewHolder for Supplier items.
*/
public static class SupplierViewHolder extends RecyclerView.ViewHolder { public static class SupplierViewHolder extends RecyclerView.ViewHolder {
final ItemSupplierBinding binding; final ItemSupplierBinding binding;
/**
* Initializes the ViewHolder with view binding.
*/
public SupplierViewHolder(@NonNull ItemSupplierBinding binding) { public SupplierViewHolder(@NonNull ItemSupplierBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
} }
} }
// Create a new row view /**
* Inflates the layout for a Supplier item.
*/
@NonNull @NonNull
@Override @Override
public SupplierViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public SupplierViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -71,7 +86,9 @@ public class SupplierAdapter extends RecyclerView.Adapter<SupplierAdapter.Suppli
return new SupplierViewHolder(binding); return new SupplierViewHolder(binding);
} }
//populate the row with supplier data /**
* Binds supplier data to the UI components.
*/
@Override @Override
public void onBindViewHolder(@NonNull SupplierViewHolder holder, int position) { public void onBindViewHolder(@NonNull SupplierViewHolder holder, int position) {
SupplierDTO supplier = supplierList.get(position); SupplierDTO supplier = supplierList.get(position);

View File

@@ -12,9 +12,8 @@ import androidx.core.content.ContextCompat;
import com.example.petstoremobile.R; import com.example.petstoremobile.R;
import java.util.List; import java.util.List;
/** // A class that overrides the arrayAdapter so the text color changes based on theme
* A class that overrides the arrayAdapter so the text color is white and background is transparent. // Used to make spinners have white text on dark background no matter the theme
*/
public class WhiteTextArrayAdapter<T> extends ArrayAdapter<T> { public class WhiteTextArrayAdapter<T> extends ArrayAdapter<T> {
public WhiteTextArrayAdapter(@NonNull Context context, int resource, @NonNull T[] objects) { public WhiteTextArrayAdapter(@NonNull Context context, int resource, @NonNull T[] objects) {
super(context, resource, objects); super(context, resource, objects);

View File

@@ -8,8 +8,10 @@ import retrofit2.Call;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.Query; import retrofit2.http.Query;
// api calls to get activity logs
public interface ActivityLogApi { public interface ActivityLogApi {
// Get activity logs with filters
@GET("api/v1/activity-logs") @GET("api/v1/activity-logs")
Call<List<ActivityLogDTO>> getActivityLogs( Call<List<ActivityLogDTO>> getActivityLogs(
@Query("limit") int limit, @Query("limit") int limit,

View File

@@ -14,8 +14,10 @@ import retrofit2.http.PUT;
import retrofit2.http.Path; import retrofit2.http.Path;
import retrofit2.http.Query; import retrofit2.http.Query;
// api calls for adoptions
public interface AdoptionApi { public interface AdoptionApi {
// Get all adoptions with filters
@GET("api/v1/adoptions") @GET("api/v1/adoptions")
Call<PageResponse<AdoptionDTO>> getAllAdoptions( Call<PageResponse<AdoptionDTO>> getAllAdoptions(
@Query("page") int page, @Query("page") int page,
@@ -27,18 +29,23 @@ public interface AdoptionApi {
@Query("employeeId") Long employeeId, @Query("employeeId") Long employeeId,
@Query("sort") String sort); @Query("sort") String sort);
// Get adoption by id
@GET("api/v1/adoptions/{id}") @GET("api/v1/adoptions/{id}")
Call<AdoptionDTO> getAdoptionById(@Path("id") Long id); Call<AdoptionDTO> getAdoptionById(@Path("id") Long id);
// Create adoption
@POST("api/v1/adoptions") @POST("api/v1/adoptions")
Call<AdoptionDTO> createAdoption(@Body AdoptionDTO adoption); Call<AdoptionDTO> createAdoption(@Body AdoptionDTO adoption);
// Update adoption
@PUT("api/v1/adoptions/{id}") @PUT("api/v1/adoptions/{id}")
Call<AdoptionDTO> updateAdoption(@Path("id") Long id, @Body AdoptionDTO adoption); Call<AdoptionDTO> updateAdoption(@Path("id") Long id, @Body AdoptionDTO adoption);
// Delete adoption
@DELETE("api/v1/adoptions/{id}") @DELETE("api/v1/adoptions/{id}")
Call<Void> deleteAdoption(@Path("id") Long id); Call<Void> deleteAdoption(@Path("id") Long id);
// Bulk delete adoptions
@HTTP(method = "DELETE", path = "api/v1/adoptions", hasBody = true) @HTTP(method = "DELETE", path = "api/v1/adoptions", hasBody = true)
Call<Void> bulkDeleteAdoptions(@Body BulkDeleteRequest request); Call<Void> bulkDeleteAdoptions(@Body BulkDeleteRequest request);
} }

View File

@@ -14,8 +14,10 @@ import retrofit2.http.PUT;
import retrofit2.http.Path; import retrofit2.http.Path;
import retrofit2.http.Query; import retrofit2.http.Query;
// api calls for appointments
public interface AppointmentApi { public interface AppointmentApi {
// Get all appointments with filters
@GET("api/v1/appointments") @GET("api/v1/appointments")
Call<PageResponse<AppointmentDTO>> getAllAppointments( Call<PageResponse<AppointmentDTO>> getAllAppointments(
@Query("page") int page, @Query("page") int page,
@@ -27,18 +29,23 @@ public interface AppointmentApi {
@Query("employeeId") Long employeeId, @Query("employeeId") Long employeeId,
@Query("sort") String sort); @Query("sort") String sort);
// Get appointment by id
@GET("api/v1/appointments/{id}") @GET("api/v1/appointments/{id}")
Call<AppointmentDTO> getAppointmentById(@Path("id") Long id); Call<AppointmentDTO> getAppointmentById(@Path("id") Long id);
// Create appointment
@POST("api/v1/appointments") @POST("api/v1/appointments")
Call<AppointmentDTO> createAppointment(@Body AppointmentDTO appointment); Call<AppointmentDTO> createAppointment(@Body AppointmentDTO appointment);
// Update appointment
@PUT("api/v1/appointments/{id}") @PUT("api/v1/appointments/{id}")
Call<AppointmentDTO> updateAppointment(@Path("id") Long id, @Body AppointmentDTO appointment); Call<AppointmentDTO> updateAppointment(@Path("id") Long id, @Body AppointmentDTO appointment);
// Delete appointment
@DELETE("api/v1/appointments/{id}") @DELETE("api/v1/appointments/{id}")
Call<Void> deleteAppointment(@Path("id") Long id); Call<Void> deleteAppointment(@Path("id") Long id);
// Bulk delete appointments
@HTTP(method = "DELETE", path = "api/v1/appointments", hasBody = true) @HTTP(method = "DELETE", path = "api/v1/appointments", hasBody = true)
Call<Void> bulkDeleteAppointments(@Body BulkDeleteRequest request); Call<Void> bulkDeleteAppointments(@Body BulkDeleteRequest request);
} }

View File

@@ -6,8 +6,10 @@ import retrofit2.Call;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.Query; import retrofit2.http.Query;
// api calls for categories
public interface CategoryApi { public interface CategoryApi {
// Get all categories with pagination
@GET("api/v1/categories") @GET("api/v1/categories")
Call<PageResponse<CategoryDTO>> getAllCategories( Call<PageResponse<CategoryDTO>> getAllCategories(
@Query("page") int page, @Query("page") int page,

View File

@@ -12,15 +12,18 @@ import retrofit2.http.GET;
import retrofit2.http.PUT; import retrofit2.http.PUT;
import retrofit2.http.Path; import retrofit2.http.Path;
//api calls to get conversations // api calls for chat conversations
public interface ChatApi { public interface ChatApi {
// Get all conversations
@GET("api/v1/chat/conversations") @GET("api/v1/chat/conversations")
Call<List<ConversationDTO>> getAllConversations(); Call<List<ConversationDTO>> getAllConversations();
// Get conversation by id
@GET("api/v1/chat/conversations/{conversationId}") @GET("api/v1/chat/conversations/{conversationId}")
Call<ConversationDTO> getConversationById(@Path("conversationId") Long conversationId); Call<ConversationDTO> getConversationById(@Path("conversationId") Long conversationId);
// Update conversation status
@PUT("api/v1/chat/conversations/{conversationId}") @PUT("api/v1/chat/conversations/{conversationId}")
Call<ConversationDTO> updateConversationStatus(@Path("conversationId") Long conversationId, @Body UpdateConversationStatusRequest request); Call<ConversationDTO> updateConversationStatus(@Path("conversationId") Long conversationId, @Body UpdateConversationStatusRequest request);

View File

@@ -15,23 +15,30 @@ import retrofit2.http.PUT;
import retrofit2.http.Path; import retrofit2.http.Path;
import retrofit2.http.Query; import retrofit2.http.Query;
// api calls for customers
public interface CustomerApi { public interface CustomerApi {
// Get all customers with pagination
@GET("api/v1/customers") @GET("api/v1/customers")
Call<PageResponse<CustomerDTO>> getAllCustomers(@Query("page") int page, @Query("size") int size); Call<PageResponse<CustomerDTO>> getAllCustomers(@Query("page") int page, @Query("size") int size);
// Get customer by id
@GET("api/v1/customers/{customerId}") @GET("api/v1/customers/{customerId}")
Call<CustomerDTO> getCustomerById(@Path("customerId") Long customerId); Call<CustomerDTO> getCustomerById(@Path("customerId") Long customerId);
// Update customer
@PUT("api/v1/customers/{customerId}") @PUT("api/v1/customers/{customerId}")
Call<CustomerDTO> updateCustomer(@Path("customerId") Long customerId, @Body CustomerDTO customer); Call<CustomerDTO> updateCustomer(@Path("customerId") Long customerId, @Body CustomerDTO customer);
// Delete customer
@DELETE("api/v1/customers/{customerId}") @DELETE("api/v1/customers/{customerId}")
Call<Void> deleteCustomer(@Path("customerId") Long customerId); Call<Void> deleteCustomer(@Path("customerId") Long customerId);
// Register customer
@POST("api/v1/auth/register") @POST("api/v1/auth/register")
Call<CustomerDTO> registerCustomer(@Body CustomerDTO customer); Call<CustomerDTO> registerCustomer(@Body CustomerDTO customer);
// Get customer dropdowns
@GET("api/v1/dropdowns/customers") @GET("api/v1/dropdowns/customers")
Call<List<DropdownDTO>> getCustomerDropdowns(); Call<List<DropdownDTO>> getCustomerDropdowns();
} }

View File

@@ -5,28 +5,28 @@ import com.example.petstoremobile.dtos.PageResponse;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.*; import retrofit2.http.*;
// api calls for employees
public interface EmployeeApi { public interface EmployeeApi {
// Get all employees with pagination
@GET("api/v1/employees") @GET("api/v1/employees")
Call<PageResponse<EmployeeDTO>> getAllEmployees( Call<PageResponse<EmployeeDTO>> getAllEmployees(
@Query("page") int page, @Query("page") int page,
@Query("size") int size); @Query("size") int size);
// Get employee by id
@GET("api/v1/employees/{id}") @GET("api/v1/employees/{id}")
Call<EmployeeDTO> getEmployeeById(@Path("id") Long id); Call<EmployeeDTO> getEmployeeById(@Path("id") Long id);
// Create employee
@POST("api/v1/employees") @POST("api/v1/employees")
Call<EmployeeDTO> createEmployee(@Body EmployeeDTO employee); Call<EmployeeDTO> createEmployee(@Body EmployeeDTO employee);
// Update employee
@PUT("api/v1/employees/{id}") @PUT("api/v1/employees/{id}")
Call<EmployeeDTO> updateEmployee(@Path("id") Long id, @Body EmployeeDTO employee); Call<EmployeeDTO> updateEmployee(@Path("id") Long id, @Body EmployeeDTO employee);
// Delete employee
@DELETE("api/v1/employees/{id}") @DELETE("api/v1/employees/{id}")
Call<Void> deleteEmployee(@Path("id") Long id); Call<Void> deleteEmployee(@Path("id") Long id);
} }

View File

@@ -14,8 +14,10 @@ import retrofit2.http.PUT;
import retrofit2.http.Path; import retrofit2.http.Path;
import retrofit2.http.Query; import retrofit2.http.Query;
// api calls for inventory
public interface InventoryApi { public interface InventoryApi {
// Get all inventory with filters
@GET("api/v1/inventory") @GET("api/v1/inventory")
Call<PageResponse<InventoryDTO>> getAllInventory( Call<PageResponse<InventoryDTO>> getAllInventory(
@Query("page") int page, @Query("page") int page,
@@ -24,23 +26,23 @@ public interface InventoryApi {
@Query("storeId") Long storeId, @Query("storeId") Long storeId,
@Query("sort") String sort); @Query("sort") String sort);
// GET /api/v1/inventory/{id} // Get inventory by id
@GET("api/v1/inventory/{id}") @GET("api/v1/inventory/{id}")
Call<InventoryDTO> getInventoryById(@Path("id") Long id); Call<InventoryDTO> getInventoryById(@Path("id") Long id);
// POST /api/v1/inventory // Create inventory
@POST("api/v1/inventory") @POST("api/v1/inventory")
Call<InventoryDTO> createInventory(@Body InventoryDTO request); Call<InventoryDTO> createInventory(@Body InventoryDTO request);
// PUT /api/v1/inventory/{id} // Update inventory
@PUT("api/v1/inventory/{id}") @PUT("api/v1/inventory/{id}")
Call<InventoryDTO> updateInventory(@Path("id") Long id, @Body InventoryDTO request); Call<InventoryDTO> updateInventory(@Path("id") Long id, @Body InventoryDTO request);
// DELETE /api/v1/inventory/{id} // Delete inventory
@DELETE("api/v1/inventory/{id}") @DELETE("api/v1/inventory/{id}")
Call<Void> deleteInventory(@Path("id") Long 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) @HTTP(method = "DELETE", path = "api/v1/inventory", hasBody = true)
Call<Void> bulkDeleteInventory(@Body BulkDeleteRequest request); Call<Void> bulkDeleteInventory(@Body BulkDeleteRequest request);
} }

View File

@@ -15,15 +15,18 @@ import retrofit2.http.Part;
import retrofit2.http.Path; import retrofit2.http.Path;
import retrofit2.http.Streaming; import retrofit2.http.Streaming;
//api calls to get and send messages // api calls for messages
public interface MessageApi { public interface MessageApi {
// Get messages for a conversation
@GET("api/v1/chat/conversations/{id}/messages") @GET("api/v1/chat/conversations/{id}/messages")
Call<List<MessageDTO>> getMessages(@Path("id") Long conversationId); Call<List<MessageDTO>> getMessages(@Path("id") Long conversationId);
// Send a message
@POST("api/v1/chat/conversations/{id}/messages") @POST("api/v1/chat/conversations/{id}/messages")
Call<MessageDTO> sendMessage(@Path("id") Long conversationId, @Body SendMessageRequest request); Call<MessageDTO> sendMessage(@Path("id") Long conversationId, @Body SendMessageRequest request);
// Send a message with attachment
@Multipart @Multipart
@POST("api/v1/chat/conversations/{id}/attachments") @POST("api/v1/chat/conversations/{id}/attachments")
Call<MessageDTO> sendMessageWithAttachment( Call<MessageDTO> sendMessageWithAttachment(
@@ -32,6 +35,7 @@ public interface MessageApi {
@Part MultipartBody.Part file @Part MultipartBody.Part file
); );
// Download attachment
@GET("api/v1/chat/messages/{id}/attachment") @GET("api/v1/chat/messages/{id}/attachment")
@Streaming @Streaming
Call<ResponseBody> downloadAttachment(@Path("id") Long messageId); Call<ResponseBody> downloadAttachment(@Path("id") Long messageId);

View File

@@ -20,7 +20,7 @@ import retrofit2.http.Part;
import retrofit2.http.Path; import retrofit2.http.Path;
import retrofit2.http.Query; import retrofit2.http.Query;
//api calls to CRUD pets // api calls to CRUD pets
public interface PetApi { public interface PetApi {
// endpoint for downloading the pet's image file // endpoint for downloading the pet's image file
String PET_IMAGE_PATH = "api/v1/pets/%d/image"; String PET_IMAGE_PATH = "api/v1/pets/%d/image";
@@ -38,18 +38,23 @@ public interface PetApi {
@Query("sort") String sort @Query("sort") String sort
); );
// Get pets by customer id
@GET("api/v1/dropdowns/customers/{customerId}/pets") @GET("api/v1/dropdowns/customers/{customerId}/pets")
Call<List<DropdownDTO>> getCustomerPets(@Path("customerId") Long customerId); Call<List<DropdownDTO>> getCustomerPets(@Path("customerId") Long customerId);
// Get adoption pets
@GET("api/v1/dropdowns/adoption-pets") @GET("api/v1/dropdowns/adoption-pets")
Call<List<DropdownDTO>> getAdoptionPets(@Query("storeId") Long storeId); Call<List<DropdownDTO>> getAdoptionPets(@Query("storeId") Long storeId);
// Get pet dropdowns
@GET("api/v1/dropdowns/pets") @GET("api/v1/dropdowns/pets")
Call<List<DropdownDTO>> getPetDropdowns(); Call<List<DropdownDTO>> getPetDropdowns();
// Get pet species dropdowns
@GET("api/v1/dropdowns/pet-species") @GET("api/v1/dropdowns/pet-species")
Call<List<DropdownDTO>> getPetSpeciesDropdowns(); Call<List<DropdownDTO>> getPetSpeciesDropdowns();
// Get pet breeds dropdowns
@GET("api/v1/dropdowns/pet-breeds") @GET("api/v1/dropdowns/pet-breeds")
Call<List<DropdownDTO>> getPetBreedsDropdowns(@Query("species") String species); Call<List<DropdownDTO>> getPetBreedsDropdowns(@Query("species") String species);
@@ -81,5 +86,4 @@ public interface PetApi {
// Delete pet image // Delete pet image
@DELETE("api/v1/pets/{id}/image") @DELETE("api/v1/pets/{id}/image")
Call<Void> deletePetImage(@Path("id") Long id); Call<Void> deletePetImage(@Path("id") Long id);
} }

View File

@@ -9,9 +9,12 @@ import retrofit2.http.*;
import java.util.List; import java.util.List;
// api calls for products
public interface ProductApi { public interface ProductApi {
// endpoint for downloading the product's image file
String PRODUCT_IMAGE_PATH = "api/v1/products/%d/image"; String PRODUCT_IMAGE_PATH = "api/v1/products/%d/image";
// Get all products with filters
@GET("api/v1/products") @GET("api/v1/products")
Call<PageResponse<ProductDTO>> getAllProducts( Call<PageResponse<ProductDTO>> getAllProducts(
@Query("q") String query, @Query("q") String query,
@@ -20,28 +23,36 @@ public interface ProductApi {
@Query("size") int size, @Query("size") int size,
@Query("sort") String sort); @Query("sort") String sort);
// Get product by id
@GET("api/v1/products/{id}") @GET("api/v1/products/{id}")
Call<ProductDTO> getProductById(@Path("id") Long id); Call<ProductDTO> getProductById(@Path("id") Long id);
// Create product
@POST("api/v1/products") @POST("api/v1/products")
Call<ProductDTO> createProduct(@Body ProductDTO product); Call<ProductDTO> createProduct(@Body ProductDTO product);
// Update product
@PUT("api/v1/products/{id}") @PUT("api/v1/products/{id}")
Call<ProductDTO> updateProduct(@Path("id") Long id, @Body ProductDTO product); Call<ProductDTO> updateProduct(@Path("id") Long id, @Body ProductDTO product);
// Delete product
@DELETE("api/v1/products/{id}") @DELETE("api/v1/products/{id}")
Call<Void> deleteProduct(@Path("id") Long id); Call<Void> deleteProduct(@Path("id") Long id);
// Upload product image
@Multipart @Multipart
@POST("api/v1/products/{id}/image") @POST("api/v1/products/{id}/image")
Call<Void> uploadProductImage(@Path("id") Long id, @Part MultipartBody.Part image); Call<Void> uploadProductImage(@Path("id") Long id, @Part MultipartBody.Part image);
// Delete product image
@DELETE("api/v1/products/{id}/image") @DELETE("api/v1/products/{id}/image")
Call<Void> deleteProductImage(@Path("id") Long id); Call<Void> deleteProductImage(@Path("id") Long id);
// Get product dropdowns
@GET("api/v1/dropdowns/products") @GET("api/v1/dropdowns/products")
Call<List<DropdownDTO>> getProductDropdowns(); Call<List<DropdownDTO>> getProductDropdowns();
// Get category dropdowns
@GET("api/v1/dropdowns/categories") @GET("api/v1/dropdowns/categories")
Call<List<DropdownDTO>> getCategoryDropdowns(); Call<List<DropdownDTO>> getCategoryDropdowns();
} }

View File

@@ -6,8 +6,10 @@ import com.example.petstoremobile.dtos.ProductSupplierDTO;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.*; import retrofit2.http.*;
// api calls for product-supplier relationships
public interface ProductSupplierApi { public interface ProductSupplierApi {
// Get all product-suppliers with filters
@GET("api/v1/product-suppliers") @GET("api/v1/product-suppliers")
Call<PageResponse<ProductSupplierDTO>> getAllProductSuppliers( Call<PageResponse<ProductSupplierDTO>> getAllProductSuppliers(
@Query("page") int page, @Query("page") int page,
@@ -17,24 +19,30 @@ public interface ProductSupplierApi {
@Query("supplierId") Long supplierId, @Query("supplierId") Long supplierId,
@Query("sort") String sort); @Query("sort") String sort);
// Get product-supplier by composite id
@GET("api/v1/product-suppliers/{productId}/{supplierId}") @GET("api/v1/product-suppliers/{productId}/{supplierId}")
Call<ProductSupplierDTO> getProductSupplierById( Call<ProductSupplierDTO> getProductSupplierById(
@Path("productId") Long productId, @Path("productId") Long productId,
@Path("supplierId") Long supplierId); @Path("supplierId") Long supplierId);
// Create product-supplier
@POST("api/v1/product-suppliers") @POST("api/v1/product-suppliers")
Call<ProductSupplierDTO> createProductSupplier(@Body ProductSupplierDTO dto); Call<ProductSupplierDTO> createProductSupplier(@Body ProductSupplierDTO dto);
// Update product-supplier
@PUT("api/v1/product-suppliers/{productId}/{supplierId}") @PUT("api/v1/product-suppliers/{productId}/{supplierId}")
Call<ProductSupplierDTO> updateProductSupplier( Call<ProductSupplierDTO> updateProductSupplier(
@Path("productId") Long productId, @Path("productId") Long productId,
@Path("supplierId") Long supplierId, @Path("supplierId") Long supplierId,
@Body ProductSupplierDTO dto); @Body ProductSupplierDTO dto);
// Delete product-supplier
@DELETE("api/v1/product-suppliers/{productId}/{supplierId}") @DELETE("api/v1/product-suppliers/{productId}/{supplierId}")
Call<Void> deleteProductSupplier( Call<Void> deleteProductSupplier(
@Path("productId") Long productId, @Path("productId") Long productId,
@Path("supplierId") Long supplierId); @Path("supplierId") Long supplierId);
// Bulk delete product-suppliers
@HTTP(method = "DELETE", path = "api/v1/product-suppliers", hasBody = true) @HTTP(method = "DELETE", path = "api/v1/product-suppliers", hasBody = true)
Call<Void> bulkDeleteProductSuppliers(@Body BulkDeleteRequest request); Call<Void> bulkDeleteProductSuppliers(@Body BulkDeleteRequest request);
} }

View File

@@ -7,8 +7,10 @@ import retrofit2.http.GET;
import retrofit2.http.Path; import retrofit2.http.Path;
import retrofit2.http.Query; import retrofit2.http.Query;
// api calls for purchase orders
public interface PurchaseOrderApi { public interface PurchaseOrderApi {
// Get all purchase orders with filters
@GET("api/v1/purchase-orders") @GET("api/v1/purchase-orders")
Call<PageResponse<PurchaseOrderDTO>> getAllPurchaseOrders( Call<PageResponse<PurchaseOrderDTO>> getAllPurchaseOrders(
@Query("page") int page, @Query("page") int page,
@@ -17,6 +19,7 @@ public interface PurchaseOrderApi {
@Query("storeId") Long storeId, @Query("storeId") Long storeId,
@Query("sort") String sort); @Query("sort") String sort);
// Get purchase order by id
@GET("api/v1/purchase-orders/{id}") @GET("api/v1/purchase-orders/{id}")
Call<PurchaseOrderDTO> getPurchaseOrderById(@Path("id") Long id); Call<PurchaseOrderDTO> getPurchaseOrderById(@Path("id") Long id);
} }

View File

@@ -6,20 +6,26 @@ import retrofit2.http.*;
import java.util.List; import java.util.List;
// api calls for refunds
public interface RefundApi { public interface RefundApi {
// Get all refunds
@GET("api/v1/refunds") @GET("api/v1/refunds")
Call<List<RefundDTO>> getAllRefunds(); Call<List<RefundDTO>> getAllRefunds();
// Get refund by id
@GET("api/v1/refunds/{id}") @GET("api/v1/refunds/{id}")
Call<RefundDTO> getRefundById(@Path("id") Long id); Call<RefundDTO> getRefundById(@Path("id") Long id);
// Create refund
@POST("api/v1/refunds") @POST("api/v1/refunds")
Call<RefundDTO> createRefund(@Body RefundDTO refund); Call<RefundDTO> createRefund(@Body RefundDTO refund);
// Update refund
@PUT("api/v1/refunds/{id}") @PUT("api/v1/refunds/{id}")
Call<RefundDTO> updateRefund(@Path("id") Long id, @Body RefundDTO refund); Call<RefundDTO> updateRefund(@Path("id") Long id, @Body RefundDTO refund);
// Delete refund
@DELETE("api/v1/refunds/{id}") @DELETE("api/v1/refunds/{id}")
Call<Void> deleteRefund(@Path("id") Long id); Call<Void> deleteRefund(@Path("id") Long id);
} }

View File

@@ -10,8 +10,10 @@ import retrofit2.http.POST;
import retrofit2.http.Path; import retrofit2.http.Path;
import retrofit2.http.Query; import retrofit2.http.Query;
// api calls for sales
public interface SaleApi { public interface SaleApi {
// Get all sales with filters
@GET("api/v1/sales") @GET("api/v1/sales")
Call<PageResponse<SaleDTO>> getAllSales( Call<PageResponse<SaleDTO>> getAllSales(
@Query("page") int page, @Query("page") int page,
@@ -23,9 +25,11 @@ public interface SaleApi {
@Query("customerId") Long customerId, @Query("customerId") Long customerId,
@Query("sort") String sort); @Query("sort") String sort);
// Get sale by id
@GET("api/v1/sales/{id}") @GET("api/v1/sales/{id}")
Call<SaleDTO> getSaleById(@Path("id") Long id); Call<SaleDTO> getSaleById(@Path("id") Long id);
// Create sale
@POST("api/v1/sales") @POST("api/v1/sales")
Call<SaleDTO> createSale(@Body SaleDTO sale); Call<SaleDTO> createSale(@Body SaleDTO sale);
} }

View File

@@ -14,7 +14,7 @@ import retrofit2.http.PUT;
import retrofit2.http.Path; import retrofit2.http.Path;
import retrofit2.http.Query; import retrofit2.http.Query;
//api calls to CRUD services // api calls to CRUD services
public interface ServiceApi { public interface ServiceApi {
// Get all services // Get all services
@GET("api/v1/services") @GET("api/v1/services")

View File

@@ -11,16 +11,20 @@ import retrofit2.http.GET;
import retrofit2.http.Path; import retrofit2.http.Path;
import retrofit2.http.Query; import retrofit2.http.Query;
// api calls for stores
public interface StoreApi { public interface StoreApi {
// Get all stores with pagination
@GET("api/v1/stores") @GET("api/v1/stores")
Call<PageResponse<StoreDTO>> getAllStores( Call<PageResponse<StoreDTO>> getAllStores(
@Query("page") int page, @Query("page") int page,
@Query("size") int size); @Query("size") int size);
// Get store dropdowns
@GET("api/v1/dropdowns/stores") @GET("api/v1/dropdowns/stores")
Call<List<DropdownDTO>> getStoreDropdowns(); Call<List<DropdownDTO>> getStoreDropdowns();
// Get employees of a specific store
@GET("api/v1/dropdowns/stores/{storeId}/employees") @GET("api/v1/dropdowns/stores/{storeId}/employees")
Call<List<DropdownDTO>> getStoreEmployees(@Path("storeId") Long storeId); Call<List<DropdownDTO>> getStoreEmployees(@Path("storeId") Long storeId);
} }

View File

@@ -7,9 +7,12 @@ import retrofit2.Call;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.Query; import retrofit2.http.Query;
// api calls for users
public interface UserApi { public interface UserApi {
// endpoint for downloading the user's avatar file
String AVATAR_PATH = "api/v1/users/%d/avatar/file"; String AVATAR_PATH = "api/v1/users/%d/avatar/file";
// Get all users with filters
@GET("api/v1/users") @GET("api/v1/users")
Call<PageResponse<UserDTO>> getUsers(@Query("role") String role, @Query("page") int page, @Query("size") int size); Call<PageResponse<UserDTO>> getUsers(@Query("role") String role, @Query("page") int page, @Query("size") int size);
} }

View File

@@ -9,6 +9,7 @@ import javax.inject.Singleton;
import dagger.hilt.android.qualifiers.ApplicationContext; import dagger.hilt.android.qualifiers.ApplicationContext;
//Used to save and retrieve login data
@Singleton @Singleton
public class TokenManager { public class TokenManager {
public static final String ACTION_FORCE_LOGOUT = "com.example.petstoremobile.ACTION_FORCE_LOGOUT"; public static final String ACTION_FORCE_LOGOUT = "com.example.petstoremobile.ACTION_FORCE_LOGOUT";

View File

@@ -1,5 +1,8 @@
package com.example.petstoremobile.dtos; package com.example.petstoremobile.dtos;
/**
* Data Transfer Object for activity logs.
*/
public class ActivityLogDTO { public class ActivityLogDTO {
private Long logId; private Long logId;
private String activity; private String activity;

View File

@@ -2,6 +2,9 @@ package com.example.petstoremobile.dtos;
import java.math.BigDecimal; import java.math.BigDecimal;
/**
* Data Transfer Object for pet adoptions.
*/
public class AdoptionDTO { public class AdoptionDTO {
private Long adoptionId; private Long adoptionId;

View File

@@ -1,5 +1,8 @@
package com.example.petstoremobile.dtos; package com.example.petstoremobile.dtos;
/**
* Data Transfer Object for appointments.
*/
public class AppointmentDTO { public class AppointmentDTO {
private Long appointmentId; private Long appointmentId;

View File

@@ -1,6 +1,8 @@
package com.example.petstoremobile.dtos; package com.example.petstoremobile.dtos;
//Used to send login data to the backend /**
* Data Transfer Object for authentication credentials.
*/
public class AuthDTO { public class AuthDTO {
public static class LoginRequest { public static class LoginRequest {
private String username; private String username;

View File

@@ -1,5 +1,8 @@
package com.example.petstoremobile.dtos; package com.example.petstoremobile.dtos;
/**
* Response containing the URL of a newly uploaded avatar.
*/
public class AvatarUploadResponse { public class AvatarUploadResponse {
private String avatarUrl; private String avatarUrl;
private String message; private String message;

View File

@@ -2,6 +2,9 @@ package com.example.petstoremobile.dtos;
import java.util.List; import java.util.List;
/**
* Request body for deleting multiple records at once.
*/
public class BulkDeleteRequest { public class BulkDeleteRequest {
private List<Long> ids; private List<Long> ids;

View File

@@ -1,5 +1,8 @@
package com.example.petstoremobile.dtos; package com.example.petstoremobile.dtos;
/**
* Data Transfer Object for product categories.
*/
public class CategoryDTO { public class CategoryDTO {
private Long categoryId; private Long categoryId;
private String categoryName; private String categoryName;

View File

@@ -1,5 +1,8 @@
package com.example.petstoremobile.dtos; package com.example.petstoremobile.dtos;
/**
* Data Transfer Object for chat conversations.
*/
public class ConversationDTO { public class ConversationDTO {
private Long id; private Long id;
private Long customerId; private Long customerId;

View File

@@ -2,6 +2,9 @@ package com.example.petstoremobile.dtos;
import java.math.BigDecimal; import java.math.BigDecimal;
/**
* Data Transfer Object for coupons.
*/
public class CouponDTO { public class CouponDTO {
private Long couponId; private Long couponId;
private String couponCode; private String couponCode;

View File

@@ -2,6 +2,9 @@ package com.example.petstoremobile.dtos;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
/**
* Data Transfer Object for customers.
*/
public class CustomerDTO { public class CustomerDTO {
@SerializedName("id") @SerializedName("id")
private Long customerId; private Long customerId;

View File

@@ -1,5 +1,8 @@
package com.example.petstoremobile.dtos; package com.example.petstoremobile.dtos;
/**
* Data Transfer Object for simple dropdown selection lists.
*/
public class DropdownDTO { public class DropdownDTO {
private Long id; private Long id;
private String label; private String label;

View File

@@ -1,5 +1,8 @@
package com.example.petstoremobile.dtos; package com.example.petstoremobile.dtos;
/**
* Data Transfer Object for employees.
*/
public class EmployeeDTO { public class EmployeeDTO {
private Long id; private Long id;

View File

@@ -1,7 +1,8 @@
package com.example.petstoremobile.dtos; 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 { public class ErrorResponse {
private String message; private String message;

View File

@@ -1,5 +1,8 @@
package com.example.petstoremobile.dtos; package com.example.petstoremobile.dtos;
/**
* Data Transfer Object for inventory stock.
*/
public class InventoryDTO { public class InventoryDTO {
// Response fields (from backend InventoryResponse) // Response fields (from backend InventoryResponse)
private Long inventoryId; private Long inventoryId;

View File

@@ -2,6 +2,9 @@ package com.example.petstoremobile.dtos;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
/**
* Data Transfer Object for chat messages.
*/
public class MessageDTO { public class MessageDTO {
@SerializedName("id") @SerializedName("id")

View File

@@ -2,7 +2,9 @@ package com.example.petstoremobile.dtos;
import java.util.List; import java.util.List;
//Used to get data from the API /**
* Generic response wrapper for paginated API results.
*/
public class PageResponse<T> { public class PageResponse<T> {
private List<T> content; private List<T> content;
private int totalPages; private int totalPages;

View File

@@ -1,5 +1,8 @@
package com.example.petstoremobile.dtos; package com.example.petstoremobile.dtos;
/**
* Data Transfer Object representing a pet.
*/
public class PetDTO { public class PetDTO {
private Long petId; private Long petId;
private String petName; private String petName;

View File

@@ -2,6 +2,9 @@ package com.example.petstoremobile.dtos;
import java.math.BigDecimal; import java.math.BigDecimal;
/**
* Data Transfer Object for products.
*/
public class ProductDTO { public class ProductDTO {
private Long prodId; private Long prodId;
private String prodName; private String prodName;

View File

@@ -2,6 +2,9 @@ package com.example.petstoremobile.dtos;
import java.math.BigDecimal; import java.math.BigDecimal;
/**
* Data Transfer Object for mapping products to suppliers.
*/
public class ProductSupplierDTO { public class ProductSupplierDTO {
private Long productId; private Long productId;
private String productName; private String productName;

View File

@@ -1,5 +1,8 @@
package com.example.petstoremobile.dtos; package com.example.petstoremobile.dtos;
/**
* Data Transfer Object for purchase orders.
*/
public class PurchaseOrderDTO { public class PurchaseOrderDTO {
private Long purchaseOrderId; private Long purchaseOrderId;
private Long supId; private Long supId;

View File

@@ -2,6 +2,9 @@ package com.example.petstoremobile.dtos;
import java.math.BigDecimal; import java.math.BigDecimal;
/**
* Data Transfer Object for refund processing.
*/
public class RefundDTO { public class RefundDTO {
// Response fields // Response fields
private Long id; private Long id;

View File

@@ -3,6 +3,9 @@ package com.example.petstoremobile.dtos;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.List; import java.util.List;
/**
* Data Transfer Object for sales transactions.
*/
public class SaleDTO { public class SaleDTO {
// Response fields // Response fields
private Long saleId; private Long saleId;

View File

@@ -1,5 +1,8 @@
package com.example.petstoremobile.dtos; package com.example.petstoremobile.dtos;
/**
* Request body for sending a new chat message.
*/
public class SendMessageRequest { public class SendMessageRequest {
private String content; private String content;

View File

@@ -1,5 +1,8 @@
package com.example.petstoremobile.dtos; package com.example.petstoremobile.dtos;
/**
* Data Transfer Object for services.
*/
public class ServiceDTO { public class ServiceDTO {
private Long serviceId; private Long serviceId;
private String serviceName; private String serviceName;

View File

@@ -1,5 +1,8 @@
package com.example.petstoremobile.dtos; package com.example.petstoremobile.dtos;
/**
* Data Transfer Object for store information.
*/
public class StoreDTO { public class StoreDTO {
private Long storeId; private Long storeId;
private String storeName; private String storeName;

View File

@@ -1,5 +1,8 @@
package com.example.petstoremobile.dtos; package com.example.petstoremobile.dtos;
/**
* Data Transfer Object for suppliers.
*/
public class SupplierDTO { public class SupplierDTO {
private Long supId; private Long supId;
private String supCompany; private String supCompany;

View File

@@ -1,5 +1,8 @@
package com.example.petstoremobile.dtos; package com.example.petstoremobile.dtos;
/**
* Request body for updating chat conversation status.
*/
public class UpdateConversationStatusRequest { public class UpdateConversationStatusRequest {
private String status; private String status;

View File

@@ -1,5 +1,8 @@
package com.example.petstoremobile.dtos; package com.example.petstoremobile.dtos;
/**
* Data Transfer Object for user account details.
*/
public class UserDTO { public class UserDTO {
private Long id; private Long id;
private String username; private String username;

View File

@@ -65,6 +65,9 @@ import okhttp3.MultipartBody;
import okhttp3.RequestBody; import okhttp3.RequestBody;
import okhttp3.ResponseBody; import okhttp3.ResponseBody;
/**
* Fragment for handling customer support chat.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickListener, StompChatManager.MessageListener, public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickListener, StompChatManager.MessageListener,
StompChatManager.ConversationListener, StompChatManager.ConnectionListener { StompChatManager.ConversationListener, StompChatManager.ConnectionListener {
@@ -90,6 +93,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
private StompChatManager stompChatManager; private StompChatManager stompChatManager;
private ActivityResultLauncher<Intent> attachmentLauncher; private ActivityResultLauncher<Intent> attachmentLauncher;
/**
* Initializes the view model and attachment launcher.
*/
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(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 @Override
public View onCreateView(@NonNull LayoutInflater inflater, public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) { ViewGroup container, Bundle savedInstanceState) {
@@ -137,6 +146,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
return binding.getRoot(); return binding.getRoot();
} }
/**
* Sets up the logic to open and close the chat drawer.
*/
private void setupDrawerToggles() { private void setupDrawerToggles() {
binding.headerActiveChats.setOnClickListener(v -> { binding.headerActiveChats.setOnClickListener(v -> {
if (binding.rvActiveChats.getVisibility() == View.VISIBLE) { 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() { private void setupRecyclerViews() {
activeChatAdapter = new ChatAdapter(activeChatList, this); activeChatAdapter = new ChatAdapter(activeChatList, this);
binding.rvActiveChats.setLayoutManager(new LinearLayoutManager(getContext())); binding.rvActiveChats.setLayoutManager(new LinearLayoutManager(getContext()));
@@ -187,6 +202,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
setConversationActive(false, null); setConversationActive(false, null);
} }
/**
* Displays a full-screen image preview for message attachments.
*/
private void showFullScreenImage(Message message) { private void showFullScreenImage(Message message) {
if (baseUrl == null || message.getId() == null) return; if (baseUrl == null || message.getId() == null) return;
@@ -208,6 +226,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
dialog.show(); dialog.show();
} }
/**
* Initiates the download process for a message attachment.
*/
private void downloadFile(Message message) { private void downloadFile(Message message) {
if (message.getId() == null) return; 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) { private void saveFileToDownloads(ResponseBody body, String fileName, String mimeType) {
android.os.Handler mainHandler = new android.os.Handler(android.os.Looper.getMainLooper()); android.os.Handler mainHandler = new android.os.Handler(android.os.Looper.getMainLooper());
new Thread(() -> { new Thread(() -> {
@@ -270,6 +294,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
}).start(); }).start();
} }
/**
* Observes LiveData from the ViewModel to update chat lists and messages.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getActiveChats().observe(getViewLifecycleOwner(), list -> { viewModel.getActiveChats().observe(getViewLifecycleOwner(), list -> {
activeChatList.clear(); activeChatList.clear();
@@ -300,12 +327,18 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
viewModel.getIsLoading().observe(getViewLifecycleOwner(), this::setLoading); viewModel.getIsLoading().observe(getViewLifecycleOwner(), this::setLoading);
} }
/**
* Toggles the visibility of the progress bar.
*/
private void setLoading(boolean loading) { private void setLoading(boolean loading) {
if (binding != null && binding.progressBar != null) { if (binding != null && binding.progressBar != null) {
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE); 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) { private void updateTitleAndStateIfActive(List<Chat> list) {
if (activeConversationId != null) { if (activeConversationId != null) {
for (Chat chat : list) { 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() { private void loadInitialData() {
String token = tokenManager.getToken(); String token = tokenManager.getToken();
Long currentUserId = tokenManager.getUserId(); 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 @Override
public void onChatClick(Chat chat) { public void onChatClick(Chat chat) {
activeConversationId = Long.parseLong(chat.getChatId()); activeConversationId = Long.parseLong(chat.getChatId());
@@ -368,6 +407,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
viewModel.loadMessageHistory(activeConversationId); viewModel.loadMessageHistory(activeConversationId);
} }
/**
* Closes the active chat conversation.
*/
private void closeChat() { private void closeChat() {
if (activeConversationId == null) return; 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() { private void sendMessage() {
if (activeConversationId == null) return; if (activeConversationId == null) return;
String text = binding.etMessage.getText().toString().trim(); 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() { private void selectAttachment() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT); Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*"); intent.setType("*/*");
attachmentLauncher.launch(intent); attachmentLauncher.launch(intent);
} }
/**
* Displays a preview of the selected attachment.
*/
private void showAttachmentPreview(Uri uri) { private void showAttachmentPreview(Uri uri) {
pendingAttachmentUri = uri; pendingAttachmentUri = uri;
binding.layoutAttachmentPreview.setVisibility(View.VISIBLE); 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() { private void removeAttachment() {
pendingAttachmentUri = null; pendingAttachmentUri = null;
binding.layoutAttachmentPreview.setVisibility(View.GONE); binding.layoutAttachmentPreview.setVisibility(View.GONE);
} }
/**
* Sends a message with a file attachment.
*/
private void sendWithAttachment(Uri uri) { private void sendWithAttachment(Uri uri) {
if (activeConversationId == null) return; 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 @Override
public void onMessageReceived(MessageDTO dto) { public void onMessageReceived(MessageDTO dto) {
requireActivity().runOnUiThread(() -> { 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 @Override
public void onConversationUpdated(ConversationDTO dto) { public void onConversationUpdated(ConversationDTO dto) {
requireActivity().runOnUiThread(() -> { requireActivity().runOnUiThread(() -> {
@@ -489,6 +552,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
}); });
} }
/**
* Callback for when the WebSocket connection is successfully opened.
*/
@Override @Override
public void onSocketOpened() { public void onSocketOpened() {
if (!isAdded()) return; if (!isAdded()) return;
@@ -498,12 +564,18 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
}); });
} }
/**
* Callback for when the WebSocket connection is closed.
*/
@Override @Override
public void onSocketClosed() { public void onSocketClosed() {
if (!isAdded()) return; if (!isAdded()) return;
requireActivity().runOnUiThread(viewModel::loadConversations); requireActivity().runOnUiThread(viewModel::loadConversations);
} }
/**
* Callback for when a WebSocket error occurs.
*/
@Override @Override
public void onSocketError() { public void onSocketError() {
if (!isAdded()) return; 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() { private void scrollToBottom() {
if (!messageList.isEmpty()) { if (!messageList.isEmpty()) {
binding.rvMessages.post(() -> 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) { private void setConversationActive(boolean active, String status) {
boolean isClosed = "CLOSED".equalsIgnoreCase(status); boolean isClosed = "CLOSED".equalsIgnoreCase(status);
UIUtils.setViewsEnabled(active && !isClosed, binding.btnSend, binding.etMessage, binding.btnAttach); 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 @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();

View File

@@ -23,7 +23,9 @@ import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint; 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 @AndroidEntryPoint
public class ListFragment extends Fragment { public class ListFragment extends Fragment {
@@ -97,6 +99,9 @@ public class ListFragment extends Fragment {
return binding.getRoot(); return binding.getRoot();
} }
/**
* Cleans up the binding when the view is destroyed.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();

View File

@@ -173,12 +173,18 @@ public class ProfileFragment extends Fragment {
return binding.getRoot(); return binding.getRoot();
} }
/**
* Toggles the visibility of the progress bar.
*/
private void setLoading(boolean loading) { private void setLoading(boolean loading) {
if (binding != null && binding.progressBar != null) { if (binding != null && binding.progressBar != null) {
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE); binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
} }
} }
/**
* Cleans up the binding when the view is destroyed.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();

View File

@@ -34,6 +34,9 @@ import java.util.List;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
/**
* Fragment for viewing application activity logs with various filtering options.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class ActivityLogFragment extends Fragment { public class ActivityLogFragment extends Fragment {
private FragmentActivityLogBinding binding; private FragmentActivityLogBinding binding;
@@ -46,6 +49,9 @@ public class ActivityLogFragment extends Fragment {
@Inject TokenManager tokenManager; @Inject TokenManager tokenManager;
/**
* Inflates the layout, checks for admin access, and initializes ViewModel and UI components.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -69,12 +75,18 @@ public class ActivityLogFragment extends Fragment {
return binding.getRoot(); return binding.getRoot();
} }
/**
* Triggers initial data loading after the view is created.
*/
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
viewModel.loadInitialData(); viewModel.loadInitialData();
} }
/**
* Configures the RecyclerView and its scroll listener for pagination.
*/
private void setupRecyclerView() { private void setupRecyclerView() {
adapter = new ActivityLogAdapter(logList); adapter = new ActivityLogAdapter(logList);
binding.recyclerViewActivityLog.setLayoutManager(new LinearLayoutManager(getContext())); 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() { private void setupFilters() {
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter,
binding.etSearchLog, binding.spinnerRoleFilter, binding.spinnerStoreFilter); 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) { private void showDatePicker(boolean isStart) {
Calendar cal = Calendar.getInstance(); Calendar cal = Calendar.getInstance();
new DatePickerDialog(requireContext(), (view, year, month, day) -> { 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(); }, 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() { private void onStoreSelected() {
int pos = binding.spinnerStoreFilter.getSelectedItemPosition(); int pos = binding.spinnerStoreFilter.getSelectedItemPosition();
Long storeId = (pos > 0 && !storeList.isEmpty()) ? storeList.get(pos - 1).getId() : null; Long storeId = (pos > 0 && !storeList.isEmpty()) ? storeList.get(pos - 1).getId() : null;
viewModel.setStoreFilter(storeId); viewModel.setStoreFilter(storeId);
} }
/**
* Observes the ViewModel for log list updates, store options, and loading status.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getLogs().observe(getViewLifecycleOwner(), list -> { viewModel.getLogs().observe(getViewLifecycleOwner(), list -> {
logList.clear(); logList.clear();
@@ -164,6 +188,9 @@ public class ActivityLogFragment extends Fragment {
binding.swipeRefreshActivityLog.setRefreshing(loading)); binding.swipeRefreshActivityLog.setRefreshing(loading));
} }
/**
* Cleans up the binding reference.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();

View File

@@ -44,6 +44,9 @@ import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
/**
* Fragment for displaying and managing a list of adoptions with a calendar view and filtering.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdoptionClickListener { public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdoptionClickListener {
@@ -58,12 +61,18 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
@Inject TokenManager tokenManager; @Inject TokenManager tokenManager;
/**
* Initializes the ViewModel.
*/
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(AdoptionListViewModel.class); viewModel = new ViewModelProvider(this).get(AdoptionListViewModel.class);
} }
/**
* Inflates the layout and sets up UI components, calendar, and observers.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -88,6 +97,9 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
return binding.getRoot(); return binding.getRoot();
} }
/**
* Observes the ViewModel for adoption list, stores, and loading status.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getAdoptions().observe(getViewLifecycleOwner(), list -> { viewModel.getAdoptions().observe(getViewLifecycleOwner(), list -> {
adoptionList.clear(); 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() { private void setupBulkDelete() {
bulkDeleteHandler = new BulkDeleteHandler( bulkDeleteHandler = new BulkDeleteHandler(
this, this,
@@ -119,6 +134,9 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
); );
} }
/**
* Reloads adoption data and stores when the fragment resumes.
*/
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
@@ -126,6 +144,9 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
if (!isStaff()) viewModel.loadStores(); if (!isStaff()) viewModel.loadStores();
} }
/**
* Toggles between month and week display modes for the calendar.
*/
private void toggleCalendarMode() { private void toggleCalendarMode() {
isMonthMode = !isMonthMode; isMonthMode = !isMonthMode;
binding.calendarViewAdoption.state().edit() binding.calendarViewAdoption.state().edit()
@@ -133,6 +154,9 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
.commit(); .commit();
} }
/**
* Sets up the filter visibility toggle, considering user roles.
*/
private void setupFilterToggle() { private void setupFilterToggle() {
if (isStaff()) { if (isStaff()) {
UIUtils.setupFilterToggle(binding.btnToggleFilterAdoption, binding.layoutFilterAdoption, 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() { private boolean isStaff() {
return "STAFF".equalsIgnoreCase(tokenManager.getRole()); return "STAFF".equalsIgnoreCase(tokenManager.getRole());
} }
/**
* Configures the calendar view for date-based filtering.
*/
private void setupCalendar() { private void setupCalendar() {
binding.calendarViewAdoption.setOnDateChangedListener((widget, date, selected) -> { binding.calendarViewAdoption.setOnDateChangedListener((widget, date, selected) -> {
if (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() { private void updateCalendarDecorators() {
HashSet<CalendarDay> datesWithAdoptions = new HashSet<>(); HashSet<CalendarDay> datesWithAdoptions = new HashSet<>();
for (AdoptionDTO adoption : adoptionList) { for (AdoptionDTO adoption : adoptionList) {
@@ -184,6 +217,9 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
binding.calendarViewAdoption.addDecorator(new EventDecorator(Color.RED, datesWithAdoptions)); binding.calendarViewAdoption.addDecorator(new EventDecorator(Color.RED, datesWithAdoptions));
} }
/**
* Configures the RecyclerView and its scroll listener for pagination.
*/
private void setupRecyclerView() { private void setupRecyclerView() {
adapter = new AdoptionAdapter(adoptionList, this); adapter = new AdoptionAdapter(adoptionList, this);
binding.recyclerViewAdoptions.setLayoutManager(new LinearLayoutManager(getContext())); 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() { private void setupSearch() {
UIUtils.attachSearch(binding.etSearchAdoption, () -> loadAdoptions(true)); UIUtils.attachSearch(binding.etSearchAdoption, () -> loadAdoptions(true));
} }
/**
* Configures the status filter spinner.
*/
private void setupStatusFilter() { private void setupStatusFilter() {
String[] statuses = {"All Statuses", "Completed", "Pending", "Missed", "Cancelled"}; String[] statuses = {"All Statuses", "Completed", "Pending", "Missed", "Cancelled"};
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatusAdoption, statuses, () -> loadAdoptions(true)); SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatusAdoption, statuses, () -> loadAdoptions(true));
} }
/**
* Configures the store filter spinner.
*/
private void setupStoreFilter() { private void setupStoreFilter() {
SpinnerUtils.setupFilterSpinner(binding.spinnerStoreAdoption, () -> loadAdoptions(true)); SpinnerUtils.setupFilterSpinner(binding.spinnerStoreAdoption, () -> loadAdoptions(true));
} }
/**
* Configures the swipe-to-refresh layout.
*/
private void setupSwipeRefresh() { private void setupSwipeRefresh() {
binding.swipeRefreshAdoption.setOnRefreshListener(() -> loadAdoptions(true)); binding.swipeRefreshAdoption.setOnRefreshListener(() -> loadAdoptions(true));
} }
/**
* Loads adoption data based on current filters, search query, and selected date.
*/
private void loadAdoptions(boolean reset) { private void loadAdoptions(boolean reset) {
String query = binding.etSearchAdoption.getText().toString().trim(); String query = binding.etSearchAdoption.getText().toString().trim();
String status = binding.spinnerStatusAdoption.getSelectedItem() != null ? binding.spinnerStatusAdoption.getSelectedItem().toString() : "All Statuses"; 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); viewModel.loadAdoptions(reset, query, status, storeId, selectedDateString, null);
} }
/**
* Navigates to the adoption detail screen.
*/
private void openDetail(int position) { private void openDetail(int position) {
Bundle args = new Bundle(); Bundle args = new Bundle();
if (position != -1) { 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); NavHostFragment.findNavController(this).navigate(R.id.nav_adoption_detail, args);
} }
/**
* Handles adoption item clicks by opening details.
*/
@Override @Override
public void onAdoptionClick(int position) { openDetail(position); } public void onAdoptionClick(int position) { openDetail(position); }
/**
* Forwards selection changes to the bulk delete handler.
*/
@Override @Override
public void onSelectionChanged(int selectedCount) { public void onSelectionChanged(int selectedCount) {
if (bulkDeleteHandler != null) { if (bulkDeleteHandler != null) {
@@ -269,6 +329,9 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
} }
} }
/**
* Cleans up the binding reference.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();

View File

@@ -18,6 +18,9 @@ import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.util.*; import java.util.*;
/**
* Fragment for displaying business analytics, including revenue, transactions, and product performance.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class AnalyticsFragment extends Fragment { 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 String[] TOP_N_OPTIONS = {"5", "10", "15", "20"};
private static final int[] TOP_N_VALUES = { 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 @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -51,6 +57,9 @@ public class AnalyticsFragment extends Fragment {
private static final int COLOR_SELECTED = 0xFF4ECDC4; private static final int COLOR_SELECTED = 0xFF4ECDC4;
private static final int COLOR_UNSELECTED = 0xFFCBD5E1; private static final int COLOR_UNSELECTED = 0xFFCBD5E1;
/**
* Configures the view mode toggle buttons (My Analytics vs Store Analytics).
*/
private void setupViewModeToggle() { private void setupViewModeToggle() {
updateViewModeButtonStyles(viewModel.getViewMode()); 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) { private void updateViewModeButtonStyles(String mode) {
binding.btnMyAnalytics.setBackgroundTintList( binding.btnMyAnalytics.setBackgroundTintList(
android.content.res.ColorStateList.valueOf(mode.equals("mine") ? COLOR_SELECTED : COLOR_UNSELECTED)); 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)); 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) { private void updateStoreFilterVisibility(String mode) {
boolean isAdmin = "ADMIN".equalsIgnoreCase(tokenManager.getRole()); boolean isAdmin = "ADMIN".equalsIgnoreCase(tokenManager.getRole());
int vis = (isAdmin && mode.equals("store")) ? View.VISIBLE : View.GONE; int vis = (isAdmin && mode.equals("store")) ? View.VISIBLE : View.GONE;
@@ -81,8 +96,10 @@ public class AnalyticsFragment extends Fragment {
binding.spinnerFilterStore.setVisibility(vis); binding.spinnerFilterStore.setVisibility(vis);
} }
// Filter Panel
/**
* Configures the filter panel, including date pickers, presets, and action buttons.
*/
private void setupFilterPanel() { private void setupFilterPanel() {
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerTopN, TOP_N_OPTIONS); SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerTopN, TOP_N_OPTIONS);
@@ -111,12 +128,18 @@ public class AnalyticsFragment extends Fragment {
binding.btnFilterReset.setOnClickListener(v -> resetFilters()); binding.btnFilterReset.setOnClickListener(v -> resetFilters());
} }
/**
* Toggles the visibility of the filter content section.
*/
private void toggleFilters() { private void toggleFilters() {
filtersExpanded = !filtersExpanded; filtersExpanded = !filtersExpanded;
binding.llFilterContent.setVisibility(filtersExpanded ? View.VISIBLE : View.GONE); binding.llFilterContent.setVisibility(filtersExpanded ? View.VISIBLE : View.GONE);
binding.tvFilterToggleIcon.setText(filtersExpanded ? "" : ""); binding.tvFilterToggleIcon.setText(filtersExpanded ? "" : "");
} }
/**
* Applies a date range preset to the filter fields.
*/
private void applyPreset(int startOffset, int endOffset) { private void applyPreset(int startOffset, int endOffset) {
binding.etFilterStartDate.setText(getDateString(startOffset)); binding.etFilterStartDate.setText(getDateString(startOffset));
binding.etFilterEndDate.setText(getDateString(endOffset)); binding.etFilterEndDate.setText(getDateString(endOffset));
@@ -124,6 +147,9 @@ public class AnalyticsFragment extends Fragment {
applyFiltersFromUI(); applyFiltersFromUI();
} }
/**
* Reads filter values from the UI and applies them to the ViewModel.
*/
private void applyFiltersFromUI() { private void applyFiltersFromUI() {
AnalyticsViewModel.FilterState filter = new AnalyticsViewModel.FilterState(); AnalyticsViewModel.FilterState filter = new AnalyticsViewModel.FilterState();
filter.startDate = binding.etFilterStartDate.getText().toString().trim(); filter.startDate = binding.etFilterStartDate.getText().toString().trim();
@@ -142,6 +168,9 @@ public class AnalyticsFragment extends Fragment {
viewModel.applyFilter(filter); viewModel.applyFilter(filter);
} }
/**
* Resets all filters to their default values.
*/
private void resetFilters() { private void resetFilters() {
binding.etFilterStartDate.setText(""); binding.etFilterStartDate.setText("");
binding.etFilterEndDate.setText(""); binding.etFilterEndDate.setText("");
@@ -152,6 +181,9 @@ public class AnalyticsFragment extends Fragment {
viewModel.resetFilter(); viewModel.resetFilter();
} }
/**
* Updates the text summary of the currently selected date range.
*/
private void updateFilterSummary() { private void updateFilterSummary() {
String start = binding.etFilterStartDate.getText().toString().trim(); String start = binding.etFilterStartDate.getText().toString().trim();
String end = binding.etFilterEndDate.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) { private String shortDate(String date) {
return (date != null && date.length() >= 10) ? date.substring(5) : 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) { private String getDateString(int offsetDays) {
Calendar c = Calendar.getInstance(); Calendar c = Calendar.getInstance();
c.add(Calendar.DAY_OF_YEAR, offsetDays); 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)); 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() { private void observeViewModel() {
viewModel.getAnalyticsData().observe(getViewLifecycleOwner(), this::computeAndDisplay); viewModel.getAnalyticsData().observe(getViewLifecycleOwner(), this::computeAndDisplay);
@@ -216,14 +256,19 @@ public class AnalyticsFragment extends Fragment {
}); });
} }
/**
* Cleans up the binding reference.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
binding = null; binding = null;
} }
// Display
/**
* Computes and displays analytics data in summary cards and bar charts.
*/
private void computeAndDisplay(AnalyticsViewModel.AnalyticsData data) { private void computeAndDisplay(AnalyticsViewModel.AnalyticsData data) {
if (data == null) return; 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) { private void addBarRow(LinearLayout parent, String label, String value, float ratio, String color) {
if (getContext() == null) return; if (getContext() == null) return;
LinearLayout row = new LinearLayout(getContext()); LinearLayout row = new LinearLayout(getContext());
@@ -359,6 +406,9 @@ public class AnalyticsFragment extends Fragment {
parent.addView(row); parent.addView(row);
} }
/**
* Adds an empty message row to a given layout container.
*/
private void addEmptyRow(LinearLayout parent, String message) { private void addEmptyRow(LinearLayout parent, String message) {
if (getContext() == null) return; if (getContext() == null) return;
TextView tv = new TextView(getContext()); TextView tv = new TextView(getContext());
@@ -368,6 +418,9 @@ public class AnalyticsFragment extends Fragment {
parent.addView(tv); parent.addView(tv);
} }
/**
* Displays an error message and updates UI to reflect the error state.
*/
private void showError(String msg) { private void showError(String msg) {
if (getContext() == null || binding == null) return; if (getContext() == null || binding == null) return;
binding.tvTotalRevenue.setText("Error"); binding.tvTotalRevenue.setText("Error");

View File

@@ -45,6 +45,9 @@ import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
/**
* Fragment for displaying and managing a list of appointments with calendar integration and filtering.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class AppointmentFragment extends Fragment implements AppointmentAdapter.OnAppointmentClickListener { public class AppointmentFragment extends Fragment implements AppointmentAdapter.OnAppointmentClickListener {
@@ -63,6 +66,9 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
private Long currentUserId = null; private Long currentUserId = null;
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
/**
* Initializes ViewModels for appointment and authentication data.
*/
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@@ -70,6 +76,9 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
authViewModel = new ViewModelProvider(this).get(AuthViewModel.class); authViewModel = new ViewModelProvider(this).get(AuthViewModel.class);
} }
/**
* Inflates the layout and sets up UI components, calendar, and observers.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -97,6 +106,9 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
return binding.getRoot(); return binding.getRoot();
} }
/**
* Observes the ViewModel for appointment list, stores, and loading status.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getAppointments().observe(getViewLifecycleOwner(), list -> { viewModel.getAppointments().observe(getViewLifecycleOwner(), list -> {
appointmentList.clear(); 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() { private void setupBulkDelete() {
bulkDeleteHandler = new BulkDeleteHandler( bulkDeleteHandler = new BulkDeleteHandler(
this, this,
@@ -128,6 +143,9 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
); );
} }
/**
* Reloads appointment data and stores when the fragment resumes.
*/
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
@@ -135,6 +153,9 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
if (!isStaff()) viewModel.loadStores(); if (!isStaff()) viewModel.loadStores();
} }
/**
* Toggles between month and week display modes for the calendar.
*/
private void toggleCalendarMode() { private void toggleCalendarMode() {
isMonthMode = !isMonthMode; isMonthMode = !isMonthMode;
binding.calendarView.state().edit() binding.calendarView.state().edit()
@@ -142,12 +163,18 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
.commit(); .commit();
} }
/**
* Sets up the "My Appointments" filter button.
*/
private void setupMyAppointmentFilter() { private void setupMyAppointmentFilter() {
binding.btnMyAppointments.setOnClickListener(v -> { binding.btnMyAppointments.setOnClickListener(v -> {
loadAppointmentData(true); loadAppointmentData(true);
}); });
} }
/**
* Loads information about the currently logged-in user.
*/
private void loadCurrentUserInfo() { private void loadCurrentUserInfo() {
authViewModel.getMe().observe(getViewLifecycleOwner(), resource -> { authViewModel.getMe().observe(getViewLifecycleOwner(), resource -> {
if (resource.status == Resource.Status.SUCCESS && resource.data != null) { 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() { private void setupFilterToggle() {
if (isStaff()) { if (isStaff()) {
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchAppointment, binding.spinnerStatus); 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() { private void setupCalendar() {
binding.calendarView.setOnDateChangedListener((widget, date, selected) -> { binding.calendarView.setOnDateChangedListener((widget, date, selected) -> {
if (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() { private void updateCalendarDecorators() {
HashSet<CalendarDay> datesWithAppointments = new HashSet<>(); HashSet<CalendarDay> datesWithAppointments = new HashSet<>();
for (AppointmentDTO appointment : appointmentList) { for (AppointmentDTO appointment : appointmentList) {
@@ -200,23 +236,38 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
binding.calendarView.addDecorator(new EventDecorator(Color.RED, datesWithAppointments)); binding.calendarView.addDecorator(new EventDecorator(Color.RED, datesWithAppointments));
} }
/**
* Sets up the search input listener.
*/
private void setupSearch() { private void setupSearch() {
UIUtils.attachSearch(binding.etSearchAppointment, () -> loadAppointmentData(true)); UIUtils.attachSearch(binding.etSearchAppointment, () -> loadAppointmentData(true));
} }
/**
* Configures the status filter spinner.
*/
private void setupStatusFilter() { private void setupStatusFilter() {
String[] statuses = {"All Statuses", "Booked", "Completed", "Cancelled", "Missed"}; String[] statuses = {"All Statuses", "Booked", "Completed", "Cancelled", "Missed"};
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatus, statuses, () -> loadAppointmentData(true)); SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatus, statuses, () -> loadAppointmentData(true));
} }
/**
* Configures the store filter spinner.
*/
private void setupStoreFilter() { private void setupStoreFilter() {
SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadAppointmentData(true)); SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadAppointmentData(true));
} }
/**
* Configures the swipe-to-refresh layout.
*/
private void setupSwipeRefresh() { private void setupSwipeRefresh() {
binding.swipeRefreshAppointment.setOnRefreshListener(() -> loadAppointmentData(true)); binding.swipeRefreshAppointment.setOnRefreshListener(() -> loadAppointmentData(true));
} }
/**
* Navigates to the appointment detail screen.
*/
private void openAppointmentDetails(int position) { private void openAppointmentDetails(int position) {
Bundle args = new Bundle(); Bundle args = new Bundle();
if (position != -1) { if (position != -1) {
@@ -226,11 +277,17 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
NavHostFragment.findNavController(this).navigate(R.id.nav_appointment_detail, args); NavHostFragment.findNavController(this).navigate(R.id.nav_appointment_detail, args);
} }
/**
* Handles appointment item clicks by opening details.
*/
@Override @Override
public void onAppointmentClick(int position) { public void onAppointmentClick(int position) {
openAppointmentDetails(position); openAppointmentDetails(position);
} }
/**
* Forwards selection changes to the bulk delete handler.
*/
@Override @Override
public void onSelectionChanged(int count) { public void onSelectionChanged(int count) {
if (bulkDeleteHandler != null) { 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() { private boolean isStaff() {
return "STAFF".equalsIgnoreCase(tokenManager.getRole()); return "STAFF".equalsIgnoreCase(tokenManager.getRole());
} }
/**
* Loads appointment data based on current filters, search query, and selected date.
*/
private void loadAppointmentData(boolean reset) { private void loadAppointmentData(boolean reset) {
String query = binding.etSearchAppointment.getText().toString().trim(); String query = binding.etSearchAppointment.getText().toString().trim();
String status = binding.spinnerStatus.getSelectedItem() != null ? binding.spinnerStatus.getSelectedItem().toString() : "All Statuses"; 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); viewModel.loadAppointments(reset, query, status, storeId, selectedDateString, employeeId);
} }
/**
* Configures the RecyclerView and its scroll listener for pagination.
*/
private void setupRecyclerView() { private void setupRecyclerView() {
adapter = new AppointmentAdapter(appointmentList, this); adapter = new AppointmentAdapter(appointmentList, this);
binding.recyclerViewAppointments.setLayoutManager(new LinearLayoutManager(getContext())); binding.recyclerViewAppointments.setLayoutManager(new LinearLayoutManager(getContext()));
@@ -296,6 +362,9 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
}); });
} }
/**
* Cleans up the binding reference.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();

View File

@@ -27,6 +27,9 @@ import java.util.List;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
/**
* Fragment for displaying and managing a list of coupons.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class CouponFragment extends Fragment implements CouponAdapter.OnCouponClickListener { public class CouponFragment extends Fragment implements CouponAdapter.OnCouponClickListener {
private FragmentCouponBinding binding; private FragmentCouponBinding binding;
@@ -34,6 +37,9 @@ public class CouponFragment extends Fragment implements CouponAdapter.OnCouponCl
private CouponAdapter adapter; private CouponAdapter adapter;
private final List<CouponDTO> couponList = new ArrayList<>(); private final List<CouponDTO> couponList = new ArrayList<>();
/**
* Inflates the layout, initializes ViewModel, and sets up UI components.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -59,6 +65,9 @@ public class CouponFragment extends Fragment implements CouponAdapter.OnCouponCl
return binding.getRoot(); return binding.getRoot();
} }
/**
* Observes the ViewModel for coupon list updates and loading status.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getCoupons().observe(getViewLifecycleOwner(), list -> { viewModel.getCoupons().observe(getViewLifecycleOwner(), list -> {
couponList.clear(); 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() { private void setupRecyclerView() {
adapter = new CouponAdapter(couponList, this); adapter = new CouponAdapter(couponList, this);
binding.recyclerViewCoupon.setLayoutManager(new LinearLayoutManager(getContext())); 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() { private void setupStatusFilter() {
String[] statuses = {"All Statuses", "Active", "Inactive"}; String[] statuses = {"All Statuses", "Active", "Inactive"};
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatusCoupon, statuses, () -> applyFilters(true)); SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatusCoupon, statuses, () -> applyFilters(true));
} }
/**
* Configures the discount type filter spinner.
*/
private void setupTypeFilter() { private void setupTypeFilter() {
String[] types = {"All Types", "FIXED", "PERCENT"}; String[] types = {"All Types", "FIXED", "PERCENT"};
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerTypeCoupon, types, () -> applyFilters(true)); SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerTypeCoupon, types, () -> applyFilters(true));
} }
/**
* Sets up the search input listener.
*/
private void setupSearch() { private void setupSearch() {
UIUtils.attachSearch(binding.etSearchCoupon, () -> applyFilters(true)); UIUtils.attachSearch(binding.etSearchCoupon, () -> applyFilters(true));
} }
/**
* Configures the swipe-to-refresh layout.
*/
private void setupSwipeRefresh() { private void setupSwipeRefresh() {
binding.swipeRefreshCoupon.setOnRefreshListener(() -> applyFilters(true)); binding.swipeRefreshCoupon.setOnRefreshListener(() -> applyFilters(true));
} }
/**
* Applies filters and loads the coupon list.
*/
private void applyFilters(boolean reset) { private void applyFilters(boolean reset) {
String statusStr = binding.spinnerStatusCoupon.getSelectedItem() != null ? String statusStr = binding.spinnerStatusCoupon.getSelectedItem() != null ?
binding.spinnerStatusCoupon.getSelectedItem().toString() : "All Statuses"; binding.spinnerStatusCoupon.getSelectedItem().toString() : "All Statuses";
@@ -125,22 +152,34 @@ public class CouponFragment extends Fragment implements CouponAdapter.OnCouponCl
viewModel.loadCoupons(reset, active, discountType, null); viewModel.loadCoupons(reset, active, discountType, null);
} }
/**
* Navigates to the coupon detail screen.
*/
private void openDetail(long id) { private void openDetail(long id) {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putLong("couponId", id); args.putLong("couponId", id);
Navigation.findNavController(requireView()).navigate(R.id.couponDetailFragment, args); Navigation.findNavController(requireView()).navigate(R.id.couponDetailFragment, args);
} }
/**
* Handles coupon item clicks by opening details.
*/
@Override @Override
public void onCouponClick(CouponDTO coupon) { public void onCouponClick(CouponDTO coupon) {
openDetail(coupon.getCouponId()); openDetail(coupon.getCouponId());
} }
/**
* Shows or hides the bulk delete button based on selection count.
*/
@Override @Override
public void onSelectionChanged(int count) { public void onSelectionChanged(int count) {
binding.btnBulkDeleteCoupons.setVisibility(count > 0 ? View.VISIBLE : View.GONE); binding.btnBulkDeleteCoupons.setVisibility(count > 0 ? View.VISIBLE : View.GONE);
} }
/**
* Displays a confirmation dialog for deleting multiple coupons.
*/
private void confirmBulkDelete() { private void confirmBulkDelete() {
new AlertDialog.Builder(requireContext()) new AlertDialog.Builder(requireContext())
.setTitle("Confirm Bulk Delete") .setTitle("Confirm Bulk Delete")

View File

@@ -22,6 +22,9 @@ import java.util.*;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
/**
* Fragment for displaying and managing a list of customers.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class CustomerFragment extends Fragment implements CustomerAdapter.OnCustomerClickListener { 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 @Named("baseUrl") String baseUrl;
@Inject TokenManager tokenManager; @Inject TokenManager tokenManager;
/**
* Inflates the layout, initializes ViewModel, and sets up UI components and filters.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -56,6 +62,9 @@ public class CustomerFragment extends Fragment implements CustomerAdapter.OnCust
return binding.getRoot(); return binding.getRoot();
} }
/**
* Observes the ViewModel for customer list updates and loading status.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getFilteredCustomers().observe(getViewLifecycleOwner(), list -> { viewModel.getFilteredCustomers().observe(getViewLifecycleOwner(), list -> {
customerList.clear(); 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() { private void setupRecyclerView() {
adapter = new CustomerAdapter(customerList, this); adapter = new CustomerAdapter(customerList, this);
adapter.setBaseUrl(baseUrl); adapter.setBaseUrl(baseUrl);
@@ -92,15 +104,24 @@ public class CustomerFragment extends Fragment implements CustomerAdapter.OnCust
}); });
} }
/**
* Configures the status filter spinner.
*/
private void setupStatusFilter() { private void setupStatusFilter() {
String[] statuses = {"All Statuses", "Active", "Inactive"}; String[] statuses = {"All Statuses", "Active", "Inactive"};
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatusCustomer, statuses, this::applyFilters); SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatusCustomer, statuses, this::applyFilters);
} }
/**
* Sets up the search input listener.
*/
private void setupSearch() { private void setupSearch() {
UIUtils.attachSearch(binding.etSearchCustomer, this::applyFilters); UIUtils.attachSearch(binding.etSearchCustomer, this::applyFilters);
} }
/**
* Applies filters and triggers data reloading or filtering in ViewModel.
*/
private void applyFilters() { private void applyFilters() {
String query = binding.etSearchCustomer.getText().toString().trim(); String query = binding.etSearchCustomer.getText().toString().trim();
String status = binding.spinnerStatusCustomer.getSelectedItem() != null ? String status = binding.spinnerStatusCustomer.getSelectedItem() != null ?
@@ -108,10 +129,16 @@ public class CustomerFragment extends Fragment implements CustomerAdapter.OnCust
viewModel.filter(query, status); viewModel.filter(query, status);
} }
/**
* Configures the swipe-to-refresh layout.
*/
private void setupSwipeRefresh() { private void setupSwipeRefresh() {
binding.swipeRefreshCustomer.setOnRefreshListener(() -> viewModel.loadCustomers(true)); binding.swipeRefreshCustomer.setOnRefreshListener(() -> viewModel.loadCustomers(true));
} }
/**
* Navigates to the customer detail screen.
*/
private void openDetail(int position) { private void openDetail(int position) {
Bundle args = new Bundle(); Bundle args = new Bundle();
if (position != -1) { 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); NavHostFragment.findNavController(this).navigate(R.id.nav_customer_detail, args);
} }
/**
* Handles customer item clicks by opening details.
*/
@Override @Override
public void onCustomerClick(int position) { public void onCustomerClick(int position) {
openDetail(position); openDetail(position);
} }
/**
* Cleans up the binding reference.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();

View File

@@ -32,6 +32,9 @@ import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
/**
* Fragment for displaying and managing inventory items.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class InventoryFragment extends Fragment implements InventoryAdapter.OnInventoryClickListener { public class InventoryFragment extends Fragment implements InventoryAdapter.OnInventoryClickListener {
@@ -43,12 +46,18 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
@Inject TokenManager tokenManager; @Inject TokenManager tokenManager;
/**
* Initializes the ViewModel.
*/
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(InventoryListViewModel.class); viewModel = new ViewModelProvider(this).get(InventoryListViewModel.class);
} }
/**
* Inflates the layout and sets up UI components, filters, and observers.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -71,6 +80,9 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
return binding.getRoot(); return binding.getRoot();
} }
/**
* Observes the ViewModel for inventory list updates, store list, and loading status.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getInventory().observe(getViewLifecycleOwner(), list -> { viewModel.getInventory().observe(getViewLifecycleOwner(), list -> {
inventoryList.clear(); 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() { private void setupBulkDelete() {
bulkDeleteHandler = new BulkDeleteHandler( bulkDeleteHandler = new BulkDeleteHandler(
this, this,
@@ -101,18 +116,27 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
); );
} }
/**
* Reloads store data if necessary when the fragment resumes.
*/
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
if (!isStaff()) viewModel.loadStores(); if (!isStaff()) viewModel.loadStores();
} }
/**
* Cleans up the binding reference.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
binding = null; binding = null;
} }
/**
* Sets up the filter visibility toggle, considering user roles.
*/
private void setupFilterToggle() { private void setupFilterToggle() {
if (isStaff()) { if (isStaff()) {
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchInventory); 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() { private boolean isStaff() {
return "STAFF".equalsIgnoreCase(tokenManager.getRole()); return "STAFF".equalsIgnoreCase(tokenManager.getRole());
} }
/**
* Sets up the search input listener.
*/
private void setupSearch() { private void setupSearch() {
UIUtils.attachSearch(binding.etSearchInventory, () -> loadInventory(true)); UIUtils.attachSearch(binding.etSearchInventory, () -> loadInventory(true));
} }
/**
* Configures the store filter spinner.
*/
private void setupStoreFilter() { private void setupStoreFilter() {
SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadInventory(true)); SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadInventory(true));
} }
/**
* Configures the RecyclerView and its scroll listener for pagination.
*/
private void setupRecyclerView() { private void setupRecyclerView() {
adapter = new InventoryAdapter(inventoryList, this); adapter = new InventoryAdapter(inventoryList, this);
binding.recyclerViewInventory.setLayoutManager(new LinearLayoutManager(getContext())); 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() { private void setupSwipeRefresh() {
binding.swipeRefreshInventory.setOnRefreshListener(() -> loadInventory(true)); binding.swipeRefreshInventory.setOnRefreshListener(() -> loadInventory(true));
} }
/**
* Loads inventory data based on current search query and store filter.
*/
private void loadInventory(boolean reset) { private void loadInventory(boolean reset) {
String query = binding.etSearchInventory != null ? binding.etSearchInventory.getText().toString().trim() : ""; String query = binding.etSearchInventory != null ? binding.etSearchInventory.getText().toString().trim() : "";
if (query.isEmpty()) query = null; if (query.isEmpty()) query = null;
@@ -178,6 +220,9 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
viewModel.loadInventory(reset, query, storeId); viewModel.loadInventory(reset, query, storeId);
} }
/**
* Navigates to the inventory detail screen.
*/
private void openDetail(InventoryDTO inv) { private void openDetail(InventoryDTO inv) {
Bundle args = new Bundle(); Bundle args = new Bundle();
if (inv != null) { 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); NavHostFragment.findNavController(this).navigate(R.id.nav_inventory_detail, args);
} }
/**
* Handles inventory item clicks by opening details.
*/
@Override @Override
public void onInventoryClick(int position) { public void onInventoryClick(int position) {
if (position >= 0 && position < inventoryList.size()) { 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 @Override
public void onSelectionChanged(int selectedCount) { public void onSelectionChanged(int selectedCount) {
if (bulkDeleteHandler != null) { if (bulkDeleteHandler != null) {

View File

@@ -33,6 +33,9 @@ import javax.inject.Named;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
/**
* Fragment for displaying and managing a list of pets.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class PetFragment extends Fragment implements PetAdapter.OnPetClickListener { public class PetFragment extends Fragment implements PetAdapter.OnPetClickListener {
private FragmentPetBinding binding; private FragmentPetBinding binding;
@@ -44,12 +47,18 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
@Inject @Named("baseUrl") String baseUrl; @Inject @Named("baseUrl") String baseUrl;
@Inject TokenManager tokenManager; @Inject TokenManager tokenManager;
/**
* Initializes the view model.
*/
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(PetListViewModel.class); viewModel = new ViewModelProvider(this).get(PetListViewModel.class);
} }
/**
* Inflates the layout and initializes UI components and filters.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -72,6 +81,9 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
return binding.getRoot(); return binding.getRoot();
} }
/**
* Observes LiveData from the ViewModel to update the list and filter options.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getPets().observe(getViewLifecycleOwner(), list -> { viewModel.getPets().observe(getViewLifecycleOwner(), list -> {
petList.clear(); 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() { private void setupBulkDelete() {
bulkDeleteHandler = new BulkDeleteHandler( bulkDeleteHandler = new BulkDeleteHandler(
this, this,
@@ -107,6 +122,9 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
); );
} }
/**
* Refreshes pet data and filters when the fragment is resumed.
*/
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
@@ -115,6 +133,9 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
if (!isStaff()) viewModel.loadStores(); if (!isStaff()) viewModel.loadStores();
} }
/**
* Sets up the visibility of filters based on the user's role.
*/
private void setupFilterToggle() { private void setupFilterToggle() {
if (isStaff()) { if (isStaff()) {
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchPet, 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() { private boolean isStaff() {
return "STAFF".equalsIgnoreCase(tokenManager.getRole()); return "STAFF".equalsIgnoreCase(tokenManager.getRole());
} }
/**
* Attaches search functionality to the search input field.
*/
private void setupSearch() { private void setupSearch() {
UIUtils.attachSearch(binding.etSearchPet, () -> loadPetData(true)); UIUtils.attachSearch(binding.etSearchPet, () -> loadPetData(true));
} }
/**
* Initializes the status filter spinner.
*/
private void setupStatusFilter() { private void setupStatusFilter() {
String[] statuses = {"All Statuses", "Available", "Adopted", "Owned", "Pending"}; String[] statuses = {"All Statuses", "Available", "Adopted", "Owned", "Pending"};
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatus, statuses, () -> loadPetData(true)); SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatus, statuses, () -> loadPetData(true));
} }
/**
* Initializes the species filter spinner.
*/
private void setupSpeciesFilter() { private void setupSpeciesFilter() {
String[] initial = {"All Species"}; String[] initial = {"All Species"};
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerSpecies, initial, () -> loadPetData(true)); SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerSpecies, initial, () -> loadPetData(true));
} }
/**
* Initializes the store filter spinner.
*/
private void setupStoreFilter() { private void setupStoreFilter() {
SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadPetData(true)); SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadPetData(true));
} }
/**
* Configures the swipe-to-refresh layout.
*/
private void setupSwipeRefresh() { private void setupSwipeRefresh() {
binding.swipeRefreshPet.setOnRefreshListener(() -> loadPetData(true)); binding.swipeRefreshPet.setOnRefreshListener(() -> loadPetData(true));
} }
/**
* Triggers loading of pet data from the backend with current filters.
*/
private void loadPetData(boolean reset) { private void loadPetData(boolean reset) {
String query = binding.etSearchPet.getText().toString().trim(); String query = binding.etSearchPet.getText().toString().trim();
String status = binding.spinnerStatus.getSelectedItem() != null ? binding.spinnerStatus.getSelectedItem().toString() : "All Statuses"; 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); viewModel.loadPets(reset, query, status, species, storeId);
} }
/**
* Configures the RecyclerView and its scroll listener for pagination.
*/
private void setupRecyclerView() { private void setupRecyclerView() {
adapter = new PetAdapter(petList, this); adapter = new PetAdapter(petList, this);
adapter.setBaseUrl(baseUrl); 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) { private void openPetProfile(int position) {
Bundle args = new Bundle(); Bundle args = new Bundle();
PetDTO pet = petList.get(position); 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); NavHostFragment.findNavController(this).navigate(R.id.nav_pet_profile, args);
} }
/**
* Navigates to the screen for adding a new pet.
*/
private void openPetDetails() { private void openPetDetails() {
NavHostFragment.findNavController(this).navigate(R.id.nav_pet_detail); NavHostFragment.findNavController(this).navigate(R.id.nav_pet_detail);
} }
/**
* Handles clicks on individual pets in the list.
*/
@Override @Override
public void onPetClick(int position) { public void onPetClick(int position) {
openPetProfile(position); openPetProfile(position);
} }
/**
* Notifies the bulk delete handler when item selection changes.
*/
@Override @Override
public void onSelectionChanged(int selectedCount) { public void onSelectionChanged(int selectedCount) {
if (bulkDeleteHandler != null) { 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 @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();

View File

@@ -31,6 +31,9 @@ import javax.inject.Named;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
/**
* Fragment for displaying and managing a list of products.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class ProductFragment extends Fragment implements ProductAdapter.OnProductClickListener { 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; @Inject @Named("baseUrl") String baseUrl;
/**
* Initializes the ViewModel.
*/
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(ProductListViewModel.class); viewModel = new ViewModelProvider(this).get(ProductListViewModel.class);
} }
/**
* Inflates the layout and sets up UI components.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -66,6 +75,9 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
return binding.getRoot(); return binding.getRoot();
} }
/**
* Observes the ViewModel for product list, categories, and loading status.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getProducts().observe(getViewLifecycleOwner(), list -> { viewModel.getProducts().observe(getViewLifecycleOwner(), list -> {
productList.clear(); productList.clear();
@@ -83,6 +95,9 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
}); });
} }
/**
* Reloads product data and categories when the fragment resumes.
*/
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
@@ -90,23 +105,38 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
viewModel.loadCategories(); viewModel.loadCategories();
} }
/**
* Sets up the filter visibility toggle.
*/
private void setupFilterToggle() { private void setupFilterToggle() {
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter,
binding.etSearchProduct, binding.spinnerCategory); binding.etSearchProduct, binding.spinnerCategory);
} }
/**
* Sets up the search input listener.
*/
private void setupSearch() { private void setupSearch() {
UIUtils.attachSearch(binding.etSearchProduct, () -> loadProductData(true)); UIUtils.attachSearch(binding.etSearchProduct, () -> loadProductData(true));
} }
/**
* Configures the category filter spinner.
*/
private void setupCategoryFilter() { private void setupCategoryFilter() {
SpinnerUtils.setupFilterSpinner(binding.spinnerCategory, () -> loadProductData(true)); SpinnerUtils.setupFilterSpinner(binding.spinnerCategory, () -> loadProductData(true));
} }
/**
* Configures the swipe-to-refresh layout.
*/
private void setupSwipeRefresh() { private void setupSwipeRefresh() {
binding.swipeRefreshProduct.setOnRefreshListener(() -> loadProductData(true)); binding.swipeRefreshProduct.setOnRefreshListener(() -> loadProductData(true));
} }
/**
* Loads product data based on current filters and search query.
*/
private void loadProductData(boolean reset) { private void loadProductData(boolean reset) {
String query = binding.etSearchProduct.getText().toString().trim(); String query = binding.etSearchProduct.getText().toString().trim();
if (query.isEmpty()) query = null; if (query.isEmpty()) query = null;
@@ -120,6 +150,9 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
viewModel.loadProducts(reset, query, categoryId); viewModel.loadProducts(reset, query, categoryId);
} }
/**
* Configures the RecyclerView and its scroll listener for pagination.
*/
private void setupRecyclerView() { private void setupRecyclerView() {
adapter = new ProductAdapter(productList, this); adapter = new ProductAdapter(productList, this);
adapter.setBaseUrl(baseUrl); 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) { private void openProductDetails(int position) {
Bundle args = new Bundle(); Bundle args = new Bundle();
if (position != -1) { 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); NavHostFragment.findNavController(this).navigate(R.id.nav_product_detail, args);
} }
/**
* Handles product item clicks by opening details.
*/
@Override @Override
public void onProductClick(int position) { public void onProductClick(int position) {
openProductDetails(position); openProductDetails(position);
} }
/**
* Cleans up the binding reference.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();

View File

@@ -29,6 +29,9 @@ import java.util.List;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
/**
* Fragment for displaying and managing the relationships between products and suppliers.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class ProductSupplierFragment extends Fragment public class ProductSupplierFragment extends Fragment
implements ProductSupplierAdapter.OnProductSupplierClickListener { implements ProductSupplierAdapter.OnProductSupplierClickListener {
@@ -40,12 +43,18 @@ public class ProductSupplierFragment extends Fragment
private ProductSupplierListViewModel viewModel; private ProductSupplierListViewModel viewModel;
private BulkDeleteHandler bulkDeleteHandler; private BulkDeleteHandler bulkDeleteHandler;
/**
* Initializes the ViewModel.
*/
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(ProductSupplierListViewModel.class); viewModel = new ViewModelProvider(this).get(ProductSupplierListViewModel.class);
} }
/**
* Inflates the layout and sets up UI components, filters, and observers.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -67,6 +76,9 @@ public class ProductSupplierFragment extends Fragment
return binding.getRoot(); return binding.getRoot();
} }
/**
* Observes the ViewModel for product-supplier list, products, suppliers, and loading status.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getProductSuppliers().observe(getViewLifecycleOwner(), list -> { viewModel.getProductSuppliers().observe(getViewLifecycleOwner(), list -> {
psList.clear(); 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() { private void setupBulkDelete() {
bulkDeleteHandler = new BulkDeleteHandler( bulkDeleteHandler = new BulkDeleteHandler(
this, this,
@@ -102,6 +117,9 @@ public class ProductSupplierFragment extends Fragment
); );
} }
/**
* Reloads data and filter options when the fragment resumes.
*/
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
@@ -109,17 +127,26 @@ public class ProductSupplierFragment extends Fragment
viewModel.loadFilterData(); viewModel.loadFilterData();
} }
/**
* Cleans up the binding reference.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
binding = null; binding = null;
} }
/**
* Sets up the filter visibility toggle.
*/
private void setupFilterToggle() { private void setupFilterToggle() {
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchPS, UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchPS,
binding.spinnerProduct, binding.spinnerSupplier); binding.spinnerProduct, binding.spinnerSupplier);
} }
/**
* Configures the RecyclerView and its scroll listener for pagination.
*/
private void setupRecyclerView() { private void setupRecyclerView() {
adapter = new ProductSupplierAdapter(psList, this); adapter = new ProductSupplierAdapter(psList, this);
binding.recyclerViewPS.setLayoutManager(new LinearLayoutManager(getContext())); binding.recyclerViewPS.setLayoutManager(new LinearLayoutManager(getContext()));
@@ -142,22 +169,37 @@ public class ProductSupplierFragment extends Fragment
}); });
} }
/**
* Sets up the search input listener.
*/
private void setupSearch() { private void setupSearch() {
UIUtils.attachSearch(binding.etSearchPS, () -> loadData(true)); UIUtils.attachSearch(binding.etSearchPS, () -> loadData(true));
} }
/**
* Configures the product filter spinner.
*/
private void setupProductFilter() { private void setupProductFilter() {
SpinnerUtils.setupFilterSpinner(binding.spinnerProduct, () -> loadData(true)); SpinnerUtils.setupFilterSpinner(binding.spinnerProduct, () -> loadData(true));
} }
/**
* Configures the supplier filter spinner.
*/
private void setupSupplierFilter() { private void setupSupplierFilter() {
SpinnerUtils.setupFilterSpinner(binding.spinnerSupplier, () -> loadData(true)); SpinnerUtils.setupFilterSpinner(binding.spinnerSupplier, () -> loadData(true));
} }
/**
* Configures the swipe-to-refresh layout.
*/
private void setupSwipeRefresh() { private void setupSwipeRefresh() {
binding.swipeRefreshPS.setOnRefreshListener(() -> loadData(true)); binding.swipeRefreshPS.setOnRefreshListener(() -> loadData(true));
} }
/**
* Loads product-supplier data based on current search query and filters.
*/
private void loadData(boolean reset) { private void loadData(boolean reset) {
String query = binding.etSearchPS.getText().toString().trim(); String query = binding.etSearchPS.getText().toString().trim();
if (query.isEmpty()) query = null; if (query.isEmpty()) query = null;
@@ -177,6 +219,9 @@ public class ProductSupplierFragment extends Fragment
viewModel.loadProductSuppliers(reset, query, productId, supplierId); viewModel.loadProductSuppliers(reset, query, productId, supplierId);
} }
/**
* Navigates to the product-supplier detail screen.
*/
private void openDetail(int position) { private void openDetail(int position) {
Bundle args = new Bundle(); Bundle args = new Bundle();
if (position != -1) { if (position != -1) {
@@ -187,9 +232,15 @@ public class ProductSupplierFragment extends Fragment
NavHostFragment.findNavController(this).navigate(R.id.nav_product_supplier_detail, args); NavHostFragment.findNavController(this).navigate(R.id.nav_product_supplier_detail, args);
} }
/**
* Handles product-supplier item clicks by opening details.
*/
@Override @Override
public void onProductSupplierClick(int position) { openDetail(position); } public void onProductSupplierClick(int position) { openDetail(position); }
/**
* Forwards selection changes to the bulk delete handler.
*/
@Override @Override
public void onSelectionChanged(int count) { public void onSelectionChanged(int count) {
if (bulkDeleteHandler != null) { if (bulkDeleteHandler != null) {

View File

@@ -30,6 +30,9 @@ import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
/**
* Fragment for displaying and managing a list of purchase orders.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class PurchaseOrderFragment extends Fragment public class PurchaseOrderFragment extends Fragment
implements PurchaseOrderAdapter.OnPurchaseOrderClickListener { implements PurchaseOrderAdapter.OnPurchaseOrderClickListener {
@@ -41,12 +44,18 @@ public class PurchaseOrderFragment extends Fragment
@Inject TokenManager tokenManager; @Inject TokenManager tokenManager;
/**
* Initializes the ViewModel.
*/
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(PurchaseOrderListViewModel.class); viewModel = new ViewModelProvider(this).get(PurchaseOrderListViewModel.class);
} }
/**
* Inflates the layout and sets up UI components, filters, and observers.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -64,6 +73,9 @@ public class PurchaseOrderFragment extends Fragment
return binding.getRoot(); return binding.getRoot();
} }
/**
* Observes the ViewModel for purchase order list, stores, and loading status.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getPurchaseOrders().observe(getViewLifecycleOwner(), list -> { viewModel.getPurchaseOrders().observe(getViewLifecycleOwner(), list -> {
poList.clear(); poList.clear();
@@ -81,6 +93,9 @@ public class PurchaseOrderFragment extends Fragment
}); });
} }
/**
* Reloads data and stores if necessary when the fragment resumes.
*/
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
@@ -88,6 +103,9 @@ public class PurchaseOrderFragment extends Fragment
if (!isStaff()) viewModel.loadStores(); if (!isStaff()) viewModel.loadStores();
} }
/**
* Sets up the filter visibility toggle
*/
private void setupFilterToggle() { private void setupFilterToggle() {
if (isStaff()) { if (isStaff()) {
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchPO); 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() { private boolean isStaff() {
return "STAFF".equalsIgnoreCase(tokenManager.getRole()); return "STAFF".equalsIgnoreCase(tokenManager.getRole());
} }
/**
* Sets up the search input listener.
*/
private void setupSearch() { private void setupSearch() {
UIUtils.attachSearch(binding.etSearchPO, () -> loadData(true)); UIUtils.attachSearch(binding.etSearchPO, () -> loadData(true));
} }
/**
* Configures the store filter spinner.
*/
private void setupStoreFilter() { private void setupStoreFilter() {
SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadData(true)); SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadData(true));
} }
/**
* Configures the RecyclerView and its scroll listener for pagination.
*/
private void setupRecyclerView() { private void setupRecyclerView() {
adapter = new PurchaseOrderAdapter(poList, this); adapter = new PurchaseOrderAdapter(poList, this);
binding.recyclerViewPO.setLayoutManager(new LinearLayoutManager(getContext())); binding.recyclerViewPO.setLayoutManager(new LinearLayoutManager(getContext()));
@@ -131,10 +161,16 @@ public class PurchaseOrderFragment extends Fragment
}); });
} }
/**
* Configures the swipe-to-refresh layout.
*/
private void setupSwipeRefresh() { private void setupSwipeRefresh() {
binding.swipeRefreshPO.setOnRefreshListener(() -> loadData(true)); binding.swipeRefreshPO.setOnRefreshListener(() -> loadData(true));
} }
/**
* Loads purchase order data based on current search query and store filter.
*/
private void loadData(boolean reset) { private void loadData(boolean reset) {
String query = binding.etSearchPO != null ? binding.etSearchPO.getText().toString().trim() : ""; String query = binding.etSearchPO != null ? binding.etSearchPO.getText().toString().trim() : "";
if (query.isEmpty()) query = null; if (query.isEmpty()) query = null;
@@ -153,6 +189,9 @@ public class PurchaseOrderFragment extends Fragment
viewModel.loadPurchaseOrders(reset, query, storeId); viewModel.loadPurchaseOrders(reset, query, storeId);
} }
/**
* Navigates to the purchase order detail screen.
*/
private void openDetail(int position) { private void openDetail(int position) {
Bundle args = new Bundle(); Bundle args = new Bundle();
PurchaseOrderDTO po = poList.get(position); 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); NavHostFragment.findNavController(this).navigate(R.id.nav_purchase_order_detail, args);
} }
/**
* Handles purchase order item clicks by opening details.
*/
@Override @Override
public void onPurchaseOrderClick(int position) { public void onPurchaseOrderClick(int position) {
openDetail(position); openDetail(position);
} }
/**
* Cleans up the binding reference.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();

View File

@@ -31,6 +31,9 @@ import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
/**
* Fragment for displaying and managing a list of sales.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickListener { public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickListener {
@@ -41,6 +44,9 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
@Inject TokenManager tokenManager; @Inject TokenManager tokenManager;
/**
* Inflates the layout for this fragment.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -48,6 +54,9 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
return binding.getRoot(); return binding.getRoot();
} }
/**
* Initializes UI components and observers after the view is created.
*/
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, 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)); NavHostFragment.findNavController(this).navigate(R.id.nav_refund));
} }
/**
* Observes LiveData from the ViewModel to update the list and filter options.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getSales().observe(getViewLifecycleOwner(), list -> { viewModel.getSales().observe(getViewLifecycleOwner(), list -> {
saleList.clear(); saleList.clear();
@@ -101,6 +113,9 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
}); });
} }
/**
* Refreshes lookup data when the fragment is resumed.
*/
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
@@ -108,6 +123,9 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
viewModel.loadCustomers(); viewModel.loadCustomers();
} }
/**
* Sets up the visibility of filters.
*/
private void setupFilterToggle() { private void setupFilterToggle() {
if (isStaff()) { if (isStaff()) {
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchSale, 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() { private boolean isStaff() {
return "STAFF".equalsIgnoreCase(tokenManager.getRole()); return "STAFF".equalsIgnoreCase(tokenManager.getRole());
} }
/**
* Checks if the current user has the 'ADMIN' role.
*/
private boolean isAdmin() { private boolean isAdmin() {
return "ADMIN".equalsIgnoreCase(tokenManager.getRole()); return "ADMIN".equalsIgnoreCase(tokenManager.getRole());
} }
/**
* Initializes the store filter spinner.
*/
private void setupStoreFilter() { private void setupStoreFilter() {
SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadSales(true)); SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadSales(true));
} }
/**
* Initializes the payment method filter spinner.
*/
private void setupPaymentMethodFilter() { private void setupPaymentMethodFilter() {
String[] paymentMethods = {"All Payments", "Cash", "Card"}; String[] paymentMethods = {"All Payments", "Cash", "Card"};
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerPaymentMethod, paymentMethods, () -> loadSales(true)); SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerPaymentMethod, paymentMethods, () -> loadSales(true));
} }
/**
* Initializes the refund status filter spinner.
*/
private void setupRefundStatusFilter() { private void setupRefundStatusFilter() {
String[] refundStatuses = {"All Status", "Sale", "Refund"}; String[] refundStatuses = {"All Status", "Sale", "Refund"};
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerRefundStatus, refundStatuses, () -> loadSales(true)); SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerRefundStatus, refundStatuses, () -> loadSales(true));
} }
/**
* Initializes the customer filter spinner.
*/
private void setupCustomerFilter() { private void setupCustomerFilter() {
SpinnerUtils.setupFilterSpinner(binding.spinnerCustomer, () -> loadSales(true)); SpinnerUtils.setupFilterSpinner(binding.spinnerCustomer, () -> loadSales(true));
} }
/**
* Configures the RecyclerView and its scroll listener for pagination.
*/
private void setupRecyclerView() { private void setupRecyclerView() {
adapter = new SaleAdapter(saleList, this); adapter = new SaleAdapter(saleList, this);
binding.recyclerViewSales.setLayoutManager(new LinearLayoutManager(getContext())); 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() { private void setupSearch() {
UIUtils.attachSearch(binding.etSearchSale, () -> loadSales(true)); UIUtils.attachSearch(binding.etSearchSale, () -> loadSales(true));
} }
/**
* Configures the swipe-to-refresh layout.
*/
private void setupSwipeRefresh() { private void setupSwipeRefresh() {
binding.swipeRefreshSale.setOnRefreshListener(() -> loadSales(true)); binding.swipeRefreshSale.setOnRefreshListener(() -> loadSales(true));
} }
/**
* Triggers loading of sale data from the backend with current filters.
*/
private void loadSales(boolean reset) { private void loadSales(boolean reset) {
String query = binding.etSearchSale != null ? binding.etSearchSale.getText().toString().trim() : ""; String query = binding.etSearchSale != null ? binding.etSearchSale.getText().toString().trim() : "";
if (query.isEmpty()) query = null; 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); viewModel.loadSales(reset, query, paymentMethod, storeId, isRefund, customerId);
} }
/**
* Handles clicks on individual sales in the list.
*/
@Override @Override
public void onSaleClick(int position) { public void onSaleClick(int position) {
if (position < 0 || position >= saleList.size()) return; 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); NavHostFragment.findNavController(this).navigate(R.id.nav_sale_detail, args);
} }
/**
* Cleans up the binding when the view is destroyed.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();

View File

@@ -39,12 +39,18 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
private ServiceListViewModel viewModel; private ServiceListViewModel viewModel;
private BulkDeleteHandler bulkDeleteHandler; private BulkDeleteHandler bulkDeleteHandler;
/**
* Initializes the ViewModel.
*/
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(ServiceListViewModel.class); viewModel = new ViewModelProvider(this).get(ServiceListViewModel.class);
} }
/**
* Inflates the layout and sets up UI components and observers.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -67,6 +73,9 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
return binding.getRoot(); return binding.getRoot();
} }
/**
* Observes the ViewModel for service list updates and loading status.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getServices().observe(getViewLifecycleOwner(), list -> { viewModel.getServices().observe(getViewLifecycleOwner(), list -> {
serviceList.clear(); 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() { private void setupBulkDelete() {
bulkDeleteHandler = new BulkDeleteHandler( bulkDeleteHandler = new BulkDeleteHandler(
this, this,
@@ -92,20 +104,32 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
); );
} }
/**
* Cleans up the binding reference.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
binding = null; binding = null;
} }
/**
* Sets up the filter visibility toggle.
*/
private void setupFilterToggle() { private void setupFilterToggle() {
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchService); UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchService);
} }
/**
* Sets up the search input listener.
*/
private void setupSearch() { private void setupSearch() {
UIUtils.attachSearch(binding.etSearchService, () -> loadServices(true)); UIUtils.attachSearch(binding.etSearchService, () -> loadServices(true));
} }
/**
* Configures the RecyclerView and its scroll listener for pagination.
*/
private void setupRecyclerView() { private void setupRecyclerView() {
adapter = new ServiceAdapter(serviceList, this); adapter = new ServiceAdapter(serviceList, this);
binding.recyclerViewServices.setLayoutManager(new LinearLayoutManager(getContext())); 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() { private void setupSwipeRefresh() {
binding.swipeRefreshService.setOnRefreshListener(() -> loadServices(true)); binding.swipeRefreshService.setOnRefreshListener(() -> loadServices(true));
} }
/**
* Loads service data based on current filters and search query.
*/
private void loadServices(boolean reset) { private void loadServices(boolean reset) {
String query = binding.etSearchService.getText().toString().trim(); String query = binding.etSearchService.getText().toString().trim();
if (query.isEmpty()) query = null; if (query.isEmpty()) query = null;
viewModel.loadServices(reset, query); viewModel.loadServices(reset, query);
} }
/**
* Navigates to the service detail screen.
*/
private void openDetail(ServiceDTO service) { private void openDetail(ServiceDTO service) {
Bundle args = new Bundle(); Bundle args = new Bundle();
if (service != null) { 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); NavHostFragment.findNavController(this).navigate(R.id.nav_service_detail, args);
} }
/**
* Handles service item clicks by opening details.
*/
@Override @Override
public void onServiceClick(int position) { public void onServiceClick(int position) {
if (position >= 0 && position < serviceList.size()) { 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 @Override
public void onSelectionChanged(int count) { public void onSelectionChanged(int count) {
if (bulkDeleteHandler != null) { if (bulkDeleteHandler != null) {

View File

@@ -24,6 +24,9 @@ import java.util.*;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
/**
* Fragment for displaying and managing a list of staff members.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmployeeClickListener { 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 @Named("baseUrl") String baseUrl;
@Inject TokenManager tokenManager; @Inject TokenManager tokenManager;
/**
* Inflates the layout and initializes UI components, filters, and observers.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -60,6 +66,9 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
return binding.getRoot(); return binding.getRoot();
} }
/**
* Observes LiveData from the ViewModel to update the list and filter options.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getFilteredEmployees().observe(getViewLifecycleOwner(), list -> { viewModel.getFilteredEmployees().observe(getViewLifecycleOwner(), list -> {
staffList.clear(); 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() { private void setupRecyclerView() {
adapter = new EmployeeAdapter(staffList, this); adapter = new EmployeeAdapter(staffList, this);
adapter.setBaseUrl(baseUrl); adapter.setBaseUrl(baseUrl);
@@ -101,19 +113,31 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
}); });
} }
/**
* Initializes the status filter spinner.
*/
private void setupStatusFilter() { private void setupStatusFilter() {
String[] statuses = {"All Statuses", "Active", "Inactive"}; String[] statuses = {"All Statuses", "Active", "Inactive"};
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatusStaff, statuses, this::applyFilters); SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatusStaff, statuses, this::applyFilters);
} }
/**
* Initializes the store filter spinner.
*/
private void setupStoreFilter() { private void setupStoreFilter() {
SpinnerUtils.setupFilterSpinner(binding.spinnerStoreStaff, this::applyFilters); SpinnerUtils.setupFilterSpinner(binding.spinnerStoreStaff, this::applyFilters);
} }
/**
* Attaches search functionality to the search input field.
*/
private void setupSearch() { private void setupSearch() {
UIUtils.attachSearch(binding.etSearchStaff, this::applyFilters); UIUtils.attachSearch(binding.etSearchStaff, this::applyFilters);
} }
/**
* Applies the selected filters and triggers a data reload from the view model.
*/
private void applyFilters() { private void applyFilters() {
String query = binding.etSearchStaff.getText().toString().trim(); String query = binding.etSearchStaff.getText().toString().trim();
String status = binding.spinnerStatusStaff.getSelectedItem() != null ? String status = binding.spinnerStatusStaff.getSelectedItem() != null ?
@@ -128,10 +152,16 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
viewModel.filter(query, storeId, status); viewModel.filter(query, storeId, status);
} }
/**
* Configures the swipe-to-refresh layout.
*/
private void setupSwipeRefresh() { private void setupSwipeRefresh() {
binding.swipeRefreshStaff.setOnRefreshListener(() -> viewModel.loadStaff(true)); binding.swipeRefreshStaff.setOnRefreshListener(() -> viewModel.loadStaff(true));
} }
/**
* Navigates to the staff detail screen for adding or editing an employee.
*/
private void openDetail(int position) { private void openDetail(int position) {
Bundle args = new Bundle(); Bundle args = new Bundle();
if (position != -1) { 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); NavHostFragment.findNavController(this).navigate(R.id.nav_staff_detail, args);
} }
/**
* Handles clicks on individual staff members in the list.
*/
@Override @Override
public void onEmployeeClick(int position) { public void onEmployeeClick(int position) {
openDetail(position); openDetail(position);
} }
/**
* Cleans up the binding when the view is destroyed.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();

View File

@@ -27,6 +27,9 @@ import java.util.List;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
/**
* Fragment for displaying and managing a list of suppliers.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupplierClickListener { public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupplierClickListener {
@@ -36,12 +39,18 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
private SupplierListViewModel viewModel; private SupplierListViewModel viewModel;
private BulkDeleteHandler bulkDeleteHandler; private BulkDeleteHandler bulkDeleteHandler;
/**
* Initializes the ViewModel.
*/
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(SupplierListViewModel.class); viewModel = new ViewModelProvider(this).get(SupplierListViewModel.class);
} }
/**
* Inflates the layout and sets up UI components, bulk delete, and observers.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -63,6 +72,9 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
return binding.getRoot(); return binding.getRoot();
} }
/**
* Observes the ViewModel for supplier list updates and loading status.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getSuppliers().observe(getViewLifecycleOwner(), list -> { viewModel.getSuppliers().observe(getViewLifecycleOwner(), list -> {
supplierList.clear(); 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() { private void setupBulkDelete() {
bulkDeleteHandler = new BulkDeleteHandler( bulkDeleteHandler = new BulkDeleteHandler(
this, this,
@@ -88,24 +103,39 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
); );
} }
/**
* Cleans up the binding reference.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
binding = null; binding = null;
} }
/**
* Sets up the filter visibility toggle.
*/
private void setupFilterToggle() { private void setupFilterToggle() {
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchSupplier); UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchSupplier);
} }
/**
* Sets up the search input listener.
*/
private void setupSearch() { private void setupSearch() {
UIUtils.attachSearch(binding.etSearchSupplier, () -> loadSupplierData(true)); UIUtils.attachSearch(binding.etSearchSupplier, () -> loadSupplierData(true));
} }
/**
* Configures the swipe-to-refresh layout.
*/
private void setupSwipeRefresh() { private void setupSwipeRefresh() {
binding.swipeRefreshSupplier.setOnRefreshListener(() -> loadSupplierData(true)); binding.swipeRefreshSupplier.setOnRefreshListener(() -> loadSupplierData(true));
} }
/**
* Navigates to the supplier detail screen.
*/
private void openSupplierDetails(int position) { private void openSupplierDetails(int position) {
Bundle args = new Bundle(); Bundle args = new Bundle();
if (position != -1) { 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); NavHostFragment.findNavController(this).navigate(R.id.nav_supplier_detail, args);
} }
/**
* Handles supplier item clicks by opening details.
*/
@Override @Override
public void onSupplierClick(int position) { public void onSupplierClick(int position) {
openSupplierDetails(position); openSupplierDetails(position);
} }
/**
* Forwards selection changes to the bulk delete handler.
*/
@Override @Override
public void onSelectionChanged(int count) { public void onSelectionChanged(int count) {
if (bulkDeleteHandler != null) { 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) { private void loadSupplierData(boolean reset) {
String query = binding.etSearchSupplier != null ? binding.etSearchSupplier.getText().toString().trim() : ""; String query = binding.etSearchSupplier != null ? binding.etSearchSupplier.getText().toString().trim() : "";
if (query.isEmpty()) query = null; if (query.isEmpty()) query = null;
viewModel.loadSuppliers(reset, query); viewModel.loadSuppliers(reset, query);
} }
/**
* Configures the RecyclerView and its scroll listener for pagination.
*/
private void setupRecyclerView() { private void setupRecyclerView() {
adapter = new SupplierAdapter(supplierList, this); adapter = new SupplierAdapter(supplierList, this);
binding.recyclerViewSuppliers.setLayoutManager(new LinearLayoutManager(getContext())); binding.recyclerViewSuppliers.setLayoutManager(new LinearLayoutManager(getContext()));

View File

@@ -39,12 +39,18 @@ public class AdoptionDetailFragment extends Fragment {
@Inject TokenManager tokenManager; @Inject TokenManager tokenManager;
/**
* Initializes the fragment and its ViewModel.
*/
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(AdoptionDetailViewModel.class); viewModel = new ViewModelProvider(this).get(AdoptionDetailViewModel.class);
} }
/**
* Inflates the layout for the fragment
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -52,6 +58,9 @@ public class AdoptionDetailFragment extends Fragment {
return binding.getRoot(); return binding.getRoot();
} }
/**
* Sets up UI components, observers, and loads initial data after the view is created.
*/
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
@@ -68,6 +77,9 @@ public class AdoptionDetailFragment extends Fragment {
binding.btnDeleteAdoption.setOnClickListener(v -> confirmDelete()); binding.btnDeleteAdoption.setOnClickListener(v -> confirmDelete());
} }
/**
* Sets up observers for ViewModel to update the UI dynamically.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getViewState().observe(getViewLifecycleOwner(), this::applyViewState); 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) { private void setLoading(boolean loading) {
if (binding != null) { if (binding != null) {
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE); binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
} }
} }
/**
* Cleans up the binding reference.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
binding = null; binding = null;
} }
/**
* Initializes the spinners with data and selection listeners.
*/
private void setupSpinners() { private void setupSpinners() {
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerAdoptionStatus, new String[]{}); SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerAdoptionStatus, new String[]{});
@@ -141,11 +162,17 @@ public class AdoptionDetailFragment extends Fragment {
SpinnerUtils.setOnIndexSelectedListener(binding.spinnerAdoptionStatus, p -> notifyDateStatusChange()); SpinnerUtils.setOnIndexSelectedListener(binding.spinnerAdoptionStatus, p -> notifyDateStatusChange());
} }
/**
* Configures the date picker for the adoption date field.
*/
private void setupDatePicker() { private void setupDatePicker() {
binding.etAdoptionDate.setOnClickListener(v -> binding.etAdoptionDate.setOnClickListener(v ->
UIUtils.showDatePicker(requireContext(), binding.etAdoptionDate, this::notifyDateStatusChange)); UIUtils.showDatePicker(requireContext(), binding.etAdoptionDate, this::notifyDateStatusChange));
} }
/**
* Notifies the ViewModel when the date or status changes to update available options.
*/
private void notifyDateStatusChange() { private void notifyDateStatusChange() {
if (isUpdatingUI) return; if (isUpdatingUI) return;
String date = binding.etAdoptionDate.getText().toString(); String date = binding.etAdoptionDate.getText().toString();
@@ -154,6 +181,9 @@ public class AdoptionDetailFragment extends Fragment {
viewModel.onDateChanged(date, status); viewModel.onDateChanged(date, status);
} }
/**
* Uses fragment arguments to determine if we are editing an existing record.
*/
private void handleArguments() { private void handleArguments() {
Bundle a = getArguments(); Bundle a = getArguments();
if (a != null && a.containsKey("adoptionId")) { if (a != null && a.containsKey("adoptionId")) {
@@ -164,6 +194,9 @@ public class AdoptionDetailFragment extends Fragment {
viewModel.setAdoptionId(-1); viewModel.setAdoptionId(-1);
} }
/**
* Load the adoption data from the backend.
*/
private void loadAdoptionData() { private void loadAdoptionData() {
viewModel.loadAdoption().observe(getViewLifecycleOwner(), resource -> { viewModel.loadAdoption().observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return; 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) { private void applyViewState(AdoptionDetailViewModel.ViewState state) {
isUpdatingUI = true; isUpdatingUI = true;
@@ -224,10 +261,16 @@ public class AdoptionDetailFragment extends Fragment {
isUpdatingUI = false; isUpdatingUI = false;
} }
/**
* Checks if the currently logged-in user has the STAFF role.
*/
private boolean isStaff() { private boolean isStaff() {
return "STAFF".equalsIgnoreCase(tokenManager.getRole()); return "STAFF".equalsIgnoreCase(tokenManager.getRole());
} }
/**
* Validates input and saves the adoption record.
*/
private void saveAdoption() { private void saveAdoption() {
if (!InputValidator.isSpinnerSelected(binding.spinnerAdoptionCustomer, "Customer")) return; if (!InputValidator.isSpinnerSelected(binding.spinnerAdoptionCustomer, "Customer")) return;
if (!InputValidator.isSpinnerSelected(binding.spinnerAdoptionPet, "Pet")) 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() { private void confirmDelete() {
DialogUtils.showDeleteConfirmDialog(requireContext(), "Adoption Record", () -> DialogUtils.showDeleteConfirmDialog(requireContext(), "Adoption Record", () ->
viewModel.deleteAdoption().observe(getViewLifecycleOwner(), resource -> { viewModel.deleteAdoption().observe(getViewLifecycleOwner(), resource -> {
@@ -289,6 +335,9 @@ public class AdoptionDetailFragment extends Fragment {
})); }));
} }
/**
* Navigates back to the previous screen.
*/
private void navigateBack() { private void navigateBack() {
NavHostFragment.findNavController(this).popBackStack(); NavHostFragment.findNavController(this).popBackStack();
} }

View File

@@ -47,18 +47,27 @@ public class AppointmentDetailFragment extends Fragment {
@Inject TokenManager tokenManager; @Inject TokenManager tokenManager;
/**
* Initializes the fragment and its ViewModel.
*/
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(AppointmentDetailViewModel.class); viewModel = new ViewModelProvider(this).get(AppointmentDetailViewModel.class);
} }
/**
* Inflates the layout for the fragment
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
binding = FragmentAppointmentDetailBinding.inflate(inflater, container, false); binding = FragmentAppointmentDetailBinding.inflate(inflater, container, false);
return binding.getRoot(); return binding.getRoot();
} }
/**
* Sets up UI components, observers, and loads initial data after the view is created.
*/
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
@@ -73,12 +82,18 @@ public class AppointmentDetailFragment extends Fragment {
binding.btnDeleteAppointment.setOnClickListener(v -> confirmDelete()); binding.btnDeleteAppointment.setOnClickListener(v -> confirmDelete());
} }
/**
* Cleans up the binding reference.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
binding = null; binding = null;
} }
/**
* Initializes the spinners with data and selection listeners.
*/
private void setupSpinners() { private void setupSpinners() {
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerAppointmentStatus, new String[]{}); SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerAppointmentStatus, new String[]{});
@@ -108,11 +123,17 @@ public class AppointmentDetailFragment extends Fragment {
SpinnerUtils.setOnIndexSelectedListener(binding.spinnerAppointmentStatus, p -> notifyDateTimeStatusChange()); SpinnerUtils.setOnIndexSelectedListener(binding.spinnerAppointmentStatus, p -> notifyDateTimeStatusChange());
} }
/**
* Configures the date picker for the appointment date field.
*/
private void setupDatePicker() { private void setupDatePicker() {
binding.etAppointmentDate.setOnClickListener(v -> binding.etAppointmentDate.setOnClickListener(v ->
UIUtils.showDatePicker(requireContext(), binding.etAppointmentDate, this::notifyDateTimeStatusChange)); UIUtils.showDatePicker(requireContext(), binding.etAppointmentDate, this::notifyDateTimeStatusChange));
} }
/**
* Sets up observers for ViewModel to update the UI dynamically.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getViewState().observe(getViewLifecycleOwner(), this::applyViewState); 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) { private void setLoading(boolean loading) {
if (binding != null && binding.progressBar != null) { if (binding != null && binding.progressBar != null) {
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE); 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) { private void applyViewState(AppointmentDetailViewModel.ViewState state) {
isUpdatingUI = true; isUpdatingUI = true;
@@ -201,10 +229,16 @@ public class AppointmentDetailFragment extends Fragment {
isUpdatingUI = false; isUpdatingUI = false;
} }
/**
* Checks if the currently logged-in user has the STAFF role.
*/
private boolean isStaff() { private boolean isStaff() {
return "STAFF".equalsIgnoreCase(tokenManager.getRole()); return "STAFF".equalsIgnoreCase(tokenManager.getRole());
} }
/**
* Notifies the ViewModel when the date, time, or status changes to update available options.
*/
private void notifyDateTimeStatusChange() { private void notifyDateTimeStatusChange() {
if (isUpdatingUI) return; if (isUpdatingUI) return;
@@ -215,6 +249,9 @@ public class AppointmentDetailFragment extends Fragment {
viewModel.onDateOrTimeChanged(date, time, status); viewModel.onDateOrTimeChanged(date, time, status);
} }
/**
* Uses fragment arguments to determine if we are editing an existing record.
*/
private void handleArguments() { private void handleArguments() {
Bundle a = getArguments(); Bundle a = getArguments();
if (a != null && a.containsKey("appointmentId")) { 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() { private void loadAppointmentData() {
viewModel.loadAppointment().observe(getViewLifecycleOwner(), resource -> { viewModel.loadAppointment().observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return; if (resource == null) return;
@@ -244,6 +284,9 @@ public class AppointmentDetailFragment extends Fragment {
}); });
} }
/**
* Validates input and saves the appointment record.
*/
private void saveAppointment() { private void saveAppointment() {
if (!validateRequiredFields()) return; if (!validateRequiredFields()) return;
@@ -275,6 +318,9 @@ public class AppointmentDetailFragment extends Fragment {
}); });
} }
/**
* Validates that all required fields are selected or filled.
*/
private boolean validateRequiredFields() { private boolean validateRequiredFields() {
if (!InputValidator.isSpinnerSelected(binding.spinnerCustomer, "Customer")) return false; if (!InputValidator.isSpinnerSelected(binding.spinnerCustomer, "Customer")) return false;
if (!InputValidator.isSpinnerSelected(binding.spinnerStore, "Store")) return false; if (!InputValidator.isSpinnerSelected(binding.spinnerStore, "Store")) return false;
@@ -284,15 +330,24 @@ public class AppointmentDetailFragment extends Fragment {
return true; return true;
} }
/**
* Formats the selected hour and minute from spinners into a time string.
*/
private String buildTimeString() { private String buildTimeString() {
return DateTimeUtils.formatTime(HOURS[binding.spinnerHour.getSelectedItemPosition()], MINUTES[binding.spinnerMinute.getSelectedItemPosition()]); return DateTimeUtils.formatTime(HOURS[binding.spinnerHour.getSelectedItemPosition()], MINUTES[binding.spinnerMinute.getSelectedItemPosition()]);
} }
/**
* Handles errors that occur during the save process.
*/
private void handleSaveError(String errorMessage) { private void handleSaveError(String errorMessage) {
if (errorMessage != null && errorMessage.toLowerCase().contains("not available")) showNoAvailabilityDialog(); if (errorMessage != null && errorMessage.toLowerCase().contains("not available")) showNoAvailabilityDialog();
else Toast.makeText(getContext(), errorMessage != null ? errorMessage : "Error saving", Toast.LENGTH_SHORT).show(); 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() { private void showNoAvailabilityDialog() {
new androidx.appcompat.app.AlertDialog.Builder(requireContext()) new androidx.appcompat.app.AlertDialog.Builder(requireContext())
.setTitle("No Availability") .setTitle("No Availability")
@@ -301,6 +356,9 @@ public class AppointmentDetailFragment extends Fragment {
.setNegativeButton("Cancel Booking", (d, w) -> navigateBack()).show(); .setNegativeButton("Cancel Booking", (d, w) -> navigateBack()).show();
} }
/**
* Displays a confirmation dialog before deleting the appointment record.
*/
private void confirmDelete() { private void confirmDelete() {
DialogUtils.showDeleteConfirmDialog(requireContext(), "Appointment", () -> DialogUtils.showDeleteConfirmDialog(requireContext(), "Appointment", () ->
viewModel.deleteAppointment().observe(getViewLifecycleOwner(), resource -> { viewModel.deleteAppointment().observe(getViewLifecycleOwner(), resource -> {
@@ -310,10 +368,16 @@ public class AppointmentDetailFragment extends Fragment {
})); }));
} }
/**
* Navigates back to the previous screen.
*/
private void navigateBack() { private void navigateBack() {
NavHostFragment.findNavController(this).popBackStack(); NavHostFragment.findNavController(this).popBackStack();
} }
/**
* Parses a time string and sets the hour and minute spinners accordingly.
*/
private void parseAndSetTimeSpinners(String time) { private void parseAndSetTimeSpinners(String time) {
int[] parsedTime = DateTimeUtils.parseTimeString(time); int[] parsedTime = DateTimeUtils.parseTimeString(time);
if (parsedTime == null) return; if (parsedTime == null) return;

View File

@@ -29,12 +29,18 @@ import java.util.Locale;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
/**
* Fragment for displaying and editing coupon details.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class CouponDetailFragment extends Fragment { public class CouponDetailFragment extends Fragment {
private FragmentCouponDetailBinding binding; private FragmentCouponDetailBinding binding;
private CouponDetailViewModel viewModel; private CouponDetailViewModel viewModel;
private long couponId = -1; private long couponId = -1;
/**
* Inflates the layout, initializes ViewModel, and sets up UI components.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -74,10 +80,16 @@ public class CouponDetailFragment extends Fragment {
return binding.getRoot(); return binding.getRoot();
} }
/**
* Initializes the discount type spinner with options.
*/
private void setupDiscountTypeSpinner() { private void setupDiscountTypeSpinner() {
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerDiscountTypeDetail, new String[]{"FIXED", "PERCENT"}); 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) { private void setupDatePicker(android.widget.EditText editText, android.widget.EditText dependOn, Runnable onDateSet) {
editText.setFocusable(false); editText.setFocusable(false);
editText.setClickable(true); 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() { private void loadCouponDetails() {
binding.tvCouponId.setText(DateTimeUtils.formatId(couponId)); binding.tvCouponId.setText(DateTimeUtils.formatId(couponId));
binding.tvCouponId.setVisibility(View.VISIBLE); binding.tvCouponId.setVisibility(View.VISIBLE);
@@ -111,6 +126,9 @@ public class CouponDetailFragment extends Fragment {
}); });
} }
/**
* Validates input and saves the coupon record.
*/
private void saveCoupon() { private void saveCoupon() {
if (!InputValidator.isNotEmpty(binding.etCouponCodeDetail, "Coupon Code")) return; if (!InputValidator.isNotEmpty(binding.etCouponCodeDetail, "Coupon Code")) return;
if (!InputValidator.isGreaterThanZero(binding.etDiscountValueDetail, "Discount Value")) 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() { private void confirmDelete() {
new AlertDialog.Builder(requireContext()) new AlertDialog.Builder(requireContext())
.setTitle("Delete Coupon") .setTitle("Delete Coupon")

View File

@@ -21,6 +21,9 @@ import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
/**
* Fragment for displaying and editing customer details.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class CustomerDetailFragment extends Fragment { public class CustomerDetailFragment extends Fragment {
@@ -31,6 +34,9 @@ public class CustomerDetailFragment extends Fragment {
private final String[] STATUSES = {"Active", "Inactive"}; private final String[] STATUSES = {"Active", "Inactive"};
/**
* Inflates the layout, initializes ViewModel, and sets up UI components.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -49,10 +55,16 @@ public class CustomerDetailFragment extends Fragment {
return binding.getRoot(); return binding.getRoot();
} }
/**
* Initializes the spinners with data.
*/
private void setupSpinners() { private void setupSpinners() {
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerCustomerStatus, STATUSES); SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerCustomerStatus, STATUSES);
} }
/**
* Uses fragment arguments to determine if we are editing an existing record.
*/
private void handleArguments() { private void handleArguments() {
Bundle a = getArguments(); Bundle a = getArguments();
if (a != null && a.getBoolean("isEditing", false)) { 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) { private void loadCustomerData(long id) {
viewModel.loadCustomer(id).observe(getViewLifecycleOwner(), resource -> { viewModel.loadCustomer(id).observe(getViewLifecycleOwner(), resource -> {
if (resource != null) { if (resource != null) {
@@ -105,12 +120,18 @@ public class CustomerDetailFragment extends Fragment {
}); });
} }
/**
* Shows or hides the loading bar.
*/
private void setLoading(boolean loading) { private void setLoading(boolean loading) {
if (binding != null && binding.progressBar != null) { if (binding != null && binding.progressBar != null) {
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE); binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
} }
} }
/**
* Validates input and saves the customer record.
*/
private void save() { private void save() {
if (!InputValidator.isNotEmpty(binding.etCustomerUsername, "Username")) return; 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() { private void confirmDelete() {
DialogUtils.showDeleteConfirmDialog(requireContext(), "Customer", () -> DialogUtils.showDeleteConfirmDialog(requireContext(), "Customer", () ->
viewModel.deleteCustomer().observe(getViewLifecycleOwner(), resource -> { viewModel.deleteCustomer().observe(getViewLifecycleOwner(), resource -> {
@@ -175,10 +199,16 @@ public class CustomerDetailFragment extends Fragment {
})); }));
} }
/**
* Navigates back to the previous screen.
*/
private void navigateBack() { private void navigateBack() {
NavHostFragment.findNavController(this).popBackStack(); NavHostFragment.findNavController(this).popBackStack();
} }
/**
* Cleans up the binding reference.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();

View File

@@ -37,12 +37,18 @@ public class InventoryDetailFragment extends Fragment {
private long preselectedStoreId = -1; private long preselectedStoreId = -1;
private long preselectedProductId = -1; private long preselectedProductId = -1;
/**
* Initializes the fragment and its ViewModel.
*/
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(InventoryDetailViewModel.class); viewModel = new ViewModelProvider(this).get(InventoryDetailViewModel.class);
} }
/**
* Inflates the layout for the fragment.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -50,6 +56,9 @@ public class InventoryDetailFragment extends Fragment {
return binding.getRoot(); return binding.getRoot();
} }
/**
* Sets up UI components, observers, and loads initial data after the view is created.
*/
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
@@ -63,23 +72,35 @@ public class InventoryDetailFragment extends Fragment {
binding.btnDeleteInventory.setOnClickListener(v -> confirmDelete()); binding.btnDeleteInventory.setOnClickListener(v -> confirmDelete());
} }
/**
* Sets up observers for ViewModel to update the UI dynamically.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getStoreList().observe(getViewLifecycleOwner(), list -> refreshStoreSpinner()); viewModel.getStoreList().observe(getViewLifecycleOwner(), list -> refreshStoreSpinner());
viewModel.getProductList().observe(getViewLifecycleOwner(), list -> refreshProductSpinner()); viewModel.getProductList().observe(getViewLifecycleOwner(), list -> refreshProductSpinner());
} }
/**
* Shows or hides the loading bar.
*/
private void setLoading(boolean loading) { private void setLoading(boolean loading) {
if (binding != null && binding.progressBar != null) { if (binding != null && binding.progressBar != null) {
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE); binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
} }
} }
/**
* Cleans up the binding reference.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
binding = null; binding = null;
} }
/**
* Loads initial data for the store and product spinners.
*/
private void loadSpinnersData() { private void loadSpinnersData() {
viewModel.loadStores().observe(getViewLifecycleOwner(), resource -> { viewModel.loadStores().observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return; if (resource == null) return;
@@ -97,18 +118,27 @@ public class InventoryDetailFragment extends Fragment {
}); });
} }
/**
* Refreshes the store spinner with the current data.
*/
private void refreshStoreSpinner() { private void refreshStoreSpinner() {
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerInventoryStore, viewModel.getStoreList().getValue(), SpinnerUtils.populateSpinner(requireContext(), binding.spinnerInventoryStore, viewModel.getStoreList().getValue(),
DropdownDTO::getLabel, "-- Select Store --", DropdownDTO::getLabel, "-- Select Store --",
preselectedStoreId, DropdownDTO::getId); preselectedStoreId, DropdownDTO::getId);
} }
/**
* Refreshes the product spinner with the current data.
*/
private void refreshProductSpinner() { private void refreshProductSpinner() {
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerInventoryProduct, viewModel.getProductList().getValue(), SpinnerUtils.populateSpinner(requireContext(), binding.spinnerInventoryProduct, viewModel.getProductList().getValue(),
DropdownDTO::getLabel, "-- Select Product --", DropdownDTO::getLabel, "-- Select Product --",
preselectedProductId, DropdownDTO::getId); preselectedProductId, DropdownDTO::getId);
} }
/**
* Uses fragment arguments to determine if we are editing an existing record.
*/
private void handleArguments() { private void handleArguments() {
Bundle args = getArguments(); Bundle args = getArguments();
if (args != null && args.containsKey("inventoryId")) { 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() { private void loadInventoryData() {
viewModel.loadInventory().observe(getViewLifecycleOwner(), resource -> { viewModel.loadInventory().observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return; if (resource == null) return;
@@ -149,6 +182,9 @@ public class InventoryDetailFragment extends Fragment {
}); });
} }
/**
* Validates input and saves the inventory record.
*/
private void saveInventory() { private void saveInventory() {
if (!InputValidator.isSpinnerSelected(binding.spinnerInventoryStore, "Store")) return; if (!InputValidator.isSpinnerSelected(binding.spinnerInventoryStore, "Store")) return;
if (!InputValidator.isSpinnerSelected(binding.spinnerInventoryProduct, "Product")) 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() { private void confirmDelete() {
DialogUtils.showDeleteConfirmDialog(requireContext(), "Inventory Item", this::deleteInventory); DialogUtils.showDeleteConfirmDialog(requireContext(), "Inventory Item", this::deleteInventory);
} }
/**
* Calls the ViewModel to delete the inventory record.
*/
private void deleteInventory() { private void deleteInventory() {
setButtonsEnabled(false); setButtonsEnabled(false);
viewModel.deleteInventory().observe(getViewLifecycleOwner(), resource -> { viewModel.deleteInventory().observe(getViewLifecycleOwner(), resource -> {
@@ -197,10 +239,16 @@ public class InventoryDetailFragment extends Fragment {
}); });
} }
/**
* Navigates back to the previous screen.
*/
private void navigateBack() { private void navigateBack() {
NavHostFragment.findNavController(this).popBackStack(); NavHostFragment.findNavController(this).popBackStack();
} }
/**
* Enables or disables the action buttons.
*/
private void setButtonsEnabled(boolean enabled) { private void setButtonsEnabled(boolean enabled) {
UIUtils.setViewsEnabled(enabled, binding.btnSaveInventory, binding.btnDeleteInventory, binding.btnInventoryBack); UIUtils.setViewsEnabled(enabled, binding.btnSaveInventory, binding.btnDeleteInventory, binding.btnInventoryBack);
} }

View File

@@ -58,6 +58,9 @@ public class ProductDetailFragment extends Fragment {
@Inject @Named("baseUrl") String baseUrl; @Inject @Named("baseUrl") String baseUrl;
@Inject TokenManager tokenManager; @Inject TokenManager tokenManager;
/**
* Initializes the fragment, its ViewModel, and the ImagePickerHelper.
*/
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@@ -88,6 +91,9 @@ public class ProductDetailFragment extends Fragment {
}); });
} }
/**
* Inflates the layout for the fragment.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -95,6 +101,9 @@ public class ProductDetailFragment extends Fragment {
return binding.getRoot(); return binding.getRoot();
} }
/**
* Sets up UI components, observers, and handles arguments after the view is created.
*/
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
@@ -108,6 +117,9 @@ public class ProductDetailFragment extends Fragment {
binding.ivProductImage.setOnClickListener(v -> imagePickerHelper.showImagePickerDialog("Select Product Image", hasImage)); binding.ivProductImage.setOnClickListener(v -> imagePickerHelper.showImagePickerDialog("Select Product Image", hasImage));
} }
/**
* Sets up observers for the ViewModel and loads product categories.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getCategoryList().observe(getViewLifecycleOwner(), list -> updateCategorySpinner()); 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) { private void setLoading(boolean loading) {
if (binding != null && binding.progressBar != null) { if (binding != null && binding.progressBar != null) {
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE); binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
} }
} }
/**
* Populates the category spinner with available categories.
*/
private void updateCategorySpinner() { private void updateCategorySpinner() {
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerProductCategory, viewModel.getCategoryList().getValue(), SpinnerUtils.populateSpinner(requireContext(), binding.spinnerProductCategory, viewModel.getCategoryList().getValue(),
DropdownDTO::getLabel, "-- Select Category --", DropdownDTO::getLabel, "-- Select Category --",
preselectedCategoryId, DropdownDTO::getId); preselectedCategoryId, DropdownDTO::getId);
} }
/**
* Cleans up the binding reference.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
binding = null; binding = null;
} }
/**
* Uses fragment arguments to determine if we are editing an existing record.
*/
private void handleArguments() { private void handleArguments() {
Bundle a = getArguments(); Bundle a = getArguments();
if (a != null && a.containsKey("prodId")) { 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() { private void loadProductData() {
viewModel.loadProduct().observe(getViewLifecycleOwner(), resource -> { viewModel.loadProduct().observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return; if (resource == null) return;
@@ -175,6 +202,9 @@ public class ProductDetailFragment extends Fragment {
}); });
} }
/**
* Loads the product image from the server.
*/
private void loadProductImage() { private void loadProductImage() {
String imageUrl = baseUrl + String.format(Locale.US, ProductApi.PRODUCT_IMAGE_PATH, viewModel.getProdId()); String imageUrl = baseUrl + String.format(Locale.US, ProductApi.PRODUCT_IMAGE_PATH, viewModel.getProdId());
String token = tokenManager.getToken(); 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) { private void performPendingImageActions(String successMsg) {
if (isImageRemoved) { if (isImageRemoved) {
viewModel.deleteProductImage().observe(getViewLifecycleOwner(), resource -> { 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) { private void uploadProductImageAndNavigate(Uri uri, String successMsg) {
File file = FileUtils.getFileFromUri(requireContext(), uri); File file = FileUtils.getFileFromUri(requireContext(), uri);
if (file == null) { if (file == null) {
@@ -239,6 +275,9 @@ public class ProductDetailFragment extends Fragment {
}); });
} }
/**
* Validates input and saves the product record.
*/
private void saveProduct() { private void saveProduct() {
if (!InputValidator.isNotEmpty(binding.etProductName, "Product Name")) return; if (!InputValidator.isNotEmpty(binding.etProductName, "Product Name")) return;
if (!InputValidator.isSpinnerSelected(binding.spinnerProductCategory, "Category")) 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() { private void confirmDelete() {
DialogUtils.showDeleteConfirmDialog(requireContext(), "Product", () -> DialogUtils.showDeleteConfirmDialog(requireContext(), "Product", () ->
viewModel.deleteProduct().observe(getViewLifecycleOwner(), resource -> { viewModel.deleteProduct().observe(getViewLifecycleOwner(), resource -> {
@@ -280,6 +322,9 @@ public class ProductDetailFragment extends Fragment {
})); }));
} }
/**
* Navigates back to the previous screen.
*/
private void navigateBack() { private void navigateBack() {
NavHostFragment.findNavController(this).popBackStack(); NavHostFragment.findNavController(this).popBackStack();
} }

View File

@@ -35,12 +35,18 @@ public class ProductSupplierDetailFragment extends Fragment {
private long preselectedProductId = -1; private long preselectedProductId = -1;
private long preselectedSupplierId = -1; private long preselectedSupplierId = -1;
/**
* Initializes the view model.
*/
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(ProductSupplierDetailViewModel.class); viewModel = new ViewModelProvider(this).get(ProductSupplierDetailViewModel.class);
} }
/**
* Inflates the layout for this fragment.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -48,6 +54,9 @@ public class ProductSupplierDetailFragment extends Fragment {
return binding.getRoot(); return binding.getRoot();
} }
/**
* Initializes the UI components and observers after the view is created.
*/
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
@@ -60,23 +69,35 @@ public class ProductSupplierDetailFragment extends Fragment {
binding.btnDeletePS.setOnClickListener(v -> confirmDelete()); binding.btnDeletePS.setOnClickListener(v -> confirmDelete());
} }
/**
* Observes LiveData from the ViewModel to update the UI.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getProductList().observe(getViewLifecycleOwner(), list -> refreshProductSpinner()); viewModel.getProductList().observe(getViewLifecycleOwner(), list -> refreshProductSpinner());
viewModel.getSupplierList().observe(getViewLifecycleOwner(), list -> refreshSupplierSpinner()); viewModel.getSupplierList().observe(getViewLifecycleOwner(), list -> refreshSupplierSpinner());
} }
/**
* Toggles the visibility of the progress bar.
*/
private void setLoading(boolean loading) { private void setLoading(boolean loading) {
if (binding != null && binding.progressBar != null) { if (binding != null && binding.progressBar != null) {
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE); binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
} }
} }
/**
* Cleans up the binding when the view is destroyed.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
binding = null; binding = null;
} }
/**
* Loads lookup data for products and suppliers.
*/
private void loadSpinnersData() { private void loadSpinnersData() {
viewModel.loadProducts().observe(getViewLifecycleOwner(), resource -> { viewModel.loadProducts().observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return; 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() { private void refreshProductSpinner() {
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerPSProduct, viewModel.getProductList().getValue(), SpinnerUtils.populateSpinner(requireContext(), binding.spinnerPSProduct, viewModel.getProductList().getValue(),
ProductDTO::getProdName, "-- Select Product --", ProductDTO::getProdName, "-- Select Product --",
preselectedProductId, ProductDTO::getProdId); preselectedProductId, ProductDTO::getProdId);
} }
/**
* Refreshes the supplier spinner with data from the view model.
*/
private void refreshSupplierSpinner() { private void refreshSupplierSpinner() {
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerPSSupplier, viewModel.getSupplierList().getValue(), SpinnerUtils.populateSpinner(requireContext(), binding.spinnerPSSupplier, viewModel.getSupplierList().getValue(),
SupplierDTO::getSupCompany, "-- Select Supplier --", SupplierDTO::getSupCompany, "-- Select Supplier --",
preselectedSupplierId, SupplierDTO::getSupId); preselectedSupplierId, SupplierDTO::getSupId);
} }
/**
* Handles fragment arguments to determine mode (new vs edit).
*/
private void handleArguments() { private void handleArguments() {
Bundle a = getArguments(); Bundle a = getArguments();
if (a != null && a.containsKey("productId") && a.containsKey("supplierId")) { 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() { private void save() {
if (!InputValidator.isSpinnerSelected(binding.spinnerPSProduct, "Product")) return; if (!InputValidator.isSpinnerSelected(binding.spinnerPSProduct, "Product")) return;
if (!InputValidator.isSpinnerSelected(binding.spinnerPSSupplier, "Supplier")) 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() { private void confirmDelete() {
DialogUtils.showDeleteConfirmDialog(requireContext(), "Product Supplier Relationship", () -> DialogUtils.showDeleteConfirmDialog(requireContext(), "Product Supplier Relationship", () ->
viewModel.deleteProductSupplier().observe(getViewLifecycleOwner(), resource -> { viewModel.deleteProductSupplier().observe(getViewLifecycleOwner(), resource -> {
@@ -169,6 +205,9 @@ public class ProductSupplierDetailFragment extends Fragment {
})); }));
} }
/**
* Navigates back to the previous fragment.
*/
private void navigateBack() { private void navigateBack() {
NavHostFragment.findNavController(this).popBackStack(); NavHostFragment.findNavController(this).popBackStack();
} }

View File

@@ -27,6 +27,9 @@ public class PurchaseOrderDetailFragment extends Fragment {
private PurchaseOrderDetailViewModel viewModel; private PurchaseOrderDetailViewModel viewModel;
private long purchaseOrderId; private long purchaseOrderId;
/**
* Initializes the view model.
*/
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(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() { private void handleArguments() {
Bundle a = getArguments(); Bundle a = getArguments();
if (a != null && a.containsKey("purchaseOrderId")) { 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) { private void setLoading(boolean loading) {
if (binding != null && binding.progressBar != null) { if (binding != null && binding.progressBar != null) {
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE); binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
} }
} }
/**
* Loads the details of the specified purchase order.
*/
private void loadPurchaseOrderData() { private void loadPurchaseOrderData() {
viewModel.loadPurchaseOrder(purchaseOrderId).observe(getViewLifecycleOwner(), resource -> { viewModel.loadPurchaseOrder(purchaseOrderId).observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return; if (resource == null) return;
@@ -87,6 +99,9 @@ public class PurchaseOrderDetailFragment extends Fragment {
}); });
} }
/**
* Cleans up the binding when the view is destroyed.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();

View File

@@ -21,6 +21,9 @@ import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.util.*; import java.util.*;
/**
* Fragment for processing refunds for existing sales.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class RefundFragment extends Fragment { public class RefundFragment extends Fragment {
@@ -29,6 +32,9 @@ public class RefundFragment extends Fragment {
private final String[] PAYMENT_METHODS = {"Cash", "Card"}; private final String[] PAYMENT_METHODS = {"Cash", "Card"};
/**
* Initializes the fragment's UI and view model.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -51,25 +57,37 @@ public class RefundFragment extends Fragment {
return binding.getRoot(); return binding.getRoot();
} }
/**
* Sets up the payment method spinner.
*/
private void setupSpinner() { private void setupSpinner() {
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerRefundPayment, PAYMENT_METHODS); SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerRefundPayment, PAYMENT_METHODS);
} }
/**
* Observes LiveData from the ViewModel to update the UI.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getAvailableItems().observe(getViewLifecycleOwner(), items -> renderOriginalItems()); viewModel.getAvailableItems().observe(getViewLifecycleOwner(), items -> renderOriginalItems());
viewModel.getRefundCart().observe(getViewLifecycleOwner(), cart -> { viewModel.getRefundCart().observe(getViewLifecycleOwner(), cart -> {
renderRefundCart(); renderRefundCart();
updateRefundTotal(); updateRefundTotal();
renderOriginalItems(); // Re-render to reflect quantities in cart renderOriginalItems();
}); });
} }
/**
* Toggles the visibility of the progress bar.
*/
private void setLoading(boolean loading) { private void setLoading(boolean loading) {
if (binding != null && binding.progressBar != null) { if (binding != null && binding.progressBar != null) {
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE); 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() { private void loadAllSales() {
viewModel.loadAllSales().observe(getViewLifecycleOwner(), resource -> { viewModel.loadAllSales().observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return; 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() { private void loadSale() {
String idStr = binding.etRefundSaleId.getText().toString().trim(); String idStr = binding.etRefundSaleId.getText().toString().trim();
if (idStr.isEmpty()) { if (idStr.isEmpty()) {
@@ -145,6 +166,9 @@ public class RefundFragment extends Fragment {
binding.btnProcessRefund.setVisibility(View.VISIBLE); binding.btnProcessRefund.setVisibility(View.VISIBLE);
} }
/**
* Renders the items from the original sale that are available for refund.
*/
private void renderOriginalItems() { private void renderOriginalItems() {
binding.llOriginalItems.removeAllViews(); binding.llOriginalItems.removeAllViews();
List<RefundViewModel.RefundItem> available = viewModel.getAvailableItems().getValue(); 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() { private void renderRefundCart() {
binding.llRefundItems.removeAllViews(); binding.llRefundItems.removeAllViews();
List<RefundViewModel.RefundItem> cart = viewModel.getRefundCart().getValue(); 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) { private void addTableHeader(LinearLayout parent) {
if (getContext() == null) return; if (getContext() == null) return;
LinearLayout header = new LinearLayout(getContext()); LinearLayout header = new LinearLayout(getContext());
@@ -220,6 +250,9 @@ public class RefundFragment extends Fragment {
parent.addView(header); 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, private LinearLayout buildItemRow(String name, int qty, BigDecimal unitPrice,
boolean isAdd, Runnable action) { boolean isAdd, Runnable action) {
if (getContext() == null) return new LinearLayout(getContext()); if (getContext() == null) return new LinearLayout(getContext());
@@ -267,6 +300,9 @@ public class RefundFragment extends Fragment {
return row; return row;
} }
/**
* Shows a dialog to select the quantity of an item to refund.
*/
private void showQuantityDialog(RefundViewModel.RefundItem item, int available) { private void showQuantityDialog(RefundViewModel.RefundItem item, int available) {
EditText input = new EditText(getContext()); EditText input = new EditText(getContext());
input.setInputType(android.text.InputType.TYPE_CLASS_NUMBER); input.setInputType(android.text.InputType.TYPE_CLASS_NUMBER);
@@ -301,11 +337,17 @@ public class RefundFragment extends Fragment {
.show(); .show();
} }
/**
* Updates the total refund amount display.
*/
private void updateRefundTotal() { private void updateRefundTotal() {
BigDecimal total = viewModel.calculateRefundTotal(); BigDecimal total = viewModel.calculateRefundTotal();
binding.tvRefundTotal.setText("Refund Total: $" + total.setScale(2, RoundingMode.HALF_UP)); binding.tvRefundTotal.setText("Refund Total: $" + total.setScale(2, RoundingMode.HALF_UP));
} }
/**
* Validates and prepares the refund for processing.
*/
private void processRefund() { private void processRefund() {
if (viewModel.getCurrentSale() == null) { if (viewModel.getCurrentSale() == null) {
Toast.makeText(getContext(), "Load a sale first", Toast.LENGTH_SHORT).show(); Toast.makeText(getContext(), "Load a sale first", Toast.LENGTH_SHORT).show();
@@ -325,6 +367,9 @@ public class RefundFragment extends Fragment {
() -> submitRefund(payment)); () -> submitRefund(payment));
} }
/**
* Submits the refund request to the backend.
*/
private void submitRefund(String payment) { private void submitRefund(String payment) {
viewModel.submitRefund(payment).observe(getViewLifecycleOwner(), resource -> { viewModel.submitRefund(payment).observe(getViewLifecycleOwner(), resource -> {
if (resource != null) { if (resource != null) {
@@ -339,10 +384,16 @@ public class RefundFragment extends Fragment {
}); });
} }
/**
* Navigates back to the previous fragment.
*/
private void navigateBack() { private void navigateBack() {
NavHostFragment.findNavController(this).popBackStack(); NavHostFragment.findNavController(this).popBackStack();
} }
/**
* Cleans up the binding when the view is destroyed.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();

View File

@@ -26,6 +26,9 @@ import dagger.hilt.android.AndroidEntryPoint;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.*; import java.util.*;
/**
* Fragment for viewing or creating sale details.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class SaleDetailFragment extends Fragment { public class SaleDetailFragment extends Fragment {
@@ -36,6 +39,9 @@ public class SaleDetailFragment extends Fragment {
private final String[] PAYMENT_METHODS = { "Cash", "Card"}; private final String[] PAYMENT_METHODS = { "Cash", "Card"};
/**
* Initializes the fragment's UI and view model.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -65,14 +71,23 @@ public class SaleDetailFragment extends Fragment {
return binding.getRoot(); return binding.getRoot();
} }
/**
* Checks if the current user has the 'STAFF' role.
*/
private boolean isStaff() { private boolean isStaff() {
return "STAFF".equalsIgnoreCase(tokenManager.getRole()); return "STAFF".equalsIgnoreCase(tokenManager.getRole());
} }
/**
* Checks if the current user has the 'ADMIN' role.
*/
private boolean isAdmin() { private boolean isAdmin() {
return "ADMIN".equalsIgnoreCase(tokenManager.getRole()); return "ADMIN".equalsIgnoreCase(tokenManager.getRole());
} }
/**
* Observes LiveData from the ViewModel to update the UI.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getStoreList().observe(getViewLifecycleOwner(), list -> { viewModel.getStoreList().observe(getViewLifecycleOwner(), list -> {
Long primaryStoreId = tokenManager.getPrimaryStoreId(); 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() { private void handleArguments() {
Bundle a = getArguments(); Bundle a = getArguments();
if (a != null && a.containsKey("saleId")) { 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) { private void setLoading(boolean loading) {
if (binding != null && binding.progressBar != null) { if (binding != null && binding.progressBar != null) {
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE); binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
} }
} }
/**
* Loads lookup data for stores, customers, and products.
*/
private void loadData() { private void loadData() {
viewModel.loadStores().observe(getViewLifecycleOwner(), resource -> { viewModel.loadStores().observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return; if (resource == null) return;
@@ -183,6 +207,9 @@ public class SaleDetailFragment extends Fragment {
}); });
} }
/**
* Loads the details of an existing sale.
*/
private void loadSaleDetails() { private void loadSaleDetails() {
viewModel.loadSaleDetails().observe(getViewLifecycleOwner(), resource -> { viewModel.loadSaleDetails().observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return; if (resource == null) return;
@@ -238,6 +265,9 @@ public class SaleDetailFragment extends Fragment {
}); });
} }
/**
* Sets up the coupon application logic.
*/
private void setupCoupon() { private void setupCoupon() {
binding.btnApplyCoupon.setOnClickListener(v -> { binding.btnApplyCoupon.setOnClickListener(v -> {
String code = binding.etCouponCode.getText().toString().trim(); 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) { private void applyAppliedCouponUI(CouponDTO coupon) {
String info; String info;
if ("PERCENTAGE".equalsIgnoreCase(coupon.getDiscountType())) { if ("PERCENTAGE".equalsIgnoreCase(coupon.getDiscountType())) {
@@ -296,12 +329,18 @@ public class SaleDetailFragment extends Fragment {
binding.etCouponCode.setEnabled(false); binding.etCouponCode.setEnabled(false);
} }
/**
* Displays an error message related to coupon validation.
*/
private void showCouponError(String message) { private void showCouponError(String message) {
binding.tvCouponInfo.setText(message); binding.tvCouponInfo.setText(message);
binding.tvCouponInfo.setTextColor(0xFFE53935); binding.tvCouponInfo.setTextColor(0xFFE53935);
binding.tvCouponInfo.setVisibility(View.VISIBLE); binding.tvCouponInfo.setVisibility(View.VISIBLE);
} }
/**
* Sets up the logic for adding items to the sale cart.
*/
private void setupAddItem() { private void setupAddItem() {
binding.btnAddItem.setOnClickListener(v -> { binding.btnAddItem.setOnClickListener(v -> {
if (!InputValidator.isSpinnerSelected(binding.spinnerSaleProduct, "Product")) return; 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() { private void renderCartItems() {
binding.llSaleItems.removeAllViews(); binding.llSaleItems.removeAllViews();
List<SaleDTO.SaleItemDTO> items = viewModel.getCartItems().getValue(); 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) { private void addItemRow(String name, int qty, BigDecimal price, Long prodId) {
if (getContext() == null) return; if (getContext() == null) return;
LinearLayout row = new LinearLayout(getContext()); LinearLayout row = new LinearLayout(getContext());
@@ -394,6 +439,9 @@ public class SaleDetailFragment extends Fragment {
binding.llSaleItems.addView(row); binding.llSaleItems.addView(row);
} }
/**
* Updates the subtotal, discounts, and total amount display.
*/
private void updateTotal() { private void updateTotal() {
BigDecimal subtotal = viewModel.calculateSubtotal(); BigDecimal subtotal = viewModel.calculateSubtotal();
BigDecimal couponDiscount = viewModel.calculateCouponDiscount(); BigDecimal couponDiscount = viewModel.calculateCouponDiscount();
@@ -429,6 +477,9 @@ public class SaleDetailFragment extends Fragment {
} }
} }
/**
* Validates and saves the new sale.
*/
private void saveSale() { private void saveSale() {
if (!InputValidator.isSpinnerSelected(binding.spinnerSaleStore, "Store")) return; 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() { private void showRefundDialog() {
DialogUtils.showConfirmDialog(requireContext(), "Process Refund", DialogUtils.showConfirmDialog(requireContext(), "Process Refund",
"Are you sure you want to process a refund for this sale?", () -> { "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() { private void navigateBack() {
NavHostFragment.findNavController(this).popBackStack(); NavHostFragment.findNavController(this).popBackStack();
} }
/**
* Cleans up the binding when the view is destroyed.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();

View File

@@ -35,12 +35,18 @@ public class ServiceDetailFragment extends Fragment {
private FragmentServiceDetailBinding binding; private FragmentServiceDetailBinding binding;
private ServiceDetailViewModel viewModel; private ServiceDetailViewModel viewModel;
/**
* Initializes the fragment and its ViewModel.
*/
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(ServiceDetailViewModel.class); viewModel = new ViewModelProvider(this).get(ServiceDetailViewModel.class);
} }
/**
* Inflates the layout for the fragment.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -48,6 +54,9 @@ public class ServiceDetailFragment extends Fragment {
return binding.getRoot(); return binding.getRoot();
} }
/**
* Sets up UI components, observers, and handles arguments after the view is created.
*/
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
@@ -60,22 +69,34 @@ public class ServiceDetailFragment extends Fragment {
binding.btnDeleteService.setOnClickListener(v -> deleteService()); binding.btnDeleteService.setOnClickListener(v -> deleteService());
} }
/**
* Sets up observers for ViewModel to update the UI dynamically.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getViewState().observe(getViewLifecycleOwner(), this::applyViewState); viewModel.getViewState().observe(getViewLifecycleOwner(), this::applyViewState);
} }
/**
* Shows or hides the loading bar.
*/
private void setLoading(boolean loading) { private void setLoading(boolean loading) {
if (binding != null) { if (binding != null) {
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE); binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
} }
} }
/**
* Cleans up the binding reference.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
binding = null; binding = null;
} }
/**
* Validates input and saves the service record.
*/
private void saveService() { private void saveService() {
if (!InputValidator.isNotEmpty(binding.etServiceName, "Service Name")) return; if (!InputValidator.isNotEmpty(binding.etServiceName, "Service Name")) return;
if (!InputValidator.isNotEmpty(binding.etServiceDesc, "Description")) 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() { private void deleteService() {
DialogUtils.showDeleteConfirmDialog(requireContext(), "Service", () -> DialogUtils.showDeleteConfirmDialog(requireContext(), "Service", () ->
viewModel.deleteService().observe(getViewLifecycleOwner(), resource -> { viewModel.deleteService().observe(getViewLifecycleOwner(), resource -> {
@@ -126,10 +150,16 @@ public class ServiceDetailFragment extends Fragment {
})); }));
} }
/**
* Navigates back to the previous screen.
*/
private void navigateBack() { private void navigateBack() {
NavHostFragment.findNavController(this).popBackStack(); NavHostFragment.findNavController(this).popBackStack();
} }
/**
* Uses fragment arguments to determine if we are editing an existing record.
*/
private void handleArguments() { private void handleArguments() {
if (getArguments() != null && getArguments().containsKey("serviceId")) { if (getArguments() != null && getArguments().containsKey("serviceId")) {
viewModel.setServiceId(getArguments().getLong("serviceId")); viewModel.setServiceId(getArguments().getLong("serviceId"));
@@ -140,6 +170,9 @@ public class ServiceDetailFragment extends Fragment {
viewModel.setServiceId(-1); viewModel.setServiceId(-1);
} }
/**
* Loads the service details from the backend.
*/
private void loadServiceData() { private void loadServiceData() {
viewModel.loadService().observe(getViewLifecycleOwner(), resource -> { viewModel.loadService().observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return; 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) { private void applyViewState(ServiceDetailViewModel.ViewState state) {
binding.tvMode.setText(state.modeTitle); binding.tvMode.setText(state.modeTitle);
binding.tvServiceId.setText(DateTimeUtils.formatId(viewModel.getServiceId())); binding.tvServiceId.setText(DateTimeUtils.formatId(viewModel.getServiceId()));
@@ -169,6 +205,9 @@ public class ServiceDetailFragment extends Fragment {
updateIfDifferent(binding.etServicePrice, state.servicePrice); 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) { private void updateIfDifferent(EditText field, String value) {
String current = field.getText() != null ? field.getText().toString() : ""; String current = field.getText() != null ? field.getText().toString() : "";
String next = value != null ? value : ""; String next = value != null ? value : "";

View File

@@ -22,6 +22,9 @@ import java.util.List;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
/**
* Fragment for displaying and editing staff details.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class StaffDetailFragment extends Fragment { public class StaffDetailFragment extends Fragment {
@@ -32,6 +35,9 @@ public class StaffDetailFragment extends Fragment {
private long preselectedStoreId = -1; private long preselectedStoreId = -1;
/**
* Inflates the layout, initializes ViewModel, and sets up UI components.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -52,14 +58,23 @@ public class StaffDetailFragment extends Fragment {
return binding.getRoot(); return binding.getRoot();
} }
/**
* Sets up observers for the ViewModel to refresh the store spinner.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getStoreList().observe(getViewLifecycleOwner(), list -> refreshStoreSpinner()); viewModel.getStoreList().observe(getViewLifecycleOwner(), list -> refreshStoreSpinner());
} }
/**
* Initializes the status spinner with options.
*/
private void setupSpinners() { private void setupSpinners() {
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerStaffStatus, STATUSES); SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerStaffStatus, STATUSES);
} }
/**
* Loads the list of stores from the backend.
*/
private void loadStores() { private void loadStores() {
viewModel.loadStores().observe(getViewLifecycleOwner(), resource -> { viewModel.loadStores().observe(getViewLifecycleOwner(), resource -> {
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) { 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() { private void refreshStoreSpinner() {
List<DropdownDTO> list = viewModel.getStoreList().getValue(); List<DropdownDTO> list = viewModel.getStoreList().getValue();
if (list == null) return; if (list == null) return;
@@ -76,6 +94,9 @@ public class StaffDetailFragment extends Fragment {
preselectedStoreId, DropdownDTO::getId); preselectedStoreId, DropdownDTO::getId);
} }
/**
* Uses fragment arguments to determine if we are editing an existing record.
*/
private void handleArguments() { private void handleArguments() {
Bundle a = getArguments(); Bundle a = getArguments();
if (a != null && a.getBoolean("isEditing", false)) { 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) { private void loadEmployeeData(long id) {
viewModel.loadEmployee(id).observe(getViewLifecycleOwner(), resource -> { viewModel.loadEmployee(id).observe(getViewLifecycleOwner(), resource -> {
if (resource != null) { if (resource != null) {
@@ -117,12 +141,18 @@ public class StaffDetailFragment extends Fragment {
}); });
} }
/**
* Shows or hides the loading bar.
*/
private void setLoading(boolean loading) { private void setLoading(boolean loading) {
if (binding != null && binding.progressBar != null) { if (binding != null && binding.progressBar != null) {
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE); binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
} }
} }
/**
* Validates input and saves the staff record.
*/
private void save() { private void save() {
if (!InputValidator.isNotEmpty(binding.etStaffUsername, "Username")) return; 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() { private void confirmDelete() {
DialogUtils.showDeleteConfirmDialog(requireContext(), "Staff Account", () -> DialogUtils.showDeleteConfirmDialog(requireContext(), "Staff Account", () ->
viewModel.deleteEmployee().observe(getViewLifecycleOwner(), resource -> { viewModel.deleteEmployee().observe(getViewLifecycleOwner(), resource -> {
@@ -213,10 +246,16 @@ public class StaffDetailFragment extends Fragment {
})); }));
} }
/**
* Navigates back to the previous screen.
*/
private void navigateBack() { private void navigateBack() {
NavHostFragment.findNavController(this).popBackStack(); NavHostFragment.findNavController(this).popBackStack();
} }
/**
* Cleans up the binding reference.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();

View File

@@ -35,12 +35,18 @@ public class SupplierDetailFragment extends Fragment {
private FragmentSupplierDetailBinding binding; private FragmentSupplierDetailBinding binding;
private SupplierDetailViewModel viewModel; private SupplierDetailViewModel viewModel;
/**
* Initializes the view model.
*/
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(SupplierDetailViewModel.class); viewModel = new ViewModelProvider(this).get(SupplierDetailViewModel.class);
} }
/**
* Inflates the layout for this fragment.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -48,6 +54,9 @@ public class SupplierDetailFragment extends Fragment {
return binding.getRoot(); return binding.getRoot();
} }
/**
* Initializes the UI components and observers after the view is created.
*/
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
@@ -61,22 +70,34 @@ public class SupplierDetailFragment extends Fragment {
binding.btnDeleteSupplier.setOnClickListener(v -> deleteSupplier()); binding.btnDeleteSupplier.setOnClickListener(v -> deleteSupplier());
} }
/**
* Observes LiveData from the ViewModel to update the UI.
*/
private void observeViewModel() { private void observeViewModel() {
viewModel.getViewState().observe(getViewLifecycleOwner(), this::applyViewState); viewModel.getViewState().observe(getViewLifecycleOwner(), this::applyViewState);
} }
/**
* Toggles the visibility of the progress bar.
*/
private void setLoading(boolean loading) { private void setLoading(boolean loading) {
if (binding != null) { if (binding != null) {
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE); binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
} }
} }
/**
* Cleans up the binding when the view is destroyed.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
binding = null; binding = null;
} }
/**
* Validates and saves the supplier information.
*/
private void saveSupplier() { private void saveSupplier() {
if (!InputValidator.isNotEmpty(binding.etSupCompany, "Company Name")) return; if (!InputValidator.isNotEmpty(binding.etSupCompany, "Company Name")) return;
if (!InputValidator.isNotEmpty(binding.etSupContactFirstName, "First 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() { private void deleteSupplier() {
DialogUtils.showDeleteConfirmDialog(requireContext(), "Supplier", () -> DialogUtils.showDeleteConfirmDialog(requireContext(), "Supplier", () ->
viewModel.deleteSupplier().observe(getViewLifecycleOwner(), resource -> { viewModel.deleteSupplier().observe(getViewLifecycleOwner(), resource -> {
@@ -130,10 +154,16 @@ public class SupplierDetailFragment extends Fragment {
})); }));
} }
/**
* Navigates back to the previous fragment.
*/
private void navigateBack() { private void navigateBack() {
NavHostFragment.findNavController(this).popBackStack(); NavHostFragment.findNavController(this).popBackStack();
} }
/**
* Handles fragment arguments to determine mode (new vs edit).
*/
private void handleArguments() { private void handleArguments() {
if (getArguments() != null && getArguments().containsKey("supId")) { if (getArguments() != null && getArguments().containsKey("supId")) {
viewModel.setSupId(getArguments().getLong("supId")); viewModel.setSupId(getArguments().getLong("supId"));
@@ -144,6 +174,9 @@ public class SupplierDetailFragment extends Fragment {
viewModel.setSupId(-1); viewModel.setSupId(-1);
} }
/**
* Loads the details of the specified supplier.
*/
private void loadSupplierData() { private void loadSupplierData() {
viewModel.loadSupplier().observe(getViewLifecycleOwner(), resource -> { viewModel.loadSupplier().observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return; 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) { private void applyViewState(SupplierDetailViewModel.ViewState state) {
binding.tvMode.setText(state.modeTitle); binding.tvMode.setText(state.modeTitle);
binding.tvSupId.setText(DateTimeUtils.formatId(viewModel.getSupId())); binding.tvSupId.setText(DateTimeUtils.formatId(viewModel.getSupId()));
@@ -175,6 +211,9 @@ public class SupplierDetailFragment extends Fragment {
updateIfDifferent(binding.etSupPhone, state.supPhone); 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) { private void updateIfDifferent(EditText field, String value) {
String current = field.getText() != null ? field.getText().toString() : ""; String current = field.getText() != null ? field.getText().toString() : "";
String next = value != null ? value : ""; String next = value != null ? value : "";

View File

@@ -36,6 +36,9 @@ import okhttp3.MediaType;
import okhttp3.MultipartBody; import okhttp3.MultipartBody;
import okhttp3.RequestBody; import okhttp3.RequestBody;
/**
* Fragment for displaying and managing a pet's profile.
*/
@AndroidEntryPoint @AndroidEntryPoint
public class PetProfileFragment extends Fragment { public class PetProfileFragment extends Fragment {
@@ -49,6 +52,9 @@ public class PetProfileFragment extends Fragment {
private PetProfileViewModel viewModel; private PetProfileViewModel viewModel;
private ImagePickerHelper imagePickerHelper; private ImagePickerHelper imagePickerHelper;
/**
* Initializes the ViewModel and image picker helper.
*/
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@@ -67,6 +73,9 @@ public class PetProfileFragment extends Fragment {
}); });
} }
/**
* Inflates the layout, handles arguments, and sets up click listeners.
*/
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -95,18 +104,27 @@ public class PetProfileFragment extends Fragment {
return binding.getRoot(); return binding.getRoot();
} }
/**
* Shows or hides the loading progress bar.
*/
private void setLoading(boolean loading) { private void setLoading(boolean loading) {
if (binding != null && binding.progressBar != null) { if (binding != null && binding.progressBar != null) {
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE); binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
} }
} }
/**
* Cleans up the binding reference.
*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
binding = null; binding = null;
} }
/**
* Loads pet data from the ViewModel and updates the UI.
*/
private void loadPetData() { private void loadPetData() {
viewModel.getPetById(petId).observe(getViewLifecycleOwner(), resource -> { viewModel.getPetById(petId).observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return; 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) { private void loadPetImage(int petId) {
String imageUrl = baseUrl + String.format(Locale.US, PetApi.PET_IMAGE_PATH, petId); String imageUrl = baseUrl + String.format(Locale.US, PetApi.PET_IMAGE_PATH, petId);
String token = tokenManager.getToken(); 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) { private void uploadPetImage(Uri uri) {
try { try {
File file = FileUtils.getFileFromUri(requireContext(), uri); File file = FileUtils.getFileFromUri(requireContext(), uri);
@@ -193,6 +217,9 @@ public class PetProfileFragment extends Fragment {
} }
} }
/**
* Deletes the pet's current image.
*/
private void deletePetImage() { private void deletePetImage() {
viewModel.deletePetImage(petId).observe(getViewLifecycleOwner(), resource -> { viewModel.deletePetImage(petId).observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return; if (resource == null) return;

Some files were not shown because too many files have changed in this diff Show More