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: () => (
),
})