/** * @license * SPDX-License-Identifier: Apache-2.0 */ import React, { useState, useEffect } from 'react'; import { ArrowLeft, Calendar, TrendingUp, Layers, Users, Phone, MapPin, AlertTriangle, Clock, Activity, CheckCircle2, Package, Search, ShoppingCart, Send, Download, X, Battery, ShieldCheck, Globe, UploadCloud, FileText, Mail, UserCheck, CreditCard, History, Building, Award, ShoppingBag } from 'lucide-react'; import { useFiestaStockStatement, useFiestaTenantCustomers, FIESTA_TENANT_ID } from '../services/fiestaQueries'; import { str as fstr } from '../services/fiestaApi'; import { initialInventory, initialCustomerOrders, operationalAlerts } from '../data'; import ragulStoreCover from '../assets/images/store_front_view_1780299351800.png'; import OrdersDeliveriesView from './OrdersDeliveriesView'; interface StoreDetailViewProps { store: { locationid?: number; name: string; zone: string; deliveries: number; sales: string; orders?: number; staff: string; color: string; status: string; }; onBack: () => void; } // ── Master Global Catalogue Items ── const GLOBAL_CATALOGUE_ITEMS = [ { sku: 'SALT-TATA-1KG', name: 'Tata Salt Premium Iodized 1kg', category: 'Staples / Salt', price: 28, image: 'https://images.unsplash.com/photo-1626132647523-66f5bf380027?auto=format&fit=crop&w=150&q=80' }, { sku: 'SUN-OIL-1LIT', name: 'Gold Winner Sunflower Oil 1L', category: 'Groceries / Oils', price: 145, image: 'https://images.unsplash.com/photo-1474979266404-7eaacbcd87c5?auto=format&fit=crop&w=150&q=80' }, { sku: 'BISCUIT-MAR-GD', name: 'Britannia Marie Gold Biscuit 250g', category: 'Snacks / Biscuits', price: 35, image: 'https://images.unsplash.com/photo-1558961363-fa8fdf82db35?auto=format&fit=crop&w=150&q=80' }, { sku: 'SPICE-SAMBAR-MTR', name: 'MTR Sambar Powder 200g', category: 'Groceries / Spices', price: 85, image: 'https://images.unsplash.com/photo-1596040033229-a9821ebd058d?auto=format&fit=crop&w=150&q=80' }, { sku: 'AAVIN-BUTTER-500', name: 'Aavin Salted Butter 500g', category: 'Dairy / Butter', price: 260, image: 'https://images.unsplash.com/photo-1589985270826-4b7bb135bc9d?auto=format&fit=crop&w=150&q=80' } ]; // Fallback cover images const DETAIL_STORE_COVERS = [ 'https://images.unsplash.com/photo-1542838132-92c53300491e?auto=format&fit=crop&w=800&q=80', 'https://images.unsplash.com/photo-1578916171728-46686eac8d58?auto=format&fit=crop&w=800&q=80', 'https://images.unsplash.com/photo-1604719312566-8912e9227c6a?auto=format&fit=crop&w=800&q=80', 'https://images.unsplash.com/photo-1534723452862-4c874018d66d?auto=format&fit=crop&w=800&q=80', 'https://images.unsplash.com/photo-1582408929130-98a2c2640b8a?auto=format&fit=crop&w=800&q=80', 'https://images.unsplash.com/photo-1516594798947-e65505dbb29d?auto=format&fit=crop&w=800&q=80', 'https://images.unsplash.com/photo-1601599561263-60a4e4e083cd?auto=format&fit=crop&w=800&q=80', 'https://images.unsplash.com/photo-1441986300917-64674bd600d8?auto=format&fit=crop&w=800&q=80', 'https://images.unsplash.com/photo-1528698827591-e19ccd7bc23d?auto=format&fit=crop&w=800&q=80', 'https://images.unsplash.com/photo-1536697246787-1f7ae568d89a?auto=format&fit=crop&w=800&q=80', 'https://images.unsplash.com/photo-1506617498306-bd97b3663b65?auto=format&fit=crop&w=800&q=80', 'https://images.unsplash.com/photo-1579621970563-ebec7560ff3e?auto=format&fit=crop&w=800&q=80' ]; export default function StoreDetailView({ store, onBack }: StoreDetailViewProps) { const [activeTab, setActiveTab] = useState<'overview' | 'inventory' | 'customers' | 'orders'>('overview'); const isRagul = store.name.toLowerCase().includes('ragul'); const getStoreCover = () => { if (isRagul) return ragulStoreCover; let hash = 0; for (let j = 0; j < store.name.length; j++) { hash = store.name.charCodeAt(j) + ((hash << 5) - hash); } const idx = Math.abs(hash) % DETAIL_STORE_COVERS.length; return DETAIL_STORE_COVERS[idx]; }; const storeCoverImage = getStoreCover(); const [stockSearch, setStockSearch] = useState(''); const [customerSearch, setCustomerSearch] = useState(''); const [hoveredChartIndex, setHoveredChartIndex] = useState(null); // ── Toast Notification state ────────────────────────────────────────────── const [toast, setToast] = useState<{ show: boolean; message: string; type: 'success' | 'info' | 'warning' }>({ show: false, message: '', type: 'success' }); const showToast = (message: string, type: 'success' | 'info' | 'warning' = 'success') => { setToast({ show: true, message, type }); setTimeout(() => { setToast(prev => ({ ...prev, show: false })); }, 4000); }; // ── Replenishment Modal state ────────────────────────────────────────────── const [replenishModal, setReplenishModal] = useState<{ show: boolean; item: any | null }>({ show: false, item: null }); const [replenishQty, setReplenishQty] = useState(100); // ── Catalogue Local State & Modals ──────────────────────────────────────── const [localInventory, setLocalInventory] = useState([]); const [showImportModal, setShowImportModal] = useState(false); const [importState, setImportState] = useState<'idle' | 'reading' | 'parsing' | 'saving' | 'done'>('idle'); const [showGlobalModal, setShowGlobalModal] = useState(false); const [selectedGlobalSkus, setSelectedGlobalSkus] = useState([]); // ── Customer CRM Profile Drawer state ────────────────────────────────────── const [selectedCustomer, setSelectedCustomer] = useState(null); // ── API Queries with live locationid ─────────────────────────────────────── const locationid = store.locationid || 1097; const stockQ = useFiestaStockStatement({ tenantid: FIESTA_TENANT_ID, locationid, pagesize: 100 }); const customersQ = useFiestaTenantCustomers({ tenantid: FIESTA_TENANT_ID, locationid, pagesize: 100 }); // ── Seed / Fallback calculation helpers ──────────────────────────────────── const parseOrdersCount = (salesStr: string): number => { const num = parseInt(salesStr.replace(/[^0-9]/g, ''), 10); return isNaN(num) ? 45 : num; }; const baseOrders = parseOrdersCount(store.sales); const revenueToday = baseOrders > 100 ? Math.round(baseOrders * 320) : 48200; // ── Interval slots with heights for chart representation ────────────────── const intervalSlots = [ { time: '06:00 AM - 10:00 AM', label: 'Morning Rush', orders: Math.round(store.deliveries * 0.35) || 14, sales: `₹${Math.round(revenueToday * 0.3).toLocaleString('en-IN')}`, height: '70%', status: 'PEAK' }, { time: '10:00 AM - 02:00 PM', label: 'Mid-day Deliveries', orders: Math.round(store.deliveries * 0.25) || 10, sales: `₹${Math.round(revenueToday * 0.25).toLocaleString('en-IN')}`, height: '50%', status: 'NORMAL' }, { time: '02:00 PM - 06:00 PM', label: 'Afternoon Dispatch', orders: Math.round(store.deliveries * 0.15) || 6, sales: `₹${Math.round(revenueToday * 0.15).toLocaleString('en-IN')}`, height: '30%', status: 'LOW' }, { time: '06:00 PM - 10:00 PM', label: 'Evening Surge', orders: Math.round(store.deliveries * 0.2) || 8, sales: `₹${Math.round(revenueToday * 0.25).toLocaleString('en-IN')}`, height: '45%', status: 'HIGH' }, { time: '10:00 PM - 06:00 AM', label: 'Night Prep', orders: Math.round(store.deliveries * 0.05) || 2, sales: `₹${Math.round(revenueToday * 0.05).toLocaleString('en-IN')}`, height: '15%', status: 'LOW' } ]; const activeSlotIndex = hoveredChartIndex !== null ? hoveredChartIndex : 0; const activeSlot = intervalSlots[activeSlotIndex]; // Past 7 Days Log const pastDaysLog = [ { day: 'Wednesday (Today)', orders: store.deliveries || 40, sales: `₹${revenueToday.toLocaleString('en-IN')}`, rate: '98.2%', change: '+4.5%' }, { day: 'Tuesday', orders: Math.round(store.deliveries * 0.9) || 36, sales: `₹${Math.round(revenueToday * 0.88).toLocaleString('en-IN')}`, rate: '100%', change: '+1.2%' }, { day: 'Monday', orders: Math.round(store.deliveries * 0.85) || 34, sales: `₹${Math.round(revenueToday * 0.82).toLocaleString('en-IN')}`, rate: '96.8%', change: '-2.1%' }, { day: 'Sunday', orders: Math.round(store.deliveries * 1.2) || 48, sales: `₹${Math.round(revenueToday * 1.15).toLocaleString('en-IN')}`, rate: '94.5%', change: '+12.4%' }, { day: 'Saturday', orders: Math.round(store.deliveries * 1.15) || 46, sales: `₹${Math.round(revenueToday * 1.12).toLocaleString('en-IN')}`, rate: '99.1%', change: '+8.6%' }, { day: 'Friday', orders: Math.round(store.deliveries * 0.95) || 38, sales: `₹${Math.round(revenueToday * 0.92).toLocaleString('en-IN')}`, rate: '97.4%', change: '+2.0%' }, { day: 'Thursday', orders: Math.round(store.deliveries * 0.9) || 36, sales: `₹${Math.round(revenueToday * 0.89).toLocaleString('en-IN')}`, rate: '100%', change: '+0.5%' } ]; // Inventory mapping (derived + live merges) const getMergedInventory = () => { const rawStock = stockQ.data ?? []; const resolveMetadata = (name: string) => { const nameLower = name.toLowerCase(); let price = 60; let image = 'https://images.unsplash.com/photo-1542838132-92c53300491e?auto=format&fit=crop&w=150&q=80'; if (nameLower.includes('rice')) { price = 1400; image = 'https://images.unsplash.com/photo-1586201375761-83865001e31c?auto=format&fit=crop&w=150&q=80'; } else if (nameLower.includes('oil')) { price = 340; image = 'https://images.unsplash.com/photo-1474979266404-7eaacbcd87c5?auto=format&fit=crop&w=150&q=80'; } else if (nameLower.includes('coffee')) { price = 195; image = 'https://images.unsplash.com/photo-1514432324607-a09d9b4aefdd?auto=format&fit=crop&w=150&q=80'; } else if (nameLower.includes('carrot')) { price = 60; image = 'https://images.unsplash.com/photo-1598170845058-32b9d6a5da37?auto=format&fit=crop&w=150&q=80'; } else if (nameLower.includes('ghee')) { price = 320; image = 'https://images.unsplash.com/photo-1589985270826-4b7bb135bc9d?auto=format&fit=crop&w=150&q=80'; } else if (nameLower.includes('butter')) { price = 260; image = 'https://images.unsplash.com/photo-1589985270826-4b7bb135bc9d?auto=format&fit=crop&w=150&q=80'; } else if (nameLower.includes('salt')) { price = 28; image = 'https://images.unsplash.com/photo-1626132647523-66f5bf380027?auto=format&fit=crop&w=150&q=80'; } else if (nameLower.includes('atta') || nameLower.includes('flour')) { price = 440; image = 'https://images.unsplash.com/photo-1574316071802-0d684efa7bf5?auto=format&fit=crop&w=150&q=80'; } return { price, image }; }; if (rawStock.length > 0) { return rawStock.map((item: any) => { const name = fstr(item.productname) || fstr(item.name) || 'Product Item'; const meta = resolveMetadata(name); return { sku: fstr(item.sku) || fstr(item.productsku) || 'SKU-UNKNOWN', name, category: fstr(item.subcategoryname) || fstr(item.categoryname) || 'Groceries / Staples', stockLevel: Number(item.physicalstock) || Number(item.stock) || 0, maxCapacity: Number(item.maxcapacity) || 500, status: (Number(item.physicalstock) || 0) < 50 ? 'Critical' : (Number(item.physicalstock) || 0) < 150 ? 'Low Stock' : 'Optimal', price: meta.price, image: meta.image }; }); } // High fidelity fallback seeded catalog if live API results are empty return [ { sku: 'RICE-PN-50', name: 'Premium Ponni Rice Bag 25kg', category: 'Staples / Rice', stockLevel: Math.round(baseOrders * 1.2) || 450, maxCapacity: 1000, status: 'Optimal', price: 1400, image: 'https://images.unsplash.com/photo-1586201375761-83865001e31c?auto=format&fit=crop&w=150&q=80' }, { sku: 'ATTA-ASH-10', name: 'Aashirvaad Chakki Atta 10kg', category: 'Staples / Flour', stockLevel: 12, maxCapacity: 120, status: 'Critical', price: 440, image: 'https://images.unsplash.com/photo-1574316071802-0d684efa7bf5?auto=format&fit=crop&w=150&q=80' }, { sku: 'OIL-IDH-05', name: 'Idhayam Sesame Oil Can 5L', category: 'Groceries / Oils', stockLevel: 32, maxCapacity: 150, status: 'Low Stock', price: 340, image: 'https://images.unsplash.com/photo-1474979266404-7eaacbcd87c5?auto=format&fit=crop&w=150&q=80' }, { sku: 'COF-NAR-01', name: 'Narasus Filter Coffee 1kg Pack', category: 'Beverages / Coffee', stockLevel: Math.round(baseOrders * 0.5) || 120, maxCapacity: 300, status: 'Optimal', price: 195, image: 'https://images.unsplash.com/photo-1514432324607-a09d9b4aefdd?auto=format&fit=crop&w=150&q=80' }, { sku: 'MILK-AAV-50', name: 'Aavin Premium Pouch Milk 500ml', category: 'Dairy / Milk', stockLevel: 18, maxCapacity: 200, status: 'Critical', price: 28, image: 'https://images.unsplash.com/photo-1542838132-92c53300491e?auto=format&fit=crop&w=150&q=80' }, { sku: 'OOTY-CARROT-1KG', name: 'Ooty Fresh Quality Carrots 1kg', category: 'Fresh Produce / Veg', stockLevel: 45, maxCapacity: 100, status: 'Low Stock', price: 60, image: 'https://images.unsplash.com/photo-1598170845058-32b9d6a5da37?auto=format&fit=crop&w=150&q=80' }, { sku: 'TURMERIC-200G', name: 'Organic Turmeric Powder 200g', category: 'Groceries / Spices', stockLevel: 280, maxCapacity: 300, status: 'Optimal', price: 90, image: 'https://images.unsplash.com/photo-1596040033229-a9821ebd058d?auto=format&fit=crop&w=150&q=80' }, { sku: 'GHEE-500ML', name: 'Pure Cow Ghee 500ml', category: 'Dairy / Ghee', stockLevel: 75, maxCapacity: 250, status: 'Low Stock', price: 320, image: 'https://images.unsplash.com/photo-1589985270826-4b7bb135bc9d?auto=format&fit=crop&w=150&q=80' } ]; }; // Sync loaded stock to state useEffect(() => { setLocalInventory(getMergedInventory()); }, [stockQ.data, store.locationid]); const inventoryList = localInventory.filter(item => item.name.toLowerCase().includes(stockSearch.toLowerCase()) || item.sku.toLowerCase().includes(stockSearch.toLowerCase()) || item.category.toLowerCase().includes(stockSearch.toLowerCase()) ); // Customer Directory mapping (derived + live merges) const getMergedCustomers = () => { const rawCustomers = customersQ.data ?? []; if (rawCustomers.length > 0) { return rawCustomers.map((c: any) => ({ name: fstr(c.fullname) || `${fstr(c.firstname)} ${fstr(c.lastname)}`.trim() || 'Customer', phone: fstr(c.contactno) || '—', address: fstr(c.address) || 'Coimbatore', ordersCount: Number(c.orderscount) || Math.floor(Math.random() * 8) + 1, totalSpent: `₹${(Number(c.totalspent) || Math.floor(Math.random() * 4000) + 500).toLocaleString('en-IN')}`, lastOrder: '2 Days ago' })); } // High fidelity fallback customer base return [ { name: 'Meenakshi Sundaram', phone: '+91 94432 18942', address: 'Plot 4, Lakshmipuram Ext, RS Puram, Coimbatore - 641002', ordersCount: 42, totalSpent: '₹34,820.00', lastOrder: 'Today, 14:24 PM' }, { name: 'Senthil Kumar VSD', phone: '+91 98421 00234', address: 'Flat 2C, Whispering Palms, Avinashi Road, Peelamedu - 641004', ordersCount: 28, totalSpent: '₹22,910.00', lastOrder: 'Yesterday, 14:10 PM' }, { name: 'Kavitha Ramaswamy', phone: '+91 90035 88921', address: 'No 15, Cross Cut Road, Gandhipuram, Coimbatore - 641012', ordersCount: 19, totalSpent: '₹14,240.00', lastOrder: '2 Days ago' }, { name: 'Dr. Anand Selvapandian', phone: '+91 97890 22104', address: 'Villa 12, Sobha Elanza, Sathy Road, Saravanampatti - 641035', ordersCount: 12, totalSpent: '₹9,800.00', lastOrder: '3 Days ago' }, { name: 'Rajesh Subramaniam', phone: '+91 94421 88902', address: '45, West Club Road, Race Course, Coimbatore - 641018', ordersCount: 64, totalSpent: '₹84,900.00', lastOrder: 'Today, 10:15 AM' }, { name: 'Priya Krishnan', phone: '+91 90432 11094', address: '8C, Royal Arcade, Trichy Road, Singanallur - 641005', ordersCount: 7, totalSpent: '₹4,120.00', lastOrder: '1 Week ago' } ]; }; const customersList = getMergedCustomers().filter(c => c.name.toLowerCase().includes(customerSearch.toLowerCase()) || c.phone.includes(customerSearch) || c.address.toLowerCase().includes(customerSearch.toLowerCase()) ); // Store Alerts specific to the store const storeAlerts = operationalAlerts.filter(alert => alert.title.toLowerCase().includes(store.name.split(' ')[0].toLowerCase()) || alert.details.toLowerCase().includes(store.name.split(' ')[0].toLowerCase()) ); // ── Riders fleet data list ──────────────────────────────────────────────── const activeRiders = [ { name: 'Karthikeyan Radhakrishnan', initial: 'KR', status: 'Delivering', orders: 3, battery: 92, lastPing: '2m ago' }, { name: 'Arun Kumar Chinnasamy', initial: 'AC', status: 'Delivering', orders: 2, battery: 48, lastPing: '10m ago' }, { name: 'Suresh Balasubramaniam', initial: 'SB', status: 'Idle', orders: 0, battery: 84, lastPing: 'Just now' }, { name: 'Manoj Kumar Gowda', initial: 'MG', status: 'Delivering', orders: 1, battery: 14, lastPing: '1m ago' } ]; // Actions simulation handles const handleReplenishSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!replenishModal.item) return; setLocalInventory(prev => prev.map(item => { if (item.sku === replenishModal.item.sku) { const newStock = Math.min(item.stockLevel + replenishQty, item.maxCapacity); return { ...item, stockLevel: newStock, status: newStock < 50 ? 'Critical' : newStock < 150 ? 'Low Stock' : 'Optimal' }; } return item; })); showToast(`Replenishment batch ticket generated for ${replenishQty} units of ${replenishModal.item.name}.`, 'success'); setReplenishModal({ show: false, item: null }); }; // CSV Import simulation trigger const handleStartCsvImport = () => { setImportState('reading'); setTimeout(() => { setImportState('parsing'); setTimeout(() => { setImportState('saving'); setTimeout(() => { const newItems = [ { sku: 'NOODLE-MAG-12', name: 'Maggi 2-Min Masala Noodles 280g', category: 'Snacks / Noodles', stockLevel: 80, maxCapacity: 250, status: 'Optimal', price: 45, image: 'https://images.unsplash.com/photo-1569718212165-3a8278d5f624?auto=format&fit=crop&w=150&q=80' }, { sku: 'DET-SURF-1KG', name: 'Surf Excel Easy Wash Detergent 1kg', category: 'Household / Detergent', stockLevel: 14, maxCapacity: 100, status: 'Critical', price: 165, image: 'https://images.unsplash.com/photo-1542838132-92c53300491e?auto=format&fit=crop&w=150&q=80' }, { sku: 'SALT-TATA-1KG', name: 'Tata Salt Premium Iodized 1kg', category: 'Staples / Salt', stockLevel: 180, maxCapacity: 300, status: 'Optimal', price: 28, image: 'https://images.unsplash.com/photo-1626132647523-66f5bf380027?auto=format&fit=crop&w=150&q=80' } ]; // Add to localInventory state preventing duplicates setLocalInventory(prev => { const filtered = prev.filter(item => !newItems.some(ni => ni.sku === item.sku)); return [...filtered, ...newItems]; }); setImportState('done'); showToast(`CSV Inventory manifest sync complete. 3 items added to local stocks.`, 'success'); }, 800); }, 700); }, 700); }; // Add items from Global Catalog const handleAddGlobalCatalogue = () => { if (selectedGlobalSkus.length === 0) { showToast('Kindly select at least one catalogue item.', 'warning'); return; } const itemsToAdd = GLOBAL_CATALOGUE_ITEMS.filter(item => selectedGlobalSkus.includes(item.sku)).map(item => ({ ...item, stockLevel: Math.floor(Math.random() * 80) + 20, maxCapacity: 200, status: 'Optimal' })); setLocalInventory(prev => { const filtered = prev.filter(item => !itemsToAdd.some(ni => ni.sku === item.sku)); return [...filtered, ...itemsToAdd]; }); showToast(`${itemsToAdd.length} products synced from Master Global Catalogue successfully!`, 'success'); setSelectedGlobalSkus([]); setShowGlobalModal(false); }; const handleExportLedger = () => { showToast(`Generating secure PDF ledger audit reports for ${store.name}...`, 'info'); setTimeout(() => { showToast(`Ledger spreadsheet decrypted and downloaded successfully.`, 'success'); }, 2000); }; const handleStaffBroadcast = () => { const text = prompt('Type notification message to broadcast to all cashiers and staff at this store:'); if (text) { showToast(`Broadcast notification dispatched to all active terminals at ${store.name}.`, 'success'); } }; return (
{/* ── Floating Alert Toast UI ── */} {toast.show && (
{toast.type === 'success' && } {toast.type === 'info' && } {toast.type === 'warning' && } {toast.message}
)} {/* ── Subheader Navigation Bar ── */}
System Sync Active
{/* ── Immersive Analytics Banner (With Store Cover Image & Slate Gradient Overlay) ── */}
{/* Cover Image Background */}
{store.name}
{/* Background decorative glowing circles */}
Outlet Node #{locationid} Operations

