import { setRequestLocale, getTranslations } from 'next-intl/server'; import type { Prisma } from '@prisma/client'; import { prisma } from '@/lib/prisma'; import { requireAdmin } from '@/lib/requireAdmin'; import { Link } from '@/i18n/routing'; import { formatOre } from '@/lib/money'; import { StatusBadge } from '@/components/StatusBadge'; import { AdminFilters } from '@/components/admin/AdminFilters'; import { SortHeader } from '@/components/admin/SortHeader'; import { Pagination } from '@/components/admin/Pagination'; export const dynamic = 'force-dynamic'; const PAGE_SIZE = 25; const SORT_WHITELIST: Record = { createdAt: { createdAt: 'desc' }, bookingNumber: { bookingNumber: 'desc' }, orgName: { orgName: 'desc' }, contactName: { contactName: 'desc' }, totalOre: { totalOre: 'desc' }, status: { status: 'desc' }, pickupSlot: { pickupSlot: { startsAt: 'desc' } }, }; function buildOrderBy( sort: string, dir: 'asc' | 'desc', ): Prisma.BookingOrderByWithRelationInput { const base = SORT_WHITELIST[sort] ?? SORT_WHITELIST.createdAt; // Replace direction if ('pickupSlot' in base) { return { pickupSlot: { startsAt: dir } }; } const [key] = Object.keys(base); return { [key]: dir } as Prisma.BookingOrderByWithRelationInput; } export default async function AdminBookingsPage({ params, searchParams, }: { params: Promise<{ locale: string }>; searchParams: Promise<{ q?: string; status?: string; page?: string; sort?: string; dir?: string; }>; }) { const { locale } = await params; setRequestLocale(locale); await requireAdmin(); const t = await getTranslations('admin.bookings'); const sp = await searchParams; const loc = locale as 'sv' | 'en'; const page = Math.max(1, parseInt(sp.page ?? '1', 10) || 1); const sort = sp.sort && SORT_WHITELIST[sp.sort] ? sp.sort : 'createdAt'; const dir: 'asc' | 'desc' = sp.dir === 'asc' ? 'asc' : 'desc'; const where: Prisma.BookingWhereInput = { ...(sp.status && sp.status !== 'all' ? { status: sp.status } : {}), ...(sp.q ? { OR: [ { bookingNumber: { contains: sp.q } }, { orgName: { contains: sp.q } }, { email: { contains: sp.q } }, { contactName: { contains: sp.q } }, { orgNumber: { contains: sp.q } }, ], } : {}), }; const [total, bookings] = await Promise.all([ prisma.booking.count({ where }), prisma.booking.findMany({ where, orderBy: buildOrderBy(sort, dir), skip: (page - 1) * PAGE_SIZE, take: PAGE_SIZE, include: { pickupSlot: true }, }), ]); // Build export URL preserving current filters (skip page/sort). const exportParams = new URLSearchParams(); if (sp.status) exportParams.set('status', sp.status); if (sp.q) exportParams.set('q', sp.q); return (

{t('title')}

↓ {t('export')}
{bookings.length === 0 ? (
{t('empty')}
) : (
{bookings.map((b) => ( ))}
{b.bookingNumber} {b.createdAt.toLocaleDateString( loc === 'sv' ? 'sv-SE' : 'en-SE', )}
{b.orgName}
{b.orgNumber}
{b.contactName}
{b.email}
{b.pickupSlot ? ( <>
{loc === 'sv' ? b.pickupSlot.labelSv : b.pickupSlot.labelEn}
{b.pickupSlot.startsAt.toLocaleString( loc === 'sv' ? 'sv-SE' : 'en-SE', { dateStyle: 'short', timeStyle: 'short' }, )}
) : ( )}
{formatOre(b.totalOre, loc)} {t('view')} →
)}
{total > 0 && ( )}
); }