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:
@@ -1,12 +1,14 @@
|
||||
"use client"
|
||||
|
||||
import { usePathname, useRouter, useSearchParams } from "next/navigation"
|
||||
import { useEffect, useMemo } from "react"
|
||||
import { useRouter, useSearchParams } from "next/navigation"
|
||||
import { useCallback, useEffect, useMemo, useTransition } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { useRateSelectionStore } from "@/stores/select-rate/rate-selection"
|
||||
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 { trackLowestRoomPrice } from "@/utils/tracking"
|
||||
import { convertObjToSearchParams, convertSearchParamsToObj } from "@/utils/url"
|
||||
@@ -34,9 +36,9 @@ export default function Rooms({
|
||||
vat,
|
||||
}: SelectRateProps) {
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
const searchParams = useSearchParams()
|
||||
const intl = useIntl()
|
||||
const [isPending, startTransition] = useTransition()
|
||||
|
||||
const hotelId = searchParams.get("hotel")
|
||||
const arrivalDate = searchParams.get("fromDate")
|
||||
@@ -48,6 +50,8 @@ export default function Rooms({
|
||||
calculateRateSummary,
|
||||
initializeRates,
|
||||
setGuestsInRooms,
|
||||
modifyRateIndex,
|
||||
closeModifyRate,
|
||||
} = useRateSelectionStore()
|
||||
|
||||
const {
|
||||
@@ -133,13 +137,6 @@ export default function Rooms({
|
||||
calculateRateSummary,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (!rateSummary?.some((rate) => rate === null)) return
|
||||
|
||||
const hasAnySelection = selectedRates.some((rate) => rate !== undefined)
|
||||
if (!hasAnySelection) return
|
||||
}, [rateSummary, selectedRates])
|
||||
|
||||
useEffect(() => {
|
||||
const pricesWithCurrencies = visibleRooms.flatMap((room) =>
|
||||
room.products.map((product) => ({
|
||||
@@ -163,35 +160,35 @@ export default function Rooms({
|
||||
})
|
||||
}, [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>) {
|
||||
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()}`)
|
||||
router.push(`details?${queryParams}`)
|
||||
const newSearchParams = convertObjToSearchParams({ rooms }, searchParams)
|
||||
router.push(`details?${newSearchParams}`)
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
requestAnimationFrame(() => {
|
||||
const SCROLL_OFFSET = 100
|
||||
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]
|
||||
|
||||
if (selectedRoom) {
|
||||
const elementPosition = selectedRoom.getBoundingClientRect().top
|
||||
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 (
|
||||
<div className={styles.content}>
|
||||
{isMultipleRooms ? (
|
||||
bookingWidgetSearchData.rooms.map((room, index) => {
|
||||
const classNames = roomSelectionPanelVariants({
|
||||
active:
|
||||
(index === 0 || selectedRates[index - 1] !== undefined) &&
|
||||
selectedRates[index] === undefined,
|
||||
selected: selectedRates[index] !== undefined,
|
||||
})
|
||||
const roomState = getRoomState(index)
|
||||
const classNames = roomSelectionPanelVariants(roomState)
|
||||
|
||||
return (
|
||||
<div key={index} className={styles.roomContainer}>
|
||||
{selectedRates[index] === undefined && (
|
||||
<Subtitle>
|
||||
{intl.formatMessage(
|
||||
{ id: "Room {roomIndex}" },
|
||||
{ roomIndex: index + 1 }
|
||||
)}
|
||||
,{" "}
|
||||
{intl.formatMessage(
|
||||
{
|
||||
id: room.childrenInRoom?.length
|
||||
? "{adults} adults, {children} children"
|
||||
: "{adults} adults",
|
||||
},
|
||||
{
|
||||
adults: room.adults,
|
||||
children: room.childrenInRoom?.length,
|
||||
}
|
||||
)}
|
||||
</Subtitle>
|
||||
{!roomState.selected && (
|
||||
<header className={styles.header}>
|
||||
<Subtitle>
|
||||
{intl.formatMessage(
|
||||
{ id: "Room {roomIndex}" },
|
||||
{ roomIndex: index + 1 }
|
||||
)}
|
||||
,{" "}
|
||||
{intl.formatMessage(
|
||||
{
|
||||
id: room.childrenInRoom?.length
|
||||
? "{adults} adults, {children} children"
|
||||
: "{adults} adults",
|
||||
},
|
||||
{
|
||||
adults: room.adults,
|
||||
children: room.childrenInRoom?.length,
|
||||
}
|
||||
)}
|
||||
</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={styles.roomPanel}>
|
||||
<SelectedRoomPanel
|
||||
|
||||
@@ -5,6 +5,14 @@
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
padding: var(--Spacing-x2) 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.roomContainer {
|
||||
@@ -12,7 +20,8 @@
|
||||
flex-direction: column;
|
||||
border: 1px solid var(--Base-Border-Subtle);
|
||||
border-radius: var(--Corner-radius-Large);
|
||||
padding: var(--Spacing-x2);
|
||||
padding: var(--Spacing-x3);
|
||||
background: var(--Base-Surface-Primary-light-Normal);
|
||||
}
|
||||
|
||||
.roomPanel,
|
||||
@@ -24,6 +33,7 @@
|
||||
transition:
|
||||
opacity 0.3s ease,
|
||||
grid-template-rows 0.3s ease;
|
||||
transform-origin: bottom;
|
||||
}
|
||||
|
||||
.roomPanel > * {
|
||||
@@ -47,6 +57,7 @@
|
||||
grid-template-rows: 1fr;
|
||||
opacity: 1;
|
||||
height: auto;
|
||||
padding-top: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.hotelAlert {
|
||||
@@ -54,3 +65,9 @@
|
||||
margin: 0 auto;
|
||||
padding: var(--Spacing-x-one-and-half);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.roomContainer {
|
||||
padding: var(--Spacing-x2);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user