Spaces:
Sleeping
Sleeping
| import React, { useState, useEffect } from 'react'; | |
| import { db } from '../../firebase/config'; | |
| import { ref, onValue, set, update, push, remove } from 'firebase/database'; | |
| import { Layout, Plus, Trash2, Map as MapIcon, Grid, Save, MousePointer2 } from 'lucide-react'; | |
| export default function TableManager() { | |
| const [tables, setTables] = useState([]); | |
| const [newTable, setNewTable] = useState({ number: '', capacity: 4 }); | |
| useEffect(() => { | |
| onValue(ref(db, 'config/tables'), (snapshot) => { | |
| const data = snapshot.val(); | |
| if (data) { | |
| setTables(Object.keys(data).map(id => ({ id, ...data[id] })).sort((a,b) => a.number - b.number)); | |
| } else { | |
| setTables([]); | |
| } | |
| }); | |
| }, []); | |
| const addTable = async (e) => { | |
| e.preventDefault(); | |
| if (!newTable.number) return; | |
| const tableRef = push(ref(db, 'config/tables')); | |
| await set(tableRef, { | |
| number: parseInt(newTable.number), | |
| capacity: parseInt(newTable.capacity), | |
| status: 'available', | |
| x: 0, | |
| y: 0 | |
| }); | |
| setNewTable({ number: '', capacity: 4 }); | |
| }; | |
| const deleteTable = async (id) => { | |
| if (window.confirm('¿Eliminar esta mesa del mapa?')) { | |
| await remove(ref(db, `config/tables/${id}`)); | |
| } | |
| }; | |
| return ( | |
| <div className="animate-fade-in" style={{ padding: '0 1rem' }}> | |
| <header style={{ marginBottom: '2.5rem', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> | |
| <div> | |
| <h2 className="text-gradient" style={{ fontSize: '2rem', fontWeight: '800', display: 'flex', alignItems: 'center', gap: '0.75rem' }}> | |
| <MapIcon size={28} /> Diseño de Salón | |
| </h2> | |
| <p style={{ color: 'var(--text-muted)' }}>Configura la distribución física de tu restaurante</p> | |
| </div> | |
| </header> | |
| <div style={{ display: 'grid', gridTemplateColumns: '300px 1fr', gap: '2rem' }}> | |
| {/* Sidebar: Add Tables */} | |
| <aside className="glass-card" style={{ alignSelf: 'start' }}> | |
| <h3 style={{ marginBottom: '1.5rem', fontSize: '1.1rem' }}>Añadir Mesas</h3> | |
| <form onSubmit={addTable} style={{ display: 'flex', flexDirection: 'column', gap: '1.25rem' }}> | |
| <div> | |
| <label style={labelStyle}>Número de Mesa</label> | |
| <input type="number" value={newTable.number} onChange={e => setNewTable({...newTable, number: e.target.value})} style={inputStyle} placeholder="Ej. 10" /> | |
| </div> | |
| <div> | |
| <label style={labelStyle}>Capacidad (Personas)</label> | |
| <input type="number" value={newTable.capacity} onChange={e => setNewTable({...newTable, capacity: e.target.value})} style={inputStyle} placeholder="Ej. 4" /> | |
| </div> | |
| <button type="submit" className="btn-primary" style={{ padding: '0.8rem' }}> | |
| <Plus size={18} /> Crear Mesa | |
| </button> | |
| </form> | |
| <div style={{ marginTop: '2.5rem' }}> | |
| <h4 style={{ fontSize: '0.8rem', color: 'var(--text-muted)', marginBottom: '1rem', borderTop: '1px solid var(--border-subtle)', paddingTop: '1.5rem' }}>Listado</h4> | |
| <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem', maxHeight: '300px', overflowY: 'auto' }}> | |
| {tables.map(t => ( | |
| <div key={t.id} style={{ display: 'flex', justifyContent: 'space-between', padding: '0.6rem 1rem', background: 'rgba(255,255,255,0.03)', borderRadius: '8px', alignItems: 'center' }}> | |
| <span style={{ fontWeight: '700' }}>Mesa {t.number}</span> | |
| <div style={{ display: 'flex', gap: '0.5rem' }}> | |
| <span style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>{t.capacity} px</span> | |
| <button onClick={() => deleteTable(t.id)} style={{ color: 'var(--primary)', background: 'none', border: 'none' }}><Trash2 size={14} /></button> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| </aside> | |
| {/* Visual Map Area */} | |
| <section className="glass-panel" style={{ height: '600px', position: 'relative', overflow: 'hidden', background: 'radial-gradient(circle, rgba(255,255,255,0.02) 1px, transparent 1px)', backgroundSize: '30px 30px', border: '1px solid var(--border-subtle)' }}> | |
| <div style={{ position: 'absolute', top: '1rem', left: '1rem', display: 'flex', alignItems: 'center', gap: '0.5rem', color: 'var(--text-muted)', fontSize: '0.8rem' }}> | |
| <MousePointer2 size={14} /> Arrastra para posicionar (Modo Edición) | |
| </div> | |
| <div style={{ width: '100%', height: '100%', padding: '3rem', display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(100px, 1fr))', gap: '2rem' }}> | |
| {tables.map(t => ( | |
| <div key={t.id} style={{ | |
| width: '90px', height: '90px', | |
| background: 'rgba(255,90,95,0.05)', | |
| border: '2px solid var(--primary)', | |
| borderRadius: t.capacity > 4 ? '12px' : '50%', | |
| display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', | |
| boxShadow: '0 0 15px rgba(255,90,95,0.1)', | |
| cursor: 'move' | |
| }}> | |
| <span style={{ fontSize: '1.2rem', fontWeight: '900', color: 'var(--primary)' }}>{t.number}</span> | |
| <span style={{ fontSize: '0.65rem', opacity: 0.6 }}>{t.capacity} cap</span> | |
| </div> | |
| ))} | |
| {tables.length === 0 && ( | |
| <div style={{ gridColumn: '1 / -1', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', opacity: 0.2 }}> | |
| <Grid size={80} /> | |
| </div> | |
| )} | |
| </div> | |
| </section> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| const inputStyle = { width: '100%', padding: '0.8rem', borderRadius: '8px', background: 'rgba(255,255,255,0.05)', border: '1px solid var(--border-subtle)', color: '#fff', outline: 'none' }; | |
| const labelStyle = { display: 'block', fontSize: '0.8rem', color: 'var(--text-muted)', marginBottom: '0.5rem' }; | |