/** * @license * SPDX-License-Identifier: Apache-2.0 */ import React, { useState } from 'react'; import { motion } from 'motion/react'; import { TrendingUp, Package, AlertTriangle, FileText, UserCheck, Currency, ArrowRight, Plus, RefreshCw, Search, ShoppingCart } from 'lucide-react'; import { TireProduct, Invoice, StaffUser, SystemSettings } from '../types'; interface DashboardProps { products: TireProduct[]; invoices: Invoice[]; currentStaff: StaffUser; settings: SystemSettings; setActiveTab: (tab: string) => void; onQuickAddStock: (productId: string, amount: number) => void; setSelectedInvoiceForView: (invoice: Invoice | null) => void; } export default function Dashboard({ products, invoices, currentStaff, settings, setActiveTab, onQuickAddStock, setSelectedInvoiceForView }: DashboardProps) { const [replenishAmount, setReplenishAmount] = useState<{ [key: string]: number }>({}); const [searchTerm, setSearchTerm] = useState(''); // 1. Calculate general statistics const totalInvoiced = invoices.reduce((sum, inv) => sum + (inv.paymentStatus === 'PAID' ? inv.total : 0), 0); const totalActiveItems = products.reduce((sum, p) => sum + p.stock, 0); const lowStockItems = products.filter(p => p.stock <= p.minStock); const totalInvoicesCount = invoices.length; const currentMonthName = "June 2026"; const juneInvoices = invoices.filter(inv => { // Basic date checking return inv.dateTime.includes('-06-') || inv.dateTime.includes('/06/') || inv.dateTime.startsWith('02-06-') || inv.dateTime.startsWith('03-06-'); }); const juneSalesTotal = juneInvoices.reduce((sum, inv) => sum + (inv.paymentStatus === 'PAID' ? inv.total : 0), 0); // 2. Generate custom monthly line metrics for our 2026 sales chart // Group invoices by month const monthlyData = [ { label: 'Jan', value: invoices.filter(i => i.dateTime.includes('-01-')).reduce((sum, i) => sum + i.total, 0) }, { label: 'Feb', value: invoices.filter(i => i.dateTime.includes('-02-')).reduce((sum, i) => sum + i.total, 0) }, { label: 'Mar', value: invoices.filter(i => i.dateTime.includes('-03-')).reduce((sum, i) => sum + i.total, 0) }, { label: 'Apr', value: invoices.filter(i => i.dateTime.includes('-04-')).reduce((sum, i) => sum + i.total, 0) }, { label: 'May', value: invoices.filter(i => i.dateTime.includes('-05-')).reduce((sum, i) => sum + i.total, 0) }, { label: 'Jun', value: invoices.filter(i => i.dateTime.includes('-06-')).reduce((sum, i) => sum + i.total, 0) }, ]; const maxMonthlyVal = Math.max(...monthlyData.map(d => d.value), 100000); // 3. Category distribution const categories = ['Passenger', 'SUV', 'Commercial', 'Light Truck', 'Truck/Bus'] as const; const categoryStats = categories.map(cat => { const items = products.filter(p => p.category === cat); const count = items.reduce((sum, p) => sum + p.stock, 0); const totalValue = items.reduce((sum, p) => sum + (p.stock * p.price), 0); return { name: cat, count, value: totalValue }; }); const handleQuickAddClick = (productId: string) => { const amt = replenishAmount[productId] || 10; onQuickAddStock(productId, amt); // Reset counter setReplenishAmount(prev => ({ ...prev, [productId]: 10 })); }; // Filter products for low stock alert segment const filteredAlertProducts = lowStockItems.filter(p => p.brand.toLowerCase().includes(searchTerm.toLowerCase()) || p.model.toLowerCase().includes(searchTerm.toLowerCase()) || p.size.toLowerCase().includes(searchTerm.toLowerCase()) ); return (
{/* Welcome Banner */}
★ Active Session — {currentStaff.role.toUpperCase()} MODE

{settings.shopName}

Direct invoicing, tax calculations, dynamic offline persistence, and real-time stock alerts. Welcomed operator: {currentStaff.name}.

{/* Abstract tire background shadow */}
{/* Main Core KPI Cards */}
{/* Metric 1 */}

Mtd Cash Flow ({currentMonthName})

{settings.currencySymbol} {juneSalesTotal.toLocaleString()}

+14.2% vs last month

{/* Metric 2 */}

Total Warehouse Stock

{totalActiveItems} pcs

Across {products.length} distinct models

{/* Metric 3 */}

Critical Alerts

{lowStockItems.length} items

Urgent restock alert

{/* Metric 4 */}

Total Sales Invoices

{totalInvoicesCount} sales

Invoiced: {settings.currencySymbol}{totalInvoiced.toLocaleString()}

{/* Analytics SVG Graph Row */}
{/* Core SVG Revenue Chart */}

Monthly Sales Reporting

Telemetry of invoice turnover for year 2026

Year {new Date().getFullYear()}
{/* SVG Animated Chart */}
{/* Grid Lines */} {/* Chart line plotting */} {(() => { const getCoords = () => { const xSpacing = 540 / 5; return monthlyData.map((d, index) => { const x = 40 + index * xSpacing; // Scale value from 210 (bottom) to 20 (top) const y = 210 - (d.value / maxMonthlyVal) * 180; return { x, y, label: d.label, val: d.value }; }); }; const coords = getCoords(); const pathD = coords.reduce((acc, c, idx) => { return acc + (idx === 0 ? `M ${c.x} ${c.y}` : ` L ${c.x} ${c.y}`); }, ""); const areaD = coords.reduce((acc, c, idx) => { if (idx === 0) return `M ${c.x} 210 L ${c.x} ${c.y}`; let ret = acc + ` L ${c.x} ${c.y}`; if (idx === coords.length - 1) ret += ` L ${c.x} 210 Z`; return ret; }, ""); return ( <> {/* Shaded Area */} {/* Connective Line */} {/* Dots and Labels */} {coords.map((c, i) => ( {/* Hidden interactive target block */} {c.label} {/* Turnover Value Overlay on Hover */} {settings.currencySymbol}{Math.round(c.val).toLocaleString()} ))} {/* Gradiant definitions */} ); })()}
Graph starts: Jan 2026 Real-time custom math calculation engine active Graph terminal: June 2026
{/* Category Breakdown list */}

Tire Category Audit

Warehouse composition and valuation

{categoryStats.map((stat, i) => { const maxCount = Math.max(...categoryStats.map(s => s.count), 1); const percentage = Math.round((stat.count / maxCount) * 100); return (
{stat.name} {stat.count} pcs — {settings.currencySymbol}{stat.value.toLocaleString()}
); })}
Total Inventory Valuation:

{settings.currencySymbol} {products.reduce((sum, p) => sum + (p.stock * p.price), 0).toLocaleString()}

{/* Stock critical alerts and Quick Add Section */}
{/* Real-time low stock warnings widget */}

Live Replenishment Alerts

{lowStockItems.length} Low Stock

The products listed below are currently equal to or below their alert limit. Top up immediately:

{/* Search alerts */}
setSearchTerm(e.target.value)} className="w-full text-xs border border-slate-200 outline-none focus:border-blue-500 bg-slate-50 hover:bg-slate-100 focus:bg-white rounded-xl pl-10 pr-4 py-2.5 transition duration-150" />
{filteredAlertProducts.length === 0 ? (

No matching low stock warnings!

) : ( filteredAlertProducts.map((p) => { const stockPercent = Math.max(Math.min((p.stock / p.minStock) * 100, 100), 5); const currentReplValue = replenishAmount[p.id] || 10; return (
{p.brand} {p.model} {p.size}
Min. stock threshold: {p.minStock} | Qty: {p.stock} remaining
{/* Critical line representing the severe level */}
{/* Stock quick load inputs */} {['owner', 'manager'].includes(currentStaff.role) ? (
setReplenishAmount(prev => ({ ...prev, [p.id]: parseInt(e.target.value) || 1 }))} className="w-16 border border-slate-200 focus:border-blue-500 bg-white shadow-inner outline-none rounded-lg text-xs py-1 px-2 font-mono text-center" />
) : ( Restock restricted to Managers )}
); }) )}
{/* Recent Invoices Table list */}

Recent Transactions

Review, query, and print historical customer receipts

{invoices.length === 0 ? (

No invoices drafted yet!

) : ( [...invoices].reverse().slice(0, 5).map((inv) => (
setSelectedInvoiceForView(inv)} className="group border border-slate-50 hover:border-blue-100 hover:bg-blue-50/20 rounded-xl p-3 flex justify-between items-center transition cursor-pointer" >
{inv.invoiceNumber} {inv.isOfflineCreated && ( OFFLINE )}

{inv.customerName}

{inv.dateTime}

{inv.currencySymbol} {inv.total.toLocaleString()}
{inv.paymentStatus} {inv.paymentMethod}
)) )}
); }