feat: consume serach params in summary and step page
This commit is contained in:
@@ -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,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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 >
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -11,7 +11,6 @@ import { CloseLargeIcon } from "@/components/Icons"
|
||||
import { debounce } from "@/utils/debounce"
|
||||
import { getFormattedUrlQueryParams } from "@/utils/url"
|
||||
|
||||
import getHotelReservationQueryParams from "../HotelReservation/SelectRate/RoomSelection/utils"
|
||||
import MobileToggleButton from "./MobileToggleButton"
|
||||
|
||||
import styles from "./bookingWidget.module.css"
|
||||
@@ -41,10 +40,10 @@ export default function BookingWidgetClient({
|
||||
const bookingWidgetSearchData: BookingWidgetSearchParams | undefined =
|
||||
searchParams
|
||||
? (getFormattedUrlQueryParams(new URLSearchParams(searchParams), {
|
||||
adults: "number",
|
||||
age: "number",
|
||||
bed: "number",
|
||||
}) as BookingWidgetSearchParams)
|
||||
adults: "number",
|
||||
age: "number",
|
||||
bed: "number",
|
||||
}) as BookingWidgetSearchParams)
|
||||
: undefined
|
||||
|
||||
const getLocationObj = (destination: string): Location | undefined => {
|
||||
@@ -70,9 +69,9 @@ export default function BookingWidgetClient({
|
||||
|
||||
const selectedLocation = bookingWidgetSearchData
|
||||
? getLocationObj(
|
||||
(bookingWidgetSearchData.city ??
|
||||
bookingWidgetSearchData.hotel) as string
|
||||
)
|
||||
(bookingWidgetSearchData.city ??
|
||||
bookingWidgetSearchData.hotel) as string
|
||||
)
|
||||
: undefined
|
||||
|
||||
const methods = useForm<BookingWidgetSchema>({
|
||||
|
||||
@@ -16,7 +16,18 @@ import styles from "./bedOptions.module.css"
|
||||
|
||||
import type { BedTypeSchema } from "@/types/components/hotelReservation/enterDetails/bedType"
|
||||
|
||||
export default function BedType() {
|
||||
export default function BedType({
|
||||
roomTypes,
|
||||
}: {
|
||||
roomTypes: {
|
||||
description: string
|
||||
size: {
|
||||
min: number
|
||||
max: number
|
||||
}
|
||||
value: string
|
||||
}[]
|
||||
}) {
|
||||
const intl = useIntl()
|
||||
const bedType = useEnterDetailsStore((state) => state.userData.bedType)
|
||||
|
||||
@@ -57,38 +68,25 @@ export default function BedType() {
|
||||
return (
|
||||
<FormProvider {...methods}>
|
||||
<form className={styles.form} onSubmit={methods.handleSubmit(onSubmit)}>
|
||||
<RadioCard
|
||||
Icon={KingBedIcon}
|
||||
iconWidth={46}
|
||||
id={BedTypeEnum.KING}
|
||||
name="bedType"
|
||||
subtitle={intl.formatMessage(
|
||||
{ id: "{width} cm × {length} cm" },
|
||||
{
|
||||
length: "210",
|
||||
width: "180",
|
||||
}
|
||||
)}
|
||||
text={text}
|
||||
title={intl.formatMessage({ id: "King bed" })}
|
||||
value={BedTypeEnum.KING}
|
||||
/>
|
||||
<RadioCard
|
||||
Icon={KingBedIcon}
|
||||
iconWidth={46}
|
||||
id={BedTypeEnum.QUEEN}
|
||||
name="bedType"
|
||||
subtitle={intl.formatMessage(
|
||||
{ id: "{width} cm × {length} cm" },
|
||||
{
|
||||
length: "200",
|
||||
width: "160",
|
||||
}
|
||||
)}
|
||||
text={text}
|
||||
title={intl.formatMessage({ id: "Queen bed" })}
|
||||
value={BedTypeEnum.QUEEN}
|
||||
/>
|
||||
{roomTypes.map((roomType) => {
|
||||
const width =
|
||||
roomType.size.max === roomType.size.min
|
||||
? roomType.size.max
|
||||
: `${roomType.size.min} cm - ${roomType.size.max} cm`
|
||||
return (
|
||||
<RadioCard
|
||||
key={roomType.value}
|
||||
Icon={KingBedIcon}
|
||||
iconWidth={46}
|
||||
id={roomType.value}
|
||||
name="bedType"
|
||||
subtitle={width}
|
||||
text={text}
|
||||
title={roomType.description}
|
||||
value={roomType.description}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</form>
|
||||
</FormProvider>
|
||||
)
|
||||
|
||||
@@ -3,5 +3,5 @@ import { z } from "zod"
|
||||
import { BedTypeEnum } from "@/types/enums/bedType"
|
||||
|
||||
export const bedTypeSchema = z.object({
|
||||
bedType: z.nativeEnum(BedTypeEnum),
|
||||
bedType: z.string(),
|
||||
})
|
||||
|
||||
@@ -56,7 +56,7 @@ export default function Payment({
|
||||
const intl = useIntl()
|
||||
const queryParams = useSearchParams()
|
||||
const { firstName, lastName, email, phoneNumber, countryCode } =
|
||||
useEnterDetailsStore((state) => state.data)
|
||||
useEnterDetailsStore((state) => state.userData)
|
||||
const [confirmationNumber, setConfirmationNumber] = useState<string>("")
|
||||
|
||||
const methods = useForm<PaymentFormData>({
|
||||
|
||||
@@ -1,52 +1,43 @@
|
||||
"use client"
|
||||
|
||||
import { useEffect, useState } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { dt } from "@/lib/dt"
|
||||
import { trpc } from "@/lib/trpc/client"
|
||||
import { useEnterDetailsStore } from "@/stores/enter-details"
|
||||
|
||||
import { ArrowRightIcon } from "@/components/Icons"
|
||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||
import Divider from "@/components/TempDesignSystem/Divider"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import useLang from "@/hooks/useLang"
|
||||
import { formatNumber } from "@/utils/format"
|
||||
|
||||
import styles from "./summary.module.css"
|
||||
|
||||
import { RoomsData } from "@/types/components/hotelReservation/enterDetails/bookingData"
|
||||
|
||||
export default function Summary({ isMember }: { isMember: boolean }) {
|
||||
export default function Summary({
|
||||
isMember,
|
||||
room,
|
||||
}: {
|
||||
isMember: boolean
|
||||
room: RoomsData
|
||||
}) {
|
||||
const [chosenBed, setChosenBed] = useState<string | undefined>()
|
||||
const [chosenBreakfast, setCosenBreakfast] = useState<string | undefined>()
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
const { fromDate, toDate, rooms, hotel, bedType, breakfast } =
|
||||
useEnterDetailsStore((state) => ({
|
||||
fromDate: state.roomData.fromdate,
|
||||
toDate: state.roomData.todate,
|
||||
const { fromDate, toDate, bedType, breakfast } = useEnterDetailsStore(
|
||||
(state) => ({
|
||||
fromDate: state.roomData.fromDate,
|
||||
toDate: state.roomData.toDate,
|
||||
rooms: state.roomData.room,
|
||||
hotel: state.roomData.hotel,
|
||||
bedType: state.userData.bedType,
|
||||
breakfast: state.userData.breakfast,
|
||||
}))
|
||||
|
||||
const totalAdults = rooms.reduce((total, room) => total + room.adults, 0)
|
||||
|
||||
const {
|
||||
data: availabilityData,
|
||||
isLoading,
|
||||
error,
|
||||
} = trpc.hotel.availability.rooms.useQuery(
|
||||
{
|
||||
hotelId: parseInt(hotel),
|
||||
adults: totalAdults,
|
||||
roomStayStartDate: dt(fromDate).format("YYYY-MM-DD"),
|
||||
roomStayEndDate: dt(toDate).format("YYYY-MM-DD"),
|
||||
},
|
||||
{ enabled: !!hotel && !!fromDate && !!toDate }
|
||||
})
|
||||
)
|
||||
|
||||
const diff = dt(toDate).diff(fromDate, "days")
|
||||
@@ -56,37 +47,15 @@ export default function Summary({ isMember }: { isMember: boolean }) {
|
||||
{ totalNights: diff }
|
||||
)
|
||||
|
||||
if (isLoading) {
|
||||
return <LoadingSpinner />
|
||||
let color: "uiTextHighContrast" | "red" = "uiTextHighContrast"
|
||||
if (isMember) {
|
||||
color = "red"
|
||||
}
|
||||
const populatedRooms = rooms
|
||||
.map((room) => {
|
||||
const chosenRoom = availabilityData?.roomConfigurations.find(
|
||||
(availRoom) => room.roomtypecode === availRoom.roomTypeCode
|
||||
)
|
||||
const cancellationText = availabilityData?.rateDefinitions.find(
|
||||
(rate) => rate.rateCode === room.ratecode
|
||||
)?.cancellationText
|
||||
|
||||
if (chosenRoom) {
|
||||
const memberPrice = chosenRoom.products.find(
|
||||
(rate) => rate.productType.member?.rateCode === room.ratecode
|
||||
)?.productType.member?.localPrice.pricePerStay
|
||||
const publicPrice = chosenRoom.products.find(
|
||||
(rate) => rate.productType.public?.rateCode === room.ratecode
|
||||
)?.productType.public?.localPrice.pricePerStay
|
||||
|
||||
return {
|
||||
roomType: chosenRoom.roomType,
|
||||
memberPrice: memberPrice && formatNumber(parseInt(memberPrice)),
|
||||
publicPrice: publicPrice && formatNumber(parseInt(publicPrice)),
|
||||
adults: room.adults,
|
||||
children: room.child,
|
||||
cancellationText,
|
||||
}
|
||||
}
|
||||
})
|
||||
.filter((room): room is RoomsData => room !== undefined)
|
||||
useEffect(() => {
|
||||
setChosenBed(bedType)
|
||||
setCosenBreakfast(breakfast)
|
||||
}, [bedType, breakfast])
|
||||
|
||||
return (
|
||||
<section className={styles.summary}>
|
||||
@@ -100,14 +69,48 @@ export default function Summary({ isMember }: { isMember: boolean }) {
|
||||
</header>
|
||||
<Divider color="primaryLightSubtle" />
|
||||
<div className={styles.addOns}>
|
||||
{populatedRooms.map((room, idx) => (
|
||||
<RoomBreakdown key={idx} room={room} isMember={isMember} />
|
||||
))}
|
||||
|
||||
{bedType ? (
|
||||
<div>
|
||||
<div className={styles.entry}>
|
||||
<Body color="textHighContrast">{bedType}</Body>
|
||||
<Caption color="red">
|
||||
<Body color="textHighContrast">{room.roomType}</Body>
|
||||
<Caption color={color}>
|
||||
{intl.formatMessage(
|
||||
{ id: "{amount} {currency}" },
|
||||
{ amount: room.price, currency: "SEK" }
|
||||
)}
|
||||
</Caption>
|
||||
</div>
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{intl.formatMessage(
|
||||
{ id: "booking.adults" },
|
||||
{ totalAdults: room.adults }
|
||||
)}
|
||||
</Caption>
|
||||
{room.children?.length ? (
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{intl.formatMessage(
|
||||
{ id: "booking.children" },
|
||||
{ totalChildren: room.children.length }
|
||||
)}
|
||||
</Caption>
|
||||
) : null}
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{room.cancellationText}
|
||||
</Caption>
|
||||
<Link color="burgundy" href="#" variant="underscored" size="small">
|
||||
{intl.formatMessage({ id: "Rate details" })}
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{chosenBed ? (
|
||||
<div className={styles.entry}>
|
||||
<div>
|
||||
<Body color="textHighContrast">{chosenBed}</Body>
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{intl.formatMessage({ id: "Based on availability" })}
|
||||
</Caption>
|
||||
</div>
|
||||
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{intl.formatMessage(
|
||||
{ id: "{amount} {currency}" },
|
||||
{ amount: "0", currency: "SEK" }
|
||||
@@ -115,10 +118,11 @@ export default function Summary({ isMember }: { isMember: boolean }) {
|
||||
</Caption>
|
||||
</div>
|
||||
) : null}
|
||||
{breakfast ? (
|
||||
|
||||
{chosenBreakfast ? (
|
||||
<div className={styles.entry}>
|
||||
<Body color="textHighContrast">{breakfast}</Body>
|
||||
<Caption color="red">
|
||||
<Body color="textHighContrast">{chosenBreakfast}</Body>
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{intl.formatMessage(
|
||||
{ id: "{amount} {currency}" },
|
||||
{ amount: "0", currency: "SEK" }
|
||||
@@ -130,77 +134,35 @@ export default function Summary({ isMember }: { isMember: boolean }) {
|
||||
<Divider color="primaryLightSubtle" />
|
||||
<div className={styles.total}>
|
||||
<div className={styles.entry}>
|
||||
<Body textTransform="bold">
|
||||
{intl.formatMessage({ id: "Total price (incl VAT)" })}
|
||||
</Body>
|
||||
<Body textTransform="bold">
|
||||
{intl.formatMessage(
|
||||
{ id: "{amount} {currency}" },
|
||||
{ amount: "4686", currency: "SEK" }
|
||||
)}
|
||||
</Body>
|
||||
</div>
|
||||
<div className={styles.entry}>
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{intl.formatMessage({ id: "Approx." })}
|
||||
</Caption>
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{intl.formatMessage(
|
||||
{ id: "{amount} {currency}" },
|
||||
{ amount: "455", currency: "EUR" }
|
||||
)}
|
||||
</Caption>
|
||||
<div>
|
||||
<Body>
|
||||
{intl.formatMessage<React.ReactNode>(
|
||||
{ id: "<b>Total price</b> (incl VAT)" },
|
||||
{ b: (str) => <b>{str}</b> }
|
||||
)}
|
||||
</Body>
|
||||
<Link color="burgundy" href="#" variant="underscored" size="small">
|
||||
{intl.formatMessage({ id: "Price details" })}
|
||||
</Link>
|
||||
</div>
|
||||
<div>
|
||||
<Body textTransform="bold">
|
||||
{intl.formatMessage(
|
||||
{ id: "{amount} {currency}" },
|
||||
{ amount: room.price, currency: "SEK" } // TODO: calculate total price
|
||||
)}
|
||||
</Body>
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{intl.formatMessage({ id: "Approx." })}{" "}
|
||||
{intl.formatMessage(
|
||||
{ id: "{amount} {currency}" },
|
||||
{ amount: "455", currency: "EUR" }
|
||||
)}
|
||||
</Caption>
|
||||
</div>
|
||||
</div>
|
||||
<Divider color="primaryLightSubtle" />
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
function RoomBreakdown({
|
||||
room,
|
||||
isMember,
|
||||
}: {
|
||||
room: RoomsData
|
||||
isMember: boolean
|
||||
}) {
|
||||
const intl = useIntl()
|
||||
|
||||
let color: "uiTextHighContrast" | "red" = "uiTextHighContrast"
|
||||
let price = room.publicPrice
|
||||
if (isMember) {
|
||||
color = "red"
|
||||
price = room.memberPrice
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.entry}>
|
||||
<Body color="textHighContrast">{room.roomType}</Body>
|
||||
<Caption color={color}>
|
||||
{intl.formatMessage(
|
||||
{ id: "{amount} {currency}" },
|
||||
{ amount: price, currency: "SEK" }
|
||||
)}
|
||||
</Caption>
|
||||
</div>
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{intl.formatMessage(
|
||||
{ id: "booking.adults" },
|
||||
{ totalAdults: room.adults }
|
||||
)}
|
||||
</Caption>
|
||||
{room.children?.length ? (
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{intl.formatMessage(
|
||||
{ id: "booking.children" },
|
||||
{ totalChildren: room.children.length }
|
||||
)}
|
||||
</Caption>
|
||||
) : null}
|
||||
<Caption color="uiTextMediumContrast">{room.cancellationText}</Caption>
|
||||
<Link color="burgundy" href="#" variant="underscored" size="small">
|
||||
{intl.formatMessage({ id: "Rate details" })}
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
.summary {
|
||||
background-color: var(--Main-Grey-White);
|
||||
border: 1px solid var(--Primary-Light-On-Surface-Divider-subtle);
|
||||
border-radius: var(--Corner-radius-Large);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
padding: var(--Spacing-x2);
|
||||
padding: var(--Spacing-x3);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.date {
|
||||
@@ -31,6 +30,9 @@
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.entry > :last-child {
|
||||
justify-items: flex-end;
|
||||
}
|
||||
.total {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useMemo, useState } from "react"
|
||||
|
||||
import RateSummary from "./RateSummary"
|
||||
import RoomCard from "./RoomCard"
|
||||
import getHotelReservationQueryParams from "./utils"
|
||||
import { getHotelReservationQueryParams } from "./utils"
|
||||
|
||||
import styles from "./roomSelection.module.css"
|
||||
|
||||
|
||||
@@ -2,11 +2,22 @@ import { getFormattedUrlQueryParams } from "@/utils/url"
|
||||
|
||||
import type { SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||
|
||||
function getHotelReservationQueryParams(searchParams: URLSearchParams) {
|
||||
export function getHotelReservationQueryParams(searchParams: URLSearchParams) {
|
||||
return getFormattedUrlQueryParams(searchParams, {
|
||||
adults: "number",
|
||||
age: "number",
|
||||
}) as SelectRateSearchParams
|
||||
}
|
||||
|
||||
export default getHotelReservationQueryParams
|
||||
export function getQueryParamsForEnterDetails(searchParams: URLSearchParams) {
|
||||
const selectRoomParamsObject = getHotelReservationQueryParams(searchParams)
|
||||
|
||||
const { room } = selectRoomParamsObject
|
||||
return {
|
||||
...selectRoomParamsObject,
|
||||
adults: room[0].adults, // TODO: Handle multiple rooms
|
||||
children: room[0].child?.length.toString(), // TODO: Handle multiple rooms
|
||||
roomTypeCode: room[0].roomtypecode,
|
||||
rateCode: room[0].ratecode,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ const config = {
|
||||
textHighContrast: styles.textHighContrast,
|
||||
white: styles.white,
|
||||
peach50: styles.peach50,
|
||||
baseTextMediumContrast: styles.baseTextMediumContrast,
|
||||
uiTextHighContrast: styles.uiTextHighContrast,
|
||||
uiTextMediumContrast: styles.uiTextMediumContrast,
|
||||
uiTextPlaceholder: styles.uiTextPlaceholder,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"<b>Included</b> (based on availability)": "<b>Inkluderet</b> (baseret på tilgængelighed)",
|
||||
"<b>Total price</b> (incl VAT)": "<b>Samlet pris</b> (inkl. moms)",
|
||||
"<b>{amount} {currency}</b>/night per adult": "<b>{amount} {currency}</b>/nat pr. voksen",
|
||||
"A destination or hotel name is needed to be able to search for a hotel room.": "Et destinations- eller hotelnavn er nødvendigt for at kunne søge efter et hotelværelse.",
|
||||
"A photo of the room": "Et foto af værelset",
|
||||
"ACCE": "Tilgængelighed",
|
||||
@@ -35,6 +37,7 @@
|
||||
"Attractions": "Attraktioner",
|
||||
"Back to scandichotels.com": "Tilbage til scandichotels.com",
|
||||
"Bar": "Bar",
|
||||
"Based on availability": "Baseret på tilgængelighed",
|
||||
"Bed type": "Seng type",
|
||||
"Birth date": "Fødselsdato",
|
||||
"Book": "Book",
|
||||
@@ -242,12 +245,14 @@
|
||||
"Points needed to stay on level": "Point nødvendige for at holde sig på niveau",
|
||||
"Previous": "Forudgående",
|
||||
"Previous victories": "Tidligere sejre",
|
||||
"Price details": "Prisoplysninger",
|
||||
"Proceed to login": "Fortsæt til login",
|
||||
"Proceed to payment method": "Fortsæt til betalingsmetode",
|
||||
"Provide a payment card in the next step": "Giv os dine betalingsoplysninger i næste skridt",
|
||||
"Public price from": "Offentlig pris fra",
|
||||
"Public transport": "Offentlig transport",
|
||||
"Queen bed": "Queensize-seng",
|
||||
"Rate details": "Oplysninger om værelsespris",
|
||||
"Read more": "Læs mere",
|
||||
"Read more & book a table": "Read more & book a table",
|
||||
"Read more about the hotel": "Læs mere om hotellet",
|
||||
@@ -405,6 +410,5 @@
|
||||
"uppercase letter": "stort bogstav",
|
||||
"{amount} out of {total}": "{amount} ud af {total}",
|
||||
"{amount} {currency}": "{amount} {currency}",
|
||||
"{difference}{amount} {currency}": "{difference}{amount} {currency}",
|
||||
"{width} cm × {length} cm": "{width} cm × {length} cm"
|
||||
"{difference}{amount} {currency}": "{difference}{amount} {currency}"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"<b>Included</b> (based on availability)": "<b>Inbegriffen</b> (je nach Verfügbarkeit)",
|
||||
"<b>Total price</b> (incl VAT)": "<b>Gesamtpreis</b> (inkl. MwSt.)",
|
||||
"<b>{amount} {currency}</b>/night per adult": "<b>{amount} {currency}</b>/Nacht pro Erwachsener",
|
||||
"A destination or hotel name is needed to be able to search for a hotel room.": "Ein Reiseziel oder Hotelname wird benötigt, um nach einem Hotelzimmer suchen zu können.",
|
||||
"A photo of the room": "Ein Foto des Zimmers",
|
||||
"ACCE": "Zugänglichkeit",
|
||||
@@ -35,6 +37,7 @@
|
||||
"Attraction": "Attraktion",
|
||||
"Back to scandichotels.com": "Zurück zu scandichotels.com",
|
||||
"Bar": "Bar",
|
||||
"Based on availability": "Je nach Verfügbarkeit",
|
||||
"Bed type": "Bettentyp",
|
||||
"Birth date": "Geburtsdatum",
|
||||
"Book": "Buchen",
|
||||
@@ -240,12 +243,14 @@
|
||||
"Points needed to stay on level": "Erforderliche Punkte, um auf diesem Level zu bleiben",
|
||||
"Previous": "Früher",
|
||||
"Previous victories": "Bisherige Siege",
|
||||
"Price details": "Preisdetails",
|
||||
"Proceed to login": "Weiter zum Login",
|
||||
"Proceed to payment method": "Weiter zur Zahlungsmethode",
|
||||
"Provide a payment card in the next step": "Geben Sie Ihre Zahlungskarteninformationen im nächsten Schritt an",
|
||||
"Public price from": "Öffentlicher Preis ab",
|
||||
"Public transport": "Öffentliche Verkehrsmittel",
|
||||
"Queen bed": "Queensize-Bett",
|
||||
"Rate details": "Preisdetails",
|
||||
"Read more": "Mehr lesen",
|
||||
"Read more & book a table": "Read more & book a table",
|
||||
"Read more about the hotel": "Lesen Sie mehr über das Hotel",
|
||||
@@ -404,6 +409,5 @@
|
||||
"uppercase letter": "großbuchstabe",
|
||||
"{amount} out of {total}": "{amount} von {total}",
|
||||
"{amount} {currency}": "{amount} {currency}",
|
||||
"{difference}{amount} {currency}": "{difference}{amount} {currency}",
|
||||
"{width} cm × {length} cm": "{width} cm × {length} cm"
|
||||
"{difference}{amount} {currency}": "{difference}{amount} {currency}"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"<b>Included</b> (based on availability)": "<b>Included</b> (based on availability)",
|
||||
"<b>Total price</b> (incl VAT)": "<b>Total price</b> (incl VAT)",
|
||||
"<b>{amount} {currency}</b>/night per adult": "<b>{amount} {currency}</b>/night per adult",
|
||||
"A destination or hotel name is needed to be able to search for a hotel room.": "A destination or hotel name is needed to be able to search for a hotel room.",
|
||||
"A photo of the room": "A photo of the room",
|
||||
"ACCE": "Accessibility",
|
||||
@@ -38,6 +40,7 @@
|
||||
"Attractions": "Attractions",
|
||||
"Back to scandichotels.com": "Back to scandichotels.com",
|
||||
"Bar": "Bar",
|
||||
"Based on availability": "Based on availability",
|
||||
"Bed": "Bed",
|
||||
"Bed type": "Bed type",
|
||||
"Birth date": "Birth date",
|
||||
@@ -252,6 +255,7 @@
|
||||
"Points needed to stay on level": "Points needed to stay on level",
|
||||
"Previous": "Previous",
|
||||
"Previous victories": "Previous victories",
|
||||
"Price details": "Price details",
|
||||
"Print confirmation": "Print confirmation",
|
||||
"Proceed to login": "Proceed to login",
|
||||
"Proceed to payment method": "Proceed to payment method",
|
||||
@@ -259,6 +263,7 @@
|
||||
"Public price from": "Public price from",
|
||||
"Public transport": "Public transport",
|
||||
"Queen bed": "Queen bed",
|
||||
"Rate details": "Rate details",
|
||||
"Read more": "Read more",
|
||||
"Read more & book a table": "Read more & book a table",
|
||||
"Read more about the hotel": "Read more about the hotel",
|
||||
@@ -329,7 +334,6 @@
|
||||
"Total cost": "Total cost",
|
||||
"Total price": "Total price",
|
||||
"Total Points": "Total Points",
|
||||
"Total price (incl VAT)": "Total price (incl VAT)",
|
||||
"Tourist": "Tourist",
|
||||
"Transaction date": "Transaction date",
|
||||
"Transactions": "Transactions",
|
||||
@@ -427,6 +431,5 @@
|
||||
"{amount} out of {total}": "{amount} out of {total}",
|
||||
"{amount} {currency}": "{amount} {currency}",
|
||||
"{card} ending with {cardno}": "{card} ending with {cardno}",
|
||||
"{difference}{amount} {currency}": "{difference}{amount} {currency}",
|
||||
"{width} cm × {length} cm": "{width} cm × {length} cm"
|
||||
"{difference}{amount} {currency}": "{difference}{amount} {currency}"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"<b>Included</b> (based on availability)": "<b>Sisältyy</b> (saatavuuden mukaan)",
|
||||
"<b>Total price</b> (incl VAT)": "<b>Kokonaishinta</b> (sis. ALV)",
|
||||
"<b>{amount} {currency}</b>/night per adult": "<b>{amount} {currency}</b>/yö per aikuinen",
|
||||
"A destination or hotel name is needed to be able to search for a hotel room.": "Kohteen tai hotellin nimi tarvitaan, jotta hotellihuonetta voidaan hakea.",
|
||||
"A photo of the room": "Kuva huoneesta",
|
||||
"ACCE": "Saavutettavuus",
|
||||
@@ -35,6 +37,7 @@
|
||||
"Attractions": "Nähtävyydet",
|
||||
"Back to scandichotels.com": "Takaisin scandichotels.com",
|
||||
"Bar": "Bar",
|
||||
"Based on availability": "Saatavuuden mukaan",
|
||||
"Bed type": "Vuodetyyppi",
|
||||
"Birth date": "Syntymäaika",
|
||||
"Book": "Varaa",
|
||||
@@ -242,12 +245,14 @@
|
||||
"Points needed to stay on level": "Tällä tasolla pysymiseen tarvittavat pisteet",
|
||||
"Previous": "Aikaisempi",
|
||||
"Previous victories": "Edelliset voitot",
|
||||
"Price details": "Hintatiedot",
|
||||
"Proceed to login": "Jatka kirjautumiseen",
|
||||
"Proceed to payment method": "Siirry maksutavalle",
|
||||
"Provide a payment card in the next step": "Anna maksukortin tiedot seuraavassa vaiheessa",
|
||||
"Public price from": "Julkinen hinta alkaen",
|
||||
"Public transport": "Julkinen liikenne",
|
||||
"Queen bed": "Queen-vuode",
|
||||
"Rate details": "Hintatiedot",
|
||||
"Read more": "Lue lisää",
|
||||
"Read more & book a table": "Read more & book a table",
|
||||
"Read more about the hotel": "Lue lisää hotellista",
|
||||
@@ -404,6 +409,5 @@
|
||||
"uppercase letter": "iso kirjain",
|
||||
"{amount} out of {total}": "{amount}/{total}",
|
||||
"{amount} {currency}": "{amount} {currency}",
|
||||
"{difference}{amount} {currency}": "{difference}{amount} {currency}",
|
||||
"{width} cm × {length} cm": "{width} cm × {length} cm"
|
||||
"{difference}{amount} {currency}": "{difference}{amount} {currency}"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"<b>Included</b> (based on availability)": "<b>Inkludert</b> (basert på tilgjengelighet)",
|
||||
"<b>Total price</b> (incl VAT)": "<b>Totalpris</b> (inkl. mva)",
|
||||
"<b>{amount} {currency}</b>/night per adult": "<b>{amount} {currency}</b>/natt per voksen",
|
||||
"A destination or hotel name is needed to be able to search for a hotel room.": "Et reisemål eller hotellnavn er nødvendig for å kunne søke etter et hotellrom.",
|
||||
"A photo of the room": "Et bilde av rommet",
|
||||
"ACCE": "Tilgjengelighet",
|
||||
@@ -35,6 +37,7 @@
|
||||
"Attractions": "Attraksjoner",
|
||||
"Back to scandichotels.com": "Tilbake til scandichotels.com",
|
||||
"Bar": "Bar",
|
||||
"Based on availability": "Basert på tilgjengelighet",
|
||||
"Bed type": "Seng type",
|
||||
"Birth date": "Fødselsdato",
|
||||
"Book": "Bestill",
|
||||
@@ -240,12 +243,14 @@
|
||||
"Points needed to stay on level": "Poeng som trengs for å holde seg på nivå",
|
||||
"Previous": "Tidligere",
|
||||
"Previous victories": "Tidligere seire",
|
||||
"Price details": "Prisdetaljer",
|
||||
"Proceed to login": "Fortsett til innlogging",
|
||||
"Proceed to payment method": "Fortsett til betalingsmetode",
|
||||
"Provide a payment card in the next step": "Gi oss dine betalingskortdetaljer i neste steg",
|
||||
"Public price from": "Offentlig pris fra",
|
||||
"Public transport": "Offentlig transport",
|
||||
"Queen bed": "Queen-size-seng",
|
||||
"Rate details": "Prisdetaljer",
|
||||
"Read more": "Les mer",
|
||||
"Read more & book a table": "Read more & book a table",
|
||||
"Read more about the hotel": "Les mer om hotellet",
|
||||
@@ -402,6 +407,5 @@
|
||||
"uppercase letter": "stor bokstav",
|
||||
"{amount} out of {total}": "{amount} av {total}",
|
||||
"{amount} {currency}": "{amount} {currency}",
|
||||
"{difference}{amount} {currency}": "{difference}{amount} {currency}",
|
||||
"{width} cm × {length} cm": "{width} cm × {length} cm"
|
||||
"{difference}{amount} {currency}": "{difference}{amount} {currency}"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"<b>Included</b> (based on availability)": "<b>Ingår</b> (baserat på tillgänglighet)",
|
||||
"<b>Total price</b> (incl VAT)": "<b>Totalpris</b> (inkl moms)",
|
||||
"<b>{amount} {currency}</b>/night per adult": "<b>{amount} {currency}</b>/natt per vuxen",
|
||||
"A destination or hotel name is needed to be able to search for a hotel room.": "Ett destinations- eller hotellnamn behövs för att kunna söka efter ett hotellrum.",
|
||||
"A photo of the room": "Ett foto av rummet",
|
||||
"ACCE": "Tillgänglighet",
|
||||
@@ -35,6 +37,7 @@
|
||||
"Attractions": "Sevärdheter",
|
||||
"Back to scandichotels.com": "Tillbaka till scandichotels.com",
|
||||
"Bar": "Bar",
|
||||
"Based on availability": "Baserat på tillgänglighet",
|
||||
"Bed type": "Sängtyp",
|
||||
"Birth date": "Födelsedatum",
|
||||
"Book": "Boka",
|
||||
@@ -240,12 +243,14 @@
|
||||
"Points needed to stay on level": "Poäng som behövs för att hålla sig på nivå",
|
||||
"Previous": "Föregående",
|
||||
"Previous victories": "Tidigare segrar",
|
||||
"Price details": "Prisdetaljer",
|
||||
"Proceed to login": "Fortsätt till inloggning",
|
||||
"Proceed to payment method": "Gå vidare till betalningsmetod",
|
||||
"Provide a payment card in the next step": "Ge oss dina betalkortdetaljer i nästa steg",
|
||||
"Public price from": "Offentligt pris från",
|
||||
"Public transport": "Kollektivtrafik",
|
||||
"Queen bed": "Queen size-säng",
|
||||
"Rate details": "Detaljer om rumspriset",
|
||||
"Read more": "Läs mer",
|
||||
"Read more & book a table": "Read more & book a table",
|
||||
"Read more about the hotel": "Läs mer om hotellet",
|
||||
@@ -405,6 +410,5 @@
|
||||
"paying": "betalar",
|
||||
"uppercase letter": "stor bokstav",
|
||||
"{amount} {currency}": "{amount} {currency}",
|
||||
"{difference}{amount} {currency}": "{difference}{amount} {currency}",
|
||||
"{width} cm × {length} cm": "{width} cm × {length} cm"
|
||||
"{difference}{amount} {currency}": "{difference}{amount} {currency}"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { cache } from "react"
|
||||
|
||||
import { Lang } from "@/constants/languages"
|
||||
import {
|
||||
GetRoomsAvailabilityInput,
|
||||
HotelIncludeEnum,
|
||||
} from "@/server/routers/hotels/input"
|
||||
|
||||
import { serverClient } from "../server"
|
||||
|
||||
@@ -53,12 +57,14 @@ export const getUserTracking = cache(async function getMemoizedUserTracking() {
|
||||
export const getHotelData = cache(async function getMemoizedHotelData(
|
||||
hotelId: string,
|
||||
language: string,
|
||||
isCardOnlyPayment?: boolean
|
||||
isCardOnlyPayment?: boolean,
|
||||
include?: HotelIncludeEnum[]
|
||||
) {
|
||||
return serverClient().hotel.hotelData.get({
|
||||
hotelId,
|
||||
language,
|
||||
isCardOnlyPayment,
|
||||
include,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -71,17 +77,9 @@ export const getRoomAvailability = cache(
|
||||
children,
|
||||
promotionCode,
|
||||
rateCode,
|
||||
}: {
|
||||
hotelId: string
|
||||
adults: number
|
||||
roomStayStartDate: string
|
||||
roomStayEndDate: string
|
||||
children?: string
|
||||
promotionCode?: string
|
||||
rateCode?: string
|
||||
}) {
|
||||
}: GetRoomsAvailabilityInput) {
|
||||
return serverClient().hotel.availability.rooms({
|
||||
hotelId: parseInt(hotelId),
|
||||
hotelId,
|
||||
adults,
|
||||
roomStayStartDate,
|
||||
roomStayEndDate,
|
||||
|
||||
@@ -29,17 +29,26 @@ export const getRoomsAvailabilityInputSchema = z.object({
|
||||
rateCode: z.string().optional(),
|
||||
})
|
||||
|
||||
export type GetRoomsAvailabilityInput = z.input<
|
||||
typeof getRoomsAvailabilityInputSchema
|
||||
>
|
||||
|
||||
export const getRatesInputSchema = z.object({
|
||||
hotelId: z.string(),
|
||||
})
|
||||
|
||||
export const getlHotelDataInputSchema = z.object({
|
||||
export enum HotelIncludeEnum {
|
||||
"RoomCategories",
|
||||
"NearbyHotels",
|
||||
"Restaurants",
|
||||
"City",
|
||||
}
|
||||
|
||||
export const getHotelDataInputSchema = z.object({
|
||||
hotelId: z.string(),
|
||||
language: z.string(),
|
||||
isCardOnlyPayment: z.boolean().optional(),
|
||||
include: z
|
||||
.array(z.enum(["RoomCategories", "NearbyHotels", "Restaurants", "City"]))
|
||||
.optional(),
|
||||
include: z.array(z.nativeEnum(HotelIncludeEnum)).optional(),
|
||||
})
|
||||
|
||||
export const getBreakfastPackageInput = z.object({
|
||||
|
||||
@@ -32,9 +32,9 @@ import {
|
||||
} from "./schemas/packages"
|
||||
import {
|
||||
getBreakfastPackageInput,
|
||||
getHotelDataInputSchema,
|
||||
getHotelInputSchema,
|
||||
getHotelsAvailabilityInputSchema,
|
||||
getlHotelDataInputSchema,
|
||||
getRatesInputSchema,
|
||||
getRoomsAvailabilityInputSchema,
|
||||
} from "./input"
|
||||
@@ -584,7 +584,7 @@ export const hotelQueryRouter = router({
|
||||
}),
|
||||
hotelData: router({
|
||||
get: serviceProcedure
|
||||
.input(getlHotelDataInputSchema)
|
||||
.input(getHotelDataInputSchema)
|
||||
.query(async ({ ctx, input }) => {
|
||||
const { hotelId, language, include, isCardOnlyPayment } = input
|
||||
|
||||
|
||||
@@ -87,8 +87,11 @@ export const roomSchema = z
|
||||
name: data.attributes.name,
|
||||
occupancy: data.attributes.occupancy,
|
||||
roomSize: data.attributes.roomSize,
|
||||
roomTypes: data.attributes.roomTypes,
|
||||
sortOrder: data.attributes.sortOrder,
|
||||
type: data.type,
|
||||
roomFacilities: data.attributes.roomFacilities,
|
||||
}
|
||||
})
|
||||
|
||||
export type RoomType = Pick<z.output<typeof roomSchema>, "roomTypes" | "name">
|
||||
|
||||
@@ -6,21 +6,20 @@ import { create, useStore } from "zustand"
|
||||
import { bedTypeSchema } from "@/components/HotelReservation/EnterDetails/BedType/schema"
|
||||
import { breakfastStoreSchema } from "@/components/HotelReservation/EnterDetails/Breakfast/schema"
|
||||
import { detailsSchema } from "@/components/HotelReservation/EnterDetails/Details/schema"
|
||||
import getHotelReservationQueryParams from "@/components/HotelReservation/SelectRate/RoomSelection/utils"
|
||||
import { getHotelReservationQueryParams } from "@/components/HotelReservation/SelectRate/RoomSelection/utils"
|
||||
|
||||
import type { 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 { StepEnum } from "@/types/components/hotelReservation/enterDetails/step"
|
||||
import { BedTypeEnum } from "@/types/enums/bedType"
|
||||
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
|
||||
import type { BookingData } from "@/types/components/hotelReservation/enterDetails/bookingData"
|
||||
import type { DetailsSchema } from "@/types/components/hotelReservation/enterDetails/details"
|
||||
|
||||
const SESSION_STORAGE_KEY = "enterDetails"
|
||||
|
||||
interface EnterDetailsState {
|
||||
userData: {
|
||||
bedType: BedTypeEnum | undefined
|
||||
bedType: string | undefined
|
||||
breakfast: BreakfastPackage | BreakfastPackageEnum.NO_BREAKFAST | undefined
|
||||
} & DetailsSchema
|
||||
roomData: BookingData
|
||||
|
||||
@@ -5,23 +5,21 @@ interface Child {
|
||||
|
||||
interface Room {
|
||||
adults: number
|
||||
roomtypecode: string
|
||||
ratecode: string
|
||||
child: Child[]
|
||||
roomtypecode?: string
|
||||
ratecode?: string
|
||||
child?: Child[]
|
||||
}
|
||||
|
||||
export interface BookingData {
|
||||
hotel: string
|
||||
fromdate: string
|
||||
todate: string
|
||||
fromDate: string
|
||||
toDate: string
|
||||
room: Room[]
|
||||
}
|
||||
|
||||
export type RoomsData = {
|
||||
roomType: string
|
||||
memberPrice: string | undefined
|
||||
publicPrice: string | undefined
|
||||
price: string
|
||||
adults: number
|
||||
children: Child[]
|
||||
cancellationText: string | undefined
|
||||
children?: Child[]
|
||||
cancellationText: string
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ export interface Child {
|
||||
|
||||
interface Room {
|
||||
adults: number
|
||||
roomcode?: string
|
||||
roomtypecode?: string
|
||||
ratecode?: string
|
||||
child?: Child[]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user