import React, { useState } from 'react'; import { Search, FileText, CreditCard, Printer, CheckCircle } from 'lucide-react'; import { bookingService, Booking } from '../../services/api'; import { toast } from 'react-toastify'; import Loading from '../../components/common/Loading'; import CurrencyIcon from '../../components/common/CurrencyIcon'; import { useFormatCurrency } from '../../hooks/useFormatCurrency'; interface ServiceItem { service_name: string; quantity: number; price: number; total: number; } const CheckOutPage: React.FC = () => { const { formatCurrency } = useFormatCurrency(); const [bookingNumber, setBookingNumber] = useState(''); const [booking, setBooking] = useState(null); const [loading, setLoading] = useState(false); const [searching, setSearching] = useState(false); const [services, setServices] = useState([]); const [paymentMethod, setPaymentMethod] = useState<'cash' | 'stripe'>('cash'); const [discount, setDiscount] = useState(0); const [showInvoice, setShowInvoice] = useState(false); const handleSearch = async () => { if (!bookingNumber.trim()) { toast.error('Please enter booking number'); return; } try { setSearching(true); const response = await bookingService.checkBookingByNumber(bookingNumber); const foundBooking = response.data.booking; if (foundBooking.status !== 'checked_in') { toast.warning('Only checked-in bookings can be checked out'); } setBooking(foundBooking); // Mock services data - in production will fetch from API setServices([ { 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'); setBooking(null); } finally { setSearching(false); } }; const calculateRoomFee = () => { if (!booking) return 0; return booking.total_price || 0; }; const calculateServiceFee = () => { return services.reduce((sum, service) => sum + service.total, 0); }; const calculateAdditionalFee = () => { // Additional fees from check-in (children, extra person) return 0; // In production will get from booking data }; const calculateDeposit = () => { // Deposit already paid return booking?.total_price ? booking.total_price * 0.3 : 0; }; const calculateSubtotal = () => { return calculateRoomFee() + calculateServiceFee() + calculateAdditionalFee(); }; const calculateDiscount = () => { return discount; }; const calculateTotal = () => { return calculateSubtotal() - calculateDiscount(); }; const calculateRemaining = () => { return calculateTotal() - calculateDeposit(); }; const handleCheckOut = async () => { if (!booking) return; if (calculateRemaining() < 0) { toast.error('Invalid refund amount'); return; } try { setLoading(true); // Update booking status await bookingService.updateBooking(booking.id, { status: 'checked_out', } as any); // Create payment record (if needed) // await paymentService.createPayment({...}); toast.success('Check-out successful'); setShowInvoice(true); } catch (error: any) { toast.error(error.response?.data?.message || 'An error occurred during check-out'); } finally { setLoading(false); } }; const handlePrintInvoice = () => { window.print(); }; const resetForm = () => { setBooking(null); setBookingNumber(''); setServices([]); setDiscount(0); setPaymentMethod('cash'); setShowInvoice(false); }; if (loading) { return ; } return (

Check-out

Payment and check-out process

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

1. Search booking

setBookingNumber(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && handleSearch()} placeholder="Enter booking number or room number" className="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500" />
)} {/* Invoice */} {booking && !showInvoice && ( <> {/* Booking Info */}

2. Booking information

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

3. Invoice details

{/* Room Fee */}

Room fee

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

Services used

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

Additional fees

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

Discount

setDiscount(parseFloat(e.target.value) || 0)} placeholder="Enter discount amount" className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500" />
{/* Summary */}
Subtotal: {formatCurrency(calculateSubtotal())}
{discount > 0 && (
Discount: -{formatCurrency(discount)}
)}
Total: {formatCurrency(calculateTotal())}
Deposit paid: -{formatCurrency(calculateDeposit())}
Remaining payment: {formatCurrency(calculateRemaining())}
{/* Payment Method */}

4. Payment method

{/* Action */}

Confirm check-out

Total payment: {formatCurrency(calculateRemaining())}

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

PAYMENT INVOICE

Check-out successful

Booking number:

{booking.booking_number}

Check-out date:

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

Customer:

{booking.user?.full_name}

Payment method:

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

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

No booking selected

Please enter booking number to start check-out process

)}
); }; export default CheckOutPage;