diff --git a/src/components/hotel/StaffManagement.tsx b/src/components/hotel/StaffManagement.tsx new file mode 100644 index 0000000..306e797 --- /dev/null +++ b/src/components/hotel/StaffManagement.tsx @@ -0,0 +1,371 @@ +import { useState } from 'react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Badge } from '@/components/ui/badge'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { Plus, Users, Clock, Calendar, Phone, Mail, Shield } from 'lucide-react'; +import { toast } from 'sonner'; + +interface HotelStaff { + id: string; + name: string; + role: string; + department: string; + email: string; + phone: string; + status: 'active' | 'inactive' | 'on_leave'; + shift: string; + hiredDate: Date; + hourlyRate: number; + accessLevel: string; +} + +const StaffManagement = () => { + const [staff, setStaff] = useState([ + { + id: '1', + name: 'María González', + role: 'Recepcionista', + department: 'Recepción', + email: 'maria@hotel.com', + phone: '+34 600 111 222', + status: 'active', + shift: 'Mañana', + hiredDate: new Date('2022-05-10'), + hourlyRate: 13.00, + accessLevel: 'Alto' + }, + { + id: '2', + name: 'Pedro Sánchez', + role: 'Conserje', + department: 'Recepción', + email: 'pedro@hotel.com', + phone: '+34 600 222 333', + status: 'active', + shift: 'Tarde', + hiredDate: new Date('2023-01-15'), + hourlyRate: 12.00, + accessLevel: 'Medio' + }, + { + id: '3', + name: 'Carmen Ruiz', + role: 'Supervisora', + department: 'Limpieza', + email: 'carmen@hotel.com', + phone: '+34 600 333 444', + status: 'active', + shift: 'Mañana', + hiredDate: new Date('2021-08-20'), + hourlyRate: 14.50, + accessLevel: 'Alto' + }, + { + id: '4', + name: 'José Martín', + role: 'Camarero', + department: 'Limpieza', + email: 'jose@hotel.com', + phone: '+34 600 444 555', + status: 'active', + shift: 'Mañana', + hiredDate: new Date('2023-06-01'), + hourlyRate: 11.00, + accessLevel: 'Bajo' + }, + { + id: '5', + name: 'Isabel Torres', + role: 'Técnico', + department: 'Mantenimiento', + email: 'isabel@hotel.com', + phone: '+34 600 555 666', + status: 'on_leave', + shift: 'Completo', + hiredDate: new Date('2022-11-12'), + hourlyRate: 15.00, + accessLevel: 'Alto' + } + ]); + + const roles = [ + 'Recepcionista', 'Conserje', 'Supervisor', 'Camarero', + 'Técnico de Mantenimiento', 'Gerente', 'Botones' + ]; + const departments = ['Recepción', 'Limpieza', 'Mantenimiento', 'Seguridad', 'Administración']; + const shifts = ['Mañana', 'Tarde', 'Noche', 'Completo']; + const accessLevels = ['Bajo', 'Medio', 'Alto']; + + const activeStaff = staff.filter(s => s.status === 'active').length; + const onLeave = staff.filter(s => s.status === 'on_leave').length; + + const getStatusColor = (status: HotelStaff['status']) => { + switch (status) { + case 'active': return 'default'; + case 'inactive': return 'secondary'; + case 'on_leave': return 'outline'; + default: return 'secondary'; + } + }; + + const getStatusLabel = (status: HotelStaff['status']) => { + switch (status) { + case 'active': return 'Activo'; + case 'inactive': return 'Inactivo'; + case 'on_leave': return 'De Permiso'; + default: return status; + } + }; + + const updateStatus = (staffId: string, newStatus: HotelStaff['status']) => { + setStaff(staff.map(s => + s.id === staffId ? { ...s, status: newStatus } : s + )); + toast.success('Estado actualizado'); + }; + + const getMonthsEmployed = (hiredDate: Date) => { + const months = Math.floor((Date.now() - hiredDate.getTime()) / (1000 * 60 * 60 * 24 * 30)); + return months; + }; + + const staffByDepartment = departments.map(dept => ({ + department: dept, + count: staff.filter(s => s.department === dept).length + })); + + return ( +
+ {/* Stats */} +
+ + + Total Personal + + + +
{staff.length}
+

