From ed1574838a1922d8a137b3954a76ab47353fa75f Mon Sep 17 00:00:00 2001 From: Simon Emanuelsson Date: Thu, 28 Nov 2024 14:22:31 +0100 Subject: [PATCH] feat: new booking confirmation page --- .../booking-confirmation/loading.tsx | 5 - .../booking-confirmation/page.module.css | 39 ++++- .../booking-confirmation/page.tsx | 39 ++++- .../(confirmation)/layout.module.css | 5 - .../(confirmation)/layout.tsx | 12 -- .../(confirmation)/loading.tsx | 5 - .../payment-callback/layout.tsx | 4 - .../hotelreservation/(standard)/layout.tsx | 7 - .../hotelreservation/(standard)/step/page.tsx | 22 +-- .../(public)/hotelreservation/layout.tsx | 12 ++ app/globals.css | 2 + .../Details/details.module.css | 31 ---- .../BookingConfirmation/Details/index.tsx | 61 ------- .../Header/Actions/actions.module.css | 2 +- .../Header/header.module.css | 6 + .../HotelDetails/hotelDetails.module.css | 37 +++++ .../HotelDetails/index.tsx | 74 +++++++++ .../HotelImage/image.module.css | 7 - .../BookingConfirmation/HotelImage/index.tsx | 24 --- .../PaymentDetails/index.tsx | 59 +++++++ .../PaymentDetails/paymentDetails.module.css | 18 ++ .../Promos/Promo/index.tsx | 23 +++ .../Promos/Promo/promo.module.css | 38 +++++ .../BookingConfirmation/Promos/index.tsx | 27 +++ .../Promos/promos.module.css | 12 ++ .../BookingConfirmation/Receipt/index.tsx | 143 ++++++++++++++++ .../Receipt/receipt.module.css | 40 +++++ .../BookingConfirmation/Rooms/Room/index.tsx | 138 +++++++++++++++- .../Rooms/Room/room.module.css | 96 +++++++++++ .../BookingConfirmation/Rooms/index.tsx | 29 +++- .../Rooms/rooms.module.css | 3 +- .../BookingConfirmation/Summary/index.tsx | 5 - .../Summary/summary.module.css | 4 - .../BookingConfirmation/TotalPrice/index.tsx | 136 ---------------- .../TotalPrice/totalPrice.module.css | 14 -- .../BookingConfirmation/_Summary/index.tsx | 154 ------------------ .../_Summary/summary.module.css | 31 ---- .../EnterDetails/Summary/Client.tsx | 97 ----------- .../EnterDetails/Summary/Desktop.tsx | 13 ++ .../BottomSheet/bottomSheet.module.css | 0 .../Summary/Mobile}/BottomSheet/index.tsx | 2 +- .../EnterDetails/Summary/Mobile/index.tsx | 18 ++ .../Summary/Mobile/mobile.module.css | 20 +++ .../Summary/UI}/index.tsx | 88 ++++++---- .../Summary/UI/ui.module.css} | 0 .../EnterDetails/Summary/index.tsx | 57 ------- .../HotelReservation/SidePanel/index.tsx | 19 +++ .../sidePanel.module.css} | 76 ++++----- .../HotelReservation/SidePanel/variants.ts | 15 ++ .../TempDesignSystem/Link/link.module.css | 14 +- components/TempDesignSystem/Link/variants.ts | 1 + .../Text/Subtitle/subtitle.module.css | 4 + .../Text/Subtitle/variants.ts | 1 + components/TempDesignSystem/Toasts/index.tsx | 16 +- .../TempDesignSystem/Toasts/toasts.module.css | 5 + components/TempDesignSystem/Toasts/toasts.ts | 19 ++- i18n/dictionaries/en.json | 17 ++ package-lock.json | 23 +-- package.json | 3 +- stores/enter-details/helpers.ts | 5 +- .../bookingConfirmation/promo.ts | 5 + .../bookingConfirmation/room.ts | 11 ++ .../hotelReservation/enterDetails/summary.ts | 1 - .../components/hotelReservation/sidePanel.ts | 6 + types/components/hotelReservation/summary.ts | 29 +--- utils/getBookedHotelRoom.ts | 23 +++ 66 files changed, 1127 insertions(+), 825 deletions(-) delete mode 100644 app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/loading.tsx delete mode 100644 app/[lang]/(live)/(public)/hotelreservation/(confirmation)/layout.module.css delete mode 100644 app/[lang]/(live)/(public)/hotelreservation/(confirmation)/layout.tsx delete mode 100644 app/[lang]/(live)/(public)/hotelreservation/(confirmation)/loading.tsx create mode 100644 app/[lang]/(live)/(public)/hotelreservation/layout.tsx delete mode 100644 components/HotelReservation/BookingConfirmation/Details/details.module.css delete mode 100644 components/HotelReservation/BookingConfirmation/Details/index.tsx create mode 100644 components/HotelReservation/BookingConfirmation/HotelDetails/hotelDetails.module.css create mode 100644 components/HotelReservation/BookingConfirmation/HotelDetails/index.tsx delete mode 100644 components/HotelReservation/BookingConfirmation/HotelImage/image.module.css delete mode 100644 components/HotelReservation/BookingConfirmation/HotelImage/index.tsx create mode 100644 components/HotelReservation/BookingConfirmation/PaymentDetails/index.tsx create mode 100644 components/HotelReservation/BookingConfirmation/PaymentDetails/paymentDetails.module.css create mode 100644 components/HotelReservation/BookingConfirmation/Promos/Promo/index.tsx create mode 100644 components/HotelReservation/BookingConfirmation/Promos/Promo/promo.module.css create mode 100644 components/HotelReservation/BookingConfirmation/Promos/index.tsx create mode 100644 components/HotelReservation/BookingConfirmation/Promos/promos.module.css create mode 100644 components/HotelReservation/BookingConfirmation/Receipt/index.tsx create mode 100644 components/HotelReservation/BookingConfirmation/Receipt/receipt.module.css delete mode 100644 components/HotelReservation/BookingConfirmation/Summary/index.tsx delete mode 100644 components/HotelReservation/BookingConfirmation/Summary/summary.module.css delete mode 100644 components/HotelReservation/BookingConfirmation/TotalPrice/index.tsx delete mode 100644 components/HotelReservation/BookingConfirmation/TotalPrice/totalPrice.module.css delete mode 100644 components/HotelReservation/BookingConfirmation/_Summary/index.tsx delete mode 100644 components/HotelReservation/BookingConfirmation/_Summary/summary.module.css delete mode 100644 components/HotelReservation/EnterDetails/Summary/Client.tsx create mode 100644 components/HotelReservation/EnterDetails/Summary/Desktop.tsx rename components/HotelReservation/{Summary => EnterDetails/Summary/Mobile}/BottomSheet/bottomSheet.module.css (100%) rename components/HotelReservation/{Summary => EnterDetails/Summary/Mobile}/BottomSheet/index.tsx (96%) create mode 100644 components/HotelReservation/EnterDetails/Summary/Mobile/index.tsx create mode 100644 components/HotelReservation/EnterDetails/Summary/Mobile/mobile.module.css rename components/HotelReservation/{Summary => EnterDetails/Summary/UI}/index.tsx (77%) rename components/HotelReservation/{Summary/summary.module.css => EnterDetails/Summary/UI/ui.module.css} (100%) delete mode 100644 components/HotelReservation/EnterDetails/Summary/index.tsx create mode 100644 components/HotelReservation/SidePanel/index.tsx rename components/HotelReservation/{EnterDetails/Summary/summary.module.css => SidePanel/sidePanel.module.css} (71%) create mode 100644 components/HotelReservation/SidePanel/variants.ts create mode 100644 types/components/hotelReservation/bookingConfirmation/promo.ts create mode 100644 types/components/hotelReservation/bookingConfirmation/room.ts create mode 100644 types/components/hotelReservation/sidePanel.ts create mode 100644 utils/getBookedHotelRoom.ts diff --git a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/loading.tsx b/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/loading.tsx deleted file mode 100644 index c739b6635..000000000 --- a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/loading.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import LoadingSpinner from "@/components/LoadingSpinner" - -export default function Loading() { - return -} diff --git a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.module.css b/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.module.css index bb1cf59f0..bba79b2d0 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.module.css +++ b/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.module.css @@ -1,7 +1,42 @@ .main { + background-color: var(--Base-Surface-Primary-light-Normal); + display: grid; + gap: var(--Spacing-x5); + grid-template-areas: "header" "booking"; + margin: 0 auto; + min-height: 100dvh; + padding-top: var(--Spacing-x5); + width: var(--max-width-page); +} + +.booking { display: flex; flex-direction: column; gap: var(--Spacing-x5); - margin: 0 auto; - width: min(calc(100dvw - (var(--Spacing-x3) * 2)), 948px); + grid-area: booking; + padding-bottom: var(--Spacing-x9); +} + +.aside { + display: none; +} + +@media screen and (min-width: 1367px) { + .main { + grid-template-areas: + "header receipt" + "booking receipt"; + grid-template-columns: 1fr 340px; + grid-template-rows: auto 1fr; + padding-top: var(--Spacing-x9); + } + + .mobileReceipt { + display: none; + } + + .aside { + display: grid; + grid-area: receipt; + } } 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 ab15f040c..c8096f3e6 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.tsx +++ b/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.tsx @@ -1,8 +1,16 @@ +import { Suspense } from "react" + import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests" -import Details from "@/components/HotelReservation/BookingConfirmation/Details" import Header from "@/components/HotelReservation/BookingConfirmation/Header" -import TotalPrice from "@/components/HotelReservation/BookingConfirmation/TotalPrice" +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 { setLang } from "@/i18n/serverContext" import styles from "./page.module.css" @@ -18,10 +26,27 @@ export default async function BookingConfirmationPage({ const { confirmationNumber } = searchParams return ( -
-
-
- -
+
+ }> +
+
+ + + + + +
+ +
+
+ + +
) } diff --git a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/layout.module.css b/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/layout.module.css deleted file mode 100644 index 3f89e6f51..000000000 --- a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/layout.module.css +++ /dev/null @@ -1,5 +0,0 @@ -.layout { - background-color: var(--Base-Surface-Primary-light-Normal); - min-height: 100dvh; - padding: 80px 0 160px; -} diff --git a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/layout.tsx b/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/layout.tsx deleted file mode 100644 index 5f59de245..000000000 --- a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/layout.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { notFound } from "next/navigation" - -import { env } from "@/env/server" - -import styles from "./layout.module.css" - -// route groups needed as layouts have different bgc -export default function ConfirmedBookingLayout({ - children, -}: React.PropsWithChildren) { - return
{children}
-} diff --git a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/loading.tsx b/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/loading.tsx deleted file mode 100644 index 92ff5739e..000000000 --- a/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/loading.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import LoadingSpinner from "@/components/LoadingSpinner" - -export default function Loading() { - return -} diff --git a/app/[lang]/(live)/(public)/hotelreservation/(payment-callback)/payment-callback/layout.tsx b/app/[lang]/(live)/(public)/hotelreservation/(payment-callback)/payment-callback/layout.tsx index f8fefdb5b..056db9936 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/(payment-callback)/payment-callback/layout.tsx +++ b/app/[lang]/(live)/(public)/hotelreservation/(payment-callback)/payment-callback/layout.tsx @@ -1,7 +1,3 @@ -import { notFound } from "next/navigation" - -import { env } from "@/env/server" - import styles from "./layout.module.css" import { LangParams, LayoutArgs } from "@/types/params" diff --git a/app/[lang]/(live)/(public)/hotelreservation/(standard)/layout.tsx b/app/[lang]/(live)/(public)/hotelreservation/(standard)/layout.tsx index b846b3ec8..66352fe80 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/(standard)/layout.tsx +++ b/app/[lang]/(live)/(public)/hotelreservation/(standard)/layout.tsx @@ -1,7 +1,3 @@ -import { notFound } from "next/navigation" - -import { env } from "@/env/server" - import styles from "./layout.module.css" import { LangParams, LayoutArgs } from "@/types/params" @@ -12,9 +8,6 @@ export default function HotelReservationLayout({ }: React.PropsWithChildren> & { sidePeek: React.ReactNode }) { - if (!env.ENABLE_BOOKING_FLOW) { - return notFound() - } return (
{children} diff --git a/app/[lang]/(live)/(public)/hotelreservation/(standard)/step/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/(standard)/step/page.tsx index eebfbd289..57e2bae83 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/(standard)/step/page.tsx +++ b/app/[lang]/(live)/(public)/hotelreservation/(standard)/step/page.tsx @@ -17,7 +17,8 @@ import HistoryStateManager from "@/components/HotelReservation/EnterDetails/Hist import Payment from "@/components/HotelReservation/EnterDetails/Payment" import SectionAccordion from "@/components/HotelReservation/EnterDetails/SectionAccordion" import SelectedRoom from "@/components/HotelReservation/EnterDetails/SelectedRoom" -import Summary from "@/components/HotelReservation/EnterDetails/Summary" +import DesktopSummary from "@/components/HotelReservation/EnterDetails/Summary/Desktop" +import MobileSummary from "@/components/HotelReservation/EnterDetails/Summary/Mobile" import { generateChildrenString, getQueryParamsForEnterDetails, @@ -140,6 +141,13 @@ export default async function StepPage({ } : undefined + const summary = { + cancellationText: roomAvailability.cancellationText, + isMember: !!user, + rateDetails: roomAvailability.rateDetails, + roomType: roomAvailability.selectedRoom.roomType, + } + return (
diff --git a/app/[lang]/(live)/(public)/hotelreservation/layout.tsx b/app/[lang]/(live)/(public)/hotelreservation/layout.tsx new file mode 100644 index 000000000..a8fd03992 --- /dev/null +++ b/app/[lang]/(live)/(public)/hotelreservation/layout.tsx @@ -0,0 +1,12 @@ +import { notFound } from "next/navigation" + +import { env } from "@/env/server" + +export default function HotelReservationLayout({ + children, +}: React.PropsWithChildren) { + if (!env.ENABLE_BOOKING_FLOW) { + return notFound() + } + return <>{children} +} diff --git a/app/globals.css b/app/globals.css index 486ad6cfc..a2d667892 100644 --- a/app/globals.css +++ b/app/globals.css @@ -104,6 +104,7 @@ --max-width-text-block: 49.5rem; --current-mobile-site-header-height: 70.047px; --max-width-navigation: 89.5rem; + --max-width-spacing: calc(var(--Layout-Mobile-Margin-Margin-min) * 2); --max-width-page: min( calc(100dvw - var(--max-width-spacing)), @@ -156,6 +157,7 @@ ul { :root { --max-width-spacing: calc(var(--Layout-Tablet-Margin-Margin-min) * 2); } + body.overflow-hidden { overflow: auto; overflow-x: hidden; diff --git a/components/HotelReservation/BookingConfirmation/Details/details.module.css b/components/HotelReservation/BookingConfirmation/Details/details.module.css deleted file mode 100644 index 13460ac82..000000000 --- a/components/HotelReservation/BookingConfirmation/Details/details.module.css +++ /dev/null @@ -1,31 +0,0 @@ -.details { - background-color: var(--Base-Surface-Subtle-Normal); - border-radius: var(--Corner-radius-Medium); - display: flex; - flex-direction: column; - gap: var(--Spacing-x3); - grid-area: details; - padding: var(--Spacing-x2); -} - -.list { - display: flex; - flex-direction: column; - gap: var(--Spacing-x-one-and-half); - list-style: none; - margin: 0; - padding: 0; -} - -.listItem { - align-items: center; - display: flex; - gap: var(--Spacing-x1); - justify-content: space-between; -} - -@media screen and (min-width: 768px) { - .details { - padding: var(--Spacing-x3) var(--Spacing-x3) var(--Spacing-x2); - } -} diff --git a/components/HotelReservation/BookingConfirmation/Details/index.tsx b/components/HotelReservation/BookingConfirmation/Details/index.tsx deleted file mode 100644 index 5d23e55a8..000000000 --- a/components/HotelReservation/BookingConfirmation/Details/index.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { dt } from "@/lib/dt" -import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests" - -import Body from "@/components/TempDesignSystem/Text/Body" -import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" -import { getIntl } from "@/i18n" -import { getLang } from "@/i18n/serverContext" - -import styles from "./details.module.css" - -import type { BookingConfirmationProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation" - -export default async function Details({ - confirmationNumber, -}: BookingConfirmationProps) { - const intl = await getIntl() - const lang = getLang() - const { booking } = await getBookingConfirmation(confirmationNumber) - - const fromDate = dt(booking.checkInDate).locale(lang) - const toDate = dt(booking.checkOutDate).locale(lang) - - return ( -
-
- - {intl.formatMessage( - { id: "Reference #{bookingNr}" }, - { bookingNr: booking.confirmationNumber } - )} - -
-
    -
  • - {intl.formatMessage({ id: "Check-in" })} - - {`${fromDate.format("ddd, D MMM")} ${intl.formatMessage({ id: "from" })} ${fromDate.format("HH:mm")}`} - -
  • -
  • - {intl.formatMessage({ id: "Check-out" })} - - {`${toDate.format("ddd, D MMM")} ${intl.formatMessage({ id: "from" })} ${toDate.format("HH:mm")}`} - -
  • -
  • - {intl.formatMessage({ id: "Breakfast" })} - N/A -
  • -
  • - {intl.formatMessage({ id: "Cancellation policy" })} - {booking.rateDefinition.cancellationText} -
  • -
  • - {intl.formatMessage({ id: "Rebooking" })} - N/A -
  • -
-
- ) -} diff --git a/components/HotelReservation/BookingConfirmation/Header/Actions/actions.module.css b/components/HotelReservation/BookingConfirmation/Header/Actions/actions.module.css index 5e9865b30..c54e54d13 100644 --- a/components/HotelReservation/BookingConfirmation/Header/Actions/actions.module.css +++ b/components/HotelReservation/BookingConfirmation/Header/Actions/actions.module.css @@ -2,6 +2,7 @@ border-radius: var(--Corner-radius-Medium); display: grid; grid-area: actions; + justify-content: flex-start; } @media screen and (min-width: 768px) { @@ -10,6 +11,5 @@ grid-auto-columns: auto; grid-auto-flow: column; grid-template-columns: auto; - justify-content: flex-start; } } diff --git a/components/HotelReservation/BookingConfirmation/Header/header.module.css b/components/HotelReservation/BookingConfirmation/Header/header.module.css index 57d87e49d..5468c7dbf 100644 --- a/components/HotelReservation/BookingConfirmation/Header/header.module.css +++ b/components/HotelReservation/BookingConfirmation/Header/header.module.css @@ -16,3 +16,9 @@ .body { max-width: 720px; } + +@media screen and (min-width: 1367px) { + .header { + padding-bottom: var(--Spacing-x4); + } +} diff --git a/components/HotelReservation/BookingConfirmation/HotelDetails/hotelDetails.module.css b/components/HotelReservation/BookingConfirmation/HotelDetails/hotelDetails.module.css new file mode 100644 index 000000000..484ae03e2 --- /dev/null +++ b/components/HotelReservation/BookingConfirmation/HotelDetails/hotelDetails.module.css @@ -0,0 +1,37 @@ +.contact, +.container, +.details, +.hotel { + display: flex; + flex-direction: column; +} + +.container { + gap: var(--Spacing-x4); +} + +.details { + gap: var(--Spacing-x-one-and-half); +} + +.contact, +.hotel { + gap: var(--Spacing-x-half); +} + +.coordinates { + margin-top: var(--Spacing-x-half); +} + +.toast { + align-self: flex-start; + min-width: 300px; +} + +.list { + padding-left: var(--Spacing-x2); +} + +.link { + word-break: break-all; +} diff --git a/components/HotelReservation/BookingConfirmation/HotelDetails/index.tsx b/components/HotelReservation/BookingConfirmation/HotelDetails/index.tsx new file mode 100644 index 000000000..dcef584a5 --- /dev/null +++ b/components/HotelReservation/BookingConfirmation/HotelDetails/index.tsx @@ -0,0 +1,74 @@ +import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests" + +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" + +export default async function HotelDetails({ + confirmationNumber, +}: BookingConfirmationProps) { + const intl = await getIntl() + const { hotel } = await getBookingConfirmation(confirmationNumber) + return ( +
+
+ + {intl.formatMessage({ id: "Hotel details" })} + +
+ {hotel.name} + + {hotel.address.streetAddress}, {hotel.address.zipCode}{" "} + {hotel.address.city} + + + + {hotel.contactInformation.phoneNumber} + + +
+ + {intl.formatMessage( + { id: "Long {long} ∙ Lat {lat}" }, + { + lat: hotel.location.latitude, + long: hotel.location.longitude, + } + )} + +
+
+ + {hotel.contactInformation.email} + + + {hotel.contactInformation.websiteUrl} + +
+
+ +
    +
  • N/A
  • +
+
+
+
+ ) +} diff --git a/components/HotelReservation/BookingConfirmation/HotelImage/image.module.css b/components/HotelReservation/BookingConfirmation/HotelImage/image.module.css deleted file mode 100644 index 34e5748bb..000000000 --- a/components/HotelReservation/BookingConfirmation/HotelImage/image.module.css +++ /dev/null @@ -1,7 +0,0 @@ -.imageContainer { - align-items: center; - border-radius: var(--Corner-radius-Medium); - display: flex; - grid-area: image; - justify-content: center; -} diff --git a/components/HotelReservation/BookingConfirmation/HotelImage/index.tsx b/components/HotelReservation/BookingConfirmation/HotelImage/index.tsx deleted file mode 100644 index b6d99889e..000000000 --- a/components/HotelReservation/BookingConfirmation/HotelImage/index.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests" - -import Image from "@/components/Image" - -import styles from "./image.module.css" - -import type { BookingConfirmationProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation" - -export default async function HotelImage({ - confirmationNumber, -}: BookingConfirmationProps) { - const { hotel } = await getBookingConfirmation(confirmationNumber) - return ( - - ) -} diff --git a/components/HotelReservation/BookingConfirmation/PaymentDetails/index.tsx b/components/HotelReservation/BookingConfirmation/PaymentDetails/index.tsx new file mode 100644 index 000000000..67f327455 --- /dev/null +++ b/components/HotelReservation/BookingConfirmation/PaymentDetails/index.tsx @@ -0,0 +1,59 @@ +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 styles from "./paymentDetails.module.css" + +import type { BookingConfirmationProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation" + +export default async function PaymentDetails({ + confirmationNumber, +}: BookingConfirmationProps) { + const intl = await getIntl() + const lang = getLang() + const { booking } = await getBookingConfirmation(confirmationNumber) + return ( +
+ + {intl.formatMessage({ id: "Payment details" })} + +
+ + {intl.formatNumber(booking.totalPrice, { + currency: booking.currencyCode, + style: "currency", + })}{" "} + {intl.formatMessage({ id: "has been paid" })} + + + {dt(booking.createDateTime) + .locale(lang) + .format("ddd D MMM YYYY, hh:mm")} + + + {intl.formatMessage( + { id: "{card} ending with {cardno}" }, + { card: "N/A", cardno: "N/A" } + )} + +
+ +
+ ) +} diff --git a/components/HotelReservation/BookingConfirmation/PaymentDetails/paymentDetails.module.css b/components/HotelReservation/BookingConfirmation/PaymentDetails/paymentDetails.module.css new file mode 100644 index 000000000..c8535da2f --- /dev/null +++ b/components/HotelReservation/BookingConfirmation/PaymentDetails/paymentDetails.module.css @@ -0,0 +1,18 @@ +.details, +.payment { + display: flex; + flex-direction: column; +} + +.details { + gap: var(--Spacing-x-one-and-half); +} + +.payment { + gap: var(--Spacing-x-half); +} + +.details button.btn { + align-self: flex-start; + margin-top: var(--Spacing-x-half); +} diff --git a/components/HotelReservation/BookingConfirmation/Promos/Promo/index.tsx b/components/HotelReservation/BookingConfirmation/Promos/Promo/index.tsx new file mode 100644 index 000000000..f8319c644 --- /dev/null +++ b/components/HotelReservation/BookingConfirmation/Promos/Promo/index.tsx @@ -0,0 +1,23 @@ +import Button from "@/components/TempDesignSystem/Button" +import Body from "@/components/TempDesignSystem/Text/Body" +import Title from "@/components/TempDesignSystem/Text/Title" + +import styles from "./promo.module.css" + +import type { PromoProps } from "@/types/components/hotelReservation/bookingConfirmation/promo" + +export default function Promo({ buttonText, text, title }: PromoProps) { + return ( +
+ + {title} + + + {text} + + +
+ ) +} diff --git a/components/HotelReservation/BookingConfirmation/Promos/Promo/promo.module.css b/components/HotelReservation/BookingConfirmation/Promos/Promo/promo.module.css new file mode 100644 index 000000000..ed4714228 --- /dev/null +++ b/components/HotelReservation/BookingConfirmation/Promos/Promo/promo.module.css @@ -0,0 +1,38 @@ +.promo { + align-items: center; + background-position: 50%; + background-repeat: no-repeat; + background-size: cover; + border-radius: var(--Medium, 8px); + display: flex; + flex: 1 0 320px; + flex-direction: column; + gap: var(--Spacing-x2); + height: 320px; + justify-content: center; + padding: var(--Spacing-x4) var(--Spacing-x3); +} + +.promo:nth-of-type(1) { + background-image: linear-gradient( + 180deg, + rgba(0, 0, 0, 0) 0%, + rgba(0, 0, 0, 0.36) 37.88%, + rgba(0, 0, 0, 0.75) 100% + ); + /* , url(""); uncomment and add image once we have it */ +} + +.promo:nth-of-type(2) { + background-image: linear-gradient( + 180deg, + rgba(0, 0, 0, 0) 0%, + rgba(0, 0, 0, 0.36) 37.88%, + rgba(0, 0, 0, 0.75) 100% + ); + /* , url(""); uncomment and add image once we have it */ +} + +.text { + max-width: 400px; +} diff --git a/components/HotelReservation/BookingConfirmation/Promos/index.tsx b/components/HotelReservation/BookingConfirmation/Promos/index.tsx new file mode 100644 index 000000000..d0692f917 --- /dev/null +++ b/components/HotelReservation/BookingConfirmation/Promos/index.tsx @@ -0,0 +1,27 @@ +import { getIntl } from "@/i18n" + +import Promo from "./Promo" + +import styles from "./promos.module.css" + +export default async function Promos() { + const intl = await getIntl() + return ( +
+ + +
+ ) +} diff --git a/components/HotelReservation/BookingConfirmation/Promos/promos.module.css b/components/HotelReservation/BookingConfirmation/Promos/promos.module.css new file mode 100644 index 000000000..0092c6af1 --- /dev/null +++ b/components/HotelReservation/BookingConfirmation/Promos/promos.module.css @@ -0,0 +1,12 @@ +.promos { + display: flex; + flex-direction: column; + gap: var(--Spacing-x2); + padding: var(--Spacing-x5) 0; +} + +@media screen and (min-width: 1367px) { + .promos { + flex-direction: row; + } +} diff --git a/components/HotelReservation/BookingConfirmation/Receipt/index.tsx b/components/HotelReservation/BookingConfirmation/Receipt/index.tsx new file mode 100644 index 000000000..d700474d2 --- /dev/null +++ b/components/HotelReservation/BookingConfirmation/Receipt/index.tsx @@ -0,0 +1,143 @@ +import { notFound } from "next/navigation" + +import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests" + +import { ChevronRightSmallIcon, InfoCircleIcon } from "@/components/Icons" +import Button from "@/components/TempDesignSystem/Button" +import Divider from "@/components/TempDesignSystem/Divider" +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 { getBookedHotelRoom } from "@/utils/getBookedHotelRoom" + +import styles from "./receipt.module.css" + +import type { BookingConfirmationProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation" +import { BreakfastPackageEnum } from "@/types/enums/breakfast" + +export default async function Receipt({ + confirmationNumber, +}: BookingConfirmationProps) { + const intl = await getIntl() + const { booking, hotel } = await getBookingConfirmation(confirmationNumber) + const roomAndBed = getBookedHotelRoom(hotel, booking.roomTypeCode ?? "") + if (!roomAndBed) { + return notFound() + } + + const breakfastPkgSelected = booking.packages.find( + (pkg) => pkg.code === BreakfastPackageEnum.REGULAR_BREAKFAST + ) + const breakfastPkgIncluded = booking.packages.find( + (pkg) => pkg.code === BreakfastPackageEnum.FREE_MEMBER_BREAKFAST + ) + return ( +
+ {intl.formatMessage({ id: "Summary" })} +
+
+ {roomAndBed.name} + {booking.rateDefinition.isMemberRate ? ( +
+ + N/A + + + {intl.formatNumber(booking.roomPrice, { + currency: booking.currencyCode, + style: "currency", + })} + +
+ ) : ( + + {intl.formatNumber(booking.roomPrice, { + currency: booking.currencyCode, + style: "currency", + })} + + )} + + {intl.formatMessage( + { id: "booking.adults" }, + { + totalAdults: booking.adults, + } + )} + + + {booking.rateDefinition.cancellationText} + + + {intl.formatMessage({ id: "Reservation policy" })} + + +
+
+ + {roomAndBed.bedType.description} + + + {intl.formatNumber(0, { + currency: booking.currencyCode, + style: "currency", + })} + +
+
+ {intl.formatMessage({ id: "Breakfast buffet" })} + {booking.rateDefinition.breakfastIncluded ?? breakfastPkgIncluded ? ( + {intl.formatMessage({ id: "Included" })} + ) : null} + + {breakfastPkgSelected ? ( + + {intl.formatNumber(breakfastPkgSelected.totalPrice, { + currency: breakfastPkgSelected.currency, + style: "currency", + })} + + ) : null} +
+
+ +
+
+ + {intl.formatMessage({ id: "Total price" })} + + + {intl.formatNumber(booking.totalPrice, { + currency: booking.currencyCode, + style: "currency", + })} + +
+
+ + + {intl.formatMessage({ id: "Approx." })} N/A EUR + +
+
+
+ ) +} diff --git a/components/HotelReservation/BookingConfirmation/Receipt/receipt.module.css b/components/HotelReservation/BookingConfirmation/Receipt/receipt.module.css new file mode 100644 index 000000000..3625add0e --- /dev/null +++ b/components/HotelReservation/BookingConfirmation/Receipt/receipt.module.css @@ -0,0 +1,40 @@ +.receipt { + display: flex; + flex-direction: column; + gap: var(--Spacing-x-one-and-half); +} + +.room { + display: flex; + flex-direction: column; + gap: var(--Spacing-x-one-and-half); +} + +.roomHeader { + display: grid; + grid-template-columns: 1fr auto; +} + +.roomHeader :nth-child(n + 3) { + grid-column: 1/-1; +} + +.memberPrice { + display: flex; + gap: var(--Spacing-x1); +} + +.entry { + display: flex; + justify-content: space-between; +} + +.receipt .price button.btn { + padding: 0; +} + +@media screen and (min-width: 1367px) { + .receipt { + padding: var(--Spacing-x3); + } +} diff --git a/components/HotelReservation/BookingConfirmation/Rooms/Room/index.tsx b/components/HotelReservation/BookingConfirmation/Rooms/Room/index.tsx index 0dcb32216..e721c3d8c 100644 --- a/components/HotelReservation/BookingConfirmation/Rooms/Room/index.tsx +++ b/components/HotelReservation/BookingConfirmation/Rooms/Room/index.tsx @@ -1,5 +1,139 @@ +import { dt } from "@/lib/dt" + +import { + CheckCircleIcon, + ChevronRightSmallIcon, + CrossCircle, +} from "@/components/Icons" +import Image from "@/components/Image" +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 styles from "./room.module.css" -export default function Room() { - return
+import type { RoomProps } from "@/types/components/hotelReservation/bookingConfirmation/room" + +export default async function Room({ booking, img, roomName }: RoomProps) { + const intl = await getIntl() + const lang = getLang() + + const fromDate = dt(booking.checkInDate).locale(lang) + const toDate = dt(booking.checkOutDate).locale(lang) + return ( +
+
+
+ {/* + {intl.formatMessage({ id: "Room" })} 1 + */} + + {`${intl.formatMessage({ id: "Reservation number" })} ${booking.confirmationNumber}`} + +
+
+ {booking.rateDefinition.isMemberRate ? ( + <> + + + {intl.formatMessage({ id: "Membership benefits applied" })} + + + ) : ( + <> + + + {intl.formatMessage({ id: "No membership benefits applied" })} + + + )} +
+
+
+ {img.metaData.altText} +
+
+ + {roomName} + + + {intl.formatMessage({ id: "View room details" })} + + +
+
    +
  • + + {intl.formatMessage({ id: "Check-in" })} + + + {`${fromDate.format("ddd, D MMM")} ${intl.formatMessage({ id: "from" })} ${fromDate.format("HH:mm")}`} + +
  • +
  • + + {intl.formatMessage({ id: "Check-out" })} + + + {`${toDate.format("ddd, D MMM")} ${intl.formatMessage({ id: "from" })} ${toDate.format("HH:mm")}`} + +
  • +
  • + + {intl.formatMessage({ id: "Breakfast" })} + + N/A +
  • +
  • + + {intl.formatMessage({ id: "Cancellation policy" })} + + + {booking.rateDefinition.cancellationText} + +
  • +
  • + + {intl.formatMessage({ id: "Rebooking" })} + + N/A +
  • +
+
+ + {intl.formatMessage({ id: "Main guest" })} + + + {`${booking.guest.firstName} ${booking.guest.lastName}`} + + {booking.guest.membershipNumber ? ( + + {`${intl.formatMessage({ id: "Friend no." })} ${booking.guest.membershipNumber}`} + + ) : null} + {booking.guest.phoneNumber ? ( + + {booking.guest.phoneNumber} + + ) : null} + {booking.guest.email ? ( + {booking.guest.email} + ) : null} +
+
+
+
+ ) } diff --git a/components/HotelReservation/BookingConfirmation/Rooms/Room/room.module.css b/components/HotelReservation/BookingConfirmation/Rooms/Room/room.module.css index e69de29bb..d93ab691c 100644 --- a/components/HotelReservation/BookingConfirmation/Rooms/Room/room.module.css +++ b/components/HotelReservation/BookingConfirmation/Rooms/Room/room.module.css @@ -0,0 +1,96 @@ +.room { + display: flex; + flex-direction: column; + gap: var(--Spacing-x2); +} + +.header { + align-items: flex-end; + display: grid; + gap: var(--Spacing-x2); + grid-template-columns: 1fr; +} + +.benefits { + align-items: center; + border: 1px solid var(--Base-Border-Subtle); + border-radius: var(--Corner-radius-Medium); + display: flex; + gap: var(--Spacing-x1); + padding: var(--Spacing-x1) var(--Spacing-x-one-and-half); + width: max-content; +} + +.booking { + background-color: var(--Base-Background-Primary-Normal); + border-radius: var(--Corner-radius-Large); + display: grid; + gap: var(--Spacing-x2); + padding: var(--Spacing-x2) var(--Spacing-x2) var(--Spacing-x3) + var(--Spacing-x2); +} + +.img { + width: 100%; +} + +.roomDetails { + display: grid; + gap: var(--Spacing-x2); +} + +.roomName { + display: flex; + flex-direction: column; + gap: var(--Spacing-x-half); + grid-column: 1/-1; +} + +.details { + display: grid; + gap: var(--Spacing-x-half) var(--Spacing-x3); + list-style: none; +} + +.listItem { + display: flex; + justify-content: space-between; +} + +.guest { + display: flex; + flex-direction: column; + gap: var(--Spacing-x-half); +} + +@media screen and (max-width: 1366px) { + .details { + padding-bottom: var(--Spacing-x1); + } + + .details p:nth-of-type(even) { + text-align: right; + } +} + +@media screen and (min-width: 1367px) { + .header { + grid-template-columns: 1fr auto; + } + + .booking { + gap: var(--Spacing-x3); + grid-template-columns: auto 1fr; + padding: var(--Spacing-x2) var(--Spacing-x3) var(--Spacing-x2) + var(--Spacing-x2); + } + + .roomDetails { + grid-template-columns: 1fr 1fr; + } + + .guest { + align-items: flex-end; + align-self: flex-end; + } +} diff --git a/components/HotelReservation/BookingConfirmation/Rooms/index.tsx b/components/HotelReservation/BookingConfirmation/Rooms/index.tsx index 871dbc4e4..fdc973971 100644 --- a/components/HotelReservation/BookingConfirmation/Rooms/index.tsx +++ b/components/HotelReservation/BookingConfirmation/Rooms/index.tsx @@ -1,5 +1,30 @@ +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" -export default function Rooms() { - return
+import type { BookingConfirmationProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation" + +export default async function Rooms({ + confirmationNumber, +}: BookingConfirmationProps) { + const { booking, hotel } = await getBookingConfirmation(confirmationNumber) + const roomAndBed = getBookedHotelRoom(hotel, booking.roomTypeCode ?? "") + if (!roomAndBed) { + return notFound() + } + return ( +
+ +
+ ) } diff --git a/components/HotelReservation/BookingConfirmation/Rooms/rooms.module.css b/components/HotelReservation/BookingConfirmation/Rooms/rooms.module.css index 9713547bf..b5b084c16 100644 --- a/components/HotelReservation/BookingConfirmation/Rooms/rooms.module.css +++ b/components/HotelReservation/BookingConfirmation/Rooms/rooms.module.css @@ -1,6 +1,5 @@ .rooms { display: flex; flex-direction: column; - gap: var(--Spacing-x9); - grid-area: booking; + gap: var(--Spacing-x5); } diff --git a/components/HotelReservation/BookingConfirmation/Summary/index.tsx b/components/HotelReservation/BookingConfirmation/Summary/index.tsx deleted file mode 100644 index ce95e08bb..000000000 --- a/components/HotelReservation/BookingConfirmation/Summary/index.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import styles from "./summary.module.css" - -export default function Summary() { - return -} diff --git a/components/HotelReservation/BookingConfirmation/Summary/summary.module.css b/components/HotelReservation/BookingConfirmation/Summary/summary.module.css deleted file mode 100644 index 8d8015b28..000000000 --- a/components/HotelReservation/BookingConfirmation/Summary/summary.module.css +++ /dev/null @@ -1,4 +0,0 @@ -.summary { - background-color: hotpink; - grid-area: summary; -} diff --git a/components/HotelReservation/BookingConfirmation/TotalPrice/index.tsx b/components/HotelReservation/BookingConfirmation/TotalPrice/index.tsx deleted file mode 100644 index d7ee0ff11..000000000 --- a/components/HotelReservation/BookingConfirmation/TotalPrice/index.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests" - -import { - CoffeeIcon, - DiscountIcon, - DoorClosedIcon, - PriceTagIcon, -} from "@/components/Icons" -import Divider from "@/components/TempDesignSystem/Divider" -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 styles from "./totalPrice.module.css" - -import type { BookingConfirmationProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation" -import { BreakfastPackageEnum } from "@/types/enums/breakfast" - -export default async function TotalPrice({ - confirmationNumber, -}: BookingConfirmationProps) { - const intl = await getIntl() - const { booking } = await getBookingConfirmation(confirmationNumber) - - const totalPrice = intl.formatNumber(booking.totalPrice, { - currency: booking.currencyCode, - style: "currency", - }) - const breakfastPackage = booking.packages.find( - (p) => p.code === BreakfastPackageEnum.REGULAR_BREAKFAST - ) - return ( -
-
- - {intl.formatMessage({ id: "Total price" })} - - - {totalPrice} (~ EUR) - -
-
-
- - - {`${intl.formatMessage({ id: "Room" })}, ${intl.formatMessage({ id: "booking.nights" }, { totalNights: 1 })}`} - - {totalPrice} -
-
- - - {intl.formatMessage({ id: "Breakfast" })} - - - {breakfastPackage - ? intl.formatNumber(breakfastPackage.totalPrice, { - currency: breakfastPackage.currency, - style: "currency", - }) - : intl.formatMessage({ id: "No breakfast" })} - -
-
- - - {intl.formatMessage({ id: "Member discount" })} - - N/A -
-
- - - {intl.formatMessage({ id: "Points used" })} - - N/A -
-
- -
-
- - {intl.formatMessage({ id: "Price excl VAT" })} - - - {intl.formatNumber(booking.totalPriceExVat, { - currency: booking.currencyCode, - style: "currency", - })} - -
-
- - {intl.formatMessage({ id: "VAT" })} - - {booking.vatPercentage}% -
-
- - {intl.formatMessage({ id: "VAT amount" })} - - - {intl.formatNumber(booking.vatAmount, { - currency: booking.currencyCode, - style: "currency", - })} - -
-
- - {intl.formatMessage({ id: "Price incl VAT" })} - - - {intl.formatNumber(booking.totalPrice, { - currency: booking.currencyCode, - style: "currency", - })} - -
-
- - {intl.formatMessage({ id: "Payment method" })} - - N/A -
-
- - {intl.formatMessage({ id: "Payment status" })} - - N/A -
-
-
- ) -} diff --git a/components/HotelReservation/BookingConfirmation/TotalPrice/totalPrice.module.css b/components/HotelReservation/BookingConfirmation/TotalPrice/totalPrice.module.css deleted file mode 100644 index 0bb8cf70a..000000000 --- a/components/HotelReservation/BookingConfirmation/TotalPrice/totalPrice.module.css +++ /dev/null @@ -1,14 +0,0 @@ -.container { - background-color: var(--Base-Background-Primary-Normal); - border-radius: var(--Corner-radius-Large); - display: flex; - flex-direction: column; - gap: var(--Spacing-x3); - padding: var(--Spacing-x2) var(--Spacing-x3) var(--Spacing-x3); -} - -.items { - display: grid; - gap: var(--Spacing-x3) var(--Spacing-x1); - grid-template-columns: repeat(4, minmax(100px, 1fr)); -} diff --git a/components/HotelReservation/BookingConfirmation/_Summary/index.tsx b/components/HotelReservation/BookingConfirmation/_Summary/index.tsx deleted file mode 100644 index 05800f2d2..000000000 --- a/components/HotelReservation/BookingConfirmation/_Summary/index.tsx +++ /dev/null @@ -1,154 +0,0 @@ -import { profile } from "@/constants/routes/myPages" -import { dt } from "@/lib/dt" -import { - getBookingConfirmation, - getProfileSafely, -} from "@/lib/trpc/memoizedRequests" - -import { CreditCardAddIcon, EditIcon, PersonIcon } from "@/components/Icons" -import Divider from "@/components/TempDesignSystem/Divider" -import Link from "@/components/TempDesignSystem/Link" -import Body from "@/components/TempDesignSystem/Text/Body" -import Caption from "@/components/TempDesignSystem/Text/Caption" -import { getIntl } from "@/i18n" -import { getLang } from "@/i18n/serverContext" - -import styles from "./summary.module.css" - -import type { BookingConfirmationProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation" -import { BreakfastPackageEnum } from "@/types/enums/breakfast" - -export default async function Summary({ - confirmationNumber, -}: BookingConfirmationProps) { - const intl = await getIntl() - const lang = getLang() - const { booking, hotel } = await getBookingConfirmation(confirmationNumber) - const user = await getProfileSafely() - const { firstName, lastName } = booking.guest - const membershipNumber = user?.membership?.membershipNumber - const totalNights = dt(booking.checkOutDate.setHours(0, 0, 0)).diff( - dt(booking.checkInDate.setHours(0, 0, 0)), - "days" - ) - - const breakfastPackage = booking.packages.find( - (pkg) => pkg.code === BreakfastPackageEnum.REGULAR_BREAKFAST - ) - return ( -
-
-
- - {intl.formatMessage({ id: "Guest" })} - - {`${firstName} ${lastName}`} - {membershipNumber ? ( - - {intl.formatMessage( - { id: "membership.no" }, - { membershipNumber } - )} - - ) : null} - {booking.guest.email} - {booking.guest.phoneNumber} -
- {user ? ( - - - - {intl.formatMessage({ id: "Go to profile" })} - - - ) : null} -
- -
-
- - {intl.formatMessage({ id: "Payment" })} - - - {intl.formatMessage( - { id: "guest.paid" }, - { - amount: intl.formatNumber(booking.totalPrice), - currency: booking.currencyCode, - } - )} - - Date information N/A - Card information N/A -
- {/* # href until more info */} - {user ? ( - - - - {intl.formatMessage({ id: "Save card to profile" })} - - - ) : null} -
- -
-
- - {intl.formatMessage({ id: "Booking" })} - - - N/A, {intl.formatMessage({ id: "booking.nights" }, { totalNights })} - ,{" "} - {intl.formatMessage( - { id: "booking.adults" }, - { totalAdults: booking.adults } - )} - - {breakfastPackage ? ( - - {intl.formatMessage({ id: "Breakfast added" })} - - ) : null} - Bedtype N/A -
- {/* # href until more info */} - - - - {intl.formatMessage({ id: "Manage booking" })} - - -
- -
-
- - {intl.formatMessage({ id: "Hotel" })} - - {hotel.name} - - {`${hotel.address.streetAddress}, ${hotel.address.zipCode} ${hotel.address.city}`} - - - {hotel.contactInformation.phoneNumber} - - - {`${intl.formatMessage({ id: "Longitude" }, { long: hotel.location.longitude })} ∙ ${intl.formatMessage({ id: "Latitude" }, { lat: hotel.location.latitude })}`} - -
-
- - {hotel.contactInformation.websiteUrl} - - - {hotel.contactInformation.email} - -
-
-
- ) -} diff --git a/components/HotelReservation/BookingConfirmation/_Summary/summary.module.css b/components/HotelReservation/BookingConfirmation/_Summary/summary.module.css deleted file mode 100644 index f7f4e6635..000000000 --- a/components/HotelReservation/BookingConfirmation/_Summary/summary.module.css +++ /dev/null @@ -1,31 +0,0 @@ -.summary { - display: grid; - gap: var(--Spacing-x3); -} - -.container, -.textContainer { - display: flex; - flex-direction: column; -} - -.container { - gap: var(--Spacing-x-one-and-half); -} - -.textContainer { - gap: var(--Spacing-x-half); -} - -.container .textContainer .latLong { - padding-top: var(--Spacing-x1); -} - -.hotelLinks { - display: flex; - flex-direction: column; -} - -.summary .container .link { - gap: var(--Spacing-x1); -} diff --git a/components/HotelReservation/EnterDetails/Summary/Client.tsx b/components/HotelReservation/EnterDetails/Summary/Client.tsx deleted file mode 100644 index 1fe83bc57..000000000 --- a/components/HotelReservation/EnterDetails/Summary/Client.tsx +++ /dev/null @@ -1,97 +0,0 @@ -"use client" - -import { useEnterDetailsStore } from "@/stores/enter-details" - -import Summary from "@/components/HotelReservation/Summary" -import { SummaryBottomSheet } from "@/components/HotelReservation/Summary/BottomSheet" - -import styles from "./summary.module.css" - -import type { ClientSummaryProps } from "@/types/components/hotelReservation/enterDetails/summary" -import type { DetailsState } from "@/types/stores/enter-details" - -function storeSelector(state: DetailsState) { - return { - bedType: state.bedType, - breakfast: state.breakfast, - fromDate: state.booking.fromDate, - join: state.guest.join, - membershipNo: state.guest.membershipNo, - packages: state.packages, - roomRate: state.roomRate, - roomPrice: state.roomPrice, - toDate: state.booking.toDate, - toggleSummaryOpen: state.actions.toggleSummaryOpen, - totalPrice: state.totalPrice, - } -} - -export default function ClientSummary({ - adults, - cancellationText, - isMember, - kids, - memberRate, - rateDetails, - roomType, -}: ClientSummaryProps) { - const { - bedType, - breakfast, - fromDate, - join, - membershipNo, - packages, - roomPrice, - toDate, - toggleSummaryOpen, - totalPrice, - } = useEnterDetailsStore(storeSelector) - - const showMemberPrice = !!(isMember && memberRate) || join || !!membershipNo - const room = { - adults, - cancellationText, - children: kids, - packages, - rateDetails, - roomPrice, - roomType, - } - - return ( - <> -
- -
- -
-
-
-
-
-
- -
-
-
- - ) -} diff --git a/components/HotelReservation/EnterDetails/Summary/Desktop.tsx b/components/HotelReservation/EnterDetails/Summary/Desktop.tsx new file mode 100644 index 000000000..3c90539b4 --- /dev/null +++ b/components/HotelReservation/EnterDetails/Summary/Desktop.tsx @@ -0,0 +1,13 @@ +import SidePanel from "@/components/HotelReservation/SidePanel" + +import SummaryUI from "./UI" + +import type { SummaryProps } from "@/types/components/hotelReservation/summary" + +export default function DesktopSummary(props: SummaryProps) { + return ( + + + + ) +} diff --git a/components/HotelReservation/Summary/BottomSheet/bottomSheet.module.css b/components/HotelReservation/EnterDetails/Summary/Mobile/BottomSheet/bottomSheet.module.css similarity index 100% rename from components/HotelReservation/Summary/BottomSheet/bottomSheet.module.css rename to components/HotelReservation/EnterDetails/Summary/Mobile/BottomSheet/bottomSheet.module.css diff --git a/components/HotelReservation/Summary/BottomSheet/index.tsx b/components/HotelReservation/EnterDetails/Summary/Mobile/BottomSheet/index.tsx similarity index 96% rename from components/HotelReservation/Summary/BottomSheet/index.tsx rename to components/HotelReservation/EnterDetails/Summary/Mobile/BottomSheet/index.tsx index 085325a13..4041b4fd8 100644 --- a/components/HotelReservation/Summary/BottomSheet/index.tsx +++ b/components/HotelReservation/EnterDetails/Summary/Mobile/BottomSheet/index.tsx @@ -12,7 +12,7 @@ import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" import styles from "./bottomSheet.module.css" -export function SummaryBottomSheet({ children }: PropsWithChildren) { +export default function SummaryBottomSheet({ children }: PropsWithChildren) { const intl = useIntl() const { isSummaryOpen, toggleSummaryOpen, totalPrice, isSubmittingDisabled } = diff --git a/components/HotelReservation/EnterDetails/Summary/Mobile/index.tsx b/components/HotelReservation/EnterDetails/Summary/Mobile/index.tsx new file mode 100644 index 000000000..07f18807e --- /dev/null +++ b/components/HotelReservation/EnterDetails/Summary/Mobile/index.tsx @@ -0,0 +1,18 @@ +import SummaryUI from "../UI" +import SummaryBottomSheet from "./BottomSheet" + +import styles from "./mobile.module.css" + +import type { SummaryProps } from "@/types/components/hotelReservation/summary" + +export default function MobileSummary(props: SummaryProps) { + return ( +
+ +
+ +
+
+
+ ) +} diff --git a/components/HotelReservation/EnterDetails/Summary/Mobile/mobile.module.css b/components/HotelReservation/EnterDetails/Summary/Mobile/mobile.module.css new file mode 100644 index 000000000..30b077e92 --- /dev/null +++ b/components/HotelReservation/EnterDetails/Summary/Mobile/mobile.module.css @@ -0,0 +1,20 @@ +.mobileSummary { + display: block; +} + +@media screen and (max-width: 1366px) { + .wrapper { + background-color: var(--Main-Grey-White); + border-color: var(--Primary-Light-On-Surface-Divider-subtle); + border-style: solid; + border-width: 1px; + border-bottom: none; + z-index: 10; + } +} + +@media screen and (min-width: 1367px) { + .mobileSummary { + display: none; + } +} diff --git a/components/HotelReservation/Summary/index.tsx b/components/HotelReservation/EnterDetails/Summary/UI/index.tsx similarity index 77% rename from components/HotelReservation/Summary/index.tsx rename to components/HotelReservation/EnterDetails/Summary/UI/index.tsx index 1b309362a..3b7705990 100644 --- a/components/HotelReservation/Summary/index.tsx +++ b/components/HotelReservation/EnterDetails/Summary/UI/index.tsx @@ -2,6 +2,7 @@ import { useIntl } from "react-intl" import { dt } from "@/lib/dt" +import { useEnterDetailsStore } from "@/stores/enter-details" import { ArrowRightIcon, ChevronDownSmallIcon } from "@/components/Icons" import Button from "@/components/TempDesignSystem/Button" @@ -13,25 +14,58 @@ import Caption from "@/components/TempDesignSystem/Text/Caption" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" import useLang from "@/hooks/useLang" -import styles from "./summary.module.css" +import styles from "./ui.module.css" import type { SummaryProps } from "@/types/components/hotelReservation/summary" import { CurrencyEnum } from "@/types/enums/currency" +import type { DetailsState } from "@/types/stores/enter-details" -export default function Summary({ - bedType, - breakfast, - fromDate, - showMemberPrice, - room, - toDate, - toggleSummaryOpen, - totalPrice, +export function storeSelector(state: DetailsState) { + return { + bedType: state.bedType, + booking: state.booking, + breakfast: state.breakfast, + join: state.guest.join, + membershipNo: state.guest.membershipNo, + packages: state.packages, + roomRate: state.roomRate, + roomPrice: state.roomPrice, + toggleSummaryOpen: state.actions.toggleSummaryOpen, + totalPrice: state.totalPrice, + } +} + +export default function SummaryUI({ + cancellationText, + isMember, + rateDetails, + roomType, }: SummaryProps) { const intl = useIntl() const lang = useLang() - const diff = dt(toDate).diff(fromDate, "days") + const { + bedType, + booking, + breakfast, + join, + membershipNo, + packages, + roomPrice, + roomRate, + toggleSummaryOpen, + totalPrice, + } = useEnterDetailsStore(storeSelector) + + const adults = booking.rooms[0].adults + const children = booking.rooms[0].children + + const showMemberPrice = !!( + (isMember || join || membershipNo) && + roomRate.memberRate + ) + + const diff = dt(booking.toDate).diff(booking.fromDate, "days") const nights = intl.formatMessage( { id: "booking.nights" }, @@ -51,9 +85,9 @@ export default function Summary({ {intl.formatMessage({ id: "Summary" })} - {dt(fromDate).locale(lang).format("ddd, D MMM")} + {dt(booking.fromDate).locale(lang).format("ddd, D MMM")} - {dt(toDate).locale(lang).format("ddd, D MMM")} ({nights}) + {dt(booking.toDate).locale(lang).format("ddd, D MMM")} ({nights}) + {message ? ( + {message} + ) : ( +
{children}
+ )} + {onClose ? ( + + ) : null}
) } diff --git a/components/TempDesignSystem/Toasts/toasts.module.css b/components/TempDesignSystem/Toasts/toasts.module.css index d49b8d57e..e5f811cb4 100644 --- a/components/TempDesignSystem/Toasts/toasts.module.css +++ b/components/TempDesignSystem/Toasts/toasts.module.css @@ -8,6 +8,11 @@ align-items: center; } +.content { + padding: var(--Spacing-x-one-and-half) var(--Spacing-x3) + var(--Spacing-x-one-and-half) var(--Spacing-x2); +} + @media screen and (min-width: 768px) { .toast { width: var(--width); diff --git a/components/TempDesignSystem/Toasts/toasts.ts b/components/TempDesignSystem/Toasts/toasts.ts index 573f50a1f..1bf37d114 100644 --- a/components/TempDesignSystem/Toasts/toasts.ts +++ b/components/TempDesignSystem/Toasts/toasts.ts @@ -2,9 +2,16 @@ import { toastVariants } from "./variants" import type { VariantProps } from "class-variance-authority" -export interface ToastsProps - extends Omit, "color">, - VariantProps { - message: React.ReactNode - onClose: () => void -} +export type ToastsProps = Omit, "color"> & + VariantProps & { + onClose?: () => void + } & ( + | { + children: React.ReactNode + message?: never + } + | { + children?: never + message: React.ReactNode + } + ) diff --git a/i18n/dictionaries/en.json b/i18n/dictionaries/en.json index ca0093717..3a89e05b9 100644 --- a/i18n/dictionaries/en.json +++ b/i18n/dictionaries/en.json @@ -48,7 +48,9 @@ "Bed type": "Bed type", "Birth date": "Birth date", "Book": "Book", + "Book another stay": "Book another stay", "Book reward night": "Book reward night", + "Book your next stay": "Book your next stay", "Booking": "Booking", "Booking number": "Booking number", "Breakfast": "Breakfast", @@ -114,6 +116,7 @@ "Disabled booking options text": "Codes, cheques and reward nights aren't available on the new website yet.", "Discard changes": "Discard changes", "Discard unsaved changes?": "Discard unsaved changes?", + "Discover the little extra touches to make your upcoming stay even more unforgettable.": "Discover the little extra touches to make your upcoming stay even more unforgettable.", "Distance in km to city centre": "{number} km to city centre", "Distance to city centre": "Distance to city centre", "Distance to hotel": "Distance to hotel: {distance} m", @@ -152,9 +155,11 @@ "Free parking": "Free parking", "Free rebooking": "Free rebooking", "Free until": "Free until", + "Friend no.": "Friend no.", "From": "From", "Garage": "Garage", "Get inspired": "Get inspired", + "Get inspired and start dreaming beyond your next trip. Explore more Scandic destinations.": "Get inspired and start dreaming beyond your next trip. Explore more Scandic destinations.", "Get member benefits & offers": "Get member benefits & offers", "Gift(s) added to your benefits": "{amount, plural, one {Gift} other {Gifts}} added to your benefits", "Go back to edit": "Go back to edit", @@ -171,6 +176,7 @@ "Home": "Home", "Hospital": "Hospital", "Hotel": "Hotel", + "Hotel details": "Hotel details", "Hotel facilities": "Hotel facilities", "Hotel reservation": "Hotel reservation", "Hotel surroundings": "Hotel surroundings", @@ -213,8 +219,10 @@ "Log in here": "Log in here", "Log in/Join": "Log in/Join", "Log out": "Log out", + "Long {long} ∙ Lat {lat}": "Long {long} ∙ Lat {lat}", "Longitude": "Longitude {long}", "MY SAVED CARDS": "MY SAVED CARDS", + "Main guest": "Main guest", "Main menu": "Main menu", "Manage booking": "Manage booking", "Manage preferences": "Manage preferences", @@ -228,6 +236,7 @@ "Members": "Members", "Membership ID": "Membership ID", "Membership ID copied to clipboard": "Membership ID copied to clipboard", + "Membership benefits applied": "Membership benefits applied", "Membership cards": "Membership cards", "Membership no": "Membership no", "Membership terms and conditions": "Membership terms and conditions", @@ -253,6 +262,7 @@ "No breakfast": "No breakfast", "No content published": "No content published", "No matching location found": "No matching location found", + "No membership benefits applied": "No membership benefits applied", "No prices available": "No prices available", "No results": "No results", "No transactions available": "No transactions available", @@ -285,6 +295,7 @@ "Pay now": "Pay now", "Payment": "Payment", "Payment Guarantee": "Payment Guarantee", + "Payment details": "Payment details", "Payment info": "Payment info", "Payment method": "Payment method", "Payment received": "Payment received", @@ -331,6 +342,8 @@ "Relax": "Relax", "Remove card from member profile": "Remove card from member profile", "Request bedtype": "Request bedtype", + "Reservation number": "Reservation number", + "Reservation policy": "Reservation policy", "Restaurant": "{count, plural, one {#Restaurant} other {#Restaurants}}", "Restaurant & Bar": "Restaurant & Bar", "Restaurants & Bars": "Restaurants & Bars", @@ -386,6 +399,7 @@ "Something went wrong and we couldn't remove your card. Please try again later.": "Something went wrong and we couldn't remove your card. Please try again later.", "Something went wrong!": "Something went wrong!", "Sort by": "Sort by", + "Spice things up": "Spice things up", "Sports": "Sports", "Standard price": "Standard price", "Stay at HOTEL_NAME | Hotel in DESTINATION": "Stay at {hotelName} | Hotel in {destination}", @@ -424,8 +438,10 @@ "User information": "User information", "VAT": "VAT", "VAT amount": "VAT amount", + "View and buy add-ons": "View and buy add-ons", "View as list": "View as list", "View as map": "View as map", + "View room details": "View room details", "View terms": "View terms", "View your booking": "View your booking", "Visiting address": "Visiting address", @@ -498,6 +514,7 @@ "guest": "guest", "guest.paid": "{amount} {currency} has been paid", "guests": "guests", + "has been paid": "has been paid", "hotelPages.rooms.roomCard.persons": "{size} ({totalOccupancy, plural, one {# person} other {# persons}})", "hotelPages.rooms.roomCard.seeRoomDetails": "See room details", "km to city center": "km to city center", diff --git a/package-lock.json b/package-lock.json index 2a2f3b61a..b4509615e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,6 +35,7 @@ "dayjs": "^1.11.10", "deepmerge": "^4.3.1", "downshift": "^9.0.8", + "fast-deep-equal": "^3.1.3", "fetch-retry": "^6.0.0", "framer-motion": "^11.3.28", "graphql": "^16.8.1", @@ -43,7 +44,6 @@ "immer": "10.1.1", "json-stable-stringify-without-jsonify": "^1.0.1", "libphonenumber-js": "^1.10.60", - "lodash.isequal": "^4.5.0", "next": "^14.2.18", "next-auth": "^5.0.0-beta.19", "react": "^18", @@ -67,7 +67,6 @@ "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", "@types/json-stable-stringify-without-jsonify": "^1.0.2", - "@types/lodash.isequal": "^4.5.8", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", @@ -6866,21 +6865,6 @@ "@types/node": "*" } }, - "node_modules/@types/lodash": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.13.tgz", - "integrity": "sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==", - "dev": true - }, - "node_modules/@types/lodash.isequal": { - "version": "4.5.8", - "resolved": "https://registry.npmjs.org/@types/lodash.isequal/-/lodash.isequal-4.5.8.tgz", - "integrity": "sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==", - "dev": true, - "dependencies": { - "@types/lodash": "*" - } - }, "node_modules/@types/node": { "version": "20.12.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", @@ -15001,11 +14985,6 @@ "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", "integrity": "sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==" }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" - }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", diff --git a/package.json b/package.json index b6e68c685..efb824197 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "dayjs": "^1.11.10", "deepmerge": "^4.3.1", "downshift": "^9.0.8", + "fast-deep-equal": "^3.1.3", "fetch-retry": "^6.0.0", "framer-motion": "^11.3.28", "graphql": "^16.8.1", @@ -58,7 +59,6 @@ "immer": "10.1.1", "json-stable-stringify-without-jsonify": "^1.0.1", "libphonenumber-js": "^1.10.60", - "lodash.isequal": "^4.5.0", "next": "^14.2.18", "next-auth": "^5.0.0-beta.19", "react": "^18", @@ -82,7 +82,6 @@ "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", "@types/json-stable-stringify-without-jsonify": "^1.0.2", - "@types/lodash.isequal": "^4.5.8", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", diff --git a/stores/enter-details/helpers.ts b/stores/enter-details/helpers.ts index 567e96334..496b75ba6 100644 --- a/stores/enter-details/helpers.ts +++ b/stores/enter-details/helpers.ts @@ -1,13 +1,10 @@ -import isEqual from "lodash.isequal" -import { z } from "zod" +import isEqual from "fast-deep-equal" import { Lang } from "@/constants/languages" -import { breakfastPackageSchema } from "@/server/routers/hotels/output" import { getLang } from "@/i18n/serverContext" import type { BookingData } from "@/types/components/hotelReservation/enterDetails/bookingData" -import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter" import { CurrencyEnum } from "@/types/enums/currency" import { StepEnum } from "@/types/enums/step" import type { DetailsState, RoomRate } from "@/types/stores/enter-details" diff --git a/types/components/hotelReservation/bookingConfirmation/promo.ts b/types/components/hotelReservation/bookingConfirmation/promo.ts new file mode 100644 index 000000000..22cf695d8 --- /dev/null +++ b/types/components/hotelReservation/bookingConfirmation/promo.ts @@ -0,0 +1,5 @@ +export interface PromoProps { + buttonText: string + text: string + title: string +} diff --git a/types/components/hotelReservation/bookingConfirmation/room.ts b/types/components/hotelReservation/bookingConfirmation/room.ts new file mode 100644 index 000000000..5f379c4ec --- /dev/null +++ b/types/components/hotelReservation/bookingConfirmation/room.ts @@ -0,0 +1,11 @@ +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/enterDetails/summary.ts b/types/components/hotelReservation/enterDetails/summary.ts index 5b6f82754..54d896d92 100644 --- a/types/components/hotelReservation/enterDetails/summary.ts +++ b/types/components/hotelReservation/enterDetails/summary.ts @@ -1,5 +1,4 @@ import type { Child } from "@/types/components/hotelReservation/selectRate/selectRate" -import type { Packages } from "@/types/requests/packages" import type { RoomAvailability } from "@/types/trpc/routers/hotel/availability" export interface ClientSummaryProps diff --git a/types/components/hotelReservation/sidePanel.ts b/types/components/hotelReservation/sidePanel.ts new file mode 100644 index 000000000..5a7f1910a --- /dev/null +++ b/types/components/hotelReservation/sidePanel.ts @@ -0,0 +1,6 @@ +import { sidePanelVariants } from "@/components/HotelReservation/SidePanel/variants" + +import type { VariantProps } from "class-variance-authority" + +export interface SidePanelProps + extends VariantProps {} diff --git a/types/components/hotelReservation/summary.ts b/types/components/hotelReservation/summary.ts index de8e20c4c..e705f3751 100644 --- a/types/components/hotelReservation/summary.ts +++ b/types/components/hotelReservation/summary.ts @@ -1,10 +1,6 @@ -import { RoomPackageCodeEnum } from "./selectRate/roomFilter" - import type { Packages } from "@/types/requests/packages" import type { DetailsState, Price } from "@/types/stores/enter-details" import type { RoomAvailability } from "@/types/trpc/routers/hotel/availability" -import type { BedTypeSchema } from "./enterDetails/bedType" -import type { BreakfastPackage } from "./enterDetails/breakfast" import type { Child } from "./selectRate/selectRate" export type RoomsData = Pick & @@ -15,25 +11,8 @@ export type RoomsData = Pick & packages: Packages | null } -interface SharedSummaryProps { - fromDate: string - toDate: string -} - -export interface SummaryProps extends SharedSummaryProps { - bedType: BedTypeSchema | undefined - breakfast: BreakfastPackage | false | undefined - showMemberPrice: boolean - room: RoomsData - toggleSummaryOpen?: () => void - totalPrice: Price -} - -export interface SummaryPageProps extends SharedSummaryProps { - adults: number - hotelId: string - kids: Child[] | undefined - packageCodes: RoomPackageCodeEnum[] | undefined - rateCode: string - roomTypeCode: string +export interface SummaryProps + extends Pick, + Pick { + isMember: boolean } diff --git a/utils/getBookedHotelRoom.ts b/utils/getBookedHotelRoom.ts new file mode 100644 index 000000000..45b5d65b8 --- /dev/null +++ b/utils/getBookedHotelRoom.ts @@ -0,0 +1,23 @@ +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, + } +}