import React, { useState, useEffect } from 'react'; import { LogIn, LogOut, Search, User, Hotel, CheckCircle, AlertCircle, FileText, CreditCard, Printer, Sparkles, ChevronRight, Eye, XCircle, Loader2, Plus, Edit, Trash2, X, Upload, Image as ImageIcon, Check, Calendar, Wrench } from 'lucide-react'; import { bookingService, Booking, roomService, Room, serviceService, Service } from '../../services/api'; import { toast } from 'react-toastify'; import Loading from '../../components/common/Loading'; import CurrencyIcon from '../../components/common/CurrencyIcon'; import Pagination from '../../components/common/Pagination'; import apiClient from '../../services/api/apiClient'; import { useFormatCurrency } from '../../hooks/useFormatCurrency'; import { parseDateLocal } from '../../utils/format'; type ReceptionTab = 'overview' | 'check-in' | 'check-out' | 'bookings' | 'rooms' | 'services'; interface GuestInfo { name: string; id_number: string; phone: string; } interface ServiceItem { service_name: string; quantity: number; price: number; total: number; } const ReceptionDashboardPage: React.FC = () => { const { formatCurrency } = useFormatCurrency(); const [activeTab, setActiveTab] = useState('overview'); // Check-in State const [checkInBookingNumber, setCheckInBookingNumber] = useState(''); const [checkInBooking, setCheckInBooking] = useState(null); const [checkInLoading, setCheckInLoading] = useState(false); const [checkInSearching, setCheckInSearching] = useState(false); const [actualRoomNumber, setActualRoomNumber] = useState(''); const [guests, setGuests] = useState([{ name: '', id_number: '', phone: '' }]); const [extraPersons, setExtraPersons] = useState(0); const [children, setChildren] = useState(0); const [additionalFee, setAdditionalFee] = useState(0); // Check-out State const [checkOutBookingNumber, setCheckOutBookingNumber] = useState(''); const [checkOutBooking, setCheckOutBooking] = useState(null); const [checkOutLoading, setCheckOutLoading] = useState(false); const [checkOutSearching, setCheckOutSearching] = useState(false); const [checkOutServices, setCheckOutServices] = useState([]); const [paymentMethod, setPaymentMethod] = useState<'cash' | 'stripe'>('cash'); const [discount, setDiscount] = useState(0); const [showInvoice, setShowInvoice] = useState(false); // Bookings Management State const [bookings, setBookings] = useState([]); const [bookingsLoading, setBookingsLoading] = useState(true); const [selectedBooking, setSelectedBooking] = useState(null); const [showBookingDetailModal, setShowBookingDetailModal] = useState(false); const [updatingBookingId, setUpdatingBookingId] = useState(null); const [cancellingBookingId, setCancellingBookingId] = useState(null); const [bookingFilters, setBookingFilters] = useState({ search: '', status: '', }); const [bookingCurrentPage, setBookingCurrentPage] = useState(1); const [bookingTotalPages, setBookingTotalPages] = useState(1); const [bookingTotalItems, setBookingTotalItems] = useState(0); const bookingItemsPerPage = 5; // Rooms Management State const [rooms, setRooms] = useState([]); const [roomsLoading, setRoomsLoading] = useState(true); const [showRoomModal, setShowRoomModal] = useState(false); const [editingRoom, setEditingRoom] = useState(null); const [selectedRooms, setSelectedRooms] = useState([]); const [roomFilters, setRoomFilters] = useState({ search: '', status: '', type: '', }); const [roomCurrentPage, setRoomCurrentPage] = useState(1); const [roomTotalPages, setRoomTotalPages] = useState(1); const [roomTotalItems, setRoomTotalItems] = useState(0); const roomItemsPerPage = 5; const [roomFormData, setRoomFormData] = useState({ room_number: '', floor: 1, room_type_id: 1, status: 'available' as 'available' | 'occupied' | 'maintenance', featured: false, price: '', description: '', capacity: '', room_size: '', view: '', amenities: [] as string[], }); const [availableAmenities, setAvailableAmenities] = useState([]); const [roomTypes, setRoomTypes] = useState>([]); const [uploadingImages, setUploadingImages] = useState(false); const [selectedFiles, setSelectedFiles] = useState([]); // Services Management State const [services, setServices] = useState([]); const [servicesLoading, setServicesLoading] = useState(true); const [showServiceModal, setShowServiceModal] = useState(false); const [editingService, setEditingService] = useState(null); const [serviceFilters, setServiceFilters] = useState({ search: '', status: '', }); const [serviceCurrentPage, setServiceCurrentPage] = useState(1); const [serviceTotalPages, setServiceTotalPages] = useState(1); const [serviceTotalItems, setServiceTotalItems] = useState(0); const serviceItemsPerPage = 5; const [serviceFormData, setServiceFormData] = useState({ name: '', description: '', price: 0, unit: 'time', status: 'active' as 'active' | 'inactive', }); // Check-in Functions const handleCheckInSearch = async () => { if (!checkInBookingNumber.trim()) { toast.error('Please enter booking number'); return; } try { setCheckInSearching(true); const response = await bookingService.checkBookingByNumber(checkInBookingNumber); setCheckInBooking(response.data.booking); setActualRoomNumber(response.data.booking.room?.room_number || ''); toast.success('Booking found'); } catch (error: any) { toast.error(error.response?.data?.message || 'Booking not found'); setCheckInBooking(null); } finally { setCheckInSearching(false); } }; const handleAddGuest = () => { setGuests([...guests, { name: '', id_number: '', phone: '' }]); }; const handleRemoveGuest = (index: number) => { if (guests.length > 1) { setGuests(guests.filter((_, i) => i !== index)); } }; const handleGuestChange = (index: number, field: keyof GuestInfo, value: string) => { const newGuests = [...guests]; newGuests[index][field] = value; setGuests(newGuests); }; const calculateCheckInAdditionalFee = () => { const extraPersonFee = extraPersons * 200000; const childrenFee = children * 100000; const total = extraPersonFee + childrenFee; setAdditionalFee(total); return total; }; const handleCheckIn = async () => { if (!checkInBooking) return; if (!actualRoomNumber.trim()) { toast.error('Please enter actual room number'); return; } const mainGuest = guests[0]; if (!mainGuest.name || !mainGuest.id_number || !mainGuest.phone) { toast.error('Please fill in all main guest information'); return; } try { setCheckInLoading(true); calculateCheckInAdditionalFee(); await bookingService.updateBooking(checkInBooking.id, { status: 'checked_in', } as any); toast.success('Check-in successful'); setCheckInBooking(null); setCheckInBookingNumber(''); setActualRoomNumber(''); setGuests([{ name: '', id_number: '', phone: '' }]); setExtraPersons(0); setChildren(0); setAdditionalFee(0); } catch (error: any) { toast.error(error.response?.data?.message || 'An error occurred during check-in'); } finally { setCheckInLoading(false); } }; // Check-out Functions const handleCheckOutSearch = async () => { if (!checkOutBookingNumber.trim()) { toast.error('Please enter booking number'); return; } try { setCheckOutSearching(true); const response = await bookingService.checkBookingByNumber(checkOutBookingNumber); const foundBooking = response.data.booking; if (foundBooking.status !== 'checked_in') { toast.warning('Only checked-in bookings can be checked out'); } setCheckOutBooking(foundBooking); setCheckOutServices([ { service_name: 'Laundry', quantity: 2, price: 50000, total: 100000 }, { service_name: 'Minibar', quantity: 1, price: 150000, total: 150000 }, ]); toast.success('Booking found'); } catch (error: any) { toast.error(error.response?.data?.message || 'Booking not found'); setCheckOutBooking(null); } finally { setCheckOutSearching(false); } }; const calculateRoomFee = () => { if (!checkOutBooking) return 0; return checkOutBooking.total_price || 0; }; const calculateServiceFee = () => { return checkOutServices.reduce((sum, service) => sum + service.total, 0); }; const calculateCheckOutAdditionalFee = () => { return 0; }; const calculateDeposit = () => { return checkOutBooking?.total_price ? checkOutBooking.total_price * 0.3 : 0; }; const calculateSubtotal = () => { return calculateRoomFee() + calculateServiceFee() + calculateCheckOutAdditionalFee(); }; const calculateTotal = () => { return calculateSubtotal() - discount; }; const calculateRemaining = () => { return calculateTotal() - calculateDeposit(); }; const handleCheckOut = async () => { if (!checkOutBooking) return; if (calculateRemaining() < 0) { toast.error('Invalid refund amount'); return; } try { setCheckOutLoading(true); await bookingService.updateBooking(checkOutBooking.id, { status: 'checked_out', } as any); toast.success('Check-out successful'); setShowInvoice(true); } catch (error: any) { toast.error(error.response?.data?.message || 'An error occurred during check-out'); } finally { setCheckOutLoading(false); } }; const handlePrintInvoice = () => { window.print(); }; const resetCheckOutForm = () => { setCheckOutBooking(null); setCheckOutBookingNumber(''); setCheckOutServices([]); setDiscount(0); setPaymentMethod('cash'); setShowInvoice(false); }; // Bookings Management Functions useEffect(() => { setBookingCurrentPage(1); }, [bookingFilters]); useEffect(() => { if (activeTab === 'bookings') { fetchBookings(); } }, [bookingFilters, bookingCurrentPage, activeTab]); const fetchBookings = async () => { try { setBookingsLoading(true); const response = await bookingService.getAllBookings({ ...bookingFilters, page: bookingCurrentPage, limit: bookingItemsPerPage, }); setBookings(response.data.bookings); if (response.data.pagination) { setBookingTotalPages(response.data.pagination.totalPages); setBookingTotalItems(response.data.pagination.total); } } catch (error: any) { toast.error(error.response?.data?.message || 'Unable to load bookings list'); } finally { setBookingsLoading(false); } }; const handleUpdateBookingStatus = async (id: number, status: string) => { try { setUpdatingBookingId(id); await bookingService.updateBooking(id, { status } as any); toast.success('Status updated successfully'); await fetchBookings(); } catch (error: any) { toast.error(error.response?.data?.message || 'Unable to update status'); } finally { setUpdatingBookingId(null); } }; const handleCancelBooking = async (id: number) => { if (!window.confirm('Are you sure you want to cancel this booking?')) return; try { setCancellingBookingId(id); await bookingService.cancelBooking(id); toast.success('Booking cancelled successfully'); await fetchBookings(); } catch (error: any) { toast.error(error.response?.data?.message || 'Unable to cancel booking'); } finally { setCancellingBookingId(null); } }; const getBookingStatusBadge = (status: string) => { const badges: Record = { pending: { bg: 'bg-gradient-to-r from-amber-50 to-yellow-50', text: 'text-amber-800', label: 'Pending confirmation', border: 'border-amber-200' }, confirmed: { bg: 'bg-gradient-to-r from-blue-50 to-indigo-50', text: 'text-blue-800', label: 'Confirmed', border: 'border-blue-200' }, checked_in: { bg: 'bg-gradient-to-r from-emerald-50 to-green-50', text: 'text-emerald-800', label: 'Checked in', border: 'border-emerald-200' }, checked_out: { bg: 'bg-gradient-to-r from-slate-50 to-gray-50', text: 'text-slate-700', label: 'Checked out', border: 'border-slate-200' }, cancelled: { bg: 'bg-gradient-to-r from-rose-50 to-red-50', text: 'text-rose-800', label: 'Cancelled', border: 'border-rose-200' }, }; const badge = badges[status] || badges.pending; return ( {badge.label} ); }; // Rooms Management Functions useEffect(() => { setRoomCurrentPage(1); setSelectedRooms([]); }, [roomFilters]); useEffect(() => { if (activeTab === 'rooms') { fetchRooms(); fetchAvailableAmenities(); } }, [roomFilters, roomCurrentPage, activeTab]); useEffect(() => { const fetchAllRoomTypes = async () => { try { const response = await roomService.getRooms({ limit: 100, page: 1 }); const allUniqueRoomTypes = new Map(); response.data.rooms.forEach((room: Room) => { if (room.room_type && !allUniqueRoomTypes.has(room.room_type.id)) { allUniqueRoomTypes.set(room.room_type.id, { id: room.room_type.id, name: room.room_type.name, }); } }); if (response.data.pagination && response.data.pagination.totalPages > 1) { const totalPages = response.data.pagination.totalPages; for (let page = 2; page <= totalPages; page++) { try { const pageResponse = await roomService.getRooms({ limit: 100, page }); pageResponse.data.rooms.forEach((room: Room) => { if (room.room_type && !allUniqueRoomTypes.has(room.room_type.id)) { allUniqueRoomTypes.set(room.room_type.id, { id: room.room_type.id, name: room.room_type.name, }); } }); } catch (err) { console.error(`Failed to fetch page ${page}:`, err); } } } if (allUniqueRoomTypes.size > 0) { const roomTypesList = Array.from(allUniqueRoomTypes.values()); setRoomTypes(roomTypesList); if (!editingRoom && roomFormData.room_type_id === 1 && roomTypesList.length > 0) { setRoomFormData(prev => ({ ...prev, room_type_id: roomTypesList[0].id })); } } } catch (err) { console.error('Failed to fetch room types:', err); } }; if (activeTab === 'rooms') { fetchAllRoomTypes(); } }, [activeTab]); const fetchAvailableAmenities = async () => { try { const response = await roomService.getAmenities(); if (response.data?.amenities) { setAvailableAmenities(response.data.amenities); } } catch (error) { console.error('Failed to fetch amenities:', error); } }; const fetchRooms = async () => { try { setRoomsLoading(true); const response = await roomService.getRooms({ ...roomFilters, page: roomCurrentPage, limit: roomItemsPerPage, }); setRooms(response.data.rooms); if (response.data.pagination) { setRoomTotalPages(response.data.pagination.totalPages); setRoomTotalItems(response.data.pagination.total); } const uniqueRoomTypes = new Map(); response.data.rooms.forEach((room: Room) => { if (room.room_type && !uniqueRoomTypes.has(room.room_type.id)) { uniqueRoomTypes.set(room.room_type.id, { id: room.room_type.id, name: room.room_type.name, }); } }); setRoomTypes(Array.from(uniqueRoomTypes.values())); } catch (error: any) { toast.error(error.response?.data?.message || 'Unable to load rooms list'); } finally { setRoomsLoading(false); } }; const handleRoomSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { if (editingRoom) { const updateData = { ...roomFormData, price: roomFormData.price ? parseFloat(roomFormData.price) : undefined, description: roomFormData.description || undefined, capacity: roomFormData.capacity ? parseInt(roomFormData.capacity) : undefined, room_size: roomFormData.room_size || undefined, view: roomFormData.view || undefined, amenities: Array.isArray(roomFormData.amenities) ? roomFormData.amenities : [], }; await roomService.updateRoom(editingRoom.id, updateData); toast.success('Room updated successfully'); await fetchRooms(); try { const updatedRoom = await roomService.getRoomByNumber(editingRoom.room_number); setEditingRoom(updatedRoom.data.room); } catch (err) { console.error('Failed to refresh room data:', err); } } else { const createData = { ...roomFormData, price: roomFormData.price ? parseFloat(roomFormData.price) : undefined, description: roomFormData.description || undefined, capacity: roomFormData.capacity ? parseInt(roomFormData.capacity) : undefined, room_size: roomFormData.room_size || undefined, view: roomFormData.view || undefined, amenities: Array.isArray(roomFormData.amenities) ? roomFormData.amenities : [], }; const response = await roomService.createRoom(createData); toast.success('Room added successfully'); if (response.data?.room) { if (selectedFiles.length > 0) { try { setUploadingImages(true); const uploadFormData = new FormData(); selectedFiles.forEach(file => { uploadFormData.append('images', file); }); await apiClient.post(`/rooms/${response.data.room.id}/images`, uploadFormData, { headers: { 'Content-Type': 'multipart/form-data', }, }); toast.success('Images uploaded successfully'); setSelectedFiles([]); const updatedRoom = await roomService.getRoomByNumber(response.data.room.room_number); setEditingRoom(updatedRoom.data.room); } catch (uploadError: any) { toast.error(uploadError.response?.data?.message || 'Room created but failed to upload images'); const updatedRoom = await roomService.getRoomByNumber(response.data.room.room_number); setEditingRoom(updatedRoom.data.room); } finally { setUploadingImages(false); } } else { const updatedRoom = await roomService.getRoomByNumber(response.data.room.room_number); setEditingRoom(updatedRoom.data.room); } setRoomFormData({ room_number: response.data.room.room_number, floor: response.data.room.floor, room_type_id: response.data.room.room_type_id, status: response.data.room.status, featured: response.data.room.featured, price: response.data.room.price?.toString() || '', description: response.data.room.description || '', capacity: response.data.room.capacity?.toString() || '', room_size: response.data.room.room_size || '', view: response.data.room.view || '', amenities: response.data.room.amenities || [], }); await fetchRooms(); return; } } setShowRoomModal(false); resetRoomForm(); fetchRooms(); } catch (error: any) { toast.error(error.response?.data?.message || 'An error occurred'); } }; const handleEditRoom = async (room: Room) => { setEditingRoom(room); let amenitiesArray: string[] = []; if (room.amenities) { if (Array.isArray(room.amenities)) { amenitiesArray = room.amenities; } else if (typeof room.amenities === 'string') { try { const parsed = JSON.parse(room.amenities); amenitiesArray = Array.isArray(parsed) ? parsed : []; } catch { const amenitiesStr: string = room.amenities; amenitiesArray = amenitiesStr.split(',').map((a: string) => a.trim()).filter(Boolean); } } } setRoomFormData({ room_number: room.room_number, floor: room.floor, room_type_id: room.room_type_id, status: room.status, featured: room.featured, price: room.price?.toString() || '', description: room.description || '', capacity: room.capacity?.toString() || '', room_size: room.room_size || '', view: room.view || '', amenities: amenitiesArray, }); setShowRoomModal(true); try { const fullRoom = await roomService.getRoomByNumber(room.room_number); const roomData = fullRoom.data.room; let updatedAmenitiesArray: string[] = []; if (roomData.amenities) { if (Array.isArray(roomData.amenities)) { updatedAmenitiesArray = roomData.amenities; } else if (typeof roomData.amenities === 'string') { try { const parsed = JSON.parse(roomData.amenities); updatedAmenitiesArray = Array.isArray(parsed) ? parsed : []; } catch { const amenitiesStr: string = roomData.amenities; updatedAmenitiesArray = amenitiesStr.split(',').map((a: string) => a.trim()).filter(Boolean); } } } setRoomFormData({ room_number: roomData.room_number, floor: roomData.floor, room_type_id: roomData.room_type_id, status: roomData.status, featured: roomData.featured, price: roomData.price?.toString() || '', description: roomData.description || '', capacity: roomData.capacity?.toString() || '', room_size: roomData.room_size || '', view: roomData.view || '', amenities: updatedAmenitiesArray, }); setEditingRoom(roomData); } catch (error) { console.error('Failed to fetch full room details:', error); } }; const handleDeleteRoom = async (id: number) => { if (!window.confirm('Are you sure you want to delete this room?')) return; try { await roomService.deleteRoom(id); toast.success('Room deleted successfully'); setSelectedRooms(selectedRooms.filter(roomId => roomId !== id)); fetchRooms(); } catch (error: any) { toast.error(error.response?.data?.message || 'Unable to delete room'); } }; const handleBulkDeleteRooms = async () => { if (selectedRooms.length === 0) { toast.warning('Please select at least one room to delete'); return; } if (!window.confirm(`Are you sure you want to delete ${selectedRooms.length} room(s)?`)) return; try { await roomService.bulkDeleteRooms(selectedRooms); toast.success(`Successfully deleted ${selectedRooms.length} room(s)`); setSelectedRooms([]); fetchRooms(); } catch (error: any) { toast.error(error.response?.data?.message || error.response?.data?.detail || 'Unable to delete rooms'); } }; const handleSelectRoom = (roomId: number) => { setSelectedRooms(prev => prev.includes(roomId) ? prev.filter(id => id !== roomId) : [...prev, roomId] ); }; const handleSelectAllRooms = () => { if (selectedRooms.length === rooms.length) { setSelectedRooms([]); } else { setSelectedRooms(rooms.map(room => room.id)); } }; const resetRoomForm = () => { setEditingRoom(null); setRoomFormData({ room_number: '', floor: 1, room_type_id: 1, status: 'available', featured: false, price: '', description: '', capacity: '', room_size: '', view: '', amenities: [], }); setSelectedFiles([]); setUploadingImages(false); }; const toggleAmenity = (amenity: string) => { setRoomFormData(prev => ({ ...prev, amenities: prev.amenities.includes(amenity) ? prev.amenities.filter(a => a !== amenity) : [...prev.amenities, amenity] })); }; const handleFileSelect = (e: React.ChangeEvent) => { if (e.target.files) { const files = Array.from(e.target.files); setSelectedFiles(files); } }; const handleUploadImages = async () => { if (!editingRoom || selectedFiles.length === 0) return; try { setUploadingImages(true); const formData = new FormData(); selectedFiles.forEach(file => { formData.append('images', file); }); await apiClient.post(`/rooms/${editingRoom.id}/images`, formData, { headers: { 'Content-Type': 'multipart/form-data', }, }); toast.success('Images uploaded successfully'); setSelectedFiles([]); fetchRooms(); const response = await roomService.getRoomByNumber(editingRoom.room_number); setEditingRoom(response.data.room); } catch (error: any) { toast.error(error.response?.data?.message || 'Unable to upload images'); } finally { setUploadingImages(false); } }; const handleDeleteImage = async (imageUrl: string) => { if (!editingRoom) return; if (!window.confirm('Are you sure you want to delete this image?')) return; try { let imagePath = imageUrl; if (imageUrl.startsWith('http://') || imageUrl.startsWith('https://')) { try { const url = new URL(imageUrl); imagePath = url.pathname; } catch (e) { const match = imageUrl.match(/(\/uploads\/.*)/); imagePath = match ? match[1] : imageUrl; } } await apiClient.delete(`/rooms/${editingRoom.id}/images`, { params: { image_url: imagePath }, }); toast.success('Image deleted successfully'); fetchRooms(); const response = await roomService.getRoomByNumber(editingRoom.room_number); setEditingRoom(response.data.room); } catch (error: any) { console.error('Error deleting image:', error); toast.error(error.response?.data?.message || error.response?.data?.detail || 'Unable to delete image'); } }; const getRoomStatusBadge = (status: string) => { const badges: Record = { available: { bg: 'bg-gradient-to-r from-emerald-50 to-green-50', text: 'text-emerald-800', label: 'Available', border: 'border-emerald-200' }, occupied: { bg: 'bg-gradient-to-r from-blue-50 to-indigo-50', text: 'text-blue-800', label: 'Occupied', border: 'border-blue-200' }, maintenance: { bg: 'bg-gradient-to-r from-amber-50 to-yellow-50', text: 'text-amber-800', label: 'Maintenance', border: 'border-amber-200' }, }; const badge = badges[status] || badges.available; return ( {badge.label} ); }; // Services Management Functions useEffect(() => { setServiceCurrentPage(1); }, [serviceFilters]); useEffect(() => { if (activeTab === 'services') { fetchServices(); } }, [serviceFilters, serviceCurrentPage, activeTab]); const fetchServices = async () => { try { setServicesLoading(true); const response = await serviceService.getServices({ ...serviceFilters, page: serviceCurrentPage, limit: serviceItemsPerPage, }); setServices(response.data.services); if (response.data.pagination) { setServiceTotalPages(response.data.pagination.totalPages); setServiceTotalItems(response.data.pagination.total); } } catch (error: any) { toast.error(error.response?.data?.message || 'Unable to load services list'); } finally { setServicesLoading(false); } }; const handleServiceSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { if (editingService) { await serviceService.updateService(editingService.id, serviceFormData); toast.success('Service updated successfully'); } else { await serviceService.createService(serviceFormData); toast.success('Service added successfully'); } setShowServiceModal(false); resetServiceForm(); fetchServices(); } catch (error: any) { toast.error(error.response?.data?.message || 'An error occurred'); } }; const handleEditService = (service: Service) => { setEditingService(service); setServiceFormData({ name: service.name, description: service.description || '', price: service.price, unit: service.unit || 'time', status: service.status, }); setShowServiceModal(true); }; const handleDeleteService = async (id: number) => { if (!window.confirm('Are you sure you want to delete this service?')) return; try { await serviceService.deleteService(id); toast.success('Service deleted successfully'); fetchServices(); } catch (error: any) { toast.error(error.response?.data?.message || 'Unable to delete service'); } }; const resetServiceForm = () => { setEditingService(null); setServiceFormData({ name: '', description: '', price: 0, unit: 'time', status: 'active', }); }; const getServiceStatusBadge = (status: string) => { return status === 'active' ? ( Active ) : ( Inactive ); }; const tabs = [ { id: 'overview' as ReceptionTab, label: 'Overview', icon: LogIn }, { id: 'check-in' as ReceptionTab, label: 'Check-in', icon: LogIn }, { id: 'check-out' as ReceptionTab, label: 'Check-out', icon: LogOut }, { id: 'bookings' as ReceptionTab, label: 'Bookings', icon: Calendar }, { id: 'rooms' as ReceptionTab, label: 'Rooms', icon: Hotel }, { id: 'services' as ReceptionTab, label: 'Services', icon: Wrench }, ]; return (
{/* Luxury Header */}

Reception Dashboard

Manage guest check-in and check-out processes

{/* Premium Tab Navigation */}
{tabs.map((tab) => { const Icon = tab.icon; const isActive = activeTab === tab.id; return ( ); })}
{/* Overview Tab */} {activeTab === 'overview' && (
setActiveTab('check-in')} className="group relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-emerald-100/50 p-8 cursor-pointer transition-all duration-300 hover:shadow-2xl hover:scale-105 hover:border-emerald-300/60 overflow-hidden" >

Check-in

Process guest check-in and room assignment

Start Check-in
setActiveTab('check-out')} className="group relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-teal-100/50 p-8 cursor-pointer transition-all duration-300 hover:shadow-2xl hover:scale-105 hover:border-teal-300/60 overflow-hidden" >

Check-out

Process guest check-out and payment collection

Start Check-out
setActiveTab('bookings')} className="group relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-blue-100/50 p-8 cursor-pointer transition-all duration-300 hover:shadow-2xl hover:scale-105 hover:border-blue-300/60 overflow-hidden" >

