Merged in fix/book-607-page-not-found-confirmation-page (pull request #3345)

* fix(BOOK-607): updated to read from base URL env and contentstack slug instead

* made url optional on interface

* added url to findBooking query

* added the correct (new) link in the confirmation header, add to calendar


Approved-by: Erik Tiekstra
This commit is contained in:
Matilda Haneling
2025-12-17 07:53:04 +00:00
parent c796f5bb81
commit b8bc94acb3
7 changed files with 69 additions and 32 deletions

View File

@@ -9,12 +9,14 @@ export const env = createEnv({
*/ */
isServer: typeof window === "undefined" || "Deno" in window, isServer: typeof window === "undefined" || "Deno" in window,
server: { server: {
PUBLIC_URL: z.string().default(""),
GOOGLE_STATIC_MAP_KEY: z.string(), GOOGLE_STATIC_MAP_KEY: z.string(),
GOOGLE_STATIC_MAP_SIGNATURE_SECRET: z.string(), GOOGLE_STATIC_MAP_SIGNATURE_SECRET: z.string(),
GOOGLE_DYNAMIC_MAP_ID: z.string(), GOOGLE_DYNAMIC_MAP_ID: z.string(),
}, },
emptyStringAsUndefined: true, emptyStringAsUndefined: true,
runtimeEnv: { runtimeEnv: {
PUBLIC_URL: process.env.NEXT_PUBLIC_PUBLIC_URL,
GOOGLE_STATIC_MAP_KEY: process.env.GOOGLE_STATIC_MAP_KEY, GOOGLE_STATIC_MAP_KEY: process.env.GOOGLE_STATIC_MAP_KEY,
GOOGLE_STATIC_MAP_SIGNATURE_SECRET: GOOGLE_STATIC_MAP_SIGNATURE_SECRET:
process.env.GOOGLE_STATIC_MAP_SIGNATURE_SECRET, process.env.GOOGLE_STATIC_MAP_SIGNATURE_SECRET,

View File

@@ -10,13 +10,16 @@ import type { BookingConfirmation } from "@scandic-hotels/trpc/types/bookingConf
import type { BookingConfirmationRoom } from "../../../types/components/bookingConfirmation/bookingConfirmation" import type { BookingConfirmationRoom } from "../../../types/components/bookingConfirmation/bookingConfirmation"
interface ConfirmationProps interface ConfirmationProps extends Pick<
extends Pick<BookingConfirmation, "booking" | "hotel"> { BookingConfirmation,
"booking" | "hotel" | "url"
> {
room: BookingConfirmationRoom room: BookingConfirmationRoom
} }
export function Confirmation({ export function Confirmation({
booking, booking,
url,
hotel, hotel,
children, children,
}: React.PropsWithChildren<ConfirmationProps>) { }: React.PropsWithChildren<ConfirmationProps>) {
@@ -24,7 +27,7 @@ export function Confirmation({
return ( return (
<main className={styles.main} ref={mainRef}> <main className={styles.main} ref={mainRef}>
<Header booking={booking} hotel={hotel} mainRef={mainRef} /> <Header url={url} booking={booking} hotel={hotel} mainRef={mainRef} />
{children} {children}
</main> </main>
) )

View File

@@ -18,7 +18,7 @@ import type { MutableRefObject } from "react"
interface BookingConfirmationHeaderProps extends Pick< interface BookingConfirmationHeaderProps extends Pick<
BookingConfirmation, BookingConfirmation,
"booking" | "hotel" "booking" | "hotel" | "url"
> { > {
mainRef: MutableRefObject<HTMLElement | null> mainRef: MutableRefObject<HTMLElement | null>
} }
@@ -26,6 +26,7 @@ interface BookingConfirmationHeaderProps extends Pick<
export function Header({ export function Header({
booking, booking,
hotel, hotel,
url,
// mainRef, // mainRef,
}: BookingConfirmationHeaderProps) { }: BookingConfirmationHeaderProps) {
const intl = useIntl() const intl = useIntl()
@@ -52,7 +53,7 @@ export function Header({
startInputType: "utc", startInputType: "utc",
status: "CONFIRMED", status: "CONFIRMED",
title: hotel.name, title: hotel.name,
url: hotel.contactInformation.websiteUrl, url: url ?? undefined,
} }
return ( return (

View File

@@ -11,10 +11,13 @@ import type { BookingConfirmation } from "@scandic-hotels/trpc/types/bookingConf
export function HotelDetails({ export function HotelDetails({
hotel, hotel,
url,
}: { }: {
hotel: BookingConfirmation["hotel"] hotel: BookingConfirmation["hotel"]
url: string | null
}) { }) {
const intl = useIntl() const intl = useIntl()
return ( return (
<div className={styles.container}> <div className={styles.container}>
<div className={styles.details}> <div className={styles.details}>
@@ -62,14 +65,16 @@ export function HotelDetails({
> >
{hotel.contactInformation.email} {hotel.contactInformation.email}
</Link> </Link>
{url && (
<Link <Link
className={styles.link} className={styles.link}
color="Text/Interactive/Secondary" color="Text/Interactive/Secondary"
href={hotel.contactInformation.websiteUrl} href={url}
textDecoration="underline" textDecoration="underline"
> >
{hotel.contactInformation.websiteUrl} {url}
</Link> </Link>
)}
</div> </div>
</div> </div>
) )

View File

@@ -7,6 +7,7 @@ import { Alert } from "@scandic-hotels/design-system/Alert"
import { Divider } from "@scandic-hotels/design-system/Divider" import { Divider } from "@scandic-hotels/design-system/Divider"
import { HotelTypeEnum } from "@scandic-hotels/trpc/enums/hotelType" import { HotelTypeEnum } from "@scandic-hotels/trpc/enums/hotelType"
import { env } from "../../../env/server"
import { BookingConfirmationProvider } from "../../providers/BookingConfirmationProvider" import { BookingConfirmationProvider } from "../../providers/BookingConfirmationProvider"
import { getBookingConfirmation } from "../../trpc/memoizedRequests/getBookingConfirmation" import { getBookingConfirmation } from "../../trpc/memoizedRequests/getBookingConfirmation"
import { SidePanel } from "../SidePanel" import { SidePanel } from "../SidePanel"
@@ -41,7 +42,10 @@ export async function BookingConfirmation({
return notFound() return notFound()
} }
const { booking, hotel, room, roomCategories } = bookingConfirmation const { booking, url, hotel, room, roomCategories } = bookingConfirmation
const baseUrl = env.PUBLIC_URL || "https://www.scandichotels.com"
const hotelUrl = new URL(`${baseUrl}${url}`)
if (!room) { if (!room) {
return notFound() return notFound()
@@ -69,7 +73,12 @@ export async function BookingConfirmation({
]} ]}
vat={booking.vatPercentage} vat={booking.vatPercentage}
> >
<Confirmation booking={booking} hotel={hotel} room={room}> <Confirmation
url={hotelUrl.toString()}
booking={booking}
hotel={hotel}
room={room}
>
<div className={styles.booking}> <div className={styles.booking}>
{membershipFailedError && ( {membershipFailedError && (
<Alert <Alert
@@ -93,7 +102,7 @@ export async function BookingConfirmation({
/> />
<PaymentDetails /> <PaymentDetails />
<Divider color="Border/Divider/Subtle" /> <Divider color="Border/Divider/Subtle" />
<HotelDetails hotel={hotel} /> <HotelDetails url={hotelUrl.toString()} hotel={hotel} />
{validAlerts.map((alert) => ( {validAlerts.map((alert) => (
<div key={alert.id}> <div key={alert.id}>
<Alert <Alert

View File

@@ -11,6 +11,7 @@ import {
import { toApiLang } from "../../utils" import { toApiLang } from "../../utils"
import { encrypt } from "../../utils/encryption" import { encrypt } from "../../utils/encryption"
import { isValidSession } from "../../utils/session" import { isValidSession } from "../../utils/session"
import { getHotelPageUrls } from "../contentstack/hotelPage/utils"
import { getHotel } from "../hotels/services/getHotel" import { getHotel } from "../hotels/services/getHotel"
import { createBookingSchema } from "./mutation/create/schema" import { createBookingSchema } from "./mutation/create/schema"
import { getHotelRoom } from "./helpers" import { getHotelRoom } from "./helpers"
@@ -62,13 +63,19 @@ export const bookingQueryRouter = router({
return null return null
} }
const hotelData = await getHotel( const [hotelData, hotelPages] = await Promise.all([
getHotel(
{ {
hotelId: booking.hotelId, hotelId: booking.hotelId,
isCardOnlyPayment: false, isCardOnlyPayment: false,
language: lang, language: lang,
}, },
serviceToken serviceToken
),
getHotelPageUrls(lang),
])
const hotelPage = hotelPages.find(
(page) => page.hotelId === booking.hotelId
) )
if (!hotelData) { if (!hotelData) {
@@ -86,6 +93,7 @@ export const bookingQueryRouter = router({
return { return {
...hotelData, ...hotelData,
url: hotelPage?.url || null,
booking, booking,
room: getHotelRoom(hotelData.roomCategories, booking.roomTypeCode), room: getHotelRoom(hotelData.roomCategories, booking.roomTypeCode),
} }
@@ -132,13 +140,19 @@ export const bookingQueryRouter = router({
return null return null
} }
const hotelData = await getHotel( const [hotelData, hotelPages] = await Promise.all([
getHotel(
{ {
hotelId: booking.hotelId, hotelId: booking.hotelId,
isCardOnlyPayment: false, isCardOnlyPayment: false,
language: lang, language: lang,
}, },
serviceToken serviceToken
),
getHotelPageUrls(lang),
])
const hotelPage = hotelPages.find(
(page) => page.hotelId === booking.hotelId
) )
if (!hotelData) { if (!hotelData) {
@@ -156,6 +170,7 @@ export const bookingQueryRouter = router({
return { return {
...hotelData, ...hotelData,
url: hotelPage?.url || null,
booking, booking,
room: getHotelRoom(hotelData.roomCategories, booking.roomTypeCode), room: getHotelRoom(hotelData.roomCategories, booking.roomTypeCode),
} }

View File

@@ -6,12 +6,14 @@ import type {
} from "../routers/booking/output" } from "../routers/booking/output"
import type { HotelData, Room } from "./hotel" import type { HotelData, Room } from "./hotel"
export interface BookingConfirmationSchema export interface BookingConfirmationSchema extends z.output<
extends z.output<typeof bookingConfirmationSchema> {} typeof bookingConfirmationSchema
> {}
export interface PackageSchema extends z.output<typeof packageSchema> {} export interface PackageSchema extends z.output<typeof packageSchema> {}
export interface BookingConfirmation extends HotelData { export interface BookingConfirmation extends HotelData {
url: string | null
booking: BookingConfirmationSchema booking: BookingConfirmationSchema
room: room:
| (Room & { | (Room & {