'use client'; import { useQuery } from '@tanstack/react-query'; import { PermissionGuard } from '@/components/layout/PermissionGuard'; import { PERMS } from '@/lib/permissions'; import { Ticket, AlertCircle, BookOpen, Banknote, ArrowRight, ScanLine } from 'lucide-react'; import { dashboardApi } from '@/lib/api/dashboard'; import { apiClient } from '@/lib/api-client'; import { formatCurrency } from '@/lib/utils'; import { PieChart, Pie, Cell, Tooltip, ResponsiveContainer } from 'recharts'; import Link from 'next/link'; const COLORS = ['#2563eb', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6']; function StatCard({ icon, iconBg, label, total, loading, rows, href, }: { icon: React.ReactNode; iconBg: string; label: string; total: number; loading: boolean; rows: { label: string; value: number; icon?: React.ReactNode; href: string }[]; href: string; }) { return (
{icon}
{label}

{loading ? '—' : total.toLocaleString()}

{rows.map((r) => (
{r.icon}{r.label} {loading ? '—' : r.value.toLocaleString()}
))}
View all
); } function RevenueSection({ label, bookingCount, rows, subtotal, loading, renderRow, }: { label: React.ReactNode; bookingCount: number; rows: { currency: string; totalMinor: number }[]; subtotal: number; loading: boolean; renderRow: (r: { currency: string; totalMinor: number }) => React.ReactNode; }) { return (
{label} {loading ? '—' : bookingCount.toLocaleString()} bookings
{rows.length === 0 ?

No revenue yet

: rows.map(renderRow)} {rows.length > 0 && (
Subtotal {formatCurrency(subtotal, 'ETB')}
)}
); } function DashboardPageContent() { const { data: exchangeRates = [] } = useQuery({ queryKey: ['currencies'], queryFn: () => apiClient.get('/currencies'), select: (d: any) => (Array.isArray(d) ? d : d?.data ?? d?.items ?? []), }); const toEtbRate = (currency: string): number | null => { if (currency === 'ETB') return 1; const r = exchangeRates.find((x: any) => x.fromCurrency === 'ETB' && x.toCurrency === currency); return r ? 1 / r.rate : null; }; const { data: stats, isLoading: statsLoading, error: statsError } = useQuery({ queryKey: ['backoffice-stats'], queryFn: dashboardApi.getBackofficeStats, retry: 1, staleTime: 60000, }); const { data: paymentMethods } = useQuery({ queryKey: ['payment-methods'], queryFn: dashboardApi.getPaymentMethods, retry: 1, }); const calcGrand = (rows: { currency: string; totalMinor: number }[]) => rows.reduce((sum, { currency, totalMinor }) => { const rate = toEtbRate(currency); return rate !== null ? sum + Math.round(totalMinor * rate) : sum; }, 0); const normalRows = stats?.revenueByCurrency ?? []; const packageRows = stats?.packageRevenueByCurrency ?? []; const normalGrand = calcGrand(normalRows); const packageGrand = calcGrand(packageRows); const overallGrand = normalGrand + packageGrand; const renderRevenueRow = ({ currency, totalMinor }: { currency: string; totalMinor: number }) => { const rate = toEtbRate(currency); const etbMinor = rate !== null ? Math.round(totalMinor * rate) : null; return (
{currency}
{formatCurrency(totalMinor, currency)} {currency !== 'ETB' && etbMinor !== null && ( ({formatCurrency(etbMinor, 'ETB')}) )}
); }; return (

Dashboard

Welcome back! Here's your operational summary.

Boarding
{statsError && (

Some data may be outdated

Unable to fetch live data. Showing cached or sample information.

)} {/* Stat cards */}
} iconBg="bg-blue-100 dark:bg-blue-900/30" label="Bookings" total={stats?.totalBookings ?? 0} loading={statsLoading} href="/bookings" rows={[ { label: 'Regular', value: stats?.totalNormalBookings ?? 0, href: '/bookings' }, { label: 'Package', value: stats?.totalPackageBookings ?? 0, href: '/package-bookings' }, ]} /> } iconBg="bg-emerald-100 dark:bg-emerald-900/30" label="Tickets" total={stats?.totalTickets ?? 0} loading={statsLoading} href="/tickets" rows={[ { label: 'Regular', value: stats?.totalNormalTickets ?? 0, href: '/tickets' }, { label: 'Package', value: stats?.totalPackageTickets ?? 0, href: '/tickets' }, ]} /> {/* Revenue card */}
Revenue
{statsLoading ? (

Loading…

) : ( <>

{formatCurrency(overallGrand, 'ETB')}

Regular

{formatCurrency(normalGrand, 'ETB')}

Package

{formatCurrency(packageGrand, 'ETB')}

View payments )}
{/* Revenue breakdown */}

Revenue Breakdown

{statsLoading ? (

Loading…

) : !normalRows.length && !packageRows.length ? (

No revenue data yet.

) : (
)}
{/* Payment Methods Distribution */} {paymentMethods && paymentMethods.length > 0 && (

Payment Methods Distribution

{paymentMethods.map((_: any, index: number) => ( ))}
)}
); } export default function DashboardPage() { return ( ); }