Merged in feat/SW-965-select-rate-modify-room (pull request #1326)
Feat/SW-965 select rate modify room * feat(SW-965): added new state for modify room and smaller fixes * feat(SW-965): update state handling of modifyRateIndex * fix: adjust scroll animation to handle modifyRateIndex * fix: room state logic and removed unused css class Approved-by: Pontus Dreij Approved-by: Arvid Norlin
This commit is contained in:
@@ -184,6 +184,8 @@ export default async function DetailsPage({
|
|||||||
roomType={room.roomType}
|
roomType={room.roomType}
|
||||||
roomTypeCode={booking.rooms[idx].roomTypeCode}
|
roomTypeCode={booking.rooms[idx].roomTypeCode}
|
||||||
rateDescription={room.cancellationText}
|
rateDescription={room.cancellationText}
|
||||||
|
roomIndex={idx}
|
||||||
|
searchParamsStr={selectRoomParams.toString()}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{room.bedTypes ? (
|
{room.bedTypes ? (
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
|
import { useRouter } from "next/navigation"
|
||||||
|
import { useTransition } from "react"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { selectRate } from "@/constants/routes/hotelReservation"
|
import { selectRate } from "@/constants/routes/hotelReservation"
|
||||||
|
import { useRateSelectionStore } from "@/stores/select-rate/rate-selection"
|
||||||
|
|
||||||
import { CheckIcon, EditIcon } from "@/components/Icons"
|
import { CheckIcon, EditIcon } from "@/components/Icons"
|
||||||
import Link from "@/components/TempDesignSystem/Link"
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
||||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||||
import useLang from "@/hooks/useLang"
|
import useLang from "@/hooks/useLang"
|
||||||
@@ -21,9 +24,23 @@ export default function SelectedRoom({
|
|||||||
roomType,
|
roomType,
|
||||||
roomTypeCode,
|
roomTypeCode,
|
||||||
rateDescription,
|
rateDescription,
|
||||||
|
roomIndex,
|
||||||
|
searchParamsStr,
|
||||||
}: SelectedRoomProps) {
|
}: SelectedRoomProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const lang = useLang()
|
const lang = useLang()
|
||||||
|
const router = useRouter()
|
||||||
|
const [isPending, startTransition] = useTransition()
|
||||||
|
const { modifyRate } = useRateSelectionStore()
|
||||||
|
|
||||||
|
function changeRoom() {
|
||||||
|
modifyRate(roomIndex)
|
||||||
|
startTransition(() => {
|
||||||
|
const newSearchParams = new URLSearchParams(searchParamsStr)
|
||||||
|
newSearchParams.set("modifyRateIndex", roomIndex.toString())
|
||||||
|
router.push(`${selectRate(lang)}?${newSearchParams.toString()}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
@@ -59,17 +76,16 @@ export default function SelectedRoom({
|
|||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
</Subtitle>
|
</Subtitle>
|
||||||
<Link
|
<Button
|
||||||
className={styles.button}
|
|
||||||
color="burgundy"
|
|
||||||
href={selectRate(lang)}
|
|
||||||
keepSearchParams
|
|
||||||
size="small"
|
|
||||||
variant="icon"
|
variant="icon"
|
||||||
|
size="small"
|
||||||
|
color="burgundy"
|
||||||
|
onClick={changeRoom}
|
||||||
|
disabled={isPending}
|
||||||
>
|
>
|
||||||
<EditIcon color="burgundy" />
|
<EditIcon color="burgundy" />
|
||||||
{intl.formatMessage({ id: "Change room" })}{" "}
|
{intl.formatMessage({ id: "Change room" })}
|
||||||
</Link>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{roomTypeCode && (
|
{roomTypeCode && (
|
||||||
<div className={styles.details}>
|
<div className={styles.details}>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.roomsFilter {
|
.roomsFilter {
|
||||||
|
|||||||
@@ -72,10 +72,13 @@ export default function RoomCard({
|
|||||||
const isUserLoggedIn = !!session
|
const isUserLoggedIn = !!session
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const searchParams = useSearchParams()
|
const searchParams = useSearchParams()
|
||||||
const { selectRate, selectedRates } = useRateSelectionStore((state) => ({
|
const { selectRate, selectedRates, closeModifyRate } = useRateSelectionStore(
|
||||||
selectRate: state.selectRate,
|
(state) => ({
|
||||||
selectedRates: state.selectedRates,
|
selectRate: state.selectRate,
|
||||||
}))
|
selectedRates: state.selectedRates,
|
||||||
|
closeModifyRate: state.closeModifyRate,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
const selectedRate = useRateSelectionStore(
|
const selectedRate = useRateSelectionStore(
|
||||||
(state) => state.selectedRates[roomListIndex]
|
(state) => state.selectedRates[roomListIndex]
|
||||||
@@ -389,7 +392,10 @@ export default function RoomCard({
|
|||||||
return (
|
return (
|
||||||
<FlexibilityOption
|
<FlexibilityOption
|
||||||
key={product.productType.public.rateCode}
|
key={product.productType.public.rateCode}
|
||||||
handleSelect={handleRateSelection}
|
handleSelect={(rateCode, rateName, paymentTerm) => {
|
||||||
|
handleRateSelection(rateCode, rateName, paymentTerm)
|
||||||
|
closeModifyRate()
|
||||||
|
}}
|
||||||
isSelected={
|
isSelected={
|
||||||
selectedRate?.publicRateCode ===
|
selectedRate?.publicRateCode ===
|
||||||
product.productType.public.rateCode &&
|
product.productType.public.rateCode &&
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { usePathname, useRouter, useSearchParams } from "next/navigation"
|
import { useRouter, useSearchParams } from "next/navigation"
|
||||||
import { useEffect, useMemo } from "react"
|
import { useCallback, useEffect, useMemo, useTransition } from "react"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { useRateSelectionStore } from "@/stores/select-rate/rate-selection"
|
import { useRateSelectionStore } from "@/stores/select-rate/rate-selection"
|
||||||
import { useRoomFilteringStore } from "@/stores/select-rate/room-filtering"
|
import { useRoomFilteringStore } from "@/stores/select-rate/room-filtering"
|
||||||
|
|
||||||
|
import { ChevronDownSmallIcon } from "@/components/Icons"
|
||||||
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||||
import { trackLowestRoomPrice } from "@/utils/tracking"
|
import { trackLowestRoomPrice } from "@/utils/tracking"
|
||||||
import { convertObjToSearchParams, convertSearchParamsToObj } from "@/utils/url"
|
import { convertObjToSearchParams, convertSearchParamsToObj } from "@/utils/url"
|
||||||
@@ -34,9 +36,9 @@ export default function Rooms({
|
|||||||
vat,
|
vat,
|
||||||
}: SelectRateProps) {
|
}: SelectRateProps) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const pathname = usePathname()
|
|
||||||
const searchParams = useSearchParams()
|
const searchParams = useSearchParams()
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
const [isPending, startTransition] = useTransition()
|
||||||
|
|
||||||
const hotelId = searchParams.get("hotel")
|
const hotelId = searchParams.get("hotel")
|
||||||
const arrivalDate = searchParams.get("fromDate")
|
const arrivalDate = searchParams.get("fromDate")
|
||||||
@@ -48,6 +50,8 @@ export default function Rooms({
|
|||||||
calculateRateSummary,
|
calculateRateSummary,
|
||||||
initializeRates,
|
initializeRates,
|
||||||
setGuestsInRooms,
|
setGuestsInRooms,
|
||||||
|
modifyRateIndex,
|
||||||
|
closeModifyRate,
|
||||||
} = useRateSelectionStore()
|
} = useRateSelectionStore()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -133,13 +137,6 @@ export default function Rooms({
|
|||||||
calculateRateSummary,
|
calculateRateSummary,
|
||||||
])
|
])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!rateSummary?.some((rate) => rate === null)) return
|
|
||||||
|
|
||||||
const hasAnySelection = selectedRates.some((rate) => rate !== undefined)
|
|
||||||
if (!hasAnySelection) return
|
|
||||||
}, [rateSummary, selectedRates])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const pricesWithCurrencies = visibleRooms.flatMap((room) =>
|
const pricesWithCurrencies = visibleRooms.flatMap((room) =>
|
||||||
room.products.map((product) => ({
|
room.products.map((product) => ({
|
||||||
@@ -163,35 +160,35 @@ export default function Rooms({
|
|||||||
})
|
})
|
||||||
}, [arrivalDate, departureDate, hotelId, visibleRooms])
|
}, [arrivalDate, departureDate, hotelId, visibleRooms])
|
||||||
|
|
||||||
const queryParams = useMemo(() => {
|
|
||||||
const rooms = rateSummary.map((rate, index) => ({
|
|
||||||
roomTypeCode: rate?.roomTypeCode,
|
|
||||||
rateCode: rate?.public.rateCode,
|
|
||||||
counterRateCode: rate?.member?.rateCode,
|
|
||||||
packages: selectedPackagesByRoom[index] || [],
|
|
||||||
}))
|
|
||||||
|
|
||||||
const newSearchParams = convertObjToSearchParams({ rooms }, searchParams)
|
|
||||||
|
|
||||||
return newSearchParams
|
|
||||||
}, [searchParams, rateSummary, selectedPackagesByRoom])
|
|
||||||
|
|
||||||
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
startTransition(() => {
|
||||||
|
const rooms = rateSummary.map((rate, index) => ({
|
||||||
|
roomTypeCode: rate?.roomTypeCode,
|
||||||
|
rateCode: rate?.public.rateCode,
|
||||||
|
counterRateCode: rate?.member?.rateCode,
|
||||||
|
packages: selectedPackagesByRoom[index] || [],
|
||||||
|
}))
|
||||||
|
|
||||||
window.history.pushState(null, "", `${pathname}?${queryParams.toString()}`)
|
const newSearchParams = convertObjToSearchParams({ rooms }, searchParams)
|
||||||
router.push(`details?${queryParams}`)
|
router.push(`details?${newSearchParams}`)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
const SCROLL_OFFSET = 100
|
const SCROLL_OFFSET = 100
|
||||||
const roomElements = document.querySelectorAll(`.${styles.roomContainer}`)
|
const roomElements = document.querySelectorAll(`.${styles.roomContainer}`)
|
||||||
const index = selectedRates.findIndex((rate) => rate === undefined)
|
|
||||||
|
|
||||||
const targetIndex = index === -1 ? selectedRates.length - 1 : index - 1
|
let targetIndex: number
|
||||||
|
if (modifyRateIndex !== null) {
|
||||||
|
targetIndex = modifyRateIndex
|
||||||
|
} else {
|
||||||
|
const index = selectedRates.findIndex((rate) => rate === undefined)
|
||||||
|
targetIndex = index === -1 ? selectedRates.length - 1 : index - 1
|
||||||
|
}
|
||||||
|
|
||||||
const selectedRoom = roomElements[targetIndex]
|
const selectedRoom = roomElements[targetIndex]
|
||||||
|
|
||||||
if (selectedRoom) {
|
if (selectedRoom) {
|
||||||
const elementPosition = selectedRoom.getBoundingClientRect().top
|
const elementPosition = selectedRoom.getBoundingClientRect().top
|
||||||
const offsetPosition = elementPosition + window.scrollY - SCROLL_OFFSET
|
const offsetPosition = elementPosition + window.scrollY - SCROLL_OFFSET
|
||||||
@@ -202,40 +199,79 @@ export default function Rooms({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, [selectedRates])
|
}, [selectedRates, modifyRateIndex])
|
||||||
|
|
||||||
|
const getRoomState = useCallback(
|
||||||
|
(index: number) => {
|
||||||
|
const isFirstRoom = index === 0
|
||||||
|
const hasPrevRoomBeenSelected = selectedRates[index - 1] !== undefined
|
||||||
|
const isCurrentRoomSelected = selectedRates[index] !== undefined
|
||||||
|
const isModifyRoom = modifyRateIndex === index
|
||||||
|
|
||||||
|
if (isModifyRoom && isCurrentRoomSelected) {
|
||||||
|
return { active: true, selected: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCurrentRoomSelected) {
|
||||||
|
return { active: false, selected: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(isFirstRoom || hasPrevRoomBeenSelected) &&
|
||||||
|
modifyRateIndex === null
|
||||||
|
) {
|
||||||
|
return { active: true, selected: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { active: false, selected: false }
|
||||||
|
},
|
||||||
|
[modifyRateIndex, selectedRates]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
{isMultipleRooms ? (
|
{isMultipleRooms ? (
|
||||||
bookingWidgetSearchData.rooms.map((room, index) => {
|
bookingWidgetSearchData.rooms.map((room, index) => {
|
||||||
const classNames = roomSelectionPanelVariants({
|
const roomState = getRoomState(index)
|
||||||
active:
|
const classNames = roomSelectionPanelVariants(roomState)
|
||||||
(index === 0 || selectedRates[index - 1] !== undefined) &&
|
|
||||||
selectedRates[index] === undefined,
|
|
||||||
selected: selectedRates[index] !== undefined,
|
|
||||||
})
|
|
||||||
return (
|
return (
|
||||||
<div key={index} className={styles.roomContainer}>
|
<div key={index} className={styles.roomContainer}>
|
||||||
{selectedRates[index] === undefined && (
|
{!roomState.selected && (
|
||||||
<Subtitle>
|
<header className={styles.header}>
|
||||||
{intl.formatMessage(
|
<Subtitle>
|
||||||
{ id: "Room {roomIndex}" },
|
{intl.formatMessage(
|
||||||
{ roomIndex: index + 1 }
|
{ id: "Room {roomIndex}" },
|
||||||
)}
|
{ roomIndex: index + 1 }
|
||||||
,{" "}
|
)}
|
||||||
{intl.formatMessage(
|
,{" "}
|
||||||
{
|
{intl.formatMessage(
|
||||||
id: room.childrenInRoom?.length
|
{
|
||||||
? "{adults} adults, {children} children"
|
id: room.childrenInRoom?.length
|
||||||
: "{adults} adults",
|
? "{adults} adults, {children} children"
|
||||||
},
|
: "{adults} adults",
|
||||||
{
|
},
|
||||||
adults: room.adults,
|
{
|
||||||
children: room.childrenInRoom?.length,
|
adults: room.adults,
|
||||||
}
|
children: room.childrenInRoom?.length,
|
||||||
)}
|
}
|
||||||
</Subtitle>
|
)}
|
||||||
|
</Subtitle>
|
||||||
|
{modifyRateIndex === index ? (
|
||||||
|
<Button
|
||||||
|
variant="icon"
|
||||||
|
size="medium"
|
||||||
|
intent="text"
|
||||||
|
theme="base"
|
||||||
|
onClick={closeModifyRate}
|
||||||
|
>
|
||||||
|
{intl.formatMessage({ id: "Close" })}
|
||||||
|
<ChevronDownSmallIcon />
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</header>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={classNames}>
|
<div className={classNames}>
|
||||||
<div className={styles.roomPanel}>
|
<div className={styles.roomPanel}>
|
||||||
<SelectedRoomPanel
|
<SelectedRoomPanel
|
||||||
|
|||||||
@@ -5,6 +5,14 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--Spacing-x2);
|
gap: var(--Spacing-x2);
|
||||||
padding: var(--Spacing-x2) 0;
|
padding: var(--Spacing-x2) 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.roomContainer {
|
.roomContainer {
|
||||||
@@ -12,7 +20,8 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
border: 1px solid var(--Base-Border-Subtle);
|
border: 1px solid var(--Base-Border-Subtle);
|
||||||
border-radius: var(--Corner-radius-Large);
|
border-radius: var(--Corner-radius-Large);
|
||||||
padding: var(--Spacing-x2);
|
padding: var(--Spacing-x3);
|
||||||
|
background: var(--Base-Surface-Primary-light-Normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
.roomPanel,
|
.roomPanel,
|
||||||
@@ -24,6 +33,7 @@
|
|||||||
transition:
|
transition:
|
||||||
opacity 0.3s ease,
|
opacity 0.3s ease,
|
||||||
grid-template-rows 0.3s ease;
|
grid-template-rows 0.3s ease;
|
||||||
|
transform-origin: bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
.roomPanel > * {
|
.roomPanel > * {
|
||||||
@@ -47,6 +57,7 @@
|
|||||||
grid-template-rows: 1fr;
|
grid-template-rows: 1fr;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
padding-top: var(--Spacing-x1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hotelAlert {
|
.hotelAlert {
|
||||||
@@ -54,3 +65,9 @@
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: var(--Spacing-x-one-and-half);
|
padding: var(--Spacing-x-one-and-half);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.roomContainer {
|
||||||
|
padding: var(--Spacing-x2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { useRateSelectionStore } from "@/stores/select-rate/rate-selection"
|
|||||||
import { EditIcon } from "@/components/Icons"
|
import { EditIcon } from "@/components/Icons"
|
||||||
import Image from "@/components/Image"
|
import Image from "@/components/Image"
|
||||||
import Button from "@/components/TempDesignSystem/Button"
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
|
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"
|
||||||
|
|
||||||
@@ -42,41 +43,45 @@ export default function SelectedRoomPanel({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.selectedRoomPanel}>
|
<div className={styles.selectedRoomPanel}>
|
||||||
<div>
|
<div className={styles.titleContainer}>
|
||||||
<Caption type="bold">
|
<div>
|
||||||
{intl.formatMessage(
|
<Caption type="regular">
|
||||||
{
|
{intl.formatMessage(
|
||||||
id: "Room {roomIndex}",
|
{
|
||||||
},
|
id: "Room {roomIndex}",
|
||||||
{
|
},
|
||||||
roomIndex: roomIndex + 1,
|
{
|
||||||
}
|
roomIndex: roomIndex + 1,
|
||||||
)}
|
}
|
||||||
</Caption>
|
)}
|
||||||
<Subtitle>
|
</Caption>
|
||||||
{selectedRate?.roomType},{" "}
|
<Subtitle color="uiTextHighContrast">
|
||||||
{intl.formatMessage(
|
{selectedRate?.roomType},{" "}
|
||||||
{
|
{intl.formatMessage(
|
||||||
id: room.childrenInRoom?.length
|
{
|
||||||
? "{adults} adults, {children} children"
|
id: room.childrenInRoom?.length
|
||||||
: "{adults} adults",
|
? "{adults} adults, {children} children"
|
||||||
},
|
: "{adults} adults",
|
||||||
{
|
},
|
||||||
adults: room.adults,
|
{
|
||||||
children: room.childrenInRoom?.length,
|
adults: room.adults,
|
||||||
}
|
children: room.childrenInRoom?.length,
|
||||||
)}
|
}
|
||||||
</Subtitle>
|
)}
|
||||||
<Caption>
|
</Subtitle>
|
||||||
{selectedRate?.priceName}, {selectedRate?.priceTerm}
|
</div>
|
||||||
</Caption>
|
<div>
|
||||||
<Caption>
|
<Body>
|
||||||
{selectedRate?.public.localPrice.pricePerNight}{" "}
|
{selectedRate?.priceName}, {selectedRate?.priceTerm}
|
||||||
{selectedRate?.public.localPrice.currency}/
|
</Body>
|
||||||
{intl.formatMessage({
|
<Body>
|
||||||
id: "night",
|
{selectedRate?.public.localPrice.pricePerNight}{" "}
|
||||||
})}
|
{selectedRate?.public.localPrice.currency}/
|
||||||
</Caption>
|
{intl.formatMessage({
|
||||||
|
id: "night",
|
||||||
|
})}
|
||||||
|
</Body>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.imageAndModifyButtonContainer}>
|
<div className={styles.imageAndModifyButtonContainer}>
|
||||||
{images?.[0]?.imageSizes?.tiny && (
|
{images?.[0]?.imageSizes?.tiny && (
|
||||||
|
|||||||
@@ -12,9 +12,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.imageContainer {
|
.imageContainer {
|
||||||
width: 240px;
|
width: 187px;
|
||||||
height: 160px;
|
height: 105px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
border-radius: var(--Corner-radius-Small);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.titleContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--Spacing-x1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
|
|||||||
@@ -26,7 +26,9 @@ interface RateSelectionState {
|
|||||||
isPriceDetailsModalOpen: boolean
|
isPriceDetailsModalOpen: boolean
|
||||||
isSummaryOpen: boolean
|
isSummaryOpen: boolean
|
||||||
guestsInRooms: { adults: number; children?: Child[] }[]
|
guestsInRooms: { adults: number; children?: Child[] }[]
|
||||||
|
modifyRateIndex: number | null
|
||||||
modifyRate: (index: number) => void
|
modifyRate: (index: number) => void
|
||||||
|
closeModifyRate: () => void
|
||||||
selectRate: (index: number, rate: RateCode | undefined) => void
|
selectRate: (index: number, rate: RateCode | undefined) => void
|
||||||
initializeRates: (count: number) => void
|
initializeRates: (count: number) => void
|
||||||
calculateRateSummary: ({
|
calculateRateSummary: ({
|
||||||
@@ -46,18 +48,18 @@ export const useRateSelectionStore = create<RateSelectionState>((set, get) => ({
|
|||||||
isPriceDetailsModalOpen: false,
|
isPriceDetailsModalOpen: false,
|
||||||
isSummaryOpen: false,
|
isSummaryOpen: false,
|
||||||
guestsInRooms: [{ adults: 1 }],
|
guestsInRooms: [{ adults: 1 }],
|
||||||
modifyRate: (index) =>
|
modifyRateIndex: null,
|
||||||
set((state) => {
|
modifyRate: (index) => set({ modifyRateIndex: index }),
|
||||||
const newRates = [...state.selectedRates]
|
closeModifyRate: () => set({ modifyRateIndex: null }),
|
||||||
newRates[index] = undefined
|
selectRate: (index, rate) => {
|
||||||
return { selectedRates: newRates }
|
|
||||||
}),
|
|
||||||
selectRate: (index, rate) =>
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const newRates = [...state.selectedRates]
|
const newRates = [...state.selectedRates]
|
||||||
newRates[index] = rate
|
newRates[index] = rate
|
||||||
return { selectedRates: newRates }
|
return {
|
||||||
}),
|
selectedRates: newRates,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
initializeRates: (count) =>
|
initializeRates: (count) =>
|
||||||
set({ selectedRates: new Array(count).fill(undefined) }),
|
set({ selectedRates: new Array(count).fill(undefined) }),
|
||||||
calculateRateSummary: (params) => {
|
calculateRateSummary: (params) => {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { RoomConfiguration } from "@/types/trpc/routers/hotel/roomAvailability"
|
|
||||||
|
|
||||||
export interface SelectedRoomProps {
|
export interface SelectedRoomProps {
|
||||||
hotelId: string
|
hotelId: string
|
||||||
roomType: string
|
roomType: string
|
||||||
roomTypeCode: string
|
roomTypeCode: string
|
||||||
rateDescription: string
|
rateDescription: string
|
||||||
|
roomIndex: number
|
||||||
|
searchParamsStr: string
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user