import React, { useEffect, useState } from 'react'; import { BarChart3, Users, Hotel, Calendar, TrendingUp, RefreshCw, CreditCard, LogOut } from 'lucide-react'; import { reportService, ReportData, paymentService } from '../../services/api'; import type { Payment } from '../../services/api/paymentService'; import { toast } from 'react-toastify'; import { Loading, EmptyState } from '../../components/common'; import CurrencyIcon from '../../components/common/CurrencyIcon'; import { formatDate } from '../../utils/format'; import { useFormatCurrency } from '../../hooks/useFormatCurrency'; import { useAsync } from '../../hooks/useAsync'; import { useNavigate } from 'react-router-dom'; import useAuthStore from '../../store/useAuthStore'; const DashboardPage: React.FC = () => { const { formatCurrency } = useFormatCurrency(); const navigate = useNavigate(); const { logout } = useAuthStore(); const [dateRange, setDateRange] = useState({ from: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0], to: new Date().toISOString().split('T')[0], }); const [recentPayments, setRecentPayments] = useState([]); const [loadingPayments, setLoadingPayments] = useState(false); const handleLogout = async () => { try { await logout(); navigate('/'); } catch (error) { console.error('Logout error:', error); } }; const fetchDashboardData = async () => { const response = await reportService.getReports({ from: dateRange.from, to: dateRange.to, }); return response.data; }; const { data: stats, loading, error, execute } = useAsync( fetchDashboardData, { immediate: true, onError: (error: any) => { toast.error(error.message || 'Unable to load dashboard data'); } } ); useEffect(() => { execute(); }, [dateRange]); useEffect(() => { const fetchPayments = async () => { try { setLoadingPayments(true); const response = await paymentService.getPayments({ page: 1, limit: 5 }); if (response.success && response.data?.payments) { setRecentPayments(response.data.payments); } } catch (err: any) { console.error('Error fetching payments:', err); } finally { setLoadingPayments(false); } }; fetchPayments(); }, []); const handleRefresh = () => { execute(); }; const getPaymentStatusColor = (status: string) => { switch (status) { case 'completed': return 'bg-gradient-to-r from-emerald-50 to-green-50 text-emerald-800 border-emerald-200'; case 'pending': return 'bg-gradient-to-r from-amber-50 to-yellow-50 text-amber-800 border-amber-200'; case 'failed': return 'bg-gradient-to-r from-rose-50 to-red-50 text-rose-800 border-rose-200'; case 'refunded': return 'bg-gradient-to-r from-slate-50 to-gray-50 text-slate-700 border-slate-200'; default: return 'bg-gradient-to-r from-slate-50 to-gray-50 text-slate-700 border-slate-200'; } }; const getPaymentMethodLabel = (method: string) => { switch (method) { case 'stripe': case 'credit_card': return 'Card'; case 'paypal': return 'PayPal'; case 'bank_transfer': return 'Bank Transfer'; case 'cash': return 'Cash'; default: return method; } }; if (loading) { return ; } if (error || !stats) { return (
); } return (
{}

Dashboard

Hotel operations overview and analytics

{}
setDateRange({ ...dateRange, from: e.target.value })} className="flex-1 sm:flex-none px-3 sm:px-4 py-2 sm:py-2.5 bg-white border-2 border-slate-200 rounded-xl focus:border-amber-400 focus:ring-4 focus:ring-amber-100 transition-all duration-200 text-slate-700 font-medium shadow-sm hover:shadow-md text-sm sm:text-base" /> to setDateRange({ ...dateRange, to: e.target.value })} className="flex-1 sm:flex-none px-3 sm:px-4 py-2 sm:py-2.5 bg-white border-2 border-slate-200 rounded-xl focus:border-amber-400 focus:ring-4 focus:ring-amber-100 transition-all duration-200 text-slate-700 font-medium shadow-sm hover:shadow-md text-sm sm:text-base" />
{}
{}

Total Revenue

{formatCurrency(stats?.total_revenue || 0)}

Active All time revenue
{}

Total Bookings

{stats?.total_bookings || 0}

{stats.total_bookings > 0 ? 'Total bookings recorded' : 'No bookings yet'}
{}

Available Rooms

{stats?.available_rooms || 0}

{stats?.occupied_rooms || 0} rooms in use
{}

Customers

{stats?.total_customers || 0}

Unique customers with bookings
{}
{}

Daily Revenue

{stats?.revenue_by_date && stats.revenue_by_date.length > 0 ? (
{stats.revenue_by_date.slice(0, 7).map((item, index) => { const maxRevenue = Math.max(...stats.revenue_by_date!.map(r => r.revenue)); return (
{formatDate(item.date, 'short')}
{formatCurrency(item.revenue)}
); })}
) : ( )}
{}

Booking Status

{stats?.bookings_by_status && Object.keys(stats.bookings_by_status).length > 0 ? (
{Object.entries(stats.bookings_by_status) .filter(([_, count]) => count > 0) .map(([status, count]) => { const statusColors: Record = { pending: 'bg-amber-500', confirmed: 'bg-blue-500', checked_in: 'bg-emerald-500', checked_out: 'bg-slate-500', cancelled: 'bg-rose-500', }; const statusLabels: Record = { pending: 'Pending confirmation', confirmed: 'Confirmed', checked_in: 'Checked in', checked_out: 'Checked out', cancelled: '❌ Canceled', }; return (
{statusLabels[status] || status}
{count}
); })}
) : ( )}
{}
{}

Top Booked Rooms

{stats?.top_rooms && stats.top_rooms.length > 0 ? (
{stats.top_rooms.map((room, index) => (
{index + 1}

Room {room.room_number}

{room.bookings} booking{room.bookings !== 1 ? 's' : ''}

{formatCurrency(room.revenue)}
))}
) : ( )}
{}

Services Used

{stats?.service_usage && stats.service_usage.length > 0 ? (
{stats.service_usage.map((service) => (

{service.service_name}

{service.usage_count} time{service.usage_count !== 1 ? 's' : ''} used

{formatCurrency(service.total_revenue)}
))}
) : ( )}
{}

Recent Payments

{loadingPayments ? (
) : recentPayments && recentPayments.length > 0 ? (
{recentPayments.map((payment) => (
navigate(`/admin/payments`)} >

{formatCurrency(payment.amount)}

{getPaymentMethodLabel(payment.payment_method)}

{payment.payment_date && ( • {formatDate(payment.payment_date, 'short')} )}
{payment.payment_status.charAt(0).toUpperCase() + payment.payment_status.slice(1)}
))}
) : ( navigate('/admin/payments') }} /> )}
); }; export default DashboardPage;