228 lines
6.9 KiB
TypeScript
228 lines
6.9 KiB
TypeScript
"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)
|
|
}
|