import React, { useState, useEffect } from 'react';
import {
Coffee,
Settings,
Barcode,
CreditCard,
Users,
Printer,
Plus,
Minus,
Trash2,
Search,
Check,
Sparkles,
TrendingUp,
Package,
Clock,
UtensilsCrossed,
Sliders,
FileSpreadsheet,
Tv,
Bell,
ChefHat,
Database,
Building2,
HardDrive,
Download,
FolderOpen,
MapPin,
Lock,
Unlock,
AlertTriangle,
RefreshCw,
Smartphone,
Tablet,
QrCode,
Tags,
CheckCircle2,
UserCheck,
RotateCcw
} from 'lucide-react';
const BRAND_SETTINGS = {
businessName: "La Greenzaa",
subBranding: "Restaurant & Cafe",
developerBrand: "Umang Developers",
currency: "₹",
taxRate: 5,
enableServiceCharge: true,
serviceChargePercent: 2.5
};
const currencySymbol = BRAND_SETTINGS.currency;
const INITIAL_PRODUCTS = [
{ id: 'p1', name: 'Premium Espresso', category: 'Hot Coffee', price: 120, costPrice: 30, tax: '5%', sku: 'HOT-ESP-01', image: '☕', stockOnHand: 80, committedStock: 0, availableForSale: 80, openingStock: 100, unit: 'pcs', manufacturer: 'La Greenzaa Farms', reorderPoint: 15 },
{ id: 'p2', name: 'Classic Cappuccino', category: 'Hot Coffee', price: 150, costPrice: 40, tax: '5%', sku: 'HOT-CAP-02', image: '☕', stockOnHand: 120, committedStock: 4, availableForSale: 116, openingStock: 150, unit: 'pcs', manufacturer: 'La Greenzaa Farms', reorderPoint: 20 },
{ id: 'p3', name: 'Irish Cold Coffee', category: 'Cold Coffee', price: 180, costPrice: 50, tax: '12%', sku: 'CLD-COF-03', image: '🥤', stockOnHand: 140, committedStock: 20, availableForSale: 120, openingStock: 200, unit: 'pcs', manufacturer: 'La Greenzaa Roasters', reorderPoint: 30 },
{ id: 'p4', name: 'Lemon Infusion Iced Tea', category: 'Teas & Infusions', price: 120, costPrice: 25, tax: '5%', sku: 'TEA-LIT-04', image: '🍹', stockOnHand: 95, committedStock: 5, availableForSale: 90, openingStock: 100, unit: 'pcs', manufacturer: 'Nectar Teas', reorderPoint: 10 },
{ id: 'p5', name: 'Mango Blast Smoothie', category: 'Smoothies', price: 210, costPrice: 60, tax: '12%', sku: 'SMO-MAN-05', image: '🥭', stockOnHand: 60, committedStock: 2, availableForSale: 58, openingStock: 80, unit: 'pcs', manufacturer: 'La Greenzaa Orchard', reorderPoint: 8 },
{ id: 'p6', name: 'Avocado Cream Smoothie', category: 'Smoothies', price: 250, costPrice: 80, tax: '12%', sku: 'SMO-AVO-06', image: '🥑', stockOnHand: 45, committedStock: 0, availableForSale: 45, openingStock: 50, unit: 'pcs', manufacturer: 'La Greenzaa Orchard', reorderPoint: 5 },
{ id: 'p7', name: 'Paneer Tikka Pizza', category: 'Pizzas', price: 250, costPrice: 90, tax: '12%', sku: 'PIZ-PTP-09', image: '🍕', stockOnHand: 50, committedStock: 10, availableForSale: 40, openingStock: 60, unit: 'pcs', manufacturer: 'La Greenzaa Main Kitchen', reorderPoint: 12 },
{ id: 'p8', name: 'Dark Chocolate Cake', category: 'Baked Goods', price: 180, costPrice: 55, tax: '5%', sku: 'BAK-DCC-12', image: '🍫', stockOnHand: 40, committedStock: 2, availableForSale: 38, openingStock: 50, unit: 'pcs', manufacturer: 'La Greenzaa Bakery', reorderPoint: 6 }
];
const CATEGORIES = ['All Items', 'Hot Coffee', 'Cold Coffee', 'Teas & Infusions', 'Smoothies', 'Baked Goods', 'Pizzas'];
const INITIAL_CUSTOMERS = [
{ id: 'c1', name: "Mr. Devesh Rajguru", phone: "+91 99092 12345", outstandingBalance: 200.00, loyaltyPoints: 340, email: "devesh@rajguru.com" },
{ id: 'c2', name: "Aarav Patel", phone: "+91 98765 11223", outstandingBalance: 0.00, loyaltyPoints: 120, email: "aarav@patel.com" },
{ id: 'c3', name: "Neha Sharma", phone: "+91 91122 33445", outstandingBalance: 150.00, loyaltyPoints: 450, email: "neha@sharma.in" }
];
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
console.error("ErrorBoundary caught a runtime exception:", error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
La Greenzaa POS Lifecycle Recovery
The application state experienced a validation error. Click below to restore database properties back to original defaults.
{
localStorage.clear();
window.location.reload();
}}
className="px-6 py-3 bg-emerald-600 hover:bg-emerald-700 text-white font-extrabold text-xs rounded-xl flex items-center space-x-2 shadow-lg"
>
Reset Local Cache & Re-Initialize Database
);
}
return this.props.children;
}
}
function POSApp() {
// --- APPLICATION STATE DIRECTORIES ---
// roles: kiosk | qr_ordering | waiter | biller | kds | status | owner | inventory
const [currentRole, setCurrentRole] = useState('kiosk');
// Data State Pools
const [products, setProducts] = useState(INITIAL_PRODUCTS);
const [orders, setOrders] = useState([]);
const [customers, setCustomers] = useState(INITIAL_CUSTOMERS);
const [selectedInventoryItem, setSelectedInventoryItem] = useState(null);
const [selectedCustomer, setSelectedCustomer] = useState(INITIAL_CUSTOMERS[0]);
// Terminal Cart Pools
const [kioskCart, setKioskCart] = useState([]);
const [waiterCart, setWaiterCart] = useState([]);
const [cashierCart, setCashierCart] = useState([]);
const [qrCart, setQrCart] = useState([]);
// Modifiers System
const [activeModifierItem, setActiveModifierItem] = useState(null); // { cartKey, modifiers: [] }
const [showModifierModal, setShowModifierModal] = useState(false);
const [tempModifiers, setTempModifiers] = useState([]);
// Waiter Dedicated Variables
const [waiterTable, setWaiterTable] = useState('Table 1');
const [waiterGuestName, setWaiterGuestName] = useState('Table Guest');
// Kiosk Specific Navigation Flow
const [kioskMode, setKioskMode] = useState('welcome'); // welcome | select-dining-mode | select-table | catalog | finished
const [kioskDiningMode, setKioskDiningMode] = useState('Dine-In Self-Service');
const [kioskTable, setKioskTable] = useState('Table 1');
const [kioskPaymentMode, setKioskPaymentMode] = useState('Pay at Counter');
const [kioskLastOrder, setKioskLastOrder] = useState(null);
// Table QR Self-Ordering Simulator State
const [qrTable, setQrTable] = useState('Table 3');
const [qrOrderPlaced, setQrOrderPlaced] = useState(false);
// Cashier Terminal Specific States
const [selectedUnpaidKioskOrder, setSelectedUnpaidKioskOrder] = useState(null);
const [cashierSearchQuery, setCashierSearchQuery] = useState('');
const [cashierSelectedCategory, setCashierSelectedCategory] = useState('All Items');
const [activeCategory, setActiveCategory] = useState('All Items');
const [billingOrderType, setBillingOrderType] = useState('Walk-In');
// New Smart Recall Tracking State
const [recalledOrderId, setRecalledOrderId] = useState(null);
const [orderRecallFilter, setOrderRecallFilter] = useState('');
// PIN Locking System (Owner: 8888, Inventory: 9999)
const [pinInput, setPinInput] = useState('');
const [securityTarget, setSecurityTarget] = useState(null); // 'owner' | 'inventory'
const [authorizedOwner, setAuthorizedOwner] = useState(false);
const [authorizedInventory, setAuthorizedInventory] = useState(false);
const [lockErrorMessage, setLockErrorMessage] = useState('');
// KOT Thermal Printer Dialog
const [activeKOTPrint, setActiveKOTPrint] = useState(null);
const [showPrintModal, setShowPrintModal] = useState(false);
// Custom Notifications / Alert Box
const [systemAlert, setSystemAlert] = useState(null); // { type: 'success'|'error', text: '' }
// Customer registration state (Biller-accessible)
const [showRegisterCustomerModal, setShowRegisterCustomerModal] = useState(false);
const [newCustName, setNewCustName] = useState('');
const [newCustPhone, setNewCustPhone] = useState('');
const [newCustEmail, setNewCustEmail] = useState('');
// KDS Advanced Station Filtering & Recalled order reference
const [kdsStationFilter, setKdsStationFilter] = useState('All Stations');
const [recentlyCompletedOrder, setRecentlyCompletedOrder] = useState(null);
// Barcode Printer Modal
const [selectedBarcodeItem, setSelectedBarcodeItem] = useState(null);
const [showBarcodePrintModal, setShowBarcodePrintModal] = useState(false);
// Custom Deletion Confirmation Modal State
const [confirmDeleteId, setConfirmDeleteId] = useState(null);
// --- OFFLINE DATA LOAD ON MOUNT ---
useEffect(() => {
try {
const storedCatalog = localStorage.getItem('lg_catalog_state_v3');
const storedOrders = localStorage.getItem('lg_orders_state_v3');
const storedCustomers = localStorage.getItem('lg_customers_state_v3');
if (storedCatalog) {
const parsedCatalog = JSON.parse(storedCatalog);
if (parsedCatalog && parsedCatalog.length > 0) {
setProducts(parsedCatalog);
setSelectedInventoryItem(parsedCatalog[0]);
}
} else {
setProducts(INITIAL_PRODUCTS);
setSelectedInventoryItem(INITIAL_PRODUCTS[0]);
}
if (storedOrders) {
const parsedOrders = JSON.parse(storedOrders);
if (parsedOrders) setOrders(parsedOrders);
} else {
setOrders([
{ id: 'ord-101', tokenNo: 101, items: [{ name: 'Classic Cappuccino', quantity: 2, price: 150, modifiers: ['No Sugar', 'Keep Vegan'] }], grandTotal: 300, customerName: 'Mr. Devesh Rajguru', orderType: 'Dine-In Self-Service', tableNo: 'Table 4', status: 'Pending', timestamp: '10:12 AM', station: 'Beverage Bar', paid: true, createdAt: Date.now() - 360000 },
{ id: 'ord-102', tokenNo: 102, items: [{ name: 'Paneer Tikka Pizza', quantity: 1, price: 250, modifiers: ['Extra Cheese'] }], grandTotal: 250, customerName: 'Aarav Patel', orderType: 'Takeaway Parcel', tableNo: 'N/A', status: 'Cooking', timestamp: '10:20 AM', station: 'Main Kitchen', paid: false, createdAt: Date.now() - 120000 }
]);
}
if (storedCustomers) {
const parsedCustomers = JSON.parse(storedCustomers);
if (parsedCustomers) setCustomers(parsedCustomers);
} else {
setCustomers(INITIAL_CUSTOMERS);
}
} catch (e) {
console.error("Local sync bypassed safely: ", e);
}
}, []);
const saveStateToStorage = (updatedCatalog, updatedOrders, updatedCustomers) => {
try {
if (updatedCatalog) localStorage.setItem('lg_catalog_state_v3', JSON.stringify(updatedCatalog));
if (updatedOrders) localStorage.setItem('lg_orders_state_v3', JSON.stringify(updatedOrders));
if (updatedCustomers) localStorage.setItem('lg_customers_state_v3', JSON.stringify(updatedCustomers));
} catch (e) {
console.error("Local writes failed: ", e);
}
};
const showNotification = (text, type = 'success') => {
setSystemAlert({ text, type });
setTimeout(() => setSystemAlert(null), 4500);
};
const [timerTick, setTimerTick] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setTimerTick(prev => prev + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
const getElapsedTimeText = (createdAt) => {
if (!createdAt) return "00:00";
const elapsedSeconds = Math.floor((Date.now() - createdAt) / 1000);
if (elapsedSeconds < 0) return "00:00";
const minutes = Math.floor(elapsedSeconds / 60);
const seconds = elapsedSeconds % 60;
return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
};
// --- AUDIO ALERTS ---
const triggerAudioChime = () => {
try {
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const osc = audioCtx.createOscillator();
const gainNode = audioCtx.createGain();
osc.connect(gainNode);
gainNode.connect(audioCtx.destination);
osc.type = 'sine';
osc.frequency.setValueAtTime(587.33, audioCtx.currentTime); // D5
osc.frequency.setValueAtTime(698.46, audioCtx.currentTime + 0.15); // F5
gainNode.gain.setValueAtTime(0.08, audioCtx.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.35);
osc.start();
osc.stop(audioCtx.currentTime + 0.35);
} catch (e) {
console.log("Audio alert chime executed.");
}
};
// --- REAL-TIME OFFLINE DATA EXPORT BACKUPS ---
const handleDownloadStats = () => {
try {
const fileReport = {
meta: {
brand: BRAND_SETTINGS.businessName,
developer: BRAND_SETTINGS.developerBrand,
timestamp: new Date().toISOString(),
format: "La Greenzaa Central Sales Audit Backup"
},
financials: {
grossMonthlySales: 445790.00,
ordersProcessed: orders?.length || 0,
inventoryUniqueSKUs: products?.length || 0
},
ordersLedger: orders,
activeInventoryLevels: products,
registeredCustomers: customers
};
const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(fileReport, null, 2));
const downloadAnchor = document.createElement('a');
downloadAnchor.setAttribute("href", dataStr);
downloadAnchor.setAttribute("download", `la_greenzaa_offline_report_${new Date().toISOString().split('T')[0]}.json`);
document.body.appendChild(downloadAnchor);
downloadAnchor.click();
downloadAnchor.remove();
showNotification("SaaS offline backup logs generated and downloaded!");
} catch (err) {
console.error("Backup log write failed:", err);
showNotification("Log write exception occurred", "error");
}
};
const handleVerifyPINSubmit = (e) => {
e.preventDefault();
if (securityTarget === 'owner' && pinInput === '8888') {
setAuthorizedOwner(true);
setCurrentRole('owner');
setSecurityTarget(null);
setLockErrorMessage('');
showNotification("Authorized Owner Panel Successfully.");
} else if (securityTarget === 'inventory' && pinInput === '9999') {
setAuthorizedInventory(true);
setCurrentRole('inventory');
setSecurityTarget(null);
setLockErrorMessage('');
showNotification("Authorized Inventory Suite Successfully.");
} else {
setLockErrorMessage('Incorrect Passcode. Access Restricted.');
setPinInput('');
}
};
const handleSwitchDeviceRole = (role) => {
if (role === 'owner') {
if (authorizedOwner) setCurrentRole('owner');
else {
setSecurityTarget('owner');
setPinInput('');
setLockErrorMessage('');
}
} else if (role === 'inventory') {
if (authorizedInventory) setCurrentRole('inventory');
else {
setSecurityTarget('inventory');
setPinInput('');
setLockErrorMessage('');
}
} else {
setCurrentRole(role);
}
};
// --- MULTI-TERMINAL CART MANAGERS ---
const getDeviceCart = (role) => {
if (role === 'kiosk') return kioskCart;
if (role === 'waiter') return waiterCart;
if (role === 'cashier') return cashierCart;
if (role === 'qr_ordering') return qrCart;
return [];
};
const getDeviceCartSetter = (role) => {
if (role === 'kiosk') return setKioskCart;
if (role === 'waiter') return setWaiterCart;
if (role === 'cashier') return setCashierCart;
if (role === 'qr_ordering') return setQrCart;
return () => {};
};
const handleAddCartItemWithModifiers = (product, role) => {
const cart = getDeviceCart(role);
const setCart = getDeviceCartSetter(role);
const cartKey = `${product.id}-${Date.now()}`;
const newCartItem = {
cartKey,
product,
quantity: 1,
modifiers: []
};
setCart([...cart, newCartItem]);
// Open modifiers selection box
setActiveModifierItem({ cartKey, role, modifiers: [] });
setTempModifiers([]);
setShowModifierModal(true);
};
const handleApplyModifiers = () => {
if (!activeModifierItem) return;
const setCart = getDeviceCartSetter(activeModifierItem.role);
setCart(prev => prev.map(item => {
if (item.cartKey === activeModifierItem.cartKey) {
return { ...item, modifiers: tempModifiers };
}
return item;
}));
setShowModifierModal(false);
setActiveModifierItem(null);
showNotification("Preparation modifiers applied successfully!");
};
const handleUpdateCartQuantity = (cartKey, delta, role) => {
const cart = getDeviceCart(role);
const setCart = getDeviceCartSetter(role);
const updated = cart.map(item => {
if (item.cartKey === cartKey) {
const nextQty = item.quantity + delta;
return nextQty > 0 ? { ...item, quantity: nextQty } : null;
}
return item;
}).filter(Boolean);
setCart(updated);
};
const handleRemoveItemFromCart = (cartKey, role) => {
const setCart = getDeviceCartSetter(role);
setCart(prev => prev.filter(item => item.cartKey !== cartKey));
};
// Safe Math Aggregates
const calculateAggregateBill = (cart) => {
if (!cart || cart.length === 0) {
return { subtotal: 0, tax: 0, serviceCharge: 0, grandTotal: 0, roundOff: 0 };
}
const subtotal = cart.reduce((sum, item) => sum + (item.product.price * item.quantity), 0);
const tax = Math.round((subtotal * 0.05) * 100) / 100;
const serviceCharge = Math.round((subtotal * 0.025) * 100) / 100;
const rawTotal = subtotal + tax + serviceCharge;
const grandTotal = Math.round(rawTotal);
const roundOff = Math.round((grandTotal - rawTotal) * 100) / 100;
return { subtotal, tax, serviceCharge, grandTotal, roundOff };
};
const kioskTotals = calculateAggregateBill(kioskCart);
const waiterTotals = calculateAggregateBill(waiterCart);
const cashierTotals = calculateAggregateBill(cashierCart);
const qrTotals = calculateAggregateBill(qrCart);
const handleExecuteCheckout = (role, paymentMethod, type, tableNum = 'N/A', name = 'Walk-In Guest', explicitlyUnpaid = false) => {
const cart = getDeviceCart(role);
const setCart = getDeviceCartSetter(role);
if (cart.length === 0) return;
const totals = calculateAggregateBill(cart);
// Decrement local inventory stock
const updatedProducts = products.map(p => {
const match = cart.find(c => c.product.id === p.id);
if (match) {
const newOnHand = Math.max(0, p.stockOnHand - match.quantity);
return {
...p,
stockOnHand: newOnHand,
availableForSale: Math.max(0, newOnHand - p.committedStock)
};
}
return p;
});
const tokenNo = orders.length > 0 ? Math.max(...orders.map(o => o.tokenNo)) + 1 : 101;
const timestamp = new Date().toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true });
const newKOT = {
id: `ord-${Date.now()}`,
tokenNo,
items: cart.map(item => ({
name: item.product.name,
quantity: item.quantity,
price: item.product.price,
modifiers: item.modifiers || []
})),
grandTotal: totals.grandTotal,
customerName: name,
orderType: type,
tableNo: tableNum,
status: explicitlyUnpaid ? 'Pending Payment' : 'Pending',
timestamp,
station: cart.some(c => c.product.category === 'Pizzas') ? 'Main Kitchen' : 'Beverage Bar',
paid: !explicitlyUnpaid,
createdAt: Date.now()
};
const updatedOrders = [...orders, newKOT];
// Update Loyalty points if matched with a registered customer
const updatedCustomers = customers.map(cust => {
if (cust.name === name) {
return { ...cust, loyaltyPoints: cust.loyaltyPoints + Math.round(totals.grandTotal / 10) };
}
return cust;
});
setProducts(updatedProducts);
setOrders(updatedOrders);
setCustomers(updatedCustomers);
saveStateToStorage(updatedProducts, updatedOrders, updatedCustomers);
if (role === 'kiosk') {
setKioskLastOrder(newKOT);
setKioskMode('finished');
setKioskCart([]);
} else if (role === 'qr_ordering') {
setQrOrderPlaced(true);
setQrCart([]);
} else {
// If we are settling a recalled order at Biller desk, mark the original pending order as settled/completed
if (role === 'cashier' && recalledOrderId) {
setOrders(prev => prev.map(o => o.id === recalledOrderId ? { ...o, status: 'Pending', paid: true } : o));
setRecalledOrderId(null);
}
setActiveKOTPrint(newKOT);
setShowPrintModal(true);
setCart([]);
}
showNotification(`Token #${tokenNo} submitted successfully!`);
};
// Smart Recall Order Handler
const handleSmartRecallOrder = (order) => {
// 1. Convert order items to cashier cart schema
const loadedCart = order.items.map(item => {
const matchedProduct = products.find(p => p.name === item.name) || products[0];
return {
cartKey: `${matchedProduct.id}-${Date.now()}-${Math.random()}`,
product: matchedProduct,
quantity: item.quantity,
modifiers: item.modifiers || []
};
});
setCashierCart(loadedCart);
setBillingOrderType(order.orderType);
setRecalledOrderId(order.id);
// 2. Auto-bind table configuration if table service
if (order.tableNo && order.tableNo !== 'N/A') {
setWaiterTable(order.tableNo);
}
// 3. Smart Search & Bind Loyalty Profile by Name or Contact Phone
const matchedCustomer = customers.find(c =>
c.name.toLowerCase().includes(order.customerName.toLowerCase()) ||
(order.customerPhone && c.phone.includes(order.customerPhone)) ||
c.phone.includes(order.customerName)
);
if (matchedCustomer) {
setSelectedCustomer(matchedCustomer);
showNotification(`Order Recalled & Linked to Loyalty Account: ${matchedCustomer.name}`);
} else {
setSelectedCustomer(customers[0]); // Fallback to Default Guest Account
showNotification(`Order Recalled. Guest account auto-assigned.`);
}
};
// Cashier Settle Pending Kiosk Orders
const handleSettleUnpaidKioskOrder = (orderId) => {
const updated = orders.map(o => {
if (o.id === orderId) {
return { ...o, status: 'Pending', paid: true };
}
return o;
});
setOrders(updated);
setSelectedUnpaidKioskOrder(null);
saveStateToStorage(products, updated, customers);
showNotification("Kiosk order paid successfully! Routed to Kitchen Display.");
};
const handleRegisterCustomerSubmit = (e) => {
e.preventDefault();
if (!newCustName || !newCustPhone) {
showNotification("Name and Phone are mandatory.", "error");
return;
}
const isDuplicate = customers.some(c => c.phone === newCustPhone);
if (isDuplicate) {
showNotification("A loyalty account with this phone number already exists.", "error");
return;
}
const newProfile = {
id: `c-${Date.now()}`,
name: newCustName,
phone: newCustPhone,
email: newCustEmail || "N/A",
outstandingBalance: 0.00,
loyaltyPoints: 50 // Signup welcome points
};
const updatedCustomers = [...customers, newProfile];
setCustomers(updatedCustomers);
setSelectedCustomer(newProfile); // Auto-bind as the selected billing customer
saveStateToStorage(products, orders, updatedCustomers);
// Reset form states
setNewCustName('');
setNewCustPhone('');
setNewCustEmail('');
setShowRegisterCustomerModal(false);
showNotification(`Registered loyalty account: ${newProfile.name}!`);
};
// KDS State Transition Handlers with Recalled Safety Cache
const handleKdsCompleteTicket = (orderId) => {
const orderToMark = orders.find(o => o.id === orderId);
if (orderToMark) {
setRecentlyCompletedOrder(orderToMark);
}
handleUpdateOrderStatus(orderId, 'Completed');
showNotification("KDS Ticket Completed.");
};
const handleKdsRecallCompletedTicket = () => {
if (!recentlyCompletedOrder) {
showNotification("No recently completed order to recall.", "error");
return;
}
handleUpdateOrderStatus(recentlyCompletedOrder.id, 'Ready');
setRecentlyCompletedOrder(null);
showNotification("Re-opened recently completed order!");
};
// Add Item (Inventory Dashboard)
const handleAddCatalogItem = (e) => {
e.preventDefault();
const fd = new FormData(e.target);
const newProduct = {
id: `p-${Date.now()}`,
name: fd.get('name') || 'Fresh Item',
category: fd.get('category') || 'Hot Coffee',
price: parseFloat(fd.get('price')) || 100,
costPrice: parseFloat(fd.get('costPrice')) || 30,
tax: fd.get('tax') || '5%',
sku: fd.get('sku') || 'SKU-NEW',
image: fd.get('image') || '☕',
stockOnHand: parseInt(fd.get('stock')) || 50,
committedStock: 0,
availableForSale: parseInt(fd.get('stock')) || 50,
openingStock: parseInt(fd.get('stock')) || 50,
unit: 'pcs',
manufacturer: 'La Greenzaa Farms',
reorderPoint: 10
};
const updatedProducts = [newProduct, ...products];
setProducts(updatedProducts);
saveStateToStorage(updatedProducts, orders, customers);
e.target.reset();
showNotification("Product saved successfully to master database.");
};
const handleConfirmRemoveProduct = (id) => {
setConfirmDeleteId(id);
};
const executeDeleteProduct = () => {
if (!confirmDeleteId) return;
const updatedProducts = products.filter(p => p.id !== confirmDeleteId);
setProducts(updatedProducts);
saveStateToStorage(updatedProducts, orders, customers);
setConfirmDeleteId(null);
showNotification("Product deleted from catalog.");
};
return (
{/* ========================================================= */}
{/* SYSTEM HEADER AND SCREEN SELECTOR */}
{/* ========================================================= */}
La Greenzaa - Terminal Simulator
Developed by Umang Developers
{/* Global Notifications Alert Widget */}
{systemAlert && (
{systemAlert.text}
)}
handleSwitchDeviceRole('kiosk')}
className={`px-3 py-1 rounded-md transition-all ${currentRole === 'kiosk' ? 'bg-emerald-600 text-white font-bold' : 'text-slate-400 hover:text-white'}`}
>
1. Self Kiosk
handleSwitchDeviceRole('qr_ordering')}
className={`px-3 py-1 rounded-md transition-all flex items-center space-x-1 ${currentRole === 'qr_ordering' ? 'bg-emerald-600 text-white font-bold' : 'text-slate-400 hover:text-white'}`}
>
2. Table QR Self-Order
handleSwitchDeviceRole('waiter')}
className={`px-3 py-1 rounded-md transition-all ${currentRole === 'waiter' ? 'bg-emerald-600 text-white font-bold' : 'text-slate-400 hover:text-white'}`}
>
3. Waiter Tablet
handleSwitchDeviceRole('biller')}
className={`px-3 py-1 rounded-md transition-all ${currentRole === 'biller' ? 'bg-emerald-600 text-white font-bold' : 'text-slate-400 hover:text-white'}`}
>
4. Biller POS
handleSwitchDeviceRole('kds')}
className={`px-3 py-1 rounded-md transition-all ${currentRole === 'kds' ? 'bg-emerald-600 text-white font-bold' : 'text-slate-400 hover:text-white'}`}
>
5. Kitchen KDS
handleSwitchDeviceRole('status')}
className={`px-3 py-1 rounded-md transition-all ${currentRole === 'status' ? 'bg-emerald-600 text-white font-bold' : 'text-slate-400 hover:text-white'}`}
>
6. TV Signage
handleSwitchDeviceRole('owner')}
className={`px-3 py-1 rounded-md transition-all flex items-center space-x-1 ${currentRole === 'owner' ? 'bg-indigo-600 text-white font-bold' : 'text-slate-400 hover:text-white'}`}
>
Owner Panel (PIN: 8888)
handleSwitchDeviceRole('inventory')}
className={`px-3 py-1 rounded-md transition-all flex items-center space-x-1 ${currentRole === 'inventory' ? 'bg-indigo-600 text-white font-bold' : 'text-slate-400 hover:text-white'}`}
>
Inventory (PIN: 9999)
{/* ========================================================= */}
{/* SCREEN CONTAINER */}
{/* ========================================================= */}
{/* ========================================================= */}
{/* ROLE 1: SELF SERVICE KIOSK SCREEN */}
{/* ========================================================= */}
{currentRole === 'kiosk' && (
{/* WELCOME VIEW */}
{kioskMode === 'welcome' && (
Welcome to La Greenzaa
Restaurant & Cafe
Skip the cashier queue! Customize, select your dining table and mode, and place orders directly below.
setKioskMode('select-dining-mode')}
className="px-8 py-4 bg-emerald-600 hover:bg-emerald-700 text-white font-black text-sm rounded-full tracking-wider uppercase shadow-lg shadow-emerald-950 transition-transform active:scale-95"
>
Touch Here to Begin
)}
{/* CHOOSE DINING MODE */}
{kioskMode === 'select-dining-mode' && (
Select Your Dining Preference
Would you like to dine inside or package a parcel takeaway?
{
setKioskDiningMode('Dine-In Self-Service');
setKioskMode('select-table');
}}
className="p-8 bg-slate-950 border-2 border-slate-800 hover:border-emerald-500 rounded-2xl flex flex-col items-center text-center space-y-3 transition-all active:scale-95"
>
🍽️
Eat Here (Dine-In)
{
setKioskDiningMode('Takeaway Parcel');
setKioskMode('catalog');
}}
className="p-8 bg-slate-950 border-2 border-slate-800 hover:border-emerald-500 rounded-2xl flex flex-col items-center text-center space-y-3 transition-all active:scale-95"
>
🛍️
Takeaway / Parcel
setKioskMode('welcome')} className="text-xs text-slate-400 hover:text-white mt-4 block text-center">← Cancel & Return
)}
{/* SELECT DINE-IN TABLE SCREEN */}
{kioskMode === 'select-table' && (
Select Your Dining Table Number
Please select an available dining table from the map layout below.
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map(num => (
{
setKioskTable(`Table ${num}`);
setKioskMode('catalog');
}}
className="p-4 bg-slate-950 hover:bg-emerald-950 border border-slate-800 hover:border-emerald-500 rounded-xl text-center transition-all active:scale-95 flex flex-col items-center"
>
Table
{num}
))}
setKioskMode('select-dining-mode')} className="text-xs text-slate-400 hover:text-white mt-4 block text-center">← Go Back
)}
{/* KIOSK CATALOG VIEW */}
{kioskMode === 'catalog' && (
{/* Touch menu lists */}
Mode Selected
{kioskDiningMode} {kioskDiningMode === 'Dine-In Self-Service' && `(${kioskTable})`}
setKioskMode('select-dining-mode')} className="text-slate-400 hover:underline">Change Mode
{/* Horiz category slider */}
{CATEGORIES.map(cat => (
setActiveCategory(cat)}
className={`whitespace-nowrap px-4 py-2 rounded-full text-xs font-bold transition-all ${
activeCategory === cat ? 'bg-emerald-600 text-white' : 'bg-slate-950 text-slate-400 border border-slate-800'
}`}
>
{cat}
))}
{products.filter(p => activeCategory === 'All Items' || p.category === activeCategory).map(prod => (
handleAddCartItemWithModifiers(prod, 'kiosk')}
className="bg-slate-950 border border-slate-850 hover:border-emerald-700 rounded-2xl p-4 flex flex-col justify-between cursor-pointer transition-all active:scale-95 group"
>
{prod.image}
{prod.name}
{prod.sku}
Price:
{currencySymbol}{prod.price}
))}
{/* Right Side Kiosk Cart */}
Order summary
Method: {kioskDiningMode}
{kioskDiningMode === 'Dine-In Self-Service' && (
Table: {kioskTable}
)}
{kioskCart.map(item => (
{item.product.name}
{currencySymbol}{item.product.price} each
handleUpdateCartQuantity(item.cartKey, -1, 'kiosk')}>
{item.quantity}
handleUpdateCartQuantity(item.cartKey, 1, 'kiosk')}>
handleRemoveItemFromCart(item.cartKey, 'kiosk')} className="text-slate-600 pl-1.5">
{item.modifiers?.length > 0 && (
{item.modifiers.map((mod, i) => (
{mod}
))}
)}
))}
{/* Settlement */}
Select Payment Location
setKioskPaymentMode('Pay at Counter')}
className={`py-2 rounded-xl border text-[10px] uppercase transition-all ${
kioskPaymentMode === 'Pay at Counter' ? 'bg-emerald-950 border-emerald-500 text-emerald-400' : 'bg-slate-900 border-slate-855 text-slate-505'
}`}
>
💸 Pay Cashier/Biller
setKioskPaymentMode('Card/UPI Online')}
className={`py-2 rounded-xl border text-[10px] uppercase transition-all ${
kioskPaymentMode === 'Card/UPI Online' ? 'bg-emerald-950 border-emerald-500 text-emerald-400' : 'bg-slate-900 border-slate-855 text-slate-505'
}`}
>
💳 Online Card/UPI
Total Due:
{currencySymbol}{kioskTotals.grandTotal}
handleExecuteCheckout(
'kiosk',
kioskPaymentMode === 'Pay at Counter' ? 'Cash Pending' : 'UPI Online',
kioskDiningMode,
kioskDiningMode === 'Dine-In Self-Service' ? kioskTable : 'N/A',
kioskDiningMode === 'Dine-In Self-Service' ? `Self Table ${kioskTable}` : 'Self Parcel Guest',
kioskPaymentMode === 'Pay at Counter'
)}
className="w-full py-3 bg-emerald-600 hover:bg-emerald-700 text-white font-black text-xs rounded-xl shadow uppercase tracking-wider transition-all disabled:opacity-50"
>
🚀 SUBMIT ORDER & PRINT TICKET
)}
{/* ORDER SUBMITTED TICKET GENERATION SCREEN */}
{kioskMode === 'finished' && kioskLastOrder && (
Order Logged Successfully!
TOKEN {kioskLastOrder.tokenNo}
Dining Style: {kioskLastOrder.orderType}
{kioskLastOrder.tableNo !== 'N/A' && (
Allocated Table: {kioskLastOrder.tableNo}
)}
Total Invoice:
{currencySymbol}{kioskLastOrder.grandTotal}
{/* Action prompts */}
{kioskLastOrder.paid ? (
🟢 Paid Successfully: Your order has routed directly to the kitchen display boards. Please watch the large TV Signage Wall Display . Once your token sounds, pick up your plate.
) : (
⚠️ Action Required (Settle at Biller): Please take your printed token ticket to the Biller Cashier Terminal . Once you pay, your order will route to the kitchen displays and television signage queue.
)}
setKioskMode('welcome')}
className="w-full py-3 bg-slate-950 hover:bg-slate-900 text-slate-300 text-xs font-bold rounded-xl border border-slate-800 transition-colors"
>
Start New Order
)}
)}
{/* ========================================================= */}
{/* ROLE 2: TABLE QR SELF-ORDERING SIMULATOR */}
{/* ========================================================= */}
{currentRole === 'qr_ordering' && (
Table QR Scan Simulator
Live ordering from private mobile device
Select Scanned Table QR:
{
setQrTable(e.target.value);
setQrOrderPlaced(false);
}}
className="bg-slate-900 border border-slate-800 rounded p-1.5 text-xs text-emerald-400 font-black focus:outline-none"
>
{[1,2,3,4,5,6,7,8].map(n => (
Table #{n}
))}
{!qrOrderPlaced ? (
{CATEGORIES.map(cat => (
setActiveCategory(cat)}
className={`whitespace-nowrap px-4 py-1.5 rounded-full text-xs font-bold transition-all ${
activeCategory === cat ? 'bg-emerald-600 text-white' : 'bg-slate-950 text-slate-400 border border-slate-800'
}`}
>
{cat}
))}
{products.filter(p => activeCategory === 'All Items' || p.category === activeCategory).map(prod => (
handleAddCartItemWithModifiers(prod, 'qr_ordering')}
className="bg-slate-950 border border-slate-855 hover:border-emerald-700 rounded-2xl p-4 flex flex-col justify-between cursor-pointer transition-all active:scale-95 group"
>
{prod.image}
{prod.name}
Price:
{currencySymbol}{prod.price}
))}
Live Table Cart
Orders sync instantly to kitchen display and television monitors
{qrCart.map(item => (
{item.product.name}
{currencySymbol}{item.product.price} each
handleUpdateCartQuantity(item.cartKey, -1, 'qr_ordering')}>
{item.quantity}
handleUpdateCartQuantity(item.cartKey, 1, 'qr_ordering')}>
handleRemoveItemFromCart(item.cartKey, 'qr_ordering')} className="text-slate-600 pl-1">
{item.modifiers?.length > 0 && (
{item.modifiers.map((mod, i) => (
{mod}
))}
)}
))}
Subtotal:
{currencySymbol}{qrTotals.subtotal}
handleExecuteCheckout('qr_ordering', 'UPI Online', 'Table Service', qrTable, 'QR Self-Service Guest')}
className="w-full py-3 bg-emerald-600 hover:bg-emerald-700 text-white font-black text-xs rounded-xl shadow uppercase tracking-wider transition-all disabled:opacity-50"
>
🚀 DISPATCH DIGITAL KOT
) : (
QR Order Placed successfully!
Your order has been routed instantly to the kitchen KDS. Please monitor the signage wall display for status updates.
setQrOrderPlaced(false)}
className="px-6 py-2.5 bg-emerald-600 hover:bg-emerald-700 text-white font-bold text-xs rounded-xl"
>
Place Another QR Order
)}
)}
{/* ========================================================= */}
{/* ROLE 3: TABLE-SIDE WAITER TABLET */}
{/* ========================================================= */}
{currentRole === 'waiter' && (
{/* Catalog (8 columns) */}
Waiter Hand-held Tablet
Allocate orders, select dining table mappings, and manage custom modifiers.
Select Active Table:
setWaiterTable(e.target.value)}
className="bg-slate-900 border border-slate-800 rounded p-1.5 text-xs text-emerald-400 font-black focus:ring-1 focus:ring-emerald-500 outline-none"
>
{[1, 2, 3, 4, 5, 6, 7, 8].map(n => (
Table #{n}
))}
{/* Grid of items */}
{products.map(prod => (
handleAddCartItemWithModifiers(prod, 'waiter')}
className="bg-slate-950 border border-slate-855 hover:border-emerald-700 rounded-2xl p-4 flex flex-col justify-between cursor-pointer transition-all active:scale-95 group"
>
{prod.image}
{prod.name}
{prod.sku}
Price:
{currencySymbol}{prod.price}
))}
{/* Right columns (4 columns) */}
{waiterCart.map(item => (
{item.product.name}
{currencySymbol}{item.product.price} each
handleUpdateCartQuantity(item.cartKey, -1, 'waiter')}>
{item.quantity}
handleUpdateCartQuantity(item.cartKey, 1, 'waiter')}>
handleRemoveItemFromCart(item.cartKey, 'waiter')} className="text-slate-600 pl-1.5">
{item.modifiers?.length > 0 && (
{item.modifiers.map((mod, i) => (
{mod}
))}
)}
))}
Guest Total:
{currencySymbol}{waiterTotals.subtotal}
handleExecuteCheckout('waiter', 'Cash', 'Table Service', waiterTable, waiterGuestName)}
className="w-full py-3 bg-emerald-600 hover:bg-emerald-700 text-white font-black text-xs rounded-xl shadow uppercase tracking-wider transition-colors disabled:opacity-50"
>
🚀 Send to Kitchen (KOT)
)}
{/* ========================================================= */}
{/* ROLE 4: POS CASHIER TERMINAL (BILLER DESK) */}
{/* ========================================================= */}
{currentRole === 'biller' && (
{/* Left Column POS list (8 columns) */}
{/* SMART RECALL QUEUE PANEL */}
{orders.filter(o => {
const matchesUnpaid = o.status === 'Pending Payment' || !o.paid;
const matchesQuery = o.tokenNo.toString().includes(orderRecallFilter) ||
o.customerName.toLowerCase().includes(orderRecallFilter.toLowerCase()) ||
o.tableNo.toLowerCase().includes(orderRecallFilter.toLowerCase());
return matchesUnpaid && matchesQuery;
}).map(o => (
handleSmartRecallOrder(o)}
className={`p-3 rounded-xl border cursor-pointer min-w-[230px] flex flex-col justify-between transition-all ${
recalledOrderId === o.id ? 'bg-emerald-950/40 border-emerald-500 shadow-md ring-1 ring-emerald-500/30' : 'bg-slate-900 border-slate-855 hover:border-slate-700'
}`}
>
Token #{o.tokenNo}
{o.tableNo !== 'N/A' ? `Dining: ${o.tableNo}` : o.orderType}
{o.items.length} Items
Guest: {o.customerName}
{currencySymbol}{o.grandTotal}
))}
{orders.filter(o => o.status === 'Pending Payment' || !o.paid).length === 0 && (
No active unpaid or recalled tickets pending in the local pipeline...
)}
{/* Standard POS catalog search and Customer bind */}
setSearchQuery(e.target.value)}
/>
{/* Select Loyalty Customer & Add Customer Button */}
Link Loyalty Account:
{
const found = customers.find(c => c.id === e.target.value);
if (found) {
setSelectedCustomer(found);
showNotification(`Bill Shipped to Account: ${found.name}`);
}
}}
className="bg-slate-900 border border-slate-800 rounded p-1.5 text-xs text-emerald-400 font-black focus:outline-none"
>
{customers.map(c => (
{c.name} ({c.phone})
))}
{/* Option to Register/Add Customer Profile */}
setShowRegisterCustomerModal(true)}
className="px-2.5 py-1.5 bg-indigo-600 hover:bg-indigo-700 text-white font-black rounded-lg transition-colors flex items-center space-x-1"
>
Register Cust.
{/* POS Category Tabs */}
{CATEGORIES.map(cat => (
setCashierSelectedCategory(cat)}
className={`whitespace-nowrap px-3 py-1.5 rounded-full text-[11px] font-bold transition-all ${
cashierSelectedCategory === cat
? 'bg-emerald-600 text-white'
: 'bg-slate-900 text-slate-400 border border-slate-800'
}`}
>
{cat}
))}
{/* Grid of items */}
{products.filter(p => {
const matchesCategory = cashierSelectedCategory === 'All Items' || p.category === cashierSelectedCategory;
const matchesSearch = p.name.toLowerCase().includes(cashierSearchQuery.toLowerCase());
return matchesCategory && matchesSearch;
}).map(prod => (
handleAddCartItemWithModifiers(prod, 'cashier')}
className="bg-slate-950 border border-slate-855 hover:border-emerald-700 rounded-2xl p-3 flex flex-col justify-between cursor-pointer transition-all active:scale-95"
>
{prod.image}
{prod.name}
{prod.sku}
Price:
{currencySymbol}{prod.price}
))}
{/* Right Billing Desk columns (4 columns) */}
{/* Customer ledger tracking */}
{selectedCustomer.name}
{selectedCustomer.phone}
Loyalty Points Balance:
{selectedCustomer.loyaltyPoints} PTS
setBillingOrderType('Walk-In')}
className={`py-2 rounded-xl border transition-all ${
billingOrderType === 'Walk-In' ? 'bg-emerald-950 border-emerald-500 text-emerald-400' : 'bg-slate-900 border-slate-800 text-slate-500'
}`}
>
🚶 Walk-In
setBillingOrderType('Home Delivery')}
className={`py-2 rounded-xl border transition-all ${
billingOrderType === 'Home Delivery' ? 'bg-emerald-950 border-emerald-500 text-emerald-400' : 'bg-slate-900 border-slate-800 text-slate-500'
}`}
>
🛵 Home Delivery
{/* Items loaded */}
{cashierCart.map(item => (
{item.product.name}
{currencySymbol}{item.product.price} each
handleUpdateCartQuantity(item.cartKey, -1, 'cashier')}>
{item.quantity}
handleUpdateCartQuantity(item.cartKey, 1, 'cashier')}>
{currencySymbol}{(item.product.price * item.quantity).toFixed(2)}
handleRemoveItemFromCart(item.cartKey, 'cashier')} className="text-slate-600 pl-1">
{item.modifiers?.length > 0 && (
{item.modifiers.map((mod, i) => (
{mod}
))}
)}
))}
{/* Calculations Summary */}
Subtotal:
{currencySymbol}{cashierTotals.subtotal.toFixed(2)}
Tax (5% GST Included):
{currencySymbol}{cashierTotals.tax.toFixed(2)}
Round Off:
{currencySymbol}{cashierTotals.roundOff.toFixed(2)}
Total Due:
{currencySymbol}{cashierTotals.grandTotal.toFixed(2)}
{/* Checkout triggers */}
handleExecuteCheckout('cashier', 'Cash', billingOrderType, 'N/A', selectedCustomer.name)}
className="col-span-2 py-3 bg-emerald-600 hover:bg-emerald-700 text-white font-black rounded-xl shadow disabled:opacity-50"
>
CASH BILLING & PRINT KOT [F1]
Card [F2]
UPI QR [F3]
)}
{/* ========================================================= */}
{/* ROLE 5: KITCHEN DISPLAY MONITOR (KDS) - PROFESSIONAL UPGRADE */}
{/* ========================================================= */}
{currentRole === 'kds' && (
{/* Upgrade Dashboard Header */}
Advanced KDS Command Board
Pro Upgrade
Preparation tickets categorized by station, monitored with real-time elapsed timers.
{/* Station Filter & Recall controls */}
{['All Stations', 'Beverage Bar', 'Main Kitchen'].map(station => (
setKdsStationFilter(station)}
className={`px-3 py-1 rounded transition-all font-bold ${
kdsStationFilter === station ? 'bg-emerald-600 text-white shadow' : 'text-slate-400 hover:text-white'
}`}
>
{station}
))}
{recentlyCompletedOrder && (
Recall completed
)}
{/* KDS Active Tickets Grid */}
{orders.filter(o => {
const isNotCompleted = o.status !== 'Completed' && o.status !== 'Pending Payment';
const matchesStation = kdsStationFilter === 'All Stations' || o.station === kdsStationFilter;
return isNotCompleted && matchesStation;
}).map(o => {
const elapsedSeconds = o.createdAt ? Math.floor((Date.now() - o.createdAt) / 1000) : 0;
const isDelayed = elapsedSeconds > 300; // Trigger delay state after 5 mins
return (
{/* Header with real-time timers */}
TOKEN #{o.tokenNo}
{o.tableNo !== 'N/A' ? o.tableNo : o.orderType}
{/* Real time timer ticker */}
{getElapsedTimeText(o.createdAt)}
{/* Ticket list with interactive crossed-off checkboxes */}
{o.items.map((it, idx) => (
{it.name}
{it.quantity}x
{it.modifiers?.length > 0 && (
{it.modifiers.map((mod, i) => (
{mod}
))}
)}
))}
{o.status === 'Pending' && (
handleUpdateOrderStatus(o.id, 'Cooking')}
className="w-full py-2 bg-indigo-600 hover:bg-indigo-700 text-white font-extrabold rounded-lg shadow transition-all"
>
Accept & Start Cooking
)}
{o.status === 'Cooking' && (
handleKdsCompleteTicket(o.id)}
className="w-full py-2 bg-amber-500 hover:bg-amber-600 text-white font-extrabold rounded-lg shadow transition-all"
>
Mark Ready (Chime Out)
)}
{o.status === 'Ready' && (
⌛ Pending Server Pickup
)}
);
})}
{orders.filter(o => {
const isNotCompleted = o.status !== 'Completed' && o.status !== 'Pending Payment';
const matchesStation = kdsStationFilter === 'All Stations' || o.station === kdsStationFilter;
return isNotCompleted && matchesStation;
}).length === 0 && (
Kitchen prep board is fully cleared!
)}
)}
{/* ========================================================= */}
{/* ROLE 5: TV DISPLAY SIGNAGE WALL MONITOR */}
{/* ========================================================= */}
{currentRole === 'status' && (
Live Dining Hall Screen
Guest pickup & Waiter dispatch monitor
{/* Left Column: Customer self pickup parcel status */}
Self-Service Pickup Status
Pickup your order when your Token turns green
{/* Preparing list */}
Preparing ⏳
{orders.filter(o => o.status !== 'Completed' && (o.orderType.includes('Self-Service') || o.orderType.includes('Takeaway')) && (o.status === 'Pending' || o.status === 'Cooking')).map(o => (
Token {o.tokenNo}
))}
{/* Ready list */}
Ready for Pickup 📦
{orders.filter(o => o.status !== 'Completed' && (o.orderType.includes('Self-Service') || o.orderType.includes('Takeaway')) && o.status === 'Ready').map(o => (
Token {o.tokenNo}
handleUpdateOrderStatus(o.id, 'Completed')}
className="absolute inset-0 bg-emerald-700 opacity-0 group-hover:opacity-100 flex items-center justify-center text-[10px] font-black tracking-wider transition-opacity"
>
✓ HAND OVER
))}
{/* Right Column: Waiter table serve alerts */}
Dining Tables Dispatch
Waiters: Dispatch plates to tables as soon as they sound
{orders.filter(o => o.status !== 'Completed' && o.orderType.includes('Table Service') && o.status === 'Ready').map(o => (
{o.tableNo}
READY TO SERVE
{o.items.map((it, idx) => (
- {it.name} ({it.quantity}x)
))}
handleUpdateOrderStatus(o.id, 'Completed')}
className="w-full py-1.5 bg-slate-950 hover:bg-slate-900 text-white font-extrabold text-[10px] rounded transition-colors"
>
✓ Mark Table Served
))}
{orders.filter(o => o.status !== 'Completed' && o.orderType.includes('Table Service') && o.status === 'Ready').length === 0 && (
No active dining table dispatch calls
)}
)}
{/* ========================================================= */}
{/* ROLE 6: SECURE AUTHORIZED OWNER BACKOFFICE */}
{/* ========================================================= */}
{currentRole === 'owner' && authorizedOwner && (
Enterprise Owner Backoffice Dashboard
{BRAND_SETTINGS.businessName} Analytics and Loyalty Hub
{/* Data downloaders */}
Export Audit Logs (JSON)
{/* Simulated executive metrics */}
Gross Sales Value
₹4,45,790.00
✓ Profit margin: 76.5%
Active Orders Pipeline
{orders.filter(o => o.status !== 'Completed').length} Orders
↗ Live Transit Routing
Top Selling Category
Hot Coffee
☕ Best hour: 4 PM - 7 PM
Loyalty Accounts
{customers.length} Accounts
✓ GST Invoices Settled
{/* Split layout: Customers ledger (Left) vs insights (Right) */}
Customer Loyalty Ledger
Centralized database
Customer Name
Phone
Loyalty Points
O/S Balance
{customers.map(c => (
{c.name}
{c.phone}
{c.loyaltyPoints} PTS
{currencySymbol}{c.outstandingBalance.toFixed(2)}
))}
{/* Insights Column */}
Business Health Insights
Category Optimization
Hot Coffee and Smoothies constitute 78% of your total sales. Consider running loyalty combos on low-margin baked goods during morning slots.
Peak Performance Hours
Footfall peak occurs at 4:30 PM. Ensuring self-service kiosks and Table QR order scanners are fully optimized reduces front counter bottleneck by 40%.
)}
{/* ========================================================= */}
{/* ROLE 7: SECURE AUTHORIZED INVENTORY PANEL */}
{/* ========================================================= */}
{currentRole === 'inventory' && authorizedInventory && (
{/* Left Stock Matrix & Catalog (8 columns) */}
La Greenzaa Inventory Management Panel
Update recipes catalog, track opening vs. hand stocks, and adjust warehouse values.
{/* Master stock compiler */}
Accounting vs. Physical Stock distribution
Select any recipe item below to inspect detailed matrices or print barcodes
Item details
SKU
Opening Stock
Committed
Available
Barcode
Action
{products.map(p => (
setSelectedInventoryItem(p)}
className={`hover:bg-slate-900/40 cursor-pointer ${
selectedInventoryItem?.id === p.id ? 'bg-slate-900' : ''
}`}
>
{p.image}
{p.name}
{p.sku}
{p.openingStock}.00
{p.committedStock}.00
{p.availableForSale}.00
{
e.stopPropagation();
setSelectedBarcodeItem(p);
setShowBarcodePrintModal(true);
}}
className="px-2.5 py-1 bg-slate-800 hover:bg-slate-700 text-slate-300 rounded font-bold text-[10px] flex items-center space-x-1"
>
Preview
{
e.stopPropagation();
handleConfirmRemoveProduct(p.id);
}}
className="text-slate-600 hover:text-rose-500 p-1"
>
))}
{/* Selected Item Stock details */}
{selectedInventoryItem && (
Stock Matrix: {selectedInventoryItem.name}
Base Unit: {selectedInventoryItem.unit}
Opening Stock
{selectedInventoryItem.openingStock}.00
Stock On Hand
{selectedInventoryItem.stockOnHand}.00
Committed Stock
{selectedInventoryItem.committedStock}.00
Available For Sale
{selectedInventoryItem.availableForSale}.00
)}
{/* Right configuration panel (4 columns) */}
{/* Add Item form */}
{/* Barcode Preview widget */}
Printing Barcode Preview
{selectedInventoryItem ? (
{selectedInventoryItem.name}
Price: {currencySymbol}{selectedInventoryItem.price}
{[1, 2, 1, 3, 2, 1.5, 3, 1, 2, 1, 3, 2, 1.5].map((w, i) => (
))}
{selectedInventoryItem.sku}
) : (
Please select an item to preview barcode template.
)}
)}
{/* ========================================================= */}
{/* PIN CODE KEYPAD LOCK OVERLAY */}
{/* ========================================================= */}
{securityTarget && (
Access PIN Required
Owner Portal Code: 8888 | Inventory Code: 9999
{/* Keypad entry visual feedback */}
{[0, 1, 2, 3].map((idx) => (
idx ? 'bg-indigo-500 shadow-lg shadow-indigo-500/50 scale-110' : 'bg-slate-950'
}`}
/>
))}
{lockErrorMessage && (
{lockErrorMessage}
)}
{/* Keypad Matrix Grid */}
{[1, 2, 3, 4, 5, 6, 7, 8, 9].map(num => (
{
if (pinInput.length < 4) setPinInput(prev => prev + num);
}}
className="py-2.5 bg-slate-950 hover:bg-slate-855 active:scale-95 text-xs font-bold rounded-lg border border-slate-800 text-slate-200 transition-all"
>
{num}
))}
setPinInput('')}
className="py-2.5 bg-slate-950 hover:bg-rose-950/20 text-rose-500 text-[10px] font-black rounded-lg border border-slate-800 uppercase"
>
Clear
{
if (pinInput.length < 4) setPinInput(prev => prev + '0');
}}
className="py-2.5 bg-slate-950 hover:bg-slate-850 text-xs font-bold rounded-lg border border-slate-800"
>
0
Enter
setSecurityTarget(null)}
className="text-[10px] text-slate-500 hover:text-slate-300 transition-all"
>
Go Back
)}
{/* ========================================================= */}
{/* MODAL: CUSTOM INGREDIENTS/PREPARATION MODIFIERS */}
{/* ========================================================= */}
{showModifierModal && activeModifierItem && (
Preparation Custom Modifiers
Select dynamic modifiers to send alongside KOT ticket data structures:
{['Keep Vegan', 'No Onion No Garlic', 'Extra Hot', 'Less Sugar', 'Keep Sugar-Free', 'Extra Cheese'].map(mod => {
const active = tempModifiers.includes(mod);
return (
{
if (active) setTempModifiers(prev => prev.filter(m => m !== mod));
else setTempModifiers(prev => [...prev, mod]);
}}
className={`p-2.5 rounded-xl border text-center transition-all ${
active ? 'bg-emerald-950 border-emerald-500 text-emerald-400' : 'bg-slate-950 border-slate-855 text-slate-550'
}`}
>
{mod}
);
})}
Apply Modifications
setShowModifierModal(false)}
className="flex-1 py-2.5 bg-slate-800 hover:bg-slate-700 text-slate-400 text-xs rounded-xl"
>
Skip Modifiers
)}
{/* ========================================================= */}
{/* MODAL: CUSTOM CUSTOMER REGISTRATION DIALOG (BILLER PORTAL) */}
{/* ========================================================= */}
{showRegisterCustomerModal && (
Register Loyalty Member
setShowRegisterCustomerModal(false)} className="text-slate-500 hover:text-white">
)}
{/* ========================================================= */}
{/* MODAL: CUSTOM INLINE PRODUCT DELETE CONFIRMATION */}
{/* ========================================================= */}
{confirmDeleteId && (
Permanently Delete Product?
This action cannot be undone. This recipe will be deleted from the local database master catalog.
Yes, Delete
setConfirmDeleteId(null)}
className="flex-1 py-2.5 bg-slate-800 text-slate-400 rounded-xl transition-all"
>
Cancel
)}
{/* ========================================================= */}
{/* MODAL: SINGLE PRODUCT BARCODE LABEL PRINT SIMULATOR */}
{/* ========================================================= */}
{showBarcodePrintModal && selectedBarcodeItem && (
Barcode Decal Printer
setShowBarcodePrintModal(false)} className="hover:text-slate-200">✕
{selectedBarcodeItem.name}
Price: {currencySymbol}{selectedBarcodeItem.price}
{[1, 3, 1, 2, 4, 1.5, 3, 2.5, 1, 2, 1.5, 3, 1, 2, 4, 1.5, 3, 2.5, 1, 2].map((w, i) => (
))}
{selectedBarcodeItem.sku}
window.print()} className="py-2 bg-slate-900 text-white rounded-xl font-bold">Print Decal
setShowBarcodePrintModal(false)} className="py-2 border rounded-xl font-bold">Cancel
)}
{/* ========================================================= */}
{/* POPUP MODAL: KOT THERMAL PRINTER SIMULATOR */}
{/* ========================================================= */}
{showPrintModal && activeKOTPrint && (
Dynamic KOT Saved Natively
setShowPrintModal(false)} className="hover:text-slate-100 font-extrabold text-sm">✕
{BRAND_SETTINGS.businessName} KOT
Token Ticket #{activeKOTPrint.tokenNo}
Date: May 22, 2026 Time: {activeKOTPrint.timestamp}
Order Mode: {activeKOTPrint.orderType} Terminal: REG-A01
{activeKOTPrint.tableNo !== 'N/A' && (
Dining Table:
{activeKOTPrint.tableNo}
)}
Customer Name: {activeKOTPrint.customerName}
Qty
Recipe Detail
{activeKOTPrint.items.map((it, idx) => (
{it.quantity}x
{it.name}
{it.modifiers?.length > 0 && (
* MOD: {it.modifiers.join(', ')}
)}
))}
* KOT Issued Automatically to Prep Stations *
Developed by Umang Developers
window.print()}
className="flex-1 py-2 bg-emerald-600 hover:bg-emerald-700 text-white font-bold text-xs rounded-xl flex items-center justify-center space-x-1.5 shadow"
>
Print Bill
setShowPrintModal(false)}
className="flex-1 py-2 border text-slate-600 text-xs font-bold rounded-xl transition-all"
>
Start New Bill
)}
);
}
// --- APP WITH BOUNDED CRASH RECOVERY ---
export default function App() {
return (
);
}