diff --git a/src/App.tsx b/src/App.tsx index 66d0da4..b3b5ed1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -32,6 +32,7 @@ import Profile from "./pages/dashboard/Profile"; import Settings from "./pages/dashboard/Settings"; import Invoices from "./pages/dashboard/Invoices"; import InvoiceDetail from "./pages/dashboard/InvoiceDetail"; +import HotelManagement from "./pages/dashboard/HotelManagement"; import NotFound from "./pages/NotFound"; const queryClient = new QueryClient(); @@ -239,6 +240,14 @@ const AppRouter = () => ( } /> + + + + + + } /> + {/* Catch-all route */} } /> diff --git a/src/components/DashboardLayout.tsx b/src/components/DashboardLayout.tsx index 2a1bbbe..018917c 100644 --- a/src/components/DashboardLayout.tsx +++ b/src/components/DashboardLayout.tsx @@ -36,7 +36,8 @@ import { Eye, Megaphone, ChevronDown, - ChevronRight + ChevronRight, + Hotel } from 'lucide-react'; const DashboardLayout = ({ children }: { children: React.ReactNode }) => { @@ -59,6 +60,7 @@ const DashboardLayout = ({ children }: { children: React.ReactNode }) => { { icon: Home, label: 'Dashboard', path: '/dashboard' }, { icon: Settings, label: 'Admin Panel', path: '/dashboard/admin' }, { icon: Plus, label: 'Channel Manager', path: '/dashboard/channel-manager' }, + { icon: Hotel, label: 'Hotel Management', path: '/dashboard/hotel-management' }, { icon: Wallet, label: 'Wallet', path: '/dashboard/wallet' }, { icon: MessageSquare, label: 'Message', path: '/dashboard/messages', badge: '2' }, ]; diff --git a/src/components/hotel/CheckInSystem.tsx b/src/components/hotel/CheckInSystem.tsx new file mode 100644 index 0000000..e84df31 --- /dev/null +++ b/src/components/hotel/CheckInSystem.tsx @@ -0,0 +1,319 @@ +import React, { useState } from 'react'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +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 { + QrCode, + Scan, + UserCheck, + Calendar, + Clock, + MapPin, + CreditCard, + CheckCircle, + Download, + Mail +} from 'lucide-react'; +import { useToast } from '@/hooks/use-toast'; + +interface CheckInData { + reservationId: string; + guestName: string; + roomNumber: string; + checkIn: string; + checkOut: string; + adults: number; + children: number; +} + +const CheckInSystem = () => { + const { toast } = useToast(); + const [scanMode, setScanMode] = useState(false); + const [reservationCode, setReservationCode] = useState(''); + const [checkInData, setCheckInData] = useState(null); + const [isProcessing, setIsProcessing] = useState(false); + + const handleSearchReservation = () => { + setIsProcessing(true); + + // Simular búsqueda + setTimeout(() => { + setCheckInData({ + reservationId: reservationCode || 'RES-2025-001', + guestName: 'Juan Pérez García', + roomNumber: '305', + checkIn: '2025-01-15', + checkOut: '2025-01-20', + adults: 2, + children: 1 + }); + setIsProcessing(false); + toast({ + title: "Reserva Encontrada", + description: "La información de la reserva ha sido cargada.", + }); + }, 1500); + }; + + const handleCheckIn = () => { + if (!checkInData) return; + + setIsProcessing(true); + + setTimeout(() => { + toast({ + title: "Check-in Exitoso", + description: `Habitación ${checkInData.roomNumber} asignada a ${checkInData.guestName}`, + }); + setIsProcessing(false); + + // Reset + setCheckInData(null); + setReservationCode(''); + }, 2000); + }; + + const generateQRCode = () => { + toast({ + title: "Código QR Generado", + description: "El código QR ha sido enviado al huésped por email.", + }); + }; + + return ( +
+ {/* Search Reservation */} + + + + + Buscar Reserva + + + +
+
+ + setReservationCode(e.target.value)} + onKeyPress={(e) => e.key === 'Enter' && handleSearchReservation()} + /> +
+
+ + +
+
+ + {scanMode && ( +
+ +

Escanea el código QR de la reserva

+

El huésped puede mostrar el QR desde su email de confirmación

+
+ )} +
+
+ + {/* Reservation Details */} + {checkInData && ( + <> + + +
+ Detalles de la Reserva + + Confirmada + +
+
+ +
+
+
+ +

{checkInData.guestName}

+
+
+ +

{checkInData.reservationId}

+
+
+ +

#{checkInData.roomNumber}

+
+
+ +
+
+ +
+ +

{checkInData.checkIn}

+
+
+
+ +
+ +

{checkInData.checkOut}

+
+
+
+ +
+ +

15:00

+
+
+
+
+ +
+ + {checkInData.adults} Adulto(s) + + {checkInData.children > 0 && ( + + {checkInData.children} Niño(s) + + )} +
+
+
+ + {/* Actions */} + + + Acciones de Check-in + + +
+ + + + +
+ +
+

+ + Información de Pago +

+
+
+ Tarifa por noche: + $150.00 +
+
+ Noches: + 5 +
+
+ Impuestos (18%): + $135.00 +
+
+ Total: + $885.00 +
+
+ + ✓ Pago Confirmado + +
+
+
+
+
+ + )} + + {/* Quick Stats */} +
+ + +
+
+

Check-ins Hoy

+

15

+
+ +
+
+
+ + +
+
+

Pendientes

+

8

+
+ +
+
+
+ + +
+
+

Check-outs Hoy

+

12

+
+ +
+
+
+
+
+ ); +}; + +export default CheckInSystem; diff --git a/src/components/hotel/KeylessEntry.tsx b/src/components/hotel/KeylessEntry.tsx new file mode 100644 index 0000000..a177240 --- /dev/null +++ b/src/components/hotel/KeylessEntry.tsx @@ -0,0 +1,387 @@ +import React, { useState } from 'react'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +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 { + Key, + QrCode, + Smartphone, + Clock, + Shield, + CheckCircle, + AlertCircle, + Copy, + Send, + Unlock, + Lock, + Calendar, + RefreshCw +} from 'lucide-react'; +import { useToast } from '@/hooks/use-toast'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; + +interface AccessCode { + id: string; + roomNumber: string; + guestName: string; + code: string; + qrCode: string; + validFrom: string; + validUntil: string; + status: 'active' | 'expired' | 'revoked'; + accessCount: number; +} + +const KeylessEntry = () => { + const { toast } = useToast(); + const [accessCodes, setAccessCodes] = useState([ + { + id: '1', + roomNumber: '305', + guestName: 'Juan Pérez', + code: '8A9B-C4D7', + qrCode: 'QR-305-8A9BC4D7', + validFrom: '2025-01-15 14:00', + validUntil: '2025-01-20 12:00', + status: 'active', + accessCount: 12 + }, + { + id: '2', + roomNumber: '412', + guestName: 'María González', + code: '5F2E-H8J3', + qrCode: 'QR-412-5F2EH8J3', + validFrom: '2025-01-14 15:00', + validUntil: '2025-01-19 11:00', + status: 'active', + accessCount: 8 + } + ]); + + const [newCodeForm, setNewCodeForm] = useState({ + roomNumber: '', + guestName: '', + validFrom: '', + validUntil: '' + }); + + const generateCode = () => { + const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; + let code = ''; + for (let i = 0; i < 8; i++) { + if (i === 4) code += '-'; + code += chars.charAt(Math.floor(Math.random() * chars.length)); + } + return code; + }; + + const handleGenerateAccessCode = () => { + if (!newCodeForm.roomNumber || !newCodeForm.guestName) { + toast({ + title: "Error", + description: "Por favor completa todos los campos requeridos", + variant: "destructive" + }); + return; + } + + const newCode: AccessCode = { + id: String(accessCodes.length + 1), + roomNumber: newCodeForm.roomNumber, + guestName: newCodeForm.guestName, + code: generateCode(), + qrCode: `QR-${newCodeForm.roomNumber}-${Date.now()}`, + validFrom: newCodeForm.validFrom || new Date().toISOString(), + validUntil: newCodeForm.validUntil || new Date(Date.now() + 5 * 24 * 60 * 60 * 1000).toISOString(), + status: 'active', + accessCount: 0 + }; + + setAccessCodes([...accessCodes, newCode]); + + toast({ + title: "Código Generado", + description: `Código de acceso creado para habitación ${newCodeForm.roomNumber}`, + }); + + // Reset form + setNewCodeForm({ + roomNumber: '', + guestName: '', + validFrom: '', + validUntil: '' + }); + }; + + const copyCode = (code: string) => { + navigator.clipboard.writeText(code); + toast({ + title: "Código Copiado", + description: "El código ha sido copiado al portapapeles", + }); + }; + + const sendCode = (code: AccessCode) => { + toast({ + title: "Código Enviado", + description: `Código enviado a ${code.guestName} por email y SMS`, + }); + }; + + const revokeCode = (codeId: string) => { + setAccessCodes(accessCodes.map(code => + code.id === codeId ? { ...code, status: 'revoked' as const } : code + )); + toast({ + title: "Código Revocado", + description: "El código de acceso ha sido desactivado", + variant: "destructive" + }); + }; + + const statusConfig = { + active: { label: 'Activo', color: 'bg-green-100 text-green-800', icon: CheckCircle }, + expired: { label: 'Expirado', color: 'bg-gray-100 text-gray-800', icon: Clock }, + revoked: { label: 'Revocado', color: 'bg-red-100 text-red-800', icon: AlertCircle } + }; + + return ( +
+ {/* Stats */} +
+ + +
+
+

Códigos Activos

+

{accessCodes.filter(c => c.status === 'active').length}

+
+ +
+
+
+ + +
+
+

Total Accesos Hoy

+

47

+
+ +
+
+
+ + +
+
+

Códigos QR

+

{accessCodes.length}

+
+ +
+
+
+ + +
+
+

Seguridad

+

99.9%

+
+ +
+
+
+
+ + {/* Generate New Code */} + + + + + Generar Nuevo Código de Acceso + + + +
+
+ + setNewCodeForm({ ...newCodeForm, roomNumber: e.target.value })} + /> +
+
+ + setNewCodeForm({ ...newCodeForm, guestName: e.target.value })} + /> +
+
+ + setNewCodeForm({ ...newCodeForm, validFrom: e.target.value })} + /> +
+
+ + setNewCodeForm({ ...newCodeForm, validUntil: e.target.value })} + /> +
+
+ +
+
+ + {/* Active Codes */} + + + Códigos de Acceso Generados + + +
+ {accessCodes.map((code) => { + const StatusIcon = statusConfig[code.status].icon; + return ( +
+
+
+
+
+ {code.roomNumber} +
+
+

{code.guestName}

+

ID: {code.id}

+
+ + + {statusConfig[code.status].label} + +
+ +
+
+ +
+

Código PIN

+

{code.code}

+
+ +
+
+ +
+

Código QR

+

{code.qrCode}

+
+
+
+ +
+
+ + {code.validFrom} +
+ +
+ + {code.validUntil} +
+
+ + {code.accessCount} accesos +
+
+
+ +
+ {code.status === 'active' && ( + <> + + + + )} +
+
+
+ ); + })} +
+
+
+ + {/* Security Info */} + + + + + Seguridad del Sistema + + + +
    +
  • + + Códigos encriptados de 256-bit +
  • +
  • + + Expiración automática de códigos +
  • +
  • + + Registro de todos los accesos +
  • +
  • + + Notificaciones en tiempo real +
  • +