{store.name}

{store.zone} Node Lead: {store.staff}
{/* Metrics grid */}
Node Status

{store.status}

● Online Operations

Today's Orders

{Math.max(store.deliveries, store.orders ?? 0)}

Incoming Volume

Today's Dispatches

{store.deliveries}

Dispatched Deliveries

Fulfillment Rate

{Math.max(store.deliveries, store.orders ?? 0) > 0 ? `${Math.min(100, Math.round((store.deliveries / Math.max(store.deliveries, store.orders ?? 0)) * 100))}%` : '100%'}

Outlet OTIF Rate

{/* ── Visual Glass-look Tab Controls ── */}
{/* ── TAB PAYLOAD AREA ── */} {activeTab === 'overview' && (
{/* Top Metric Cards */}
OTIF Fulfillment

98.2%

▲ 0.4% vs past week
Est. Revenue

₹{revenueToday.toLocaleString('en-IN')}

▲ 12.4% growth threshold
Total Dispatches

{store.deliveries}

Daily cap quota: 200
Active Fleet

4 Riders

3 dispatches live
{/* Interactive Timeline Pipeline Flow */}

Dispatch Flow Pipeline

Audit orders & revenue progression by selecting nodes along the daily operational path.

Time Flow
{/* The Pipeline Line and Nodes */}
{/* Flow track base line */}
{/* Glowing progress fill line */}
{/* Operational Nodes */}
{intervalSlots.map((slot, index) => { const isActive = activeSlotIndex === index; const isHovered = hoveredChartIndex === index; // Status colors let dotColor = 'bg-indigo-500'; let ringColor = 'border-indigo-100 hover:border-indigo-300'; let rippleColor = 'bg-indigo-400'; if (slot.status === 'PEAK') { dotColor = 'bg-rose-500'; ringColor = isActive ? 'border-rose-300 bg-rose-50/50' : 'border-rose-100 hover:border-rose-300'; rippleColor = 'bg-rose-400'; } else if (slot.status === 'HIGH') { dotColor = 'bg-amber-500'; ringColor = isActive ? 'border-amber-300 bg-amber-50/50' : 'border-amber-100 hover:border-amber-300'; rippleColor = 'bg-amber-400'; } else if (slot.status === 'LOW') { dotColor = 'bg-emerald-500'; ringColor = isActive ? 'border-emerald-300 bg-emerald-50/50' : 'border-emerald-100 hover:border-emerald-300'; rippleColor = 'bg-emerald-400'; } return (
setHoveredChartIndex(index)} onMouseLeave={() => setHoveredChartIndex(null)} onClick={() => setHoveredChartIndex(index)} className="relative flex flex-col items-center cursor-pointer group z-10" > {/* Time label above node */}
{slot.time.split(' - ')[0]}
{/* Node Circle */}
{/* Pulse ripples if active */} {isActive && ( )} {/* Inner dot */}
{/* Label and dispatches below node */}
{slot.label.split(' ')[0]} {slot.orders} orders
); })}
{/* Interactive Glassmorphic Stats audit drawer below chart */}
Audit Interval {activeSlot.label} {activeSlot.time}
Dispatches {activeSlot.orders} dispatches
Est. Revenue {activeSlot.sales}
Load level {activeSlot.status}
{/* Quick Actions Console */}