empleados totales

+
+
+ + + + Activos + + + +
{activeStaff}
+

trabajando hoy

+
+
+ + + + De Permiso + + + +
{onLeave}
+

ausentes hoy

+
+
+ + + + Departamentos + + + +
{departments.length}
+

áreas operativas

+
+
+
+ + {/* Department Distribution */} + + + Distribución por Departamento + + +
+ {staffByDepartment.map(dept => ( +
+
{dept.count}
+
{dept.department}
+
+ ))} +
+
+
+ + {/* Actions */} +
+
+

Equipo del Hotel

+

Gestiona tu personal hotelero

+
+ + + + + + + Agregar Nuevo Empleado + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+ + {/* Staff List */} +
+ {staff.map((member) => ( + + +
+
+ {member.name} +

{member.role}

+ {member.department} +
+ + {getStatusLabel(member.status)} + +
+
+ +
+
+ + {member.email} +
+
+ + {member.phone} +
+
+ + Turno: {member.shift} +
+
+ + Acceso: {member.accessLevel} +
+
+ + {getMonthsEmployed(member.hiredDate)} meses en el hotel +
+
+ +
+
Tarifa por Hora
+
€{member.hourlyRate.toFixed(2)}
+
+ + +
+
+ ))} +
+
+ ); +}; + +export default StaffManagement; diff --git a/src/components/restaurant/InventoryManagement.tsx b/src/components/restaurant/InventoryManagement.tsx new file mode 100644 index 0000000..5cef4b2 --- /dev/null +++ b/src/components/restaurant/InventoryManagement.tsx @@ -0,0 +1,364 @@ +import { useState } from 'react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Badge } from '@/components/ui/badge'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { Plus, Package, AlertTriangle, TrendingUp, Search } from 'lucide-react'; +import { toast } from 'sonner'; + +interface InventoryItem { + id: string; + name: string; + category: string; + quantity: number; + unit: string; + minStock: number; + supplier: string; + lastRestocked: Date; + cost: number; +} + +const InventoryManagement = () => { + const [searchTerm, setSearchTerm] = useState(''); + const [selectedCategory, setSelectedCategory] = useState('all'); + + const [inventory, setInventory] = useState([ + { + id: '1', + name: 'Arroz Bomba', + category: 'Ingredientes', + quantity: 25, + unit: 'kg', + minStock: 10, + supplier: 'Distribuciones García', + lastRestocked: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000), + cost: 3.50 + }, + { + id: '2', + name: 'Gambas Frescas', + category: 'Mariscos', + quantity: 5, + unit: 'kg', + minStock: 8, + supplier: 'Pescadería El Mar', + lastRestocked: new Date(Date.now() - 1 * 24 * 60 * 60 * 1000), + cost: 18.00 + }, + { + id: '3', + name: 'Vino Tinto Reserva', + category: 'Bebidas', + quantity: 45, + unit: 'botellas', + minStock: 20, + supplier: 'Bodegas Rioja', + lastRestocked: new Date(Date.now() - 10 * 24 * 60 * 60 * 1000), + cost: 8.50 + }, + { + id: '4', + name: 'Aceite de Oliva Virgen Extra', + category: 'Ingredientes', + quantity: 8, + unit: 'litros', + minStock: 5, + supplier: 'Aceites del Sur', + lastRestocked: new Date(Date.now() - 15 * 24 * 60 * 60 * 1000), + cost: 12.00 + } + ]); + + const categories = ['all', 'Ingredientes', 'Mariscos', 'Bebidas', 'Carnes', 'Verduras']; + + const lowStockItems = inventory.filter(item => item.quantity <= item.minStock); + + const filteredInventory = inventory.filter(item => { + const matchesSearch = item.name.toLowerCase().includes(searchTerm.toLowerCase()) || + item.supplier.toLowerCase().includes(searchTerm.toLowerCase()); + const matchesCategory = selectedCategory === 'all' || item.category === selectedCategory; + return matchesSearch && matchesCategory; + }); + + const addStock = (itemId: string, quantity: number) => { + setInventory(inventory.map(item => + item.id === itemId + ? { ...item, quantity: item.quantity + quantity, lastRestocked: new Date() } + : item + )); + toast.success('Stock actualizado correctamente'); + }; + + const getDaysAgo = (date: Date) => { + const days = Math.floor((Date.now() - date.getTime()) / (1000 * 60 * 60 * 24)); + return days === 0 ? 'Hoy' : days === 1 ? 'Ayer' : `Hace ${days} días`; + }; + + return ( +
+ {/* Stats */} +
+ + + Total Items + + + +
{inventory.length}
+

