"use client" import { produce } from "immer" import { createContext, useContext } from "react" import { create, useStore } from "zustand" import { BedTypeEnum } 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(BedTypeEnum.IN_ADULTS_BED) } else if (child.age <= 5) { room.childrenInAdultsBed = room.childrenInAdultsBed - 1 } if (child.age < 3) { allowedBedTypes.push(BedTypeEnum.IN_CRIB) } if (child.age > 2) { allowedBedTypes.push(BedTypeEnum.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 == BedTypeEnum.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()((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 == BedTypeEnum.IN_ADULTS_BED ) if (toUpdateIndex != -1) { state.rooms[roomIndex].child[toUpdateIndex].bed = state.rooms[roomIndex].child[toUpdateIndex].age < 3 ? BedTypeEnum.IN_CRIB : BedTypeEnum.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 == BedTypeEnum.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 export const GuestsRoomsContext = createContext(null) export const useGuestsRoomsStore = ( selector: (store: GuestsRoomsStoreState) => T ): T => { const guestsRoomsContextStore = useContext(GuestsRoomsContext) if (!guestsRoomsContextStore) { throw new Error( `guestsRoomsContextStore must be used within GuestsRoomsContextProvider` ) } return useStore(guestsRoomsContextStore, selector) }