From 770c82e57a85f4e20bb4c0c96722f21d3b97250e Mon Sep 17 00:00:00 2001 From: Hrishikesh Vaipurkar Date: Mon, 7 Oct 2024 00:40:50 +0200 Subject: [PATCH] feat: SW-276 Implemented Store usage --- components/Forms/BookingWidget/schema.ts | 22 +-- .../AdultSelector/adult-selector.module.css | 0 .../GuestsRoomsPicker/AdultSelector/index.tsx | 75 +++++++++ .../ChildSelector/ChildInfoSelector.tsx | 131 ++++++++++++++++ .../ChildSelector/child-selector.module.css | 0 .../GuestsRoomsPicker/ChildSelector/index.tsx | 68 +++++++++ .../GuestsRoomPicker/AdultSelector/index.tsx | 40 ----- .../ChildSelector/ChildInfoSelector.tsx | 85 ----------- .../GuestsRoomPicker/ChildSelector/index.tsx | 114 -------------- .../guests-room-picker.module.css | 5 - .../GuestsRoomPicker/index.tsx | 51 ------- .../GuestsRoomsPicker/GuestsRoomsPicker.tsx | 84 +++------- .../guests-rooms-picker.module.css | 5 + components/GuestsRoomsPicker/index.tsx | 55 ++----- stores/guests-rooms.ts | 143 ++++++++++++++++++ types/components/bookingWidget/enums.ts | 5 + .../bookingWidget/guestsRoomsPicker.ts | 28 ++-- 17 files changed, 487 insertions(+), 424 deletions(-) rename components/GuestsRoomsPicker/{GuestsRoomPicker => }/AdultSelector/adult-selector.module.css (100%) create mode 100644 components/GuestsRoomsPicker/AdultSelector/index.tsx create mode 100644 components/GuestsRoomsPicker/ChildSelector/ChildInfoSelector.tsx rename components/GuestsRoomsPicker/{GuestsRoomPicker => }/ChildSelector/child-selector.module.css (100%) create mode 100644 components/GuestsRoomsPicker/ChildSelector/index.tsx delete mode 100644 components/GuestsRoomsPicker/GuestsRoomPicker/AdultSelector/index.tsx delete mode 100644 components/GuestsRoomsPicker/GuestsRoomPicker/ChildSelector/ChildInfoSelector.tsx delete mode 100644 components/GuestsRoomsPicker/GuestsRoomPicker/ChildSelector/index.tsx delete mode 100644 components/GuestsRoomsPicker/GuestsRoomPicker/guests-room-picker.module.css delete mode 100644 components/GuestsRoomsPicker/GuestsRoomPicker/index.tsx create mode 100644 stores/guests-rooms.ts create mode 100644 types/components/bookingWidget/enums.ts diff --git a/components/Forms/BookingWidget/schema.ts b/components/Forms/BookingWidget/schema.ts index 9541abd42..cfa3cb03f 100644 --- a/components/Forms/BookingWidget/schema.ts +++ b/components/Forms/BookingWidget/schema.ts @@ -2,17 +2,17 @@ import { z } from "zod" import type { Location } from "@/types/trpc/routers/hotel/locations" -export const guestRoomsSchema = z.array( - z.object({ - adults: z.number().default(1), - children: z.array( - z.object({ - age: z.number().nonnegative(), - bed: z.number(), - }) - ), - }) -) +export const guestRoomSchema = z.object({ + adults: z.number().default(1), + children: z.array( + z.object({ + age: z.number().nonnegative(), + bed: z.number(), + }) + ), +}) + +export const guestRoomsSchema = z.array(guestRoomSchema) export const bookingWidgetSchema = z.object({ bookingCode: z.string(), // Update this as required when working with booking codes component diff --git a/components/GuestsRoomsPicker/GuestsRoomPicker/AdultSelector/adult-selector.module.css b/components/GuestsRoomsPicker/AdultSelector/adult-selector.module.css similarity index 100% rename from components/GuestsRoomsPicker/GuestsRoomPicker/AdultSelector/adult-selector.module.css rename to components/GuestsRoomsPicker/AdultSelector/adult-selector.module.css diff --git a/components/GuestsRoomsPicker/AdultSelector/index.tsx b/components/GuestsRoomsPicker/AdultSelector/index.tsx new file mode 100644 index 000000000..b591e7eb0 --- /dev/null +++ b/components/GuestsRoomsPicker/AdultSelector/index.tsx @@ -0,0 +1,75 @@ +"use client" + +import { useFormContext } from "react-hook-form" +import { useIntl } from "react-intl" + +import { guestsRoomsStore } from "@/stores/guests-rooms" + +import Button from "@/components/TempDesignSystem/Button" +import Caption from "@/components/TempDesignSystem/Text/Caption" + +import styles from "./adult-selector.module.css" + +import { BedTypeEnum } from "@/types/components/bookingWidget/enums" +import { AdultSelectorProps } from "@/types/components/bookingWidget/guestsRoomsPicker" + +export default function AdultSelector({ roomIndex = 0 }: AdultSelectorProps) { + const intl = useIntl() + const adultsLabel = intl.formatMessage({ id: "Adults" }) + const { setValue } = useFormContext() + const { adults, children } = guestsRoomsStore().rooms[roomIndex] + const { increaseAdults, decreaseAdults } = guestsRoomsStore() + + function increaseAdultsCount(roomIndex: number) { + if (adults < 6) { + increaseAdults(roomIndex) + setValue(`rooms.${roomIndex}.adults`, adults + 1) + } + } + + function decreaseAdultsCount(roomIndex: number) { + if (adults > 1) { + decreaseAdults(roomIndex) + setValue(`rooms.${roomIndex}.adults`, adults - 1) + let inAdultsBed = 0 + let toUpdateIndex = -1 + children.forEach((child, index) => { + if (child.bed == BedTypeEnum["In adults bed"]) { + inAdultsBed = inAdultsBed + 1 + if (inAdultsBed > adults - 1) toUpdateIndex = index + } + }) + if (toUpdateIndex != -1) { + setValue( + `rooms.${roomIndex}.children.${toUpdateIndex}.bed`, + children[toUpdateIndex].age < 3 ? 1 : 2 + ) + } + } + } + + return ( +
+ {adultsLabel} + + {adults} + +
+ ) +} diff --git a/components/GuestsRoomsPicker/ChildSelector/ChildInfoSelector.tsx b/components/GuestsRoomsPicker/ChildSelector/ChildInfoSelector.tsx new file mode 100644 index 000000000..85f37f9ce --- /dev/null +++ b/components/GuestsRoomsPicker/ChildSelector/ChildInfoSelector.tsx @@ -0,0 +1,131 @@ +import { useFormContext } from "react-hook-form" +import { useIntl } from "react-intl" + +import { guestsRoomsStore } from "@/stores/guests-rooms" + +import Select from "@/components/TempDesignSystem/Select" + +import { BedTypeEnum } from "@/types/components/bookingWidget/enums" +import { + ChildBed, + ChildInfoSelectorProps, +} from "@/types/components/bookingWidget/guestsRoomsPicker" + +export default function ChildInfoSelector({ + child = { age: -1, bed: -1 }, + index = 0, + roomIndex = 0, +}: ChildInfoSelectorProps) { + const intl = useIntl() + const ageLabel = intl.formatMessage({ id: "Age" }) + const ageReqdErrMsg = intl.formatMessage({ id: "Child age is required" }) + const bedLabel = intl.formatMessage({ id: "Bed" }) + const { setValue } = useFormContext() + const { adults, childrenInAdultsBed } = guestsRoomsStore().rooms[roomIndex] + const { + isValidated, + updateChildAge, + updateChildBed, + increaseChildInAdultsBed, + decreaseChildInAdultsBed, + } = guestsRoomsStore() + + const ageList = [ + { label: "0", value: 0 }, + { label: "1", value: 1 }, + { label: "2", value: 2 }, + { label: "3", value: 3 }, + { label: "4", value: 4 }, + { label: "5", value: 5 }, + { label: "6", value: 6 }, + { label: "7", value: 7 }, + { label: "8", value: 8 }, + { label: "9", value: 9 }, + { label: "10", value: 10 }, + { label: "11", value: 11 }, + { label: "12", value: 12 }, + ] + + function updateSelectedAge(age: number) { + updateChildAge(age, roomIndex, index) + setValue(`rooms.${roomIndex}.children.${index}.age`, age, { + shouldTouch: true, + }) + const availableBedTypes = getAvailableBeds(age) + updateSelectedBed(availableBedTypes[0].value) + } + + function updateSelectedBed(bed: number) { + if (bed == BedTypeEnum["In adults bed"] && childrenInAdultsBed < adults) { + increaseChildInAdultsBed(roomIndex) + } else if (child.bed == BedTypeEnum["In adults bed"]) { + decreaseChildInAdultsBed(roomIndex) + } + updateChildBed(bed, roomIndex, index) + setValue(`rooms.${roomIndex}.children.${index}.bed`, bed) + } + + const allBedTypes: ChildBed[] = [ + { + label: intl.formatMessage({ id: "In adults bed" }), + value: BedTypeEnum["In adults bed"], + }, + { + label: intl.formatMessage({ id: "In crib" }), + value: BedTypeEnum["In crib"], + }, + { + label: intl.formatMessage({ id: "In extra bed" }), + value: BedTypeEnum["In extra bed"], + }, + ] + + function getAvailableBeds(age: number) { + let availableBedTypes: ChildBed[] = [] + if (age <= 5 && (adults > childrenInAdultsBed || child.bed === 0)) { + availableBedTypes.push(allBedTypes[0]) + } + if (age < 3) { + availableBedTypes.push(allBedTypes[1]) + } + if (age > 2) { + availableBedTypes.push(allBedTypes[2]) + } + return availableBedTypes + } + + return ( + <> +
+ { + updateSelectedBed(parseInt(key.toString())) + }} + name={`rooms.${roomIndex}.children.${index}.age`} + placeholder={bedLabel} + /> + ) : null} +
+ + ) +} diff --git a/components/GuestsRoomsPicker/GuestsRoomPicker/ChildSelector/child-selector.module.css b/components/GuestsRoomsPicker/ChildSelector/child-selector.module.css similarity index 100% rename from components/GuestsRoomsPicker/GuestsRoomPicker/ChildSelector/child-selector.module.css rename to components/GuestsRoomsPicker/ChildSelector/child-selector.module.css diff --git a/components/GuestsRoomsPicker/ChildSelector/index.tsx b/components/GuestsRoomsPicker/ChildSelector/index.tsx new file mode 100644 index 000000000..e9d8ff625 --- /dev/null +++ b/components/GuestsRoomsPicker/ChildSelector/index.tsx @@ -0,0 +1,68 @@ +import { useFormContext } from "react-hook-form" +import { useIntl } from "react-intl" + +import { guestsRoomsStore } from "@/stores/guests-rooms" + +import Button from "@/components/TempDesignSystem/Button" +import Caption from "@/components/TempDesignSystem/Text/Caption" + +import ChildInfoSelector from "./ChildInfoSelector" + +import styles from "./child-selector.module.css" + +import { ChildSelectorProps } from "@/types/components/bookingWidget/guestsRoomsPicker" + +export default function ChildSelector({ roomIndex = 0 }: ChildSelectorProps) { + const intl = useIntl() + const childrenLabel = intl.formatMessage({ id: "Children" }) + const { setValue } = useFormContext() + const children = guestsRoomsStore().rooms[roomIndex].children + const { increaseChildren, decreaseChildren, childCount } = guestsRoomsStore() + + function updateChildrenCount(direction: string, roomIndex: number) { + if (direction == "up" && children.length < 5) { + increaseChildren(roomIndex) + setValue(`rooms.${roomIndex}.children.${children.length}`, { + age: -1, + bed: -1, + }) + } else if (children.length > 0) { + decreaseChildren(roomIndex) + let newChildrenList = JSON.parse(JSON.stringify(children)) + newChildrenList.pop() + setValue(`rooms.${roomIndex}.children`, newChildrenList) + } + } + + return ( + <> +
+ {childrenLabel} + + {children.length} + +
+ {children.map((child, index) => ( +
+ +
+ ))} + + ) +} diff --git a/components/GuestsRoomsPicker/GuestsRoomPicker/AdultSelector/index.tsx b/components/GuestsRoomsPicker/GuestsRoomPicker/AdultSelector/index.tsx deleted file mode 100644 index f4bf255b5..000000000 --- a/components/GuestsRoomsPicker/GuestsRoomPicker/AdultSelector/index.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { useIntl } from "react-intl" - -import Button from "@/components/TempDesignSystem/Button" -import Caption from "@/components/TempDesignSystem/Text/Caption" - -import styles from "./adult-selector.module.css" - -import { AdultSelectorProps } from "@/types/components/bookingWidget/guestsRoomsPicker" - -export default function AdultSelector({ - adults = 1, - updateAdults = (count: number) => {}, -}: AdultSelectorProps) { - const intl = useIntl() - const adultsLabel = intl.formatMessage({ id: "Adults" }) - - function decreaseAdults() { - if (adults > 1) { - updateAdults(adults - 1) - } - } - function increaseAdults() { - if (adults < 6) { - updateAdults(adults + 1) - } - } - - return ( -
- {adultsLabel} - - {adults} - -
- ) -} diff --git a/components/GuestsRoomsPicker/GuestsRoomPicker/ChildSelector/ChildInfoSelector.tsx b/components/GuestsRoomsPicker/GuestsRoomPicker/ChildSelector/ChildInfoSelector.tsx deleted file mode 100644 index 698d74756..000000000 --- a/components/GuestsRoomsPicker/GuestsRoomPicker/ChildSelector/ChildInfoSelector.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { useIntl } from "react-intl" - -import Select from "@/components/TempDesignSystem/Select" - -import { - Child, - ChildInfoSelectorProps, -} from "@/types/components/bookingWidget/guestsRoomsPicker" - -export default function ChildInfoSelector({ - child = { age: -1, bed: -1 }, - index = 0, - availableBedTypes = [ - { label: "In adults bed", value: 0 }, - { label: "In crib", value: 1 }, - { label: "In extra bed", value: 2 }, - ], - updateChild = (child: Child, index: number) => {}, - isValid, -}: ChildInfoSelectorProps) { - const intl = useIntl() - const ageLabel = intl.formatMessage({ id: "Age" }) - const ageReqdErrMsg = intl.formatMessage({ id: "Child age is required" }) - const bedLabel = intl.formatMessage({ id: "Bed" }) - - const ageList = [ - { label: "0", value: 0 }, - { label: "1", value: 1 }, - { label: "2", value: 2 }, - { label: "3", value: 3 }, - { label: "4", value: 4 }, - { label: "5", value: 5 }, - { label: "6", value: 6 }, - { label: "7", value: 7 }, - { label: "8", value: 8 }, - { label: "9", value: 9 }, - { label: "10", value: 10 }, - { label: "11", value: 11 }, - { label: "12", value: 12 }, - ] - - function handleOnSelect(selectedKey: number, childInfo: string) { - if (childInfo == "age") { - child.age = selectedKey - } else if (childInfo == "bed") { - child.bed = selectedKey - } - updateChild(child, index) - } - - return ( - <> -
- { - handleOnSelect(parseInt(key.toString()), "bed") - }} - name="bed" - placeholder={bedLabel} - /> - ) : null} -
- - ) -} diff --git a/components/GuestsRoomsPicker/GuestsRoomPicker/ChildSelector/index.tsx b/components/GuestsRoomsPicker/GuestsRoomPicker/ChildSelector/index.tsx deleted file mode 100644 index 58e79c876..000000000 --- a/components/GuestsRoomsPicker/GuestsRoomPicker/ChildSelector/index.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import { useIntl } from "react-intl" - -import Button from "@/components/TempDesignSystem/Button" -import Caption from "@/components/TempDesignSystem/Text/Caption" - -import ChildInfoSelector from "./ChildInfoSelector" - -import styles from "./child-selector.module.css" - -import { - Child, - ChildBed, - ChildSelectorProps, -} from "@/types/components/bookingWidget/guestsRoomsPicker" - -export default function ChildSelector({ - roomChildren = [], - adultCount = 1, - updateChildren = (children: Child[]) => {}, - isValid, -}: ChildSelectorProps) { - const intl = useIntl() - const childrenLabel = intl.formatMessage({ id: "Children" }) - - function decreaseChildren() { - if (roomChildren.length > 0) { - roomChildren.pop() - updateChildren(roomChildren) - } - } - - function increaseChildren() { - if (roomChildren.length < 5) { - roomChildren.push({ age: -1, bed: -1 }) - updateChildren(roomChildren) - } - } - - function updateChildInfo(child: Child, index: number) { - roomChildren[index] = child - updateChildren(roomChildren) - } - - const childInAdultsBedIndices: number[] = [] - let availableInAdultsBed = adultCount - const availableBedTypes: ChildBed[] = [ - { label: intl.formatMessage({ id: "In adults bed" }), value: 0 }, - { label: intl.formatMessage({ id: "In crib" }), value: 1 }, - { label: intl.formatMessage({ id: "In extra bed" }), value: 2 }, - ] - - const childBedTypes: ChildBed[][] = [] - for (let i = 0; i < roomChildren.length; i++) { - if (roomChildren[i].bed == 0 && availableInAdultsBed > 0) { - childInAdultsBedIndices.push(i) - availableInAdultsBed = availableInAdultsBed - 1 - } - } - roomChildren.forEach((child, index) => { - let types: typeof availableBedTypes = [] - let selectedBed: boolean = false - if ( - child.age <= 5 && - (availableInAdultsBed > 0 || childInAdultsBedIndices.indexOf(index) != -1) - ) { - types.push(availableBedTypes[0]) - if (child.bed == 0) { - selectedBed = true - } - } - if (child.age < 3) { - types.push(availableBedTypes[1]) - if (child.bed == 1) { - selectedBed = true - } - } - if (child.age > 2) { - types.push(availableBedTypes[2]) - if (child.bed == 2) { - selectedBed = true - } - } - childBedTypes[index] = types - if (!selectedBed) { - child.bed = types[0].value - } - }) - - return ( - <> -
- {childrenLabel} - - {roomChildren.length} - -
- {roomChildren.map((child, index) => ( -
- -
- ))} - - ) -} diff --git a/components/GuestsRoomsPicker/GuestsRoomPicker/guests-room-picker.module.css b/components/GuestsRoomsPicker/GuestsRoomPicker/guests-room-picker.module.css deleted file mode 100644 index 59ab72488..000000000 --- a/components/GuestsRoomsPicker/GuestsRoomPicker/guests-room-picker.module.css +++ /dev/null @@ -1,5 +0,0 @@ -.container { - display: grid; - gap: var(--Spacing-x2); - padding-bottom: var(--Spacing-x1); -} diff --git a/components/GuestsRoomsPicker/GuestsRoomPicker/index.tsx b/components/GuestsRoomsPicker/GuestsRoomPicker/index.tsx deleted file mode 100644 index 4cf4ba766..000000000 --- a/components/GuestsRoomsPicker/GuestsRoomPicker/index.tsx +++ /dev/null @@ -1,51 +0,0 @@ -"use client" - -import { useIntl } from "react-intl" - -import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" - -import AdultSelector from "./AdultSelector" -import ChildSelector from "./ChildSelector" - -import styles from "./guests-room-picker.module.css" - -import { - Child, - GuestsRoom, - GuestsRoomPickerProps, -} from "@/types/components/bookingWidget/guestsRoomsPicker" - -export default function GuestsRoomPicker({ - handleOnSelect = (selected: GuestsRoom, index: number) => {}, - room = { adults: 1, children: [] }, - index = 1, - isValid, -}: GuestsRoomPickerProps) { - const intl = useIntl() - const roomLabel = intl.formatMessage({ id: "Room" }) - - function updateAdults(count: number) { - room.adults = count - handleOnSelect(room, index) - } - - function updateChildren(children: Child[]) { - room.children = children - handleOnSelect(room, index) - } - - return ( -
- - {roomLabel} {index + 1} - - - -
- ) -} diff --git a/components/GuestsRoomsPicker/GuestsRoomsPicker.tsx b/components/GuestsRoomsPicker/GuestsRoomsPicker.tsx index 7e8f4cabf..6eb6022b4 100644 --- a/components/GuestsRoomsPicker/GuestsRoomsPicker.tsx +++ b/components/GuestsRoomsPicker/GuestsRoomsPicker.tsx @@ -1,83 +1,47 @@ "use client" -import { useState } from "react" import { useIntl } from "react-intl" -import useLang from "@/hooks/useLang" +import { guestsRoomsStore } from "@/stores/guests-rooms" +import { guestRoomsSchema } from "../Forms/BookingWidget/schema" import Button from "../TempDesignSystem/Button" import Divider from "../TempDesignSystem/Divider" -import GuestsRoomPicker from "./GuestsRoomPicker" +import Subtitle from "../TempDesignSystem/Text/Subtitle" +import AdultSelector from "./AdultSelector" +import ChildSelector from "./ChildSelector" import styles from "./guests-rooms-picker.module.css" -import { - GuestsRoom, - GuestsRoomsPickerProps, -} from "@/types/components/bookingWidget/guestsRoomsPicker" +import { GuestsRoomsPickerProps } from "@/types/components/bookingWidget/guestsRoomsPicker" export default function GuestsRoomsPicker({ - handleOnSelect, - initialSelected = [ - { - adults: 1, - children: [], - }, - ], closePicker, - isValid, }: GuestsRoomsPickerProps) { - const lang = useLang() const intl = useIntl() const doneLabel = intl.formatMessage({ id: "Done" }) - const [selectedGuests, setSelectedGuests] = - useState(initialSelected) - function handleSelectRoomGuests( - selectedGuestsRoom: GuestsRoom, - index: number - ) { - let updatedSelectedGuests = JSON.parse(JSON.stringify(selectedGuests)) - updatedSelectedGuests[index] = selectedGuestsRoom - handleOnSelect(updatedSelectedGuests) - setSelectedGuests(updatedSelectedGuests) - } + const guestsData = guestsRoomsStore().rooms + const guestRoomsValidData = guestRoomsSchema.safeParse(guestsData) + const isInValid = !guestRoomsValidData.success + const roomLabel = intl.formatMessage({ id: "Room" }) - // Not in MVP scope - function addRoom() { - if (selectedGuests.length < 4) { - let updatedSelectedGuests = JSON.parse(JSON.stringify(selectedGuests)) - updatedSelectedGuests.push({ - adults: 1, - children: [], - }) - setSelectedGuests(updatedSelectedGuests) - handleOnSelect(updatedSelectedGuests) - } - } - - // Not in MVP scope - function removeRoom(index: number) { - if (selectedGuests.length > 1) { - let updatedSelectedGuests = JSON.parse(JSON.stringify(selectedGuests)) - updatedSelectedGuests.splice(index, 1) - setSelectedGuests(updatedSelectedGuests) - handleOnSelect(updatedSelectedGuests) - } - } + // Not in MVP + // const { increaseRoom, decreaseRoom } = guestsRoomsStore() return ( <> - {selectedGuests.map((room, index) => ( + {guestsData.map((room, index) => (
- +
+ + {roomLabel} {index + 1} + + + +
{/* Not in MVP {index > 0 ? ( - ) : null} */} @@ -86,12 +50,12 @@ export default function GuestsRoomsPicker({ ))}
{/* Not in MVP - {selectedGuests.length < 4 ? ( - ) : null} */} -
diff --git a/components/GuestsRoomsPicker/guests-rooms-picker.module.css b/components/GuestsRoomsPicker/guests-rooms-picker.module.css index a00f96ab1..7beb166d9 100644 --- a/components/GuestsRoomsPicker/guests-rooms-picker.module.css +++ b/components/GuestsRoomsPicker/guests-rooms-picker.module.css @@ -10,6 +10,11 @@ display: grid; gap: var(--Spacing-x1); } +.roomDetailsContainer { + display: grid; + gap: var(--Spacing-x2); + padding-bottom: var(--Spacing-x1); +} .hideWrapper { background-color: var(--Main-Grey-White); border-radius: var(--Corner-radius-Medium); diff --git a/components/GuestsRoomsPicker/index.tsx b/components/GuestsRoomsPicker/index.tsx index dae0b4f7d..8258939e1 100644 --- a/components/GuestsRoomsPicker/index.tsx +++ b/components/GuestsRoomsPicker/index.tsx @@ -1,9 +1,10 @@ "use client" import { useCallback, useEffect, useRef, useState } from "react" -import { useFormContext, useWatch } from "react-hook-form" import { useIntl } from "react-intl" +import { guestsRoomsStore } from "@/stores/guests-rooms" + import { guestRoomsSchema } from "@/components/Forms/BookingWidget/schema" import Body from "@/components/TempDesignSystem/Text/Body" @@ -11,35 +12,24 @@ import GuestsRoomsPicker from "./GuestsRoomsPicker" import styles from "./guests-rooms-picker.module.css" -import { - GuestsRoom, - GuestsRoomsFormProps, -} from "@/types/components/bookingWidget/guestsRoomsPicker" - -export default function GuestsRoomsPickerForm({ - name = "rooms", -}: GuestsRoomsFormProps) { +export default function GuestsRoomsPickerForm() { const intl = useIntl() const [isOpen, setIsOpen] = useState(false) - const [isValid, setIsValid] = useState(true) - const selectedGuests = useWatch({ name }) - const { register, setValue } = useFormContext() + const guestsData = guestsRoomsStore().rooms + const { adultCount, childCount, setIsValidated } = guestsRoomsStore() const ref = useRef(null) function handleOnClick() { setIsOpen((prevIsOpen) => !prevIsOpen) } - function handleSelectGuest(selected: GuestsRoom[]) { - setValue(name, selected) - setIsValid(true) - } const closePicker = useCallback(() => { - const guestRoomsValidData = guestRoomsSchema.safeParse(selectedGuests) + const guestRoomsValidData = guestRoomsSchema.safeParse(guestsData) if (guestRoomsValidData.success) { setIsOpen(false) + setIsValidated(false) } else { - setIsValid(false) + setIsValidated(true) } - }, [selectedGuests]) + }, [guestsData, setIsValidated, setIsOpen]) useEffect(() => { function handleClickOutside(evt: Event) { @@ -54,16 +44,10 @@ export default function GuestsRoomsPickerForm({ } }, [closePicker]) - let selectedAdultsCount = 0 - let selectedChildrenCount = 0 - selectedGuests.forEach((room: GuestsRoom) => { - selectedAdultsCount = selectedAdultsCount + room.adults - selectedChildrenCount = - selectedChildrenCount + (room.children ? room.children.length : 0) - }) - const selectedRoomsCount = selectedGuests.length + const selectedRoomsCount = guestsData.length + const childCountLabel = - selectedChildrenCount > 1 + childCount > 1 ? intl.formatMessage({ id: "Children" }) : intl.formatMessage({ id: "Child" }) @@ -76,22 +60,15 @@ export default function GuestsRoomsPickerForm({ ? intl.formatMessage({ id: "Rooms" }) : intl.formatMessage({ id: "Room" })} {", "} - {selectedAdultsCount}{" "} - {selectedAdultsCount > 1 + {adultCount}{" "} + {adultCount > 1 ? intl.formatMessage({ id: "Adults" }) : intl.formatMessage({ id: "Adult" })} - {selectedChildrenCount > 0 - ? ", " + selectedChildrenCount + " " + childCountLabel - : null} + {childCount > 0 ? ", " + childCount + " " + childCountLabel : null}
- +
) diff --git a/stores/guests-rooms.ts b/stores/guests-rooms.ts new file mode 100644 index 000000000..403cbd73c --- /dev/null +++ b/stores/guests-rooms.ts @@ -0,0 +1,143 @@ +"use client" + +import { produce } from "immer" +import { create } from "zustand" + +import { BedTypeEnum } from "@/types/components/bookingWidget/enums" +import { Child } from "@/types/components/bookingWidget/guestsRoomsPicker" + +interface GuestsRooms { + rooms: [ + { + adults: number + children: Child[] + childrenInAdultsBed: number + }, + ] + adultCount: number + childCount: number + isValidated: boolean + increaseAdults: (roomIndex: number) => void + decreaseAdults: (roomIndex: number) => void + increaseChildren: (roomIndex: number) => void + decreaseChildren: (roomIndex: number) => void + 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: (status: boolean) => void +} + +export const guestsRoomsStore = create((set, get) => ({ + rooms: [ + { + adults: 1, + children: [], + childrenInAdultsBed: 0, + }, + ], + adultCount: 1, + childCount: 0, + isValidated: false, + increaseAdults: (roomIndex) => + set( + produce((state: GuestsRooms) => { + state.rooms[roomIndex].adults = state.rooms[roomIndex].adults + 1 + state.adultCount = state.adultCount + 1 + }) + ), + decreaseAdults: (roomIndex) => + set( + produce((state: GuestsRooms) => { + state.rooms[roomIndex].adults = state.rooms[roomIndex].adults - 1 + state.adultCount = state.adultCount - 1 + let childrenInAdultsBed = 0 + state.rooms[roomIndex].children = state.rooms[roomIndex].children.map( + (child) => { + if (child.bed == BedTypeEnum["In adults bed"]) { + childrenInAdultsBed = childrenInAdultsBed + 1 + if (childrenInAdultsBed > state.rooms[roomIndex].adults) + child.bed = + child.age < 3 + ? BedTypeEnum["In crib"] + : BedTypeEnum["In extra bed"] + } + return child + } + ) + state.rooms[roomIndex].childrenInAdultsBed = + state.rooms[roomIndex].adults + }) + ), + increaseChildren: (roomIndex) => + set( + produce((state: GuestsRooms) => { + state.rooms[roomIndex].children.push({ + age: -1, + bed: -1, + }) + state.childCount = state.childCount + 1 + }) + ), + decreaseChildren: (roomIndex) => + set( + produce((state: GuestsRooms) => { + const roomChildren = state.rooms[roomIndex].children + if ( + roomChildren.length && + roomChildren[roomChildren.length - 1].bed == + BedTypeEnum["In adults bed"] + ) { + state.rooms[roomIndex].childrenInAdultsBed = + state.rooms[roomIndex].childrenInAdultsBed - 1 + } + state.rooms[roomIndex].children.pop() + state.childCount = state.childCount - 1 + }) + ), + updateChildAge: (age, roomIndex, childIndex) => + set( + produce((state: GuestsRooms) => { + state.rooms[roomIndex].children[childIndex].age = age + }) + ), + updateChildBed: (bed, roomIndex, childIndex) => + set( + produce((state: GuestsRooms) => { + state.rooms[roomIndex].children[childIndex].bed = bed + }) + ), + increaseChildInAdultsBed: (roomIndex) => + set( + produce((state: GuestsRooms) => { + state.rooms[roomIndex].childrenInAdultsBed = + state.rooms[roomIndex].childrenInAdultsBed + 1 + }) + ), + decreaseChildInAdultsBed: (roomIndex) => + set( + produce((state: GuestsRooms) => { + state.rooms[roomIndex].childrenInAdultsBed = + state.rooms[roomIndex].childrenInAdultsBed - 1 + }) + ), + increaseRoom: () => + set( + produce((state: GuestsRooms) => { + state.rooms.push({ + adults: 1, + children: [], + childrenInAdultsBed: 0, + }) + }) + ), + decreaseRoom: (roomIndex) => + set( + produce((state: GuestsRooms) => { + state.rooms.splice(roomIndex, 1) + }) + ), + setIsValidated: (status) => set(() => ({ isValidated: status })), +})) diff --git a/types/components/bookingWidget/enums.ts b/types/components/bookingWidget/enums.ts new file mode 100644 index 000000000..9fd14a9af --- /dev/null +++ b/types/components/bookingWidget/enums.ts @@ -0,0 +1,5 @@ +export enum BedTypeEnum { + "In adults bed" = 0, + "In crib" = 1, + "In extra bed" = 2, +} diff --git a/types/components/bookingWidget/guestsRoomsPicker.ts b/types/components/bookingWidget/guestsRoomsPicker.ts index 638229d1e..198d84d08 100644 --- a/types/components/bookingWidget/guestsRoomsPicker.ts +++ b/types/components/bookingWidget/guestsRoomsPicker.ts @@ -13,40 +13,30 @@ export type GuestsRoom = { children: Child[] } -export type GuestsRoomsFormProps = { - name?: string -} - export interface GuestsRoomsPickerProps { - handleOnSelect: (selected: GuestsRoom[]) => void - initialSelected?: GuestsRoom[] + // handleOnSelect: (selected: GuestsRoom[]) => void + // initialSelected?: GuestsRoom[] closePicker: () => void - isValid: boolean + // isValid: boolean } export type GuestsRoomPickerProps = { - handleOnSelect: (selected: GuestsRoom, index: number) => void - room: GuestsRoom + // handleOnSelect: (selected: GuestsRoom, index: number) => void + // room: GuestsRoom index: number - isValid: boolean + // isValid: boolean } export type AdultSelectorProps = { - adults: number - updateAdults: (count: number) => void + roomIndex: number } export type ChildSelectorProps = { - roomChildren: Child[] - adultCount: number - updateChildren: (children: Child[]) => void - isValid: boolean + roomIndex: number } export type ChildInfoSelectorProps = { child: Child index: number - availableBedTypes?: ChildBed[] - updateChild: (child: Child, index: number) => void - isValid: boolean + roomIndex: number }