productos en inventario

+
+
+ + + + Stock Bajo + + + +
{lowStockItems.length}
+

items necesitan reposición

+
+
+ + + + Valor Total + + + +
+ €{inventory.reduce((sum, item) => sum + (item.quantity * item.cost), 0).toFixed(2)} +
+

valor del inventario

+
+
+ + + + Categorías + + + +
{categories.length - 1}
+

categorías activas

+
+
+
+ + {/* Low Stock Alert */} + {lowStockItems.length > 0 && ( + + + + + Alertas de Stock Bajo + + + +
+ {lowStockItems.map(item => ( +
+
+ {item.name} + + Stock: {item.quantity} {item.unit} + +
+ +
+ ))} +
+
+
+ )} + + {/* Filters and Actions */} +
+
+ + setSearchTerm(e.target.value)} + className="pl-10" + /> +
+ + + + + + + + Agregar Item al Inventario + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+ +
+
+
+
+ + {/* Inventory List */} +
+ {filteredInventory.map((item) => ( + + +
+
+
+

{item.name}

+ {item.category} +
+ {item.quantity <= item.minStock && ( + + )} +
+ +
+
+ Stock actual: + + {item.quantity} {item.unit} + +
+
+ Stock mínimo: + {item.minStock} {item.unit} +
+
+ Costo: + €{item.cost.toFixed(2)} +
+
+ Valor total: + €{(item.quantity * item.cost).toFixed(2)} +
+
+ Proveedor: + {item.supplier} +
+
+ Última reposición: + {getDaysAgo(item.lastRestocked)} +
+
+ +
+ + + + + + + Reponer Stock - {item.name} + +
+
+ + +
+
+
Nuevo stock será:
+
+ {item.quantity + item.minStock} {item.unit} +
+
+ +
+
+
+
+
+
+
+ ))} +
+ + {filteredInventory.length === 0 && ( +
+ No se encontraron productos +
+ )} +
+ ); +}; + +export default InventoryManagement; diff --git a/src/components/restaurant/POSTerminal.tsx b/src/components/restaurant/POSTerminal.tsx new file mode 100644 index 0000000..d9f74be --- /dev/null +++ b/src/components/restaurant/POSTerminal.tsx @@ -0,0 +1,290 @@ +import { useState } from 'react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Badge } from '@/components/ui/badge'; +import { Separator } from '@/components/ui/separator'; +import { Search, Plus, Minus, Trash2, CreditCard, DollarSign, Printer } from 'lucide-react'; +import { toast } from 'sonner'; + +interface MenuItem { + id: string; + name: string; + price: number; + category: string; +} + +interface CartItem extends MenuItem { + quantity: number; + notes?: string; +} + +const POSTerminal = () => { + const [searchTerm, setSearchTerm] = useState(''); + const [selectedCategory, setSelectedCategory] = useState('all'); + const [selectedTable, setSelectedTable] = useState(null); + const [cart, setCart] = useState([]); + + const menuItems: MenuItem[] = [ + { id: '1', name: 'Paella Valenciana', price: 18.50, category: 'Platos Principales' }, + { id: '2', name: 'Gazpacho Andaluz', price: 6.50, category: 'Entrantes' }, + { id: '3', name: 'Pulpo a la Gallega', price: 16.00, category: 'Platos Principales' }, + { id: '4', name: 'Tarta de Santiago', price: 5.50, category: 'Postres' }, + { id: '5', name: 'Vino Tinto Reserva', price: 12.00, category: 'Bebidas' }, + { id: '6', name: 'Ensalada Mixta', price: 7.50, category: 'Entrantes' }, + { id: '7', name: 'Café Espresso', price: 2.00, category: 'Bebidas' }, + { id: '8', name: 'Crema Catalana', price: 4.50, category: 'Postres' } + ]; + + const categories = ['all', 'Entrantes', 'Platos Principales', 'Postres', 'Bebidas']; + + const filteredItems = menuItems.filter(item => { + const matchesSearch = item.name.toLowerCase().includes(searchTerm.toLowerCase()); + const matchesCategory = selectedCategory === 'all' || item.category === selectedCategory; + return matchesSearch && matchesCategory; + }); + + const addToCart = (item: MenuItem) => { + const existingItem = cart.find(i => i.id === item.id); + if (existingItem) { + setCart(cart.map(i => + i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i + )); + } else { + setCart([...cart, { ...item, quantity: 1 }]); + } + toast.success(`${item.name} agregado`); + }; + + const updateQuantity = (itemId: string, delta: number) => { + setCart(cart.map(item => { + if (item.id === itemId) { + const newQuantity = item.quantity + delta; + return newQuantity > 0 ? { ...item, quantity: newQuantity } : item; + } + return item; + }).filter(item => item.quantity > 0)); + }; + + const removeFromCart = (itemId: string) => { + setCart(cart.filter(item => item.id !== itemId)); + }; + + const subtotal = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0); + const tax = subtotal * 0.10; // 10% IVA + const total = subtotal + tax; + + const processPayment = (method: 'cash' | 'card') => { + if (!selectedTable) { + toast.error('Por favor selecciona una mesa'); + return; + } + if (cart.length === 0) { + toast.error('El carrito está vacío'); + return; + } + + toast.success(`Pago procesado - Mesa ${selectedTable}`); + setCart([]); + setSelectedTable(null); + }; + + const printReceipt = () => { + toast.success('Imprimiendo recibo...'); + }; + + return ( +
+ {/* Left: Menu Items */} +
+ {/* Table Selection */} + + +
+ Mesa: + {[1, 2, 3, 4, 5, 6, 7, 8].map(num => ( + + ))} +
+
+
+ + {/* Search and Categories */} +
+
+ + setSearchTerm(e.target.value)} + className="pl-10" + /> +
+ +
+ {categories.map(cat => ( + + ))} +
+
+ + {/* Menu Grid */} +
+ {filteredItems.map(item => ( + addToCart(item)} + > + +
+

{item.name}

+ {item.category} +
+ €{item.price.toFixed(2)} +
+
+
+
+ ))} +
+
+ + {/* Right: Cart and Checkout */} +
+ + + + Pedido Actual + {selectedTable && ( + Mesa {selectedTable} + )} + + + + + {/* Cart Items */} +
+ {cart.length === 0 ? ( +
+ Carrito vacío +
+ ) : ( + cart.map(item => ( +
+
+
+
{item.name}
+
+ €{item.price.toFixed(2)} × {item.quantity} +
+
+
+ €{(item.price * item.quantity).toFixed(2)} +
+
+ +
+ + + +
+
+ )) + )} +
+ + + + {/* Totals */} +
+
+ Subtotal: + €{subtotal.toFixed(2)} +
+
+ IVA (10%): + €{tax.toFixed(2)} +
+ +
+ Total: + €{total.toFixed(2)} +
+
+ + {/* Payment Buttons */} +
+ + + +
+
+
+
+
+ ); +}; + +export default POSTerminal; diff --git a/src/components/restaurant/StaffManagement.tsx b/src/components/restaurant/StaffManagement.tsx new file mode 100644 index 0000000..4d51397 --- /dev/null +++ b/src/components/restaurant/StaffManagement.tsx @@ -0,0 +1,292 @@ +import { useState } from 'react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Badge } from '@/components/ui/badge'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { Plus, Users, Clock, Calendar, Phone, Mail } from 'lucide-react'; +import { toast } from 'sonner'; + +interface Staff { + id: string; + name: string; + role: string; + email: string; + phone: string; + status: 'active' | 'inactive' | 'on_leave'; + shift: string; + hiredDate: Date; + hourlyRate: number; +} + +const StaffManagement = () => { + const [staff, setStaff] = useState([ + { + id: '1', + name: 'Carlos Martínez', + role: 'Mesero', + email: 'carlos@restaurant.com', + phone: '+34 600 123 456', + status: 'active', + shift: 'Mañana', + hiredDate: new Date('2023-01-15'), + hourlyRate: 12.50 + }, + { + id: '2', + name: 'Ana López', + role: 'Mesera', + email: 'ana@restaurant.com', + phone: '+34 600 234 567', + status: 'active', + shift: 'Tarde', + hiredDate: new Date('2023-03-20'), + hourlyRate: 12.50 + }, + { + id: '3', + name: 'Miguel Rodríguez', + role: 'Chef', + email: 'miguel@restaurant.com', + phone: '+34 600 345 678', + status: 'active', + shift: 'Completo', + hiredDate: new Date('2022-06-10'), + hourlyRate: 18.00 + }, + { + id: '4', + name: 'Laura García', + role: 'Ayudante de Cocina', + email: 'laura@restaurant.com', + phone: '+34 600 456 789', + status: 'on_leave', + shift: 'Mañana', + hiredDate: new Date('2023-09-05'), + hourlyRate: 10.50 + } + ]); + + const roles = ['Mesero', 'Chef', 'Ayudante de Cocina', 'Bartender', 'Host', 'Gerente']; + const shifts = ['Mañana', 'Tarde', 'Noche', 'Completo']; + + const activeStaff = staff.filter(s => s.status === 'active').length; + const onLeave = staff.filter(s => s.status === 'on_leave').length; + + const getStatusColor = (status: Staff['status']) => { + switch (status) { + case 'active': return 'default'; + case 'inactive': return 'secondary'; + case 'on_leave': return 'outline'; + default: return 'secondary'; + } + }; + + const getStatusLabel = (status: Staff['status']) => { + switch (status) { + case 'active': return 'Activo'; + case 'inactive': return 'Inactivo'; + case 'on_leave': return 'De Permiso'; + default: return status; + } + }; + + const updateStatus = (staffId: string, newStatus: Staff['status']) => { + setStaff(staff.map(s => + s.id === staffId ? { ...s, status: newStatus } : s + )); + toast.success('Estado actualizado'); + }; + + const getMonthsEmployed = (hiredDate: Date) => { + const months = Math.floor((Date.now() - hiredDate.getTime()) / (1000 * 60 * 60 * 24 * 30)); + return months; + }; + + return ( +
+ {/* Stats */} +
+ + + Total Personal + + + +
{staff.length}
+

