import React, { useState, useEffect } from 'react'; import { useAuth } from '../context/AuthContext'; import { useNavigate } from 'react-router-dom'; import { db } from '../firebase/config'; import { ref, onValue, push, set } from 'firebase/database'; import { LogOut, Coffee, Send, Trash2, ShoppingBag, Truck, Receipt, CheckCircle, XCircle, CreditCard, Banknote, QrCode } from 'lucide-react'; export default function WaiterPOS() { const { logout, currentUser } = useAuth(); const navigate = useNavigate(); const [menu, setMenu] = useState([]); const [activeTable, setActiveTable] = useState(null); const [cart, setCart] = useState([]); const [orderType, setOrderType] = useState('Mesa'); // Mesa, Llevar, Delivery const [discount, setDiscount] = useState(0); const [tip, setTip] = useState(0); const [paymentMethod, setPaymentMethod] = useState('Efectivo'); const [isCheckoutOpen, setIsCheckoutOpen] = useState(false); const [tableStatuses, setTableStatuses] = useState({}); const [selectedCategory, setSelectedCategory] = useState('Todos'); // Fetch Menu useEffect(() => { onValue(ref(db, 'menu'), (snapshot) => { const data = snapshot.val(); if (data) { setMenu(Object.keys(data).map(k => ({ id: k, ...data[k] }))); } }); onValue(ref(db, 'tables'), (snapshot) => { const data = snapshot.val(); if (data) setTableStatuses(data); }); }, []); const handleLogout = async () => { await logout(); navigate('/login'); }; const addToCart = async (product) => { if (!activeTable && orderType === 'Mesa') { alert("Selecciona una mesa primero."); return; } // Mark table as occupied if (orderType === 'Mesa' && activeTable) { await set(ref(db, `tables/${activeTable}`), 'occupied'); } const existing = cart.find(item => item.id === product.id); if (existing) { setCart(cart.map(item => item.id === product.id ? { ...item, qty: item.qty + 1 } : item)); } else { setCart([...cart, { ...product, qty: 1, note: '' }]); } }; const updateCartItemNote = (id, note) => { setCart(cart.map(item => item.id === id ? { ...item, note } : item)); }; const removeFromCart = (id) => { setCart(cart.filter(item => item.id !== id)); }; const subtotal = cart.reduce((acc, item) => acc + (item.price * item.qty), 0); const discountAmount = (subtotal * discount) / 100; const totalCart = subtotal - discountAmount + Number(tip || 0); const sendOrder = async () => { if ((orderType === 'Mesa' && !activeTable) || cart.length === 0) return; const orderRef = push(ref(db, 'orders')); const orderData = { table: orderType === 'Mesa' ? activeTable : orderType, type: orderType, waiter: currentUser?.email, items: cart, subtotal, discount, tip, total: totalCart, paymentMethod: paymentMethod, status: 'pending', timestamp: Date.now() }; await set(orderRef, orderData); if (!isCheckoutOpen) { setCart([]); setActiveTable(null); alert('¡Comanda enviada a cocina exitosamente!'); } else { // Finalizar y descontar stock await set(ref(db, `orders/${orderRef.key}/status`), 'completed'); // Mark table as free if (orderType === 'Mesa' && activeTable) { await set(ref(db, `tables/${activeTable}`), 'free'); } // Lógica de Descuento de Inventario // ... (existing logic continues) for (const item of cart) { // Buscamos el producto en el menú para ver su receta (ingredients) // Nota: En un entorno real, esto se haría en el backend para evitar race conditions y asegurar integridad. const productRef = ref(db, `menu/${item.id}`); onValue(productRef, async (snapshot) => { const product = snapshot.val(); if (product && product.ingredients) { for (const ing of product.ingredients) { const invRef = ref(db, `inventory/${ing.id}/quantity`); // Obtenemos cantidad actual y restamos (qty_producto * qty_ingrediente) onValue(ref(db, `inventory/${ing.id}`), async (invSnap) => { const invData = invSnap.val(); if (invData) { const newQty = invData.quantity - (item.qty * ing.qty); await set(invRef, newQty); } }, { onlyOnce: true }); } } }, { onlyOnce: true }); } setIsCheckoutOpen(false); setCart([]); setActiveTable(null); alert('¡Venta realizada y stock actualizado!'); } }; const printTicket = () => { const printWindow = window.open('', '_blank'); printWindow.document.write(` Ticket de Venta

RESTAURANT OS


Mesa: ${activeTable || orderType}

Fecha: ${new Date().toLocaleString()}


${cart.map(item => `

${item.qty}x ${item.name.padEnd(20)} $${(item.price * item.qty).toFixed(2)}

`).join('')}

Subtotal: $${subtotal.toFixed(2)}

Descuento (${discount}%): -$${discountAmount.toFixed(2)}

Propina: $${Number(tip || 0).toFixed(2)}

Total: $${totalCart.toFixed(2)}

¡Gracias por su visita!

`); printWindow.document.close(); printWindow.print(); }; return (
{/* Navbar */}

Terminal POS

{currentUser?.email}
{/* Left Column: Tables & Menu */}

Tipo de Pedido

{[ { id: 'Mesa', icon: }, { id: 'Llevar', icon: }, { id: 'Delivery', icon: } ].map(type => ( ))}
{orderType === 'Mesa' && (
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map(table => ( ))}
)}

Catálogo de Productos

{['Todos', ...new Set(menu.map(p => p.category))].map(cat => ( ))}
{menu .filter(p => selectedCategory === 'Todos' || p.category === selectedCategory) .map(product => ( ))}
{/* Right Column: Order Details */}

{orderType === 'Mesa' ? `Mesa ${activeTable || '?'}` : orderType}

{cart.map((item) => (
{item.name}
{item.qty} x ${item.price}
updateCartItemNote(item.id, e.target.value)} style={{ width: '100%', marginTop: '0.5rem', padding: '4px 8px', fontSize: '0.75rem', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '4px', color: '#fff' }} />
${(item.qty * item.price).toFixed(2)}
))}
Subtotal: ${subtotal.toFixed(2)}
Descuento ({discount}%): -${discountAmount.toFixed(2)}
{tip > 0 && (
Propina: +${Number(tip).toFixed(2)}
)}
Total: ${totalCart.toFixed(2)}
{/* Checkout Modal */} {isCheckoutOpen && (

Finalizar Venta

setTip(e.target.value)} style={{ width: '100%', padding: '0.8rem', background: 'rgba(255,255,255,0.05)', border: '1px solid var(--border-subtle)', borderRadius: 'var(--radius-md)', color: '#fff' }} />
{[ { id: 'Efectivo', icon: }, { id: 'Tarjeta', icon: }, { id: 'QR', icon: } ].map(m => ( ))}
Total a Pagar: ${totalCart.toFixed(2)}
)}
); }