fix: remove guest picker store
This commit is contained in:
@@ -114,6 +114,8 @@
|
|||||||
/* Z-INDEX */
|
/* Z-INDEX */
|
||||||
--header-z-index: 11;
|
--header-z-index: 11;
|
||||||
--menu-overlay-z-index: 11;
|
--menu-overlay-z-index: 11;
|
||||||
|
--booking-widget-z-index: 10;
|
||||||
|
--booking-widget-open-z-index: 100;
|
||||||
--dialog-z-index: 9;
|
--dialog-z-index: 9;
|
||||||
--sidepeek-z-index: 100;
|
--sidepeek-z-index: 100;
|
||||||
--lightbox-z-index: 150;
|
--lightbox-z-index: 150;
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ export default function BookingWidgetClient({
|
|||||||
name: StickyElementNameEnum.BOOKING_WIDGET,
|
name: StickyElementNameEnum.BOOKING_WIDGET,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const bookingWidgetSearchData: BookingWidgetSearchParams | undefined =
|
const bookingWidgetSearchData: BookingWidgetSearchParams | undefined =
|
||||||
searchParams
|
searchParams
|
||||||
? (getFormattedUrlQueryParams(new URLSearchParams(searchParams), {
|
? (getFormattedUrlQueryParams(new URLSearchParams(searchParams), {
|
||||||
@@ -79,9 +78,7 @@ export default function BookingWidgetClient({
|
|||||||
const methods = useForm<BookingWidgetSchema>({
|
const methods = useForm<BookingWidgetSchema>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
search: selectedLocation?.name ?? "",
|
search: selectedLocation?.name ?? "",
|
||||||
location: selectedLocation
|
location: selectedLocation ? JSON.stringify(selectedLocation) : undefined,
|
||||||
? JSON.stringify(selectedLocation)
|
|
||||||
: undefined,
|
|
||||||
date: {
|
date: {
|
||||||
// UTC is required to handle requests from far away timezones https://scandichotels.atlassian.net/browse/SWAP-6375 & PET-507
|
// UTC is required to handle requests from far away timezones https://scandichotels.atlassian.net/browse/SWAP-6375 & PET-507
|
||||||
// This is specifically to handle timezones falling in different dates.
|
// This is specifically to handle timezones falling in different dates.
|
||||||
@@ -147,29 +144,34 @@ export default function BookingWidgetClient({
|
|||||||
? JSON.parse(sessionStorageSearchData)
|
? JSON.parse(sessionStorageSearchData)
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
!(selectedLocation?.name) && initialSelectedLocation?.name &&
|
!selectedLocation?.name &&
|
||||||
|
initialSelectedLocation?.name &&
|
||||||
methods.setValue("search", initialSelectedLocation.name)
|
methods.setValue("search", initialSelectedLocation.name)
|
||||||
!selectedLocation && sessionStorageSearchData &&
|
!selectedLocation &&
|
||||||
|
sessionStorageSearchData &&
|
||||||
methods.setValue("location", encodeURIComponent(sessionStorageSearchData))
|
methods.setValue("location", encodeURIComponent(sessionStorageSearchData))
|
||||||
}, [methods, selectedLocation])
|
}, [methods, selectedLocation])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...methods}>
|
<FormProvider {...methods}>
|
||||||
<section ref={bookingWidgetRef} className={styles.containerDesktop}>
|
<section
|
||||||
<Form locations={locations} type={type} />
|
ref={bookingWidgetRef}
|
||||||
</section>
|
className={styles.wrapper}
|
||||||
<section className={styles.containerMobile} data-open={isOpen}>
|
data-open={isOpen}
|
||||||
<button
|
>
|
||||||
className={styles.close}
|
<MobileToggleButton openMobileSearch={openMobileSearch} />
|
||||||
onClick={closeMobileSearch}
|
<div className={styles.formContainer}>
|
||||||
type="button"
|
<button
|
||||||
>
|
className={styles.close}
|
||||||
<CloseLargeIcon />
|
onClick={closeMobileSearch}
|
||||||
</button>
|
type="button"
|
||||||
<Form locations={locations} type={type} />
|
>
|
||||||
|
<CloseLargeIcon />
|
||||||
|
</button>
|
||||||
|
<Form locations={locations} type={type} />
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<div className={styles.backdrop} onClick={closeMobileSearch} />
|
<div className={styles.backdrop} onClick={closeMobileSearch} />
|
||||||
<MobileToggleButton openMobileSearch={openMobileSearch} />
|
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,6 @@
|
|||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--Spacing-x-one-and-half);
|
gap: var(--Spacing-x-one-and-half);
|
||||||
padding: var(--Spacing-x2);
|
padding: var(--Spacing-x2);
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,12 +31,6 @@ export default function MobileToggleButton({
|
|||||||
const location = useWatch({ name: "location" })
|
const location = useWatch({ name: "location" })
|
||||||
const rooms: BookingWidgetSchema["rooms"] = useWatch({ name: "rooms" })
|
const rooms: BookingWidgetSchema["rooms"] = useWatch({ name: "rooms" })
|
||||||
|
|
||||||
const bookingWidgetMobileRef = useRef(null)
|
|
||||||
useStickyPosition({
|
|
||||||
ref: bookingWidgetMobileRef,
|
|
||||||
name: StickyElementNameEnum.BOOKING_WIDGET_MOBILE,
|
|
||||||
})
|
|
||||||
|
|
||||||
const parsedLocation: Location | null = location
|
const parsedLocation: Location | null = location
|
||||||
? JSON.parse(decodeURIComponent(location))
|
? JSON.parse(decodeURIComponent(location))
|
||||||
: null
|
: null
|
||||||
@@ -75,7 +69,6 @@ export default function MobileToggleButton({
|
|||||||
className={locationAndDateIsSet ? styles.complete : styles.partial}
|
className={locationAndDateIsSet ? styles.complete : styles.partial}
|
||||||
onClick={openMobileSearch}
|
onClick={openMobileSearch}
|
||||||
role="button"
|
role="button"
|
||||||
ref={bookingWidgetMobileRef}
|
|
||||||
>
|
>
|
||||||
{!locationAndDateIsSet && (
|
{!locationAndDateIsSet && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,60 +1,62 @@
|
|||||||
.containerDesktop,
|
.wrapper {
|
||||||
.containerMobile,
|
position: sticky;
|
||||||
.close {
|
z-index: var(--booking-widget-z-index);
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 767px) {
|
.formContainer {
|
||||||
.containerMobile {
|
display: grid;
|
||||||
background-color: var(--UI-Input-Controls-Surface-Normal);
|
grid-template-rows: 36px 1fr;
|
||||||
bottom: -100%;
|
background-color: var(--UI-Input-Controls-Surface-Normal);
|
||||||
display: grid;
|
border-radius: var(--Corner-radius-Large) var(--Corner-radius-Large) 0 0;
|
||||||
gap: var(--Spacing-x3);
|
gap: var(--Spacing-x3);
|
||||||
grid-template-rows: 36px 1fr;
|
height: calc(100dvh - 20px);
|
||||||
height: calc(100dvh - 20px);
|
width: 100%;
|
||||||
padding: var(--Spacing-x3) var(--Spacing-x2) var(--Spacing-x7);
|
padding: var(--Spacing-x3) var(--Spacing-x2) var(--Spacing-x7);
|
||||||
position: fixed;
|
position: fixed;
|
||||||
transition: bottom 300ms ease;
|
bottom: -100%;
|
||||||
width: 100%;
|
transition: bottom 300ms ease;
|
||||||
z-index: 10000;
|
}
|
||||||
border-radius: var(--Corner-radius-Large) var(--Corner-radius-Large) 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.containerMobile[data-open="true"] {
|
.wrapper[data-open="true"] {
|
||||||
bottom: 0;
|
z-index: var(--booking-widget-open-z-index);
|
||||||
}
|
}
|
||||||
|
|
||||||
.close {
|
.wrapper[data-open="true"] .formContainer {
|
||||||
background: none;
|
bottom: 0;
|
||||||
border: none;
|
}
|
||||||
cursor: pointer;
|
|
||||||
justify-self: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.containerMobile[data-open="true"] + .backdrop {
|
.close {
|
||||||
background-color: rgba(0, 0, 0, 0.4);
|
background: none;
|
||||||
height: 100%;
|
border: none;
|
||||||
left: 0;
|
cursor: pointer;
|
||||||
position: absolute;
|
justify-self: flex-end;
|
||||||
top: 0;
|
}
|
||||||
width: 100%;
|
|
||||||
z-index: 1000;
|
.wrapper[data-open="true"] + .backdrop {
|
||||||
}
|
background-color: rgba(0, 0, 0, 0.4);
|
||||||
|
height: 100%;
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
z-index: calc(var(--booking-widget-open-z-index) - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
.containerDesktop {
|
.wrapper {
|
||||||
display: block;
|
|
||||||
box-shadow: 0px 4px 24px 0px rgba(0, 0, 0, 0.05);
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 10;
|
|
||||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1367px) {
|
.formContainer {
|
||||||
.container {
|
display: block;
|
||||||
z-index: 9;
|
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||||
|
box-shadow: 0px 4px 24px 0px rgba(0, 0, 0, 0.05);
|
||||||
|
height: auto;
|
||||||
|
position: static;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useState } from "react"
|
|
||||||
import { useWatch } from "react-hook-form"
|
import { useWatch } from "react-hook-form"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
@@ -7,7 +6,6 @@ import { dt } from "@/lib/dt"
|
|||||||
|
|
||||||
import DatePicker from "@/components/DatePicker"
|
import DatePicker from "@/components/DatePicker"
|
||||||
import GuestsRoomsPickerForm from "@/components/GuestsRoomsPicker"
|
import GuestsRoomsPickerForm from "@/components/GuestsRoomsPicker"
|
||||||
import GuestsRoomsProvider from "@/components/GuestsRoomsPicker/Provider/GuestsRoomsProvider"
|
|
||||||
import { SearchIcon } from "@/components/Icons"
|
import { SearchIcon } from "@/components/Icons"
|
||||||
import Button from "@/components/TempDesignSystem/Button"
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||||
@@ -26,12 +24,10 @@ export default function FormContent({
|
|||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const selectedDate = useWatch({ name: "date" })
|
const selectedDate = useWatch({ name: "date" })
|
||||||
|
|
||||||
const rooms = intl.formatMessage({ id: "Guests & Rooms" })
|
const roomsLabel = intl.formatMessage({ id: "Guests & Rooms" })
|
||||||
|
|
||||||
const nights = dt(selectedDate.toDate).diff(dt(selectedDate.fromDate), "days")
|
const nights = dt(selectedDate.toDate).diff(dt(selectedDate.fromDate), "days")
|
||||||
|
|
||||||
const selectedGuests = useWatch({ name: "rooms" })
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.input}>
|
<div className={styles.input}>
|
||||||
@@ -51,12 +47,10 @@ export default function FormContent({
|
|||||||
<div className={styles.rooms}>
|
<div className={styles.rooms}>
|
||||||
<label>
|
<label>
|
||||||
<Caption color="red" type="bold" asChild>
|
<Caption color="red" type="bold" asChild>
|
||||||
<span>{rooms}</span>
|
<span>{roomsLabel}</span>
|
||||||
</Caption>
|
</Caption>
|
||||||
</label>
|
</label>
|
||||||
<GuestsRoomsProvider selectedGuests={selectedGuests}>
|
<GuestsRoomsPickerForm />
|
||||||
<GuestsRoomsPickerForm name="rooms" />
|
|
||||||
</GuestsRoomsProvider>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.voucherContainer}>
|
<div className={styles.voucherContainer}>
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
import { useFormContext } from "react-hook-form"
|
import { useFormContext } from "react-hook-form"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { useGuestsRoomsStore } from "@/stores/guests-rooms"
|
|
||||||
|
|
||||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||||
|
|
||||||
import Counter from "../Counter"
|
import Counter from "../Counter"
|
||||||
@@ -13,39 +11,37 @@ import styles from "./adult-selector.module.css"
|
|||||||
|
|
||||||
import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums"
|
import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums"
|
||||||
import {
|
import {
|
||||||
AdultSelectorProps,
|
|
||||||
Child,
|
Child,
|
||||||
|
SelectorProps,
|
||||||
} from "@/types/components/bookingWidget/guestsRoomsPicker"
|
} from "@/types/components/bookingWidget/guestsRoomsPicker"
|
||||||
|
|
||||||
export default function AdultSelector({ roomIndex = 0 }: AdultSelectorProps) {
|
export default function AdultSelector({
|
||||||
|
roomIndex = 0,
|
||||||
|
currentAdults,
|
||||||
|
currentChildren,
|
||||||
|
childrenInAdultsBed,
|
||||||
|
}: SelectorProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const adultsLabel = intl.formatMessage({ id: "Adults" })
|
const adultsLabel = intl.formatMessage({ id: "Adults" })
|
||||||
const { setValue } = useFormContext()
|
const { setValue } = useFormContext()
|
||||||
const { adults, child, childrenInAdultsBed } = useGuestsRoomsStore(
|
|
||||||
(state) => state.rooms[roomIndex]
|
|
||||||
)
|
|
||||||
const increaseAdults = useGuestsRoomsStore((state) => state.increaseAdults)
|
|
||||||
const decreaseAdults = useGuestsRoomsStore((state) => state.decreaseAdults)
|
|
||||||
|
|
||||||
function increaseAdultsCount(roomIndex: number) {
|
function increaseAdultsCount(roomIndex: number) {
|
||||||
if (adults < 6) {
|
if (currentAdults < 6) {
|
||||||
increaseAdults(roomIndex)
|
setValue(`rooms.${roomIndex}.adults`, currentAdults + 1)
|
||||||
setValue(`rooms.${roomIndex}.adults`, adults + 1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function decreaseAdultsCount(roomIndex: number) {
|
function decreaseAdultsCount(roomIndex: number) {
|
||||||
if (adults > 1) {
|
if (currentAdults > 1) {
|
||||||
decreaseAdults(roomIndex)
|
setValue(`rooms.${roomIndex}.adults`, currentAdults - 1)
|
||||||
setValue(`rooms.${roomIndex}.adults`, adults - 1)
|
if (childrenInAdultsBed > currentAdults) {
|
||||||
if (childrenInAdultsBed > adults) {
|
const toUpdateIndex = currentChildren.findIndex(
|
||||||
const toUpdateIndex = child.findIndex(
|
|
||||||
(child: Child) => child.bed == ChildBedMapEnum.IN_ADULTS_BED
|
(child: Child) => child.bed == ChildBedMapEnum.IN_ADULTS_BED
|
||||||
)
|
)
|
||||||
if (toUpdateIndex != -1) {
|
if (toUpdateIndex != -1) {
|
||||||
setValue(
|
setValue(
|
||||||
`rooms.${roomIndex}.children.${toUpdateIndex}.bed`,
|
`rooms.${roomIndex}.children.${toUpdateIndex}.bed`,
|
||||||
child[toUpdateIndex].age < 3
|
currentChildren[toUpdateIndex].age < 3
|
||||||
? ChildBedMapEnum.IN_CRIB
|
? ChildBedMapEnum.IN_CRIB
|
||||||
: ChildBedMapEnum.IN_EXTRA_BED
|
: ChildBedMapEnum.IN_EXTRA_BED
|
||||||
)
|
)
|
||||||
@@ -60,15 +56,15 @@ export default function AdultSelector({ roomIndex = 0 }: AdultSelectorProps) {
|
|||||||
{adultsLabel}
|
{adultsLabel}
|
||||||
</Caption>
|
</Caption>
|
||||||
<Counter
|
<Counter
|
||||||
count={adults}
|
count={currentAdults}
|
||||||
handleOnDecrease={() => {
|
handleOnDecrease={() => {
|
||||||
decreaseAdultsCount(roomIndex)
|
decreaseAdultsCount(roomIndex)
|
||||||
}}
|
}}
|
||||||
handleOnIncrease={() => {
|
handleOnIncrease={() => {
|
||||||
increaseAdultsCount(roomIndex)
|
increaseAdultsCount(roomIndex)
|
||||||
}}
|
}}
|
||||||
disableDecrease={adults == 1}
|
disableDecrease={currentAdults == 1}
|
||||||
disableIncrease={adults == 6}
|
disableIncrease={currentAdults == 6}
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
import { useFormContext } from "react-hook-form"
|
import { useFormContext } from "react-hook-form"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { useGuestsRoomsStore } from "@/stores/guests-rooms"
|
|
||||||
|
|
||||||
import { ErrorCircleIcon } from "@/components/Icons"
|
import { ErrorCircleIcon } from "@/components/Icons"
|
||||||
import Select from "@/components/TempDesignSystem/Select"
|
import Select from "@/components/TempDesignSystem/Select"
|
||||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||||
@@ -19,6 +17,8 @@ import {
|
|||||||
|
|
||||||
export default function ChildInfoSelector({
|
export default function ChildInfoSelector({
|
||||||
child = { age: -1, bed: -1 },
|
child = { age: -1, bed: -1 },
|
||||||
|
childrenInAdultsBed,
|
||||||
|
adults,
|
||||||
index = 0,
|
index = 0,
|
||||||
roomIndex = 0,
|
roomIndex = 0,
|
||||||
}: ChildInfoSelectorProps) {
|
}: ChildInfoSelectorProps) {
|
||||||
@@ -26,23 +26,7 @@ export default function ChildInfoSelector({
|
|||||||
const ageLabel = intl.formatMessage({ id: "Age" })
|
const ageLabel = intl.formatMessage({ id: "Age" })
|
||||||
const ageReqdErrMsg = intl.formatMessage({ id: "Child age is required" })
|
const ageReqdErrMsg = intl.formatMessage({ id: "Child age is required" })
|
||||||
const bedLabel = intl.formatMessage({ id: "Bed" })
|
const bedLabel = intl.formatMessage({ id: "Bed" })
|
||||||
const { setValue } = useFormContext()
|
const { setValue, formState } = useFormContext()
|
||||||
const { adults, childrenInAdultsBed } = useGuestsRoomsStore(
|
|
||||||
(state) => state.rooms[roomIndex]
|
|
||||||
)
|
|
||||||
const {
|
|
||||||
isValidated,
|
|
||||||
updateChildAge,
|
|
||||||
updateChildBed,
|
|
||||||
increaseChildInAdultsBed,
|
|
||||||
decreaseChildInAdultsBed,
|
|
||||||
} = useGuestsRoomsStore((state) => ({
|
|
||||||
isValidated: state.isValidated,
|
|
||||||
updateChildAge: state.updateChildAge,
|
|
||||||
updateChildBed: state.updateChildBed,
|
|
||||||
increaseChildInAdultsBed: state.increaseChildInAdultsBed,
|
|
||||||
decreaseChildInAdultsBed: state.decreaseChildInAdultsBed,
|
|
||||||
}))
|
|
||||||
|
|
||||||
const ageList = Array.from(Array(13).keys()).map((age) => ({
|
const ageList = Array.from(Array(13).keys()).map((age) => ({
|
||||||
label: `${age}`,
|
label: `${age}`,
|
||||||
@@ -50,7 +34,6 @@ export default function ChildInfoSelector({
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
function updateSelectedAge(age: number) {
|
function updateSelectedAge(age: number) {
|
||||||
updateChildAge(age, roomIndex, index)
|
|
||||||
setValue(`rooms.${roomIndex}.child.${index}.age`, age, {
|
setValue(`rooms.${roomIndex}.child.${index}.age`, age, {
|
||||||
shouldValidate: true,
|
shouldValidate: true,
|
||||||
})
|
})
|
||||||
@@ -59,12 +42,6 @@ export default function ChildInfoSelector({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateSelectedBed(bed: number) {
|
function updateSelectedBed(bed: number) {
|
||||||
if (bed == ChildBedMapEnum.IN_ADULTS_BED) {
|
|
||||||
increaseChildInAdultsBed(roomIndex)
|
|
||||||
} else if (child.bed == ChildBedMapEnum.IN_ADULTS_BED) {
|
|
||||||
decreaseChildInAdultsBed(roomIndex)
|
|
||||||
}
|
|
||||||
updateChildBed(bed, roomIndex, index)
|
|
||||||
setValue(`rooms.${roomIndex}.child.${index}.bed`, bed)
|
setValue(`rooms.${roomIndex}.child.${index}.bed`, bed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +74,7 @@ export default function ChildInfoSelector({
|
|||||||
return availableBedTypes
|
return availableBedTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("ALL TJHE ERORRORS", formState.errors)
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div key={index} className={styles.childInfoContainer}>
|
<div key={index} className={styles.childInfoContainer}>
|
||||||
@@ -131,12 +109,13 @@ export default function ChildInfoSelector({
|
|||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{isValidated && child.age < 0 ? (
|
|
||||||
|
{/* {isValidated && child.age < 0 ? (
|
||||||
<Caption color="red" className={styles.error}>
|
<Caption color="red" className={styles.error}>
|
||||||
<ErrorCircleIcon color="red" />
|
<ErrorCircleIcon color="red" />
|
||||||
{ageReqdErrMsg}
|
{ageReqdErrMsg}
|
||||||
</Caption>
|
</Caption>
|
||||||
) : null}
|
) : null} */}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
import { useFormContext } from "react-hook-form"
|
import { useFormContext } from "react-hook-form"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { useGuestsRoomsStore } from "@/stores/guests-rooms"
|
|
||||||
|
|
||||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||||
|
|
||||||
import Counter from "../Counter"
|
import Counter from "../Counter"
|
||||||
@@ -13,25 +11,22 @@ import ChildInfoSelector from "./ChildInfoSelector"
|
|||||||
import styles from "./child-selector.module.css"
|
import styles from "./child-selector.module.css"
|
||||||
|
|
||||||
import { BookingWidgetSchema } from "@/types/components/bookingWidget"
|
import { BookingWidgetSchema } from "@/types/components/bookingWidget"
|
||||||
import { ChildSelectorProps } from "@/types/components/bookingWidget/guestsRoomsPicker"
|
import { SelectorProps } from "@/types/components/bookingWidget/guestsRoomsPicker"
|
||||||
|
|
||||||
export default function ChildSelector({ roomIndex = 0 }: ChildSelectorProps) {
|
export default function ChildSelector({
|
||||||
|
roomIndex = 0,
|
||||||
|
currentAdults,
|
||||||
|
childrenInAdultsBed,
|
||||||
|
currentChildren,
|
||||||
|
}: SelectorProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const childrenLabel = intl.formatMessage({ id: "Children" })
|
const childrenLabel = intl.formatMessage({ id: "Children" })
|
||||||
const { setValue, trigger } = useFormContext<BookingWidgetSchema>()
|
const { setValue } = useFormContext<BookingWidgetSchema>()
|
||||||
const children = useGuestsRoomsStore((state) => state.rooms[roomIndex].child)
|
|
||||||
const increaseChildren = useGuestsRoomsStore(
|
|
||||||
(state) => state.increaseChildren
|
|
||||||
)
|
|
||||||
const decreaseChildren = useGuestsRoomsStore(
|
|
||||||
(state) => state.decreaseChildren
|
|
||||||
)
|
|
||||||
|
|
||||||
function increaseChildrenCount(roomIndex: number) {
|
function increaseChildrenCount(roomIndex: number) {
|
||||||
if (children.length < 5) {
|
if (currentChildren.length < 5) {
|
||||||
increaseChildren(roomIndex)
|
|
||||||
setValue(
|
setValue(
|
||||||
`rooms.${roomIndex}.child.${children.length}`,
|
`rooms.${roomIndex}.child.${currentChildren.length}`,
|
||||||
{
|
{
|
||||||
age: -1,
|
age: -1,
|
||||||
bed: -1,
|
bed: -1,
|
||||||
@@ -41,9 +36,9 @@ export default function ChildSelector({ roomIndex = 0 }: ChildSelectorProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
function decreaseChildrenCount(roomIndex: number) {
|
function decreaseChildrenCount(roomIndex: number) {
|
||||||
if (children.length > 0) {
|
if (currentChildren.length > 0) {
|
||||||
const newChildrenList = decreaseChildren(roomIndex)
|
currentChildren.pop()
|
||||||
setValue(`rooms.${roomIndex}.child`, newChildrenList, {
|
setValue(`rooms.${roomIndex}.child`, currentChildren, {
|
||||||
shouldValidate: true,
|
shouldValidate: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -56,23 +51,25 @@ export default function ChildSelector({ roomIndex = 0 }: ChildSelectorProps) {
|
|||||||
{childrenLabel}
|
{childrenLabel}
|
||||||
</Caption>
|
</Caption>
|
||||||
<Counter
|
<Counter
|
||||||
count={children.length}
|
count={currentChildren.length}
|
||||||
handleOnDecrease={() => {
|
handleOnDecrease={() => {
|
||||||
decreaseChildrenCount(roomIndex)
|
decreaseChildrenCount(roomIndex)
|
||||||
}}
|
}}
|
||||||
handleOnIncrease={() => {
|
handleOnIncrease={() => {
|
||||||
increaseChildrenCount(roomIndex)
|
increaseChildrenCount(roomIndex)
|
||||||
}}
|
}}
|
||||||
disableDecrease={children.length == 0}
|
disableDecrease={currentChildren.length == 0}
|
||||||
disableIncrease={children.length == 5}
|
disableIncrease={currentChildren.length == 5}
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
{children.map((child, index) => (
|
{currentChildren.map((child, index) => (
|
||||||
<ChildInfoSelector
|
<ChildInfoSelector
|
||||||
roomIndex={roomIndex}
|
roomIndex={roomIndex}
|
||||||
index={index}
|
index={index}
|
||||||
child={child}
|
child={child}
|
||||||
|
adults={currentAdults}
|
||||||
key={"child_" + index}
|
key={"child_" + index}
|
||||||
|
childrenInAdultsBed={childrenInAdultsBed}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,130 +0,0 @@
|
|||||||
"use client"
|
|
||||||
import { Dialog } from "react-aria-components"
|
|
||||||
import { useFormContext } from "react-hook-form"
|
|
||||||
import { useIntl } from "react-intl"
|
|
||||||
|
|
||||||
import { useGuestsRoomsStore } from "@/stores/guests-rooms"
|
|
||||||
|
|
||||||
import { CloseLargeIcon, PlusCircleIcon, PlusIcon } from "../Icons"
|
|
||||||
import Button from "../TempDesignSystem/Button"
|
|
||||||
import Divider from "../TempDesignSystem/Divider"
|
|
||||||
import Subtitle from "../TempDesignSystem/Text/Subtitle"
|
|
||||||
import { Tooltip } from "../TempDesignSystem/Tooltip"
|
|
||||||
import AdultSelector from "./AdultSelector"
|
|
||||||
import ChildSelector from "./ChildSelector"
|
|
||||||
|
|
||||||
import styles from "./guests-rooms-picker.module.css"
|
|
||||||
|
|
||||||
import { BookingWidgetSchema } from "@/types/components/bookingWidget"
|
|
||||||
|
|
||||||
export default function GuestsRoomsPickerDialog() {
|
|
||||||
const intl = useIntl()
|
|
||||||
const doneLabel = intl.formatMessage({ id: "Done" })
|
|
||||||
const roomLabel = intl.formatMessage({ id: "Room" })
|
|
||||||
const disabledBookingOptionsHeader = intl.formatMessage({
|
|
||||||
id: "Disabled booking options header",
|
|
||||||
})
|
|
||||||
const disabledBookingOptionsText = intl.formatMessage({
|
|
||||||
id: "Disabled adding room",
|
|
||||||
})
|
|
||||||
const addRoomLabel = intl.formatMessage({ id: "Add Room" })
|
|
||||||
|
|
||||||
const { getFieldState } = useFormContext<BookingWidgetSchema>()
|
|
||||||
|
|
||||||
const rooms = useGuestsRoomsStore((state) => state.rooms)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog className={styles.pickerContainer}>
|
|
||||||
{({ close }) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<header className={styles.header}>
|
|
||||||
<button type="button" className={styles.close}>
|
|
||||||
<CloseLargeIcon />
|
|
||||||
</button>
|
|
||||||
</header>
|
|
||||||
<div className={styles.contentContainer}>
|
|
||||||
{rooms.map((room, index) => (
|
|
||||||
<div className={styles.roomContainer} key={index}>
|
|
||||||
<section className={styles.roomDetailsContainer}>
|
|
||||||
<Subtitle type="two" className={styles.roomHeading}>
|
|
||||||
{roomLabel} {index + 1}
|
|
||||||
</Subtitle>
|
|
||||||
<AdultSelector roomIndex={index} />
|
|
||||||
<ChildSelector roomIndex={index} />
|
|
||||||
</section>
|
|
||||||
<Divider color="primaryLightSubtle" />
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div className={styles.addRoomMobileContainer}>
|
|
||||||
<Tooltip
|
|
||||||
heading={disabledBookingOptionsHeader}
|
|
||||||
text={disabledBookingOptionsText}
|
|
||||||
position="top"
|
|
||||||
arrow="left"
|
|
||||||
>
|
|
||||||
{rooms.length < 4 ? (
|
|
||||||
<Button
|
|
||||||
intent="text"
|
|
||||||
variant="icon"
|
|
||||||
wrapping
|
|
||||||
disabled
|
|
||||||
theme="base"
|
|
||||||
fullWidth
|
|
||||||
>
|
|
||||||
<PlusIcon />
|
|
||||||
{addRoomLabel}
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<footer className={styles.footer}>
|
|
||||||
<div className={styles.hideOnMobile}>
|
|
||||||
<Tooltip
|
|
||||||
heading={disabledBookingOptionsHeader}
|
|
||||||
text={disabledBookingOptionsText}
|
|
||||||
position="top"
|
|
||||||
arrow="left"
|
|
||||||
>
|
|
||||||
{rooms.length < 4 ? (
|
|
||||||
<Button
|
|
||||||
intent="text"
|
|
||||||
variant="icon"
|
|
||||||
wrapping
|
|
||||||
disabled
|
|
||||||
theme="base"
|
|
||||||
>
|
|
||||||
<PlusCircleIcon />
|
|
||||||
{addRoomLabel}
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
onPress={close}
|
|
||||||
disabled={getFieldState("rooms").invalid}
|
|
||||||
className={styles.hideOnMobile}
|
|
||||||
intent="tertiary"
|
|
||||||
theme="base"
|
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
{doneLabel}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onPress={close}
|
|
||||||
disabled={getFieldState("rooms").invalid}
|
|
||||||
className={styles.hideOnDesktop}
|
|
||||||
intent="tertiary"
|
|
||||||
theme="base"
|
|
||||||
size="large"
|
|
||||||
>
|
|
||||||
{doneLabel}
|
|
||||||
</Button>
|
|
||||||
</footer>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</Dialog>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
146
components/GuestsRoomsPicker/Form.tsx
Normal file
146
components/GuestsRoomsPicker/Form.tsx
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useFormContext } from "react-hook-form"
|
||||||
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import { CloseLargeIcon, PlusCircleIcon, PlusIcon } from "../Icons"
|
||||||
|
import Button from "../TempDesignSystem/Button"
|
||||||
|
import Divider from "../TempDesignSystem/Divider"
|
||||||
|
import Subtitle from "../TempDesignSystem/Text/Subtitle"
|
||||||
|
import { Tooltip } from "../TempDesignSystem/Tooltip"
|
||||||
|
import AdultSelector from "./AdultSelector"
|
||||||
|
import ChildSelector from "./ChildSelector"
|
||||||
|
|
||||||
|
import styles from "./guests-rooms-picker.module.css"
|
||||||
|
|
||||||
|
import { BookingWidgetSchema } from "@/types/components/bookingWidget"
|
||||||
|
import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums"
|
||||||
|
import { GuestsRoom } from "@/types/components/bookingWidget/guestsRoomsPicker"
|
||||||
|
|
||||||
|
export default function GuestsRoomsPickerDialog({
|
||||||
|
rooms,
|
||||||
|
onClose,
|
||||||
|
}: {
|
||||||
|
rooms: GuestsRoom[]
|
||||||
|
onClose: () => void
|
||||||
|
}) {
|
||||||
|
const intl = useIntl()
|
||||||
|
const doneLabel = intl.formatMessage({ id: "Done" })
|
||||||
|
const roomLabel = intl.formatMessage({ id: "Room" })
|
||||||
|
const disabledBookingOptionsHeader = intl.formatMessage({
|
||||||
|
id: "Disabled booking options header",
|
||||||
|
})
|
||||||
|
const disabledBookingOptionsText = intl.formatMessage({
|
||||||
|
id: "Disabled adding room",
|
||||||
|
})
|
||||||
|
const addRoomLabel = intl.formatMessage({ id: "Add Room" })
|
||||||
|
|
||||||
|
const { getFieldState } = useFormContext<BookingWidgetSchema>()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<header className={styles.header}>
|
||||||
|
<button type="button" className={styles.close} onClick={onClose}>
|
||||||
|
<CloseLargeIcon />
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
<div className={styles.contentContainer}>
|
||||||
|
{rooms.map((room, index) => {
|
||||||
|
const currentAdults = room.adults
|
||||||
|
const currentChildren = room.child
|
||||||
|
const childrenInAdultsBed = currentChildren.filter(
|
||||||
|
(child) => child.bed === ChildBedMapEnum.IN_ADULTS_BED
|
||||||
|
).length
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.roomContainer} key={index}>
|
||||||
|
<section className={styles.roomDetailsContainer}>
|
||||||
|
<Subtitle type="two" className={styles.roomHeading}>
|
||||||
|
{roomLabel} {index + 1}
|
||||||
|
</Subtitle>
|
||||||
|
<AdultSelector
|
||||||
|
roomIndex={index}
|
||||||
|
currentAdults={currentAdults}
|
||||||
|
currentChildren={currentChildren}
|
||||||
|
childrenInAdultsBed={childrenInAdultsBed}
|
||||||
|
/>
|
||||||
|
<ChildSelector
|
||||||
|
roomIndex={index}
|
||||||
|
currentAdults={currentAdults}
|
||||||
|
currentChildren={currentChildren}
|
||||||
|
childrenInAdultsBed={childrenInAdultsBed}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
<Divider color="primaryLightSubtle" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<div className={styles.addRoomMobileContainer}>
|
||||||
|
<Tooltip
|
||||||
|
heading={disabledBookingOptionsHeader}
|
||||||
|
text={disabledBookingOptionsText}
|
||||||
|
position="top"
|
||||||
|
arrow="left"
|
||||||
|
>
|
||||||
|
{rooms.length < 4 ? (
|
||||||
|
<Button
|
||||||
|
intent="text"
|
||||||
|
variant="icon"
|
||||||
|
wrapping
|
||||||
|
disabled
|
||||||
|
theme="base"
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
<PlusIcon />
|
||||||
|
{addRoomLabel}
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer className={styles.footer}>
|
||||||
|
<div className={styles.hideOnMobile}>
|
||||||
|
<Tooltip
|
||||||
|
heading={disabledBookingOptionsHeader}
|
||||||
|
text={disabledBookingOptionsText}
|
||||||
|
position="top"
|
||||||
|
arrow="left"
|
||||||
|
>
|
||||||
|
{rooms.length < 4 ? (
|
||||||
|
<Button
|
||||||
|
intent="text"
|
||||||
|
variant="icon"
|
||||||
|
wrapping
|
||||||
|
disabled
|
||||||
|
theme="base"
|
||||||
|
>
|
||||||
|
<PlusCircleIcon />
|
||||||
|
{addRoomLabel}
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
onPress={onClose}
|
||||||
|
disabled={getFieldState("rooms").invalid}
|
||||||
|
className={styles.hideOnMobile}
|
||||||
|
intent="tertiary"
|
||||||
|
theme="base"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
{doneLabel}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onPress={onClose}
|
||||||
|
disabled={getFieldState("rooms").invalid}
|
||||||
|
className={styles.hideOnDesktop}
|
||||||
|
intent="tertiary"
|
||||||
|
theme="base"
|
||||||
|
size="large"
|
||||||
|
>
|
||||||
|
{doneLabel}
|
||||||
|
</Button>
|
||||||
|
</footer>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
"use client"
|
|
||||||
import { PropsWithChildren, useRef } from "react"
|
|
||||||
|
|
||||||
import {
|
|
||||||
GuestsRoomsContext,
|
|
||||||
type GuestsRoomsStore,
|
|
||||||
initGuestsRoomsState,
|
|
||||||
} from "@/stores/guests-rooms"
|
|
||||||
|
|
||||||
import { GuestsRoom } from "@/types/components/bookingWidget/guestsRoomsPicker"
|
|
||||||
|
|
||||||
export default function GuestsRoomsProvider({
|
|
||||||
selectedGuests,
|
|
||||||
children,
|
|
||||||
}: PropsWithChildren<{ selectedGuests?: GuestsRoom[] }>) {
|
|
||||||
const initialStore = useRef<GuestsRoomsStore>()
|
|
||||||
if (!initialStore.current) {
|
|
||||||
initialStore.current = initGuestsRoomsState(selectedGuests)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<GuestsRoomsContext.Provider value={initialStore.current}>
|
|
||||||
{children}
|
|
||||||
</GuestsRoomsContext.Provider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,23 @@
|
|||||||
.container {
|
.triggerDesktop {
|
||||||
overflow: hidden;
|
display: none;
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pickerContainerMobile {
|
||||||
|
background-color: var(--Main-Grey-White);
|
||||||
|
border-radius: var(--Corner-radius-Large) var(--Corner-radius-Large) 0 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
transition: top 300ms ease;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerContainerDesktop {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.roomContainer {
|
.roomContainer {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--Spacing-x2);
|
gap: var(--Spacing-x2);
|
||||||
@@ -23,9 +39,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
.body {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
.footer {
|
.footer {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--Spacing-x1);
|
gap: var(--Spacing-x1);
|
||||||
@@ -33,25 +47,7 @@
|
|||||||
margin-top: var(--Spacing-x2);
|
margin-top: var(--Spacing-x2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pickerContainer {
|
|
||||||
--header-height: 72px;
|
|
||||||
--sticky-button-height: 140px;
|
|
||||||
background-color: var(--Main-Grey-White);
|
|
||||||
display: grid;
|
|
||||||
|
|
||||||
border-radius: var(--Corner-radius-Large);
|
|
||||||
box-shadow: 0px 0px 14px 6px rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
max-width: calc(100vw - 20px);
|
|
||||||
padding: var(--Spacing-x2) var(--Spacing-x3);
|
|
||||||
|
|
||||||
width: 360px;
|
|
||||||
}
|
|
||||||
@media screen and (max-width: 1366px) {
|
@media screen and (max-width: 1366px) {
|
||||||
.container[data-isopen="true"] .hideWrapper {
|
|
||||||
top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contentContainer {
|
.contentContainer {
|
||||||
grid-area: content;
|
grid-area: content;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
@@ -107,6 +103,32 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 1367px) {
|
@media screen and (min-width: 1367px) {
|
||||||
|
.pickerContainerMobisse {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.triggerMobile {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.triggerDesktop {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerContainerDesktop {
|
||||||
|
--header-height: 72px;
|
||||||
|
--sticky-button-height: 140px;
|
||||||
|
background-color: var(--Main-Grey-White);
|
||||||
|
display: grid;
|
||||||
|
|
||||||
|
border-radius: var(--Corner-radius-Large);
|
||||||
|
box-shadow: 0px 0px 14px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
max-width: calc(100vw - 20px);
|
||||||
|
padding: var(--Spacing-x2) var(--Spacing-x3);
|
||||||
|
|
||||||
|
width: 360px;
|
||||||
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,64 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useCallback, useEffect, useRef, useState } from "react"
|
import {
|
||||||
import { Button, DialogTrigger, Popover } from "react-aria-components"
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogTrigger,
|
||||||
|
Modal,
|
||||||
|
Popover,
|
||||||
|
} from "react-aria-components"
|
||||||
import { useFormContext } from "react-hook-form"
|
import { useFormContext } from "react-hook-form"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { useGuestsRoomsStore } from "@/stores/guests-rooms"
|
|
||||||
|
|
||||||
import { guestRoomsSchema } from "@/components/Forms/BookingWidget/schema"
|
|
||||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||||
|
|
||||||
import Dialog from "./Dialog"
|
import PickerForm from "./Form"
|
||||||
|
|
||||||
import styles from "./guests-rooms-picker.module.css"
|
import styles from "./guests-rooms-picker.module.css"
|
||||||
|
|
||||||
export default function GuestsRoomsPickerForm({
|
import { GuestsRoom } from "@/types/components/bookingWidget/guestsRoomsPicker"
|
||||||
name = "rooms",
|
|
||||||
|
export default function GuestsRoomsPickerForm() {
|
||||||
|
const { watch } = useFormContext()
|
||||||
|
|
||||||
|
const rooms = watch("rooms") as GuestsRoom[]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DialogTrigger>
|
||||||
|
<Trigger rooms={rooms} className={styles.triggerMobile} />
|
||||||
|
<Modal className="my-modal">
|
||||||
|
<Dialog className={styles.pickerContainerMobile}>
|
||||||
|
{({ close }) => <PickerForm rooms={rooms} onClose={close} />}
|
||||||
|
</Dialog>
|
||||||
|
</Modal>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogTrigger>
|
||||||
|
<Trigger rooms={rooms} className={styles.triggerDesktop} />
|
||||||
|
<Popover placement="bottom start" offset={22}>
|
||||||
|
<Dialog className={styles.pickerContainerDesktop}>
|
||||||
|
{({ close }) => <PickerForm rooms={rooms} onClose={close} />}
|
||||||
|
</Dialog>
|
||||||
|
</Popover>
|
||||||
|
</DialogTrigger>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Trigger({
|
||||||
|
rooms,
|
||||||
|
className,
|
||||||
}: {
|
}: {
|
||||||
name: string
|
rooms: GuestsRoom[]
|
||||||
|
className: string
|
||||||
}) {
|
}) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
|
||||||
const { rooms, adultCount, childCount } = useGuestsRoomsStore((state) => ({
|
|
||||||
rooms: state.rooms,
|
|
||||||
adultCount: state.adultCount,
|
|
||||||
childCount: state.childCount,
|
|
||||||
}))
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DialogTrigger>
|
<Button className={`${className} ${styles.btn}`} type="button">
|
||||||
<Button className={styles.btn} type="button">
|
<Body>
|
||||||
<Body className={styles.body} asChild>
|
{rooms.map((room, i) => (
|
||||||
<span>
|
<span key={i}>
|
||||||
{intl.formatMessage(
|
{intl.formatMessage(
|
||||||
{ id: "booking.rooms" },
|
{ id: "booking.rooms" },
|
||||||
{ totalRooms: rooms.length }
|
{ totalRooms: rooms.length }
|
||||||
@@ -39,21 +66,18 @@ export default function GuestsRoomsPickerForm({
|
|||||||
{", "}
|
{", "}
|
||||||
{intl.formatMessage(
|
{intl.formatMessage(
|
||||||
{ id: "booking.adults" },
|
{ id: "booking.adults" },
|
||||||
{ totalAdults: adultCount }
|
{ totalAdults: room.adults }
|
||||||
)}
|
)}
|
||||||
{childCount > 0
|
{room.child.length > 0
|
||||||
? ", " +
|
? ", " +
|
||||||
intl.formatMessage(
|
intl.formatMessage(
|
||||||
{ id: "booking.children" },
|
{ id: "booking.children" },
|
||||||
{ totalChildren: childCount }
|
{ totalChildren: room.child.length }
|
||||||
)
|
)
|
||||||
: null}
|
: null}
|
||||||
</span>
|
</span>
|
||||||
</Body>
|
))}
|
||||||
</Button>
|
</Body>
|
||||||
<Popover>
|
</Button>
|
||||||
<Dialog />
|
|
||||||
</Popover>
|
|
||||||
</DialogTrigger>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,227 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { produce } from "immer"
|
|
||||||
import { createContext, useContext } from "react"
|
|
||||||
import { create, useStore } from "zustand"
|
|
||||||
|
|
||||||
import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums"
|
|
||||||
import {
|
|
||||||
Child,
|
|
||||||
GuestsRoom,
|
|
||||||
} from "@/types/components/bookingWidget/guestsRoomsPicker"
|
|
||||||
|
|
||||||
const SESSION_STORAGE_KEY = "guests_rooms"
|
|
||||||
|
|
||||||
interface extendedGuestsRoom extends GuestsRoom {
|
|
||||||
childrenInAdultsBed: number
|
|
||||||
}
|
|
||||||
interface GuestsRoomsState {
|
|
||||||
rooms: extendedGuestsRoom[]
|
|
||||||
adultCount: number
|
|
||||||
childCount: number
|
|
||||||
isValidated: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GuestsRoomsStoreState extends GuestsRoomsState {
|
|
||||||
increaseAdults: (roomIndex: number) => void
|
|
||||||
decreaseAdults: (roomIndex: number) => void
|
|
||||||
increaseChildren: (roomIndex: number) => void
|
|
||||||
decreaseChildren: (roomIndex: number) => Child[]
|
|
||||||
updateChildAge: (age: number, roomIndex: number, childIndex: number) => void
|
|
||||||
updateChildBed: (bed: number, roomIndex: number, childIndex: number) => void
|
|
||||||
increaseChildInAdultsBed: (roomIndex: number) => void
|
|
||||||
decreaseChildInAdultsBed: (roomIndex: number) => void
|
|
||||||
increaseRoom: () => void
|
|
||||||
decreaseRoom: (roomIndex: number) => void
|
|
||||||
setIsValidated: (isValidated: boolean) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateBedTypes(data: extendedGuestsRoom[]) {
|
|
||||||
data.forEach((room) => {
|
|
||||||
room.child.forEach((child) => {
|
|
||||||
const allowedBedTypes: number[] = []
|
|
||||||
if (child.age <= 5 && room.adults >= room.childrenInAdultsBed) {
|
|
||||||
allowedBedTypes.push(ChildBedMapEnum.IN_ADULTS_BED)
|
|
||||||
} else if (child.age <= 5) {
|
|
||||||
room.childrenInAdultsBed = room.childrenInAdultsBed - 1
|
|
||||||
}
|
|
||||||
if (child.age < 3) {
|
|
||||||
allowedBedTypes.push(ChildBedMapEnum.IN_CRIB)
|
|
||||||
}
|
|
||||||
if (child.age > 2) {
|
|
||||||
allowedBedTypes.push(ChildBedMapEnum.IN_EXTRA_BED)
|
|
||||||
}
|
|
||||||
if (!allowedBedTypes.includes(child.bed)) {
|
|
||||||
child.bed = allowedBedTypes[0]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function initGuestsRoomsState(initData?: GuestsRoom[]) {
|
|
||||||
const isBrowser = typeof window !== "undefined"
|
|
||||||
const sessionData = isBrowser
|
|
||||||
? sessionStorage.getItem(SESSION_STORAGE_KEY)
|
|
||||||
: null
|
|
||||||
|
|
||||||
const defaultGuestsData: extendedGuestsRoom = {
|
|
||||||
adults: 1,
|
|
||||||
child: [],
|
|
||||||
childrenInAdultsBed: 0,
|
|
||||||
}
|
|
||||||
const defaultData: GuestsRoomsState = {
|
|
||||||
rooms: [defaultGuestsData],
|
|
||||||
adultCount: 1,
|
|
||||||
childCount: 0,
|
|
||||||
isValidated: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
let inputData: GuestsRoomsState = defaultData
|
|
||||||
if (sessionData) {
|
|
||||||
inputData = JSON.parse(sessionData)
|
|
||||||
}
|
|
||||||
if (initData) {
|
|
||||||
inputData.rooms = initData.map((room) => {
|
|
||||||
const childrenInAdultsBed = room.child
|
|
||||||
? room.child.reduce((acc, child) => {
|
|
||||||
acc = acc + (child.bed == ChildBedMapEnum.IN_ADULTS_BED ? 1 : 0)
|
|
||||||
return acc
|
|
||||||
}, 0)
|
|
||||||
: 0
|
|
||||||
return { ...defaultGuestsData, ...room, childrenInAdultsBed }
|
|
||||||
}) as extendedGuestsRoom[]
|
|
||||||
|
|
||||||
inputData.adultCount = initData.reduce((acc, room) => {
|
|
||||||
acc = acc + room.adults
|
|
||||||
return acc
|
|
||||||
}, 0)
|
|
||||||
inputData.childCount = initData.reduce((acc, room) => {
|
|
||||||
acc = acc + room.child?.length
|
|
||||||
return acc
|
|
||||||
}, 0)
|
|
||||||
validateBedTypes(inputData.rooms)
|
|
||||||
}
|
|
||||||
|
|
||||||
return create<GuestsRoomsStoreState>()((set, get) => ({
|
|
||||||
...inputData,
|
|
||||||
increaseAdults: (roomIndex) =>
|
|
||||||
set(
|
|
||||||
produce((state: GuestsRoomsState) => {
|
|
||||||
state.rooms[roomIndex].adults = state.rooms[roomIndex].adults + 1
|
|
||||||
state.adultCount = state.adultCount + 1
|
|
||||||
})
|
|
||||||
),
|
|
||||||
decreaseAdults: (roomIndex) =>
|
|
||||||
set(
|
|
||||||
produce((state: GuestsRoomsState) => {
|
|
||||||
state.rooms[roomIndex].adults = state.rooms[roomIndex].adults - 1
|
|
||||||
state.adultCount = state.adultCount - 1
|
|
||||||
if (
|
|
||||||
state.rooms[roomIndex].childrenInAdultsBed >
|
|
||||||
state.rooms[roomIndex].adults
|
|
||||||
) {
|
|
||||||
const toUpdateIndex = state.rooms[roomIndex].child.findIndex(
|
|
||||||
(child) => child.bed == ChildBedMapEnum.IN_ADULTS_BED
|
|
||||||
)
|
|
||||||
if (toUpdateIndex != -1) {
|
|
||||||
state.rooms[roomIndex].child[toUpdateIndex].bed =
|
|
||||||
state.rooms[roomIndex].child[toUpdateIndex].age < 3
|
|
||||||
? ChildBedMapEnum.IN_CRIB
|
|
||||||
: ChildBedMapEnum.IN_EXTRA_BED
|
|
||||||
state.rooms[roomIndex].childrenInAdultsBed =
|
|
||||||
state.rooms[roomIndex].adults
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
),
|
|
||||||
increaseChildren: (roomIndex) =>
|
|
||||||
set(
|
|
||||||
produce((state: GuestsRoomsState) => {
|
|
||||||
state.rooms[roomIndex].child.push({
|
|
||||||
age: -1,
|
|
||||||
bed: -1,
|
|
||||||
})
|
|
||||||
state.childCount = state.childCount + 1
|
|
||||||
})
|
|
||||||
),
|
|
||||||
decreaseChildren: (roomIndex) => {
|
|
||||||
set(
|
|
||||||
produce((state: GuestsRoomsState) => {
|
|
||||||
const roomChildren = state.rooms[roomIndex].child
|
|
||||||
if (
|
|
||||||
roomChildren.length &&
|
|
||||||
roomChildren[roomChildren.length - 1].bed ==
|
|
||||||
ChildBedMapEnum.IN_ADULTS_BED
|
|
||||||
) {
|
|
||||||
state.rooms[roomIndex].childrenInAdultsBed =
|
|
||||||
state.rooms[roomIndex].childrenInAdultsBed - 1
|
|
||||||
}
|
|
||||||
state.rooms[roomIndex].child.pop()
|
|
||||||
state.childCount = state.childCount - 1
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return get().rooms[roomIndex].child
|
|
||||||
},
|
|
||||||
updateChildAge: (age, roomIndex, childIndex) =>
|
|
||||||
set(
|
|
||||||
produce((state: GuestsRoomsState) => {
|
|
||||||
state.rooms[roomIndex].child[childIndex].age = age
|
|
||||||
})
|
|
||||||
),
|
|
||||||
updateChildBed: (bed, roomIndex, childIndex) =>
|
|
||||||
set(
|
|
||||||
produce((state: GuestsRoomsState) => {
|
|
||||||
state.rooms[roomIndex].child[childIndex].bed = bed
|
|
||||||
})
|
|
||||||
),
|
|
||||||
increaseChildInAdultsBed: (roomIndex) =>
|
|
||||||
set(
|
|
||||||
produce((state: GuestsRoomsState) => {
|
|
||||||
state.rooms[roomIndex].childrenInAdultsBed =
|
|
||||||
state.rooms[roomIndex].childrenInAdultsBed + 1
|
|
||||||
})
|
|
||||||
),
|
|
||||||
decreaseChildInAdultsBed: (roomIndex) =>
|
|
||||||
set(
|
|
||||||
produce((state: GuestsRoomsState) => {
|
|
||||||
state.rooms[roomIndex].childrenInAdultsBed =
|
|
||||||
state.rooms[roomIndex].childrenInAdultsBed - 1
|
|
||||||
})
|
|
||||||
),
|
|
||||||
increaseRoom: () =>
|
|
||||||
set(
|
|
||||||
produce((state: GuestsRoomsState) => {
|
|
||||||
state.rooms.push({
|
|
||||||
adults: 1,
|
|
||||||
child: [],
|
|
||||||
childrenInAdultsBed: 0,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
),
|
|
||||||
decreaseRoom: (roomIndex) =>
|
|
||||||
set(
|
|
||||||
produce((state: GuestsRoomsState) => {
|
|
||||||
state.rooms.splice(roomIndex, 1)
|
|
||||||
})
|
|
||||||
),
|
|
||||||
setIsValidated: (isValidated) => set(() => ({ isValidated })),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GuestsRoomsStore = ReturnType<typeof initGuestsRoomsState>
|
|
||||||
|
|
||||||
export const GuestsRoomsContext = createContext<GuestsRoomsStore | null>(null)
|
|
||||||
|
|
||||||
export const useGuestsRoomsStore = <T>(
|
|
||||||
selector: (store: GuestsRoomsStoreState) => T
|
|
||||||
): T => {
|
|
||||||
const guestsRoomsContextStore = useContext(GuestsRoomsContext)
|
|
||||||
|
|
||||||
if (!guestsRoomsContextStore) {
|
|
||||||
throw new Error(
|
|
||||||
`guestsRoomsContextStore must be used within GuestsRoomsContextProvider`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return useStore(guestsRoomsContextStore, selector)
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,6 @@ import { create } from "zustand"
|
|||||||
export enum StickyElementNameEnum {
|
export enum StickyElementNameEnum {
|
||||||
SITEWIDE_ALERT = "SITEWIDE_ALERT",
|
SITEWIDE_ALERT = "SITEWIDE_ALERT",
|
||||||
BOOKING_WIDGET = "BOOKING_WIDGET",
|
BOOKING_WIDGET = "BOOKING_WIDGET",
|
||||||
BOOKING_WIDGET_MOBILE = "BOOKING_WIDGET_MOBILE",
|
|
||||||
HOTEL_TAB_NAVIGATION = "HOTEL_TAB_NAVIGATION",
|
HOTEL_TAB_NAVIGATION = "HOTEL_TAB_NAVIGATION",
|
||||||
HOTEL_STATIC_MAP = "HOTEL_STATIC_MAP",
|
HOTEL_STATIC_MAP = "HOTEL_STATIC_MAP",
|
||||||
}
|
}
|
||||||
@@ -32,7 +31,6 @@ interface StickyStore {
|
|||||||
const priorityMap: Record<StickyElementNameEnum, number> = {
|
const priorityMap: Record<StickyElementNameEnum, number> = {
|
||||||
[StickyElementNameEnum.SITEWIDE_ALERT]: 1,
|
[StickyElementNameEnum.SITEWIDE_ALERT]: 1,
|
||||||
[StickyElementNameEnum.BOOKING_WIDGET]: 2,
|
[StickyElementNameEnum.BOOKING_WIDGET]: 2,
|
||||||
[StickyElementNameEnum.BOOKING_WIDGET_MOBILE]: 2,
|
|
||||||
|
|
||||||
[StickyElementNameEnum.HOTEL_TAB_NAVIGATION]: 3,
|
[StickyElementNameEnum.HOTEL_TAB_NAVIGATION]: 3,
|
||||||
[StickyElementNameEnum.HOTEL_STATIC_MAP]: 3,
|
[StickyElementNameEnum.HOTEL_STATIC_MAP]: 3,
|
||||||
|
|||||||
@@ -17,18 +17,19 @@ export type GuestsRoomPickerProps = {
|
|||||||
index: number
|
index: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AdultSelectorProps = {
|
export type SelectorProps = {
|
||||||
roomIndex: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ChildSelectorProps = {
|
|
||||||
roomIndex: number
|
roomIndex: number
|
||||||
|
currentAdults: number
|
||||||
|
currentChildren: Child[]
|
||||||
|
childrenInAdultsBed: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ChildInfoSelectorProps = {
|
export type ChildInfoSelectorProps = {
|
||||||
child: Child
|
child: Child
|
||||||
|
adults: number
|
||||||
index: number
|
index: number
|
||||||
roomIndex: number
|
roomIndex: number
|
||||||
|
childrenInAdultsBed: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CounterProps {
|
export interface CounterProps {
|
||||||
|
|||||||
Reference in New Issue
Block a user