Merged in feat(SW-1944)-update-url-to-mystay (pull request #1566)

feat(SW-1944) Update to correct URL to my stay (for my pages/my stays and confirmation page)

* feat(SW-1944) Update to correct URL to my stay (for my pages/my stays and confirmation page)

* feat(SW-1944) updated to RefId

* feat(SW-1944) updated myStay path

* feat(SW-1944) updated refId check


Approved-by: Christian Andolf
This commit is contained in:
Pontus Dreij
2025-03-20 09:55:24 +00:00
parent e0b7c3be7b
commit 8f9e268802
13 changed files with 93 additions and 65 deletions

View File

@@ -4,7 +4,7 @@ import {
BookingErrorCodeEnum,
PaymentCallbackStatusEnum,
} from "@/constants/booking"
import { hotelreservation } from "@/constants/routes/hotelReservation"
import { myStay } from "@/constants/routes/myStay"
import { serverClient } from "@/lib/trpc/server"
import LoadingSpinner from "@/components/LoadingSpinner"
@@ -27,7 +27,7 @@ export default async function GuaranteePaymentCallbackPage({
const status = searchParams.status
const confirmationNumber = searchParams.confirmationNumber
const refId = searchParams.refId
const myStayUrl = `${hotelreservation(lang)}/my-stay?RefId=${refId}`
const myStayUrl = `${myStay[lang]}?RefId=${refId}`
if (
status === PaymentCallbackStatusEnum.Success &&

View File

@@ -11,12 +11,13 @@ export default function Confirmation({
booking,
hotel,
children,
refId,
}: React.PropsWithChildren<ConfirmationProps>) {
const mainRef = useRef<HTMLElement | null>(null)
return (
<main className={styles.main} ref={mainRef}>
<Header booking={booking} hotel={hotel} mainRef={mainRef} />
<Header booking={booking} hotel={hotel} mainRef={mainRef} refId={refId} />
{children}
</main>
)

View File

@@ -1,23 +1,15 @@
"use client"
import { useIntl } from "react-intl"
import { myBooking } from "@/constants/myBooking"
import { env } from "@/env/client"
import { EditIcon } from "@/components/Icons"
import Button from "@/components/TempDesignSystem/Button"
import Link from "@/components/TempDesignSystem/Link"
import useLang from "@/hooks/useLang"
import type { ManageBookingProps } from "@/types/components/hotelReservation/bookingConfirmation/actions/manageBooking"
export default function ManageBooking({
confirmationNumber,
lastName,
}: ManageBookingProps) {
export default function ManageBooking({ bookingUrl }: ManageBookingProps) {
const intl = useIntl()
const lang = useLang()
const myBookingUrl = myBooking[env.NEXT_PUBLIC_NODE_ENV][lang]
return (
<Button
asChild
@@ -27,11 +19,7 @@ export default function ManageBooking({
variant="icon"
wrapping
>
<Link
color="none"
href={`${myBookingUrl}?bookingId=${confirmationNumber}&lastName=${lastName}`}
weight="bold"
>
<Link color="none" href={bookingUrl} weight="bold">
<EditIcon />
{intl.formatMessage({ id: "Manage booking" })}
</Link>

View File

@@ -1,10 +1,13 @@
"use client"
import { useIntl } from "react-intl"
import { myStay } from "@/constants/routes/myStay"
import Link from "@/components/TempDesignSystem/Link"
import Body from "@/components/TempDesignSystem/Text/Body"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import Title from "@/components/TempDesignSystem/Text/Title"
import useLang from "@/hooks/useLang"
import AddToCalendar from "../../AddToCalendar"
import AddToCalendarButton from "./Actions/AddToCalendarButton"
@@ -22,8 +25,10 @@ export default function Header({
booking,
hotel,
mainRef,
refId,
}: BookingConfirmationHeaderProps) {
const intl = useIntl()
const lang = useLang()
const text = intl.formatMessage(
{
@@ -57,6 +62,8 @@ export default function Header({
url: hotel.contactInformation.websiteUrl,
}
const bookingUrlPath = `${myStay[lang]}?RefId=${refId}`
return (
<header className={styles.header}>
<hgroup className={styles.hgroup}>
@@ -83,10 +90,7 @@ export default function Header({
hotelName={hotel.name}
renderButton={(onPress) => <AddToCalendarButton onPress={onPress} />}
/>
<ManageBooking
confirmationNumber={booking.confirmationNumber}
lastName={booking.guest.lastName}
/>
<ManageBooking bookingUrl={bookingUrlPath} />
<DownloadInvoice mainRef={mainRef} />
</div>
</header>

View File

@@ -1,6 +1,7 @@
import { notFound } from "next/navigation"
import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests"
import { encrypt } from "@/server/routers/utils/encryption"
import HotelDetails from "@/components/HotelReservation/BookingConfirmation/HotelDetails"
import PaymentDetails from "@/components/HotelReservation/BookingConfirmation/PaymentDetails"
@@ -33,6 +34,10 @@ export default async function BookingConfirmation({
return notFound()
}
const refId = encrypt(
`${booking.confirmationNumber},${booking.guest.lastName}`
)
return (
<BookingConfirmationProvider
bookingCode={booking.bookingCode}
@@ -46,7 +51,7 @@ export default async function BookingConfirmation({
]}
vat={booking.vatPercentage}
>
<Confirmation booking={booking} hotel={hotel} room={room}>
<Confirmation booking={booking} hotel={hotel} room={room} refId={refId}>
<div className={styles.booking}>
<Alerts booking={booking} />
<Rooms

View File

@@ -6,7 +6,7 @@ import { FormProvider, useForm } from "react-hook-form"
import { useIntl } from "react-intl"
import { customerService } from "@/constants/currentWebHrefs"
import { hotelreservation } from "@/constants/routes/hotelReservation"
import { myStay } from "@/constants/routes/myStay"
import { trpc } from "@/lib/trpc/client"
import Button from "@/components/TempDesignSystem/Button"
@@ -44,7 +44,7 @@ export default function FindMyBooking() {
const values = form.getValues()
const value = new URLSearchParams(values).toString()
document.cookie = `bv=${encodeURIComponent(value)}; Path=/; Max-Age=600; Secure; SameSite=Strict`
router.push(`${hotelreservation(lang)}/my-stay?RefId=${result.refId}`)
router.push(`${myStay[lang]}?RefId=${result.refId}`)
},
onError: (error) => {
console.error("Failed to create ref id", error)

View File

@@ -0,0 +1,14 @@
/**
* @file Due to these records being used in next.config.js, and that is required
* to be a js file, we use jsdoc to type these.
*/
/** @type {import('@/types/routes').LangRoute} */
export const myStay = {
da: "/da/hotelreservation/min-ophold",
de: "/de/hotelreservation/meine-aufenthalte",
en: "/en/hotelreservation/my-stay",
fi: "/fi/hotelreservation/min-vistelma",
no: "/no/hotelreservation/min-ophold",
sv: "/sv/hotelreservation/min-vistelse",
}

View File

@@ -5,6 +5,7 @@ import { fileURLToPath } from "url"
import { findMyBooking } from "./constants/routes/findMyBooking.js"
import { login, logout } from "./constants/routes/handleAuth.js"
import { myPages } from "./constants/routes/myPages.js"
import { myStay } from "./constants/routes/myStay.js"
const path = fileURLToPath(new URL(import.meta.url))
const jiti = createJiti(path)
@@ -308,6 +309,31 @@ const nextConfig = {
source: findMyBooking.sv,
destination: "/sv/hotelreservation/get-booking",
},
// My stay
{
source: `${myStay.en}`,
destination: "/en/hotelreservation/my-stay",
},
{
source: `${myStay.sv}`,
destination: "/sv/hotelreservation/my-stay",
},
{
source: `${myStay.da}`,
destination: "/da/hotelreservation/my-stay",
},
{
source: `${myStay.de}`,
destination: "/de/hotelreservation/my-stay",
},
{
source: `${myStay.fi}`,
destination: "/fi/hotelreservation/my-stay",
},
{
source: `${myStay.no}`,
destination: "/no/hotelreservation/my-stay",
},
{
source: `/sitemap-:id(\\d{1,}).xml`,
destination: `/sitemap/:id`,

View File

@@ -3,6 +3,7 @@ import { z } from "zod"
import { baseUrls } from "@/constants/routes/baseUrls"
import { findMyBooking } from "@/constants/routes/findMyBooking"
import { hotelreservation } from "@/constants/routes/hotelReservation"
import { myStay } from "@/constants/routes/myStay"
import { env } from "@/env/server"
import { attributesSchema as hotelAttributesSchema } from "../../hotels/schemas/hotel"
@@ -142,6 +143,14 @@ export function getNonContentstackUrls(lang: Lang, pathName: string) {
}, urls)
}
if (Object.values(myStay).includes(pathName)) {
const urls: LanguageSwitcherData = {}
return Object.entries(myStay).reduce((acc, [lang, url]) => {
acc[lang as Lang] = { url }
return urls
}, urls)
}
if (pathName.startsWith(hotelreservation(lang))) {
return baseUrls
}

View File

@@ -1,6 +1,8 @@
import { metrics } from "@opentelemetry/api"
import { homeHrefs } from "@/constants/homeHrefs"
import { Lang } from "@/constants/languages"
import { myStay } from "@/constants/routes/myStay"
import { env } from "@/env/server"
import * as api from "@/lib/api"
@@ -41,35 +43,6 @@ async function updateStaysBookingUrl(
Authorization: `Bearer ${token}`,
},
})
// Temporary Url, domain and lang support for current web
const bookingUrl = new URL(
"/hotelreservation/my-booking",
env.PUBLIC_URL || "https://www.scandichotels.com" // fallback to production for ephemeral envs (like deploy previews)
)
switch (lang) {
case Lang.sv:
bookingUrl.host = bookingUrl.host.replace(".com", ".se")
bookingUrl.pathname = "/hotelreservation/din-bokning"
break
case Lang.no:
bookingUrl.host = bookingUrl.host.replace(".com", ".no")
bookingUrl.pathname = "/hotelreservation/my-booking"
break
case Lang.da:
bookingUrl.host = bookingUrl.host.replace(".com", ".dk")
bookingUrl.pathname = "/hotelreservation/min-booking"
break
case Lang.fi:
bookingUrl.host = bookingUrl.host.replace(".com", ".fi")
bookingUrl.pathname = "/varaa-hotelli/varauksesi"
break
case Lang.de:
bookingUrl.host = bookingUrl.host.replace(".com", ".de")
bookingUrl.pathname = "/hotelreservation/my-booking"
break
default:
break
}
if (!apiResponse.ok) {
getProfileFailCounter.add(1, { error: JSON.stringify(apiResponse) })
@@ -94,12 +67,20 @@ async function updateStaysBookingUrl(
"," +
apiJson.data.attributes.lastName
const encryptedBookingValue = encrypt(originalString)
if (!!encryptedBookingValue) {
bookingUrl.searchParams.set("RefId", encryptedBookingValue)
} else {
bookingUrl.searchParams.set("lastName", apiJson.data.attributes.lastName)
bookingUrl.searchParams.set("bookingId", d.attributes.confirmationNumber)
}
// Construct URL using myStay route and append encrypted value
const bookingUrlPath = encryptedBookingValue
? `${myStay[lang]}?RefId=${encryptedBookingValue}`
: `${myStay[lang]}?bookingId=${d.attributes.confirmationNumber}&lastName=${apiJson.data.attributes.lastName}`
// Construct full URL with domain
const domain = homeHrefs[env.NODE_ENV][lang]
const bookingUrl = new URL(bookingUrlPath, domain)
// Update TLD based on language
if (lang !== Lang.en)
bookingUrl.host = bookingUrl.host.replace(".com", `.${lang}`)
return {
...d,
attributes: {

View File

@@ -1,5 +1,3 @@
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
export interface ManageBookingProps
extends Pick<BookingConfirmation["booking"], "confirmationNumber">,
Pick<BookingConfirmation["booking"]["guest"], "lastName"> {}
export interface ManageBookingProps {
bookingUrl: string
}

View File

@@ -14,6 +14,7 @@ export interface BookingConfirmationRoom extends Room {
export interface ConfirmationProps extends BookingConfirmation {
room: BookingConfirmationRoom
refId: string
}
export interface BookingConfirmationAlertsProps {

View File

@@ -5,4 +5,5 @@ import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmat
export interface BookingConfirmationHeaderProps
extends Pick<BookingConfirmation, "booking" | "hotel"> {
mainRef: MutableRefObject<HTMLElement | null>
refId: string
}