130 lines
4.7 KiB
TypeScript
130 lines
4.7 KiB
TypeScript
import { setRequestLocale, getTranslations } from 'next-intl/server';
|
||
import { notFound } from 'next/navigation';
|
||
import { prisma } from '@/lib/prisma';
|
||
import { Header } from '@/components/Header';
|
||
import { formatOre } from '@/lib/money';
|
||
import { Link } from '@/i18n/routing';
|
||
|
||
export default async function BookingConfirmedPage({
|
||
params,
|
||
}: {
|
||
params: Promise<{ locale: string; number: string }>;
|
||
}) {
|
||
const { locale, number } = await params;
|
||
setRequestLocale(locale);
|
||
const t = await getTranslations();
|
||
|
||
const booking = await prisma.booking.findUnique({
|
||
where: { bookingNumber: number },
|
||
include: { items: true, pickupSlot: true },
|
||
});
|
||
if (!booking) notFound();
|
||
|
||
const loc = locale as 'sv' | 'en';
|
||
|
||
return (
|
||
<div className="min-h-screen bg-ink-50">
|
||
<Header />
|
||
<main className="mx-auto max-w-3xl px-4 py-8 sm:py-12">
|
||
<div className="card overflow-hidden">
|
||
<div className="bg-brand-600 px-6 py-5 text-white">
|
||
<div className="flex items-center gap-2 text-sm">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
viewBox="0 0 24 24"
|
||
fill="currentColor"
|
||
className="h-5 w-5"
|
||
aria-hidden="true"
|
||
>
|
||
<path
|
||
fillRule="evenodd"
|
||
d="M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20zm4.7-12.3a1 1 0 0 0-1.4-1.4L11 12.6 8.7 10.3a1 1 0 1 0-1.4 1.4l3 3a1 1 0 0 0 1.4 0l5-5z"
|
||
clipRule="evenodd"
|
||
/>
|
||
</svg>
|
||
<span>{t('booking.success.title')}</span>
|
||
</div>
|
||
<div className="mt-3 text-2xl font-semibold">
|
||
{t('booking.success.bookingNumber', { number: booking.bookingNumber })}
|
||
</div>
|
||
<div className="mt-1 text-sm text-white/80">
|
||
{t('booking.success.subtitle', { email: booking.email })}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-6 p-6">
|
||
<div>
|
||
<h2 className="text-xs font-medium uppercase tracking-wide text-ink-500">
|
||
{t('email.orderSummary')}
|
||
</h2>
|
||
<table className="mt-2 w-full text-sm">
|
||
<tbody className="divide-y divide-ink-100">
|
||
{booking.items.map((it) => (
|
||
<tr key={it.id}>
|
||
<td className="py-2">
|
||
{loc === 'sv' ? it.nameSv : it.nameEn}
|
||
<span className="ml-2 text-ink-400">×{it.quantity}</span>
|
||
</td>
|
||
<td className="py-2 text-right tabular-nums">
|
||
{formatOre(it.lineTotalOre, loc)}
|
||
</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
<tfoot>
|
||
<tr className="border-t border-ink-200">
|
||
<td className="pt-2 text-ink-600">
|
||
{t('common.subtotal')}
|
||
</td>
|
||
<td className="pt-2 text-right tabular-nums">
|
||
{formatOre(booking.subtotalOre, loc)}
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td className="text-ink-500">{t('common.ofWhichVat')}</td>
|
||
<td className="text-right tabular-nums text-ink-500">
|
||
{formatOre(booking.vatOre, loc)}
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td className="pt-1 font-semibold">{t('common.total')}</td>
|
||
<td className="pt-1 text-right font-semibold tabular-nums">
|
||
{formatOre(booking.totalOre, loc)}
|
||
</td>
|
||
</tr>
|
||
</tfoot>
|
||
</table>
|
||
</div>
|
||
|
||
{booking.pickupSlot && (
|
||
<div>
|
||
<h2 className="text-xs font-medium uppercase tracking-wide text-ink-500">
|
||
{t('email.pickup')}
|
||
</h2>
|
||
<p className="mt-1 text-sm">
|
||
{loc === 'sv'
|
||
? booking.pickupSlot.labelSv
|
||
: booking.pickupSlot.labelEn}{' '}
|
||
·{' '}
|
||
{booking.pickupSlot.startsAt.toLocaleString(
|
||
loc === 'sv' ? 'sv-SE' : 'en-SE',
|
||
{ dateStyle: 'medium', timeStyle: 'short' },
|
||
)}
|
||
</p>
|
||
</div>
|
||
)}
|
||
|
||
<div className="rounded-lg bg-ink-50 p-4 text-sm text-ink-600">
|
||
{t('email.invoiceInfo')}
|
||
</div>
|
||
|
||
<Link href="/" className="btn-secondary w-full justify-center">
|
||
{t('booking.success.newOrder')}
|
||
</Link>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
);
|
||
}
|