added comments to android

This commit is contained in:
Alex
2026-04-19 20:18:28 -06:00
parent 1163961b90
commit 8aeccb0cab
160 changed files with 3098 additions and 81 deletions

View File

@@ -32,6 +32,9 @@ public class ForgotPasswordActivity extends AppCompatActivity {
private ActivityForgotPasswordBinding binding;
/**
* Set the content view for forget password page
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
EdgeToEdge.enable(this);
@@ -54,6 +57,10 @@ public class ForgotPasswordActivity extends AppCompatActivity {
binding.btnBackToLogin.setOnClickListener(v -> finish());
}
/**
* A function to send a reset link to the given email address.
* Calls the forgotPassword endpoint. To send the reset link
* */
private void sendResetLink(String email) {
binding.btnSubmit.setEnabled(false);

View File

@@ -78,7 +78,7 @@ public class HomeActivity extends AppCompatActivity {
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent); // Set the new intent so fragments can access updated extras
setIntent(intent);
handleIntent(intent);
}
@@ -103,7 +103,7 @@ public class HomeActivity extends AppCompatActivity {
}
/**
* Requests POST_NOTIFICATIONS permission from the user if running on Android 13 and above.
* Requests for notification permission from the user if running on Android 13 and above.
*/
private void requestNotificationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {

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) {
viewModel.login(username, password).observe(this, resource -> {
@@ -112,6 +112,7 @@ public class MainActivity extends AppCompatActivity {
case SUCCESS:
if (resource.data != null) {
String role = resource.data.getRole();
//Check if role is staff/admin or customer
if ("CUSTOMER".equalsIgnoreCase(role)) {
UIUtils.setViewsEnabled(true, binding.btnLogin);
binding.tvLoginStatus.setText("Customers are not allowed to log in");
@@ -132,7 +133,7 @@ public class MainActivity extends AppCompatActivity {
}
/**
* Retrieves the logged-in user's profile information to save their ID before navigating to the home screen.
* Retrieves the user's profile information to save their ID before navigating to the home screen.
*/
private void fetchUserIdAndNavigate() {
viewModel.getMe().observe(this, resource -> {

View File

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

View File

@@ -23,6 +23,9 @@ public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.Adopti
void onSelectionChanged(int count);
}
/**
* Constructor for AdoptionAdapter.
*/
public AdoptionAdapter(List<AdoptionDTO> adoptionList, OnAdoptionClickListener listener) {
this.adoptionList = adoptionList;
this.listener = listener;
@@ -39,11 +42,17 @@ public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.Adopti
});
}
/**
* Returns a list of IDs for the currently selected adoption items.
*/
@Override
public List<String> getSelectedKeys() {
return selectionHelper.getSelectedKeys();
}
/**
* Resets the selection state, deselecting all items.
*/
@Override
public void clearSelection() {
selectionHelper.clearSelection();
@@ -58,6 +67,9 @@ public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.Adopti
}
}
/**
* Inflates the layout for an adoption item and creates the ViewHolder.
*/
@NonNull
@Override
public AdoptionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -65,6 +77,9 @@ public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.Adopti
return new AdoptionViewHolder(binding);
}
/**
* Binds adoption data to the UI components and handles click/long-click logic.
*/
@Override
public void onBindViewHolder(@NonNull AdoptionViewHolder holder, int position) {
AdoptionDTO a = adoptionList.get(position);
@@ -123,6 +138,9 @@ public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.Adopti
});
}
/**
* Returns the total number of adoption items in the list.
*/
@Override
public int getItemCount() { return adoptionList.size(); }
}

View File

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

View File

@@ -12,6 +12,7 @@ import com.example.petstoremobile.R;
import java.util.List;
// A class that overrides the arrayAdapter so the text color changes based on theme
// Used to make spinners have black text on white background no matter the theme
public class BlackTextArrayAdapter<T> extends ArrayAdapter<T> {
public BlackTextArrayAdapter(@NonNull Context context, int resource, @NonNull T[] objects) {
super(context, resource, objects);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -37,40 +37,64 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
private String baseUrl;
private OnAttachmentClickListener attachmentClickListener;
/**
* Constructor for MessageAdapter.
*/
public MessageAdapter(List<Message> messages, Long currentUserId) {
this.messages = messages;
this.currentUserId = currentUserId;
setHasStableIds(true);
}
/**
* Returns an ID for each message.
*/
@Override
public long getItemId(int position) {
Message m = messages.get(position);
return m.getId() != null ? m.getId() : position;
}
/**
* Updates the current user's ID and refreshes the list.
*/
public void setCurrentUserId(Long id) {
this.currentUserId = id;
notifyDataSetChanged();
}
/**
* Updates the staff ID to identify staff messages in the UI.
*/
public void setStaffId(Long id) {
this.staffId = id;
notifyDataSetChanged();
}
/**
* Sets the authentication token.
*/
public void setToken(String token) {
this.token = token;
}
/**
* Sets the base API URL.
*/
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
/**
* Sets a listener for clicks on message attachments.
*/
public void setOnAttachmentClickListener(OnAttachmentClickListener listener) {
this.attachmentClickListener = listener;
}
/**
* Determines if a message is 'sent' or 'received' based on the sender's ID.
*/
@Override
public int getItemViewType(int position) {
Message m = messages.get(position);
@@ -80,6 +104,9 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
return TYPE_RECEIVED;
}
/**
* Inflates the chat layout for a message.
*/
@NonNull @Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inf = LayoutInflater.from(parent.getContext());
@@ -92,6 +119,9 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
}
}
/**
* Binds message data to the appropriate ViewHolder.
*/
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
Message m = messages.get(position);
@@ -99,14 +129,26 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
if (holder instanceof ReceivedHolder) ((ReceivedHolder) holder).bind(m, token, baseUrl, attachmentClickListener, staffId);
}
/**
* Returns the total number of messages.
*/
@Override public int getItemCount() { return messages.size(); }
/**
* ViewHolder for messages sent by the user.
*/
static class SentHolder extends RecyclerView.ViewHolder {
final ItemMessageSentBinding binding;
/**
* Initializes the SentHolder with view binding.
*/
SentHolder(ItemMessageSentBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
/**
* Binds sent message data to the bubble UI.
*/
void bind(Message m, String token, String baseUrl, OnAttachmentClickListener listener) {
binding.tvSenderName.setText("You");
binding.tvTimestamp.setText(formatTimestamp(m.getTimestamp()));
@@ -128,12 +170,21 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
}
}
/**
* ViewHolder for messages received from others.
*/
static class ReceivedHolder extends RecyclerView.ViewHolder {
final ItemMessageReceivedBinding binding;
/**
* Initializes the ReceivedHolder with view binding.
*/
ReceivedHolder(ItemMessageReceivedBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
/**
* Binds received message data to the bubble UI.
*/
void bind(Message m, String token, String baseUrl, OnAttachmentClickListener listener, Long staffId) {
binding.tvSenderName.setText(resolveSenderName(m, staffId));
binding.tvTimestamp.setText(formatTimestamp(m.getTimestamp()));
@@ -155,6 +206,9 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
}
}
/**
* Resolves the display name of the sender.
*/
private static String resolveSenderName(Message m, Long staffId) {
if ("BOT".equalsIgnoreCase(m.getSenderRole())) {
return (m.getSenderDisplayName() != null && !m.getSenderDisplayName().isEmpty())
@@ -166,6 +220,9 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
return "Customer";
}
/**
* Formats the timestamp string into readable format.
*/
private static String formatTimestamp(String timestamp) {
if (timestamp == null || timestamp.isEmpty()) return "";
try {
@@ -178,8 +235,11 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
}
}
/**
* Logic for displaying an attachment in the chat.
*/
private static void displayAttachment(Message m, ImageView iv, TextView tvName, String token, String baseUrl) {
// Check if there's an attachment by looking at name or mime type
// Check if there's an attachment
if (m.getAttachmentName() != null || m.getAttachmentMimeType() != null) {
// Construct the download URL using the message ID
String url;
@@ -187,7 +247,7 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
String cleanBase = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl;
url = cleanBase + "/api/v1/chat/messages/" + m.getId() + "/attachment";
} else {
url = m.getAttachmentUrl(); // Fallback
url = m.getAttachmentUrl();
}
if (url == null) {
@@ -208,7 +268,7 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
.build());
}
// Use a signature to prevent Glide from showing stale cached images for the same URL/ID
// Use a signature to prevent Glide from showing cached images instead of loading new ones
String signatureKey = (m.getTimestamp() != null ? m.getTimestamp() : "") + m.getId();
Glide.with(iv.getContext()).clear(iv);

View File

@@ -31,7 +31,9 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
void onSelectionChanged(int selectedCount);
}
//Constructor
/**
* Constructor for PetAdapter.
*/
public PetAdapter(List<PetDTO> petList, OnPetClickListener petClickListener) {
this.petList = petList;
this.petClickListener = petClickListener;
@@ -48,35 +50,54 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
});
}
/**
* Sets the base URL for fetching pet images from the server.
*/
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
/**
* Sets the authentication token
*/
public void setToken(String token) {
this.token = token;
}
/**
* Returns a list of IDs for the currently selected pet items.
*/
@Override
public List<String> getSelectedKeys() {
return selectionHelper.getSelectedKeys();
}
/**
* Resets the selection state, deselecting all items.
*/
@Override
public void clearSelection() {
selectionHelper.clearSelection();
}
// Get the controls of each row in recycler view
/**
* ViewHolder class that holds references to the UI components for a pet item.
*/
public static class PetViewHolder extends RecyclerView.ViewHolder {
private final ItemPetBinding binding;
/**
* Initializes the ViewHolder with view binding.
*/
public PetViewHolder(@NonNull ItemPetBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
// Create a new row view
/**
* Inflates the layout for a pet item and creates the ViewHolder.
*/
@NonNull
@Override
public PetViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -84,7 +105,9 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
return new PetViewHolder(binding);
}
//populate the row with pet data
/**
* Binds pet data to the UI components.
*/
@Override
public void onBindViewHolder(@NonNull PetViewHolder holder, int position) {
PetDTO pet = petList.get(position);
@@ -103,7 +126,7 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
binding.tvPetStatus.setText(pet.getPetStatus());
//Set the status color depending on availability. If available, green, If Pending, yellow, otherwise red
//Set the status color depending on availability
if (pet.getPetStatus() != null) {
switch (pet.getPetStatus()) {
case "Available":
@@ -157,6 +180,9 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
});
}
/**
* Returns the total number of pet items in the list.
*/
@Override
public int getItemCount() {
return petList.size();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,6 +8,7 @@ import javax.inject.Singleton;
import dagger.hilt.android.qualifiers.ApplicationContext;
//Used to save and retrieve login data
@Singleton
public class TokenManager {
private static final String TOKEN_KEY = "token";

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,8 @@
package com.example.petstoremobile.dtos;
//Used to get messages of any errors from the backend
/**
* Used to get messages of any errors from the backend.
*/
public class ErrorResponse {
private String message;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -65,6 +65,9 @@ import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
/**
* Fragment for handling customer support chat.
*/
@AndroidEntryPoint
public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickListener, StompChatManager.MessageListener,
StompChatManager.ConversationListener, StompChatManager.ConnectionListener {
@@ -90,6 +93,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
private StompChatManager stompChatManager;
private ActivityResultLauncher<Intent> attachmentLauncher;
/**
* Initializes the view model and attachment launcher.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -105,6 +111,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
);
}
/**
* Inflates the layout and sets up UI event listeners.
*/
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
@@ -137,6 +146,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
return binding.getRoot();
}
/**
* Sets up the logic to open and close the chat drawer.
*/
private void setupDrawerToggles() {
binding.headerActiveChats.setOnClickListener(v -> {
if (binding.rvActiveChats.getVisibility() == View.VISIBLE) {
@@ -159,6 +171,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
});
}
/**
* Configures the adapters and layout managers for chat lists and message history.
*/
private void setupRecyclerViews() {
activeChatAdapter = new ChatAdapter(activeChatList, this);
binding.rvActiveChats.setLayoutManager(new LinearLayoutManager(getContext()));
@@ -187,6 +202,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
setConversationActive(false, null);
}
/**
* Displays a full-screen image preview for message attachments.
*/
private void showFullScreenImage(Message message) {
if (baseUrl == null || message.getId() == null) return;
@@ -208,6 +226,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
dialog.show();
}
/**
* Initiates the download process for a message attachment.
*/
private void downloadFile(Message message) {
if (message.getId() == null) return;
@@ -227,6 +248,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
});
}
/**
* Saves the downloaded file body to the device's downloads folder.
*/
private void saveFileToDownloads(ResponseBody body, String fileName, String mimeType) {
android.os.Handler mainHandler = new android.os.Handler(android.os.Looper.getMainLooper());
new Thread(() -> {
@@ -270,6 +294,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
}).start();
}
/**
* Observes LiveData from the ViewModel to update chat lists and messages.
*/
private void observeViewModel() {
viewModel.getActiveChats().observe(getViewLifecycleOwner(), list -> {
activeChatList.clear();
@@ -300,12 +327,18 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
viewModel.getIsLoading().observe(getViewLifecycleOwner(), this::setLoading);
}
/**
* Toggles the visibility of the progress bar.
*/
private void setLoading(boolean loading) {
if (binding != null && binding.progressBar != null) {
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
}
}
/**
* Updates the chat header and input state if the active conversation changes.
*/
private void updateTitleAndStateIfActive(List<Chat> list) {
if (activeConversationId != null) {
for (Chat chat : list) {
@@ -318,6 +351,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
}
}
/**
* Loads initial chat data and establishes WebSocket connection.
*/
private void loadInitialData() {
String token = tokenManager.getToken();
Long currentUserId = tokenManager.getUserId();
@@ -353,6 +389,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
}
}
/**
* Handles clicks on a chat from the drawer to switch the active conversation.
*/
@Override
public void onChatClick(Chat chat) {
activeConversationId = Long.parseLong(chat.getChatId());
@@ -368,6 +407,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
viewModel.loadMessageHistory(activeConversationId);
}
/**
* Closes the active chat conversation.
*/
private void closeChat() {
if (activeConversationId == null) return;
@@ -392,6 +434,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
});
}
/**
* Sends a text message to the active conversation.
*/
private void sendMessage() {
if (activeConversationId == null) return;
String text = binding.etMessage.getText().toString().trim();
@@ -408,12 +453,18 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
});
}
/**
* Opens a file picker to select an attachment.
*/
private void selectAttachment() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
attachmentLauncher.launch(intent);
}
/**
* Displays a preview of the selected attachment.
*/
private void showAttachmentPreview(Uri uri) {
pendingAttachmentUri = uri;
binding.layoutAttachmentPreview.setVisibility(View.VISIBLE);
@@ -427,11 +478,17 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
}
}
/**
* Removes the currently selected attachment from the preview.
*/
private void removeAttachment() {
pendingAttachmentUri = null;
binding.layoutAttachmentPreview.setVisibility(View.GONE);
}
/**
* Sends a message with a file attachment.
*/
private void sendWithAttachment(Uri uri) {
if (activeConversationId == null) return;
@@ -468,6 +525,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
});
}
/**
* Callback for when a new message is received through the WebSocket.
*/
@Override
public void onMessageReceived(MessageDTO dto) {
requireActivity().runOnUiThread(() -> {
@@ -478,6 +538,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
});
}
/**
* Callback for when a conversation's status or last message is updated.
*/
@Override
public void onConversationUpdated(ConversationDTO dto) {
requireActivity().runOnUiThread(() -> {
@@ -489,6 +552,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
});
}
/**
* Callback for when the WebSocket connection is successfully opened.
*/
@Override
public void onSocketOpened() {
if (!isAdded()) return;
@@ -498,12 +564,18 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
});
}
/**
* Callback for when the WebSocket connection is closed.
*/
@Override
public void onSocketClosed() {
if (!isAdded()) return;
requireActivity().runOnUiThread(viewModel::loadConversations);
}
/**
* Callback for when a WebSocket error occurs.
*/
@Override
public void onSocketError() {
if (!isAdded()) return;
@@ -513,6 +585,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
});
}
/**
* Scrolls the message list to the most recent message.
*/
private void scrollToBottom() {
if (!messageList.isEmpty()) {
binding.rvMessages.post(() ->
@@ -520,6 +595,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
}
}
/**
* Updates the UI state based on whether a conversation is active and its status.
*/
private void setConversationActive(boolean active, String status) {
boolean isClosed = "CLOSED".equalsIgnoreCase(status);
UIUtils.setViewsEnabled(active && !isClosed, binding.btnSend, binding.etMessage, binding.btnAttach);
@@ -541,6 +619,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
}
}
/**
* Cleans up resources and disconnects the WebSocket when the view is destroyed.
*/
@Override
public void onDestroyView() {
super.onDestroyView();

View File

@@ -23,7 +23,9 @@ import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
//The Fragment for the displaying the list of entities to be viewed
/**
* Fragment that serves as a container for various list-based screens, providing a navigation drawer.
*/
@AndroidEntryPoint
public class ListFragment extends Fragment {
@@ -97,6 +99,9 @@ public class ListFragment extends Fragment {
return binding.getRoot();
}
/**
* Cleans up the binding when the view is destroyed.
*/
@Override
public void onDestroyView() {
super.onDestroyView();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -27,6 +27,9 @@ public class PurchaseOrderDetailFragment extends Fragment {
private PurchaseOrderDetailViewModel viewModel;
private long purchaseOrderId;
/**
* Initializes the view model.
*/
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -57,6 +60,9 @@ public class PurchaseOrderDetailFragment extends Fragment {
});
}
/**
* Handles fragment arguments to retrieve the purchase order ID.
*/
private void handleArguments() {
Bundle a = getArguments();
if (a != null && a.containsKey("purchaseOrderId")) {
@@ -65,12 +71,18 @@ public class PurchaseOrderDetailFragment extends Fragment {
}
}
/**
* Toggles the visibility of the progress bar.
*/
private void setLoading(boolean loading) {
if (binding != null && binding.progressBar != null) {
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
}
}
/**
* Loads the details of the specified purchase order.
*/
private void loadPurchaseOrderData() {
viewModel.loadPurchaseOrder(purchaseOrderId).observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return;
@@ -87,6 +99,9 @@ public class PurchaseOrderDetailFragment extends Fragment {
});
}
/**
* Cleans up the binding when the view is destroyed.
*/
@Override
public void onDestroyView() {
super.onDestroyView();

View File

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

View File

@@ -26,6 +26,9 @@ import dagger.hilt.android.AndroidEntryPoint;
import java.math.BigDecimal;
import java.util.*;
/**
* Fragment for viewing or creating sale details.
*/
@AndroidEntryPoint
public class SaleDetailFragment extends Fragment {
@@ -36,6 +39,9 @@ public class SaleDetailFragment extends Fragment {
private final String[] PAYMENT_METHODS = { "Cash", "Card"};
/**
* Initializes the fragment's UI and view model.
*/
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@@ -65,14 +71,23 @@ public class SaleDetailFragment extends Fragment {
return binding.getRoot();
}
/**
* Checks if the current user has the 'STAFF' role.
*/
private boolean isStaff() {
return "STAFF".equalsIgnoreCase(tokenManager.getRole());
}
/**
* Checks if the current user has the 'ADMIN' role.
*/
private boolean isAdmin() {
return "ADMIN".equalsIgnoreCase(tokenManager.getRole());
}
/**
* Observes LiveData from the ViewModel to update the UI.
*/
private void observeViewModel() {
viewModel.getStoreList().observe(getViewLifecycleOwner(), list -> {
Long primaryStoreId = tokenManager.getPrimaryStoreId();
@@ -115,6 +130,9 @@ public class SaleDetailFragment extends Fragment {
});
}
/**
* Handles fragment arguments to determine mode (new sale vs view existing).
*/
private void handleArguments() {
Bundle a = getArguments();
if (a != null && a.containsKey("saleId")) {
@@ -159,12 +177,18 @@ public class SaleDetailFragment extends Fragment {
}
}
/**
* Toggles the visibility of the progress bar.
*/
private void setLoading(boolean loading) {
if (binding != null && binding.progressBar != null) {
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
}
}
/**
* Loads lookup data for stores, customers, and products.
*/
private void loadData() {
viewModel.loadStores().observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return;
@@ -183,6 +207,9 @@ public class SaleDetailFragment extends Fragment {
});
}
/**
* Loads the details of an existing sale.
*/
private void loadSaleDetails() {
viewModel.loadSaleDetails().observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return;
@@ -241,6 +268,9 @@ public class SaleDetailFragment extends Fragment {
});
}
/**
* Sets up the coupon application logic.
*/
private void setupCoupon() {
binding.btnApplyCoupon.setOnClickListener(v -> {
String code = binding.etCouponCode.getText().toString().trim();
@@ -284,6 +314,9 @@ public class SaleDetailFragment extends Fragment {
});
}
/**
* Updates the UI to reflect an applied coupon.
*/
private void applyAppliedCouponUI(CouponDTO coupon) {
String info;
if ("PERCENTAGE".equalsIgnoreCase(coupon.getDiscountType())) {
@@ -299,12 +332,18 @@ public class SaleDetailFragment extends Fragment {
binding.etCouponCode.setEnabled(false);
}
/**
* Displays an error message related to coupon validation.
*/
private void showCouponError(String message) {
binding.tvCouponInfo.setText(message);
binding.tvCouponInfo.setTextColor(0xFFE53935);
binding.tvCouponInfo.setVisibility(View.VISIBLE);
}
/**
* Sets up the logic for adding items to the sale cart.
*/
private void setupAddItem() {
binding.btnAddItem.setOnClickListener(v -> {
if (!InputValidator.isSpinnerSelected(binding.spinnerSaleProduct, "Product")) return;
@@ -338,6 +377,9 @@ public class SaleDetailFragment extends Fragment {
});
}
/**
* Renders the list of items currently in the cart.
*/
private void renderCartItems() {
binding.llSaleItems.removeAllViews();
List<SaleDTO.SaleItemDTO> items = viewModel.getCartItems().getValue();
@@ -358,6 +400,9 @@ public class SaleDetailFragment extends Fragment {
}
}
/**
* Adds a row representing a sale item to the layout.
*/
private void addItemRow(String name, int qty, BigDecimal price, Long prodId) {
if (getContext() == null) return;
LinearLayout row = new LinearLayout(getContext());
@@ -397,6 +442,9 @@ public class SaleDetailFragment extends Fragment {
binding.llSaleItems.addView(row);
}
/**
* Updates the subtotal, discounts, and total amount display.
*/
private void updateTotal() {
BigDecimal subtotal = viewModel.calculateSubtotal();
BigDecimal couponDiscount = viewModel.calculateCouponDiscount();
@@ -432,6 +480,9 @@ public class SaleDetailFragment extends Fragment {
}
}
/**
* Validates and saves the new sale.
*/
private void saveSale() {
if (!InputValidator.isSpinnerSelected(binding.spinnerSaleStore, "Store")) return;
@@ -468,6 +519,9 @@ public class SaleDetailFragment extends Fragment {
});
}
/**
* Shows a confirmation dialog before proceeding to the refund screen.
*/
private void showRefundDialog() {
DialogUtils.showConfirmDialog(requireContext(), "Process Refund",
"Are you sure you want to process a refund for this sale?", () -> {
@@ -477,10 +531,16 @@ public class SaleDetailFragment extends Fragment {
});
}
/**
* Navigates back to the previous fragment.
*/
private void navigateBack() {
NavHostFragment.findNavController(this).popBackStack();
}
/**
* Cleans up the binding when the view is destroyed.
*/
@Override
public void onDestroyView() {
super.onDestroyView();

View File

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

View File

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

View File

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

View File

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

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