initial booking

This commit is contained in:
Ola Malmgren
2026-05-22 10:50:48 +02:00
commit 4d705a1005
77 changed files with 13827 additions and 0 deletions

View File

@@ -0,0 +1,129 @@
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>
);
}