inventory activities

This commit is contained in:
Nikitha
2026-03-12 20:52:55 -06:00
parent a4080a1e5f
commit 08cc87da53
5 changed files with 572 additions and 0 deletions

View File

@@ -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<InventoryAdapter.InventoryViewHolder> {
private List<Inventory> inventoryList;
private OnInventoryClickListener inventoryClickListener;
// Interface for inventory click on recycler view
public interface OnInventoryClickListener {
void onInventoryClick(int position);
}
// Constructor
public InventoryAdapter(List<Inventory> 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();
}
}

View File

@@ -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<Inventory> inventoryList = new ArrayList<>();
private List<Inventory> 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);
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Updated: added search bar and SwipeRefreshLayout for pull-to-refresh -->
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F5F5F5">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/etSearchInventory"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:hint="Search by item name or category..."
android:inputType="text"
android:drawableStart="@android:drawable/ic_menu_search"
android:drawablePadding="8dp"
android:background="@android:color/white"
android:padding="12dp"/>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshInventory"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerViewInventory"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</LinearLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabAddInventory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:contentDescription="Add Inventory Item"
app:srcCompat="@android:drawable/ic_input_add"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -0,0 +1,153 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F5F5F5">
<LinearLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:paddingTop="20dp"
android:paddingBottom="20dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tvInventoryMode"
android:layout_width="245dp"
android:layout_height="48dp"
android:fontFamily="sans-serif-black"
android:text="Add Inventory Item"
android:textColor="@color/text_dark"
android:textSize="22sp" />
<Button
android:id="@+id/btnDeleteInventory"
android:layout_width="121dp"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_weight="1"
android:text="Delete" />
</LinearLayout>
<TextView
android:id="@+id/tvInventoryId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:text="ID: 0" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:text="Item Name"
android:textColor="@color/text_dark"
android:textSize="12sp" />
<EditText
android:id="@+id/etItemName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:hint="Enter item name"
android:inputType="text" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:text="Category"
android:textColor="@color/text_dark"
android:textSize="12sp" />
<EditText
android:id="@+id/etInventoryCategory"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:hint="e.g. Food, Toys, Medicine"
android:inputType="text" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:text="Quantity"
android:textColor="@color/text_dark"
android:textSize="12sp" />
<EditText
android:id="@+id/etQuantity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:hint="Enter quantity"
android:inputType="number" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:text="Unit Price"
android:textColor="@color/text_dark"
android:textSize="12sp" />
<EditText
android:id="@+id/etUnitPrice"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:hint="Enter unit price"
android:inputType="numberDecimal" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:text="Supplier"
android:textColor="@color/text_dark"
android:textSize="12sp" />
<EditText
android:id="@+id/etInventorySupplier"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:hint="Enter supplier name"
android:inputType="text" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btnInventoryBack"
android:layout_width="121dp"
android:layout_height="wrap_content"
android:layout_marginRight="20dp"
android:layout_marginBottom="8dp"
android:layout_weight="1"
android:text="Back" />
<Button
android:id="@+id/btnSaveInventory"
android:layout_width="121dp"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginBottom="8dp"
android:layout_weight="1"
android:text="Save" />
</LinearLayout>
</LinearLayout>
</ScrollView>