Files
Hotel-Booking/Frontend/src/pages/admin/ReceptionDashboardPage.tsx
Iliyan Angelov 39fcfff811 update
2025-11-30 22:43:09 +02:00

2140 lines
118 KiB
TypeScript

import React, { useState, useEffect, useCallback } from 'react';
import {
LogIn,
LogOut,
Search,
User,
Hotel,
CheckCircle,
AlertCircle,
FileText,
CreditCard,
Printer,
Sparkles,
ChevronRight,
Eye,
XCircle,
Loader2,
Plus,
Edit,
Trash2,
X,
Calendar,
Wrench
} from 'lucide-react';
import bookingService, { Booking } from '../../features/bookings/services/bookingService';
import serviceService, { Service } from '../../features/hotel_services/services/serviceService';
import { toast } from 'react-toastify';
import Loading from '../../shared/components/Loading';
import CurrencyIcon from '../../shared/components/CurrencyIcon';
import Pagination from '../../shared/components/Pagination';
import { useFormatCurrency } from '../../features/payments/hooks/useFormatCurrency';
import { parseDateLocal } from '../../shared/utils/format';
import CreateBookingModal from '../../features/hotel_services/components/CreateBookingModal';
type ReceptionTab = 'overview' | 'check-in' | 'check-out' | 'bookings' | '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<ReceptionTab>('overview');
const [checkInBookingNumber, setCheckInBookingNumber] = useState('');
const [checkInBooking, setCheckInBooking] = useState<Booking | null>(null);
const [checkInLoading, setCheckInLoading] = useState(false);
const [checkInSearching, setCheckInSearching] = useState(false);
const [actualRoomNumber, setActualRoomNumber] = useState('');
const [guests, setGuests] = useState<GuestInfo[]>([{ name: '', id_number: '', phone: '' }]);
const [extraPersons, setExtraPersons] = useState(0);
const [children, setChildren] = useState(0);
const [additionalFee, setAdditionalFee] = useState(0);
const [checkOutBookingNumber, setCheckOutBookingNumber] = useState('');
const [checkOutBooking, setCheckOutBooking] = useState<Booking | null>(null);
const [checkOutLoading, setCheckOutLoading] = useState(false);
const [checkOutSearching, setCheckOutSearching] = useState(false);
const [checkOutServices, setCheckOutServices] = useState<ServiceItem[]>([]);
const [paymentMethod, setPaymentMethod] = useState<'cash' | 'stripe'>('cash');
const [discount, setDiscount] = useState(0);
const [showInvoice, setShowInvoice] = useState(false);
const [bookings, setBookings] = useState<Booking[]>([]);
const [bookingsLoading, setBookingsLoading] = useState(true);
const [selectedBooking, setSelectedBooking] = useState<Booking | null>(null);
const [showBookingDetailModal, setShowBookingDetailModal] = useState(false);
const [updatingBookingId, setUpdatingBookingId] = useState<number | null>(null);
const [cancellingBookingId, setCancellingBookingId] = useState<number | null>(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;
const [showCreateBookingModal, setShowCreateBookingModal] = useState(false);
const [services, setServices] = useState<Service[]>([]);
const [servicesLoading, setServicesLoading] = useState(true);
const [showServiceModal, setShowServiceModal] = useState(false);
const [editingService, setEditingService] = useState<Service | null>(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',
});
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;
};
useEffect(() => {
const extraPersonFee = extraPersons * 200000;
const childrenFee = children * 100000;
const total = extraPersonFee + childrenFee;
setAdditionalFee(total);
}, [extraPersons, children]);
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);
}
};
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 calculateTotalPaid = () => {
if (!checkOutBooking?.payments) return 0;
return checkOutBooking.payments
.filter(payment => payment.payment_status === 'completed')
.reduce((sum, payment) => sum + (payment.amount || 0), 0);
};
const calculateSubtotal = () => {
return calculateRoomFee() + calculateServiceFee() + calculateCheckOutAdditionalFee();
};
const calculateTotal = () => {
return calculateSubtotal() - discount;
};
const calculateRemaining = () => {
const total = calculateTotal();
const totalPaid = calculateTotalPaid();
return total - totalPaid;
};
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);
};
const fetchBookings = useCallback(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);
}
}, [bookingFilters.search, bookingFilters.status, bookingCurrentPage]);
useEffect(() => {
setBookingCurrentPage(1);
}, [bookingFilters.search, bookingFilters.status]);
useEffect(() => {
if (activeTab === 'bookings') {
fetchBookings();
}
}, [activeTab, fetchBookings]);
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<string, { bg: string; text: string; label: string; border: string }> = {
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: '❌ Canceled',
border: 'border-rose-200'
},
};
const badge = badges[status] || badges.pending;
return (
<span className={`px-4 py-1.5 rounded-full text-xs font-semibold border ${badge.bg} ${badge.text} ${badge.border} shadow-sm`}>
{badge.label}
</span>
);
};
const fetchServices = useCallback(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);
}
}, [serviceFilters.search, serviceFilters.status, serviceCurrentPage]);
useEffect(() => {
setServiceCurrentPage(1);
}, [serviceFilters.search, serviceFilters.status]);
useEffect(() => {
if (activeTab === 'services') {
fetchServices();
}
}, [activeTab, fetchServices]);
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' ? (
<span className="px-4 py-1.5 rounded-full text-xs font-semibold border shadow-sm bg-gradient-to-r from-emerald-50 to-green-50 text-emerald-800 border-emerald-200">
Active
</span>
) : (
<span className="px-4 py-1.5 rounded-full text-xs font-semibold border shadow-sm bg-gradient-to-r from-slate-50 to-gray-50 text-slate-700 border-slate-200">
Inactive
</span>
);
};
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: 'services' as ReceptionTab, label: 'Services', icon: Wrench },
];
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-slate-50">
<div className="max-w-7xl mx-auto px-2 sm:px-3 md:px-4 lg:px-6 xl:px-8 py-2 sm:py-4 md:py-6 lg:py-8 space-y-3 sm:space-y-4 md:space-y-6 lg:space-y-8 animate-fade-in">
{}
<div className="relative">
<div className="absolute inset-0 bg-gradient-to-r from-emerald-400/5 via-transparent to-teal-600/5 rounded-3xl blur-3xl"></div>
<div className="relative bg-white/80 backdrop-blur-xl rounded-xl sm:rounded-2xl md:rounded-3xl shadow-2xl border border-emerald-200/30 p-3 sm:p-4 md:p-6 lg:p-8">
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4 sm:gap-6 md:gap-8">
<div className="flex items-start gap-3 sm:gap-4 md:gap-5">
<div className="relative flex-shrink-0">
<div className="absolute inset-0 bg-gradient-to-br from-emerald-400 to-teal-600 rounded-xl sm:rounded-2xl blur-lg opacity-50"></div>
<div className="relative p-2.5 sm:p-3 md:p-4 rounded-xl sm:rounded-2xl bg-gradient-to-br from-emerald-500 via-emerald-500 to-teal-600 shadow-xl border border-emerald-400/50">
<LogIn className="w-5 h-5 sm:w-6 sm:h-6 md:w-8 md:h-8 text-white" />
<div className="absolute -top-1 -right-1 w-3 h-3 sm:w-4 sm:h-4 bg-gradient-to-br from-green-300 to-emerald-500 rounded-full shadow-lg animate-pulse"></div>
</div>
</div>
<div className="space-y-2 sm:space-y-3 flex-1">
<div className="flex items-center gap-2 sm:gap-3 flex-wrap">
<h1 className="text-2xl sm:text-3xl md:text-3xl font-extrabold bg-gradient-to-r from-slate-900 via-emerald-700 to-slate-900 bg-clip-text text-transparent">
Reception Dashboard
</h1>
<Sparkles className="w-4 h-4 sm:w-5 sm:h-5 text-emerald-500 animate-pulse" />
</div>
<p className="text-gray-600 text-xs sm:text-sm md:text-sm max-w-2xl leading-relaxed">
Manage guest check-in and check-out processes
</p>
</div>
</div>
</div>
{}
<div className="mt-6 sm:mt-8 md:mt-10 pt-6 sm:pt-8 border-t border-gradient-to-r from-transparent via-emerald-200/30 to-transparent">
<div className="flex flex-wrap gap-2 sm:gap-3">
{tabs.map((tab) => {
const Icon = tab.icon;
const isActive = activeTab === tab.id;
return (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`
group relative flex items-center gap-2 sm:gap-3 px-3 sm:px-4 md:px-6 py-2 sm:py-2.5 md:py-3.5 rounded-lg sm:rounded-xl font-semibold text-xs sm:text-sm
transition-all duration-300 overflow-hidden
${
isActive
? 'bg-gradient-to-r from-emerald-500 via-emerald-500 to-teal-600 text-white shadow-xl shadow-emerald-500/40 scale-105'
: 'bg-white/80 text-gray-700 border border-gray-200/60 hover:border-emerald-300/60 hover:bg-gradient-to-r hover:from-emerald-50/50 hover:to-teal-50/30 hover:shadow-lg hover:scale-102'
}
`}
>
{isActive && (
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent animate-shimmer"></div>
)}
<Icon className={`w-5 h-5 transition-transform duration-300 ${isActive ? 'text-white' : 'text-gray-600 group-hover:text-emerald-600 group-hover:scale-110'}`} />
<span className="relative z-10">{tab.label}</span>
{isActive && (
<div className="absolute bottom-0 left-0 right-0 h-1 bg-gradient-to-r from-green-300 via-emerald-400 to-teal-400"></div>
)}
</button>
);
})}
</div>
</div>
</div>
</div>
{}
{activeTab === 'overview' && (
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 lg:gap-8">
<div
onClick={() => 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"
>
<div className="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-emerald-400/10 to-transparent rounded-bl-full"></div>
<div className="relative space-y-5">
<div className="flex items-start justify-between">
<div className="flex items-center gap-4">
<div className="relative">
<div className="absolute inset-0 bg-gradient-to-br from-emerald-400 to-emerald-600 rounded-xl blur-md opacity-50 group-hover:opacity-75 transition-opacity"></div>
<div className="relative p-3.5 rounded-xl bg-gradient-to-br from-emerald-500 to-emerald-600 shadow-lg border border-emerald-400/50 group-hover:scale-110 transition-transform">
<LogIn className="w-6 h-6 text-white" />
</div>
</div>
<div>
<h3 className="font-bold text-gray-900 text-xl mb-1">Check-in</h3>
<div className="h-1 w-12 bg-gradient-to-r from-emerald-500 to-emerald-600 rounded-full"></div>
</div>
</div>
</div>
<p className="text-gray-600 text-sm leading-relaxed">
Process guest check-in and room assignment
</p>
<div className="pt-5 border-t border-gray-100">
<div className="flex items-center justify-between text-sm">
<span className="text-gray-500 font-medium">Start Check-in</span>
<ChevronRight className="w-5 h-5 text-emerald-600 group-hover:translate-x-1 transition-transform" />
</div>
</div>
</div>
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-emerald-50/30 to-transparent opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none"></div>
</div>
<div
onClick={() => 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"
>
<div className="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-teal-400/10 to-transparent rounded-bl-full"></div>
<div className="relative space-y-5">
<div className="flex items-start justify-between">
<div className="flex items-center gap-4">
<div className="relative">
<div className="absolute inset-0 bg-gradient-to-br from-teal-400 to-teal-600 rounded-xl blur-md opacity-50 group-hover:opacity-75 transition-opacity"></div>
<div className="relative p-3.5 rounded-xl bg-gradient-to-br from-teal-500 to-teal-600 shadow-lg border border-teal-400/50 group-hover:scale-110 transition-transform">
<LogOut className="w-6 h-6 text-white" />
</div>
</div>
<div>
<h3 className="font-bold text-gray-900 text-xl mb-1">Check-out</h3>
<div className="h-1 w-12 bg-gradient-to-r from-teal-500 to-teal-600 rounded-full"></div>
</div>
</div>
</div>
<p className="text-gray-600 text-sm leading-relaxed">
Process guest check-out and payment collection
</p>
<div className="pt-5 border-t border-gray-100">
<div className="flex items-center justify-between text-sm">
<span className="text-gray-500 font-medium">Start Check-out</span>
<ChevronRight className="w-5 h-5 text-teal-600 group-hover:translate-x-1 transition-transform" />
</div>
</div>
</div>
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-teal-50/30 to-transparent opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none"></div>
</div>
<div
onClick={() => 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"
>
<div className="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-blue-400/10 to-transparent rounded-bl-full"></div>
<div className="relative space-y-5">
<div className="flex items-start justify-between">
<div className="flex items-center gap-4">
<div className="relative">
<div className="absolute inset-0 bg-gradient-to-br from-blue-400 to-blue-600 rounded-xl blur-md opacity-50 group-hover:opacity-75 transition-opacity"></div>
<div className="relative p-3.5 rounded-xl bg-gradient-to-br from-blue-500 to-blue-600 shadow-lg border border-blue-400/50 group-hover:scale-110 transition-transform">
<Calendar className="w-6 h-6 text-white" />
</div>
</div>
<div>
<h3 className="font-bold text-gray-900 text-xl mb-1">Bookings</h3>
<div className="h-1 w-12 bg-gradient-to-r from-blue-500 to-blue-600 rounded-full"></div>
</div>
</div>
</div>
<p className="text-gray-600 text-sm leading-relaxed">
Manage and track all hotel bookings
</p>
<div className="pt-5 border-t border-gray-100">
<div className="flex items-center justify-between text-sm">
<span className="text-gray-500 font-medium">Manage Bookings</span>
<ChevronRight className="w-5 h-5 text-blue-600 group-hover:translate-x-1 transition-transform" />
</div>
</div>
</div>
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-blue-50/30 to-transparent opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none"></div>
</div>
<div
onClick={() => 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"
>
<div className="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-purple-400/10 to-transparent rounded-bl-full"></div>
<div className="relative space-y-5">
<div className="flex items-start justify-between">
<div className="flex items-center gap-4">
<div className="relative">
<div className="absolute inset-0 bg-gradient-to-br from-purple-400 to-purple-600 rounded-xl blur-md opacity-50 group-hover:opacity-75 transition-opacity"></div>
<div className="relative p-3.5 rounded-xl bg-gradient-to-br from-purple-500 to-purple-600 shadow-lg border border-purple-400/50 group-hover:scale-110 transition-transform">
<Wrench className="w-6 h-6 text-white" />
</div>
</div>
<div>
<h3 className="font-bold text-gray-900 text-xl mb-1">Services</h3>
<div className="h-1 w-12 bg-gradient-to-r from-purple-500 to-purple-600 rounded-full"></div>
</div>
</div>
</div>
<p className="text-gray-600 text-sm leading-relaxed">
Manage hotel services and amenities
</p>
<div className="pt-5 border-t border-gray-100">
<div className="flex items-center justify-between text-sm">
<span className="text-gray-500 font-medium">Manage Services</span>
<ChevronRight className="w-5 h-5 text-purple-600 group-hover:translate-x-1 transition-transform" />
</div>
</div>
</div>
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-purple-50/30 to-transparent opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none"></div>
</div>
</div>
)}
{}
{activeTab === 'check-in' && (
<div className="space-y-8">
{checkInLoading && (
<Loading fullScreen text="Processing check-in..." />
)}
{}
<div className="bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
<div className="space-y-3">
<div className="flex items-center gap-3">
<div className="p-2.5 rounded-xl bg-gradient-to-br from-emerald-500/10 to-green-500/10 border border-emerald-200/40">
<LogIn className="w-6 h-6 text-emerald-600" />
</div>
<h2 className="text-xl sm:text-2xl md:text-2xl font-extrabold text-gray-900">Check-in</h2>
</div>
<p className="text-gray-600 text-base max-w-2xl leading-relaxed">
Customer check-in process and room assignment
</p>
</div>
</div>
{}
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
<h2 className="text-xl font-bold text-gray-900 mb-6 flex items-center gap-3">
<div className="w-8 h-8 rounded-lg bg-emerald-100 flex items-center justify-center">
<span className="text-emerald-600 font-bold">1</span>
</div>
Search Booking
</h2>
<div className="flex gap-4">
<div className="flex-1 relative group">
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5 group-focus-within:text-emerald-500 transition-colors" />
<input
type="text"
value={checkInBookingNumber}
onChange={(e) => 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"
/>
</div>
<button
onClick={handleCheckInSearch}
disabled={checkInSearching}
className="group relative px-8 py-3.5 bg-gradient-to-r from-emerald-500 via-emerald-500 to-teal-600 text-white font-semibold rounded-xl shadow-xl shadow-emerald-500/30 hover:shadow-2xl hover:shadow-emerald-500/40 transition-all duration-300 hover:scale-105 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100 overflow-hidden"
>
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent translate-x-[-100%] group-hover:translate-x-[100%] transition-transform duration-1000"></div>
<div className="relative flex items-center gap-2">
<Search className="w-5 h-5" />
{checkInSearching ? 'Searching...' : 'Search'}
</div>
</button>
</div>
</div>
{}
{checkInBooking && (
<>
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
<h2 className="text-xl font-bold text-gray-900 mb-6 flex items-center gap-3">
<div className="w-8 h-8 rounded-lg bg-emerald-100 flex items-center justify-center">
<CheckCircle className="w-5 h-5 text-emerald-600" />
</div>
Booking Information
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<div className="space-y-4">
<div className="flex justify-between items-center py-2 border-b border-gray-100">
<span className="text-gray-600 font-medium">Booking Number:</span>
<span className="font-bold text-gray-900 font-mono">{checkInBooking.booking_number}</span>
</div>
<div className="flex justify-between items-center py-2 border-b border-gray-100">
<span className="text-gray-600 font-medium">Customer:</span>
<span className="font-semibold text-gray-900">{checkInBooking.user?.full_name}</span>
</div>
<div className="flex justify-between items-center py-2 border-b border-gray-100">
<span className="text-gray-600 font-medium">Email:</span>
<span className="text-gray-900">{checkInBooking.user?.email}</span>
</div>
<div className="flex justify-between items-center py-2">
<span className="text-gray-600 font-medium">Phone:</span>
<span className="text-gray-900">{checkInBooking.user?.phone_number || 'N/A'}</span>
</div>
</div>
<div className="space-y-4">
<div className="flex justify-between items-center py-2 border-b border-gray-100">
<span className="text-gray-600 font-medium">Room Type:</span>
<span className="font-semibold text-gray-900">{checkInBooking.room?.room_type?.name || 'N/A'}</span>
</div>
<div className="flex justify-between items-center py-2 border-b border-gray-100">
<span className="text-gray-600 font-medium">Check-in:</span>
<span className="text-gray-900">{checkInBooking.check_in_date ? parseDateLocal(checkInBooking.check_in_date).toLocaleDateString('en-US') : 'N/A'}</span>
</div>
<div className="flex justify-between items-center py-2 border-b border-gray-100">
<span className="text-gray-600 font-medium">Check-out:</span>
<span className="text-gray-900">{checkInBooking.check_out_date ? parseDateLocal(checkInBooking.check_out_date).toLocaleDateString('en-US') : 'N/A'}</span>
</div>
<div className="flex justify-between items-center py-2">
<span className="text-gray-600 font-medium">Number of Guests:</span>
<span className="font-semibold text-gray-900">{checkInBooking.guest_count} guest(s)</span>
</div>
</div>
</div>
{}
<div className="mt-6 p-6 bg-gradient-to-r from-green-50 to-emerald-50 rounded-xl border border-green-200">
<h3 className="text-md font-semibold text-gray-900 mb-4 flex items-center gap-2">
<CheckCircle className="w-5 h-5 text-green-600" />
Payment Information
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<div className="space-y-3">
<div className="flex justify-between items-center py-2 border-b border-green-100">
<span className="text-gray-600 font-medium">Payment Method:</span>
<span className="font-semibold text-gray-900">
{checkInBooking.payment_method === 'cash'
? '💵 Pay at Hotel'
: checkInBooking.payment_method === 'stripe'
? '💳 Stripe (Card)'
: checkInBooking.payment_method === 'paypal'
? '💳 PayPal'
: checkInBooking.payment_method || 'N/A'}
</span>
</div>
<div className="flex justify-between items-center py-2">
<span className="text-gray-600 font-medium">Payment Status:</span>
<span className={`font-semibold ${
checkInBooking.payment_status === 'paid'
? 'text-green-600'
: checkInBooking.payment_status === 'refunded'
? 'text-orange-600'
: 'text-red-600'
}`}>
{checkInBooking.payment_status === 'paid'
? '✅ Paid'
: checkInBooking.payment_status === 'refunded'
? '💰 Refunded'
: '❌ Unpaid'}
</span>
</div>
</div>
</div>
<div>
<div className="space-y-3">
{(() => {
const completedPayments = checkInBooking.payments?.filter(
(p) => p.payment_status === 'completed'
) || [];
const amountPaid = completedPayments.reduce(
(sum, p) => sum + (p.amount || 0),
0
);
const remainingDue = checkInBooking.total_price - amountPaid;
const hasPayments = completedPayments.length > 0;
return (
<>
<div className="flex justify-between items-center py-2 border-b border-green-100">
<span className="text-gray-600 font-medium">Total Price:</span>
<span className="font-semibold text-gray-900">{formatCurrency(checkInBooking.total_price)}</span>
</div>
{hasPayments && (
<>
<div className="flex justify-between items-center py-2 border-b border-green-100">
<span className="text-gray-600 font-medium">Amount Paid:</span>
<span className="font-semibold text-green-600">{formatCurrency(amountPaid)}</span>
</div>
{remainingDue > 0 && (
<div className="flex justify-between items-center py-2">
<span className="text-gray-600 font-medium">Remaining Due:</span>
<span className="font-semibold text-amber-600">{formatCurrency(remainingDue)}</span>
</div>
)}
{completedPayments.length > 0 && (
<div className="mt-3 pt-3 border-t border-green-200">
<p className="text-xs text-gray-500 mb-2 font-medium">Payment Details:</p>
{completedPayments.map((payment, idx) => (
<div key={payment.id || idx} className="text-xs text-gray-600 mb-1">
{formatCurrency(payment.amount)} via {payment.payment_method === 'stripe' ? 'Stripe' : payment.payment_method === 'paypal' ? 'PayPal' : payment.payment_method || 'Cash'}
{payment.payment_type === 'deposit' && ' (Deposit 20%)'}
{payment.transaction_id && ` - ${payment.transaction_id}`}
</div>
))}
</div>
)}
</>
)}
</>
);
})()}
</div>
</div>
</div>
</div>
{checkInBooking.status !== 'confirmed' && (
<div className="mt-6 p-4 bg-gradient-to-br from-amber-50 to-yellow-50 border border-amber-200 rounded-xl flex items-start gap-3">
<AlertCircle className="w-5 h-5 text-amber-600 mt-0.5 flex-shrink-0" />
<div>
<p className="text-sm font-semibold text-amber-900">Warning</p>
<p className="text-sm text-amber-800 mt-1">
Booking status: <span className="font-semibold">{checkInBooking.status}</span>. Only check-in confirmed bookings.
</p>
</div>
</div>
)}
</div>
{}
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
<h2 className="text-xl font-bold text-gray-900 mb-6 flex items-center gap-3">
<div className="w-8 h-8 rounded-lg bg-blue-100 flex items-center justify-center">
<Hotel className="w-5 h-5 text-blue-600" />
</div>
Assign Actual Room Number
</h2>
<div className="max-w-md space-y-3">
<label className="block text-sm font-semibold text-gray-900">
Room Number <span className="text-red-500">*</span>
</label>
<input
type="text"
value={actualRoomNumber}
onChange={(e) => 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"
/>
<p className="text-xs text-gray-500">
Enter the actual room number to assign to the guest
</p>
</div>
</div>
{}
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
<h2 className="text-xl font-bold text-gray-900 mb-6 flex items-center gap-3">
<div className="w-8 h-8 rounded-lg bg-purple-100 flex items-center justify-center">
<User className="w-5 h-5 text-purple-600" />
</div>
Guest Information
</h2>
<div className="space-y-6">
{guests.map((guest, index) => (
<div key={index} className="p-6 bg-gradient-to-br from-gray-50 to-white border-2 border-gray-200 rounded-xl hover:border-purple-200 transition-colors">
<div className="flex justify-between items-center mb-4">
<h3 className="font-bold text-gray-900">
{index === 0 ? 'Main Guest' : `Guest ${index + 1}`}
{index === 0 && <span className="text-red-500 ml-1">*</span>}
</h3>
{index > 0 && (
<button
onClick={() => handleRemoveGuest(index)}
className="px-4 py-2 text-red-600 hover:text-red-700 hover:bg-red-50 rounded-lg transition-colors text-sm font-semibold"
>
Remove
</button>
)}
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="space-y-2">
<label className="block text-sm font-semibold text-gray-700">
Full Name {index === 0 && <span className="text-red-500">*</span>}
</label>
<input
type="text"
value={guest.name}
onChange={(e) => 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"
/>
</div>
<div className="space-y-2">
<label className="block text-sm font-semibold text-gray-700">
ID Number {index === 0 && <span className="text-red-500">*</span>}
</label>
<input
type="text"
value={guest.id_number}
onChange={(e) => 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"
/>
</div>
<div className="space-y-2">
<label className="block text-sm font-semibold text-gray-700">
Phone Number {index === 0 && <span className="text-red-500">*</span>}
</label>
<input
type="tel"
value={guest.phone}
onChange={(e) => 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"
/>
</div>
</div>
</div>
))}
<button
onClick={handleAddGuest}
className="px-6 py-3 text-emerald-600 hover:text-emerald-700 hover:bg-emerald-50 rounded-xl transition-colors text-sm font-semibold border-2 border-emerald-200 hover:border-emerald-300"
>
+ Add Guest
</button>
</div>
</div>
{}
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
<h2 className="text-xl font-bold text-gray-900 mb-6">Additional Fees (if any)</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="space-y-3">
<label className="block text-sm font-semibold text-gray-900">Extra Persons</label>
<input
type="number"
min="0"
value={extraPersons}
onChange={(e) => {
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"
/>
<p className="text-xs text-gray-500">50/person</p>
</div>
<div className="space-y-3">
<label className="block text-sm font-semibold text-gray-900">Number of Children</label>
<input
type="number"
min="0"
value={children}
onChange={(e) => {
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"
/>
<p className="text-xs text-gray-500">25/child</p>
</div>
<div className="space-y-3">
<label className="block text-sm font-semibold text-gray-900">Total Additional Fee</label>
<div className="px-4 py-3.5 bg-gradient-to-br from-emerald-50 to-green-50 border-2 border-emerald-200 rounded-xl text-lg font-bold text-emerald-600">
{formatCurrency(additionalFee)}
</div>
</div>
</div>
</div>
{}
<div className="relative bg-gradient-to-r from-emerald-500 via-emerald-600 to-teal-600 rounded-2xl shadow-2xl p-8 text-white overflow-hidden">
<div className="absolute top-0 right-0 w-64 h-64 bg-white/10 rounded-full -mr-32 -mt-32"></div>
<div className="relative flex flex-col md:flex-row justify-between items-start md:items-center gap-6">
<div>
<h3 className="text-xl font-bold mb-2">Confirm Check-in</h3>
<p className="text-emerald-100 text-sm">
Guest: <span className="font-semibold">{checkInBooking.user?.full_name}</span> |
Room: <span className="font-semibold">{actualRoomNumber || 'Not assigned'}</span>
{additionalFee > 0 && (
<> | Additional Fee: <span className="font-semibold">{formatCurrency(additionalFee)}</span></>
)}
</p>
</div>
<button
onClick={handleCheckIn}
disabled={!actualRoomNumber || !guests[0].name || checkInBooking.status !== 'confirmed'}
className="px-8 py-4 bg-white text-emerald-600 rounded-xl hover:bg-emerald-50 disabled:bg-gray-300 disabled:text-gray-500 disabled:cursor-not-allowed font-bold flex items-center gap-2 transition-all duration-200 shadow-xl hover:shadow-2xl hover:scale-105 disabled:hover:scale-100"
>
<CheckCircle className="w-5 h-5" />
Confirm Check-in
</button>
</div>
</div>
</>
)}
{}
{!checkInBooking && !checkInSearching && (
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-12 text-center">
<div className="w-20 h-20 mx-auto mb-4 rounded-2xl bg-gradient-to-br from-emerald-100 to-emerald-200 flex items-center justify-center">
<Search className="w-10 h-10 text-emerald-600" />
</div>
<h3 className="text-xl font-bold text-gray-900 mb-2">No booking selected</h3>
<p className="text-gray-600">
Please enter booking number above to start check-in process
</p>
</div>
)}
</div>
)}
{}
{activeTab === 'check-out' && (
<div className="space-y-8">
{checkOutLoading && (
<Loading fullScreen text="Processing check-out..." />
)}
{}
<div className="bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
<div className="space-y-3">
<div className="flex items-center gap-3">
<div className="p-2.5 rounded-xl bg-gradient-to-br from-teal-500/10 to-cyan-500/10 border border-teal-200/40">
<LogOut className="w-6 h-6 text-teal-600" />
</div>
<h2 className="text-xl sm:text-2xl md:text-2xl font-extrabold text-gray-900">Check-out</h2>
</div>
<p className="text-gray-600 text-base max-w-2xl leading-relaxed">
Payment and check-out process for guests
</p>
</div>
</div>
{}
{!showInvoice && (
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
<h2 className="text-xl font-bold text-gray-900 mb-6 flex items-center gap-3">
<div className="w-8 h-8 rounded-lg bg-teal-100 flex items-center justify-center">
<span className="text-teal-600 font-bold">1</span>
</div>
Search Booking
</h2>
<div className="flex gap-4">
<div className="flex-1 relative group">
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5 group-focus-within:text-teal-500 transition-colors" />
<input
type="text"
value={checkOutBookingNumber}
onChange={(e) => 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"
/>
</div>
<button
onClick={handleCheckOutSearch}
disabled={checkOutSearching}
className="group relative px-8 py-3.5 bg-gradient-to-r from-teal-500 via-teal-500 to-cyan-600 text-white font-semibold rounded-xl shadow-xl shadow-teal-500/30 hover:shadow-2xl hover:shadow-teal-500/40 transition-all duration-300 hover:scale-105 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100 overflow-hidden"
>
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent translate-x-[-100%] group-hover:translate-x-[100%] transition-transform duration-1000"></div>
<div className="relative flex items-center gap-2">
<Search className="w-5 h-5" />
{checkOutSearching ? 'Searching...' : 'Search'}
</div>
</button>
</div>
</div>
)}
{}
{checkOutBooking && !showInvoice && (
<>
{}
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
<h2 className="text-xl font-bold text-gray-900 mb-6 flex items-center gap-3">
<div className="w-8 h-8 rounded-lg bg-teal-100 flex items-center justify-center">
<CheckCircle className="w-5 h-5 text-teal-600" />
</div>
Booking Information
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<div className="space-y-4">
<div className="flex justify-between items-center py-2 border-b border-gray-100">
<span className="text-gray-600 font-medium">Booking number:</span>
<span className="font-bold text-gray-900 font-mono">{checkOutBooking.booking_number}</span>
</div>
<div className="flex justify-between items-center py-2 border-b border-gray-100">
<span className="text-gray-600 font-medium">Customer:</span>
<span className="font-semibold text-gray-900">{checkOutBooking.user?.full_name}</span>
</div>
<div className="flex justify-between items-center py-2">
<span className="text-gray-600 font-medium">Room number:</span>
<span className="font-semibold text-gray-900">{checkOutBooking.room?.room_number}</span>
</div>
</div>
<div className="space-y-4">
<div className="flex justify-between items-center py-2 border-b border-gray-100">
<span className="text-gray-600 font-medium">Check-in:</span>
<span className="text-gray-900">{checkOutBooking.check_in_date ? parseDateLocal(checkOutBooking.check_in_date).toLocaleDateString('en-US') : 'N/A'}</span>
</div>
<div className="flex justify-between items-center py-2 border-b border-gray-100">
<span className="text-gray-600 font-medium">Check-out:</span>
<span className="text-gray-900">{checkOutBooking.check_out_date ? parseDateLocal(checkOutBooking.check_out_date).toLocaleDateString('en-US') : 'N/A'}</span>
</div>
<div className="flex justify-between items-center py-2">
<span className="text-gray-600 font-medium">Nights:</span>
<span className="font-semibold text-gray-900">
{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)
</span>
</div>
</div>
</div>
</div>
{}
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
<h2 className="text-xl font-bold text-gray-900 mb-6 flex items-center gap-3">
<div className="w-8 h-8 rounded-lg bg-blue-100 flex items-center justify-center">
<FileText className="w-5 h-5 text-blue-600" />
</div>
Invoice Details
</h2>
{}
<div className="mb-6">
<h3 className="font-semibold text-gray-700 mb-3">Room Fee</h3>
<div className="bg-gradient-to-br from-gray-50 to-white p-5 rounded-xl border border-gray-200">
<div className="flex justify-between items-center">
<span className="text-gray-900 font-medium">{checkOutBooking.room?.room_type?.name || 'Room'}</span>
<span className="font-bold text-gray-900">{formatCurrency(calculateRoomFee())}</span>
</div>
</div>
</div>
{}
{checkOutServices.length > 0 && (
<div className="mb-6">
<h3 className="font-semibold text-gray-700 mb-3">Services Used</h3>
<div className="bg-gradient-to-br from-gray-50 to-white p-5 rounded-xl border border-gray-200 space-y-3">
{checkOutServices.map((service, index) => (
<div key={index} className="flex justify-between items-center text-sm py-2 border-b border-gray-200 last:border-0">
<span className="text-gray-700">
{service.service_name} (x{service.quantity})
</span>
<span className="font-semibold text-gray-900">{formatCurrency(service.total)}</span>
</div>
))}
<div className="pt-3 border-t-2 border-gray-300 flex justify-between font-bold">
<span>Total services:</span>
<span className="text-teal-600">{formatCurrency(calculateServiceFee())}</span>
</div>
</div>
</div>
)}
{}
{calculateCheckOutAdditionalFee() > 0 && (
<div className="mb-6">
<h3 className="font-semibold text-gray-700 mb-3">Additional Fees</h3>
<div className="bg-gradient-to-br from-gray-50 to-white p-5 rounded-xl border border-gray-200">
<div className="flex justify-between items-center">
<span className="text-gray-700">Extra person/children fee</span>
<span className="font-semibold text-gray-900">{formatCurrency(calculateCheckOutAdditionalFee())}</span>
</div>
</div>
</div>
)}
{}
<div className="mb-6">
<h3 className="font-semibold text-gray-700 mb-3">Discount</h3>
<input
type="number"
value={discount}
onChange={(e) => 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"
/>
</div>
{}
<div className="border-t-2 border-gray-300 pt-6 space-y-4">
<div className="flex justify-between items-center text-lg">
<span className="text-gray-700 font-medium">Subtotal:</span>
<span className="font-bold text-gray-900">{formatCurrency(calculateSubtotal())}</span>
</div>
{discount > 0 && (
<div className="flex justify-between items-center text-lg text-rose-600">
<span>Discount:</span>
<span className="font-semibold">-{formatCurrency(discount)}</span>
</div>
)}
<div className="flex justify-between items-center text-lg sm:text-xl md:text-xl font-extrabold bg-gradient-to-r from-teal-600 to-cyan-600 bg-clip-text text-transparent pt-2 border-t-2 border-gray-300">
<span>Total:</span>
<span>{formatCurrency(calculateTotal())}</span>
</div>
{calculateTotalPaid() > 0 && (
<div className="flex justify-between items-center text-lg text-gray-600">
<span>Total paid:</span>
<span className="font-semibold">-{formatCurrency(calculateTotalPaid())}</span>
</div>
)}
<div className="flex justify-between items-center text-xl sm:text-2xl md:text-2xl font-extrabold text-emerald-600 pt-4 border-t-2 border-gray-300">
<span>Remaining payment:</span>
<span>{formatCurrency(calculateRemaining())}</span>
</div>
</div>
</div>
{}
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
<h2 className="text-xl font-bold text-gray-900 mb-6 flex items-center gap-3">
<div className="w-8 h-8 rounded-lg bg-green-100 flex items-center justify-center">
<CreditCard className="w-5 h-5 text-green-600" />
</div>
Payment Method
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 max-w-2xl">
<button
onClick={() => setPaymentMethod('cash')}
className={`p-6 border-2 rounded-xl text-center transition-all duration-200 ${
paymentMethod === 'cash'
? 'border-emerald-600 bg-gradient-to-br from-emerald-50 to-green-50 text-emerald-700 shadow-lg'
: 'border-gray-200 hover:border-gray-300 text-gray-700 hover:bg-gray-50'
}`}
>
<CurrencyIcon className="mx-auto mb-3" size={40} />
<div className="font-bold text-lg">Cash</div>
</button>
<button
onClick={() => setPaymentMethod('stripe')}
className={`p-6 border-2 rounded-xl text-center transition-all duration-200 ${
paymentMethod === 'stripe'
? 'border-indigo-600 bg-gradient-to-br from-indigo-50 to-purple-50 text-indigo-700 shadow-lg'
: 'border-gray-200 hover:border-gray-300 text-gray-700 hover:bg-gray-50'
}`}
>
<CreditCard className="w-10 h-10 mx-auto mb-3" />
<div className="font-bold text-lg">Stripe</div>
</button>
</div>
</div>
{}
<div className="relative bg-gradient-to-r from-teal-500 via-teal-600 to-cyan-600 rounded-2xl shadow-2xl p-8 text-white overflow-hidden">
<div className="absolute top-0 right-0 w-64 h-64 bg-white/10 rounded-full -mr-32 -mt-32"></div>
<div className="relative flex flex-col md:flex-row justify-between items-start md:items-center gap-6">
<div>
<h3 className="text-xl font-bold mb-2">Confirm Check-out</h3>
<p className="text-teal-100 text-sm">
Total payment: <span className="font-bold text-white text-xl">{formatCurrency(calculateRemaining())}</span>
</p>
</div>
<button
onClick={handleCheckOut}
disabled={checkOutBooking.status !== 'checked_in'}
className="px-8 py-4 bg-white text-teal-600 rounded-xl hover:bg-teal-50 disabled:bg-gray-300 disabled:text-gray-500 disabled:cursor-not-allowed font-bold flex items-center gap-2 transition-all duration-200 shadow-xl hover:shadow-2xl hover:scale-105 disabled:hover:scale-100"
>
<CheckCircle className="w-5 h-5" />
Confirm payment & Check-out
</button>
</div>
</div>
</>
)}
{}
{showInvoice && checkOutBooking && (
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-2xl border border-gray-200/50 p-8">
<div className="text-center mb-8">
<h2 className="text-xl sm:text-2xl md:text-2xl font-extrabold text-gray-900">PAYMENT INVOICE</h2>
<p className="text-gray-600 mt-2">Check-out successful</p>
</div>
<div className="border-t-2 border-b-2 border-gray-300 py-6 mb-8">
<div className="grid grid-cols-2 gap-8">
<div>
<p className="text-sm text-gray-600 mb-1">Booking number:</p>
<p className="font-bold text-gray-900 font-mono">{checkOutBooking.booking_number}</p>
</div>
<div>
<p className="text-sm text-gray-600 mb-1">Check-out date:</p>
<p className="font-bold text-gray-900">{new Date().toLocaleString('en-US')}</p>
</div>
<div>
<p className="text-sm text-gray-600 mb-1">Customer:</p>
<p className="font-bold text-gray-900">{checkOutBooking.user?.full_name}</p>
</div>
<div>
<p className="text-sm text-gray-600 mb-1">Payment method:</p>
<p className="font-bold text-gray-900">
{paymentMethod === 'cash' ? 'Cash' : 'Stripe'}
</p>
</div>
</div>
</div>
<div className="mb-8">
<div className="flex justify-between items-center text-lg sm:text-xl md:text-xl font-extrabold text-emerald-600">
<span>Total payment:</span>
<span>{formatCurrency(calculateRemaining())}</span>
</div>
</div>
<div className="flex gap-4">
<button
onClick={handlePrintInvoice}
className="flex-1 px-6 py-4 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-xl hover:from-blue-600 hover:to-blue-700 font-semibold flex items-center justify-center gap-2 transition-all duration-200 shadow-lg hover:shadow-xl"
>
<Printer className="w-5 h-5" />
Print Invoice
</button>
<button
onClick={resetCheckOutForm}
className="flex-1 px-6 py-4 bg-gradient-to-r from-gray-400 to-gray-500 text-white rounded-xl hover:from-gray-500 hover:to-gray-600 font-semibold transition-all duration-200 shadow-lg hover:shadow-xl"
>
Complete
</button>
</div>
</div>
)}
{}
{!checkOutBooking && !checkOutSearching && !showInvoice && (
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-12 text-center">
<div className="w-20 h-20 mx-auto mb-4 rounded-2xl bg-gradient-to-br from-teal-100 to-teal-200 flex items-center justify-center">
<Search className="w-10 h-10 text-teal-600" />
</div>
<h3 className="text-xl font-bold text-gray-900 mb-2">No booking selected</h3>
<p className="text-gray-600">
Please enter booking number to start check-out process
</p>
</div>
)}
</div>
)}
{}
{activeTab === 'bookings' && (
<div className="space-y-8">
{bookingsLoading && <Loading />}
{}
<div className="bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<div className="space-y-3 flex-1">
<div className="flex items-center gap-3">
<div className="p-2.5 rounded-xl bg-gradient-to-br from-blue-500/10 to-indigo-500/10 border border-blue-200/40">
<Calendar className="w-6 h-6 text-blue-600" />
</div>
<h2 className="text-xl sm:text-2xl md:text-2xl font-extrabold text-gray-900">Bookings Management</h2>
</div>
<p className="text-gray-600 text-base max-w-2xl leading-relaxed">
Manage and track all hotel bookings with precision
</p>
</div>
<button
onClick={() => setShowCreateBookingModal(true)}
className="flex items-center justify-center gap-2 px-6 py-3 bg-gradient-to-r from-amber-500 to-amber-600 text-white rounded-xl font-semibold hover:from-amber-600 hover:to-amber-700 transition-all duration-200 shadow-lg hover:shadow-xl whitespace-nowrap w-full sm:w-auto"
>
<Plus className="w-5 h-5" />
Create Booking
</button>
</div>
</div>
{}
<div className="bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
<div className="relative group">
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5 group-focus-within:text-blue-500 transition-colors" />
<input
type="text"
placeholder="Search by booking number, guest name..."
value={bookingFilters.search}
onChange={(e) => 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"
/>
</div>
<select
value={bookingFilters.status}
onChange={(e) => setBookingFilters({ ...bookingFilters, status: e.target.value })}
className="w-full px-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 font-medium shadow-sm hover:shadow-md cursor-pointer"
>
<option value="">All statuses</option>
<option value="pending">Pending confirmation</option>
<option value="confirmed">Confirmed</option>
<option value="checked_in">Checked in</option>
<option value="checked_out">Checked out</option>
<option value="cancelled">Canceled</option>
</select>
</div>
</div>
{}
<div className="bg-white/90 backdrop-blur-xl rounded-xl sm:rounded-2xl shadow-xl border border-gray-200/50 overflow-hidden">
<div className="overflow-x-auto -mx-2 sm:mx-0 px-2 sm:px-0">
<table className="w-full min-w-[640px] sm:min-w-full">
<thead>
<tr className="bg-gradient-to-r from-slate-900 via-slate-800 to-slate-900">
<th className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 text-left text-xs font-bold text-amber-100 uppercase tracking-wider border-b border-slate-700">Booking Number</th>
<th className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 text-left text-xs font-bold text-amber-100 uppercase tracking-wider border-b border-slate-700">Customer</th>
<th className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 text-left text-xs font-bold text-amber-100 uppercase tracking-wider border-b border-slate-700">Room</th>
<th className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 text-left text-xs font-bold text-amber-100 uppercase tracking-wider border-b border-slate-700">Check-in/out</th>
<th className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 text-left text-xs font-bold text-amber-100 uppercase tracking-wider border-b border-slate-700">Total Price</th>
<th className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 text-left text-xs font-bold text-amber-100 uppercase tracking-wider border-b border-slate-700">Status</th>
<th className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 text-right text-xs font-bold text-amber-100 uppercase tracking-wider border-b border-slate-700">Actions</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-slate-100">
{bookings.map((booking) => (
<tr
key={booking.id}
className="hover:bg-gradient-to-r hover:from-blue-50/30 hover:to-indigo-50/30 transition-all duration-200 group border-b border-slate-100"
>
<td className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 whitespace-nowrap">
<div className="text-sm font-bold text-gray-900 group-hover:text-blue-700 transition-colors font-mono">
{booking.booking_number}
</div>
</td>
<td className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 whitespace-nowrap">
<div className="text-xs sm:text-sm font-semibold text-gray-900">{booking.guest_info?.full_name || booking.user?.name}</div>
<div className="text-xs text-gray-500 mt-0.5 truncate max-w-[120px] sm:max-w-none">{booking.guest_info?.email || booking.user?.email}</div>
</td>
<td className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 whitespace-nowrap">
<div className="text-xs sm:text-sm font-medium text-gray-800">
<span className="text-blue-600 font-semibold">Room {booking.room?.room_number}</span>
<span className="text-gray-400 mx-1 sm:mx-2"></span>
<span className="text-gray-600 hidden sm:inline">{booking.room?.room_type?.name}</span>
</div>
</td>
<td className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 whitespace-nowrap">
<div className="text-xs sm:text-sm font-medium text-gray-900">
{parseDateLocal(booking.check_in_date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
</div>
<div className="text-xs text-gray-500 mt-0.5">
{parseDateLocal(booking.check_out_date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}
</div>
</td>
<td className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 whitespace-nowrap">
<div className="text-xs sm:text-sm font-bold text-gray-900 bg-gradient-to-r from-blue-600 to-blue-700 bg-clip-text text-transparent">
{formatCurrency(booking.total_price)}
</div>
</td>
<td className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 whitespace-nowrap">
{getBookingStatusBadge(booking.status)}
</td>
<td className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 whitespace-nowrap text-right">
<div className="flex items-center justify-end gap-1 sm:gap-2">
<button
onClick={() => {
setSelectedBooking(booking);
setShowBookingDetailModal(true);
}}
className="p-1.5 sm:p-2 rounded-lg text-gray-600 hover:text-blue-600 hover:bg-blue-50 transition-all duration-200 shadow-sm hover:shadow-md border border-gray-200 hover:border-blue-300"
title="View details"
>
<Eye className="w-4 h-4 sm:w-5 sm:h-5" />
</button>
{booking.status === 'pending' && (
<>
<button
onClick={() => handleUpdateBookingStatus(booking.id, 'confirmed')}
disabled={updatingBookingId === booking.id || cancellingBookingId === booking.id}
className="p-2 rounded-lg text-emerald-600 hover:text-emerald-700 hover:bg-emerald-50 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 shadow-sm hover:shadow-md border border-emerald-200 hover:border-emerald-300"
title="Confirm"
>
{updatingBookingId === booking.id ? (
<Loader2 className="w-5 h-5 animate-spin" />
) : (
<CheckCircle className="w-5 h-5" />
)}
</button>
<button
onClick={() => handleCancelBooking(booking.id)}
disabled={updatingBookingId === booking.id || cancellingBookingId === booking.id}
className="p-2 rounded-lg text-rose-600 hover:text-rose-700 hover:bg-rose-50 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 shadow-sm hover:shadow-md border border-rose-200 hover:border-rose-300"
title="Cancel"
>
{cancellingBookingId === booking.id ? (
<Loader2 className="w-5 h-5 animate-spin" />
) : (
<XCircle className="w-5 h-5" />
)}
</button>
</>
)}
{booking.status === 'confirmed' && (
<button
onClick={() => handleUpdateBookingStatus(booking.id, 'checked_in')}
disabled={updatingBookingId === booking.id || cancellingBookingId === booking.id}
className="p-2 rounded-lg text-emerald-600 hover:text-emerald-700 hover:bg-emerald-50 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 shadow-sm hover:shadow-md border border-emerald-200 hover:border-emerald-300"
title="Check-in"
>
{updatingBookingId === booking.id ? (
<Loader2 className="w-5 h-5 animate-spin" />
) : (
<CheckCircle className="w-5 h-5" />
)}
</button>
)}
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
<Pagination
currentPage={bookingCurrentPage}
totalPages={bookingTotalPages}
onPageChange={setBookingCurrentPage}
totalItems={bookingTotalItems}
itemsPerPage={bookingItemsPerPage}
/>
</div>
{}
{showBookingDetailModal && selectedBooking && (
<div className="fixed inset-0 bg-black/70 backdrop-blur-md flex items-center justify-center z-50 p-4">
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-3xl max-h-[90vh] overflow-hidden">
<div className="bg-gradient-to-r from-slate-900 via-slate-800 to-slate-900 px-8 py-6 border-b border-slate-700">
<div className="flex justify-between items-center">
<div>
<h2 className="text-xl sm:text-2xl md:text-2xl font-bold text-amber-100 mb-1">Booking Details</h2>
<p className="text-amber-200/80 text-sm font-light">Comprehensive booking information</p>
</div>
<button
onClick={() => setShowBookingDetailModal(false)}
className="w-10 h-10 flex items-center justify-center rounded-xl text-amber-100 hover:text-white hover:bg-slate-700/50 transition-all duration-200 border border-slate-600 hover:border-amber-400"
>
</button>
</div>
</div>
<div className="p-8 overflow-y-auto max-h-[calc(90vh-120px)]">
<div className="space-y-6">
<div className="grid grid-cols-2 gap-6">
<div className="bg-gradient-to-br from-slate-50 to-white p-5 rounded-xl border border-slate-200">
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wider mb-2 block">Booking Number</label>
<p className="text-xl font-bold text-slate-900 font-mono">{selectedBooking.booking_number}</p>
</div>
<div className="bg-gradient-to-br from-slate-50 to-white p-5 rounded-xl border border-slate-200">
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wider mb-2 block">Status</label>
<div className="mt-1">{getBookingStatusBadge(selectedBooking.status)}</div>
</div>
</div>
<div className="bg-gradient-to-br from-amber-50/50 to-yellow-50/50 p-6 rounded-xl border border-amber-100">
<label className="text-xs font-semibold text-slate-600 uppercase tracking-wider mb-4 block flex items-center gap-2">
<div className="w-1 h-4 bg-gradient-to-b from-amber-400 to-amber-600 rounded-full"></div>
Customer Information
</label>
<div className="space-y-2">
<p className="text-lg font-bold text-slate-900">{selectedBooking.guest_info?.full_name || selectedBooking.user?.name}</p>
<p className="text-slate-600">{selectedBooking.guest_info?.email || selectedBooking.user?.email}</p>
<p className="text-slate-600">{selectedBooking.guest_info?.phone || selectedBooking.user?.phone}</p>
</div>
</div>
<div className="bg-gradient-to-br from-blue-50/50 to-indigo-50/50 p-6 rounded-xl border border-blue-100">
<label className="text-xs font-semibold text-slate-600 uppercase tracking-wider mb-4 block flex items-center gap-2">
<div className="w-1 h-4 bg-gradient-to-b from-blue-400 to-blue-600 rounded-full"></div>
Room Information
</label>
<p className="text-lg font-semibold text-slate-900">
<span className="text-amber-600">Room {selectedBooking.room?.room_number}</span>
<span className="text-slate-400 mx-2"></span>
<span className="text-slate-700">{selectedBooking.room?.room_type?.name}</span>
</p>
</div>
<div className="grid grid-cols-2 gap-6">
<div className="bg-gradient-to-br from-slate-50 to-white p-5 rounded-xl border border-slate-200">
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wider mb-2 block">Check-in Date</label>
<p className="text-base font-semibold text-slate-900">{parseDateLocal(selectedBooking.check_in_date).toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' })}</p>
</div>
<div className="bg-gradient-to-br from-slate-50 to-white p-5 rounded-xl border border-slate-200">
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wider mb-2 block">Check-out Date</label>
<p className="text-base font-semibold text-slate-900">{parseDateLocal(selectedBooking.check_out_date).toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' })}</p>
</div>
</div>
<div className="bg-gradient-to-br from-slate-50 to-white p-5 rounded-xl border border-slate-200">
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wider mb-2 block">Number of Guests</label>
<p className="text-lg font-semibold text-slate-900">{selectedBooking.guest_count} guest{selectedBooking.guest_count !== 1 ? 's' : ''}</p>
</div>
{}
<div className="bg-gradient-to-br from-indigo-50/50 to-purple-50/50 p-6 rounded-xl border border-indigo-100">
<label className="text-xs font-semibold text-slate-600 uppercase tracking-wider mb-4 block flex items-center gap-2">
<div className="w-1 h-4 bg-gradient-to-b from-indigo-400 to-indigo-600 rounded-full"></div>
Payment Information
</label>
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-xs text-slate-500 mb-1">Payment Method</p>
<p className="text-base font-semibold text-slate-900">
{selectedBooking.payment_method === 'cash'
? '💵 Pay at Hotel'
: selectedBooking.payment_method === 'stripe'
? '💳 Stripe (Card)'
: selectedBooking.payment_method === 'paypal'
? '💳 PayPal'
: selectedBooking.payment_method || 'N/A'}
</p>
</div>
<div>
<p className="text-xs text-slate-500 mb-1">Payment Status</p>
<p className={`text-base font-semibold ${
selectedBooking.payment_status === 'paid'
? 'text-green-600'
: selectedBooking.payment_status === 'refunded'
? 'text-orange-600'
: 'text-red-600'
}`}>
{selectedBooking.payment_status === 'paid'
? '✅ Paid'
: selectedBooking.payment_status === 'refunded'
? '💰 Refunded'
: '❌ Unpaid'}
</p>
</div>
</div>
</div>
{}
{selectedBooking.payments && selectedBooking.payments.length > 0 && (
<div className="bg-gradient-to-br from-teal-50/50 to-cyan-50/50 p-6 rounded-xl border border-teal-100">
<label className="text-xs font-semibold text-slate-600 uppercase tracking-wider mb-4 block flex items-center gap-2">
<div className="w-1 h-4 bg-gradient-to-b from-teal-400 to-teal-600 rounded-full"></div>
Payment History
</label>
<div className="space-y-3">
{selectedBooking.payments.map((payment: any, idx: number) => (
<div key={payment.id || idx} className="p-3 bg-white rounded-lg border border-teal-100">
<div className="flex justify-between items-start mb-2">
<div>
<p className="text-sm font-semibold text-slate-900">
{formatCurrency(payment.amount || 0)}
</p>
<p className="text-xs text-slate-500 mt-1">
{payment.payment_type === 'deposit' ? 'Deposit (20%)' : payment.payment_type === 'remaining' ? 'Remaining Payment' : 'Full Payment'}
{' • '}
{payment.payment_method === 'stripe' ? 'Stripe' : payment.payment_method === 'paypal' ? 'PayPal' : payment.payment_method || 'Cash'}
</p>
</div>
<span className={`text-xs px-2 py-1 rounded-full font-medium ${
payment.payment_status === 'completed' || payment.payment_status === 'paid'
? 'bg-green-100 text-green-700'
: payment.payment_status === 'pending'
? 'bg-yellow-100 text-yellow-700'
: 'bg-red-100 text-red-700'
}`}>
{payment.payment_status === 'completed' || payment.payment_status === 'paid' ? 'Paid' : payment.payment_status || 'Pending'}
</span>
</div>
{payment.transaction_id && (
<p className="text-xs text-slate-400 font-mono">ID: {payment.transaction_id}</p>
)}
{payment.payment_date && (
<p className="text-xs text-slate-400 mt-1">
{new Date(payment.payment_date).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })}
</p>
)}
</div>
))}
</div>
</div>
)}
{}
{(() => {
const completedPayments = selectedBooking.payments?.filter(
(p) => p.payment_status === 'completed'
) || [];
const amountPaid = completedPayments.reduce(
(sum, p) => sum + (p.amount || 0),
0
);
const remainingDue = selectedBooking.total_price - amountPaid;
const hasPayments = selectedBooking.payments && selectedBooking.payments.length > 0;
return (
<>
{}
<div className="bg-gradient-to-br from-green-50 via-emerald-50 to-green-50 p-6 rounded-xl border-2 border-green-200 shadow-lg mb-4">
<label className="text-xs font-semibold text-green-700 uppercase tracking-wider mb-2 block">Amount Paid</label>
<p className="text-xl sm:text-2xl md:text-2xl font-bold bg-gradient-to-r from-green-600 via-emerald-700 to-green-600 bg-clip-text text-transparent">
{formatCurrency(amountPaid)}
</p>
{hasPayments && completedPayments.length > 0 && (
<p className="text-xs text-green-600 mt-2">
{completedPayments.length} payment{completedPayments.length !== 1 ? 's' : ''} completed
{amountPaid > 0 && selectedBooking.total_price > 0 && (
<span className="ml-2">
({((amountPaid / selectedBooking.total_price) * 100).toFixed(0)}% of total)
</span>
)}
</p>
)}
{amountPaid === 0 && !hasPayments && (
<p className="text-sm text-gray-500 mt-2">No payments made yet</p>
)}
</div>
{}
{remainingDue > 0 && (
<div className="bg-gradient-to-br from-amber-50 via-yellow-50 to-amber-50 p-6 rounded-xl border-2 border-amber-200 shadow-lg mb-4">
<label className="text-xs font-semibold text-amber-700 uppercase tracking-wider mb-2 block">Remaining Due (To be paid)</label>
<p className="text-xl sm:text-2xl md:text-2xl font-bold text-amber-600">
{formatCurrency(remainingDue)}
</p>
{selectedBooking.total_price > 0 && (
<p className="text-xs text-amber-600 mt-2">
({((remainingDue / selectedBooking.total_price) * 100).toFixed(0)}% of total)
</p>
)}
</div>
)}
{}
<div className="bg-gradient-to-br from-slate-50 to-gray-50 p-6 rounded-xl border-2 border-slate-200 shadow-lg">
<label className="text-xs font-semibold text-slate-600 uppercase tracking-wider mb-2 block">Total Booking Price</label>
{selectedBooking.original_price && selectedBooking.discount_amount && selectedBooking.discount_amount > 0 ? (
<>
<div className="mb-2">
<div className="flex justify-between items-center mb-1">
<span className="text-sm text-slate-600">Subtotal:</span>
<span className="text-lg font-semibold text-slate-700">{formatCurrency(selectedBooking.original_price)}</span>
</div>
<div className="flex justify-between items-center mb-2">
<span className="text-sm text-green-600">
Discount{selectedBooking.promotion_code ? ` (${selectedBooking.promotion_code})` : ''}:
</span>
<span className="text-lg font-semibold text-green-600">-{formatCurrency(selectedBooking.discount_amount)}</span>
</div>
<div className="border-t border-slate-300 pt-2 mt-2">
<div className="flex justify-between items-center">
<span className="text-sm font-semibold text-slate-700">Total:</span>
<span className="text-lg sm:text-xl md:text-xl font-bold text-slate-700">{formatCurrency(selectedBooking.total_price)}</span>
</div>
</div>
</div>
</>
) : (
<p className="text-lg sm:text-xl md:text-xl font-bold text-slate-700">
{formatCurrency(selectedBooking.total_price)}
</p>
)}
<p className="text-xs text-slate-500 mt-2">
This is the total amount for the booking
</p>
</div>
</>
);
})()}
{selectedBooking.notes && (
<div className="bg-gradient-to-br from-slate-50 to-white p-6 rounded-xl border border-slate-200">
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wider mb-3 block">Special Notes</label>
<p className="text-slate-700 leading-relaxed">{selectedBooking.notes}</p>
</div>
)}
</div>
<div className="mt-8 pt-6 border-t border-slate-200 flex justify-end">
<button
onClick={() => setShowBookingDetailModal(false)}
className="px-8 py-3 bg-gradient-to-r from-slate-700 to-slate-800 text-white rounded-xl font-semibold hover:from-slate-800 hover:to-slate-900 transition-all duration-200 shadow-lg hover:shadow-xl border border-slate-600"
>
Close
</button>
</div>
</div>
</div>
</div>
)}
</div>
)}
{}
{activeTab === 'services' && (
<div className="space-y-8">
{servicesLoading && <Loading />}
{}
<div className="bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
<div className="flex justify-between items-center">
<div className="space-y-3">
<div className="flex items-center gap-3">
<div className="p-2.5 rounded-xl bg-gradient-to-br from-purple-500/10 to-violet-500/10 border border-purple-200/40">
<Wrench className="w-6 h-6 text-purple-600" />
</div>
<h2 className="text-xl sm:text-2xl md:text-2xl font-extrabold text-gray-900">Service Management</h2>
</div>
<p className="text-gray-600 text-base max-w-2xl leading-relaxed">
Manage hotel services and amenities
</p>
</div>
<button
onClick={() => {
resetServiceForm();
setShowServiceModal(true);
}}
className="flex items-center gap-2 px-6 py-3 bg-gradient-to-r from-purple-500 to-purple-600 text-white rounded-xl font-semibold hover:from-purple-600 hover:to-purple-700 transition-all duration-200 shadow-lg hover:shadow-xl"
>
<Plus className="w-5 h-5" />
Add Service
</button>
</div>
</div>
{}
<div className="bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
<div className="relative group">
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5 group-focus-within:text-purple-500 transition-colors" />
<input
type="text"
placeholder="Search services..."
value={serviceFilters.search}
onChange={(e) => setServiceFilters({ ...serviceFilters, 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-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 placeholder-gray-400 font-medium shadow-sm hover:shadow-md"
/>
</div>
<select
value={serviceFilters.status}
onChange={(e) => setServiceFilters({ ...serviceFilters, status: e.target.value })}
className="w-full px-4 py-3.5 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm hover:shadow-md cursor-pointer"
>
<option value="">All Statuses</option>
<option value="active">Active</option>
<option value="inactive">Inactive</option>
</select>
</div>
</div>
{}
<div className="bg-white/90 backdrop-blur-xl rounded-xl sm:rounded-2xl shadow-xl border border-gray-200/50 overflow-hidden">
<div className="overflow-x-auto -mx-2 sm:mx-0 px-2 sm:px-0">
<table className="w-full min-w-[640px] sm:min-w-full">
<thead>
<tr className="bg-gradient-to-r from-slate-900 via-slate-800 to-slate-900">
<th className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 text-left text-xs font-bold text-amber-100 uppercase tracking-wider border-b border-slate-700">Service Name</th>
<th className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 text-left text-xs font-bold text-amber-100 uppercase tracking-wider border-b border-slate-700 hidden sm:table-cell">Description</th>
<th className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 text-left text-xs font-bold text-amber-100 uppercase tracking-wider border-b border-slate-700">Price</th>
<th className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 text-left text-xs font-bold text-amber-100 uppercase tracking-wider border-b border-slate-700">Unit</th>
<th className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 text-left text-xs font-bold text-amber-100 uppercase tracking-wider border-b border-slate-700">Status</th>
<th className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 text-right text-xs font-bold text-amber-100 uppercase tracking-wider border-b border-slate-700">Actions</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-slate-100">
{services.map((service) => (
<tr
key={service.id}
className="hover:bg-gradient-to-r hover:from-purple-50/30 hover:to-violet-50/30 transition-all duration-200 group border-b border-slate-100"
>
<td className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 whitespace-nowrap">
<div className="text-xs sm:text-sm font-semibold text-gray-900">{service.name}</div>
<div className="text-xs text-gray-500 sm:hidden mt-0.5 truncate max-w-[150px]">{service.description}</div>
</td>
<td className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 hidden sm:table-cell">
<div className="text-xs sm:text-sm text-gray-700 max-w-xs truncate">{service.description}</div>
</td>
<td className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 whitespace-nowrap">
<div className="text-xs sm:text-sm font-bold bg-gradient-to-r from-emerald-600 to-emerald-700 bg-clip-text text-transparent">
{formatCurrency(service.price)}
</div>
</td>
<td className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 whitespace-nowrap">
<div className="text-xs sm:text-sm text-gray-600">{service.unit}</div>
</td>
<td className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 whitespace-nowrap">
{getServiceStatusBadge(service.status)}
</td>
<td className="px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4 md:py-5 whitespace-nowrap text-right">
<div className="flex items-center justify-end gap-1 sm:gap-2">
<button
onClick={() => handleEditService(service)}
className="p-1.5 sm:p-2 rounded-lg text-blue-600 hover:text-blue-700 hover:bg-blue-50 transition-all duration-200 shadow-sm hover:shadow-md border border-blue-200 hover:border-blue-300"
title="Edit"
>
<Edit className="w-4 h-4 sm:w-5 sm:h-5" />
</button>
<button
onClick={() => handleDeleteService(service.id)}
className="p-1.5 sm:p-2 rounded-lg text-rose-600 hover:text-rose-700 hover:bg-rose-50 transition-all duration-200 shadow-sm hover:shadow-md border border-rose-200 hover:border-rose-300"
title="Delete"
>
<Trash2 className="w-4 h-4 sm:w-5 sm:h-5" />
</button>
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
<Pagination
currentPage={serviceCurrentPage}
totalPages={serviceTotalPages}
onPageChange={setServiceCurrentPage}
totalItems={serviceTotalItems}
itemsPerPage={serviceItemsPerPage}
/>
</div>
{}
{showServiceModal && (
<div className="fixed inset-0 bg-black/70 backdrop-blur-md flex items-center justify-center z-50 p-4">
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-md max-h-[90vh] overflow-hidden border border-slate-200">
{}
<div className="bg-gradient-to-r from-slate-900 via-slate-800 to-slate-900 px-6 py-5 border-b border-slate-700">
<div className="flex justify-between items-center">
<div>
<h2 className="text-lg sm:text-xl md:text-xl font-bold text-amber-100">
{editingService ? 'Update Service' : 'Add New Service'}
</h2>
<p className="text-amber-200/80 text-sm font-light mt-1">
{editingService ? 'Modify service information' : 'Create a new service'}
</p>
</div>
<button
onClick={() => {
setShowServiceModal(false);
resetServiceForm();
}}
className="w-10 h-10 flex items-center justify-center rounded-xl text-amber-100 hover:text-white hover:bg-slate-700/50 transition-all duration-200 border border-slate-600 hover:border-amber-400"
>
<X className="w-6 h-6" />
</button>
</div>
</div>
{}
<div className="p-6 overflow-y-auto max-h-[calc(90vh-120px)]">
<form onSubmit={handleServiceSubmit} className="space-y-5">
<div>
<label className="block text-xs font-semibold text-slate-600 uppercase tracking-wider mb-2">
Service Name
</label>
<input
type="text"
value={serviceFormData.name}
onChange={(e) => setServiceFormData({ ...serviceFormData, name: e.target.value })}
className="w-full px-4 py-3 bg-white border-2 border-slate-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-slate-700 font-medium shadow-sm"
required
/>
</div>
<div>
<label className="block text-xs font-semibold text-slate-600 uppercase tracking-wider mb-2">
Description
</label>
<textarea
value={serviceFormData.description}
onChange={(e) => setServiceFormData({ ...serviceFormData, description: e.target.value })}
className="w-full px-4 py-3 bg-white border-2 border-slate-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-slate-700 font-medium shadow-sm"
rows={3}
/>
</div>
<div>
<label className="block text-xs font-semibold text-slate-600 uppercase tracking-wider mb-2">
Price
</label>
<input
type="number"
value={serviceFormData.price}
onChange={(e) => setServiceFormData({ ...serviceFormData, price: parseFloat(e.target.value) || 0 })}
className="w-full px-4 py-3 bg-white border-2 border-slate-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-slate-700 font-medium shadow-sm"
required
min="0"
/>
</div>
<div>
<label className="block text-xs font-semibold text-slate-600 uppercase tracking-wider mb-2">
Unit
</label>
<input
type="text"
value={serviceFormData.unit}
onChange={(e) => setServiceFormData({ ...serviceFormData, unit: e.target.value })}
className="w-full px-4 py-3 bg-white border-2 border-slate-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-slate-700 font-medium shadow-sm"
placeholder="e.g: time, hour, day..."
/>
</div>
<div>
<label className="block text-xs font-semibold text-slate-600 uppercase tracking-wider mb-2">
Status
</label>
<select
value={serviceFormData.status}
onChange={(e) => setServiceFormData({ ...serviceFormData, status: e.target.value as any })}
className="w-full px-4 py-3 bg-white border-2 border-slate-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-slate-700 font-medium shadow-sm cursor-pointer"
>
<option value="active">Active</option>
<option value="inactive">Inactive</option>
</select>
</div>
<div className="flex gap-3 pt-4 border-t border-slate-200">
<button
type="button"
onClick={() => {
setShowServiceModal(false);
resetServiceForm();
}}
className="flex-1 px-6 py-3 border-2 border-slate-300 rounded-xl text-slate-700 font-semibold hover:bg-slate-50 transition-all duration-200 shadow-sm hover:shadow-md"
>
Cancel
</button>
<button
type="submit"
className="flex-1 px-6 py-3 bg-gradient-to-r from-purple-500 to-purple-600 text-white rounded-xl font-semibold hover:from-purple-600 hover:to-purple-700 transition-all duration-200 shadow-lg hover:shadow-xl"
>
{editingService ? 'Update' : 'Create'}
</button>
</div>
</form>
</div>
</div>
</div>
)}
</div>
)}
</div>
{/* Create Booking Modal */}
<CreateBookingModal
isOpen={showCreateBookingModal}
onClose={() => setShowCreateBookingModal(false)}
onSuccess={() => {
setShowCreateBookingModal(false);
fetchBookings();
}}
/>
</div>
);
};
export default ReceptionDashboardPage;