Node Operations Command

Automated actions for local outlet hubs.

{/* Past 7 days Table */}

Past 7 Days Ledger Log

{pastDaysLog.map((dayLog, index) => ( ))}
Day Period Dispatches Revenue Volume OTIF fulfillment
{dayLog.day} {dayLog.orders} dispatches {dayLog.sales} {dayLog.rate}
{/* Live Rider fleet list */}

Active Rider Fleet

Live status and battery tracking of assigned riders.

{activeRiders.map((rider, index) => (
{rider.initial}

{rider.name.split(' ')[0]} {rider.name.split(' ').slice(-1)[0]}

{rider.status} · {rider.orders} orders

{rider.battery}%
))}
)} {activeTab === 'inventory' && (
{/* Inventory search, metrics & catalogue tools */}
setStockSearch(e.target.value)} className="w-full pl-9 pr-4 py-2.5 border border-[#e2e8f0] rounded-xl text-xs outline-none bg-[#f8fafc] focus:bg-white focus:ring-1 focus:ring-[#581c87] transition-all" />
{/* Actions for Store Catalogue Management */}
{inventoryList.filter(item => item.status === 'Critical').length} Critical {inventoryList.filter(item => item.status === 'Low Stock').length} Low
{/* Stocks statement Table */}

Product Stock Levels & Catalog

Live list
{inventoryList.length === 0 ? ( ) : ( inventoryList.map((item, index) => { const capacityPct = Math.round((item.stockLevel / item.maxCapacity) * 100); // Pretty category badge mapping const isStaples = item.category.toLowerCase().includes('staple') || item.category.toLowerCase().includes('rice'); const isBeverages = item.category.toLowerCase().includes('bev'); const isProduce = item.category.toLowerCase().includes('produce') || item.category.toLowerCase().includes('fresh') || item.category.toLowerCase().includes('carrot'); const isDairy = item.category.toLowerCase().includes('dairy'); return ( ); }) )}
Product Item SKU Ref Category Est. Price Capacity Load Status Replenish
No product stocks found matching the search keyword.
{item.name} {item.name}
{item.sku} {item.category.split(' / ').slice(-1)[0]} ₹{item.price.toLocaleString('en-IN')}
{item.stockLevel} / {item.maxCapacity}
● {item.status}
)} {activeTab === 'customers' && (
{/* Customer directory search and metrics */}
setCustomerSearch(e.target.value)} className="w-full pl-9 pr-4 py-2.5 border border-[#e2e8f0] rounded-xl text-xs outline-none bg-[#f8fafc] focus:bg-white focus:ring-1 focus:ring-[#581c87] transition-all" />
Retention Rate: 88.4% AOV: ₹1,580 CSAT Index: 4.9/5
{/* Customer list directory */}

Active Customer Directory

Customer registry
{customersList.length === 0 ? ( ) : ( customersList.map((c, idx) => { const initials = c.name.split(' ').map((n: string) => n[0]).join(''); const gradients = [ 'from-purple-500 to-indigo-500 text-white', 'from-rose-500 to-pink-500 text-white', 'from-sky-500 to-indigo-500 text-white', 'from-emerald-500 to-teal-500 text-white', 'from-amber-500 to-orange-500 text-white' ]; const avatarGrad = gradients[idx % gradients.length]; return ( ); }) )}
Customer Profile Contact Details Delivery Address Total Dispatches Gross Volume Spent Audit CRM Actions
No customer accounts found matching search keyword.
{initials}
{c.name}
{c.phone} {c.address} {c.ordersCount} orders {c.totalSpent}
)} {activeTab === 'orders' && ( )} {/* ── Replenishment Modal Dialog Overlay ── */} {replenishModal.show && replenishModal.item && (
{ if (e.target === e.currentTarget) setReplenishModal({ show: false, item: null }); }} >

Replenish Inventory Stack

Replenishing Item

{replenishModal.item.name}

SKU: {replenishModal.item.sku}

setReplenishQty(Number(e.target.value))} className="w-full border border-[#e2e8f0] rounded-xl p-sm bg-[#f8fafc] focus:bg-white outline-none focus:ring-1 focus:ring-[#581c87] font-bold" min={1} required />
)} {/* ── Manual CSV Import simulated Modal ── */} {showImportModal && (
{ if (e.target === e.currentTarget && importState !== 'reading' && importState !== 'parsing' && importState !== 'saving') setShowImportModal(false); }} >

Import Catalogue Manifest

Upload or drop your CSV stock ledger files below to commission new items into the {store.name} local registry database.

{importState === 'idle' && (

Click here to import file

Mock file: coimbatore_manifest_v2.csv

)} {['reading', 'parsing', 'saving'].includes(importState) && (

{importState === 'reading' && 'Reading uploaded CSV sheets...'} {importState === 'parsing' && 'Scanning item SKU catalog mapping...'} {importState === 'saving' && 'Syncing manifest entries with local inventory...'}

Kindly keep this window open while processing dispatches.

)} {importState === 'done' && (

Ledger Imported Successfully

3 new SKU codes commissioned successfully into local registry.

)}
)} {/* ── Choose from Global Catalogue Modal ── */} {showGlobalModal && (
{ if (e.target === e.currentTarget) setShowGlobalModal(false); }} >

Select Products from Master Catalogue

Choose master items from the national database to stock and commission locally at {store.name}.

{GLOBAL_CATALOGUE_ITEMS.map((item) => { const isChecked = selectedGlobalSkus.includes(item.sku); return (
{ setSelectedGlobalSkus(prev => isChecked ? prev.filter(s => s !== item.sku) : [...prev, item.sku] ); }} className="py-2.5 flex items-center justify-between gap-sm cursor-pointer select-none hover:bg-zinc-50/50 rounded-lg px-1 transition-colors" >
{}} // handled by row click className="w-4 h-4 rounded text-[#581c87] border-[#e2e8f0] focus:ring-purple-500" /> {item.name}

{item.name}

{item.category} · SKU: {item.sku}

₹{item.price}
); })}
)} {/* ── Customer CRM Profile Side Drawer Overlay ── */} {selectedCustomer && (
{ if (e.target === e.currentTarget) setSelectedCustomer(null); }} >
{/* Header info */}
{selectedCustomer.name.split(' ').map((n: string) => n[0]).join('')}

{selectedCustomer.name}

High Value Account
{/* Profile Drawer Details Container */}
{/* Contact info list card */}
Profile Registry Details
{selectedCustomer.phone}
{selectedCustomer.name.toLowerCase().replace(/[^a-z]/g, '')}@gmail.com
{selectedCustomer.address}
{/* Metric grid info overlay */}
Dispatches {selectedCustomer.ordersCount} dispatches
Gross spend {selectedCustomer.totalSpent}
CSAT Score 5.0 / 5.0
{/* Simulated Customer Order History ledger */}
Past Interactions & Orders
DM-ORD-2091

{selectedCustomer.lastOrder}

{selectedCustomer.totalSpent} Delivered
DM-ORD-1982

1 Month ago

₹1,240.00 Delivered
DM-ORD-1721

2 Months ago

₹2,840.00 Delivered
{/* Quick Actions in Side Drawer */}
)}
); }