From 08cc87da534d31638dec68b11ef865085c83afb4 Mon Sep 17 00:00:00 2001 From: Nikitha Date: Thu, 12 Mar 2026 20:52:55 -0600 Subject: [PATCH] inventory activities --- .../adapters/InventoryAdapter.java | 79 +++++++++ .../listfragments/InventoryFragment.java | 148 +++++++++++++++++ .../InventoryDetailFragment.java | 139 ++++++++++++++++ .../main/res/layout/fragment_inventory.xml | 53 ++++++ .../res/layout/fragment_inventory_detail.xml | 153 ++++++++++++++++++ 5 files changed, 572 insertions(+) create mode 100644 app/src/main/java/com/example/petstoremobile/adapters/InventoryAdapter.java create mode 100644 app/src/main/java/com/example/petstoremobile/fragments/listfragments/InventoryFragment.java create mode 100644 app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/InventoryDetailFragment.java create mode 100644 app/src/main/res/layout/fragment_inventory.xml create mode 100644 app/src/main/res/layout/fragment_inventory_detail.xml diff --git a/app/src/main/java/com/example/petstoremobile/adapters/InventoryAdapter.java b/app/src/main/java/com/example/petstoremobile/adapters/InventoryAdapter.java new file mode 100644 index 00000000..7ae36dc5 --- /dev/null +++ b/app/src/main/java/com/example/petstoremobile/adapters/InventoryAdapter.java @@ -0,0 +1,79 @@ +package com.example.petstoremobile.adapters; + + +import android.graphics.Color; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import com.example.petstoremobile.R; +import com.example.petstoremobile.models.Inventory; +import java.util.List; + +public class InventoryAdapter extends RecyclerView.Adapter { + + private List inventoryList; + private OnInventoryClickListener inventoryClickListener; + + // Interface for inventory click on recycler view + public interface OnInventoryClickListener { + void onInventoryClick(int position); + } + + // Constructor + public InventoryAdapter(List inventoryList, OnInventoryClickListener inventoryClickListener) { + this.inventoryList = inventoryList; + this.inventoryClickListener = inventoryClickListener; + } + + // Get the controls of each row in recycler view + public static class InventoryViewHolder extends RecyclerView.ViewHolder { + TextView tvItemName, tvCategory, tvQuantity, tvUnitPrice, tvSupplier; + + public InventoryViewHolder(@NonNull View v) { + super(v); + tvItemName = v.findViewById(R.id.tvItemName); + tvCategory = v.findViewById(R.id.tvCategory); + tvQuantity = v.findViewById(R.id.tvQuantity); + tvUnitPrice = v.findViewById(R.id.tvUnitPrice); + tvSupplier = v.findViewById(R.id.tvInvSupplier); + } + } + + // Create a new row view + @NonNull + @Override + public InventoryViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_inventory, parent, false); + return new InventoryViewHolder(v); + } + + // Populate the row with inventory data + @Override + public void onBindViewHolder(@NonNull InventoryViewHolder holder, int position) { + Inventory inventory = inventoryList.get(position); + + holder.tvItemName.setText(inventory.getItemName()); + holder.tvCategory.setText(inventory.getCategory()); + holder.tvQuantity.setText("Qty: " + inventory.getQuantity()); + holder.tvUnitPrice.setText("$" + String.format("%.2f", inventory.getUnitPrice())); + holder.tvSupplier.setText("Supplier: " + inventory.getSupplier()); + + // Highlight low stock items in red + if (inventory.getQuantity() <= 5) { + holder.tvQuantity.setTextColor(Color.parseColor("#F44336")); + } else { + holder.tvQuantity.setTextColor(Color.parseColor("#4CAF50")); + } + + // When a row is clicked, open the detail view + holder.itemView.setOnClickListener(v -> inventoryClickListener.onInventoryClick(position)); + } + + @Override + public int getItemCount() { + return inventoryList.size(); + } +} diff --git a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/InventoryFragment.java b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/InventoryFragment.java new file mode 100644 index 00000000..12baebe4 --- /dev/null +++ b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/InventoryFragment.java @@ -0,0 +1,148 @@ +package com.example.petstoremobile.fragments.listfragments; + +// Added search/filter bar to filter inventory by item name or category. +// Added pull-to-refresh using SwipeRefreshLayout. + +import android.os.Bundle; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; +import com.example.petstoremobile.R; +import com.example.petstoremobile.adapters.InventoryAdapter; +import com.example.petstoremobile.fragments.ListFragment; +import com.example.petstoremobile.fragments.listfragments.detailfragments.InventoryDetailFragment; +import com.example.petstoremobile.models.Inventory; +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import java.util.ArrayList; +import java.util.List; + +public class InventoryFragment extends Fragment implements InventoryAdapter.OnInventoryClickListener { + + private List inventoryList = new ArrayList<>(); + private List filteredList = new ArrayList<>(); + private InventoryAdapter adapter; + private SwipeRefreshLayout swipeRefreshLayout; + private EditText etSearch; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_inventory, container, false); + + loadInventoryData(); // TODO: Replace with actual API call when backend is ready + setupRecyclerView(view); + setupSearch(view); + setupSwipeRefresh(view); + + FloatingActionButton fabAddInventory = view.findViewById(R.id.fabAddInventory); + fabAddInventory.setOnClickListener(v -> openInventoryDetails(-1)); + + return view; + } + + // Filters inventory list by item name or category + private void setupSearch(View view) { + etSearch = view.findViewById(R.id.etSearchInventory); + etSearch.addTextChangedListener(new TextWatcher() { + @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + @Override public void onTextChanged(CharSequence s, int start, int before, int count) { + filterInventory(s.toString()); + } + @Override public void afterTextChanged(Editable s) {} + }); + } + + private void filterInventory(String query) { + filteredList.clear(); + if (query.isEmpty()) { + filteredList.addAll(inventoryList); + } else { + String lower = query.toLowerCase(); + for (Inventory i : inventoryList) { + if (i.getItemName().toLowerCase().contains(lower) + || i.getCategory().toLowerCase().contains(lower) + || i.getSupplier().toLowerCase().contains(lower)) { + filteredList.add(i); + } + } + } + adapter.notifyDataSetChanged(); + } + + private void setupSwipeRefresh(View view) { + swipeRefreshLayout = view.findViewById(R.id.swipeRefreshInventory); + swipeRefreshLayout.setOnRefreshListener(() -> { + loadInventoryData(); // TODO: Replace with actual API call + filterInventory(etSearch.getText().toString()); + swipeRefreshLayout.setRefreshing(false); + }); + } + + private void openInventoryDetails(int position) { + InventoryDetailFragment detailFragment = new InventoryDetailFragment(); + Bundle args = new Bundle(); + args.putInt("position", position); + + if (position != -1) { + Inventory inventory = filteredList.get(position); + int realPosition = inventoryList.indexOf(inventory); + args.putInt("position", realPosition); + args.putInt("inventoryId", inventory.getInventoryId()); + args.putString("itemName", inventory.getItemName()); + args.putString("category", inventory.getCategory()); + args.putInt("quantity", inventory.getQuantity()); + args.putDouble("unitPrice", inventory.getUnitPrice()); + args.putString("supplier", inventory.getSupplier()); + } + + detailFragment.setArguments(args); + detailFragment.setInventoryFragment(this); + + ListFragment listFragment = (ListFragment) getParentFragment(); + if (listFragment != null) listFragment.loadFragment(detailFragment); + } + + public void onInventorySaved(int position, Inventory inventory) { + if (position == -1) { + inventoryList.add(inventory); + } else { + inventoryList.set(position, inventory); + } + filterInventory(etSearch.getText().toString()); + } + + public void onInventoryDeleted(int position) { + inventoryList.remove(position); + filterInventory(etSearch.getText().toString()); + } + + @Override + public void onInventoryClick(int position) { + openInventoryDetails(position); + } + + private void loadInventoryData() { + inventoryList.clear(); + inventoryList.add(new Inventory(1, "Dog Food - Large", "Food", 50, 25.99, "PetSupplies Co.")); + inventoryList.add(new Inventory(2, "Cat Litter", "Hygiene", 30, 12.99, "CleanPaws Ltd.")); + inventoryList.add(new Inventory(3, "Dog Leash", "Accessories", 4, 15.99, "PetGear Inc.")); + inventoryList.add(new Inventory(4, "Bird Cage - Medium", "Housing", 8, 79.99, "BirdWorld")); + inventoryList.add(new Inventory(5, "Flea Treatment", "Medicine", 2, 34.99, "VetCare Supply")); + filteredList.clear(); + filteredList.addAll(inventoryList); + } + + private void setupRecyclerView(View view) { + RecyclerView recyclerView = view.findViewById(R.id.recyclerViewInventory); + adapter = new InventoryAdapter(filteredList, this); + recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + recyclerView.setAdapter(adapter); + } +} diff --git a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/InventoryDetailFragment.java b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/InventoryDetailFragment.java new file mode 100644 index 00000000..175feb91 --- /dev/null +++ b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/InventoryDetailFragment.java @@ -0,0 +1,139 @@ +package com.example.petstoremobile.fragments.listfragments.detailfragments; + +// Uses InputValidator for detailed field validation and ActivityLogger to log all changes. + +import android.os.Bundle; +import androidx.fragment.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; +import com.example.petstoremobile.R; +import com.example.petstoremobile.fragments.ListFragment; +import com.example.petstoremobile.fragments.listfragments.InventoryFragment; +import com.example.petstoremobile.models.Inventory; +import com.example.petstoremobile.utils.ActivityLogger; +import com.example.petstoremobile.utils.InputValidator; + +public class InventoryDetailFragment extends Fragment { + + private TextView tvMode, tvInventoryId; + private EditText etItemName, etCategory, etQuantity, etUnitPrice, etSupplier; + private Button btnSaveInventory, btnDeleteInventory, btnBack; + private int inventoryId; + private int position; + private boolean isEditing = false; + private InventoryFragment inventoryFragment; + + // Set the inventory fragment as parent so we refer back when save or delete is done + public void setInventoryFragment(InventoryFragment fragment) { + this.inventoryFragment = fragment; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_inventory_detail, container, false); + + initViews(view); + handleArguments(); + + btnBack.setOnClickListener(v -> { + ListFragment listFragment = (ListFragment) getParentFragment(); + if (listFragment != null) listFragment.getChildFragmentManager().popBackStack(); + }); + btnSaveInventory.setOnClickListener(v -> saveInventory()); + btnDeleteInventory.setOnClickListener(v -> deleteInventory()); + + return view; + } + + // Validates all fields using InputValidator, then saves the inventory item + private void saveInventory() { + if (!InputValidator.isNotEmpty(etItemName, "Item Name")) return; + if (!InputValidator.isNotEmpty(etCategory, "Category")) return; + if (!InputValidator.isPositiveInteger(etQuantity, "Quantity")) return; + if (!InputValidator.isPositiveDecimal(etUnitPrice, "Unit Price")) return; + if (!InputValidator.isNotEmpty(etSupplier, "Supplier")) return; + + String itemName = etItemName.getText().toString().trim(); + String category = etCategory.getText().toString().trim(); + int quantity = Integer.parseInt(etQuantity.getText().toString().trim()); + double unitPrice = Double.parseDouble(etUnitPrice.getText().toString().trim()); + String supplier = etSupplier.getText().toString().trim(); + + try { + if (isEditing) { + // TODO: Replace with actual API PUT call when backend is ready + Inventory updated = new Inventory(inventoryId, itemName, category, quantity, unitPrice, supplier); + if (inventoryFragment != null) inventoryFragment.onInventorySaved(position, updated); + ActivityLogger.logChange(requireContext(), "Inventory", "UPDATED", inventoryId); + Toast.makeText(getContext(), "Inventory item updated.", Toast.LENGTH_SHORT).show(); + } else { + // TODO: Replace with actual API POST call when backend is ready + Inventory newItem = new Inventory(0, itemName, category, quantity, unitPrice, supplier); + if (inventoryFragment != null) inventoryFragment.onInventorySaved(-1, newItem); + ActivityLogger.log(requireContext(), "Added new Inventory item: " + itemName); + Toast.makeText(getContext(), "Inventory item added.", Toast.LENGTH_SHORT).show(); + } + ListFragment listFragment = (ListFragment) getParentFragment(); + if (listFragment != null) listFragment.getChildFragmentManager().popBackStack(); + } catch (Exception e) { + ActivityLogger.logException(requireContext(), "InventoryDetailFragment.saveInventory", e); + Toast.makeText(getContext(), "Error saving inventory item.", Toast.LENGTH_SHORT).show(); + } + } + + // Deletes the inventory item and logs the action + private void deleteInventory() { + try { + // TODO: Replace with actual API DELETE call when backend is ready + if (inventoryFragment != null) inventoryFragment.onInventoryDeleted(position); + ActivityLogger.logChange(requireContext(), "Inventory", "DELETED", inventoryId); + Toast.makeText(getContext(), "Inventory item deleted.", Toast.LENGTH_SHORT).show(); + ListFragment listFragment = (ListFragment) getParentFragment(); + if (listFragment != null) listFragment.getChildFragmentManager().popBackStack(); + } catch (Exception e) { + ActivityLogger.logException(requireContext(), "InventoryDetailFragment.deleteInventory", e); + Toast.makeText(getContext(), "Error deleting inventory item.", Toast.LENGTH_SHORT).show(); + } + } + + private void handleArguments() { + if (getArguments() != null && getArguments().containsKey("inventoryId")) { + isEditing = true; + inventoryId = getArguments().getInt("inventoryId"); + position = getArguments().getInt("position"); + tvMode.setText("Edit Inventory Item"); + tvInventoryId.setText("ID: " + inventoryId); + etItemName.setText(getArguments().getString("itemName")); + etCategory.setText(getArguments().getString("category")); + etQuantity.setText(String.valueOf(getArguments().getInt("quantity"))); + etUnitPrice.setText(String.valueOf(getArguments().getDouble("unitPrice"))); + etSupplier.setText(getArguments().getString("supplier")); + btnDeleteInventory.setVisibility(View.VISIBLE); + } else { + isEditing = false; + tvMode.setText("Add Inventory Item"); + tvInventoryId.setVisibility(View.GONE); + btnDeleteInventory.setVisibility(View.GONE); + btnSaveInventory.setText("Add"); + } + } + + private void initViews(View view) { + tvMode = view.findViewById(R.id.tvInventoryMode); + tvInventoryId = view.findViewById(R.id.tvInventoryId); + etItemName = view.findViewById(R.id.etItemName); + etCategory = view.findViewById(R.id.etInventoryCategory); + etQuantity = view.findViewById(R.id.etQuantity); + etUnitPrice = view.findViewById(R.id.etUnitPrice); + etSupplier = view.findViewById(R.id.etInventorySupplier); + btnSaveInventory = view.findViewById(R.id.btnSaveInventory); + btnDeleteInventory = view.findViewById(R.id.btnDeleteInventory); + btnBack = view.findViewById(R.id.btnInventoryBack); + } +} diff --git a/app/src/main/res/layout/fragment_inventory.xml b/app/src/main/res/layout/fragment_inventory.xml new file mode 100644 index 00000000..d51c88ea --- /dev/null +++ b/app/src/main/res/layout/fragment_inventory.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_inventory_detail.xml b/app/src/main/res/layout/fragment_inventory_detail.xml new file mode 100644 index 00000000..5cddb276 --- /dev/null +++ b/app/src/main/res/layout/fragment_inventory_detail.xml @@ -0,0 +1,153 @@ + + + + + + + + + +