feat(SW-659): Receive query params from Planet callbacks * feat(SW-659): read confirmation number from url and update callback url if dev * fix(SW-659): moved callback url into env variable Approved-by: Simon.Emanuelsson
282 lines
9.4 KiB
TypeScript
282 lines
9.4 KiB
TypeScript
import { dt } from "@/lib/dt"
|
|
import { serverClient } from "@/lib/trpc/server"
|
|
|
|
import {
|
|
CalendarIcon,
|
|
DownloadIcon,
|
|
ImageIcon,
|
|
PrinterIcon,
|
|
} from "@/components/Icons"
|
|
import Button from "@/components/TempDesignSystem/Button"
|
|
import Divider from "@/components/TempDesignSystem/Divider"
|
|
import Link from "@/components/TempDesignSystem/Link"
|
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
|
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
|
import Title from "@/components/TempDesignSystem/Text/Title"
|
|
import { getIntl } from "@/i18n"
|
|
|
|
import styles from "./page.module.css"
|
|
|
|
import type { LangParams, PageArgs } from "@/types/params"
|
|
|
|
export default async function BookingConfirmationPage({
|
|
params,
|
|
searchParams,
|
|
}: PageArgs<LangParams, { confirmationNumber: string }>) {
|
|
const confirmationNumber = searchParams.confirmationNumber
|
|
const booking = await serverClient().booking.confirmation({
|
|
confirmationNumber,
|
|
})
|
|
|
|
if (!booking) {
|
|
return null
|
|
}
|
|
|
|
const intl = await getIntl()
|
|
const text = intl.formatMessage<React.ReactNode>(
|
|
{ id: "booking.confirmation.text" },
|
|
{
|
|
emailLink: (str) => (
|
|
<Link color="burgundy" href="#" textDecoration="underline">
|
|
{str}
|
|
</Link>
|
|
),
|
|
}
|
|
)
|
|
|
|
const fromDate = dt(booking.temp.fromDate).locale(params.lang)
|
|
const toDate = dt(booking.temp.toDate).locale(params.lang)
|
|
const nights = intl.formatMessage(
|
|
{ id: "booking.nights" },
|
|
{
|
|
totalNights: dt(toDate.format("YYYY-MM-DD")).diff(
|
|
dt(fromDate.format("YYYY-MM-DD")),
|
|
"days"
|
|
),
|
|
}
|
|
)
|
|
|
|
return (
|
|
<main className={styles.main}>
|
|
<header className={styles.header}>
|
|
<hgroup className={styles.hgroup}>
|
|
<Title
|
|
as="h4"
|
|
color="red"
|
|
textAlign="center"
|
|
textTransform="regular"
|
|
type="h2"
|
|
>
|
|
{intl.formatMessage({ id: "booking.confirmation.title" })}
|
|
</Title>
|
|
<Title
|
|
as="h4"
|
|
color="burgundy"
|
|
textAlign="center"
|
|
textTransform="regular"
|
|
type="h1"
|
|
>
|
|
{booking.hotel.name}
|
|
</Title>
|
|
</hgroup>
|
|
<Body className={styles.body} textAlign="center">
|
|
{text}
|
|
</Body>
|
|
</header>
|
|
<section className={styles.section}>
|
|
<div className={styles.booking}>
|
|
<article className={styles.details}>
|
|
<header>
|
|
<Subtitle color="burgundy" type="two">
|
|
{intl.formatMessage(
|
|
{ id: "Reference #{bookingNr}" },
|
|
{ bookingNr: "A92320VV" }
|
|
)}
|
|
</Subtitle>
|
|
</header>
|
|
<ul className={styles.list}>
|
|
<li className={styles.listItem}>
|
|
<Body>{intl.formatMessage({ id: "Check-in" })}</Body>
|
|
<Body>
|
|
{`${fromDate.format("ddd, D MMM")} ${intl.formatMessage({ id: "from" })} ${fromDate.format("HH:mm")}`}
|
|
</Body>
|
|
</li>
|
|
<li className={styles.listItem}>
|
|
<Body>{intl.formatMessage({ id: "Check-out" })}</Body>
|
|
<Body>
|
|
{`${toDate.format("ddd, D MMM")} ${intl.formatMessage({ id: "from" })} ${toDate.format("HH:mm")}`}
|
|
</Body>
|
|
</li>
|
|
<li className={styles.listItem}>
|
|
<Body>{intl.formatMessage({ id: "Breakfast" })}</Body>
|
|
<Body>
|
|
{booking.temp.breakfastFrom} - {booking.temp.breakfastTo}
|
|
</Body>
|
|
</li>
|
|
<li className={styles.listItem}>
|
|
<Body>{intl.formatMessage({ id: "Cancellation policy" })}</Body>
|
|
<Body>
|
|
{intl.formatMessage({ id: booking.temp.cancelPolicy })}
|
|
</Body>
|
|
</li>
|
|
<li className={styles.listItem}>
|
|
<Body>{intl.formatMessage({ id: "Rebooking" })}</Body>
|
|
<Body>{`${intl.formatMessage({ id: "Free until" })} ${fromDate.subtract(3, "day").format("ddd, D MMM")}`}</Body>
|
|
</li>
|
|
</ul>
|
|
</article>
|
|
<aside className={styles.tempImage}>
|
|
<ImageIcon height={80} width={80} />
|
|
</aside>
|
|
<div className={styles.actions}>
|
|
<Button
|
|
intent="text"
|
|
size="small"
|
|
theme="base"
|
|
variant="icon"
|
|
wrapping
|
|
>
|
|
<CalendarIcon />
|
|
{intl.formatMessage({ id: "Add to calendar" })}
|
|
</Button>
|
|
<Button
|
|
intent="text"
|
|
size="small"
|
|
theme="base"
|
|
variant="icon"
|
|
wrapping
|
|
>
|
|
<PrinterIcon />
|
|
{intl.formatMessage({ id: "Print confirmation" })}
|
|
</Button>
|
|
<Button
|
|
intent="text"
|
|
size="small"
|
|
theme="base"
|
|
variant="icon"
|
|
wrapping
|
|
>
|
|
<DownloadIcon />
|
|
{intl.formatMessage({ id: "Download invoice" })}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<div className={styles.summary}>
|
|
<div className={styles.guest}>
|
|
<Caption color="uiTextPlaceholder">
|
|
{intl.formatMessage({ id: "Guest" })}
|
|
</Caption>
|
|
<div>
|
|
<Body color="burgundy" textTransform="bold">
|
|
{`${booking.guest.firstName} ${booking.guest.lastName}${booking.guest.memberbershipNumber ? ` (${intl.formatMessage({ id: "member no" })} ${booking.guest.memberbershipNumber})` : ""}`}
|
|
</Body>
|
|
<Body color="uiTextHighContrast">{booking.guest.email}</Body>
|
|
<Body color="uiTextHighContrast">
|
|
{booking.guest.phoneNumber}
|
|
</Body>
|
|
</div>
|
|
</div>
|
|
<div className={styles.hotel}>
|
|
<Caption color="uiTextPlaceholder">
|
|
{intl.formatMessage({ id: "Your hotel" })}
|
|
</Caption>
|
|
<div>
|
|
<Body color="burgundy" textTransform="bold">
|
|
{booking.hotel.name}
|
|
</Body>
|
|
<Body color="uiTextHighContrast">{booking.hotel.email}</Body>
|
|
<Body color="uiTextHighContrast">
|
|
{booking.hotel.phoneNumber}
|
|
</Body>
|
|
</div>
|
|
</div>
|
|
<Divider className={styles.divider} color="primaryLightSubtle" />
|
|
<div className={styles.receipt}>
|
|
<div>
|
|
<Body color="burgundy" textTransform="bold">
|
|
{`${booking.temp.room.type}, ${nights}`}
|
|
</Body>
|
|
<Body color="uiTextHighContrast">{booking.temp.room.price}</Body>
|
|
</div>
|
|
{booking.temp.packages.map((pkg) => (
|
|
<div key={pkg.name}>
|
|
<Body color="burgundy" textTransform="bold">
|
|
{pkg.name}
|
|
</Body>
|
|
<Body color="uiTextHighContrast">{pkg.price}</Body>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className={styles.total}>
|
|
<div>
|
|
<Body color="burgundy" textTransform="bold">
|
|
{intl.formatMessage({ id: "VAT" })}
|
|
</Body>
|
|
<Body color="uiTextHighContrast">{booking.temp.room.vat}</Body>
|
|
</div>
|
|
<div>
|
|
<Body color="burgundy" textTransform="bold">
|
|
{intl.formatMessage({ id: "Total cost" })}
|
|
</Body>
|
|
<Body color="uiTextHighContrast">{booking.temp.total}</Body>
|
|
<Caption color="uiTextPlaceholder">
|
|
{`${intl.formatMessage({ id: "Approx." })} ${booking.temp.totalInEuro}`}
|
|
</Caption>
|
|
</div>
|
|
</div>
|
|
<Divider className={styles.divider} color="primaryLightSubtle" />
|
|
<div>
|
|
<Body color="burgundy" textTransform="bold">
|
|
{`${intl.formatMessage({ id: "Payment received" })} ${dt(booking.temp.payment).locale(params.lang).format("D MMM YYYY, h:mm z")}`}
|
|
</Body>
|
|
<Caption color="uiTextPlaceholder">
|
|
{intl.formatMessage(
|
|
{ id: "{card} ending with {cardno}" },
|
|
{
|
|
card: "Mastercard",
|
|
cardno: "2202",
|
|
}
|
|
)}
|
|
</Caption>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
)
|
|
}
|
|
|
|
// const { email, hotel, stay, summary } = tempConfirmationData
|
|
|
|
// const confirmationNumber = useMemo(() => {
|
|
// if (typeof window === "undefined") return ""
|
|
|
|
// const storedConfirmationNumber = sessionStorage.getItem(
|
|
// BOOKING_CONFIRMATION_NUMBER
|
|
// )
|
|
// TODO: cleanup stored values
|
|
// sessionStorage.removeItem(BOOKING_CONFIRMATION_NUMBER)
|
|
// return storedConfirmationNumber
|
|
// }, [])
|
|
|
|
// const bookingStatus = useHandleBookingStatus(
|
|
// confirmationNumber,
|
|
// BookingStatusEnum.BookingCompleted,
|
|
// maxRetries,
|
|
// retryInterval
|
|
// )
|
|
|
|
// if (
|
|
// confirmationNumber === null ||
|
|
// bookingStatus.isError ||
|
|
// (bookingStatus.isFetched && !bookingStatus.data)
|
|
// ) {
|
|
// // TODO: handle error
|
|
// throw new Error("Error fetching booking status")
|
|
// }
|
|
|
|
// if (
|
|
// bookingStatus.data?.reservationStatus === BookingStatusEnum.BookingCompleted
|
|
// ) {
|
|
// return <LoadingSpinner />
|