+
+
+
+ ); +}; + +export default KeylessEntry; diff --git a/src/components/hotel/RoomManagement.tsx b/src/components/hotel/RoomManagement.tsx new file mode 100644 index 0000000..98e6684 --- /dev/null +++ b/src/components/hotel/RoomManagement.tsx @@ -0,0 +1,271 @@ +import React, { useState } from 'react'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Badge } from '@/components/ui/badge'; +import { + Bed, + Plus, + Search, + Filter, + Edit, + Trash2, + CheckCircle, + AlertCircle, + Wrench, + Users, + DollarSign, + Wifi, + Tv, + Wind, + Coffee +} from 'lucide-react'; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { Label } from '@/components/ui/label'; +import { Textarea } from '@/components/ui/textarea'; +import { Switch } from '@/components/ui/switch'; + +interface Room { + id: string; + number: string; + type: 'single' | 'double' | 'suite' | 'deluxe'; + status: 'available' | 'occupied' | 'maintenance' | 'cleaning'; + floor: number; + capacity: number; + price: number; + amenities: string[]; + guest?: { + name: string; + checkIn: string; + checkOut: string; + }; +} + +const RoomManagement = () => { + const [searchTerm, setSearchTerm] = useState(''); + const [filterStatus, setFilterStatus] = useState('all'); + const [rooms, setRooms] = useState([ + { + id: '1', + number: '101', + type: 'single', + status: 'available', + floor: 1, + capacity: 1, + price: 80, + amenities: ['wifi', 'tv', 'ac'] + }, + { + id: '2', + number: '102', + type: 'double', + status: 'occupied', + floor: 1, + capacity: 2, + price: 120, + amenities: ['wifi', 'tv', 'ac', 'coffee'], + guest: { + name: 'Juan Pérez', + checkIn: '2025-01-15', + checkOut: '2025-01-20' + } + }, + { + id: '3', + number: '201', + type: 'suite', + status: 'maintenance', + floor: 2, + capacity: 4, + price: 250, + amenities: ['wifi', 'tv', 'ac', 'coffee'] + }, + { + id: '4', + number: '202', + type: 'deluxe', + status: 'cleaning', + floor: 2, + capacity: 3, + price: 180, + amenities: ['wifi', 'tv', 'ac', 'coffee'] + } + ]); + + const statusConfig = { + available: { label: 'Disponible', color: 'bg-green-500', icon: CheckCircle }, + occupied: { label: 'Ocupada', color: 'bg-blue-500', icon: Users }, + maintenance: { label: 'Mantenimiento', color: 'bg-orange-500', icon: Wrench }, + cleaning: { label: 'Limpieza', color: 'bg-purple-500', icon: AlertCircle } + }; + + const amenitiesIcons: Record = { + wifi: Wifi, + tv: Tv, + ac: Wind, + coffee: Coffee + }; + + const filteredRooms = rooms.filter(room => { + const matchesSearch = room.number.toLowerCase().includes(searchTerm.toLowerCase()); + const matchesStatus = filterStatus === 'all' || room.status === filterStatus; + return matchesSearch && matchesStatus; + }); + + const changeRoomStatus = (roomId: string, newStatus: Room['status']) => { + setRooms(rooms.map(room => + room.id === roomId ? { ...room, status: newStatus } : room + )); + }; + + return ( +
+ {/* Header & Actions */} + + +
+
+
+ + setSearchTerm(e.target.value)} + className="pl-10" + /> +
+
+
+ + + + + + + + Agregar Nueva Habitación + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
+
+
+
+ + {/* Rooms Grid */} +
+ {filteredRooms.map((room) => { + const StatusIcon = statusConfig[room.status].icon; + return ( + + +
+
+ + Hab. {room.number} +
+
+
+
+ +
+ Piso {room.floor} + {room.type} +
+ +
+ + {statusConfig[room.status].label} +
+ + {room.guest && ( +
+

{room.guest.name}

+

+ {room.guest.checkIn} → {room.guest.checkOut} +

+
+ )} + +
+
+ + {room.price} +
+
+ {room.amenities.slice(0, 3).map((amenity) => { + const Icon = amenitiesIcons[amenity]; + return Icon ? : null; + })} +
+
+ +
+ + +
+
+
+ ); + })} +
+
+ ); +}; + +export default RoomManagement; diff --git a/src/components/hotel/RoomService.tsx b/src/components/hotel/RoomService.tsx new file mode 100644 index 0000000..361e949 --- /dev/null +++ b/src/components/hotel/RoomService.tsx @@ -0,0 +1,317 @@ +import React, { useState } from 'react'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Badge } from '@/components/ui/badge'; +import { Input } from '@/components/ui/input'; +import { + UtensilsCrossed, + Coffee, + Pizza, + Wine, + Salad, + Clock, + CheckCircle, + AlertCircle, + ChefHat, + Bell, + DollarSign, + Search +} from 'lucide-react'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { useToast } from '@/hooks/use-toast'; + +interface Order { + id: string; + roomNumber: string; + guestName: string; + items: { name: string; quantity: number; price: number }[]; + status: 'pending' | 'preparing' | 'delivering' | 'completed'; + orderTime: string; + total: number; +} + +const RoomService = () => { + const { toast } = useToast(); + const [filterStatus, setFilterStatus] = useState('all'); + const [searchTerm, setSearchTerm] = useState(''); + const [orders, setOrders] = useState([ + { + id: 'ORD-001', + roomNumber: '305', + guestName: 'Juan Pérez', + items: [ + { name: 'Desayuno Continental', quantity: 2, price: 25 }, + { name: 'Café Americano', quantity: 2, price: 5 } + ], + status: 'preparing', + orderTime: '08:15 AM', + total: 60 + }, + { + id: 'ORD-002', + roomNumber: '412', + guestName: 'María González', + items: [ + { name: 'Club Sandwich', quantity: 1, price: 18 }, + { name: 'Ensalada César', quantity: 1, price: 15 }, + { name: 'Limonada', quantity: 2, price: 6 } + ], + status: 'delivering', + orderTime: '12:30 PM', + total: 39 + }, + { + id: 'ORD-003', + roomNumber: '208', + guestName: 'Carlos Martínez', + items: [ + { name: 'Botella de Vino', quantity: 1, price: 45 } + ], + status: 'pending', + orderTime: '07:45 PM', + total: 45 + } + ]); + + const statusConfig = { + pending: { label: 'Pendiente', color: 'bg-yellow-100 text-yellow-800', icon: Bell }, + preparing: { label: 'Preparando', color: 'bg-blue-100 text-blue-800', icon: ChefHat }, + delivering: { label: 'En Camino', color: 'bg-purple-100 text-purple-800', icon: Clock }, + completed: { label: 'Completado', color: 'bg-green-100 text-green-800', icon: CheckCircle } + }; + + const updateOrderStatus = (orderId: string, newStatus: Order['status']) => { + setOrders(orders.map(order => + order.id === orderId ? { ...order, status: newStatus } : order + )); + toast({ + title: "Estado Actualizado", + description: `Orden ${orderId} actualizada a ${statusConfig[newStatus].label}`, + }); + }; + + const filteredOrders = orders.filter(order => { + const matchesSearch = + order.roomNumber.includes(searchTerm) || + order.guestName.toLowerCase().includes(searchTerm.toLowerCase()) || + order.id.toLowerCase().includes(searchTerm.toLowerCase()); + const matchesStatus = filterStatus === 'all' || order.status === filterStatus; + return matchesSearch && matchesStatus; + }); + + const stats = { + pending: orders.filter(o => o.status === 'pending').length, + preparing: orders.filter(o => o.status === 'preparing').length, + delivering: orders.filter(o => o.status === 'delivering').length, + completed: orders.filter(o => o.status === 'completed').length + }; + + return ( +
+ {/* Stats */} +
+ + +
+
+

Pendientes

+

{stats.pending}

+
+ +
+
+
+ + +
+
+

Preparando

+

{stats.preparing}

+
+ +
+
+
+ + +
+
+

En Camino

+

{stats.delivering}

+
+ +
+
+
+ + +
+
+

Completados

+

{stats.completed}

+
+ +
+
+
+
+ + {/* Filters */} + + +
+
+ + setSearchTerm(e.target.value)} + className="pl-10" + /> +
+ +
+
+
+ + {/* Orders List */} +
+ {filteredOrders.map((order) => { + const StatusIcon = statusConfig[order.status].icon; + return ( + + +
+
+
+
+ {order.roomNumber} +
+
+

{order.guestName}

+

Orden #{order.id}

+
+ + + {statusConfig[order.status].label} + +
+ +
+ {order.items.map((item, idx) => ( +
+ + {item.quantity}x {item.name} + + ${item.price * item.quantity} +
+ ))} +
+ +
+
+ + {order.orderTime} +
+
+ + Total: ${order.total} +
+
+
+ +
+ {order.status === 'pending' && ( + + )} + {order.status === 'preparing' && ( + + )} + {order.status === 'delivering' && ( + + )} + {order.status === 'completed' && ( + + ✓ Completado + + )} +
+
+
+
+ ); + })} +
+ + {filteredOrders.length === 0 && ( + + + +

+ No hay órdenes +

+

+ No se encontraron órdenes con los filtros aplicados +

+
+
+ )} + + {/* Popular Menu Items */} + + + Menú Más Solicitado + + +
+ {[ + { name: 'Desayuno Continental', icon: Coffee, orders: 45 }, + { name: 'Club Sandwich', icon: Pizza, orders: 32 }, + { name: 'Vino Tinto', icon: Wine, orders: 28 }, + { name: 'Ensalada César', icon: Salad, orders: 21 } + ].map((item, idx) => ( +
+ +
+

{item.name}

+

{item.orders} pedidos

+
+
+ ))} +
+
+
+
+ ); +}; + +export default RoomService; diff --git a/src/pages/dashboard/HotelManagement.tsx b/src/pages/dashboard/HotelManagement.tsx new file mode 100644 index 0000000..9cd28ca --- /dev/null +++ b/src/pages/dashboard/HotelManagement.tsx @@ -0,0 +1,279 @@ +import React, { useState } from 'react'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { + Hotel, + DoorOpen, + BellRing, + QrCode, + Users, + Bed, + Calendar, + Clock, + CheckCircle, + AlertCircle, + Sparkles, + UtensilsCrossed, + Key +} from 'lucide-react'; +import RoomManagement from '@/components/hotel/RoomManagement'; +import CheckInSystem from '@/components/hotel/CheckInSystem'; +import RoomService from '@/components/hotel/RoomService'; +import KeylessEntry from '@/components/hotel/KeylessEntry'; + +const HotelManagement = () => { + const [activeTab, setActiveTab] = useState('overview'); + + // Stats de ejemplo + const stats = { + totalRooms: 120, + occupied: 85, + available: 25, + maintenance: 10, + checkInsToday: 15, + checkOutsToday: 12, + roomServiceOrders: 8, + revenue: 45600 + }; + + const occupancyRate = ((stats.occupied / stats.totalRooms) * 100).toFixed(1); + + return ( +
+ {/* Header */} +
+
+
+ +
+
+

Hotel Management

+

Sistema integral de gestión hotelera

+
+
+
+ + {/* Overview Stats */} + {activeTab === 'overview' && ( +
+ + +
+
+

Ocupación

+

{occupancyRate}%

+

{stats.occupied}/{stats.totalRooms} habitaciones

+
+
+ +
+
+
+
+ + + +
+
+

Check-ins Hoy

+

{stats.checkInsToday}

+

{stats.checkOutsToday} check-outs

+
+
+ +
+
+
+
+ + + +
+
+

Room Service

+

{stats.roomServiceOrders}

+

Pedidos activos

+
+
+ +
+
+
+
+ + + +
+
+

Ingresos Hoy

+

${stats.revenue.toLocaleString()}

+

+12% vs ayer

+
+
+ +
+
+
+
+
+ )} + + {/* Main Content Tabs */} + + + + + Resumen + + + + Habitaciones + + + + Check-in + + + + Room Service + + + + Acceso + + + + +
+ {/* Habitaciones por Estado */} + + + + + Estado de Habitaciones + + + +
+
+
+
+ Disponibles +
+ {stats.available} +
+
+
+
+ Ocupadas +
+ {stats.occupied} +
+
+
+
+ Mantenimiento +
+ {stats.maintenance} +
+
+
+
+ + {/* Check-ins Pendientes */} + + + + + Check-ins de Hoy + + + +
+ {[1, 2, 3].map((i) => ( +
+
+

Habitación {100 + i}

+

Reserva #{1234 + i} - 2 huéspedes

+
+
+

14:00

+ Pendiente +
+
+ ))} +
+ +
+
+
+ + {/* Quick Actions */} + + + Acciones Rápidas + + +
+ + + + +
+
+
+
+ + + + + + + + + + + + + + + + +
+
+ ); +}; + +export default HotelManagement;