From 94d53e1da32c79ae7d91dea9478141c80e10cdd7 Mon Sep 17 00:00:00 2001 From: Christian Andolf Date: Wed, 4 Dec 2024 11:27:30 +0100 Subject: [PATCH 01/10] fix(LOY-39): add unwrapped surprises to benefits --- server/routers/contentstack/reward/query.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/server/routers/contentstack/reward/query.ts b/server/routers/contentstack/reward/query.ts index 10ddba478..f09dac201 100644 --- a/server/routers/contentstack/reward/query.ts +++ b/server/routers/contentstack/reward/query.ts @@ -238,15 +238,18 @@ export const rewardQueryRouter = router({ const nextCursor = limit + cursor < rewardIds.length ? limit + cursor : undefined - const surprisesIds = validatedApiRewards.data + const wrappedSurprisesIds = validatedApiRewards.data .filter( - ({ type, rewardType }) => - type === "coupon" && rewardType === "Surprise" + (reward) => + reward.type === "coupon" && + reward.rewardType === "Surprise" && + "coupon" in reward && + reward.coupon?.some(({ unwrapped }) => !unwrapped) ) .map(({ rewardId }) => rewardId) const rewards = cmsRewards.filter( - (reward) => !surprisesIds.includes(reward.reward_id) + (reward) => !wrappedSurprisesIds.includes(reward.reward_id) ) getCurrentRewardSuccessCounter.add(1) From bed674df87cecc5674cc75e6fff9e933414db878 Mon Sep 17 00:00:00 2001 From: Bianca Widstam Date: Thu, 5 Dec 2024 07:39:06 +0000 Subject: [PATCH 02/10] Merged in fix/SW-1128-side-peek (pull request #1031) Fix/SW-1128/SW-1124 side peek and gallery * fix(SW-1128): updated style and mobile design for sidepeek select hotel * fix(SW-1128): update link sidepeek * fix(SW-1124): fix padding gallery * fix(SW-1128): fix sidepeek mobile design * fix(SW-1128): fix mobile design * fix(SW-1128): fix gallery icon caption Approved-by: Niclas Edenvin --- .../Contact/contact.module.css | 25 +++++++--- components/HotelReservation/Contact/index.tsx | 47 +++++++++---------- components/ImageGallery/index.tsx | 7 +-- components/Lightbox/Lightbox.module.css | 1 + .../Accordions/Accessibility.tsx | 1 + .../Accordions/CheckInCheckOut.tsx | 1 + .../Accordions/MeetingsAndConferences.tsx | 1 + .../HotelSidePeek/Accordions/Parking.tsx | 1 + .../HotelSidePeek/Accordions/Restaurant.tsx | 1 + .../Accordions/sidePeekAccordion.module.css | 1 + .../HotelSidePeek/hotelSidePeek.module.css | 23 ++++++--- components/SidePeeks/HotelSidePeek/index.tsx | 19 ++++---- .../AccordionItem/accordionItem.module.css | 11 ++++- .../Accordion/AccordionItem/index.tsx | 22 +++++---- .../Accordion/AccordionItem/variants.ts | 1 + .../TempDesignSystem/Accordion/variants.ts | 1 + 16 files changed, 100 insertions(+), 63 deletions(-) diff --git a/components/HotelReservation/Contact/contact.module.css b/components/HotelReservation/Contact/contact.module.css index e35862fed..01451a32f 100644 --- a/components/HotelReservation/Contact/contact.module.css +++ b/components/HotelReservation/Contact/contact.module.css @@ -4,6 +4,7 @@ grid-template-rows: auto; gap: var(--Spacing-x2); font-family: var(--typography-Body-Regular-fontFamily); + margin-bottom: var(--Spacing-x3); } .address, @@ -20,6 +21,7 @@ list-style-type: none; display: flex; flex-direction: column; + min-width: 0; } .soMeIcons { @@ -28,6 +30,19 @@ } .ecoLabel { + width: 38px; + height: auto; +} + +.ecoLabel img { + width: 100%; + height: auto; + flex-shrink: 0; + grid-column: 1 / 3; + grid-row: 4 / 4; +} + +.ecoContainer { display: flex; align-items: center; column-gap: var(--Spacing-x-one-and-half); @@ -38,10 +53,6 @@ margin-bottom: var(--Spacing-x1); } -.ecoLabel img { - flex-shrink: 0; -} - .ecoLabelText { display: flex; color: var(--UI-Text-Medium-contrast); @@ -49,8 +60,8 @@ justify-content: center; } -.googleMaps { - text-decoration: none; +.link { + text-decoration: underline; font-family: var(--typography-Body-Regular-fontFamily); - color: var(--Base-Text-Medium-contrast); + color: var(--Base-Text-High-contrast); } diff --git a/components/HotelReservation/Contact/index.tsx b/components/HotelReservation/Contact/index.tsx index 360831c94..d62305355 100644 --- a/components/HotelReservation/Contact/index.tsx +++ b/components/HotelReservation/Contact/index.tsx @@ -24,31 +24,27 @@ export default function Contact({ hotel }: ContactProps) { {intl.formatMessage({ id: "Address" })} - - {`${hotel.address.streetAddress}, ${hotel.address.city}`} - + {`${hotel.address.streetAddress}, `} + {hotel.address.city}
  • {intl.formatMessage({ id: "Driving directions" })} - - Google Maps - + Google Maps +
  • {intl.formatMessage({ id: "Contact us" })} - - {hotel.contactInformation.phoneNumber} + + + {hotel.contactInformation.phoneNumber} +
  • @@ -76,23 +72,24 @@ export default function Contact({ hotel }: ContactProps) { {intl.formatMessage({ id: "Email" })} - - {hotel.contactInformation.email} + + + {hotel.contactInformation.email} +
  • {hotel.hotelFacts.ecoLabels?.nordicEcoLabel ? ( -
    - {intl.formatMessage({ +
    +
    + {intl.formatMessage({ +
    {intl.formatMessage({ id: "Nordic Swan Ecolabel" })} diff --git a/components/ImageGallery/index.tsx b/components/ImageGallery/index.tsx index d26ed0007..007785ad8 100644 --- a/components/ImageGallery/index.tsx +++ b/components/ImageGallery/index.tsx @@ -6,7 +6,8 @@ import { useIntl } from "react-intl" import { GalleryIcon } from "@/components/Icons" import Image from "@/components/Image" import Lightbox from "@/components/Lightbox" -import Footnote from "@/components/TempDesignSystem/Text/Footnote" + +import Caption from "../TempDesignSystem/Text/Caption" import styles from "./imageGallery.module.css" @@ -44,9 +45,9 @@ function ImageGallery({ />
    - + {images.length} - +
    {accessibilityElevatorPitchText} diff --git a/components/SidePeeks/HotelSidePeek/Accordions/CheckInCheckOut.tsx b/components/SidePeeks/HotelSidePeek/Accordions/CheckInCheckOut.tsx index d005e6688..6848bc801 100644 --- a/components/SidePeeks/HotelSidePeek/Accordions/CheckInCheckOut.tsx +++ b/components/SidePeeks/HotelSidePeek/Accordions/CheckInCheckOut.tsx @@ -13,6 +13,7 @@ export default function CheckinCheckOut({ checkin }: CheckInCheckOutProps) { {intl.formatMessage({ id: "Hours" })} {`${intl.formatMessage({ id: "Check in from" })}: ${checkin.checkInTime}`} diff --git a/components/SidePeeks/HotelSidePeek/Accordions/MeetingsAndConferences.tsx b/components/SidePeeks/HotelSidePeek/Accordions/MeetingsAndConferences.tsx index 558046d4b..8168a764f 100644 --- a/components/SidePeeks/HotelSidePeek/Accordions/MeetingsAndConferences.tsx +++ b/components/SidePeeks/HotelSidePeek/Accordions/MeetingsAndConferences.tsx @@ -14,6 +14,7 @@ export default function MeetingsAndConferences({ {meetingDescription} diff --git a/components/SidePeeks/HotelSidePeek/Accordions/Parking.tsx b/components/SidePeeks/HotelSidePeek/Accordions/Parking.tsx index 0e5ffcec0..ba4ee33f7 100644 --- a/components/SidePeeks/HotelSidePeek/Accordions/Parking.tsx +++ b/components/SidePeeks/HotelSidePeek/Accordions/Parking.tsx @@ -16,6 +16,7 @@ export default function Parking({ parking }: ParkingProps) { title={intl.formatMessage({ id: "Parking" })} icon={IconName.Parking} className={styles.parking} + variant="sidepeek" > {parking.map((p) => (
    diff --git a/components/SidePeeks/HotelSidePeek/Accordions/Restaurant.tsx b/components/SidePeeks/HotelSidePeek/Accordions/Restaurant.tsx index a8d1cd047..d35a30304 100644 --- a/components/SidePeeks/HotelSidePeek/Accordions/Restaurant.tsx +++ b/components/SidePeeks/HotelSidePeek/Accordions/Restaurant.tsx @@ -15,6 +15,7 @@ export default function Restaurant({ {restaurantsContentDescriptionMedium} diff --git a/components/SidePeeks/HotelSidePeek/Accordions/sidePeekAccordion.module.css b/components/SidePeeks/HotelSidePeek/Accordions/sidePeekAccordion.module.css index bd2ae8933..0134e2165 100644 --- a/components/SidePeeks/HotelSidePeek/Accordions/sidePeekAccordion.module.css +++ b/components/SidePeeks/HotelSidePeek/Accordions/sidePeekAccordion.module.css @@ -10,6 +10,7 @@ align-items: center; gap: var(--Spacing-x1); padding-left: var(--Spacing-x1); + justify-items: flex-start; } .list li svg { diff --git a/components/SidePeeks/HotelSidePeek/hotelSidePeek.module.css b/components/SidePeeks/HotelSidePeek/hotelSidePeek.module.css index 23b92b792..481e6b47d 100644 --- a/components/SidePeeks/HotelSidePeek/hotelSidePeek.module.css +++ b/components/SidePeeks/HotelSidePeek/hotelSidePeek.module.css @@ -9,13 +9,24 @@ gap: var(--Spacing-x2); } -.amenity { - font-family: var(--typography-Body-Regular-fontFamily); - border-bottom: 1px solid var(--Base-Border-Subtle); - /* padding set to align with AccordionItem which has a different composition */ - padding: calc(var(--Spacing-x1) + var(--Spacing-x-one-and-half)) - var(--Spacing-x3); +.content:last-child { + gap: 0; +} + +.content > p { + margin-bottom: var(--Spacing-x-one-and-half); +} + +.content > ul > li:first-child { + border-top: 1px solid var(--Base-Border-Subtle); +} + +.amenity > p { + border-top: 1px solid var(--Base-Border-Subtle); + padding: calc(var(--Spacing-x-one-and-half) + var(--Spacing-x1)) + var(--Spacing-x1); display: flex; + align-items: center; gap: var(--Spacing-x1); } diff --git a/components/SidePeeks/HotelSidePeek/index.tsx b/components/SidePeeks/HotelSidePeek/index.tsx index 4fc5bab8c..dad75e7b3 100644 --- a/components/SidePeeks/HotelSidePeek/index.tsx +++ b/components/SidePeeks/HotelSidePeek/index.tsx @@ -71,24 +71,21 @@ export default function HotelSidePeek({ } /> )} + +
    {amenitiesList.map((amenity) => { const Icon = mapFacilityToIcon(amenity.id) return ( -
    + {Icon && ( - + )} - - {amenity.name} - -
    + {amenity.name} + ) })} - +
    + {/* TODO: handle linking to Hotel Page */} {/* {showCTA && ( +
    +
    + + ) +} diff --git a/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/meetingsAndConferences.module.css b/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/meetingsAndConferences.module.css new file mode 100644 index 000000000..b150919a4 --- /dev/null +++ b/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/meetingsAndConferences.module.css @@ -0,0 +1,35 @@ +.wrapper { + display: flex; + flex-direction: column; + gap: var(--Spacing-x4); + margin-bottom: calc( + var(--Spacing-x4) * 2 + 80px + ); /* Creates space between the wrapper and buttonContainer */ +} + +.information { + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: 1fr 1fr; + gap: var(--Spacing-x2); +} + +.image { + width: 100%; + height: 175px; + object-fit: cover; +} + +.text { + grid-column: 1 / 3; +} + +.buttonContainer { + background-color: var(--Base-Background-Primary-Normal); + border-top: 1px solid var(--Base-Border-Subtle); + padding: var(--Spacing-x4) var(--Spacing-x2); + width: 100%; + position: absolute; + left: 0; + bottom: 0; +} diff --git a/i18n/dictionaries/da.json b/i18n/dictionaries/da.json index eefc1cd1d..3a66fc700 100644 --- a/i18n/dictionaries/da.json +++ b/i18n/dictionaries/da.json @@ -95,6 +95,7 @@ "Could not find requested resource": "Kunne ikke finde den anmodede ressource", "Country": "Land", "Country code": "Landekode", + "Creative spaces for meetings": "Kreative rum til møder", "Credit card": "Kreditkort", "Credit card deleted successfully": "Kreditkort blev slettet", "Currency Code": "DKK", diff --git a/i18n/dictionaries/de.json b/i18n/dictionaries/de.json index 8c5352c9b..8768310d2 100644 --- a/i18n/dictionaries/de.json +++ b/i18n/dictionaries/de.json @@ -95,6 +95,7 @@ "Could not find requested resource": "Die angeforderte Ressource konnte nicht gefunden werden.", "Country": "Land", "Country code": "Landesvorwahl", + "Creative spaces for meetings": "Kreative Räume für Meetings", "Credit card": "Kreditkarte", "Credit card deleted successfully": "Kreditkarte erfolgreich gelöscht", "Currency Code": "EUR", diff --git a/i18n/dictionaries/en.json b/i18n/dictionaries/en.json index 91c371258..4193038a4 100644 --- a/i18n/dictionaries/en.json +++ b/i18n/dictionaries/en.json @@ -103,6 +103,7 @@ "Could not find requested resource": "Could not find requested resource", "Country": "Country", "Country code": "Country code", + "Creative spaces for meetings": "Creative spaces for meetings", "Credit card": "Credit card", "Credit card deleted successfully": "Credit card deleted successfully", "Currency Code": "EUR", diff --git a/i18n/dictionaries/fi.json b/i18n/dictionaries/fi.json index 086e635de..4a0ca7811 100644 --- a/i18n/dictionaries/fi.json +++ b/i18n/dictionaries/fi.json @@ -95,6 +95,7 @@ "Could not find requested resource": "Pyydettyä resurssia ei löytynyt", "Country": "Maa", "Country code": "Maatunnus", + "Creative spaces for meetings": "Luovia tiloja kokouksille", "Credit card": "Luottokortti", "Credit card deleted successfully": "Luottokortti poistettu onnistuneesti", "Currency Code": "EUR", diff --git a/i18n/dictionaries/no.json b/i18n/dictionaries/no.json index 6de6aa1e4..8f849219f 100644 --- a/i18n/dictionaries/no.json +++ b/i18n/dictionaries/no.json @@ -95,6 +95,7 @@ "Could not find requested resource": "Kunne ikke finne den forespurte ressursen", "Country": "Land", "Country code": "Landskode", + "Creative spaces for meetings": "Kreative rom for møter", "Credit card deleted successfully": "Kredittkort slettet", "Currency Code": "NOK", "Current password": "Nåværende passord", diff --git a/i18n/dictionaries/sv.json b/i18n/dictionaries/sv.json index 31ad5c056..620c4c747 100644 --- a/i18n/dictionaries/sv.json +++ b/i18n/dictionaries/sv.json @@ -95,6 +95,7 @@ "Could not find requested resource": "Det gick inte att hitta den begärda resursen", "Country": "Land", "Country code": "Landskod", + "Creative spaces for meetings": "Kreativa utrymmen för möten", "Credit card deleted successfully": "Kreditkort har tagits bort", "Currency Code": "SEK", "Current password": "Nuvarande lösenord", diff --git a/server/routers/hotels/query.ts b/server/routers/hotels/query.ts index e91905c6b..f91b4a762 100644 --- a/server/routers/hotels/query.ts +++ b/server/routers/hotels/query.ts @@ -210,6 +210,87 @@ export const getHotelData = cache( ) export const hotelQueryRouter = router({ + get: contentStackUidWithServiceProcedure.query(async ({ ctx }) => { + const { lang, uid } = ctx + + const contentstackData = await getContentstackData(lang, uid) + const hotelId = contentstackData?.hotel_page_id + + if (!hotelId) { + throw notFound(`Hotel not found for uid: ${uid}`) + } + + const hotelData = await getHotelData( + { + hotelId, + language: ctx.lang, + }, + ctx.serviceToken + ) + + if (!hotelData) { + throw notFound() + } + + const included = hotelData.included || [] + + const hotelAttributes = hotelData.data.attributes + const images = hotelAttributes.gallery?.smallerImages + const hotelAlerts = hotelAttributes.specialAlerts + + const roomCategories = included + ? included.filter((item) => item.type === "roomcategories") + : [] + + const activities = contentstackData?.content + ? contentstackData?.content[0] + : null + + const facilities: Facility[] = [ + { + ...hotelData.data.attributes.restaurantImages, + id: FacilityCardTypeEnum.restaurant, + headingText: + hotelData?.data.attributes.restaurantImages?.headingText ?? "", + heroImages: + hotelData?.data.attributes.restaurantImages?.heroImages ?? [], + }, + { + ...hotelData.data.attributes.conferencesAndMeetings, + id: FacilityCardTypeEnum.conference, + headingText: + hotelData?.data.attributes.conferencesAndMeetings?.headingText ?? "", + heroImages: + hotelData?.data.attributes.conferencesAndMeetings?.heroImages ?? [], + }, + { + ...hotelData.data.attributes.healthAndWellness, + id: FacilityCardTypeEnum.wellness, + headingText: + hotelData?.data.attributes.healthAndWellness?.headingText ?? "", + heroImages: + hotelData?.data.attributes.healthAndWellness?.heroImages ?? [], + }, + ] + + return { + hotelId, + hotelName: hotelAttributes.name, + hotelDescription: hotelAttributes.hotelContent.texts.descriptions.short, + hotelLocation: hotelAttributes.location, + hotelAddress: hotelAttributes.address, + hotelRatings: hotelAttributes.ratings, + hotelDetailedFacilities: hotelAttributes.detailedFacilities, + hotelImages: images, + pointsOfInterest: hotelAttributes.pointsOfInterest, + roomCategories, + activitiesCard: activities?.upcoming_activities_card, + facilities, + alerts: hotelAlerts, + faq: contentstackData?.faq, + healthFacilities: hotelAttributes.healthFacilities, + } + }), availability: router({ hotels: serviceProcedure .input(getHotelsAvailabilityInputSchema) diff --git a/types/components/hotelPage/sidepeek/meetingsAndConferences.ts b/types/components/hotelPage/sidepeek/meetingsAndConferences.ts new file mode 100644 index 000000000..abbfc8214 --- /dev/null +++ b/types/components/hotelPage/sidepeek/meetingsAndConferences.ts @@ -0,0 +1,5 @@ +import type { Hotel } from "@/types/hotel" + +export type MeetingsAndConferencesSidePeekProps = { + meetingFacilities: Hotel["conferencesAndMeetings"] +} From 159464d6722fa5ebf3f2194b53e4e799535014b4 Mon Sep 17 00:00:00 2001 From: Fredrik Thorsson Date: Fri, 29 Nov 2024 15:50:39 +0100 Subject: [PATCH 04/10] feat(SW-936): fix merge conflict --- server/routers/hotels/query.ts | 81 ---------------------------------- 1 file changed, 81 deletions(-) diff --git a/server/routers/hotels/query.ts b/server/routers/hotels/query.ts index f91b4a762..e91905c6b 100644 --- a/server/routers/hotels/query.ts +++ b/server/routers/hotels/query.ts @@ -210,87 +210,6 @@ export const getHotelData = cache( ) export const hotelQueryRouter = router({ - get: contentStackUidWithServiceProcedure.query(async ({ ctx }) => { - const { lang, uid } = ctx - - const contentstackData = await getContentstackData(lang, uid) - const hotelId = contentstackData?.hotel_page_id - - if (!hotelId) { - throw notFound(`Hotel not found for uid: ${uid}`) - } - - const hotelData = await getHotelData( - { - hotelId, - language: ctx.lang, - }, - ctx.serviceToken - ) - - if (!hotelData) { - throw notFound() - } - - const included = hotelData.included || [] - - const hotelAttributes = hotelData.data.attributes - const images = hotelAttributes.gallery?.smallerImages - const hotelAlerts = hotelAttributes.specialAlerts - - const roomCategories = included - ? included.filter((item) => item.type === "roomcategories") - : [] - - const activities = contentstackData?.content - ? contentstackData?.content[0] - : null - - const facilities: Facility[] = [ - { - ...hotelData.data.attributes.restaurantImages, - id: FacilityCardTypeEnum.restaurant, - headingText: - hotelData?.data.attributes.restaurantImages?.headingText ?? "", - heroImages: - hotelData?.data.attributes.restaurantImages?.heroImages ?? [], - }, - { - ...hotelData.data.attributes.conferencesAndMeetings, - id: FacilityCardTypeEnum.conference, - headingText: - hotelData?.data.attributes.conferencesAndMeetings?.headingText ?? "", - heroImages: - hotelData?.data.attributes.conferencesAndMeetings?.heroImages ?? [], - }, - { - ...hotelData.data.attributes.healthAndWellness, - id: FacilityCardTypeEnum.wellness, - headingText: - hotelData?.data.attributes.healthAndWellness?.headingText ?? "", - heroImages: - hotelData?.data.attributes.healthAndWellness?.heroImages ?? [], - }, - ] - - return { - hotelId, - hotelName: hotelAttributes.name, - hotelDescription: hotelAttributes.hotelContent.texts.descriptions.short, - hotelLocation: hotelAttributes.location, - hotelAddress: hotelAttributes.address, - hotelRatings: hotelAttributes.ratings, - hotelDetailedFacilities: hotelAttributes.detailedFacilities, - hotelImages: images, - pointsOfInterest: hotelAttributes.pointsOfInterest, - roomCategories, - activitiesCard: activities?.upcoming_activities_card, - facilities, - alerts: hotelAlerts, - faq: contentstackData?.faq, - healthFacilities: hotelAttributes.healthFacilities, - } - }), availability: router({ hotels: serviceProcedure .input(getHotelsAvailabilityInputSchema) From 2fb4f6a702fedb10f8483186c21fcfd750e31368 Mon Sep 17 00:00:00 2001 From: Fredrik Thorsson Date: Fri, 29 Nov 2024 16:41:57 +0100 Subject: [PATCH 05/10] feat(SW-936): add descriptions --- .../MeetingsAndConferences/index.tsx | 49 ++++++++++++------- .../meetingsAndConferences.module.css | 19 ++----- .../ContentType/HotelPage/SidePeeks/index.ts | 1 + components/ContentType/HotelPage/index.tsx | 17 +++---- .../sidepeek/meetingsAndConferences.ts | 2 + 5 files changed, 43 insertions(+), 45 deletions(-) diff --git a/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/index.tsx b/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/index.tsx index 637c4533c..21ca2b336 100644 --- a/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/index.tsx +++ b/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/index.tsx @@ -4,6 +4,7 @@ import Image from "@/components/Image" import Button from "@/components/TempDesignSystem/Button" import Link from "@/components/TempDesignSystem/Link" import SidePeek from "@/components/TempDesignSystem/SidePeek" +import Body from "@/components/TempDesignSystem/Text/Body" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" import Title from "@/components/TempDesignSystem/Text/Title" import { getIntl } from "@/i18n" @@ -11,13 +12,20 @@ import { getLang } from "@/i18n/serverContext" import styles from "./meetingsAndConferences.module.css" -import { MeetingsAndConferencesSidePeekProps } from "@/types/components/hotelPage/sidepeek/meetingsAndConferences" +import type { MeetingsAndConferencesSidePeekProps } from "@/types/components/hotelPage/sidepeek/meetingsAndConferences" export default async function MeetingsAndConferencesSidePeek({ meetingFacilities, + descriptions, + link, }: MeetingsAndConferencesSidePeekProps) { const lang = getLang() const intl = await getIntl() + const mainImage = meetingFacilities?.heroImages[0].imageSizes.medium + const altText = + meetingFacilities?.heroImages[0].metaData.altText || + intl.formatMessage({ id: "Creative spaces for meetings" }) + return ( -
    + {mainImage && ( - -
    {meetingFacilities?.headingText}
    -
    -
    - + )} + {descriptions.medium} +
    + + [Min to Max capacity square meter info] + + + [Min to Max capacity persons info] +
    + {link && ( +
    + +
    + )}
    ) diff --git a/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/meetingsAndConferences.module.css b/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/meetingsAndConferences.module.css index b150919a4..058c7338b 100644 --- a/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/meetingsAndConferences.module.css +++ b/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/meetingsAndConferences.module.css @@ -1,27 +1,16 @@ .wrapper { - display: flex; - flex-direction: column; - gap: var(--Spacing-x4); + display: grid; + gap: var(--Spacing-x3); margin-bottom: calc( var(--Spacing-x4) * 2 + 80px ); /* Creates space between the wrapper and buttonContainer */ } -.information { - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: 1fr 1fr; - gap: var(--Spacing-x2); -} - .image { width: 100%; - height: 175px; + height: 240px; object-fit: cover; -} - -.text { - grid-column: 1 / 3; + border-radius: var(--Corner-radius-Medium); } .buttonContainer { diff --git a/components/ContentType/HotelPage/SidePeeks/index.ts b/components/ContentType/HotelPage/SidePeeks/index.ts index e7233e010..f8de0f861 100644 --- a/components/ContentType/HotelPage/SidePeeks/index.ts +++ b/components/ContentType/HotelPage/SidePeeks/index.ts @@ -1,5 +1,6 @@ export { default as AboutTheHotelSidePeek } from "./AboutTheHotel" export { default as ActivitiesSidePeek } from "./Activities" export { default as AmenitiesSidePeek } from "./Amenities" +export { default as MeetingsAndConferencesSidePeek } from "./MeetingsAndConferences" export { default as RoomSidePeek } from "./Room" export { default as WellnessAndExerciseSidePeek } from "./WellnessAndExercise" diff --git a/components/ContentType/HotelPage/index.tsx b/components/ContentType/HotelPage/index.tsx index a9125edf6..38002af81 100644 --- a/components/ContentType/HotelPage/index.tsx +++ b/components/ContentType/HotelPage/index.tsx @@ -1,9 +1,6 @@ import { notFound } from "next/navigation" -import { - meetingsAndConferences, - restaurantAndBar, -} from "@/constants/routes/hotelPageParams" +import { restaurantAndBar } from "@/constants/routes/hotelPageParams" import { env } from "@/env/server" import { getHotelData, getHotelPage } from "@/lib/trpc/memoizedRequests" @@ -30,6 +27,7 @@ import { AboutTheHotelSidePeek, ActivitiesSidePeek, AmenitiesSidePeek, + MeetingsAndConferencesSidePeek, RoomSidePeek, WellnessAndExerciseSidePeek, } from "./SidePeeks" @@ -203,13 +201,10 @@ export default async function HotelPage({ hotelId }: HotelPageProps) { {activitiesCard && ( )} - - {/* TODO */} - Meetings & Conferences - + {roomCategories.map((room) => ( ))} diff --git a/types/components/hotelPage/sidepeek/meetingsAndConferences.ts b/types/components/hotelPage/sidepeek/meetingsAndConferences.ts index abbfc8214..50dc76da0 100644 --- a/types/components/hotelPage/sidepeek/meetingsAndConferences.ts +++ b/types/components/hotelPage/sidepeek/meetingsAndConferences.ts @@ -2,4 +2,6 @@ import type { Hotel } from "@/types/hotel" export type MeetingsAndConferencesSidePeekProps = { meetingFacilities: Hotel["conferencesAndMeetings"] + descriptions: Hotel["hotelContent"]["texts"]["meetingDescription"] + link?: string } From 7e93d59842c2ceb8fda2355bfae605f9361e6742 Mon Sep 17 00:00:00 2001 From: Fredrik Thorsson Date: Mon, 2 Dec 2024 20:38:49 +0100 Subject: [PATCH 06/10] feat(SW-936): add secondary image --- .../MeetingsAndConferences/index.tsx | 51 +++++++++++-------- .../meetingsAndConferences.module.css | 6 +++ 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/index.tsx b/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/index.tsx index 21ca2b336..ed9a96f18 100644 --- a/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/index.tsx +++ b/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/index.tsx @@ -21,9 +21,15 @@ export default async function MeetingsAndConferencesSidePeek({ }: MeetingsAndConferencesSidePeekProps) { const lang = getLang() const intl = await getIntl() - const mainImage = meetingFacilities?.heroImages[0].imageSizes.medium - const altText = - meetingFacilities?.heroImages[0].metaData.altText || + + const primaryImage = meetingFacilities?.heroImages[0]?.imageSizes.medium + const primaryAltText = + meetingFacilities?.heroImages[0]?.metaData.altText || + intl.formatMessage({ id: "Creative spaces for meetings" }) + + const secondaryImage = meetingFacilities?.heroImages[1]?.imageSizes.medium + const secondaryAltText = + meetingFacilities?.heroImages[1]?.metaData.altText || intl.formatMessage({ id: "Creative spaces for meetings" }) return ( @@ -37,24 +43,29 @@ export default async function MeetingsAndConferencesSidePeek({ {intl.formatMessage({ id: "Creative spaces for meetings" })} - {mainImage && ( - {altText} + {primaryImage && ( +
    + {primaryAltText} + {secondaryImage && ( + {secondaryAltText} + )} +
    + )} + {descriptions?.medium && ( + {descriptions.medium} )} - {descriptions.medium} -
    - - [Min to Max capacity square meter info] - - - [Min to Max capacity persons info] - -
    {link && (
    diff --git a/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/meetingsAndConferences.module.css b/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/meetingsAndConferences.module.css index a8cf8b7aa..ab19b9748 100644 --- a/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/meetingsAndConferences.module.css +++ b/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/meetingsAndConferences.module.css @@ -6,19 +6,17 @@ ); /* Creates space between the wrapper and buttonContainer */ } -.imageContainer { - display: grid; - grid-template-columns: 1fr 1fr; - gap: var(--Spacing-x2); -} - .image { width: 100%; - height: 240px; + height: 300px; object-fit: cover; border-radius: var(--Corner-radius-Medium); } +.secondaryImage { + display: none; +} + .buttonContainer { background-color: var(--Base-Background-Primary-Normal); border-top: 1px solid var(--Base-Border-Subtle); @@ -28,3 +26,19 @@ left: 0; bottom: 0; } + +@media screen and (min-width: 768px) { + .imageContainer { + display: grid; + grid-template-columns: 1fr 1fr; + gap: var(--Spacing-x2); + } + + .image { + height: 240px; + } + + .secondaryImage { + display: block; + } +} From 248dc4df19dae61de6eeaa401c09c0cc2dd366be Mon Sep 17 00:00:00 2001 From: Fredrik Thorsson Date: Tue, 3 Dec 2024 23:03:23 +0100 Subject: [PATCH 09/10] feat(SW-936): remove function --- .../SidePeeks/MeetingsAndConferences/index.tsx | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/index.tsx b/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/index.tsx index 82fa7a023..f1a1c5101 100644 --- a/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/index.tsx +++ b/components/ContentType/HotelPage/SidePeeks/MeetingsAndConferences/index.tsx @@ -23,16 +23,13 @@ export default async function MeetingsAndConferencesSidePeek({ const intl = await getIntl() const fallbackAlt = intl.formatMessage({ id: "Creative spaces for meetings" }) - function getImage(index: number) { - const list = meetingFacilities?.heroImages[index] - return { - image: list?.imageSizes.medium, - altText: list?.metaData.altText || fallbackAlt, - } - } + const primaryImage = meetingFacilities?.heroImages[0]?.imageSizes.medium + const primaryAltText = + meetingFacilities?.heroImages[0]?.metaData.altText || fallbackAlt - const { image: primaryImage, altText: primaryAltText } = getImage(0) - const { image: secondaryImage, altText: secondaryAltText } = getImage(1) + const secondaryImage = meetingFacilities?.heroImages[1]?.imageSizes.medium + const secondaryAltText = + meetingFacilities?.heroImages[1]?.metaData.altText || fallbackAlt return ( Date: Tue, 3 Dec 2024 13:25:38 +0100 Subject: [PATCH 10/10] feat(SW-866): download invoice --- .../booking-confirmation/page.tsx | 43 +++----------- .../Header/Actions/DownloadInvoice.tsx | 8 ++- .../BookingConfirmation/Header/index.tsx | 19 ++++--- .../HotelDetails/index.tsx | 15 +++-- .../PaymentDetails/index.tsx | 20 +++---- .../BookingConfirmation/Promos/index.tsx | 7 ++- .../BookingConfirmation/Receipt/index.tsx | 29 +++++----- .../BookingConfirmation/Rooms/Room/index.tsx | 14 +++-- .../BookingConfirmation/Rooms/index.tsx | 25 +++----- .../confirmation.module.css | 2 +- .../BookingConfirmation/index.tsx | 57 +++++++++++++++++++ package-lock.json | 9 +++ package.json | 1 + server/routers/booking/query.ts | 2 + server/routers/booking/utils.ts | 27 +++++++++ .../actions/downloadInvoice.ts | 5 ++ .../bookingConfirmation.ts | 4 +- .../bookingConfirmation/header.ts | 8 +++ .../bookingConfirmation/hotelDetails.ts | 5 ++ .../bookingConfirmation/paymentDetails.ts | 4 ++ .../bookingConfirmation/receipt.ts | 3 + .../bookingConfirmation/room.ts | 11 ---- .../bookingConfirmation/rooms.ts | 4 ++ .../bookingConfirmation/rooms/room.ts | 7 +++ types/trpc/routers/booking/confirmation.ts | 19 +++++++ utils/getBookedHotelRoom.ts | 23 -------- 26 files changed, 229 insertions(+), 142 deletions(-) rename app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.module.css => components/HotelReservation/BookingConfirmation/confirmation.module.css (99%) create mode 100644 components/HotelReservation/BookingConfirmation/index.tsx create mode 100644 server/routers/booking/utils.ts create mode 100644 types/components/hotelReservation/bookingConfirmation/actions/downloadInvoice.ts create mode 100644 types/components/hotelReservation/bookingConfirmation/header.ts create mode 100644 types/components/hotelReservation/bookingConfirmation/hotelDetails.ts create mode 100644 types/components/hotelReservation/bookingConfirmation/paymentDetails.ts create mode 100644 types/components/hotelReservation/bookingConfirmation/receipt.ts delete mode 100644 types/components/hotelReservation/bookingConfirmation/room.ts create mode 100644 types/components/hotelReservation/bookingConfirmation/rooms.ts create mode 100644 types/components/hotelReservation/bookingConfirmation/rooms/room.ts create mode 100644 types/trpc/routers/booking/confirmation.ts delete mode 100644 utils/getBookedHotelRoom.ts 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, - } -}