fix/SW-902-update-response-city-availability (pull request #917)

Fix/SW-902 update response city availability

* fix(SW-902): update response for API changes

* fix(SW-902): add total row for pricePerStay

* fix(SW-902): fix optional requestedPrice

* fix(SW-902): fix bookingCode output

* feat(SW-903): fix sorting


Approved-by: Pontus Dreij
Approved-by: Niclas Edenvin
This commit is contained in:
Bianca Widstam
2024-11-18 15:08:12 +00:00
parent f6b14ced8f
commit 3c4907efce
27 changed files with 217 additions and 227 deletions

View File

@@ -71,20 +71,24 @@ export default async function SummaryPage({
price: availability.memberRate.localPrice.pricePerStay, price: availability.memberRate.localPrice.pricePerStay,
currency: availability.memberRate.localPrice.currency, currency: availability.memberRate.localPrice.currency,
}, },
euro: { euro: availability.memberRate.requestedPrice
price: availability.memberRate.requestedPrice.pricePerStay, ? {
currency: availability.memberRate.requestedPrice.currency, price: availability.memberRate.requestedPrice.pricePerStay,
}, currency: availability.memberRate.requestedPrice.currency,
}
: undefined,
} }
: { : {
local: { local: {
price: availability.publicRate.localPrice.pricePerStay, price: availability.publicRate.localPrice.pricePerStay,
currency: availability.publicRate.localPrice.currency, currency: availability.publicRate.localPrice.currency,
}, },
euro: { euro: availability.publicRate.requestedPrice
price: availability.publicRate.requestedPrice.pricePerStay, ? {
currency: availability.publicRate.requestedPrice.currency, price: availability.publicRate.requestedPrice.pricePerStay,
}, currency: availability.publicRate.requestedPrice.currency,
}
: undefined,
} }
return ( return (

View File

@@ -9,8 +9,6 @@ import type {
CategorizedFilters, CategorizedFilters,
Filter, Filter,
} from "@/types/components/hotelReservation/selectHotel/hotelFilters" } from "@/types/components/hotelReservation/selectHotel/hotelFilters"
import type { HotelPin } from "@/types/components/hotelReservation/selectHotel/map"
import { HotelListingEnum } from "@/types/enums/hotelListing"
const hotelSurroundingsFilterNames = [ const hotelSurroundingsFilterNames = [
"Hotel surroundings", "Hotel surroundings",
@@ -29,24 +27,8 @@ export async function fetchAvailableHotels(
if (!availableHotels) throw new Error() if (!availableHotels) throw new Error()
const language = getLang() const language = getLang()
const hotelMap = new Map<number, any>()
availableHotels.availability.forEach((hotel) => { const hotels = availableHotels.availability.map(async (hotel) => {
const existingHotel = hotelMap.get(hotel.hotelId)
if (existingHotel) {
if (hotel.ratePlanSet === HotelListingEnum.RatePlanSet.PUBLIC) {
existingHotel.bestPricePerNight.regularAmount =
hotel.bestPricePerNight?.regularAmount
} else if (hotel.ratePlanSet === HotelListingEnum.RatePlanSet.MEMBER) {
existingHotel.bestPricePerNight.memberAmount =
hotel.bestPricePerNight?.memberAmount
}
} else {
hotelMap.set(hotel.hotelId, { ...hotel })
}
})
const hotels = Array.from(hotelMap.values()).map(async (hotel) => {
const hotelData = await getHotelData({ const hotelData = await getHotelData({
hotelId: hotel.hotelId.toString(), hotelId: hotel.hotelId.toString(),
language, language,
@@ -56,7 +38,7 @@ export async function fetchAvailableHotels(
return { return {
hotelData: hotelData.data.attributes, hotelData: hotelData.data.attributes,
price: hotel.bestPricePerNight, price: hotel.productType,
} }
}) })

View File

@@ -80,7 +80,9 @@ export default function Summary({
) || { local: 0, euro: 0 } ) || { local: 0, euro: 0 }
const roomsPriceLocal = room.localPrice.price + additionalPackageCost.local const roomsPriceLocal = room.localPrice.price + additionalPackageCost.local
const roomsPriceEuro = room.euroPrice.price + additionalPackageCost.euro const roomsPriceEuro = room.euroPrice
? room.euroPrice.price + additionalPackageCost.euro
: undefined
useEffect(() => { useEffect(() => {
setChosenBed(bedType) setChosenBed(bedType)
@@ -92,10 +94,15 @@ export default function Summary({
price: roomsPriceLocal + parseInt(breakfast.localPrice.totalPrice), price: roomsPriceLocal + parseInt(breakfast.localPrice.totalPrice),
currency: room.localPrice.currency, currency: room.localPrice.currency,
}, },
euro: { euro:
price: roomsPriceEuro + parseInt(breakfast.requestedPrice.totalPrice), room.euroPrice && roomsPriceEuro
currency: room.euroPrice.currency, ? {
}, price:
roomsPriceEuro +
parseInt(breakfast.requestedPrice.totalPrice),
currency: room.euroPrice.currency,
}
: undefined,
}) })
} else { } else {
setTotalPrice({ setTotalPrice({
@@ -103,10 +110,13 @@ export default function Summary({
price: roomsPriceLocal, price: roomsPriceLocal,
currency: room.localPrice.currency, currency: room.localPrice.currency,
}, },
euro: { euro:
price: roomsPriceEuro, room.euroPrice && roomsPriceEuro
currency: room.euroPrice.currency, ? {
}, price: roomsPriceEuro,
currency: room.euroPrice.currency,
}
: undefined,
}) })
} }
}, [ }, [
@@ -114,7 +124,7 @@ export default function Summary({
breakfast, breakfast,
roomsPriceLocal, roomsPriceLocal,
room.localPrice.currency, room.localPrice.currency,
room.euroPrice.currency, room.euroPrice,
roomsPriceEuro, roomsPriceEuro,
setTotalPrice, setTotalPrice,
]) ])
@@ -269,16 +279,18 @@ export default function Summary({
} }
)} )}
</Body> </Body>
<Caption color="uiTextMediumContrast"> {totalPrice.euro && (
{intl.formatMessage({ id: "Approx." })}{" "} <Caption color="uiTextMediumContrast">
{intl.formatMessage( {intl.formatMessage({ id: "Approx." })}{" "}
{ id: "{amount} {currency}" }, {intl.formatMessage(
{ { id: "{amount} {currency}" },
amount: intl.formatNumber(totalPrice.euro.price), {
currency: totalPrice.euro.currency, amount: intl.formatNumber(totalPrice.euro.price),
} currency: totalPrice.euro.currency,
)} }
</Caption> )}
</Caption>
)}
</div> </div>
</div> </div>
<Divider className={styles.bottomDivider} color="primaryLightSubtle" /> <Divider className={styles.bottomDivider} color="primaryLightSubtle" />

View File

@@ -1,5 +1,6 @@
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import Divider from "@/components/TempDesignSystem/Divider"
import Body from "@/components/TempDesignSystem/Text/Body" import Body from "@/components/TempDesignSystem/Text/Body"
import Caption from "@/components/TempDesignSystem/Text/Caption" import Caption from "@/components/TempDesignSystem/Text/Caption"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
@@ -9,15 +10,14 @@ import styles from "../hotelPriceList.module.css"
import type { PriceCardProps } from "@/types/components/hotelReservation/selectHotel/priceCardProps" import type { PriceCardProps } from "@/types/components/hotelReservation/selectHotel/priceCardProps"
export default function HotelPriceCard({ export default function HotelPriceCard({
currency, productTypePrices,
memberAmount, isMemberPrice = false,
regularAmount,
}: PriceCardProps) { }: PriceCardProps) {
const intl = useIntl() const intl = useIntl()
return ( return (
<dl className={styles.priceCard}> <dl className={styles.priceCard}>
{memberAmount && ( {isMemberPrice && (
<div className={styles.priceRow}> <div className={styles.priceRow}>
<dt> <dt>
<Caption color="red"> <Caption color="red">
@@ -30,7 +30,7 @@ export default function HotelPriceCard({
<dt> <dt>
<Caption <Caption
type="bold" type="bold"
color={memberAmount ? "red" : "uiTextHighContrast"} color={isMemberPrice ? "red" : "uiTextHighContrast"}
> >
{intl.formatMessage({ id: "From" })} {intl.formatMessage({ id: "From" })}
</Caption> </Caption>
@@ -39,15 +39,15 @@ export default function HotelPriceCard({
<div className={styles.price}> <div className={styles.price}>
<Subtitle <Subtitle
type="two" type="two"
color={memberAmount ? "red" : "uiTextHighContrast"} color={isMemberPrice ? "red" : "uiTextHighContrast"}
> >
{memberAmount ? memberAmount : regularAmount} {productTypePrices.localPrice.pricePerNight}
</Subtitle> </Subtitle>
<Body <Body
color={memberAmount ? "red" : "uiTextHighContrast"} color={isMemberPrice ? "red" : "uiTextHighContrast"}
textTransform="bold" textTransform="bold"
> >
{currency} {productTypePrices.localPrice.currency}
<span className={styles.perNight}> <span className={styles.perNight}>
/{intl.formatMessage({ id: "night" })} /{intl.formatMessage({ id: "night" })}
</span> </span>
@@ -55,17 +55,40 @@ export default function HotelPriceCard({
</div> </div>
</dd> </dd>
</div> </div>
{/* TODO add correct local price when API change */} {productTypePrices?.requestedPrice && (
<div className={styles.priceRow}> <div className={styles.priceRow}>
<dt> <dt>
<Caption color={"disabled"}> <Caption color="uiTextMediumContrast">
{intl.formatMessage({ id: "Approx." })} {intl.formatMessage({ id: "Approx." })}
</Caption> </Caption>
</dt> </dt>
<dd> <dd>
<Caption color="disabled"> - EUR</Caption> <Caption color={"uiTextMediumContrast"}>
</dd> {productTypePrices.requestedPrice.pricePerNight}{" "}
</div> {productTypePrices.requestedPrice.currency}
</Caption>
</dd>
</div>
)}
{productTypePrices.localPrice.pricePerStay !==
productTypePrices.localPrice.pricePerNight && (
<>
<Divider color="subtle" className={styles.divider} />
<div className={styles.priceRow}>
<dt>
<Caption color="uiTextMediumContrast">
{intl.formatMessage({ id: "Total" })}
</Caption>
</dt>
<dd>
<Caption color={"uiTextMediumContrast"}>
{productTypePrices.localPrice.pricePerStay}{" "}
{productTypePrices.localPrice.currency}
</Caption>
</dd>
</div>
</>
)}
</dl> </dl>
) )
} }

View File

@@ -11,6 +11,17 @@
gap: var(--Spacing-x1); gap: var(--Spacing-x1);
} }
.prices {
display: flex;
flex-direction: column;
gap: var(--Spacing-x-one-and-half);
max-width: 260px;
}
.divider {
margin: var(--Spacing-x-half) 0;
}
.priceRow { .priceRow {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;

View File

@@ -1,6 +1,12 @@
import { useParams } from "next/dist/client/components/navigation"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { Lang } from "@/constants/languages"
import { selectRate } from "@/constants/routes/hotelReservation"
import { ErrorCircleIcon } from "@/components/Icons" import { ErrorCircleIcon } from "@/components/Icons"
import Button from "@/components/TempDesignSystem/Button"
import Link from "@/components/TempDesignSystem/Link"
import Body from "@/components/TempDesignSystem/Text/Body" import Body from "@/components/TempDesignSystem/Text/Body"
import HotelPriceCard from "./HotelPriceCard" import HotelPriceCard from "./HotelPriceCard"
@@ -9,34 +15,52 @@ import styles from "./hotelPriceList.module.css"
import { HotelPriceListProps } from "@/types/components/hotelReservation/selectHotel/hotePriceListProps" import { HotelPriceListProps } from "@/types/components/hotelReservation/selectHotel/hotePriceListProps"
export default function HotelPriceList({ price }: HotelPriceListProps) { export default function HotelPriceList({
price,
hotelId,
}: HotelPriceListProps) {
const intl = useIntl() const intl = useIntl()
const params = useParams()
const lang = params.lang as Lang
return ( return (
<> <div className={styles.prices}>
{price ? ( {price ? (
<> <>
<HotelPriceCard {price.public && <HotelPriceCard productTypePrices={price.public} />}
currency={price?.currency} {price.member && (
regularAmount={price?.regularAmount} <HotelPriceCard productTypePrices={price.member} isMemberPrice />
/> )}
<HotelPriceCard <Button
currency={price?.currency} asChild
memberAmount={price?.memberAmount} theme="base"
/> intent="primary"
size="small"
className={styles.button}
>
<Link
href={`${selectRate[lang]}?hotel=${hotelId}`}
color="none"
keepSearchParams
>
{intl.formatMessage({ id: "See rooms" })}
</Link>
</Button>
</> </>
) : ( ) : (
<div className={styles.priceCard}> <div className={styles.priceCard}>
<div className={styles.noRooms}> <div className={styles.noRooms}>
<ErrorCircleIcon color="red" /> <div>
<ErrorCircleIcon color="red" />
</div>
<Body> <Body>
{intl.formatMessage({ {intl.formatMessage({
id: "There are no rooms available that match your request", id: "There are no rooms available that match your request.",
})} })}
</Body> </Body>
</div> </div>
</div> </div>
)} )}
</> </div>
) )
} }

View File

@@ -70,13 +70,6 @@
gap: var(--Spacing-x-half); gap: var(--Spacing-x-half);
} }
.prices {
display: flex;
flex-direction: column;
gap: var(--Spacing-x-one-and-half);
width: 100%;
}
.detailsButton { .detailsButton {
border-bottom: none; border-bottom: none;
} }

View File

@@ -141,25 +141,7 @@ export default function HotelCard({
</div> </div>
)} )}
</section> </section>
<div className={styles.prices}> <HotelPriceList price={price} hotelId={hotel.hotelData.operaId} />
<HotelPriceList price={price} />
<Button
asChild
theme="base"
intent="primary"
size="small"
className={styles.button}
>
{/* TODO: use correct search params */}
<Link
href={`${selectRate[lang]}?hotel=${hotelData.operaId}`}
color="none"
keepSearchParams
>
{intl.formatMessage({ id: "See rooms" })}
</Link>
</Button>
</div>
</div> </div>
</article> </article>
) )

View File

@@ -8,9 +8,9 @@ export function getHotelPins(hotels: HotelData[]): HotelPin[] {
lng: hotel.hotelData.location.longitude, lng: hotel.hotelData.location.longitude,
}, },
name: hotel.hotelData.name, name: hotel.hotelData.name,
publicPrice: hotel.price?.regularAmount ?? null, publicPrice: hotel.price?.public?.localPrice.pricePerNight ?? null,
memberPrice: hotel.price?.memberAmount ?? null, memberPrice: hotel.price?.member?.localPrice.pricePerNight ?? null,
currency: hotel.price?.currency || null, currency: hotel.price?.public?.localPrice.currency || null,
images: [ images: [
hotel.hotelData.hotelContent.images, hotel.hotelData.hotelContent.images,
...(hotel.hotelData.gallery?.heroImages ?? []), ...(hotel.hotelData.gallery?.heroImages ?? []),

View File

@@ -12,6 +12,7 @@ import styles from "./hotelCardListing.module.css"
import { import {
type HotelCardListingProps, type HotelCardListingProps,
HotelCardListingTypeEnum, HotelCardListingTypeEnum,
type HotelData,
} from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps" } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
import { SortOrder } from "@/types/components/hotelReservation/selectHotel/hotelSorter" import { SortOrder } from "@/types/components/hotelReservation/selectHotel/hotelSorter"
@@ -43,10 +44,15 @@ export default function HotelCardListing({
(a.hotelData.ratings?.tripAdvisor.rating ?? 0) (a.hotelData.ratings?.tripAdvisor.rating ?? 0)
) )
case SortOrder.Price: case SortOrder.Price:
const getPricePerNight = (hotel: HotelData): number => {
return (
hotel.price?.member?.localPrice?.pricePerNight ??
hotel.price?.public?.localPrice?.pricePerNight ??
0
)
}
return [...hotelData].sort( return [...hotelData].sort(
(a, b) => (a, b) => getPricePerNight(a) - getPricePerNight(b)
parseInt(a.price?.memberAmount ?? "0", 10) -
parseInt(b.price?.memberAmount ?? "0", 10)
) )
case SortOrder.Distance: case SortOrder.Distance:
default: default:

View File

@@ -49,7 +49,7 @@ export function filterDuplicateRoomTypesByLowestPrice(
const previousLowest = roomMap.get(roomType) const previousLowest = roomMap.get(roomType)
const currentRequestedPrice = Math.min( const currentRequestedPrice = Math.min(
Number(publicRequestedPrice.pricePerNight) ?? Infinity, Number(publicRequestedPrice?.pricePerNight) ?? Infinity,
Number(memberRequestedPrice?.pricePerNight) ?? Infinity Number(memberRequestedPrice?.pricePerNight) ?? Infinity
) )
const currentLocalPrice = Math.min( const currentLocalPrice = Math.min(

View File

@@ -348,7 +348,6 @@
"Terms and conditions": "Geschäftsbedingungen", "Terms and conditions": "Geschäftsbedingungen",
"Thank you": "Danke", "Thank you": "Danke",
"Theatre": "Theater", "Theatre": "Theater",
"There are no rooms available that match your request": "Es sind keine Zimmer verfügbar, die Ihrer Anfrage entsprechen",
"There are no rooms available that match your request.": "Es sind keine Zimmer verfügbar, die Ihrer Anfrage entsprechen.", "There are no rooms available that match your request.": "Es sind keine Zimmer verfügbar, die Ihrer Anfrage entsprechen.",
"There are no transactions to display": "Es sind keine Transaktionen zum Anzeigen vorhanden", "There are no transactions to display": "Es sind keine Transaktionen zum Anzeigen vorhanden",
"Things nearby HOTEL_NAME": "Dinge in der Nähe von {hotelName}", "Things nearby HOTEL_NAME": "Dinge in der Nähe von {hotelName}",

View File

@@ -378,7 +378,6 @@
"Terms and conditions": "Terms and conditions", "Terms and conditions": "Terms and conditions",
"Thank you": "Thank you", "Thank you": "Thank you",
"Theatre": "Theatre", "Theatre": "Theatre",
"There are no rooms available that match your request": "There are no rooms available that match your request",
"There are no rooms available that match your request.": "There are no rooms available that match your request.", "There are no rooms available that match your request.": "There are no rooms available that match your request.",
"There are no transactions to display": "There are no transactions to display", "There are no transactions to display": "There are no transactions to display",
"Things nearby HOTEL_NAME": "Things nearby {hotelName}", "Things nearby HOTEL_NAME": "Things nearby {hotelName}",

View File

@@ -350,7 +350,6 @@
"Terms and conditions": "Käyttöehdot", "Terms and conditions": "Käyttöehdot",
"Thank you": "Kiitos", "Thank you": "Kiitos",
"Theatre": "Teatteri", "Theatre": "Teatteri",
"There are no rooms available that match your request": "Pyyntöäsi vastaavia huoneita ei ole saatavilla",
"There are no rooms available that match your request.": "Ei huoneita saatavilla, jotka vastaavat pyyntöäsi.", "There are no rooms available that match your request.": "Ei huoneita saatavilla, jotka vastaavat pyyntöäsi.",
"There are no transactions to display": "Näytettäviä tapahtumia ei ole", "There are no transactions to display": "Näytettäviä tapahtumia ei ole",
"Things nearby HOTEL_NAME": "Lähellä olevia asioita {hotelName}", "Things nearby HOTEL_NAME": "Lähellä olevia asioita {hotelName}",

View File

@@ -347,7 +347,6 @@
"Terms and conditions": "Vilkår og betingelser", "Terms and conditions": "Vilkår og betingelser",
"Thank you": "Takk", "Thank you": "Takk",
"Theatre": "Teater", "Theatre": "Teater",
"There are no rooms available that match your request": "Det er ingen tilgjengelige rom som samsvarer med forespørselen din",
"There are no rooms available that match your request.": "Det er ingen rom tilgjengelige som matcher din forespørsel.", "There are no rooms available that match your request.": "Det er ingen rom tilgjengelige som matcher din forespørsel.",
"There are no transactions to display": "Det er ingen transaksjoner å vise", "There are no transactions to display": "Det er ingen transaksjoner å vise",
"Things nearby HOTEL_NAME": "Ting i nærheten av {hotelName}", "Things nearby HOTEL_NAME": "Ting i nærheten av {hotelName}",

View File

@@ -347,7 +347,6 @@
"Terms and conditions": "Allmänna villkor", "Terms and conditions": "Allmänna villkor",
"Thank you": "Tack", "Thank you": "Tack",
"Theatre": "Teater", "Theatre": "Teater",
"There are no rooms available that match your request": "Det finns inga tillgängliga rum som matchar din förfrågan",
"There are no rooms available that match your request.": "Det finns inga rum tillgängliga som matchar din begäran.", "There are no rooms available that match your request.": "Det finns inga rum tillgängliga som matchar din begäran.",
"There are no transactions to display": "Det finns inga transaktioner att visa", "There are no transactions to display": "Det finns inga transaktioner att visa",
"Things nearby HOTEL_NAME": "Saker i närheten av {hotelName}", "Things nearby HOTEL_NAME": "Saker i närheten av {hotelName}",

View File

@@ -82,7 +82,7 @@ export const getRoomAvailability = cache(
roomStayStartDate, roomStayStartDate,
roomStayEndDate, roomStayEndDate,
children, children,
promotionCode, bookingCode,
rateCode, rateCode,
}: GetRoomsAvailabilityInput) { }: GetRoomsAvailabilityInput) {
return serverClient().hotel.availability.rooms({ return serverClient().hotel.availability.rooms({
@@ -91,7 +91,7 @@ export const getRoomAvailability = cache(
roomStayStartDate, roomStayStartDate,
roomStayEndDate, roomStayEndDate,
children, children,
promotionCode, bookingCode,
rateCode, rateCode,
}) })
} }

View File

@@ -8,9 +8,7 @@ export const getHotelsAvailabilityInputSchema = z.object({
roomStayEndDate: z.string(), roomStayEndDate: z.string(),
adults: z.number(), adults: z.number(),
children: z.string().optional(), children: z.string().optional(),
promotionCode: z.string().optional().default(""), bookingCode: z.string().optional().default(""),
reservationProfileType: z.string().optional().default(""),
attachedProfileId: z.string().optional().default(""),
}) })
export const getRoomsAvailabilityInputSchema = z.object({ export const getRoomsAvailabilityInputSchema = z.object({
@@ -19,9 +17,7 @@ export const getRoomsAvailabilityInputSchema = z.object({
roomStayEndDate: z.string(), roomStayEndDate: z.string(),
adults: z.number(), adults: z.number(),
children: z.string().optional(), children: z.string().optional(),
promotionCode: z.string().optional(), bookingCode: z.string().optional(),
reservationProfileType: z.string().optional().default(""),
attachedProfileId: z.string().optional().default(""),
rateCode: z.string().optional(), rateCode: z.string().optional(),
}) })
@@ -31,9 +27,7 @@ export const getSelectedRoomAvailabilityInputSchema = z.object({
roomStayEndDate: z.string(), roomStayEndDate: z.string(),
adults: z.number(), adults: z.number(),
children: z.string().optional(), children: z.string().optional(),
promotionCode: z.string().optional(), bookingCode: z.string().optional(),
reservationProfileType: z.string().optional().default(""),
attachedProfileId: z.string().optional().default(""),
rateCode: z.string(), rateCode: z.string(),
roomTypeCode: z.string(), roomTypeCode: z.string(),
packageCodes: z.array(z.nativeEnum(RoomPackageCodeEnum)).optional(), packageCodes: z.array(z.nativeEnum(RoomPackageCodeEnum)).optional(),

View File

@@ -491,22 +491,6 @@ const occupancySchema = z.object({
children: z.array(childrenSchema), children: z.array(childrenSchema),
}) })
const bestPricePerStaySchema = z.object({
currency: z.string(),
// TODO: remove optional when API is ready
regularAmount: z.string().optional(),
// TODO: remove optional when API is ready
memberAmount: z.string().optional(),
})
const bestPricePerNightSchema = z.object({
currency: z.string(),
// TODO: remove optional when API is ready
regularAmount: z.string().optional(),
// TODO: remove optional when API is ready
memberAmount: z.string().optional(),
})
const linksSchema = z.object({ const linksSchema = z.object({
links: z.array( links: z.array(
z.object({ z.object({
@@ -516,30 +500,6 @@ const linksSchema = z.object({
), ),
}) })
const hotelsAvailabilitySchema = z.object({
data: z.array(
z.object({
attributes: z.object({
checkInDate: z.string(),
checkOutDate: z.string(),
occupancy: occupancySchema.optional(),
status: z.string(),
hotelId: z.number(),
ratePlanSet: z.string().optional(),
bestPricePerStay: bestPricePerStaySchema.optional(),
bestPricePerNight: bestPricePerNightSchema.optional(),
}),
relationships: linksSchema.optional(),
type: z.string().optional(),
})
),
})
export const getHotelsAvailabilitySchema = hotelsAvailabilitySchema
export type HotelsAvailability = z.infer<typeof hotelsAvailabilitySchema>
export type HotelsAvailabilityPrices =
HotelsAvailability["data"][number]["attributes"]["bestPricePerNight"]
export const priceSchema = z.object({ export const priceSchema = z.object({
pricePerNight: z.coerce.number(), pricePerNight: z.coerce.number(),
pricePerStay: z.coerce.number(), pricePerStay: z.coerce.number(),
@@ -550,7 +510,7 @@ export const productTypePriceSchema = z.object({
rateCode: z.string(), rateCode: z.string(),
rateType: z.string().optional(), rateType: z.string().optional(),
localPrice: priceSchema, localPrice: priceSchema,
requestedPrice: priceSchema, requestedPrice: priceSchema.optional(),
}) })
const productSchema = z.object({ const productSchema = z.object({
@@ -560,6 +520,34 @@ const productSchema = z.object({
}), }),
}) })
const hotelsAvailabilitySchema = z.object({
data: z.array(
z.object({
attributes: z.object({
checkInDate: z.string(),
checkOutDate: z.string(),
occupancy: occupancySchema,
status: z.string(),
hotelId: z.number(),
productType: z
.object({
public: productTypePriceSchema.optional(),
member: productTypePriceSchema.optional(),
})
.optional(),
}),
relationships: linksSchema.optional(),
type: z.string().optional(),
})
),
})
export const getHotelsAvailabilitySchema = hotelsAvailabilitySchema
export type HotelsAvailability = z.infer<typeof hotelsAvailabilitySchema>
export type ProductType =
HotelsAvailability["data"][number]["attributes"]["productType"]
export type ProductTypePrices = z.infer<typeof productTypePriceSchema>
const roomConfigurationSchema = z.object({ const roomConfigurationSchema = z.object({
status: z.string(), status: z.string(),
roomTypeCode: z.string(), roomTypeCode: z.string(),

View File

@@ -369,9 +369,7 @@ export const hotelQueryRouter = router({
roomStayEndDate, roomStayEndDate,
adults, adults,
children, children,
promotionCode, bookingCode,
reservationProfileType,
attachedProfileId,
} = input } = input
const params: Record<string, string | number> = { const params: Record<string, string | number> = {
@@ -379,9 +377,7 @@ export const hotelQueryRouter = router({
roomStayEndDate, roomStayEndDate,
adults, adults,
...(children && { children }), ...(children && { children }),
promotionCode, bookingCode,
reservationProfileType,
attachedProfileId,
language: apiLang, language: apiLang,
} }
hotelsAvailabilityCounter.add(1, { hotelsAvailabilityCounter.add(1, {
@@ -390,8 +386,7 @@ export const hotelQueryRouter = router({
roomStayEndDate, roomStayEndDate,
adults, adults,
children, children,
promotionCode, bookingCode,
reservationProfileType,
}) })
console.info( console.info(
"api.hotels.hotelsAvailability start", "api.hotels.hotelsAvailability start",
@@ -414,8 +409,7 @@ export const hotelQueryRouter = router({
roomStayEndDate, roomStayEndDate,
adults, adults,
children, children,
promotionCode, bookingCode,
reservationProfileType,
error_type: "http_error", error_type: "http_error",
error: JSON.stringify({ error: JSON.stringify({
status: apiResponse.status, status: apiResponse.status,
@@ -446,8 +440,7 @@ export const hotelQueryRouter = router({
roomStayEndDate, roomStayEndDate,
adults, adults,
children, children,
promotionCode, bookingCode,
reservationProfileType,
error_type: "validation_error", error_type: "validation_error",
error: JSON.stringify(validateAvailabilityData.error), error: JSON.stringify(validateAvailabilityData.error),
}) })
@@ -466,8 +459,7 @@ export const hotelQueryRouter = router({
roomStayEndDate, roomStayEndDate,
adults, adults,
children, children,
promotionCode, bookingCode,
reservationProfileType,
}) })
console.info( console.info(
"api.hotels.hotelsAvailability success", "api.hotels.hotelsAvailability success",
@@ -493,9 +485,7 @@ export const hotelQueryRouter = router({
roomStayEndDate, roomStayEndDate,
adults, adults,
children, children,
promotionCode, bookingCode,
reservationProfileType,
attachedProfileId,
rateCode, rateCode,
} = input } = input
@@ -504,9 +494,7 @@ export const hotelQueryRouter = router({
roomStayEndDate, roomStayEndDate,
adults, adults,
...(children && { children }), ...(children && { children }),
promotionCode, bookingCode,
reservationProfileType,
attachedProfileId,
} }
roomsAvailabilityCounter.add(1, { roomsAvailabilityCounter.add(1, {
@@ -515,8 +503,7 @@ export const hotelQueryRouter = router({
roomStayEndDate, roomStayEndDate,
adults, adults,
children, children,
promotionCode, bookingCode,
reservationProfileType,
}) })
console.info( console.info(
"api.hotels.roomsAvailability start", "api.hotels.roomsAvailability start",
@@ -540,8 +527,7 @@ export const hotelQueryRouter = router({
roomStayEndDate, roomStayEndDate,
adults, adults,
children, children,
promotionCode, bookingCode,
reservationProfileType,
error_type: "http_error", error_type: "http_error",
error: JSON.stringify({ error: JSON.stringify({
status: apiResponse.status, status: apiResponse.status,
@@ -572,8 +558,7 @@ export const hotelQueryRouter = router({
roomStayEndDate, roomStayEndDate,
adults, adults,
children, children,
promotionCode, bookingCode,
reservationProfileType,
error_type: "validation_error", error_type: "validation_error",
error: JSON.stringify(validateAvailabilityData.error), error: JSON.stringify(validateAvailabilityData.error),
}) })
@@ -592,8 +577,7 @@ export const hotelQueryRouter = router({
roomStayEndDate, roomStayEndDate,
adults, adults,
children, children,
promotionCode, bookingCode,
reservationProfileType,
}) })
console.info( console.info(
"api.hotels.roomsAvailability success", "api.hotels.roomsAvailability success",
@@ -620,9 +604,7 @@ export const hotelQueryRouter = router({
roomStayEndDate, roomStayEndDate,
adults, adults,
children, children,
promotionCode, bookingCode,
reservationProfileType,
attachedProfileId,
rateCode, rateCode,
roomTypeCode, roomTypeCode,
packageCodes, packageCodes,
@@ -633,9 +615,7 @@ export const hotelQueryRouter = router({
roomStayEndDate, roomStayEndDate,
adults, adults,
...(children && { children }), ...(children && { children }),
promotionCode, bookingCode,
reservationProfileType,
attachedProfileId,
language: toApiLang(ctx.lang), language: toApiLang(ctx.lang),
} }
@@ -645,8 +625,7 @@ export const hotelQueryRouter = router({
roomStayEndDate, roomStayEndDate,
adults, adults,
children, children,
promotionCode, bookingCode,
reservationProfileType,
}) })
console.info( console.info(
"api.hotels.selectedRoomAvailability start", "api.hotels.selectedRoomAvailability start",
@@ -670,8 +649,7 @@ export const hotelQueryRouter = router({
roomStayEndDate, roomStayEndDate,
adults, adults,
children, children,
promotionCode, bookingCode,
reservationProfileType,
error_type: "http_error", error_type: "http_error",
error: JSON.stringify({ error: JSON.stringify({
status: apiResponseAvailability.status, status: apiResponseAvailability.status,
@@ -702,8 +680,7 @@ export const hotelQueryRouter = router({
roomStayEndDate, roomStayEndDate,
adults, adults,
children, children,
promotionCode, bookingCode,
reservationProfileType,
error_type: "validation_error", error_type: "validation_error",
error: JSON.stringify(validateAvailabilityData.error), error: JSON.stringify(validateAvailabilityData.error),
}) })
@@ -797,8 +774,7 @@ export const hotelQueryRouter = router({
roomStayEndDate, roomStayEndDate,
adults, adults,
children, children,
promotionCode, bookingCode,
reservationProfileType,
}) })
console.info( console.info(
"api.hotels.selectedRoomAvailability success", "api.hotels.selectedRoomAvailability success",

View File

@@ -25,7 +25,7 @@ const SESSION_STORAGE_KEY = "enterDetails"
type TotalPrice = { type TotalPrice = {
local: { price: number; currency: string } local: { price: number; currency: string }
euro: { price: number; currency: string } euro?: { price: number; currency: string }
} }
export interface EnterDetailsState { export interface EnterDetailsState {

View File

@@ -25,7 +25,7 @@ type Price = {
export type RoomsData = { export type RoomsData = {
roomType: string roomType: string
localPrice: Price localPrice: Price
euroPrice: Price euroPrice: Price | undefined
adults: number adults: number
children?: Child[] children?: Child[]
cancellationText: string cancellationText: string

View File

@@ -4,7 +4,5 @@ export type AvailabilityInput = {
roomStayEndDate: string roomStayEndDate: string
adults: number adults: number
children?: string children?: string
promotionCode?: string bookingCode?: string
reservationProfileType?: string
attachedProfileId?: string
} }

View File

@@ -1,5 +1,6 @@
import type { HotelsAvailabilityPrices } from "@/server/routers/hotels/output" import type { ProductType } from "@/server/routers/hotels/output"
export type HotelPriceListProps = { export type HotelPriceListProps = {
price: HotelsAvailabilityPrices price: ProductType
hotelId: string
} }

View File

@@ -1,4 +1,4 @@
import { HotelsAvailabilityPrices } from "@/server/routers/hotels/output" import { ProductType } from "@/server/routers/hotels/output"
import { Hotel } from "@/types/hotel" import { Hotel } from "@/types/hotel"
@@ -16,5 +16,5 @@ export type HotelCardListingProps = {
export type HotelData = { export type HotelData = {
hotelData: Hotel hotelData: Hotel
price: HotelsAvailabilityPrices price: ProductType
} }

View File

@@ -29,8 +29,8 @@ type ImageMetaData = z.infer<typeof imageMetaDataSchema>
export type HotelPin = { export type HotelPin = {
name: string name: string
coordinates: Coordinates coordinates: Coordinates
publicPrice: string | null publicPrice: number | null
memberPrice: string | null memberPrice: number | null
currency: string | null currency: string | null
images: { images: {
imageSizes: ImageSizes imageSizes: ImageSizes

View File

@@ -1,5 +1,6 @@
import { ProductTypePrices } from "@/server/routers/hotels/output"
export type PriceCardProps = { export type PriceCardProps = {
currency: string productTypePrices: ProductTypePrices
memberAmount?: string | undefined isMemberPrice?: boolean
regularAmount?: string | undefined
} }