diff --git a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/my-stay/page.tsx b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/my-stay/page.tsx
index 3308028c0..17be92f63 100644
--- a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/my-stay/page.tsx
+++ b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/my-stay/page.tsx
@@ -6,6 +6,7 @@ import { Typography } from "@scandic-hotels/design-system/Typography"
import { env } from "@/env/server"
import { dt } from "@/lib/dt"
import {
+ findBooking,
getAncillaryPackages,
getBookingConfirmation,
getLinkedReservations,
@@ -15,6 +16,7 @@ import {
} from "@/lib/trpc/memoizedRequests"
import { decrypt } from "@/server/routers/utils/encryption"
+import { auth } from "@/auth"
import AdditionalInfoForm from "@/components/HotelReservation/FindMyBooking/AdditionalInfoForm"
import accessBooking, {
ACCESS_GRANTED,
@@ -32,6 +34,7 @@ import Image from "@/components/Image"
import { getIntl } from "@/i18n"
import { setLang } from "@/i18n/serverContext"
import MyStayProvider from "@/providers/MyStay"
+import { isValidSession } from "@/utils/session"
import { getCurrentWebUrl } from "@/utils/url"
import styles from "./page.module.css"
@@ -55,8 +58,43 @@ export default async function MyStay({
return notFound()
}
+ const session = await auth()
+ const isLoggedIn = isValidSession(session)
+
const [confirmationNumber, lastName] = value.split(",")
- const bookingConfirmation = await getBookingConfirmation(confirmationNumber)
+ const bv = cookies().get("bv")?.value
+ let bookingConfirmation
+ if (isLoggedIn) {
+ bookingConfirmation = await getBookingConfirmation(confirmationNumber)
+ } else if (bv) {
+ const params = new URLSearchParams(bv)
+ const firstName = params.get("firstName")
+ const email = params.get("email")
+
+ if (firstName && email) {
+ bookingConfirmation = await findBooking(
+ confirmationNumber,
+ lastName,
+ firstName,
+ email
+ )
+ } else {
+ return (
+
+ )
+ }
+ } else {
+ return (
+
+ )
+ }
+
if (!bookingConfirmation) {
return notFound()
}
@@ -64,7 +102,7 @@ export default async function MyStay({
const { additionalData, booking, hotel, roomCategories } = bookingConfirmation
const user = await getProfileSafely()
- const bv = cookies().get("bv")?.value
+
const intl = await getIntl()
const access = accessBooking(booking.guest, lastName, user, bv)
@@ -232,3 +270,22 @@ export default async function MyStay({
return notFound()
}
+
+function RenderAdditionalInfoForm({
+ confirmationNumber,
+ lastName,
+}: {
+ confirmationNumber: string
+ lastName: string
+}) {
+ return (
+
+
+
+ )
+}
diff --git a/apps/scandic-web/app/[lang]/webview/hotelreservation/my-stay/page.tsx b/apps/scandic-web/app/[lang]/webview/hotelreservation/my-stay/page.tsx
index e083c1cde..c87a6a567 100644
--- a/apps/scandic-web/app/[lang]/webview/hotelreservation/my-stay/page.tsx
+++ b/apps/scandic-web/app/[lang]/webview/hotelreservation/my-stay/page.tsx
@@ -6,6 +6,7 @@ import { Typography } from "@scandic-hotels/design-system/Typography"
import { env } from "@/env/server"
import { dt } from "@/lib/dt"
import {
+ findBooking,
getAncillaryPackages,
getBookingConfirmation,
getLinkedReservations,
@@ -15,6 +16,7 @@ import {
} from "@/lib/trpc/memoizedRequests"
import { decrypt } from "@/server/routers/utils/encryption"
+import { auth } from "@/auth"
import AdditionalInfoForm from "@/components/HotelReservation/FindMyBooking/AdditionalInfoForm"
import accessBooking, {
ACCESS_GRANTED,
@@ -32,6 +34,7 @@ import Image from "@/components/Image"
import { getIntl } from "@/i18n"
import { setLang } from "@/i18n/serverContext"
import MyStayProvider from "@/providers/MyStay"
+import { isValidSession } from "@/utils/session"
import { getCurrentWebUrl } from "@/utils/url"
import styles from "./page.module.css"
@@ -54,9 +57,42 @@ export default async function MyStay({
if (!value) {
return notFound()
}
+ const session = await auth()
+ const isLoggedIn = isValidSession(session)
const [confirmationNumber, lastName] = value.split(",")
- const bookingConfirmation = await getBookingConfirmation(confirmationNumber)
+ const bv = cookies().get("bv")?.value
+ let bookingConfirmation
+ if (isLoggedIn) {
+ bookingConfirmation = await getBookingConfirmation(confirmationNumber)
+ } else if (bv) {
+ const params = new URLSearchParams(bv)
+ const firstName = params.get("firstName")
+ const email = params.get("email")
+
+ if (firstName && email) {
+ bookingConfirmation = await findBooking(
+ confirmationNumber,
+ lastName,
+ firstName,
+ email
+ )
+ } else {
+ return (
+
+ )
+ }
+ } else {
+ return (
+
+ )
+ }
if (!bookingConfirmation) {
return notFound()
}
@@ -64,7 +100,6 @@ export default async function MyStay({
const { additionalData, booking, hotel, roomCategories } = bookingConfirmation
const user = await getProfileSafely()
- const bv = cookies().get("bv")?.value
const intl = await getIntl()
const access = accessBooking(booking.guest, lastName, user, bv)
@@ -232,3 +267,22 @@ export default async function MyStay({
return notFound()
}
+
+function RenderAdditionalInfoForm({
+ confirmationNumber,
+ lastName,
+}: {
+ confirmationNumber: string
+ lastName: string
+}) {
+ return (
+
+
+
+ )
+}
diff --git a/apps/scandic-web/components/HotelReservation/MyStay/Receipt/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/Receipt/index.tsx
index b7f5923c3..420648db7 100644
--- a/apps/scandic-web/components/HotelReservation/MyStay/Receipt/index.tsx
+++ b/apps/scandic-web/components/HotelReservation/MyStay/Receipt/index.tsx
@@ -6,13 +6,16 @@ import { Typography } from "@scandic-hotels/design-system/Typography"
import { dt } from "@/lib/dt"
import {
+ findBooking,
getAncillaryPackages,
getBookingConfirmation,
getProfileSafely,
} from "@/lib/trpc/memoizedRequests"
import { decrypt } from "@/server/routers/utils/encryption"
+import { auth } from "@/auth"
import { getIntl } from "@/i18n"
+import { isValidSession } from "@/utils/session"
import AdditionalInfoForm from "../../FindMyBooking/AdditionalInfoForm"
import accessBooking, {
@@ -33,15 +36,48 @@ export async function Receipt({ refId }: { refId: string }) {
if (!value) {
return notFound()
}
+ const session = await auth()
+ const isLoggedIn = isValidSession(session)
+
const [confirmationNumber, lastName] = value.split(",")
- const bookingConfirmation = await getBookingConfirmation(confirmationNumber)
+ const bv = cookies().get("bv")?.value
+ let bookingConfirmation
+ if (isLoggedIn) {
+ bookingConfirmation = await getBookingConfirmation(confirmationNumber)
+ } else if (bv) {
+ const params = new URLSearchParams(bv)
+ const firstName = params.get("firstName")
+ const email = params.get("email")
+
+ if (firstName && email) {
+ bookingConfirmation = await findBooking(
+ confirmationNumber,
+ lastName,
+ firstName,
+ email
+ )
+ } else {
+ return (
+
+ )
+ }
+ } else {
+ return (
+
+ )
+ }
if (!bookingConfirmation) {
return notFound()
}
const { booking, hotel, room } = bookingConfirmation
const user = await getProfileSafely()
- const bv = cookies().get("bv")?.value
const intl = await getIntl()
const access = accessBooking(booking.guest, lastName, user, bv)
@@ -166,3 +202,22 @@ export async function Receipt({ refId }: { refId: string }) {
return notFound()
}
+
+function RenderAdditionalInfoForm({
+ confirmationNumber,
+ lastName,
+}: {
+ confirmationNumber: string
+ lastName: string
+}) {
+ return (
+
+
+
+ )
+}
diff --git a/apps/scandic-web/lib/api/endpoints.ts b/apps/scandic-web/lib/api/endpoints.ts
index d505fa9ec..07a3f5a38 100644
--- a/apps/scandic-web/lib/api/endpoints.ts
+++ b/apps/scandic-web/lib/api/endpoints.ts
@@ -60,6 +60,9 @@ export namespace endpoints {
export function booking(confirmationNumber: string) {
return `${bookings}/${confirmationNumber}`
}
+ export function find(confirmationNumber: string) {
+ return `${bookings}/${confirmationNumber}/find`
+ }
export function cancel(confirmationNumber: string) {
return `${bookings}/${confirmationNumber}/cancel`
}
diff --git a/apps/scandic-web/lib/trpc/memoizedRequests/index.ts b/apps/scandic-web/lib/trpc/memoizedRequests/index.ts
index 9dd7eb0ba..05040e27c 100644
--- a/apps/scandic-web/lib/trpc/memoizedRequests/index.ts
+++ b/apps/scandic-web/lib/trpc/memoizedRequests/index.ts
@@ -141,6 +141,20 @@ export const getBookingConfirmation = cache(
}
)
+export const findBooking = cache(async function getMemoizedFindBooking(
+ confirmationNumber: string,
+ lastName: string,
+ firstName: string,
+ email: string
+) {
+ return serverClient().booking.findBooking({
+ confirmationNumber,
+ lastName,
+ firstName,
+ email,
+ })
+})
+
export const getLinkedReservations = cache(
async function getMemoizedLinkedReservations(input: LinkedReservationsInput) {
return serverClient().booking.linkedReservations(input)
diff --git a/apps/scandic-web/server/routers/booking/input.ts b/apps/scandic-web/server/routers/booking/input.ts
index bbf444181..50bcdec28 100644
--- a/apps/scandic-web/server/routers/booking/input.ts
+++ b/apps/scandic-web/server/routers/booking/input.ts
@@ -184,6 +184,14 @@ export const getLinkedReservationsInput = z.object({
),
})
+export const findBookingInput = z.object({
+ confirmationNumber: z.string(),
+ firstName: z.string(),
+ lastName: z.string(),
+ email: z.string(),
+ lang: z.nativeEnum(Lang).optional(),
+})
+
export type LinkedReservationsInput = z.input
export const getBookingStatusInput = confirmationNumberInput
diff --git a/apps/scandic-web/server/routers/booking/query.ts b/apps/scandic-web/server/routers/booking/query.ts
index 280a49769..2c8a518d3 100644
--- a/apps/scandic-web/server/routers/booking/query.ts
+++ b/apps/scandic-web/server/routers/booking/query.ts
@@ -11,12 +11,13 @@ import { getHotel } from "../hotels/utils"
import { encrypt } from "../utils/encryption"
import {
createRefIdInput,
+ findBookingInput,
getBookingInput,
getBookingStatusInput,
getLinkedReservationsInput,
} from "./input"
import { createBookingSchema } from "./output"
-import { getBookedHotelRoom, getBooking } from "./utils"
+import { findBooking, getBookedHotelRoom, getBooking } from "./utils"
export const bookingQueryRouter = router({
get: safeProtectedServiceProcedure
@@ -69,6 +70,66 @@ export const bookingQueryRouter = router({
metricsGetBooking.success()
+ return {
+ ...hotelData,
+ booking,
+ room: getBookedHotelRoom(
+ hotelData.roomCategories,
+ booking.roomTypeCode
+ ),
+ }
+ }),
+ findBooking: safeProtectedServiceProcedure
+ .input(findBookingInput)
+
+ .query(async function ({
+ ctx,
+ input: { confirmationNumber, lastName, firstName, email },
+ }) {
+ const findBookingCounter = createCounter("trpc.booking", "findBooking")
+ const metricsFindBooking = findBookingCounter.init({ confirmationNumber })
+
+ metricsFindBooking.start()
+
+ const booking = await findBooking(
+ confirmationNumber,
+ ctx.lang,
+ ctx.serviceToken,
+ lastName,
+ firstName,
+ email
+ )
+
+ if (!booking) {
+ metricsFindBooking.dataError(
+ `Fail to find booking data for ${confirmationNumber}`,
+ { confirmationNumber }
+ )
+ return null
+ }
+
+ const hotelData = await getHotel(
+ {
+ hotelId: booking.hotelId,
+ isCardOnlyPayment: false,
+ language: ctx.lang,
+ },
+ ctx.serviceToken
+ )
+
+ if (!hotelData) {
+ metricsFindBooking.dataError(
+ `Failed to find hotel data for ${booking.hotelId}`,
+ {
+ hotelId: booking.hotelId,
+ }
+ )
+
+ throw serverErrorByStatus(404)
+ }
+
+ metricsFindBooking.success()
+
return {
...hotelData,
booking,
diff --git a/apps/scandic-web/server/routers/booking/utils.ts b/apps/scandic-web/server/routers/booking/utils.ts
index c983407b4..0941bc76b 100644
--- a/apps/scandic-web/server/routers/booking/utils.ts
+++ b/apps/scandic-web/server/routers/booking/utils.ts
@@ -78,6 +78,63 @@ export async function getBooking(
return booking.data
}
+export async function findBooking(
+ confirmationNumber: string,
+ lang: Lang,
+ token: string,
+ lastName?: string,
+ firstName?: string,
+ email?: string
+) {
+ const findBookingCounter = createCounter("booking", "find")
+ const metricsGetBooking = findBookingCounter.init({
+ confirmationNumber,
+ lastName,
+ firstName,
+ email,
+ })
+
+ metricsGetBooking.start()
+
+ const apiResponse = await api.post(
+ api.endpoints.v1.Booking.find(confirmationNumber),
+ {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ body: {
+ lastName,
+ firstName,
+ email,
+ },
+ },
+ { language: toApiLang(lang) }
+ )
+
+ if (!apiResponse.ok) {
+ await metricsGetBooking.httpError(apiResponse)
+
+ // If the booking is not found, return null.
+ // This scenario is expected to happen when a logged in user trying to access a booking that doesn't belong to them.
+ if (apiResponse.status === 400) {
+ return null
+ }
+
+ throw serverErrorByStatus(apiResponse.status, apiResponse)
+ }
+
+ const apiJson = await apiResponse.json()
+ const booking = bookingConfirmationSchema.safeParse(apiJson)
+ if (!booking.success) {
+ metricsGetBooking.validationError(booking.error)
+ throw badRequestError()
+ }
+
+ metricsGetBooking.success()
+
+ return booking.data
+}
+
export async function cancelBooking(
confirmationNumber: string,
language: Lang,