Files
web/apps/scandic-web/components/HotelReservation/MyStay/Receipt/index.tsx
Linus Flood b35ceafc00 Merged in feat/SW-2903-tokens (pull request #2508)
feat(SW-2358): Use personal token if logged in

* feat(SW-2903): Use personal token if logged in

* Avoid encoding values in cookie

* Fix tests


Approved-by: Anton Gunnarsson
2025-07-08 11:24:31 +00:00

246 lines
7.6 KiB
TypeScript

import { cookies } from "next/headers"
import { notFound } from "next/navigation"
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import { dt } from "@scandic-hotels/common/dt"
import * as maskValue from "@scandic-hotels/common/utils/maskValue"
import ScandicLogoIcon from "@scandic-hotels/design-system/Icons/ScandicLogoIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { parseRefId } from "@scandic-hotels/trpc/utils/refId"
import {
findBooking,
getAncillaryPackages,
getBookingConfirmation,
getProfileSafely,
} from "@/lib/trpc/memoizedRequests"
import AdditionalInfoForm, {
type AdditionalInfoCookieValue,
} from "@/components/HotelReservation/FindMyBooking/AdditionalInfoForm"
import { getIntl } from "@/i18n"
import { isLoggedInUser } from "@/utils/isLoggedInUser"
import accessBooking, {
ACCESS_GRANTED,
ERROR_BAD_REQUEST,
ERROR_UNAUTHORIZED,
} from "../accessBooking"
import Footer from "./Footer"
import Specification from "./Specification"
import Total from "./Total"
import Tracking from "./tracking"
import styles from "./receipt.module.css"
import type { BookingConfirmation } from "@scandic-hotels/trpc/types/bookingConfirmation"
export async function Receipt({ refId }: { refId: string }) {
const { confirmationNumber, lastName } = parseRefId(refId)
if (!confirmationNumber) {
return notFound()
}
const isLoggedIn = await isLoggedInUser()
const cookieStore = await cookies()
const bv = cookieStore.get("bv")?.value
let bookingConfirmation
if (isLoggedIn) {
bookingConfirmation = await getBookingConfirmation(refId)
} else if (bv) {
const values = JSON.parse(bv) as AdditionalInfoCookieValue
const firstName = values.firstName
const email = values.email
if (firstName && email) {
bookingConfirmation = await findBooking(
confirmationNumber,
lastName,
firstName,
email
)
} else {
return (
<RenderAdditionalInfoForm
confirmationNumber={confirmationNumber}
lastName={lastName}
/>
)
}
} else {
return (
<RenderAdditionalInfoForm
confirmationNumber={confirmationNumber}
lastName={lastName}
/>
)
}
if (!bookingConfirmation) {
return notFound()
}
const { hotel, room, booking } = bookingConfirmation
const user = await getProfileSafely()
const intl = await getIntl()
const access = accessBooking(booking.guest, lastName, user, bv)
if (access === ACCESS_GRANTED) {
const ancillaryPackages = await getAncillaryPackages({
fromDate: dt(booking.checkInDate).format("YYYY-MM-DD"),
hotelId: hotel.operaId,
toDate: dt(booking.checkOutDate).format("YYYY-MM-DD"),
})
const currency =
booking.currencyCode !== CurrencyEnum.POINTS
? booking.currencyCode
: (booking.ancillaries.find((a) => a.currency !== CurrencyEnum.POINTS)
?.currency ??
booking.packages.find((p) => p.currency !== CurrencyEnum.POINTS)
?.currency)
const maskedBookingConfirmation = {
...bookingConfirmation,
booking: {
...bookingConfirmation.booking,
guest: {
...bookingConfirmation.booking.guest,
email: maskValue.email(bookingConfirmation.booking.guest.email),
phoneNumber: maskValue.phone(
bookingConfirmation.booking.guest.phoneNumber ?? ""
),
},
},
} satisfies BookingConfirmation
const { booking: maskedBooking } = maskedBookingConfirmation
return (
<main className={styles.main}>
<div>
<ScandicLogoIcon width="89px" height="19px" />
<div className={styles.addresses}>
<div>
<Typography variant="Body/Supporting text (caption)/smRegular">
<div>{hotel.name}</div>
</Typography>
<Typography variant="Body/Supporting text (caption)/smRegular">
<div>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
{`${hotel.address.streetAddress}, ${hotel.address.zipCode} ${hotel.address.city}`}
</div>
</Typography>
<Typography variant="Body/Supporting text (caption)/smRegular">
<div className={`${styles.tertiary} ${styles.addressMargin}`}>
{hotel.contactInformation.email}
</div>
</Typography>
<Typography variant="Body/Supporting text (caption)/smRegular">
<div className={styles.tertiary}>
{hotel.contactInformation.phoneNumber}
</div>
</Typography>
</div>
<div className={styles.rightColumn}>
<Typography variant="Body/Supporting text (caption)/smRegular">
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<div>{`${maskedBooking.guest.firstName} ${maskedBooking.guest.lastName}`}</div>
</Typography>
{maskedBooking.guest.membershipNumber && (
<Typography variant="Body/Supporting text (caption)/smRegular">
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<div>{`${intl.formatMessage({
defaultMessage: "Member",
})} ${maskedBooking.guest.membershipNumber}`}</div>
</Typography>
)}
<Typography variant="Body/Supporting text (caption)/smRegular">
<div className={`${styles.tertiary} ${styles.addressMargin}`}>
{maskedBooking.guest.email}
</div>
</Typography>
<Typography variant="Body/Supporting text (caption)/smRegular">
<div className={styles.tertiary}>
{maskedBooking.guest.phoneNumber}
</div>
</Typography>
</div>
</div>
</div>
<Total booking={maskedBooking} currency={currency} />
<Specification
ancillaryPackages={ancillaryPackages}
booking={maskedBooking}
currency={currency}
/>
<hr className={styles.divider} />
<Footer booking={maskedBooking} room={room} />
<Tracking />
</main>
)
}
if (access === ERROR_BAD_REQUEST) {
return (
<main className={styles.main}>
<div className={styles.form}>
<AdditionalInfoForm
confirmationNumber={confirmationNumber}
lastName={lastName}
/>
</div>
</main>
)
}
if (access === ERROR_UNAUTHORIZED) {
return (
<main className={styles.main}>
<div className={styles.logIn}>
<Typography variant="Title/md">
<h1>
{intl.formatMessage({
defaultMessage: "You need to be logged in to view your booking",
})}
</h1>
</Typography>
<Typography variant="Body/Lead text">
<p>
{intl.formatMessage({
defaultMessage:
"And you need to be logged in with the same member account that made the booking.",
})}
</p>
</Typography>
</div>
</main>
)
}
return notFound()
}
function RenderAdditionalInfoForm({
confirmationNumber,
lastName,
}: {
confirmationNumber: string
lastName: string
}) {
return (
<main className={styles.main}>
<div className={styles.form}>
<AdditionalInfoForm
confirmationNumber={confirmationNumber}
lastName={lastName}
/>
</div>
</main>
)
}