Bookings

Manage and track all hotel bookings

Manage Bookings
setActiveTab('rooms')} className="group relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-amber-100/50 p-8 cursor-pointer transition-all duration-300 hover:shadow-2xl hover:scale-105 hover:border-amber-300/60 overflow-hidden" >

Rooms

Manage hotel room information and availability

Manage Rooms
setActiveTab('services')} className="group relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-purple-100/50 p-8 cursor-pointer transition-all duration-300 hover:shadow-2xl hover:scale-105 hover:border-purple-300/60 overflow-hidden" >

Services

Manage hotel services and amenities

Manage Services
)} {/* Check-in Tab */} {activeTab === 'check-in' && (
{checkInLoading && ( )} {/* Section Header */}

Check-in

Customer check-in process and room assignment

{/* Search Booking */}

1
Search Booking

setCheckInBookingNumber(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && handleCheckInSearch()} placeholder="Enter booking number" className="w-full pl-12 pr-4 py-3.5 bg-white border-2 border-gray-200 rounded-xl focus:border-emerald-400 focus:ring-4 focus:ring-emerald-100 transition-all duration-200 text-gray-700 placeholder-gray-400 font-medium shadow-sm hover:shadow-md" />
{/* Booking Info */} {checkInBooking && ( <>

Booking Information

Booking Number: {checkInBooking.booking_number}
Customer: {checkInBooking.user?.full_name}
Email: {checkInBooking.user?.email}
Phone: {checkInBooking.user?.phone_number || 'N/A'}
Room Type: {checkInBooking.room?.room_type?.name || 'N/A'}
Check-in: {checkInBooking.check_in_date ? parseDateLocal(checkInBooking.check_in_date).toLocaleDateString('en-US') : 'N/A'}
Check-out: {checkInBooking.check_out_date ? parseDateLocal(checkInBooking.check_out_date).toLocaleDateString('en-US') : 'N/A'}
Number of Guests: {checkInBooking.guest_count} guest(s)
{checkInBooking.status !== 'confirmed' && (

Warning

Booking status: {checkInBooking.status}. Only check-in confirmed bookings.

)}
{/* Assign Room */}

Assign Actual Room Number

setActualRoomNumber(e.target.value)} placeholder="e.g: 101, 202, 305" className="w-full px-4 py-3.5 bg-white border-2 border-gray-200 rounded-xl focus:border-emerald-400 focus:ring-4 focus:ring-emerald-100 transition-all duration-200 text-gray-700 font-medium shadow-sm hover:shadow-md" />

Enter the actual room number to assign to the guest

{/* Guest Information */}

Guest Information

{guests.map((guest, index) => (

{index === 0 ? 'Main Guest' : `Guest ${index + 1}`} {index === 0 && *}

{index > 0 && ( )}
handleGuestChange(index, 'name', e.target.value)} className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-emerald-400 focus:ring-4 focus:ring-emerald-100 transition-all duration-200 text-gray-700 font-medium shadow-sm" placeholder="John Doe" />
handleGuestChange(index, 'id_number', e.target.value)} className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-emerald-400 focus:ring-4 focus:ring-emerald-100 transition-all duration-200 text-gray-700 font-medium shadow-sm font-mono" placeholder="001234567890" />
handleGuestChange(index, 'phone', e.target.value)} className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-emerald-400 focus:ring-4 focus:ring-emerald-100 transition-all duration-200 text-gray-700 font-medium shadow-sm" placeholder="0912345678" />
))}
{/* Additional Charges */}

