Spaces:
Running
Running
File size: 6,602 Bytes
564baf9 76823f0 564baf9 76823f0 564baf9 76823f0 564baf9 76823f0 564baf9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | import React, { useState, useEffect, useRef } from 'react';
import { db } from '../firebase/config';
import { ref, onValue, update } from 'firebase/database';
import { useAuth } from '../context/AuthContext';
import { useNavigate } from 'react-router-dom';
import { CheckCircle, Clock, UtensilsCrossed, LogOut, ExternalLink, PlayCircle, Flame } from 'lucide-react';
export default function KitchenView() {
const { logout } = useAuth();
const navigate = useNavigate();
const [orders, setOrders] = useState([]);
const [currentTime, setCurrentTime] = useState(Date.now());
const lastOrderCount = useRef(0);
useEffect(() => {
const ordersRef = ref(db, 'orders');
onValue(ordersRef, (snapshot) => {
const data = snapshot.val();
if (data) {
const orderList = Object.keys(data).map(k => ({ id: k, ...data[k] }))
.filter(o => o.status !== 'completed')
.sort((a, b) => b.timestamp - a.timestamp);
// Alerta de sonido si hay nuevas órdenes
if (orderList.length > lastOrderCount.current) {
playNotificationSound();
}
lastOrderCount.current = orderList.length;
setOrders(orderList);
} else {
setOrders([]);
}
});
const timer = setInterval(() => setCurrentTime(Date.now()), 60000);
return () => clearInterval(timer);
}, []);
const playNotificationSound = () => {
const audio = new Audio('https://assets.mixkit.co/active_storage/sfx/2869/2869-preview.mp3');
audio.play().catch(e => console.log("User interaction required for audio"));
};
const updateStatus = async (id, status) => {
await update(ref(db, `orders/${id}`), { status });
};
const handleLogout = async () => {
await logout();
navigate('/login');
};
return (
<div className="app-container" style={{ flexDirection: 'column', background: 'var(--bg-base)', height: '100vh', overflow: 'hidden' }}>
<header className="glass-panel" style={{ padding: '1rem 2.5rem', display: 'flex', justifyContent: 'space-between', alignItems: 'center', borderRadius: '0', borderBottom: '1px solid var(--border-subtle)' }}>
<h1 className="text-gradient" style={{ fontSize: '1.5rem', fontWeight: '800', display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
<UtensilsCrossed size={24} /> KDS - Centro de Cocina
</h1>
<div style={{ display: 'flex', alignItems: 'center', gap: '1.5rem' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', color: 'var(--success)', fontSize: '0.9rem' }}>
<div style={{ width: '8px', height: '8px', background: 'var(--success)', borderRadius: '50%', boxShadow: '0 0 10px var(--success)' }}></div>
En Vivo
</div>
<button className="btn-glass" onClick={handleLogout} style={{ padding: '0.5rem 1rem', color: 'var(--primary)' }}>
<LogOut size={16} /> Salir
</button>
</div>
</header>
<div className="main-content" style={{ padding: '2rem', display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(320px, 1fr))', gap: '1.5rem', overflowY: 'auto' }}>
{orders.length === 0 ? (
<div style={{ gridColumn: '1 / -1', textAlign: 'center', paddingTop: '10vh', color: 'var(--text-muted)' }}>
<UtensilsCrossed size={60} style={{ opacity: 0.1, marginBottom: '1rem' }} />
<h2 style={{ fontSize: '1.5rem' }}>Cocina Despejada</h2>
<p>No hay pedidos en espera.</p>
</div>
) : (
orders.map((order) => (
<div key={order.id} className="glass-card animate-fade-in" style={{
padding: '1.5rem',
borderLeft: order.status === 'preparing' ? '4px solid var(--warning)' : '4px solid var(--primary)',
background: order.status === 'preparing' ? 'rgba(255,200,0,0.02)' : 'var(--bg-card)'
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '1.25rem', alignItems: 'center' }}>
<span style={{ fontSize: '1.2rem', fontWeight: '800', color: order.status === 'preparing' ? 'var(--warning)' : 'var(--primary)' }}>
{order.table} {order.type === 'Llevar' && '🛍️'} {order.type === 'Delivery' && '🚀'}
</span>
<span style={{ fontSize: '0.8rem', color: (currentTime - order.timestamp) > 900000 ? 'var(--primary)' : 'var(--text-muted)', fontWeight: '700' }}>
<Clock size={14} /> {Math.floor((currentTime - order.timestamp) / 60000)} min
</span>
</div>
<div style={{ marginBottom: '1.5rem', minHeight: '80px' }}>
{order.items.map((item, idx) => (
<div key={idx} style={{ display: 'flex', justifyContent: 'space-between', padding: '0.5rem 0', borderBottom: '1px solid rgba(255,255,255,0.05)' }}>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<span style={{ fontWeight: '600' }}><span style={{ color: 'var(--primary)', fontWeight: '800' }}>{item.qty}</span> {item.name}</span>
{item.note && <span style={{ fontSize: '0.75rem', color: 'var(--warning)', fontStyle: 'italic' }}>* {item.note}</span>}
</div>
</div>
))}
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0.75rem' }}>
{order.status !== 'preparing' ? (
<button
className="btn-glass"
onClick={() => updateStatus(order.id, 'preparing')}
style={{ background: 'rgba(255,160,0,0.1)', color: '#FFB000' }}
>
<Flame size={18} /> Preparar
</button>
) : (
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '0.8rem', color: 'var(--warning)', fontWeight: '700' }}>
ENCENDIDO...
</div>
)}
<button
className="btn-primary"
onClick={() => updateStatus(order.id, 'completed')}
style={{ background: 'var(--success)', color: '#fff' }}
>
<CheckCircle size={18} /> Listo
</button>
</div>
</div>
))
)}
</div>
</div>
);
}
|