restaurante / src /pages /KitchenView.jsx
dimensionalpulsar's picture
Upload 48 files
76823f0 verified
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>
);
}