As we are done with 👉 auth and private route setup . Let's continue further to complete frontend part of our project following the below requirements.
1. Create a Dashboard page pages/Dashboard.jsx
I wanted a dashboard that provides all my required components are displayed on the screen.
As, dashboard has to be a private route i.e, after login only user r allowed to access.
Fetching user details from AuthContext.
If the user is not available, provides a loading screen.
Displaying a dark-themed dashboard with a Navbar.
Showing a welcome message with user.name.
Providing three navigation links to different sections using a grid layout with appropriate styling.
import { useContext } from "react";
import { AuthContext } from "../context/AuthContext";
import { Link } from "react-router-dom";
import Navbar from "../components/Navbar";
const Dashboard = () => {
const { user } = useContext(AuthContext);
if (!user) {
return Loading...;
}
return (
{/* Navbar */}
{/* Dashboard Content */}
Welcome, {user.name}!
Manage your stock efficiently with real-time insights.
{/* Dashboard Cards */}
Manage Products
Stock Overview
Analytics Dashboard
);
};
export default Dashboard;
Let's implement the components that we defined above.
2. components/ProductManagement.jsx
- Setting up the required imports.
import { useEffect, useState } from "react"; //for statemanagement and sideeffects.
import { toast } from "react-toastify"; //for notification pop-ups
import { Trash2, Edit3 } from "lucide-react"; //Icons for delete and edit operations.
import api from "../utils/api"; //for api calls.
import Navbar from "./Navbar"; //navbar component
import { useNavigate } from "react-router-dom"; //React router navigation function.
- State Variables
- products: storing the list of products.
form: Managing product form data for adding/editing.
sellForm: Storing sales data.
showAddForm, showSellForm: Toggle forms for adding/selling products.
editingProduct: Stores the ID of the product being edited.
const [products, setProducts] = useState([]);
const [form, setForm] = useState({ name: "", category: "", price: "", quantityInStock: "" });
const [sellForm, setSellForm] = useState({ productId: "", quantity: "" });
const [showAddForm, setShowAddForm] = useState(false);
const [showSellForm, setShowSellForm] = useState(false);
const [editingProduct, setEditingProduct] = useState(null);
- Fetching the Products Fetching the products data when the component loads from the API,then updating the state with API response.
useEffect(() => {
fetchProducts();
}, []);
const fetchProducts = async () => {
try {
const { data } = await api.get("/products");
setProducts(data);
} catch (error) {
console.error(error);
toast.error("Failed to fetch products. Please try again.");
}
};
- Handling Input Changes
Updates form state dynamically as user types.
const handleChange = (e) => setForm({ ...form, [e.target.name]: e.target.value });
const handleSellChange = (e) => {
const { name, value } = e.target;
setSellForm((prev) => ({
...prev,
[name]: name === "quantity" ? Number(value) : value, // Ensures quantity is a number
}));
};
- Handling the product editing
const handleEdit = (product) => {
setEditingProduct(product._id); // Store product ID
setForm({ ...product }); // Populate form with product data
setShowAddForm(true); // Open form modal
};
- Adding or updating the products 1.If editingProduct exists → Update product (PUT /products/:id).
2.Else → Create new product (POST /products).
const handleSubmit = async (e) => {
e.preventDefault();
if (!form.name || !form.category || !form.price || !form.quantityInStock) {
toast.warn("Please fill in all fields before submitting.");
return;
}
try {
if (editingProduct) {
await api.put(`/products/${editingProduct}`, form);
toast.success("Product updated successfully");
setEditingProduct(null);
} else {
await api.post("/products", form);
toast.success("Product added successfully");
}
fetchProducts();
setForm({ name: "", category: "", price: "", quantityInStock: "" });
setShowAddForm(false);
} catch (error) {
toast.error(error.response?.data?.message || "Error adding product.");
}
};
- Selling a Product 1.Sends a sale request (POST /sales/sell).
2.Reduces stock count when a product is sold.
const handleSellSubmit = async (e) => {
e.preventDefault();
if (!sellForm.productId || !sellForm.quantity || sellForm.quantity <= 0) {
toast.warn("Please select a product and enter a valid quantity.");
return;
}
try {
await api.post("/sales/sell", sellForm);
toast.success("Product sold successfully! 💰");
fetchProducts();
setSellForm({ productId: "", quantity: "" });
setShowSellForm(false);
} catch (error) {
toast.error(error.response?.data?.message || "Error selling product.");
}
};
- Deleting a Product
const handleDelete = async (id) => {
try {
await api.delete(`/products/${id}`);
toast.success("Product deleted successfully! 🗑️");
fetchProducts();
} catch (error) {
console.error(error);
toast.error("Error deleting product. Please try again.");
}
};
- CSV Import
- Allows users to upload a CSV file.
- Sends a multipart/form-data request to /csv/import-csv.
const handleImportCSV = async (e) => {
e.preventDefault();
const file = e.target.files[0];
if (!file) {
toast.warn("Please select a CSV file to import.");
return;
}
const formData = new FormData();
formData.append("file", file);
try {
await api.post("/csv/import-csv", formData, { headers: { "Content-Type": "multipart/form-data" } });
toast.success("CSV imported successfully! 📂");
fetchProducts();
} catch (error) {
console.error(error);
toast.error("Error importing CSV. Please check the file format, and duplicates before Import.");
}
};
- CSV Export 1.Fetches CSV data from /csv/export-csv.
2.downloads a file for the user.
const handleExportCSV = async () => {
try {
const response = await api.get("/csv/export-csv", { responseType: "blob" });
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", "products.csv");
document.body.appendChild(link);
link.click();
toast.success("CSV exported successfully! 📤");
} catch (error) {
console.error(error);
toast.error("Error exporting CSV. Please try again.");
}
};
- Component Structure in return statement.
- Adding new products
Selling products
Importing/exporting CSV files
Editing and deleting products
Displaying a list of products
- Header Section Back to Dashboard button → Navigates back to the dashboard.
Title: "Product Management" → Displays the section title.
CSV Import & Export → Allows importing a CSV file.
→ Exports the product list as a CSV.
{/* Header */}
navigate("/dashboard")}
className="flex items-center gap-2 bg-gray-600 hover:bg-gray-700 text-white py-2 px-4 rounded-lg shadow-md transition duration-300"
>
Back to Dashboard
Product Management
Import CSV
Export CSV
- Action Buttons 1."Add Product" Button → Toggles the add/edit product form.
2."Sell Product" Button → Toggles the sell product form.
{/* Action Buttons */}
setShowAddForm(!showAddForm)} className="bg-green-500 hover:bg-green-600 text-white py-2 px-4 rounded-lg">
{showAddForm ? "Close" : "Add Product"}
setShowSellForm(!showSellForm)} className="bg-yellow-500 hover:bg-yellow-600 text-white py-2 px-4 rounded-lg">
{showSellForm ? "Close" : "Sell Product"}
- Add/Edit Product form 1.Displays a form when adding a new product or editing an existing one.
2.Fields:Product Name,Category,Price,Stock Quantity
3.Submit button → Adds or updates the product.
4.Cancel button → Closes the form and resets the state.
{/* Add Product Form */}
{showAddForm && (
{editingProduct ? "Edit Product" : "Add New Product"}
{editingProduct ? "Update Product" : "Add Product"}
{ setShowAddForm(false); setEditingProduct(null); }} className="bg-red-500 hover:bg-red-600 text-white py-2 px-4 rounded-lg">Cancel
)}
- Sell Product form 1.Allows selecting a product and entering the quantity to sell.
2.Fields:
Dropdown: Selects a product from available ones.
Quantity input: Specifies the number of units to sell.
Submit button → Processes the sale and updates stock.
{/* Sell Product Form */}
{showSellForm && (
Sell Product
Select Product
{products.map((product) => (
{product.name} (Stock: {product.quantityInStock})
))}
Sell Product
)}
- Product List Table
1.Displays all available products in a table.
2.Columns:
Product Name, Category, Price, Stock Quantity, Actions
Edit button → Opens the edit form with pre-filled details.
Delete button → Deletes the selected product.
{/* Product List */}
Product List
Product
Category
Price
Stock
Actions
{products.map((product) => (
{product.name}
{product.category}
${product.price}
{product.quantityInStock}
handleEdit(product)} className="text-blue-400 hover:text-blue-600 p-2">
handleDelete(product._id)} className="text-red-500 hover:text-red-700 p-2">
))}
3. Stock Overview Component components/StockOverview.jsx
- Importing Dependencies
import { useEffect, useState } from "react"; //Manages state and API calls.
import { toast } from "react-toastify"; //Displays error messages if API calls fail.
import api from "../utils/api"; //Axios instance for API requests.
import { useNavigate } from "react-router-dom"; //Enables navigation between pages.
- Component Initialization & State Management
1.stockData → Stores the stock details retrieved from the API.
loading → Tracks whether data is still being fetched.
const navigate = useNavigate();
const [stockData, setStockData] = useState(null);
const [loading, setLoading] = useState(true);
- Fetching the Stock Data
useEffect(() => {
fetchStockOverview();
}, []);
- Fetching Stock Overview from API
1.Calls the API (/stock-overview) to retrieve stock details.
2.Handles errors (logs error + displays a toast notification).
3.Sets loading to false after fetching data.
const fetchStockOverview = async () => {
try {
const { data } = await api.get("/stock-overview");
setStockData(data);
} catch (error) {
console.error(error);
toast.error("Failed to fetch stock overview");
} finally {
setLoading(false);
}
};
- UI or Component Return Statement
1.Dashboard Navigation
navigate("/dashboard")}
className="flex items-center gap-2 bg-gray-600 hover:bg-gray-700 text-white py-2 px-4 rounded-lg shadow-md transition duration-300"
>
Back to Dashboard
- Stock Overview section
Stock Overview
- Stock Data Card Displays 3 key metrics: Total Items, Total Sold, Total Revenue
Skeleton Loading Effect: While fetching data, it displays a placeholder using animate-pulse.
{loading ? (
[...Array(3)].map((_, i) => (
))
) : (
<>
Total Items
{stockData.totalItems}
Total Sold
{stockData.totalSold}
Total Revenue
${stockData.totalRevenue.toFixed(2)}
>
)}
- Sold Items List
Displays sold items in a list.
Shows a skeleton loading effect if data is still being fetched.
Handles empty data gracefully (No items sold yet.)
Sold Items
{loading ? (
[...Array(5)].map((_, i) => (
))
) : stockData.soldItems.length > 0 ? (
stockData.soldItems.map((item, index) => (
{item.name}
{item.category}
Sold: {item.quantitySold}
Revenue: ${item.revenueGenerated.toFixed(2)}
))
) : (
No items sold yet.
)}
4. Analytics Component components/Analytics.jsx
- Imports and Initial Setup.
import { useEffect, useState } from "react";
import { Bar } from "react-chartjs-2"; //Registers bar chart components.
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
} from "chart.js";
import api from "../utils/api"; // Using baseURL configured API
import { useNavigate } from "react-router-dom";
- State Management
const [stockData, setStockData] = useState([]); //Stores fetched product stock data
const [chartData, setChartData] = useState(null); // Stores formatted data for Chart.js
const [category, setCategory] = useState(""); //Stores user-input filter for product category
const [sortBy, setSortBy] = useState("totalRevenue"); //Stores sorting criteria (totalRevenue or totalSold)
const [order, setOrder] = useState("desc"); //Determines sort order (asc or desc)
const [loading, setLoading] = useState(true); //Tracks data fetching state
const [error, setError] = useState(null); //Stores error messages
- Data fetching with UseEffect Calls fetchStockData and fetchChartData whenever category, sortBy, or order changes.
useEffect(() => {
fetchStockData();
fetchChartData();
}, [category, sortBy, order]);
- Fetching Stock Data 1.Sends API request to /analytics/stock to get stock details.
2.Filters products based on category (case-insensitive).
3.Updates stockData with filtered results.
const fetchStockData = async () => {
setLoading(true);
try {
const response = await api.get(`/analytics/stock`, {
params: { sortBy, order },
});
let filteredData = response.data;
// Apply case-insensitive filtering on the frontend
if (category.trim() !== "") {
const searchQuery = category.toLowerCase();
filteredData = filteredData.filter(product =>
product.category.toLowerCase().includes(searchQuery) ||
product.name.toLowerCase().includes(searchQuery)
);
}
setStockData(filteredData);
} catch (error) {
setError("Error fetching stock data");
console.error("Error fetching stock data:", error);
} finally {
setLoading(false);
}
};
- Fetching Chart Data
1.Calls API /analytics/chart-data to get total revenue & total sold per category.
2.Converts response into chartData for Chart.js bar graph.
const fetchChartData = async () => {
try {
const response = await api.get(`/analytics/chart-data`);
const data = response.data;
setChartData({
labels: data.map((item) => item.category),
datasets: [
{
label: "Total Revenue",
data: data.map((item) => item.totalRevenue || 0),
backgroundColor: "rgba(54, 162, 235, 0.6)",
},
{
label: "Total Sold",
data: data.map((item) => item.totalSold || 0),
backgroundColor: "rgba(255, 99, 132, 0.6)",
},
],
});
} catch (error) {
setError("Error fetching chart data");
console.error("Error fetching chart data:", error);
}
};
- UI Rendering
- Navigation Button
navigate("/dashboard")}
className="flex items-center gap-2 bg-gray-600 hover:bg-gray-700 text-white py-2 px-4 rounded-lg shadow-md transition duration-300"
>
Back to Dashboard
- Filters and Sorting Options
- Text Input: Filters by category.
- Dropdowns: 1.Sort by (totalRevenue / totalSold). 2.Sort order (asc / desc).
setCategory(e.target.value)}
className="p-2 bg-gray-800 border border-gray-600 rounded"
/>
setSortBy(e.target.value)}
className="p-2 bg-gray-800 border border-gray-600 rounded"
>
Sort by Revenue
Sort by Items Sold
setOrder(e.target.value)}
className="p-2 bg-gray-800 border border-gray-600 rounded"
>
Descending
Ascending
- Table for Stock Data
Displays product, category, sales count, and revenue.
Uses stockData to generate rows.
Product
Category
Items Sold
Revenue
{stockData.map((product) => (
{product.name}
{product.category}
{product.itemsSold}
${product.totalRevenue ? product.totalRevenue.toFixed(2) : "0.00"}
))}
- Chart.js Bar Chart
- If chartData exists, renders bar chart.
{chartData && (
Sales & Revenue Overview
)}
5. Landing Page pages/LandingPage.jsx
- Handling imports
import { useNavigate } from "react-router-dom";
import { ShoppingCart, BarChart3, FileText, LogIn } from "lucide-react";
import { AuthContext } from "../context/AuthContext";
import { useContext, useState } from "react";
- Component definition and AuthChecks
1.Extracts user and logout from AuthContext to manage authentication.
2.isOpen controls the profile dropdown visibility.
3.isAuthenticated checks if the user is logged in using localStorage token.
const LandingPage = () => {
const { user, logout } = useContext(AuthContext);
const [isOpen, setIsOpen] = useState(false);
const navigate = useNavigate();
const handleLogout = () => {
logout();
navigate("/");
setIsOpen(false);
};
const isAuthenticated = !!localStorage.getItem("token");
- Handling Profile Click Toggles the profile dropdown when clicked.
const handleProfileClick = async () => {
if (!isOpen) {
await user;
}
setIsOpen(!isOpen);
};
- Handling Navigation
const handleNavigation = (path) => {
navigate(isAuthenticated ? path : "/login");
};
- Landing page layout // Dark mode styling.
return (
Enter fullscreen mode
Exit fullscreen mode
Navbar section
1.If the user is logged in, show a profile button.
2.Clicking Profile reveals a dropdown with name, email, and a logout button.3.If not logged in, show Login & Register buttons.
{user ? (
Profile
{isOpen && (
{user.name}
{user.email}
Logout
)}
) : (
navigate("/login")} className="text-white hover:text-blue-400 transition duration-300">Login
|
navigate("/auth/register")} className="text-white hover:text-green-400 transition duration-300">Register
)}
Enter fullscreen mode
Exit fullscreen mode
Hero Section
Title & Description for the landing page.
Stock Management System
A powerful tool to track inventory, manage stock levels, and generate reports efficiently.
Enter fullscreen mode
Exit fullscreen mode
Features Section
1.Displays 3 feature cards (Product Management, Stock Overview, Analysis).2.If user is not authenticated, an extra card for Secure Login appears.
{[
{
path: "/products",
icon: ,
title: "Product Management",
description: "Easily add, edit, and delete products in stock.",
},
{
path: "/stock-overview",
icon: ,
title: "Stock Overview",
description: "Monitor available stock, items sold, and revenue trends.",
},
{
path: "/analytics",
icon: ,
title: "Analysis",
description: "Generate reports in charts.",
},
].map((feature, index) => (
handleNavigation(feature.path)}
className="bg-gray-800 p-6 rounded-lg shadow-md cursor-pointer hover:bg-gray-700 transition duration-300">
{feature.icon}
{feature.title}
{feature.description}
))}
{!isAuthenticated && (
navigate("/login")}
className="bg-gray-800 p-6 rounded-lg shadow-md cursor-pointer hover:bg-gray-700 transition duration-300">
Secure Login
Ensure security with JWT-based authentication.
)}
Enter fullscreen mode
Exit fullscreen mode
Footer Section.
© {new Date().getFullYear()} Stock Management System By Shaik Reshma
Enter fullscreen mode
Exit fullscreen mode
Checkout my project source code 👉github and the you'll find the deployed link, Test it out there.This is all about frontend setup code and run the application npm run dev.For any queries react out in comment section.
Happy Developing🎉
Let's grow together!