142 lines
5.6 KiB
TypeScript
142 lines
5.6 KiB
TypeScript
import Link from 'next/link'
|
|
import { DEFAULT_COMPANY_SLUG, siteFetchOrDefault } from '@/lib/api'
|
|
import { getPublicSiteDictionary } from '@/lib/i18n'
|
|
import { getPublicSiteLanguage } from '@/lib/i18n.server'
|
|
import type { PublicSitePageSections } from '@rentaldrivego/types'
|
|
import { resolvePageSectionsConfig } from '@/lib/pageSections'
|
|
|
|
interface ReservationDetail {
|
|
id: string
|
|
status: string
|
|
startDate: string
|
|
endDate: string
|
|
totalDays: number
|
|
totalAmount: number
|
|
vehicleCategory: string | null
|
|
vehicle: { make: string; model: string; year: number } | null
|
|
customer: { firstName: string; lastName: string; email: string } | null
|
|
}
|
|
|
|
interface PageProps {
|
|
searchParams?: { id?: string }
|
|
}
|
|
|
|
interface BrandResponse {
|
|
brand: {
|
|
menuConfig?: {
|
|
pageSections?: Partial<PublicSitePageSections> | null
|
|
} | null
|
|
} | null
|
|
}
|
|
|
|
export const dynamic = 'force-dynamic'
|
|
|
|
export default async function BookingConfirmationPage({ searchParams = {} }: PageProps) {
|
|
const reservationId = searchParams.id
|
|
const slug = DEFAULT_COMPANY_SLUG
|
|
const dict = getPublicSiteDictionary(getPublicSiteLanguage())
|
|
const brand = await siteFetchOrDefault<BrandResponse | null>(`/site/${slug}/brand`, null)
|
|
const pageSections = resolvePageSectionsConfig(brand?.brand?.menuConfig)
|
|
|
|
const reservation = reservationId
|
|
? await siteFetchOrDefault<ReservationDetail | null>(
|
|
`/site/${slug}/booking/${reservationId}`,
|
|
null,
|
|
)
|
|
: null
|
|
|
|
return (
|
|
<main className="min-h-screen bg-slate-50 py-16">
|
|
<div className="shell max-w-2xl">
|
|
<div className="card p-10 text-center">
|
|
{pageSections.bookingConfirmation.hero ? (
|
|
<>
|
|
<div className="mx-auto mb-5 flex h-16 w-16 items-center justify-center rounded-full bg-emerald-100">
|
|
<svg className="h-8 w-8 text-emerald-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
|
|
</svg>
|
|
</div>
|
|
|
|
<p className="text-sm font-semibold uppercase tracking-[0.18em] text-emerald-700">{dict.confirmation.eyebrow}</p>
|
|
<h1 className="mt-3 text-4xl font-black text-slate-900">{dict.confirmation.title}</h1>
|
|
<p className="mt-4 text-slate-600">
|
|
{dict.confirmation.description}
|
|
</p>
|
|
</>
|
|
) : null}
|
|
|
|
{pageSections.bookingConfirmation.summary && reservation && (
|
|
<div className="mt-8 rounded-xl border border-slate-200 text-left divide-y divide-slate-100 overflow-hidden text-sm">
|
|
<div className="bg-slate-50 px-4 py-2.5 text-xs font-semibold uppercase tracking-[0.12em] text-slate-500">
|
|
{dict.confirmation.bookingSummary}
|
|
</div>
|
|
{reservation.vehicleCategory && (
|
|
<Row label={dict.confirmation.vehicleCategory} value={dict.vehicles.categoryLabels[reservation.vehicleCategory as keyof typeof dict.vehicles.categoryLabels] ?? reservation.vehicleCategory} />
|
|
)}
|
|
{reservation.customer && (
|
|
<Row label={dict.confirmation.customer} value={`${reservation.customer.firstName} ${reservation.customer.lastName}`} />
|
|
)}
|
|
{reservation.customer?.email && (
|
|
<Row label={dict.confirmation.email} value={reservation.customer.email} />
|
|
)}
|
|
<Row
|
|
label={dict.confirmation.dates}
|
|
value={`${new Date(reservation.startDate).toLocaleDateString()} -> ${new Date(reservation.endDate).toLocaleDateString()} (${reservation.totalDays})`}
|
|
/>
|
|
<Row
|
|
label={dict.confirmation.total}
|
|
value={`${reservation.totalAmount.toLocaleString('fr-MA', { style: 'currency', currency: 'MAD', maximumFractionDigits: 0 })}`}
|
|
valueClass="font-bold text-slate-900"
|
|
/>
|
|
<Row label={dict.confirmation.reference} value={reservation.id} valueClass="font-mono text-xs text-slate-500 break-all" />
|
|
<Row
|
|
label={dict.confirmation.status}
|
|
value={reservation.status}
|
|
valueClass="rounded-full bg-amber-100 text-amber-800 px-2 py-0.5 text-xs font-semibold uppercase"
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{pageSections.bookingConfirmation.summary && !reservation && reservationId && (
|
|
<p className="mt-6 text-sm text-slate-500">
|
|
{dict.confirmation.reference}: <span className="font-mono">{reservationId}</span>
|
|
</p>
|
|
)}
|
|
|
|
{pageSections.bookingConfirmation.actions ? <div className="mt-8 flex flex-wrap justify-center gap-3">
|
|
<Link
|
|
href="/vehicles"
|
|
className="rounded-full bg-slate-900 px-6 py-3 text-sm font-semibold text-white"
|
|
>
|
|
{dict.confirmation.browseMoreVehicles}
|
|
</Link>
|
|
<Link
|
|
href="/"
|
|
className="rounded-full border border-slate-300 px-6 py-3 text-sm font-semibold text-slate-700"
|
|
>
|
|
{dict.confirmation.backHome}
|
|
</Link>
|
|
</div> : null}
|
|
</div>
|
|
</div>
|
|
</main>
|
|
)
|
|
}
|
|
|
|
function Row({
|
|
label,
|
|
value,
|
|
valueClass = 'text-slate-700 font-medium',
|
|
}: {
|
|
label: string
|
|
value: string
|
|
valueClass?: string
|
|
}) {
|
|
return (
|
|
<div className="flex justify-between gap-4 px-4 py-3">
|
|
<span className="flex-shrink-0 text-slate-500">{label}</span>
|
|
<span className={`text-right ${valueClass}`}>{value}</span>
|
|
</div>
|
|
)
|
|
}
|