diff --git a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.tsx index ea32a277c..f7cc836ea 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.tsx +++ b/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.tsx @@ -1,20 +1,8 @@ -import { Suspense } from "react" - import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests" -import Header from "@/components/HotelReservation/BookingConfirmation/Header" -import HotelDetails from "@/components/HotelReservation/BookingConfirmation/HotelDetails" -import PaymentDetails from "@/components/HotelReservation/BookingConfirmation/PaymentDetails" -import Promos from "@/components/HotelReservation/BookingConfirmation/Promos" -import Receipt from "@/components/HotelReservation/BookingConfirmation/Receipt" -import Rooms from "@/components/HotelReservation/BookingConfirmation/Rooms" -import SidePanel from "@/components/HotelReservation/SidePanel" -import LoadingSpinner from "@/components/LoadingSpinner" -import Divider from "@/components/TempDesignSystem/Divider" +import BookingConfirmation from "@/components/HotelReservation/BookingConfirmation" import { setLang } from "@/i18n/serverContext" -import styles from "./page.module.css" - import type { LangParams, PageArgs } from "@/types/params" export default async function BookingConfirmationPage({ @@ -22,29 +10,12 @@ export default async function BookingConfirmationPage({ searchParams, }: PageArgs) { setLang(params.lang) - void getBookingConfirmation(searchParams.confirmationNumber) + const bookingConfirmationPromise = getBookingConfirmation( + searchParams.confirmationNumber + ) return ( -
- }> -
-
- - - - - -
- -
-
- - -
+ ) } diff --git a/components/HotelReservation/BookingConfirmation/Header/Actions/DownloadInvoice.tsx b/components/HotelReservation/BookingConfirmation/Header/Actions/DownloadInvoice.tsx index 3dea1f011..9fe883683 100644 --- a/components/HotelReservation/BookingConfirmation/Header/Actions/DownloadInvoice.tsx +++ b/components/HotelReservation/BookingConfirmation/Header/Actions/DownloadInvoice.tsx @@ -1,14 +1,18 @@ "use client" import { useIntl } from "react-intl" +import { useReactToPrint } from "react-to-print" import { DownloadIcon } from "@/components/Icons" import Button from "@/components/TempDesignSystem/Button" -export default function DownloadInvoice() { +import type { DownloadInvoiceProps } from "@/types/components/hotelReservation/bookingConfirmation/actions/downloadInvoice" + +export default function DownloadInvoice({ mainRef }: DownloadInvoiceProps) { const intl = useIntl() + const reactToPrintFn = useReactToPrint({ contentRef: mainRef }) function downloadBooking() { - window.print() + reactToPrintFn() } return ( diff --git a/components/HotelReservation/BookingConfirmation/Header/index.tsx b/components/HotelReservation/BookingConfirmation/Header/index.tsx index 6cb144202..0d2d08f8b 100644 --- a/components/HotelReservation/BookingConfirmation/Header/index.tsx +++ b/components/HotelReservation/BookingConfirmation/Header/index.tsx @@ -1,9 +1,9 @@ -import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests" +"use client" +import { useIntl } from "react-intl" import Link from "@/components/TempDesignSystem/Link" import Body from "@/components/TempDesignSystem/Text/Body" import Title from "@/components/TempDesignSystem/Text/Title" -import { getIntl } from "@/i18n" import AddToCalendar from "./Actions/AddToCalendar" import DownloadInvoice from "./Actions/DownloadInvoice" @@ -14,13 +14,14 @@ import styles from "./header.module.css" import type { EventAttributes } from "ics" -import type { BookingConfirmationProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation" +import type { BookingConfirmationHeaderProps } from "@/types/components/hotelReservation/bookingConfirmation/header" -export default async function Header({ - confirmationNumber, -}: BookingConfirmationProps) { - const intl = await getIntl() - const { booking, hotel } = await getBookingConfirmation(confirmationNumber) +export default function Header({ + booking, + hotel, + mainRef, +}: BookingConfirmationHeaderProps) { + const intl = useIntl() const text = intl.formatMessage( { id: "booking.confirmation.text" }, @@ -70,7 +71,7 @@ export default async function Header({ hotelName={hotel.name} /> - + ) diff --git a/components/HotelReservation/BookingConfirmation/HotelDetails/index.tsx b/components/HotelReservation/BookingConfirmation/HotelDetails/index.tsx index dcef584a5..0c1b1dd06 100644 --- a/components/HotelReservation/BookingConfirmation/HotelDetails/index.tsx +++ b/components/HotelReservation/BookingConfirmation/HotelDetails/index.tsx @@ -1,20 +1,19 @@ -import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests" +"use client" +import { useIntl } from "react-intl" import Link from "@/components/TempDesignSystem/Link" import Body from "@/components/TempDesignSystem/Text/Body" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" import { Toast } from "@/components/TempDesignSystem/Toasts" -import { getIntl } from "@/i18n" import styles from "./hotelDetails.module.css" -import type { BookingConfirmationProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation" +import type { BookingConfirmationHotelDetailsProps } from "@/types/components/hotelReservation/bookingConfirmation/hotelDetails" -export default async function HotelDetails({ - confirmationNumber, -}: BookingConfirmationProps) { - const intl = await getIntl() - const { hotel } = await getBookingConfirmation(confirmationNumber) +export default function HotelDetails({ + hotel, +}: BookingConfirmationHotelDetailsProps) { + const intl = useIntl() return (
diff --git a/components/HotelReservation/BookingConfirmation/PaymentDetails/index.tsx b/components/HotelReservation/BookingConfirmation/PaymentDetails/index.tsx index 67f327455..500c7ed74 100644 --- a/components/HotelReservation/BookingConfirmation/PaymentDetails/index.tsx +++ b/components/HotelReservation/BookingConfirmation/PaymentDetails/index.tsx @@ -1,23 +1,23 @@ +"use client" +import { useIntl } from "react-intl" + import { dt } from "@/lib/dt" -import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests" import { CreditCardAddIcon } from "@/components/Icons" import Button from "@/components/TempDesignSystem/Button" import Body from "@/components/TempDesignSystem/Text/Body" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" -import { getIntl } from "@/i18n" -import { getLang } from "@/i18n/serverContext" +import useLang from "@/hooks/useLang" import styles from "./paymentDetails.module.css" -import type { BookingConfirmationProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation" +import type { BookingConfirmationPaymentDetailsProps } from "@/types/components/hotelReservation/bookingConfirmation/paymentDetails" -export default async function PaymentDetails({ - confirmationNumber, -}: BookingConfirmationProps) { - const intl = await getIntl() - const lang = getLang() - const { booking } = await getBookingConfirmation(confirmationNumber) +export default function PaymentDetails({ + booking, +}: BookingConfirmationPaymentDetailsProps) { + const intl = useIntl() + const lang = useLang() return (
diff --git a/components/HotelReservation/BookingConfirmation/Promos/index.tsx b/components/HotelReservation/BookingConfirmation/Promos/index.tsx index d0692f917..4cf6d9076 100644 --- a/components/HotelReservation/BookingConfirmation/Promos/index.tsx +++ b/components/HotelReservation/BookingConfirmation/Promos/index.tsx @@ -1,11 +1,12 @@ -import { getIntl } from "@/i18n" +"use client" +import { useIntl } from "react-intl" import Promo from "./Promo" import styles from "./promos.module.css" -export default async function Promos() { - const intl = await getIntl() +export default function Promos() { + const intl = useIntl() return (
{intl.formatMessage({ id: "Summary" })}
- {roomAndBed.name} + {room.name} {booking.rateDefinition.isMemberRate ? (
@@ -82,9 +81,7 @@ export default async function Receipt({
- - {roomAndBed.bedType.description} - + {room.bedType.description} {intl.formatNumber(0, { currency: booking.currencyCode, diff --git a/components/HotelReservation/BookingConfirmation/Rooms/Room/index.tsx b/components/HotelReservation/BookingConfirmation/Rooms/Room/index.tsx index e721c3d8c..3d81e9511 100644 --- a/components/HotelReservation/BookingConfirmation/Rooms/Room/index.tsx +++ b/components/HotelReservation/BookingConfirmation/Rooms/Room/index.tsx @@ -1,3 +1,6 @@ +"use client" +import { useIntl } from "react-intl" + import { dt } from "@/lib/dt" import { @@ -10,16 +13,15 @@ import Link from "@/components/TempDesignSystem/Link" import Body from "@/components/TempDesignSystem/Text/Body" import Caption from "@/components/TempDesignSystem/Text/Caption" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" -import { getIntl } from "@/i18n" -import { getLang } from "@/i18n/serverContext" +import useLang from "@/hooks/useLang" import styles from "./room.module.css" -import type { RoomProps } from "@/types/components/hotelReservation/bookingConfirmation/room" +import type { RoomProps } from "@/types/components/hotelReservation/bookingConfirmation/rooms/room" -export default async function Room({ booking, img, roomName }: RoomProps) { - const intl = await getIntl() - const lang = getLang() +export default function Room({ booking, img, roomName }: RoomProps) { + const intl = useIntl() + const lang = useLang() const fromDate = dt(booking.checkInDate).locale(lang) const toDate = dt(booking.checkOutDate).locale(lang) diff --git a/components/HotelReservation/BookingConfirmation/Rooms/index.tsx b/components/HotelReservation/BookingConfirmation/Rooms/index.tsx index fdc973971..19954aec7 100644 --- a/components/HotelReservation/BookingConfirmation/Rooms/index.tsx +++ b/components/HotelReservation/BookingConfirmation/Rooms/index.tsx @@ -1,30 +1,23 @@ +"use client" + import { notFound } from "next/navigation" -import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests" - -import { getBookedHotelRoom } from "@/utils/getBookedHotelRoom" - import Room from "./Room" import styles from "./rooms.module.css" -import type { BookingConfirmationProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation" +import type { BookingConfirmationRoomsProps } from "@/types/components/hotelReservation/bookingConfirmation/rooms" -export default async function Rooms({ - confirmationNumber, -}: BookingConfirmationProps) { - const { booking, hotel } = await getBookingConfirmation(confirmationNumber) - const roomAndBed = getBookedHotelRoom(hotel, booking.roomTypeCode ?? "") - if (!roomAndBed) { +export default function Rooms({ + booking, + room, +}: BookingConfirmationRoomsProps) { + if (!room) { return notFound() } return (
- +
) } diff --git a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.module.css b/components/HotelReservation/BookingConfirmation/confirmation.module.css similarity index 99% rename from app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.module.css rename to components/HotelReservation/BookingConfirmation/confirmation.module.css index 341c3b979..bba79b2d0 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.module.css +++ b/components/HotelReservation/BookingConfirmation/confirmation.module.css @@ -39,4 +39,4 @@ display: grid; grid-area: receipt; } -} \ No newline at end of file +} diff --git a/components/HotelReservation/BookingConfirmation/index.tsx b/components/HotelReservation/BookingConfirmation/index.tsx new file mode 100644 index 000000000..c97da8908 --- /dev/null +++ b/components/HotelReservation/BookingConfirmation/index.tsx @@ -0,0 +1,57 @@ +"use client" +import { use, useRef } from "react" + +import Header from "@/components/HotelReservation/BookingConfirmation/Header" +import HotelDetails from "@/components/HotelReservation/BookingConfirmation/HotelDetails" +import PaymentDetails from "@/components/HotelReservation/BookingConfirmation/PaymentDetails" +import Promos from "@/components/HotelReservation/BookingConfirmation/Promos" +import Receipt from "@/components/HotelReservation/BookingConfirmation/Receipt" +import Rooms from "@/components/HotelReservation/BookingConfirmation/Rooms" +import SidePanel from "@/components/HotelReservation/SidePanel" +import Divider from "@/components/TempDesignSystem/Divider" + +import styles from "./confirmation.module.css" + +import type { BookingConfirmationProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation" + +export default function BookingConfirmation({ + bookingConfirmationPromise, +}: BookingConfirmationProps) { + const bookingConfirmation = use(bookingConfirmationPromise) + const mainRef = useRef(null) + return ( +
+
+
+ + + + + +
+ +
+
+ +
+ ) +} diff --git a/package-lock.json b/package-lock.json index d42a1d4e8..8fd09c24b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,6 +54,7 @@ "react-hook-form": "^7.51.2", "react-international-phone": "^4.2.6", "react-intl": "^6.6.8", + "react-to-print": "^3.0.2", "server-only": "^0.0.1", "sonner": "^1.7.0", "superjson": "^2.2.1", @@ -17417,6 +17418,14 @@ } } }, + "node_modules/react-to-print": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/react-to-print/-/react-to-print-3.0.2.tgz", + "integrity": "sha512-FS/Z4LLq0bgWaxd7obygFQ8yRFdKW74iE8fIVjFFsPJWIXmuL8CIO+4me1Hj44lrlxQ00gscSNb3BRM8olbwXg==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ~19" + } + }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", diff --git a/package.json b/package.json index 6f6f1f210..7e8af1b1e 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "react-hook-form": "^7.51.2", "react-international-phone": "^4.2.6", "react-intl": "^6.6.8", + "react-to-print": "^3.0.2", "server-only": "^0.0.1", "sonner": "^1.7.0", "superjson": "^2.2.1", diff --git a/server/routers/booking/query.ts b/server/routers/booking/query.ts index 0780549bb..3e7d1f216 100644 --- a/server/routers/booking/query.ts +++ b/server/routers/booking/query.ts @@ -8,6 +8,7 @@ import { router, serviceProcedure } from "@/server/trpc" import { getHotelData } from "../hotels/query" import { bookingConfirmationInput, getBookingStatusInput } from "./input" import { bookingConfirmationSchema, createBookingSchema } from "./output" +import { getBookedHotelRoom } from "./utils" const meter = metrics.getMeter("trpc.booking") const getBookingConfirmationCounter = meter.createCounter( @@ -144,6 +145,7 @@ export const bookingQueryRouter = router({ ...hotelData.data.attributes, included: hotelData.included, }, + room: getBookedHotelRoom(hotelData.included, booking.data.roomTypeCode), } }), status: serviceProcedure.input(getBookingStatusInput).query(async function ({ diff --git a/server/routers/booking/utils.ts b/server/routers/booking/utils.ts new file mode 100644 index 000000000..f4e5ad037 --- /dev/null +++ b/server/routers/booking/utils.ts @@ -0,0 +1,27 @@ +import { RoomData } from "@/types/hotel" +import { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" + +export function getBookedHotelRoom( + rooms: RoomData[] | undefined, + roomTypeCode: BookingConfirmation["booking"]["roomTypeCode"] +) { + if (!rooms?.length || !roomTypeCode) { + return null + } + const room = rooms?.find((r) => { + return r.roomTypes.find((roomType) => roomType.code === roomTypeCode) + }) + if (!room) { + return null + } + const bedType = room.roomTypes.find( + (roomType) => roomType.code === roomTypeCode + ) + if (!bedType) { + return null + } + return { + ...room, + bedType, + } +} diff --git a/types/components/hotelReservation/bookingConfirmation/actions/downloadInvoice.ts b/types/components/hotelReservation/bookingConfirmation/actions/downloadInvoice.ts new file mode 100644 index 000000000..6d4570893 --- /dev/null +++ b/types/components/hotelReservation/bookingConfirmation/actions/downloadInvoice.ts @@ -0,0 +1,5 @@ +import type { MutableRefObject } from "react" + +export interface DownloadInvoiceProps { + mainRef: MutableRefObject +} diff --git a/types/components/hotelReservation/bookingConfirmation/bookingConfirmation.ts b/types/components/hotelReservation/bookingConfirmation/bookingConfirmation.ts index aa178efe5..32eaf9965 100644 --- a/types/components/hotelReservation/bookingConfirmation/bookingConfirmation.ts +++ b/types/components/hotelReservation/bookingConfirmation/bookingConfirmation.ts @@ -1,3 +1,5 @@ +import type { RouterOutput } from "@/lib/trpc/client" + export interface BookingConfirmationProps { - confirmationNumber: string + bookingConfirmationPromise: Promise } diff --git a/types/components/hotelReservation/bookingConfirmation/header.ts b/types/components/hotelReservation/bookingConfirmation/header.ts new file mode 100644 index 000000000..629bcf4f1 --- /dev/null +++ b/types/components/hotelReservation/bookingConfirmation/header.ts @@ -0,0 +1,8 @@ +import type { MutableRefObject } from "react" + +import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" + +export interface BookingConfirmationHeaderProps + extends Pick { + mainRef: MutableRefObject +} diff --git a/types/components/hotelReservation/bookingConfirmation/hotelDetails.ts b/types/components/hotelReservation/bookingConfirmation/hotelDetails.ts new file mode 100644 index 000000000..f121e0aba --- /dev/null +++ b/types/components/hotelReservation/bookingConfirmation/hotelDetails.ts @@ -0,0 +1,5 @@ +import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" + +export interface BookingConfirmationHotelDetailsProps { + hotel: BookingConfirmation["hotel"] +} diff --git a/types/components/hotelReservation/bookingConfirmation/paymentDetails.ts b/types/components/hotelReservation/bookingConfirmation/paymentDetails.ts new file mode 100644 index 000000000..c85b10608 --- /dev/null +++ b/types/components/hotelReservation/bookingConfirmation/paymentDetails.ts @@ -0,0 +1,4 @@ +import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" + +export interface BookingConfirmationPaymentDetailsProps + extends Pick {} diff --git a/types/components/hotelReservation/bookingConfirmation/receipt.ts b/types/components/hotelReservation/bookingConfirmation/receipt.ts new file mode 100644 index 000000000..04824d8df --- /dev/null +++ b/types/components/hotelReservation/bookingConfirmation/receipt.ts @@ -0,0 +1,3 @@ +import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" + +export interface BookingConfirmationReceiptProps extends BookingConfirmation {} diff --git a/types/components/hotelReservation/bookingConfirmation/room.ts b/types/components/hotelReservation/bookingConfirmation/room.ts deleted file mode 100644 index 5f379c4ec..000000000 --- a/types/components/hotelReservation/bookingConfirmation/room.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { RouterOutput } from "@/lib/trpc/client" - -export interface RoomProps { - booking: RouterOutput["booking"]["confirmation"]["booking"] - img: NonNullable< - RouterOutput["booking"]["confirmation"]["hotel"]["included"] - >[number]["images"][number] - roomName: NonNullable< - RouterOutput["booking"]["confirmation"]["hotel"]["included"] - >[number]["name"] -} diff --git a/types/components/hotelReservation/bookingConfirmation/rooms.ts b/types/components/hotelReservation/bookingConfirmation/rooms.ts new file mode 100644 index 000000000..473bd18be --- /dev/null +++ b/types/components/hotelReservation/bookingConfirmation/rooms.ts @@ -0,0 +1,4 @@ +import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" + +export interface BookingConfirmationRoomsProps + extends Pick {} diff --git a/types/components/hotelReservation/bookingConfirmation/rooms/room.ts b/types/components/hotelReservation/bookingConfirmation/rooms/room.ts new file mode 100644 index 000000000..f6ccb96d6 --- /dev/null +++ b/types/components/hotelReservation/bookingConfirmation/rooms/room.ts @@ -0,0 +1,7 @@ +import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" + +export interface RoomProps { + booking: BookingConfirmation["booking"] + img: NonNullable["images"][number] + roomName: NonNullable["name"] +} diff --git a/types/trpc/routers/booking/confirmation.ts b/types/trpc/routers/booking/confirmation.ts new file mode 100644 index 000000000..7e0b580ce --- /dev/null +++ b/types/trpc/routers/booking/confirmation.ts @@ -0,0 +1,19 @@ +import { z } from "zod" + +import { bookingConfirmationSchema } from "@/server/routers/booking/output" + +import { Hotel, RoomData } from "@/types/hotel" + +export interface BookingConfirmationSchema + extends z.output {} +export interface BookingConfirmation { + booking: BookingConfirmationSchema + hotel: Hotel & { + included?: RoomData[] + } + room: + | (RoomData & { + bedType: RoomData["roomTypes"][number] + }) + | null +} diff --git a/utils/getBookedHotelRoom.ts b/utils/getBookedHotelRoom.ts deleted file mode 100644 index 45b5d65b8..000000000 --- a/utils/getBookedHotelRoom.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { RouterOutput } from "@/lib/trpc/client" - -export function getBookedHotelRoom( - hotel: RouterOutput["booking"]["confirmation"]["hotel"], - roomTypeCode: string -) { - const room = hotel.included?.find((include) => { - return include.roomTypes.find((roomType) => roomType.code === roomTypeCode) - }) - if (!room) { - return null - } - const bedType = room.roomTypes.find( - (roomType) => roomType.code === roomTypeCode - ) - if (!bedType) { - return null - } - return { - ...room, - bedType, - } -}