feat(SW-2116): avoid passing entire booking object to Room client component

This commit is contained in:
Arvid Norlin
2025-04-23 15:47:18 +02:00
committed by Michael Zetterberg
parent a839d05e09
commit 7eeb0bbcac
11 changed files with 75 additions and 43 deletions

View File

@@ -78,14 +78,19 @@ export function LinkedReservation({
if (!data?.room) { if (!data?.room) {
return <Retry handleRefetch={refetch} /> return <Retry handleRefetch={refetch} />
} }
const { booking, room } = data
return ( return (
<Room <Room
booking={data.booking} checkInDate={booking.checkInDate}
checkOutDate={booking.checkOutDate}
checkInTime={checkInTime} checkInTime={checkInTime}
checkOutTime={checkOutTime} checkOutTime={checkOutTime}
img={data.room.images[0]} confirmationNumber={booking.confirmationNumber}
roomName={data.room.name} guest={booking.guest}
guaranteeInfo={booking.guaranteeInfo}
img={room.images[0]}
rateDefinition={booking.rateDefinition}
roomName={room.name}
/> />
) )
} }

View File

@@ -20,24 +20,28 @@ import styles from "./room.module.css"
import type { RoomProps } from "@/types/components/hotelReservation/bookingConfirmation/rooms/room" import type { RoomProps } from "@/types/components/hotelReservation/bookingConfirmation/rooms/room"
export default function Room({ export default function Room({
booking, checkInDate,
checkOutDate,
checkInTime, checkInTime,
checkOutTime, checkOutTime,
confirmationNumber,
guaranteeInfo,
guest,
img, img,
rateDefinition,
roomName, roomName,
}: RoomProps) { }: RoomProps) {
const intl = useIntl() const intl = useIntl()
const lang = useLang() const lang = useLang()
const guestName = `${booking.guest.firstName} ${booking.guest.lastName}` const guestName = `${guest.firstName} ${guest.lastName}`
const fromDate = dt(booking.checkInDate).locale(lang) const fromDate = dt(checkInDate).locale(lang)
const toDate = dt(booking.checkOutDate).locale(lang) const toDate = dt(checkOutDate).locale(lang)
const isFlexBooking = const isFlexBooking =
booking.rateDefinition.cancellationRule === rateDefinition.cancellationRule ===
CancellationRuleEnum.CancellableBefore6PM CancellationRuleEnum.CancellableBefore6PM
const isChangeBooking = const isChangeBooking =
booking.rateDefinition.cancellationRule === CancellationRuleEnum.Changeable rateDefinition.cancellationRule === CancellationRuleEnum.Changeable
return ( return (
<article className={styles.room}> <article className={styles.room}>
<header className={styles.header}> <header className={styles.header}>
@@ -47,11 +51,11 @@ export default function Room({
{ {
defaultMessage: "Booking number {value}", defaultMessage: "Booking number {value}",
}, },
{ value: booking.confirmationNumber } { value: confirmationNumber }
)} )}
</h2> </h2>
</Typography> </Typography>
{booking.rateDefinition.isMemberRate ? ( {rateDefinition.isMemberRate ? (
<div className={styles.benefits}> <div className={styles.benefits}>
<> <>
<MaterialIcon <MaterialIcon
@@ -67,7 +71,7 @@ export default function Room({
</> </>
</div> </div>
) : null} ) : null}
{booking.guaranteeInfo && ( {guaranteeInfo && (
<div className={styles.benefits}> <div className={styles.benefits}>
<MaterialIcon <MaterialIcon
icon="check_circle" icon="check_circle"
@@ -168,7 +172,7 @@ export default function Room({
})} })}
</Body> </Body>
<Body color="uiTextHighContrast"> <Body color="uiTextHighContrast">
{booking.rateDefinition.cancellationText} {rateDefinition.cancellationText}
</Body> </Body>
</li> </li>
{isFlexBooking || isChangeBooking ? ( {isFlexBooking || isChangeBooking ? (
@@ -196,25 +200,23 @@ export default function Room({
})} })}
</Body> </Body>
<Body color="uiTextHighContrast">{guestName}</Body> <Body color="uiTextHighContrast">{guestName}</Body>
{booking.guest.membershipNumber ? ( {guest.membershipNumber ? (
<Body color="uiTextHighContrast"> <Body color="uiTextHighContrast">
{intl.formatMessage( {intl.formatMessage(
{ {
defaultMessage: "Friend no. {value}", defaultMessage: "Friend no. {value}",
}, },
{ {
value: booking.guest.membershipNumber, value: guest.membershipNumber,
} }
)} )}
</Body> </Body>
) : null} ) : null}
{booking.guest.phoneNumber ? ( {guest.phoneNumber ? (
<Body color="uiTextHighContrast"> <Body color="uiTextHighContrast">{guest.phoneNumber}</Body>
{booking.guest.phoneNumber}
</Body>
) : null} ) : null}
{booking.guest.email ? ( {guest.email ? (
<Body color="uiTextHighContrast">{booking.guest.email}</Body> <Body color="uiTextHighContrast">{guest.email}</Body>
) : null} ) : null}
</div> </div>
</div> </div>

View File

@@ -34,10 +34,15 @@ export default async function Rooms({
</Typography> </Typography>
) : null} ) : null}
<Room <Room
booking={booking} checkInDate={booking.checkInDate}
checkOutDate={booking.checkOutDate}
checkInTime={checkInTime} checkInTime={checkInTime}
checkOutTime={checkOutTime} checkOutTime={checkOutTime}
confirmationNumber={booking.confirmationNumber}
guaranteeInfo={booking.guaranteeInfo}
guest={booking.guest}
img={mainRoom.images[0]} img={mainRoom.images[0]}
rateDefinition={booking.rateDefinition}
roomName={mainRoom.name} roomName={mainRoom.name}
/> />
</div> </div>

View File

@@ -1,7 +1,6 @@
import { notFound } from "next/navigation" import { notFound } from "next/navigation"
import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests" import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests"
import { encrypt } from "@/server/routers/utils/encryption"
import HotelDetails from "@/components/HotelReservation/BookingConfirmation/HotelDetails" import HotelDetails from "@/components/HotelReservation/BookingConfirmation/HotelDetails"
import PaymentDetails from "@/components/HotelReservation/BookingConfirmation/PaymentDetails" import PaymentDetails from "@/components/HotelReservation/BookingConfirmation/PaymentDetails"
@@ -12,6 +11,7 @@ import SidePanel from "@/components/HotelReservation/SidePanel"
import Divider from "@/components/TempDesignSystem/Divider" import Divider from "@/components/TempDesignSystem/Divider"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import BookingConfirmationProvider from "@/providers/BookingConfirmationProvider" import BookingConfirmationProvider from "@/providers/BookingConfirmationProvider"
import { encrypt } from "@/utils/encryption"
import Alerts from "./Alerts" import Alerts from "./Alerts"
import Confirmation from "./Confirmation" import Confirmation from "./Confirmation"
@@ -66,11 +66,7 @@ export default async function BookingConfirmation({
<PaymentDetails /> <PaymentDetails />
<Divider color="primaryLightSubtle" /> <Divider color="primaryLightSubtle" />
<HotelDetails hotel={hotel} /> <HotelDetails hotel={hotel} />
<Promos <Promos refId={refId} hotelId={hotel.operaId} />
confirmationNumber={booking.confirmationNumber}
hotelId={hotel.operaId}
lastName={booking.guest.lastName}
/>
<div className={styles.mobileReceipt}> <div className={styles.mobileReceipt}>
<Receipt /> <Receipt />
</div> </div>

View File

@@ -10,9 +10,9 @@ import {
getBookingConfirmation, getBookingConfirmation,
getProfileSafely, getProfileSafely,
} from "@/lib/trpc/memoizedRequests" } from "@/lib/trpc/memoizedRequests"
import { decrypt } from "@/server/routers/utils/encryption"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import { decrypt } from "@/utils/encryption"
import AdditionalInfoForm from "../../FindMyBooking/AdditionalInfoForm" import AdditionalInfoForm from "../../FindMyBooking/AdditionalInfoForm"
import accessBooking, { import accessBooking, {
@@ -132,6 +132,7 @@ export async function Receipt({ refId }: { refId: string }) {
<main className={styles.main}> <main className={styles.main}>
<div className={styles.form}> <div className={styles.form}>
<AdditionalInfoForm <AdditionalInfoForm
// send refId here?
confirmationNumber={confirmationNumber} confirmationNumber={confirmationNumber}
lastName={lastName} lastName={lastName}
/> />

View File

@@ -7,8 +7,9 @@ import {
serviceProcedure, serviceProcedure,
} from "@/server/trpc" } from "@/server/trpc"
import { calculateRefId } from "@/utils/refId"
import { getHotel } from "../hotels/utils" import { getHotel } from "../hotels/utils"
import { encrypt } from "../utils/encryption"
import { import {
createRefIdInput, createRefIdInput,
getBookingInput, getBookingInput,
@@ -169,7 +170,7 @@ export const bookingQueryRouter = router({
.input(createRefIdInput) .input(createRefIdInput)
.mutation(async function ({ input }) { .mutation(async function ({ input }) {
const { confirmationNumber, lastName } = input const { confirmationNumber, lastName } = input
const encryptedRefId = encrypt(`${confirmationNumber},${lastName}`) const encryptedRefId = calculateRefId(confirmationNumber, lastName)
if (!encryptedRefId) { if (!encryptedRefId) {
throw serverErrorByStatus(422, "Was not able to encrypt ref id") throw serverErrorByStatus(422, "Was not able to encrypt ref id")

View File

@@ -4,10 +4,10 @@ import { myStay } from "@/constants/routes/myStay"
import { env } from "@/env/server" import { env } from "@/env/server"
import * as api from "@/lib/api" import * as api from "@/lib/api"
import { dt } from "@/lib/dt" import { dt } from "@/lib/dt"
import { encrypt } from "@/server/routers/utils/encryption"
import { createCounter } from "@/server/telemetry" import { createCounter } from "@/server/telemetry"
import { cache } from "@/utils/cache" import { cache } from "@/utils/cache"
import { encrypt } from "@/utils/encryption"
import * as maskValue from "@/utils/maskValue" import * as maskValue from "@/utils/maskValue"
import { isValidSession } from "@/utils/session" import { isValidSession } from "@/utils/session"
import { getCurrentWebUrl } from "@/utils/url" import { getCurrentWebUrl } from "@/utils/url"

View File

@@ -1,8 +1,4 @@
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" export interface PromosProps {
hotelId: string
export interface PromosProps refId: string
extends Pick< }
BookingConfirmation["booking"],
"confirmationNumber" | "hotelId"
>,
Pick<BookingConfirmation["booking"]["guest"], "lastName"> {}

View File

@@ -1,9 +1,14 @@
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
export interface RoomProps { export interface RoomProps {
booking: BookingConfirmation["booking"] checkInDate: BookingConfirmation["booking"]["checkInDate"]
checkOutDate: BookingConfirmation["booking"]["checkOutDate"]
checkInTime: string checkInTime: string
checkOutTime: string checkOutTime: string
confirmationNumber: string
guest: BookingConfirmation["booking"]["guest"]
guaranteeInfo: BookingConfirmation["booking"]["guaranteeInfo"]
img: NonNullable<BookingConfirmation["room"]>["images"][number] img: NonNullable<BookingConfirmation["room"]>["images"][number]
rateDefinition: BookingConfirmation["booking"]["rateDefinition"]
roomName: NonNullable<BookingConfirmation["room"]>["name"] roomName: NonNullable<BookingConfirmation["room"]>["name"]
} }

View File

@@ -0,0 +1,21 @@
import "server-only"
import { decrypt, encrypt } from "./encryption"
export function calculateRefId(confirmationNumber: string, lastName: string) {
const encryptedRefId = encrypt(`${confirmationNumber},${lastName}`)
return encryptedRefId
}
export function parseRefId(refId: string) {
const data = decrypt(refId)
const parts = data.split(",")
if (parts.length !== 2) {
throw new Error("Invalid refId format")
}
return {
confirmationNumber: parts[0],
lastName: parts[1],
}
}