feat: consume serach params in summary and step page

This commit is contained in:
Christel Westerberg
2024-10-24 10:53:05 +02:00
parent 85fdefb5ac
commit 7954c704d9
27 changed files with 376 additions and 263 deletions

View File

@@ -0,0 +1,77 @@
import {
getHotelData,
getProfileSafely,
getRoomAvailability,
} from "@/lib/trpc/memoizedRequests"
import { HotelIncludeEnum } from "@/server/routers/hotels/input"
import Summary from "@/components/HotelReservation/EnterDetails/Summary"
import { getQueryParamsForEnterDetails } from "@/components/HotelReservation/SelectRate/RoomSelection/utils"
import { formatNumber } from "@/utils/format"
import { SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate"
import { LangParams, PageArgs, SearchParams } from "@/types/params"
export default async function SummaryPage({
params,
searchParams,
}: PageArgs<LangParams, SearchParams<SelectRateSearchParams>>) {
const selectRoomParams = new URLSearchParams(searchParams)
const { hotel, adults, children, roomTypeCode, rateCode, fromDate, toDate } =
getQueryParamsForEnterDetails(selectRoomParams)
const user = await getProfileSafely()
const hotelData = await getHotelData(hotel, params.lang, undefined, [HotelIncludeEnum.RoomCategories])
const availability = await getRoomAvailability({
hotelId: parseInt(hotel),
adults,
children,
roomStayStartDate: fromDate,
roomStayEndDate: toDate,
})
if (!hotelData?.data || !hotelData?.included || !availability) {
console.error("No hotel or availability data", hotelData, availability)
// TODO: handle this case
return null
}
const chosenRoom = availability.roomConfigurations.find(
(availRoom) => availRoom.roomTypeCode === roomTypeCode
)
if (!chosenRoom) {
// TODO: handle this case
console.error("No chosen room", chosenRoom)
return null
}
const cancellationText =
availability?.rateDefinitions.find((rate) => rate.rateCode === rateCode)
?.cancellationText ?? ""
const memberPrice =
chosenRoom.products.find(
(rate) => rate.productType.member?.rateCode === rateCode
)?.productType.member?.localPrice.pricePerStay ?? "0"
const publicPrice =
chosenRoom.products.find(
(rate) => rate.productType.public?.rateCode === rateCode
)?.productType.public?.localPrice.pricePerStay ?? "0"
const price = user ? memberPrice : publicPrice
return (
<Summary
isMember={!!user}
room={{
roomType: chosenRoom.roomType,
price: formatNumber(parseInt(price)),
adults,
cancellationText,
}}
/>
)
}

View File

@@ -1,6 +1,5 @@
import EnterDetailsProvider from "@/components/HotelReservation/EnterDetails/Provider"
import SelectedRoom from "@/components/HotelReservation/EnterDetails/SelectedRoom"
import Summary from "@/components/HotelReservation/EnterDetails/Summary"
import { setLang } from "@/i18n/serverContext"
import { preload } from "./page"
@@ -11,6 +10,7 @@ import { StepEnum } from "@/types/components/hotelReservation/enterDetails/step"
import type { LangParams, LayoutArgs } from "@/types/params"
export default async function StepLayout({
summary,
children,
hotelHeader,
params,
@@ -19,6 +19,7 @@ export default async function StepLayout({
LayoutArgs<LangParams & { step: StepEnum }> & {
hotelHeader: React.ReactNode
sidePeek: React.ReactNode
summary: React.ReactNode
}>) {
setLang(params.lang)
preload()
@@ -29,9 +30,7 @@ export default async function StepLayout({
<div className={styles.content}>
<SelectedRoom />
{children}
<aside className={styles.summary}>
<Summary isMember={false} />
</aside>
<aside className={styles.summary}>{summary}</aside>
</div>
{sidePeek}
</main>

View File

@@ -7,6 +7,7 @@ import {
getProfileSafely,
getRoomAvailability,
} from "@/lib/trpc/memoizedRequests"
import { HotelIncludeEnum } from "@/server/routers/hotels/input"
import BedType from "@/components/HotelReservation/EnterDetails/BedType"
import Breakfast from "@/components/HotelReservation/EnterDetails/Breakfast"
@@ -14,11 +15,12 @@ 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 getHotelReservationQueryParams from "@/components/HotelReservation/SelectRate/RoomSelection/utils"
import { getQueryParamsForEnterDetails } from "@/components/HotelReservation/SelectRate/RoomSelection/utils"
import { getIntl } from "@/i18n"
import { StepEnum } from "@/types/components/hotelReservation/enterDetails/step"
import type { LangParams, PageArgs } from "@/types/params"
import { SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate"
import type { LangParams, PageArgs, } from "@/types/params"
export function preload() {
void getProfileSafely()
@@ -32,35 +34,52 @@ function isValidStep(step: string): step is StepEnum {
export default async function StepPage({
params,
searchParams,
}: PageArgs<LangParams & { step: StepEnum }, { hotel: string }>) {
}: PageArgs<
LangParams & { step: StepEnum },
SelectRateSearchParams
>) {
if (!searchParams.hotel) {
redirect(`/${params.lang}`)
}
void getBreakfastPackages(searchParams.hotel)
const stepParams = new URLSearchParams(searchParams)
const paramsObject = getHotelReservationQueryParams(stepParams)
void getRoomAvailability({
hotelId: paramsObject.hotel,
adults: paramsObject.room[0].adults,
roomStayStartDate: paramsObject.fromDate,
roomStayEndDate: paramsObject.toDate,
})
const intl = await getIntl()
const hotel = await getHotelData(searchParams.hotel, params.lang)
const intl = await getIntl()
const selectRoomParams = new URLSearchParams(searchParams)
const {
hotel: hotelId,
adults,
children,
roomTypeCode,
rateCode,
fromDate,
toDate,
} = getQueryParamsForEnterDetails(selectRoomParams)
void getRoomAvailability({
hotelId: parseInt(hotelId),
adults,
children,
roomStayStartDate: fromDate,
roomStayEndDate: toDate,
rateCode
})
const hotelData = await getHotelData(hotelId, params.lang, undefined, [HotelIncludeEnum.RoomCategories])
const user = await getProfileSafely()
const savedCreditCards = await getCreditCardsSafely()
const breakfastPackages = await getBreakfastPackages(searchParams.hotel)
const roomAvailability = await getRoomAvailability({
hotelId: paramsObject.hotel,
adults: paramsObject.room[0].adults,
roomStayStartDate: paramsObject.fromDate,
roomStayEndDate: paramsObject.toDate,
rateCode: paramsObject.room[0].ratecode,
hotelId: parseInt(hotelId),
adults,
children,
roomStayStartDate: fromDate,
roomStayEndDate: toDate,
rateCode
})
if (!isValidStep(params.step) || !hotel || !roomAvailability) {
if (!isValidStep(params.step) || !hotelData || !roomAvailability) {
return notFound()
}
@@ -79,16 +98,32 @@ export default async function StepPage({
id: "Select payment method",
})
const availableRoom = roomAvailability?.roomConfigurations
.filter((room) => room.status === "Available")
.find((room) => room.roomTypeCode === roomTypeCode)?.roomType
const roomTypes = hotelData.included
?.find((room) => room.name === availableRoom)
?.roomTypes.map((room) => ({
description: room.mainBed.description,
size: room.mainBed.widthRange,
value: room.code,
}))
return (
<section>
<HistoryStateManager />
<SectionAccordion
header={intl.formatMessage({ id: "Select bed" })}
step={StepEnum.selectBed}
label={intl.formatMessage({ id: "Request bedtype" })}
>
<BedType />
</SectionAccordion>
{/* TODO: How to handle no beds found? */}
{roomTypes ? (
<SectionAccordion
header="Select bed"
step={StepEnum.selectBed}
label={intl.formatMessage({ id: "Request bedtype" })}
>
<BedType roomTypes={roomTypes} />
</SectionAccordion>
) : null}
<SectionAccordion
header={intl.formatMessage({ id: "Food options" })}
step={StepEnum.breakfast}
@@ -111,13 +146,13 @@ export default async function StepPage({
<Payment
hotelId={searchParams.hotel}
otherPaymentOptions={
hotel.data.attributes.merchantInformationData
hotelData.data.attributes.merchantInformationData
.alternatePaymentOptions
}
savedCreditCards={savedCreditCards}
mustBeGuaranteed={mustBeGuaranteed}
/>
</SectionAccordion>
</section>
</section >
)
}

View File

@@ -10,7 +10,7 @@ import {
} from "@/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/utils"
import HotelCardListing from "@/components/HotelReservation/HotelCardListing"
import HotelFilter from "@/components/HotelReservation/SelectHotel/HotelFilter"
import getHotelReservationQueryParams from "@/components/HotelReservation/SelectRate/RoomSelection/utils"
import { getHotelReservationQueryParams } from "@/components/HotelReservation/SelectRate/RoomSelection/utils"
import { ChevronRightIcon } from "@/components/Icons"
import StaticMap from "@/components/Maps/StaticMap"
import Link from "@/components/TempDesignSystem/Link"

View File

@@ -2,10 +2,11 @@ import { notFound } from "next/navigation"
import { getProfileSafely } from "@/lib/trpc/memoizedRequests"
import { serverClient } from "@/lib/trpc/server"
import { HotelIncludeEnum } from "@/server/routers/hotels/input"
import HotelInfoCard from "@/components/HotelReservation/SelectRate/HotelInfoCard"
import Rooms from "@/components/HotelReservation/SelectRate/Rooms"
import getHotelReservationQueryParams from "@/components/HotelReservation/SelectRate/RoomSelection/utils"
import { getHotelReservationQueryParams } from "@/components/HotelReservation/SelectRate/RoomSelection/utils"
import { setLang } from "@/i18n/serverContext"
import { generateChildrenString } from "../select-hotel/utils"
@@ -38,7 +39,7 @@ export default async function SelectRatePage({
serverClient().hotel.hotelData.get({
hotelId: searchParams.hotel,
language: params.lang,
include: ["RoomCategories"],
include: [HotelIncludeEnum.RoomCategories],
}),
serverClient().hotel.availability.rooms({
hotelId: parseInt(searchParams.hotel, 10),