empleados totales

+
+
+ + + + Activos + + + +
{activeStaff}
+

trabajando hoy

+
+
+ + + + De Permiso + + + +
{onLeave}
+

ausentes hoy

+
+
+ + + + Costo Hora + + + +
+ €{staff.reduce((sum, s) => sum + s.hourlyRate, 0).toFixed(2)} +
+

por hora total

+
+
+
+ + {/* Actions */} +
+
+

Equipo de Trabajo

+

Gestiona tu personal del restaurante

+
+ + + + + + + Agregar Nuevo Empleado + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ +
+
+
+
+ + {/* Staff List */} +
+ {staff.map((member) => ( + + +
+
+ {member.name} +

{member.role}

+
+ + {getStatusLabel(member.status)} + +
+
+ +
+
+ + {member.email} +
+
+ + {member.phone} +
+
+ + Turno: {member.shift} +
+
+ + {getMonthsEmployed(member.hiredDate)} meses en la empresa +
+
+ +
+
Tarifa por Hora
+
€{member.hourlyRate.toFixed(2)}
+
+ +
+ +
+
+
+ ))} +
+
+ ); +}; + +export default StaffManagement; diff --git a/src/components/restaurant/TableConfiguration.tsx b/src/components/restaurant/TableConfiguration.tsx new file mode 100644 index 0000000..161cf66 --- /dev/null +++ b/src/components/restaurant/TableConfiguration.tsx @@ -0,0 +1,262 @@ +import { useState } from 'react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Card, CardContent } from '@/components/ui/card'; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { Plus, Edit, Trash2, Grid3x3 } from 'lucide-react'; +import { toast } from 'sonner'; + +interface Table { + id: string; + number: number; + seats: number; + section: string; + status: 'available' | 'occupied' | 'reserved' | 'maintenance'; + position: { x: number; y: number }; +} + +const TableConfiguration = () => { + const [tables, setTables] = useState([ + { id: '1', number: 1, seats: 4, section: 'Interior', status: 'available', position: { x: 50, y: 50 } }, + { id: '2', number: 2, seats: 2, section: 'Interior', status: 'occupied', position: { x: 200, y: 50 } }, + { id: '3', number: 3, seats: 6, section: 'Terraza', status: 'available', position: { x: 50, y: 200 } }, + { id: '4', number: 4, seats: 4, section: 'Terraza', status: 'reserved', position: { x: 200, y: 200 } }, + { id: '5', number: 5, seats: 8, section: 'VIP', status: 'available', position: { x: 350, y: 125 } } + ]); + + const [editingTable, setEditingTable] = useState(null); + + const getStatusColor = (status: Table['status']) => { + switch (status) { + case 'available': return 'bg-green-500'; + case 'occupied': return 'bg-red-500'; + case 'reserved': return 'bg-yellow-500'; + case 'maintenance': return 'bg-gray-500'; + default: return 'bg-gray-500'; + } + }; + + const getStatusLabel = (status: Table['status']) => { + switch (status) { + case 'available': return 'Disponible'; + case 'occupied': return 'Ocupada'; + case 'reserved': return 'Reservada'; + case 'maintenance': return 'Mantenimiento'; + default: return status; + } + }; + + const addTable = () => { + const newTable: Table = { + id: String(tables.length + 1), + number: tables.length + 1, + seats: 4, + section: 'Interior', + status: 'available', + position: { x: 100, y: 100 } + }; + setTables([...tables, newTable]); + toast.success('Mesa agregada correctamente'); + }; + + const updateTable = (updatedTable: Table) => { + setTables(tables.map(t => t.id === updatedTable.id ? updatedTable : t)); + setEditingTable(null); + toast.success('Mesa actualizada correctamente'); + }; + + const deleteTable = (tableId: string) => { + setTables(tables.filter(t => t.id !== tableId)); + toast.success('Mesa eliminada correctamente'); + }; + + const changeStatus = (tableId: string, newStatus: Table['status']) => { + setTables(tables.map(t => + t.id === tableId ? { ...t, status: newStatus } : t + )); + toast.success('Estado actualizado'); + }; + + const sections = ['Interior', 'Terraza', 'VIP', 'Bar']; + + return ( +
+
+
+

