feat: SW-1583 City search Map view redemption
This commit is contained in:
@@ -37,6 +37,7 @@ export default function BookingCode() {
|
||||
setValue,
|
||||
formState: { errors },
|
||||
getValues,
|
||||
trigger,
|
||||
} = useFormContext<BookingWidgetSchema>()
|
||||
|
||||
const bookingCode: BookingCodeSchema = getValues("bookingCode")
|
||||
@@ -52,6 +53,12 @@ export default function BookingCode() {
|
||||
|
||||
function updateBookingCodeFormValue(value: string) {
|
||||
setValue("bookingCode.value", value, { shouldValidate: true })
|
||||
if (getValues("redemption")) {
|
||||
setValue("redemption", false)
|
||||
setTimeout(function () {
|
||||
trigger("bookingCode.value")
|
||||
}, 5000)
|
||||
}
|
||||
}
|
||||
|
||||
const closeIfOutside = useCallback(
|
||||
|
||||
@@ -9,18 +9,21 @@ import type { PointsCardProps } from "@/types/components/hotelReservation/select
|
||||
|
||||
export default function HotelPointsCard({
|
||||
productTypePoints,
|
||||
redemptionPrice,
|
||||
}: PointsCardProps) {
|
||||
const intl = useIntl()
|
||||
const pointsPerStay =
|
||||
productTypePoints?.localPrice.pricePerStay ?? redemptionPrice
|
||||
|
||||
return (
|
||||
<div className={styles.poinstRow}>
|
||||
<Subtitle type="two" color="uiTextHighContrast">
|
||||
{productTypePoints.localPrice.pointsPerStay}
|
||||
{pointsPerStay}
|
||||
</Subtitle>
|
||||
<Caption color="uiTextHighContrast">
|
||||
{intl.formatMessage({ id: "Points" })}
|
||||
</Caption>
|
||||
{productTypePoints.localPrice.pricePerStay ? (
|
||||
{productTypePoints?.localPrice.pricePerStay ? (
|
||||
<>
|
||||
+
|
||||
<Subtitle type="two" color="uiTextHighContrast">
|
||||
|
||||
@@ -11,6 +11,7 @@ import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import { isValidClientSession } from "@/utils/clientSession"
|
||||
|
||||
import HotelPointsCard from "../../HotelCard/HotelPointsCard"
|
||||
import NoPriceAvailableCard from "../../HotelCard/NoPriceAvailableCard"
|
||||
import HotelCardDialogImage from "../HotelCardDialogImage"
|
||||
|
||||
@@ -44,6 +45,7 @@ export default function ListingHotelCardDialog({
|
||||
images,
|
||||
ratings,
|
||||
operaId,
|
||||
redemptionPrice,
|
||||
} = data
|
||||
|
||||
const firstImage = images[0]?.imageSizes?.small
|
||||
@@ -82,14 +84,20 @@ export default function ListingHotelCardDialog({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{publicPrice || memberPrice ? (
|
||||
{publicPrice || memberPrice || redemptionPrice ? (
|
||||
<div className={styles.bottomContainer}>
|
||||
<div className={styles.pricesContainer}>
|
||||
<Caption color="uiTextHighContrast">
|
||||
{intl.formatMessage({ id: "Per night from" })}
|
||||
</Caption>
|
||||
{redemptionPrice ? (
|
||||
<Caption color="uiTextHighContrast">
|
||||
{intl.formatMessage({ id: "Available rates" })}
|
||||
</Caption>
|
||||
) : (
|
||||
<Caption color="uiTextHighContrast">
|
||||
{intl.formatMessage({ id: "Per night from" })}
|
||||
</Caption>
|
||||
)}
|
||||
<div className={styles.listingPrices}>
|
||||
{publicPrice && !isUserLoggedIn && (
|
||||
{publicPrice && !isUserLoggedIn && memberPrice && (
|
||||
<>
|
||||
<Subtitle type="two">
|
||||
{publicPrice} {currency}
|
||||
@@ -108,6 +116,9 @@ export default function ListingHotelCardDialog({
|
||||
)}
|
||||
</Subtitle>
|
||||
)}
|
||||
{redemptionPrice && (
|
||||
<HotelPointsCard redemptionPrice={redemptionPrice} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<Button asChild theme="base" size="small" className={styles.button}>
|
||||
|
||||
@@ -13,6 +13,7 @@ import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import { isValidClientSession } from "@/utils/clientSession"
|
||||
|
||||
import HotelPointsCard from "../../HotelCard/HotelPointsCard"
|
||||
import NoPriceAvailableCard from "../../HotelCard/NoPriceAvailableCard"
|
||||
import HotelCardDialogImage from "../HotelCardDialogImage"
|
||||
|
||||
@@ -41,6 +42,7 @@ export default function StandaloneHotelCardDialog({
|
||||
name,
|
||||
publicPrice,
|
||||
memberPrice,
|
||||
redemptionPrice,
|
||||
currency,
|
||||
amenities,
|
||||
images,
|
||||
@@ -85,12 +87,18 @@ export default function StandaloneHotelCardDialog({
|
||||
})}
|
||||
</div>
|
||||
<div className={styles.pricesContainer}>
|
||||
{publicPrice || memberPrice ? (
|
||||
{publicPrice || memberPrice || redemptionPrice ? (
|
||||
<>
|
||||
<div className={styles.priceCard}>
|
||||
<Caption type="bold">
|
||||
{intl.formatMessage({ id: "From" })}
|
||||
</Caption>
|
||||
{redemptionPrice ? (
|
||||
<Caption>
|
||||
{intl.formatMessage({ id: "Available rates" })}
|
||||
</Caption>
|
||||
) : (
|
||||
<Caption type="bold">
|
||||
{intl.formatMessage({ id: "From" })}
|
||||
</Caption>
|
||||
)}
|
||||
{publicPrice && !isUserLoggedIn && (
|
||||
<Subtitle type="two">
|
||||
{intl.formatMessage(
|
||||
@@ -123,6 +131,9 @@ export default function StandaloneHotelCardDialog({
|
||||
</Body>
|
||||
</Subtitle>
|
||||
)}
|
||||
{redemptionPrice && (
|
||||
<HotelPointsCard redemptionPrice={redemptionPrice} />
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
asChild
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import { useCallback, useEffect, useRef } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
import { useMediaQuery } from "usehooks-ts"
|
||||
|
||||
import { useHotelsMapStore } from "@/stores/hotels-map"
|
||||
@@ -17,7 +18,12 @@ import type { HotelCardDialogListingProps } from "@/types/components/hotelReserv
|
||||
export default function HotelCardDialogListing({
|
||||
hotels,
|
||||
}: HotelCardDialogListingProps) {
|
||||
const hotelsPinData = hotels ? getHotelPins(hotels) : []
|
||||
const intl = useIntl()
|
||||
const isRedemption = hotels?.find((hotel) => hotel.price?.redemption)
|
||||
const currencyValue = isRedemption
|
||||
? intl.formatMessage({ id: "Points" })
|
||||
: undefined
|
||||
const hotelsPinData = hotels ? getHotelPins(hotels, currencyValue) : []
|
||||
const activeCardRef = useRef<HTMLDivElement | null>(null)
|
||||
const observerRef = useRef<IntersectionObserver | null>(null)
|
||||
const dialogRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import type { HotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
||||
import type { HotelPin } from "@/types/components/hotelReservation/selectHotel/map"
|
||||
|
||||
export function getHotelPins(hotels: HotelData[]): HotelPin[] {
|
||||
export function getHotelPins(
|
||||
hotels: HotelData[],
|
||||
currencyValue?: string
|
||||
): HotelPin[] {
|
||||
if (hotels.length === 0) return []
|
||||
|
||||
return hotels
|
||||
@@ -14,11 +17,14 @@ export function getHotelPins(hotels: HotelData[]): HotelPin[] {
|
||||
name: hotel.hotelData.name,
|
||||
publicPrice: hotel.price?.public?.localPrice.pricePerNight ?? null,
|
||||
memberPrice: hotel.price?.member?.localPrice.pricePerNight ?? null,
|
||||
redemptionPrice:
|
||||
hotel.price?.redemption?.localPrice.pointsPerNight ?? null,
|
||||
rateType:
|
||||
hotel.price?.public?.rateType ?? hotel.price?.member?.rateType ?? null,
|
||||
currency:
|
||||
hotel.price?.public?.localPrice.currency ||
|
||||
hotel.price?.member?.localPrice.currency ||
|
||||
currencyValue ||
|
||||
"N/A",
|
||||
images: [
|
||||
hotel.hotelData.hotelContent.images,
|
||||
|
||||
@@ -13,6 +13,7 @@ export function getSortedHotels({
|
||||
const getPricePerNight = (hotel: HotelData): number =>
|
||||
hotel.price?.member?.localPrice?.pricePerNight ??
|
||||
hotel.price?.public?.localPrice?.pricePerNight ??
|
||||
hotel.price?.redemption?.localPrice?.pointsPerNight ??
|
||||
Infinity
|
||||
const availableHotels = hotels.filter((hotel) => !!hotel?.price)
|
||||
const unAvailableHotels = hotels.filter((hotel) => !hotel?.price)
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from "@/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/utils"
|
||||
import { getHotelSearchDetails } from "@/app/[lang]/(live)/(public)/hotelreservation/(standard)/utils"
|
||||
import TrackingSDK from "@/components/TrackingSDK"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
import { safeTry } from "@/utils/safeTry"
|
||||
|
||||
@@ -33,6 +34,7 @@ export async function SelectHotelMapContainer({
|
||||
isAlternativeHotels,
|
||||
}: SelectHotelMapContainerProps) {
|
||||
const lang = getLang()
|
||||
const intl = await getIntl()
|
||||
const googleMapId = env.GOOGLE_DYNAMIC_MAP_ID
|
||||
const googleMapsApiKey = env.GOOGLE_STATIC_MAP_KEY
|
||||
const getHotelSearchDetailsPromise = safeTry(
|
||||
@@ -58,6 +60,7 @@ export async function SelectHotelMapContainer({
|
||||
childrenInRoomString,
|
||||
hotel: isAlternativeFor,
|
||||
bookingCode,
|
||||
redemption,
|
||||
} = searchDetails
|
||||
|
||||
if (!city) return notFound()
|
||||
@@ -70,6 +73,7 @@ export async function SelectHotelMapContainer({
|
||||
adults: adultsInRoom[0],
|
||||
children: childrenInRoomString,
|
||||
bookingCode,
|
||||
redemption,
|
||||
})
|
||||
)
|
||||
: bookingCode
|
||||
@@ -90,6 +94,7 @@ export async function SelectHotelMapContainer({
|
||||
roomStayEndDate: selectHotelParams.toDate,
|
||||
adults: adultsInRoom[0],
|
||||
children: childrenInRoomString,
|
||||
redemption,
|
||||
})
|
||||
)
|
||||
|
||||
@@ -97,7 +102,10 @@ export async function SelectHotelMapContainer({
|
||||
|
||||
const validHotels = (hotels?.filter(Boolean) as HotelData[]) || []
|
||||
|
||||
const hotelPins = getHotelPins(validHotels)
|
||||
const currencyValue = redemption
|
||||
? intl.formatMessage({ id: "Points" })
|
||||
: undefined
|
||||
const hotelPins = getHotelPins(validHotels, currencyValue)
|
||||
const filterList = getFiltersFromHotels(validHotels)
|
||||
const cityCoordinates = await getCityCoordinates({
|
||||
city: city.name,
|
||||
|
||||
@@ -58,7 +58,8 @@ function HotelListingMapContent({ hotelPins }: HotelListingMapContentProps) {
|
||||
{hotelPins.map((pin) => {
|
||||
const isActiveOrHovered =
|
||||
activeHotelPin === pin.name || hoveredHotelPin === pin.name
|
||||
const hotelPrice = pin.memberPrice ?? pin.publicPrice
|
||||
const hotelPrice =
|
||||
pin.memberPrice ?? pin.publicPrice ?? pin.redemptionPrice
|
||||
return (
|
||||
<AdvancedMarker
|
||||
key={pin.name}
|
||||
|
||||
@@ -32,6 +32,7 @@ export type HotelPin = {
|
||||
coordinates: Coordinates
|
||||
publicPrice: number | null
|
||||
memberPrice: number | null
|
||||
redemptionPrice: number | null
|
||||
rateType: string | null
|
||||
currency: string
|
||||
images: {
|
||||
|
||||
@@ -9,5 +9,6 @@ export type PriceCardProps = {
|
||||
}
|
||||
|
||||
export type PointsCardProps = {
|
||||
productTypePoints: ProductTypePoints
|
||||
productTypePoints?: ProductTypePoints
|
||||
redemptionPrice?: number
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user