import { createFileRoute, useNavigate } from '@tanstack/react-router' import { m, mKey } from '@/i18n/messages' import { useLocale, getLocale } from '@/i18n/config' import { asI18n } from '@pikku/react' import { Badge, Box, Container, Grid, Group, Paper, SimpleGrid, Stack, Text, ThemeIcon, Title, } from '@pikku/mantine/core' import { CalendarDays, FileClock, FileText, Inbox, Wallet, type LucideIcon, } from 'lucide-react' import { usePikkuQuery } from '@project/functions-sdk/pikku/api.gen' import { RequireAuth } from '../auth' import { usePageTitle } from '../components/AppLayout' import { fmtDateShort } from '../lib/dates' // One row inside an action card: clickable, navigates to the relevant workspace. function Row({ primary, secondary, date, trailing, onClick, }: { primary: string secondary?: string | null date: string trailing?: React.ReactNode onClick: () => void }) { return ( {asI18n(`${primary}`)} {secondary && ( {asI18n(`${secondary}`)} )} {trailing} {asI18n(`${date}`)} ) } function ActionCard({ icon: Icon, label, count, children, }: { icon: LucideIcon label: string count: number children: React.ReactNode }) { const empty = count === 0 return ( {asI18n(`${label}`)} {count} {!empty && {children}} ) } function DashboardInner() { useLocale() const navigate = useNavigate() usePageTitle(m.common_pages_admin_dashboard_title()) const { data, isLoading, error } = usePikkuQuery('getAdminDashboard', { venueSlug: 'drawehn', }) if (isLoading && !data) { return ( {m.common_states_loading()} ) } if (error && !data) { return ( {m.common_states_error()} ) } if (!data) return null const goEnquiries = () => navigate({ to: '/admin/enquiries' }) const goBooking = (bookingId: string) => navigate({ to: '/admin/bookings/$bookingId', params: { bookingId } }) const tk = (k: string, opts?: Record) => mKey(`common.pages.admin.dashboard.${k}` as any, opts ?? {}) const more = (bucket: { count: number; items: unknown[] }) => bucket.count > bucket.items.length ? ( {tk('more', { count: bucket.count - bucket.items.length })} ) : null const noAction = data.pendingEnquiries.count === 0 && data.contractsToSend.count === 0 && data.contractsAwaitingResponse.count === 0 && data.depositsOutstanding.count === 0 // A "needs action" hub should only surface what needs action — zero-count // cards are noise, so we drop them and let `noAction` cover the empty state. const bookingCard = ( key: string, icon: LucideIcon, label: string, bucket: typeof data.contractsToSend, ) => bucket.count === 0 ? null : ( {bucket.items.map((b) => ( goBooking(b.bookingId)} /> ))} {more(bucket)} ) const actionCards = [ data.pendingEnquiries.count === 0 ? null : ( {data.pendingEnquiries.items.map((e) => ( {tk('waitlisted')} ) : null } onClick={goEnquiries} /> ))} {more(data.pendingEnquiries)} ), bookingCard('send', FileText, tk('cards.contractsToSend'), data.contractsToSend), bookingCard( 'await', FileClock, tk('cards.contractsAwaiting'), data.contractsAwaitingResponse, ), bookingCard('dep', Wallet, tk('cards.depositsOutstanding'), data.depositsOutstanding), ].filter(Boolean) return ( {/* Needs action */} {tk('needsAction')} {noAction ? ( {tk('allClear')} ) : ( {actionCards} )} {/* Upcoming events */} {tk('upcoming')} {tk('upcomingWindow')} {data.upcomingEvents.count === 0 ? ( {tk('noUpcoming')} ) : ( {data.upcomingEvents.items.map((b) => ( goBooking(b.bookingId)} style={{ cursor: 'pointer', borderRadius: 8 }} className="dash-row" > {asI18n(`${b.eventName}`)} {asI18n( `${b.clientName}${b.expectedPersons != null ? ` · ${tk('persons', { count: b.expectedPersons })}` : ''}`, )} {asI18n(`${fmtDateShort(b.startDate, getLocale())}`)} ))} )} ) } export const Route = createFileRoute('/admin/')({ component: () => ( ), })