Additional Fees (if any)

{ setExtraPersons(parseInt(e.target.value) || 0); calculateCheckInAdditionalFee(); }} className="w-full px-4 py-3.5 bg-white border-2 border-gray-200 rounded-xl focus:border-emerald-400 focus:ring-4 focus:ring-emerald-100 transition-all duration-200 text-gray-700 font-medium shadow-sm" />

€50/person

{ setChildren(parseInt(e.target.value) || 0); calculateCheckInAdditionalFee(); }} className="w-full px-4 py-3.5 bg-white border-2 border-gray-200 rounded-xl focus:border-emerald-400 focus:ring-4 focus:ring-emerald-100 transition-all duration-200 text-gray-700 font-medium shadow-sm" />

€25/child

{formatCurrency(calculateCheckInAdditionalFee())}
{/* Summary & Action */}

Confirm Check-in

Guest: {checkInBooking.user?.full_name} | Room: {actualRoomNumber || 'Not assigned'} {additionalFee > 0 && ( <> | Additional Fee: {formatCurrency(additionalFee)} )}

)} {/* Empty State */} {!checkInBooking && !checkInSearching && (

No booking selected

Please enter booking number above to start check-in process

)}
)} {/* Check-out Tab */} {activeTab === 'check-out' && (
{checkOutLoading && ( )} {/* Section Header */}

Check-out

Payment and check-out process for guests

{/* Search Booking */} {!showInvoice && (

1
Search Booking

setCheckOutBookingNumber(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && handleCheckOutSearch()} placeholder="Enter booking number or room number" className="w-full pl-12 pr-4 py-3.5 bg-white border-2 border-gray-200 rounded-xl focus:border-teal-400 focus:ring-4 focus:ring-teal-100 transition-all duration-200 text-gray-700 placeholder-gray-400 font-medium shadow-sm hover:shadow-md" />
)} {/* Invoice */} {checkOutBooking && !showInvoice && ( <> {/* Booking Info */}

Booking Information

Booking number: {checkOutBooking.booking_number}
Customer: {checkOutBooking.user?.full_name}
Room number: {checkOutBooking.room?.room_number}
Check-in: {checkOutBooking.check_in_date ? parseDateLocal(checkOutBooking.check_in_date).toLocaleDateString('en-US') : 'N/A'}
Check-out: {checkOutBooking.check_out_date ? parseDateLocal(checkOutBooking.check_out_date).toLocaleDateString('en-US') : 'N/A'}
Nights: {checkOutBooking.check_in_date && checkOutBooking.check_out_date ? Math.ceil((parseDateLocal(checkOutBooking.check_out_date).getTime() - parseDateLocal(checkOutBooking.check_in_date).getTime()) / (1000 * 60 * 60 * 24)) : 0} night(s)
{/* Bill Details */}

Invoice Details

{/* Room Fee */}

Room Fee

{checkOutBooking.room?.room_type?.name || 'Room'} {formatCurrency(calculateRoomFee())}
{/* Service Fee */} {checkOutServices.length > 0 && (

Services Used

{checkOutServices.map((service, index) => (
{service.service_name} (x{service.quantity}) {formatCurrency(service.total)}
))}
Total services: {formatCurrency(calculateServiceFee())}
)} {/* Additional Fee */} {calculateCheckOutAdditionalFee() > 0 && (

Additional Fees

Extra person/children fee {formatCurrency(calculateCheckOutAdditionalFee())}
)} {/* Discount */}

Discount

setDiscount(parseFloat(e.target.value) || 0)} placeholder="Enter discount amount" className="w-full px-4 py-3.5 bg-white border-2 border-gray-200 rounded-xl focus:border-teal-400 focus:ring-4 focus:ring-teal-100 transition-all duration-200 text-gray-700 font-medium shadow-sm" />
{/* Summary */}
Subtotal: {formatCurrency(calculateSubtotal())}
{discount > 0 && (
Discount: -{formatCurrency(discount)}
)}
Total: {formatCurrency(calculateTotal())}
Deposit paid: -{formatCurrency(calculateDeposit())}
Remaining payment: {formatCurrency(calculateRemaining())}
{/* Payment Method */}

Payment Method

{/* Action */}

Confirm Check-out

Total payment: {formatCurrency(calculateRemaining())}

)} {/* Invoice Display */} {showInvoice && checkOutBooking && (

PAYMENT INVOICE

Check-out successful

Booking number:

{checkOutBooking.booking_number}

Check-out date:

{new Date().toLocaleString('en-US')}

Customer:

{checkOutBooking.user?.full_name}

Payment method:

{paymentMethod === 'cash' ? 'Cash' : 'Stripe'}

Total payment: {formatCurrency(calculateRemaining())}
)} {/* Empty State */} {!checkOutBooking && !checkOutSearching && !showInvoice && (

No booking selected

Please enter booking number to start check-out process

)}
)} {/* Bookings Tab */} {activeTab === 'bookings' && (
{bookingsLoading && } {/* Section Header */}

Bookings Management

Manage and track all hotel bookings with precision

{/* Filters */}
setBookingFilters({ ...bookingFilters, search: e.target.value })} className="w-full pl-12 pr-4 py-3.5 bg-white border-2 border-gray-200 rounded-xl focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all duration-200 text-gray-700 placeholder-gray-400 font-medium shadow-sm hover:shadow-md" />
{/* Bookings Table */}
{bookings.map((booking) => ( ))}
Booking Number Customer Room Check-in/out Total Price Status Actions
{booking.booking_number}
{booking.guest_info?.full_name || booking.user?.name}
{booking.guest_info?.email || booking.user?.email}
Room {booking.room?.room_number} {booking.room?.room_type?.name}
{parseDateLocal(booking.check_in_date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
→ {parseDateLocal(booking.check_out_date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}
{formatCurrency(booking.total_price)}
{getBookingStatusBadge(booking.status)}
{booking.status === 'pending' && ( <> )} {booking.status === 'confirmed' && ( )}
{/* Booking Detail Modal */} {showBookingDetailModal && selectedBooking && (

Booking Details

Comprehensive booking information

{selectedBooking.booking_number}

{getBookingStatusBadge(selectedBooking.status)}

{selectedBooking.guest_info?.full_name || selectedBooking.user?.name}

{selectedBooking.guest_info?.email || selectedBooking.user?.email}

{selectedBooking.guest_info?.phone || selectedBooking.user?.phone}

Room {selectedBooking.room?.room_number} {selectedBooking.room?.room_type?.name}

{parseDateLocal(selectedBooking.check_in_date).toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' })}

{parseDateLocal(selectedBooking.check_out_date).toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' })}

{selectedBooking.guest_count} guest{selectedBooking.guest_count !== 1 ? 's' : ''}

{formatCurrency(selectedBooking.total_price)}

{selectedBooking.notes && (

{selectedBooking.notes}

)}
)}
)} {/* Rooms Tab */} {activeTab === 'rooms' && (
{roomsLoading && } {/* Section Header */}

Room Management

Manage hotel room information and availability

{selectedRooms.length > 0 && ( )}
{/* Filters */}
setRoomFilters({ ...roomFilters, search: e.target.value })} className="w-full pl-12 pr-4 py-3.5 bg-white border-2 border-gray-200 rounded-xl focus:border-amber-400 focus:ring-4 focus:ring-amber-100 transition-all duration-200 text-gray-700 placeholder-gray-400 font-medium shadow-sm hover:shadow-md" />
{/* Rooms Table */}
{rooms.map((room) => ( ))}
0 && selectedRooms.length === rooms.length} onChange={handleSelectAllRooms} title="Select all rooms" className="w-5 h-5 text-amber-600 bg-slate-700 border-slate-600 rounded focus:ring-amber-500 cursor-pointer" /> Room Number Room Type Floor Price Status Featured Actions
handleSelectRoom(room.id)} className="w-5 h-5 text-amber-600 bg-white border-slate-300 rounded focus:ring-amber-500 cursor-pointer" title={`Select room ${room.room_number}`} />
{room.room_number}
{room.room_type?.name || 'N/A'}
Floor {room.floor}
{formatCurrency(room.price || room.room_type?.base_price || 0)}
{getRoomStatusBadge(room.status)} {room.featured ? ( ) : ( - )}
{/* Room Modal - Simplified version, will need full implementation */} {showRoomModal && (

{editingRoom ? 'Update Room' : 'Add New Room'}

{editingRoom ? 'Modify room details and amenities' : 'Create a new luxurious room'}

{/* Basic Information Section */}

Basic Information

setRoomFormData({ ...roomFormData, room_number: e.target.value })} className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300" placeholder="e.g., 1001" required />
setRoomFormData({ ...roomFormData, floor: parseInt(e.target.value) })} className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300" required min="1" />
setRoomFormData({ ...roomFormData, featured: e.target.checked })} className="w-5 h-5 text-[#d4af37] bg-[#1a1a1a] border-[#d4af37]/30 rounded focus:ring-[#d4af37]/50 focus:ring-2 cursor-pointer transition-all" />
setRoomFormData({ ...roomFormData, price: e.target.value })} className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300" placeholder="e.g., 150.00" />