diff --git a/app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/@sidePeek/[...paths]/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/(standard)/@sidePeek/[...paths]/page.tsx similarity index 100% rename from app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/@sidePeek/[...paths]/page.tsx rename to app/[lang]/(live)/(public)/hotelreservation/(standard)/@sidePeek/[...paths]/page.tsx diff --git a/app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/@sidePeek/loading.tsx b/app/[lang]/(live)/(public)/hotelreservation/(standard)/@sidePeek/loading.tsx similarity index 100% rename from app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/@sidePeek/loading.tsx rename to app/[lang]/(live)/(public)/hotelreservation/(standard)/@sidePeek/loading.tsx diff --git a/app/[lang]/(live)/(public)/hotelreservation/(standard)/@sidePeek/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/(standard)/@sidePeek/page.tsx new file mode 100644 index 000000000..3aa44655c --- /dev/null +++ b/app/[lang]/(live)/(public)/hotelreservation/(standard)/@sidePeek/page.tsx @@ -0,0 +1,27 @@ +import { getHotelData } from "@/lib/trpc/memoizedRequests" +import { HotelIncludeEnum } from "@/server/routers/hotels/input" + +import { getQueryParamsForEnterDetails } from "@/components/HotelReservation/SelectRate/RoomSelection/utils" +import SidePeek from "@/components/HotelReservation/SidePeek" + +import type { LangParams, PageArgs } from "@/types/params" + +export default async function HotelSidePeek({ + params, + searchParams, +}: PageArgs) { + const search = new URLSearchParams(searchParams) + const { hotel: hotelId } = getQueryParamsForEnterDetails(search) + + if (!hotelId) { + return + } + + const hotel = await getHotelData({ + hotelId: hotelId, + language: params.lang, + include: [HotelIncludeEnum.RoomCategories], + }) + + return +} diff --git a/app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/@sidePeek/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/@sidePeek/page.tsx deleted file mode 100644 index deca843c3..000000000 --- a/app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/@sidePeek/page.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { redirect } from "next/navigation" - -import { getHotelData } from "@/lib/trpc/memoizedRequests" - -import SidePeek from "@/components/HotelReservation/EnterDetails/SidePeek" - -import type { LangParams, PageArgs } from "@/types/params" - -export default async function HotelSidePeek({ - params, - searchParams, -}: PageArgs) { - if (!searchParams.hotel) { - redirect(`/${params.lang}`) - } - const hotel = await getHotelData({ - hotelId: searchParams.hotel, - language: params.lang, - }) - if (!hotel?.data) { - redirect(`/${params.lang}`) - } - return -} diff --git a/app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/_preload.ts b/app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/_preload.ts new file mode 100644 index 000000000..34e238e7c --- /dev/null +++ b/app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/_preload.ts @@ -0,0 +1,6 @@ +import { getCreditCardsSafely , getProfileSafely } from "@/lib/trpc/memoizedRequests" + +export function preload() { + void getProfileSafely() + void getCreditCardsSafely() +} diff --git a/app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/layout.tsx b/app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/layout.tsx index c33d65975..3e277f4a0 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/layout.tsx +++ b/app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/layout.tsx @@ -1,10 +1,12 @@ -import { getProfileSafely } from "@/lib/trpc/memoizedRequests" +import { + getCreditCardsSafely, + getProfileSafely, +} from "@/lib/trpc/memoizedRequests" import EnterDetailsProvider from "@/components/HotelReservation/EnterDetails/Provider" -import SelectedRoom from "@/components/HotelReservation/EnterDetails/SelectedRoom" import { setLang } from "@/i18n/serverContext" -import { preload } from "./page" +import { preload } from "./_preload" import styles from "./layout.module.css" @@ -16,11 +18,9 @@ export default async function StepLayout({ children, hotelHeader, params, - sidePeek, }: React.PropsWithChildren< LayoutArgs & { hotelHeader: React.ReactNode - sidePeek: React.ReactNode summary: React.ReactNode } >) { @@ -34,7 +34,6 @@ export default async function StepLayout({
{hotelHeader}
- {children}
- {sidePeek}
) diff --git a/app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/page.tsx index 1743d07b2..daf383b96 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/page.tsx +++ b/app/[lang]/(live)/(public)/hotelreservation/(standard)/[step]/page.tsx @@ -15,6 +15,7 @@ import Details from "@/components/HotelReservation/EnterDetails/Details" import HistoryStateManager from "@/components/HotelReservation/EnterDetails/HistoryStateManager" import Payment from "@/components/HotelReservation/EnterDetails/Payment" import SectionAccordion from "@/components/HotelReservation/EnterDetails/SectionAccordion" +import SelectedRoom from "@/components/HotelReservation/EnterDetails/SelectedRoom" import { generateChildrenString, getQueryParamsForEnterDetails, @@ -25,11 +26,6 @@ import { StepEnum } from "@/types/components/hotelReservation/enterDetails/step" import { SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate" import type { LangParams, PageArgs } from "@/types/params" -export function preload() { - void getProfileSafely() - void getCreditCardsSafely() -} - function isValidStep(step: string): step is StepEnum { return Object.values(StepEnum).includes(step as StepEnum) } @@ -106,6 +102,9 @@ export default async function StepPage({ return (
+ + + {/* TODO: How to handle no beds found? */} {roomAvailability.bedTypes ? ( >) { + sidePeek, +}: React.PropsWithChildren> & { + sidePeek: React.ReactNode +}) { if (env.HIDE_FOR_NEXT_RELEASE) { return notFound() } - return
{children}
+ return ( +
+ {children} + {sidePeek} +
+ ) } diff --git a/components/ContentType/HotelPage/Rooms/RoomCard/index.tsx b/components/ContentType/HotelPage/Rooms/RoomCard/index.tsx index 81ab9b720..2b0620885 100644 --- a/components/ContentType/HotelPage/Rooms/RoomCard/index.tsx +++ b/components/ContentType/HotelPage/Rooms/RoomCard/index.tsx @@ -4,15 +4,16 @@ import { useIntl } from "react-intl" import { GalleryIcon } from "@/components/Icons" import Image from "@/components/Image" -import RoomSidePeek from "@/components/SidePeeks/RoomSidePeek" import Body from "@/components/TempDesignSystem/Text/Body" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" +import RoomDetailsButton from "../RoomDetailsButton" + import styles from "./roomCard.module.css" import type { RoomCardProps } from "@/types/components/hotelPage/room" -export function RoomCard({ room }: RoomCardProps) { +export function RoomCard({ hotelId, room }: RoomCardProps) { const { images, name, roomSize, occupancy, id } = room const intl = useIntl() const mainImage = images[0] @@ -70,7 +71,10 @@ export function RoomCard({ room }: RoomCardProps) { {subtitle} - + ) diff --git a/components/ContentType/HotelPage/Rooms/RoomDetailsButton/index.tsx b/components/ContentType/HotelPage/Rooms/RoomDetailsButton/index.tsx new file mode 100644 index 000000000..d68ba8b4a --- /dev/null +++ b/components/ContentType/HotelPage/Rooms/RoomDetailsButton/index.tsx @@ -0,0 +1,34 @@ +"use client" + +import { useIntl } from "react-intl" + +import useSidePeekStore from "@/stores/sidepeek" + +import { ChevronRightSmallIcon } from "@/components/Icons" +import Button from "@/components/TempDesignSystem/Button" + +import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek" +import { ToggleSidePeekProps } from "@/types/components/hotelReservation/toggleSidePeekProps" + +export default function RoomDetailsButton({ + hotelId, + roomTypeCode, +}: ToggleSidePeekProps) { + const intl = useIntl() + const openSidePeek = useSidePeekStore((state) => state.openSidePeek) + + return ( + + ) +} diff --git a/components/ContentType/HotelPage/Rooms/index.tsx b/components/ContentType/HotelPage/Rooms/index.tsx index e976ee8af..325f60eb6 100644 --- a/components/ContentType/HotelPage/Rooms/index.tsx +++ b/components/ContentType/HotelPage/Rooms/index.tsx @@ -15,7 +15,7 @@ import styles from "./rooms.module.css" import type { RoomsProps } from "@/types/components/hotelPage/room" import { HotelHashValues } from "@/types/components/hotelPage/tabNavigation" -export function Rooms({ rooms }: RoomsProps) { +export function Rooms({ hotelId, rooms }: RoomsProps) { const intl = useIntl() const showToggleButton = rooms.length > 3 const [allRoomsVisible, setAllRoomsVisible] = useState(!showToggleButton) @@ -45,7 +45,7 @@ export function Rooms({ rooms }: RoomsProps) { > {rooms.map((room) => (
- +
))} diff --git a/components/ContentType/HotelPage/index.tsx b/components/ContentType/HotelPage/index.tsx index d3ee721a8..56babcb7a 100644 --- a/components/ContentType/HotelPage/index.tsx +++ b/components/ContentType/HotelPage/index.tsx @@ -3,6 +3,7 @@ import { env } from "@/env/server" import { serverClient } from "@/lib/trpc/server" import AccordionSection from "@/components/Blocks/Accordion" +import HotelReservationSidePeek from "@/components/HotelReservation/SidePeek" import SidePeekProvider from "@/components/SidePeeks/SidePeekProvider" import Alert from "@/components/TempDesignSystem/Alert" import SidePeek from "@/components/TempDesignSystem/SidePeek" @@ -39,6 +40,7 @@ export default async function HotelPage() { } const { + hotelId, hotelName, hotelDescription, hotelLocation, @@ -99,7 +101,7 @@ export default async function HotelPage() { ) : null} - + {faq.accordions.length > 0 && ( @@ -168,6 +170,7 @@ export default async function HotelPage() { {/* eslint-enable import/no-named-as-default-member */} + ) } diff --git a/components/HotelReservation/EnterDetails/SelectedRoom/ToggleSidePeek.tsx b/components/HotelReservation/EnterDetails/SelectedRoom/ToggleSidePeek.tsx new file mode 100644 index 000000000..cf14b995b --- /dev/null +++ b/components/HotelReservation/EnterDetails/SelectedRoom/ToggleSidePeek.tsx @@ -0,0 +1,33 @@ +"use client" + +import { useIntl } from "react-intl" + +import useSidePeekStore from "@/stores/sidepeek" + +import Button from "@/components/TempDesignSystem/Button" + +import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek" +import { ToggleSidePeekProps } from "@/types/components/hotelReservation/toggleSidePeekProps" + +export default function ToggleSidePeek({ + hotelId, + roomTypeCode, +}: ToggleSidePeekProps) { + const intl = useIntl() + const openSidePeek = useSidePeekStore((state) => state.openSidePeek) + + return ( + + ) +} diff --git a/components/HotelReservation/EnterDetails/SelectedRoom/index.tsx b/components/HotelReservation/EnterDetails/SelectedRoom/index.tsx index 7812c6be6..523c06c1c 100644 --- a/components/HotelReservation/EnterDetails/SelectedRoom/index.tsx +++ b/components/HotelReservation/EnterDetails/SelectedRoom/index.tsx @@ -2,15 +2,25 @@ import { useIntl } from "react-intl" +import { RoomConfiguration } from "@/server/routers/hotels/output" + import { EditIcon, ImageIcon } from "@/components/Icons" import Button from "@/components/TempDesignSystem/Button" import Link from "@/components/TempDesignSystem/Link" import Footnote from "@/components/TempDesignSystem/Text/Footnote" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" +import ToggleSidePeek from "./ToggleSidePeek" + import styles from "./selectedRoom.module.css" -export default function SelectedRoom() { +export default function SelectedRoom({ + hotelId, + room, +}: { + hotelId: string + room: RoomConfiguration +}) { const intl = useIntl() return (
@@ -22,42 +32,50 @@ export default function SelectedRoom() { />
-
- - {intl.formatMessage({ id: "Your room" })} - -
- {/** - * [TEMP] - * No translation on Subtitles as they will be derived - * from Room selection. - */} - +
+ - Cozy cabin - - - Free rebooking - - - Pay now - + {intl.formatMessage({ id: "Your room" })} + +
+ {/** + * [TEMP] + * No translation on Subtitles as they will be derived + * from Room selection. + */} + + {room.roomType} + + + Free rebooking + + + Pay now + +
+ {room?.roomTypeCode && ( + + )}
- ) -} diff --git a/components/HotelReservation/ReadMore/index.tsx b/components/HotelReservation/ReadMore/index.tsx index 9425b7c9f..a6ef09c06 100644 --- a/components/HotelReservation/ReadMore/index.tsx +++ b/components/HotelReservation/ReadMore/index.tsx @@ -1,111 +1,29 @@ "use client" -import { useState } from "react" -import { useIntl } from "react-intl" +import useSidePeekStore from "@/stores/sidepeek" import { ChevronRightIcon } from "@/components/Icons" -import Accordion from "@/components/TempDesignSystem/Accordion" -import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem" import Button from "@/components/TempDesignSystem/Button" -import SidePeek from "@/components/TempDesignSystem/SidePeek" -import Body from "@/components/TempDesignSystem/Text/Body" -import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" - -import Contact from "../Contact" import styles from "./readMore.module.css" -import { - ParkingProps, - ReadMoreProps, -} from "@/types/components/hotelReservation/selectHotel/selectHotel" -import type { Amenities, Hotel } from "@/types/hotel" +import { ReadMoreProps } from "@/types/components/hotelReservation/selectHotel/selectHotel" +import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek" -function getAmenitiesList(hotel: Hotel) { - const detailedAmenities: Amenities = hotel.detailedFacilities.filter( - // Remove Parking facilities since parking accordion is based on hotel.parking - (facility) => !facility.name.startsWith("Parking") && facility.public - ) - return detailedAmenities -} - -export default function ReadMore({ label, hotel, hotelId }: ReadMoreProps) { - const intl = useIntl() - - const [sidePeekOpen, setSidePeekOpen] = useState(false) - - const amenitiesList = getAmenitiesList(hotel) +export default function ReadMore({ label, hotelId }: ReadMoreProps) { + const openSidePeek = useSidePeekStore((state) => state.openSidePeek) return ( - <> - - { - setSidePeekOpen(false) - }} - > -
- - {intl.formatMessage({ id: "Practical information" })} - - - - {/* parking */} - {hotel.parking.length ? ( - - {hotel.parking.map((p) => ( - - ))} - - ) : null} - - TODO: What content should be in the accessibility section? - - {amenitiesList.map((amenity) => { - return ( -
- {amenity.name} -
- ) - })} -
- {/* TODO: handle linking to Hotel Page */} - -
-
- - ) -} - -function Parking({ parking }: ParkingProps) { - const intl = useIntl() - return ( -
- {`${intl.formatMessage({ id: parking.type })} (${parking.name})`} -
    -
  • - {`${intl.formatMessage({ - id: "Number of charging points for electric cars", - })}: ${parking.numberOfChargingSpaces}`} -
  • -
  • {`${intl.formatMessage({ id: "Parking can be reserved in advance" })}: ${parking.canMakeReservation ? intl.formatMessage({ id: "Yes" }) : intl.formatMessage({ id: "No" })}`}
  • -
  • {`${intl.formatMessage({ id: "Number of parking spots" })}: ${parking.numberOfParkingSpots}`}
  • -
  • {`${intl.formatMessage({ id: "Distance to hotel" })}: ${parking.distanceToHotel} m`}
  • -
  • {`${intl.formatMessage({ id: "Address" })}: ${parking.address}`}
  • -
-
+ ) } diff --git a/components/HotelReservation/SelectRate/RoomSelection/RoomCard/index.tsx b/components/HotelReservation/SelectRate/RoomSelection/RoomCard/index.tsx index c70ddf7ea..dfafe4927 100644 --- a/components/HotelReservation/SelectRate/RoomSelection/RoomCard/index.tsx +++ b/components/HotelReservation/SelectRate/RoomSelection/RoomCard/index.tsx @@ -5,13 +5,13 @@ import { useIntl } from "react-intl" import { RateDefinition } from "@/server/routers/hotels/output" +import ToggleSidePeek from "@/components/HotelReservation/EnterDetails/SelectedRoom/ToggleSidePeek" import FlexibilityOption from "@/components/HotelReservation/SelectRate/RoomSelection/FlexibilityOption" import Body from "@/components/TempDesignSystem/Text/Body" import Caption from "@/components/TempDesignSystem/Text/Caption" import Footnote from "@/components/TempDesignSystem/Text/Footnote" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" -import RoomSidePeek from "../../../../SidePeeks/RoomSidePeek" import ImageGallery from "../../ImageGallery" import { getIconForFeatureCode } from "../../utils" @@ -21,6 +21,7 @@ import type { RoomCardProps } from "@/types/components/hotelReservation/selectRa import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter" export default function RoomCard({ + hotelId, rateDefinitions, roomConfiguration, roomCategories, @@ -87,8 +88,11 @@ export default function RoomCard({ : `${roomSize?.min}-${roomSize?.max}`} m² - {selectedRoom && ( - + {roomConfiguration.roomTypeCode && ( + )}
diff --git a/components/HotelReservation/SelectRate/RoomSelection/index.tsx b/components/HotelReservation/SelectRate/RoomSelection/index.tsx index 112f69f76..89c4dc9a2 100644 --- a/components/HotelReservation/SelectRate/RoomSelection/index.tsx +++ b/components/HotelReservation/SelectRate/RoomSelection/index.tsx @@ -67,6 +67,7 @@ export default function RoomSelection({ {roomConfigurations.map((roomConfiguration) => (
  • state.activeSidePeek) + const hotelId = useSidePeekStore((state) => state.hotelId) + const roomTypeCode = useSidePeekStore((state) => state.roomTypeCode) + const close = useSidePeekStore((state) => state.closeSidePeek) + const lang = useLang() + + const { data: hotelData } = trpc.hotel.hotelData.get.useQuery( + { + hotelId: hotelId ?? "", + language: lang, + include: [HotelIncludeEnum.RoomCategories], + }, + { + enabled: !!hotelId, + initialData: hotel ?? undefined, + } + ) + + const selectedRoom = hotelData?.included?.find((room) => + room.roomTypes.some((type) => type.code === roomTypeCode) + ) + + if (activeSidePeek) { + return ( + <> + {hotelData && ( + + )} + {selectedRoom && ( + + )} + + ) + } + + return null +} diff --git a/components/SidePeeks/HotelSidePeek/hotelSidePeek.module.css b/components/SidePeeks/HotelSidePeek/hotelSidePeek.module.css new file mode 100644 index 000000000..57bb78db4 --- /dev/null +++ b/components/SidePeeks/HotelSidePeek/hotelSidePeek.module.css @@ -0,0 +1,23 @@ +.spacing { + display: flex; + flex-direction: column; + gap: var(--Spacing-x2); +} + +.content { + display: grid; + 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: var(--Spacing-x2) + calc(var(--Spacing-x1) + var(--Spacing-x-one-and-half)); +} + +.list { + font-family: var(--typography-Body-Regular-fontFamily); + list-style: inside; +} diff --git a/components/SidePeeks/HotelSidePeek/index.tsx b/components/SidePeeks/HotelSidePeek/index.tsx new file mode 100644 index 000000000..481db8564 --- /dev/null +++ b/components/SidePeeks/HotelSidePeek/index.tsx @@ -0,0 +1,90 @@ +import { useIntl } from "react-intl" + +import Contact from "@/components/HotelReservation/Contact" +import Accordion from "@/components/TempDesignSystem/Accordion" +import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem" +import Button from "@/components/TempDesignSystem/Button" +import SidePeek from "@/components/TempDesignSystem/SidePeek" +import Body from "@/components/TempDesignSystem/Text/Body" +import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" + +import styles from "./hotelSidePeek.module.css" + +import { HotelSidePeekProps } from "@/types/components/hotelReservation/hotelSidePeek" +import { ParkingProps } from "@/types/components/hotelReservation/selectHotel/selectHotel" +import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek" +import { Amenities, Hotel } from "@/types/hotel" + +function getAmenitiesList(hotel: Hotel) { + const detailedAmenities: Amenities = hotel.detailedFacilities.filter( + // Remove Parking facilities since parking accordion is based on hotel.parking + (facility) => !facility.name.startsWith("Parking") && facility.public + ) + return detailedAmenities +} + +export default function HotelSidePeek({ + hotel, + activeSidePeek, + close, +}: HotelSidePeekProps) { + const intl = useIntl() + const amenitiesList = getAmenitiesList(hotel) + + return ( + +
    + + {intl.formatMessage({ id: "Practical information" })} + + + + {/* parking */} + {hotel.parking.length ? ( + + {hotel.parking.map((p) => ( + + ))} + + ) : null} + + TODO: What content should be in the accessibility section? + + {amenitiesList.map((amenity) => { + return ( +
    + {amenity.name} +
    + ) + })} +
    + {/* TODO: handle linking to Hotel Page */} + +
    +
    + ) +} + +function Parking({ parking }: ParkingProps) { + const intl = useIntl() + return ( +
    + {`${intl.formatMessage({ id: parking.type })} (${parking.name})`} +
      +
    • + {`${intl.formatMessage({ + id: "Number of charging points for electric cars", + })}: ${parking.numberOfChargingSpaces}`} +
    • +
    • {`${intl.formatMessage({ id: "Parking can be reserved in advance" })}: ${parking.canMakeReservation ? intl.formatMessage({ id: "Yes" }) : intl.formatMessage({ id: "No" })}`}
    • +
    • {`${intl.formatMessage({ id: "Number of parking spots" })}: ${parking.numberOfParkingSpots}`}
    • +
    • {`${intl.formatMessage({ id: "Distance to hotel" })}: ${parking.distanceToHotel} m`}
    • +
    • {`${intl.formatMessage({ id: "Address" })}: ${parking.address}`}
    • +
    +
    + ) +} diff --git a/components/SidePeeks/RoomSidePeek/index.tsx b/components/SidePeeks/RoomSidePeek/index.tsx index 1e27577c8..4a66117b8 100644 --- a/components/SidePeeks/RoomSidePeek/index.tsx +++ b/components/SidePeeks/RoomSidePeek/index.tsx @@ -1,7 +1,5 @@ -import { useState } from "react" import { useIntl } from "react-intl" -import { ChevronRightSmallIcon } from "@/components/Icons" import Button from "@/components/TempDesignSystem/Button" import SidePeek from "@/components/TempDesignSystem/SidePeek" import Body from "@/components/TempDesignSystem/Text/Body" @@ -12,10 +10,14 @@ import { getFacilityIcon } from "./facilityIcon" import styles from "./roomSidePeek.module.css" +import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek" import type { RoomSidePeekProps } from "@/types/components/sidePeeks/roomSidePeek" -export default function RoomSidePeek({ room, buttonSize }: RoomSidePeekProps) { - const [isSidePeekOpen, setIsSidePeekOpen] = useState(false) +export default function RoomSidePeek({ + room, + activeSidePeek, + close, +}: RoomSidePeekProps) { const intl = useIntl() const roomSize = room.roomSize @@ -24,84 +26,70 @@ export default function RoomSidePeek({ room, buttonSize }: RoomSidePeekProps) { const images = room.images return ( -
    - - - setIsSidePeekOpen(false)} - > -
    -
    - - {roomSize.min === roomSize.max - ? roomSize.min - : `${roomSize.min} - ${roomSize.max}`} - m².{" "} - {intl.formatMessage( - { id: "booking.accommodatesUpTo" }, - { nrOfGuests: occupancy } - )} - - {images && ( -
    - -
    + +
    +
    + + {roomSize.min === roomSize.max + ? roomSize.min + : `${roomSize.min} - ${roomSize.max}`} + m².{" "} + {intl.formatMessage( + { id: "booking.accommodatesUpTo" }, + { nrOfGuests: occupancy } )} - {roomDescription} -
    -
    - - {intl.formatMessage({ id: "booking.thisRoomIsEquippedWith" })} - -
      - {room.roomFacilities - .sort((a, b) => a.sortOrder - b.sortOrder) - .map((facility) => { - const Icon = getFacilityIcon(facility.name) - return ( -
    • - {Icon && } - - {facility.name} - -
    • - ) - })} -
    -
    -
    - - {intl.formatMessage({ id: "booking.bedOptions" })} - - - {intl.formatMessage({ id: "booking.basedOnAvailability" })} - - {/* TODO: Get data for bed options */} -
    + + {images && ( +
    + +
    + )} + {roomDescription}
    -
    - +
    + + {intl.formatMessage({ id: "booking.thisRoomIsEquippedWith" })} + +
      + {room.roomFacilities + .sort((a, b) => a.sortOrder - b.sortOrder) + .map((facility) => { + const Icon = getFacilityIcon(facility.name) + return ( +
    • + {Icon && } + + {facility.name} + +
    • + ) + })} +
    - -
    +
    + + {intl.formatMessage({ id: "booking.bedOptions" })} + + + {intl.formatMessage({ id: "booking.basedOnAvailability" })} + + {/* TODO: Get data for bed options */} +
    +
    +
    + +
    + ) } diff --git a/server/routers/hotels/query.ts b/server/routers/hotels/query.ts index ae27a8498..636657daf 100644 --- a/server/routers/hotels/query.ts +++ b/server/routers/hotels/query.ts @@ -395,6 +395,7 @@ export const hotelQueryRouter = router({ }) ) return { + hotelId, hotelName: hotelAttributes.name, hotelDescription: hotelAttributes.hotelContent.texts.descriptions.short, hotelLocation: hotelAttributes.location, diff --git a/stores/enter-details.ts b/stores/enter-details.ts index 8b49603e2..994d24e81 100644 --- a/stores/enter-details.ts +++ b/stores/enter-details.ts @@ -11,10 +11,9 @@ import { } from "@/components/HotelReservation/EnterDetails/Details/schema" import { getQueryParamsForEnterDetails } from "@/components/HotelReservation/SelectRate/RoomSelection/utils" -import type { BookingData } from "@/types/components/hotelReservation/enterDetails/bookingData" +import { BookingData } from "@/types/components/hotelReservation/enterDetails/bookingData" import { BreakfastPackage } from "@/types/components/hotelReservation/enterDetails/breakfast" -import type { DetailsSchema } from "@/types/components/hotelReservation/enterDetails/details" -import { SidePeekEnum } from "@/types/components/hotelReservation/enterDetails/sidePeek" +import { DetailsSchema } from "@/types/components/hotelReservation/enterDetails/details" import { StepEnum } from "@/types/components/hotelReservation/enterDetails/step" import { BreakfastPackageEnum } from "@/types/enums/breakfast" @@ -28,7 +27,6 @@ interface EnterDetailsState { roomData: BookingData steps: StepEnum[] currentStep: StepEnum - activeSidePeek: SidePeekEnum | null isValid: Record completeStep: (updatedData: Partial) => void navigate: ( @@ -36,8 +34,6 @@ interface EnterDetailsState { updatedData?: Record ) => void setCurrentStep: (step: StepEnum) => void - openSidePeek: (key: SidePeekEnum | null) => void - closeSidePeek: () => void } export function initEditDetailsState( @@ -139,10 +135,7 @@ export function initEditDetailsState( window.history.pushState({ step }, "", step + window.location.search) }) ), - openSidePeek: (key) => set({ activeSidePeek: key }), - closeSidePeek: () => set({ activeSidePeek: null }), currentStep, - activeSidePeek: null, isValid, completeStep: (updatedData) => set( diff --git a/stores/sidepeek.ts b/stores/sidepeek.ts new file mode 100644 index 000000000..30d6a00e2 --- /dev/null +++ b/stores/sidepeek.ts @@ -0,0 +1,31 @@ +import { create } from "zustand" + +import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek" + +interface SidePeekState { + activeSidePeek: SidePeekEnum | null + hotelId: string | null + roomTypeCode: string | null + openSidePeek: ({ + key, + hotelId, + roomTypeCode, + }: { + key: SidePeekEnum | null + hotelId: string + roomTypeCode?: string + }) => void + closeSidePeek: () => void +} + +const useSidePeekStore = create((set) => ({ + activeSidePeek: null, + hotelId: null, + roomTypeCode: null, + openSidePeek: ({ key, hotelId, roomTypeCode }) => + set({ activeSidePeek: key, hotelId, roomTypeCode }), + closeSidePeek: () => + set({ activeSidePeek: null, hotelId: null, roomTypeCode: null }), +})) + +export default useSidePeekStore diff --git a/types/components/hotelPage/room.ts b/types/components/hotelPage/room.ts index 7be59609d..f8faaecd8 100644 --- a/types/components/hotelPage/room.ts +++ b/types/components/hotelPage/room.ts @@ -1,9 +1,11 @@ import type { RoomData } from "@/types/hotel" export interface RoomCardProps { + hotelId: string room: RoomData } export type RoomsProps = { + hotelId: string rooms: RoomData[] } diff --git a/types/components/hotelReservation/hotelSidePeek.ts b/types/components/hotelReservation/hotelSidePeek.ts new file mode 100644 index 000000000..3f5b2ce7c --- /dev/null +++ b/types/components/hotelReservation/hotelSidePeek.ts @@ -0,0 +1,8 @@ +import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek" +import { Hotel } from "@/types/hotel" + +export type HotelSidePeekProps = { + hotel: Hotel + activeSidePeek: SidePeekEnum + close: () => void +} diff --git a/types/components/hotelReservation/selectRate/roomCard.ts b/types/components/hotelReservation/selectRate/roomCard.ts index b47a8c5bb..caf025524 100644 --- a/types/components/hotelReservation/selectRate/roomCard.ts +++ b/types/components/hotelReservation/selectRate/roomCard.ts @@ -13,6 +13,7 @@ import type { RoomData } from "@/types/hotel" import type { RoomPackageCodes, RoomPackageData } from "./roomFilter" export type RoomCardProps = { + hotelId: string roomConfiguration: RoomConfiguration rateDefinitions: RateDefinition[] roomCategories: RoomData[] diff --git a/types/components/hotelReservation/enterDetails/sidePeek.ts b/types/components/hotelReservation/sidePeek.ts similarity index 79% rename from types/components/hotelReservation/enterDetails/sidePeek.ts rename to types/components/hotelReservation/sidePeek.ts index b109452c6..adeb99ec5 100644 --- a/types/components/hotelReservation/enterDetails/sidePeek.ts +++ b/types/components/hotelReservation/sidePeek.ts @@ -2,6 +2,7 @@ import { Hotel } from "@/types/hotel" export enum SidePeekEnum { hotelDetails = "hotel-detail-side-peek", + roomDetails = "room-detail-side-peek", } export type SidePeekProps = { diff --git a/types/components/hotelReservation/toggleSidePeekProps.ts b/types/components/hotelReservation/toggleSidePeekProps.ts new file mode 100644 index 000000000..b1139e180 --- /dev/null +++ b/types/components/hotelReservation/toggleSidePeekProps.ts @@ -0,0 +1,4 @@ +export type ToggleSidePeekProps = { + hotelId: string + roomTypeCode: string +} diff --git a/types/components/sidePeeks/roomSidePeek.ts b/types/components/sidePeeks/roomSidePeek.ts index 1aab7739a..0298cb83c 100644 --- a/types/components/sidePeeks/roomSidePeek.ts +++ b/types/components/sidePeeks/roomSidePeek.ts @@ -1,6 +1,9 @@ +import { SidePeekEnum } from "../hotelReservation/sidePeek" + import type { RoomData } from "@/types/hotel" export type RoomSidePeekProps = { room: RoomData - buttonSize: "small" | "medium" + activeSidePeek: SidePeekEnum | null + close: () => void }