Layout del Restaurante

+

+ {tables.length} mesas configuradas +

+
+ + + + + + + Agregar Nueva Mesa + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
+ + {/* Visual Layout */} + + +
+
+
+
+ Disponible +
+
+
+ Ocupada +
+
+
+ Reservada +
+
+ + {tables.map((table) => ( +
+
+
+
#{table.number}
+
{table.seats} personas
+
+
+
+ ))} +
+
+
+ + {/* Tables List */} +
+ {tables.map((table) => ( + + +
+
+

Mesa #{table.number}

+

{table.section}

+
+
+ + + + + + + Editar Mesa #{table.number} + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+ +
+
+ +
+
+ Capacidad: + {table.seats} personas +
+
+ Estado: + + {getStatusLabel(table.status)} + +
+
+
+
+ ))} +
+
+ ); +}; + +export default TableConfiguration; diff --git a/src/pages/dashboard/HotelManagement.tsx b/src/pages/dashboard/HotelManagement.tsx index 9cd28ca..7f7405c 100644 --- a/src/pages/dashboard/HotelManagement.tsx +++ b/src/pages/dashboard/HotelManagement.tsx @@ -22,6 +22,7 @@ import RoomManagement from '@/components/hotel/RoomManagement'; import CheckInSystem from '@/components/hotel/CheckInSystem'; import RoomService from '@/components/hotel/RoomService'; import KeylessEntry from '@/components/hotel/KeylessEntry'; +import StaffManagement from '@/components/hotel/StaffManagement'; const HotelManagement = () => { const [activeTab, setActiveTab] = useState('overview'); @@ -122,7 +123,7 @@ const HotelManagement = () => { {/* Main Content Tabs */} - + Resumen @@ -143,6 +144,10 @@ const HotelManagement = () => { Acceso + + + Personal + @@ -271,6 +276,10 @@ const HotelManagement = () => { + + + + ); diff --git a/src/pages/dashboard/RestaurantPOS.tsx b/src/pages/dashboard/RestaurantPOS.tsx index 496d47e..f3e9243 100644 --- a/src/pages/dashboard/RestaurantPOS.tsx +++ b/src/pages/dashboard/RestaurantPOS.tsx @@ -6,6 +6,10 @@ import DigitalMenu from '@/components/restaurant/DigitalMenu'; import TableOrders from '@/components/restaurant/TableOrders'; import KitchenDisplay from '@/components/restaurant/KitchenDisplay'; import BillManagement from '@/components/restaurant/BillManagement'; +import TableConfiguration from '@/components/restaurant/TableConfiguration'; +import InventoryManagement from '@/components/restaurant/InventoryManagement'; +import POSTerminal from '@/components/restaurant/POSTerminal'; +import StaffManagement from '@/components/restaurant/StaffManagement'; const RestaurantPOS = () => { const [activeOrders] = useState(12); @@ -63,20 +67,38 @@ const RestaurantPOS = () => { {/* Main Content */} - - - Menú Digital - Pedidos en Mesa + + + POS + Pedidos Cocina - Facturación + Cuentas + Mesas + Menú + Inventario + Personal + + + + Terminal Punto de Venta + + Sistema POS completo conectado con inventario + + + + + + + + - Menú Digital con QR + Gestión de Menú y Platos - Gestiona tu menú y genera códigos QR para las mesas + Configura tu menú y genera códigos QR para las mesas @@ -126,6 +148,48 @@ const RestaurantPOS = () => { + + + + + Configuración de Mesas + + Gestiona el layout y estado de las mesas del restaurante + + + + + + + + + + + + Gestión de Inventario + + Control de stock, alertas y reposición de productos + + + + + + + + + + + + Gestión de Personal + + Administra tu equipo de restaurante + + + + + + + );