import { setRequestLocale, getTranslations } from 'next-intl/server'; import { revalidatePath } from 'next/cache'; 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'; export const dynamic = 'force-dynamic'; const STATUSES = ['REQUESTED', 'SCHEDULED', 'DELIVERED', 'CANCELLED'] as const; type ReplacementStatus = (typeof STATUSES)[number]; async function setReplacementStatus(formData: FormData) { 'use server'; await requireAdmin(); const id = String(formData.get('id') ?? ''); const status = String(formData.get('status') ?? ''); if (!id || !STATUSES.includes(status as ReplacementStatus)) return; await prisma.replacementRequest.update({ where: { id }, data: { status }, }); revalidatePath('/admin/replacements'); // Find bookingId to refresh detail view too. const rr = await prisma.replacementRequest.findUnique({ where: { id }, select: { bookingId: true }, }); if (rr) revalidatePath(`/admin/bookings/${rr.bookingId}`); } export default async function AdminReplacementsPage({ params, searchParams, }: { params: Promise<{ locale: string }>; searchParams: Promise<{ status?: string }>; }) { const { locale } = await params; setRequestLocale(locale); await requireAdmin(); const sp = await searchParams; const loc = locale as 'sv' | 'en'; const t = await getTranslations('adminReplacements'); const where: Prisma.ReplacementRequestWhereInput = sp.status && STATUSES.includes(sp.status as ReplacementStatus) ? { status: sp.status } : {}; const rows = await prisma.replacementRequest.findMany({ where, orderBy: { createdAt: 'desc' }, include: { booking: true, bookingItem: true, pickupSlot: true }, take: 200, }); return (

{t('title')}

{rows.length === 0 ? (
{t('empty')}
) : (
{rows.map((r) => ( ))}
{t('columns.created')} {t('columns.booking')} {t('columns.org')} {t('columns.item')} {t('columns.quantity')} {t('columns.total')} {t('columns.pickup')} {t('columns.status')}
{r.createdAt.toLocaleString( loc === 'sv' ? 'sv-SE' : 'en-SE', { dateStyle: 'short', timeStyle: 'short' }, )} {r.booking.bookingNumber} {r.booking.orgName} {loc === 'sv' ? r.nameSv : r.nameEn} {r.quantity} {formatOre(r.lineTotalOre, loc)} {r.pickupSlot ? ( <>
{loc === 'sv' ? r.pickupSlot.labelSv : r.pickupSlot.labelEn}
{r.pickupSlot.startsAt.toLocaleString( loc === 'sv' ? 'sv-SE' : 'en-SE', { dateStyle: 'short', timeStyle: 'short' }, )}
) : ( )}
{t(`status.${r.status as ReplacementStatus}`)}
{r.status === 'REQUESTED' && ( )} {(r.status === 'REQUESTED' || r.status === 'SCHEDULED') && ( )} {r.status !== 'CANCELLED' && r.status !== 'DELIVERED' && ( )}
)}
); } function StatusAction({ id, target, label, action, destructive, }: { id: string; target: ReplacementStatus; label: string; action: (fd: FormData) => Promise; destructive?: boolean; }) { return (
); }