import React, { useState, useEffect } from 'react'; import DataTable from '../../../Components/DataTable'; import Toast from '../Components/Toast'; import { Package, Plus, AlertTriangle, Search, Filter, Building, Eye, History, ShoppingCart, ArrowUpRight, X, TrendingUp, MoreVertical, FileText, Settings, RefreshCw, BarChart3 } from 'lucide-react'; import AddProductModal from './AddProductModal'; import AdjustStockModal from './AdjustStockModal'; import ProductDetailsModal from './ProductDetailsModal'; import NewSaleModal from './NewSaleModal'; export default function InventoryIndex() { const [activeTab, setActiveTab] = useState('Stock Control'); const [products, setProducts] = useState([]); const [movements, setMovements] = useState([]); const [branches, setBranches] = useState([]); const [categories, setCategories] = useState([]); const [loading, setLoading] = useState(true); const [movementsLoading, setMovementsLoading] = useState(false); const [toast, setToast] = useState(null); // Modals const [isAddModalOpen, setIsAddModalOpen] = useState(false); const [isAdjustModalOpen, setIsAdjustModalOpen] = useState(false); const [isDetailsModalOpen, setIsDetailsModalOpen] = useState(false); const [isSaleModalOpen, setIsSaleModalOpen] = useState(false); const [selectedProduct, setSelectedProduct] = useState(null); // Filters const [filterBranch, setFilterBranch] = useState(window.__APP_DATA__?.role === 'receptionist' ? window.__APP_DATA__?.user?.branch_id : ''); const [filterStatus, setFilterStatus] = useState(''); const [startDate, setStartDate] = useState(''); const [endDate, setEndDate] = useState(''); const [searchTerm, setSearchTerm] = useState(''); const isReceptionist = window.__APP_DATA__?.role === 'receptionist'; const fetchMasters = async () => { try { // Fetch branches const bRes = await fetch('/api/branches'); if (bRes.ok) { setBranches(await bRes.json()); } else { console.error('Failed to fetch branches'); } // Fetch categories from the Master API to ensure 1:1 match const cRes = await fetch('/api/masters/product'); if (cRes.ok) { const cats = await cRes.json(); // Filter for Active only if needed, or show all setCategories(cats.filter(c => c.status === 'Active')); } else { console.error('Failed to fetch product categories from master API'); } } catch (error) { console.error('Master data fetch exception:', error); } }; const fetchProducts = async () => { setLoading(true); try { const url = `/api/inventory/products?branch_id=${filterBranch}&status=${filterStatus}`; const res = await fetch(url); setProducts(await res.json()); } finally { setLoading(false); } }; // Fetch Sales moved to Global Reports const fetchMovements = async () => { setMovementsLoading(true); try { let url = `/api/inventory/movements?branch_id=${filterBranch}`; if (startDate) url += `&start_date=${startDate}`; if (endDate) url += `&end_date=${endDate}`; const res = await fetch(url); setMovements(await res.json()); } catch (error) { console.error('Error fetching movements:', error); } finally { setMovementsLoading(false); } }; useEffect(() => { const init = async () => { setLoading(true); await Promise.all([fetchProducts(), fetchMasters()]); setLoading(false); }; init(); }, []); useEffect(() => { fetchProducts(); }, [filterBranch, filterStatus, startDate, endDate]); useEffect(() => { if (activeTab === 'Reports & Alerts') { fetchMovements(); } }, [activeTab, filterBranch, startDate, endDate]); const lowStockCount = products.filter(p => parseFloat(p.current_stock) <= parseFloat(p.reorder_level)).length; const stockColumns = [ { header: 'Branch', render: (row) => {row.branch?.name} }, { header: 'Product Info', render: (row) => (
{row.name} {row.category?.name}
SKU-{row.sku}
) }, { header: 'Cost Price', render: (row) => {parseFloat(row.cost_price).toFixed(2)} }, { header: 'Selling Price', render: (row) => {parseFloat(row.selling_price).toFixed(2)} }, { header: 'Current Stock', render: (row) => {row.current_stock} }, { header: 'Reorder Level', render: (row) => {row.reorder_level} }, { header: 'Status', render: (row) => ( {row.status} ) }, { header: 'Actions', render: (row) => (
) } ]; const movementColumns = [ { header: 'Date', render: (row) => {new Date(row.date).toLocaleDateString()} }, { header: 'Product / SKU', render: (row) => (
{row.product_name} {row.sku}
) }, { header: 'Branch', render: (row) => {row.branch} }, { header: 'Movement Type', render: (row) => {row.reason} }, { header: 'Change', render: (row) => ( = 0 ? 'text-emerald-500' : 'text-red-500'}`}> {row.change >= 0 ? : '-'} {Math.abs(row.change)} ) }, { header: 'Stock After', render: (row) => {row.new_stock} }, { header: 'Remarks', render: (row) => {row.remarks || '-'} } ]; const showToast = (message, type = 'success') => { setToast({ message, type }); setTimeout(() => setToast(null), 3000); }; return ( <> {toast && setToast(null)} />}

Inventory Management

Track stock, sales, and alerts.

{/* Tabs */}
{['Stock Control', 'Reports & Alerts'].map(tab => ( ))}
{/* Filters */}
setSearchTerm(e.target.value)} />
{!isReceptionist && (
{activeTab === 'Stock Control' && ( <>
)}
)} {isReceptionist && activeTab === 'Stock Control' && (
)} {(activeTab === 'Product Sales' || activeTab === 'Reports & Alerts') && (
From setStartDate(e.target.value)} />
To setEndDate(e.target.value)} />
)}
{activeTab === 'Stock Control' && (
p.name.toLowerCase().includes(searchTerm.toLowerCase()) || p.sku?.toLowerCase().includes(searchTerm.toLowerCase()))} loading={loading} emptyMessage="No products found." />
)} {activeTab === 'Reports & Alerts' && (

Inventory Movement Report

Global audit log of all stock adjustments across all branches.

)} {/* Modals */} setIsAddModalOpen(false)} onSave={(p) => { setProducts([p, ...products]); showToast('Product added successfully'); }} branches={branches} categories={categories} /> { setIsAdjustModalOpen(false); setSelectedProduct(null); }} onSave={(p) => { setProducts(products.map(old => old.id === p.id ? p : old)); showToast('Stock adjusted successfully'); }} product={selectedProduct} /> { setIsDetailsModalOpen(false); setSelectedProduct(null); }} product={selectedProduct} /> setIsSaleModalOpen(false)} onSave={() => { fetchProducts(); // Refresh stocks showToast('Sale completed successfully'); }} branches={branches} products={products} />
); }