import { produce } from "immer" import { useContext } from "react" import { create, useStore } from "zustand" import { clearAncillarySessionData } from "@/components/HotelReservation/MyStay/Ancillaries/utils" import { AddAncillaryContext } from "@/contexts/AddAncillary" import type { Ancillaries, Ancillary, SelectedAncillary, } from "@/types/components/myPages/myStay/ancillaries" import { BreakfastPackageEnum } from "@/types/enums/breakfast" import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" export enum AncillaryStepEnum { selectAncillary = 0, selectQuantity = 1, selectDelivery = 2, confirmation = 3, } type Step = { step: AncillaryStepEnum isValid: boolean } type Steps = { [AncillaryStepEnum.selectAncillary]?: Step [AncillaryStepEnum.selectQuantity]: Step [AncillaryStepEnum.selectDelivery]: Step [AncillaryStepEnum.confirmation]: Step } export type BreakfastData = { nrOfAdults: number nrOfPayingChildren: number nrOfFreeChildren: number nrOfNights: number priceAdult: number priceChild: number currency: string } export interface AddAncillaryState { currentStep: number steps: Steps booking: BookingConfirmation["booking"] ancillaries: Ancillaries categories: Ancillary["categoryName"][] selectedCategory: string selectCategory: (category: string) => void ancillariesBySelectedCategory: Ancillary["ancillaryContent"] openModal: VoidFunction closeModal: VoidFunction prevStep: VoidFunction breakfastData: BreakfastData | null setBreakfastData: (breakfastData: BreakfastData | null) => void isBreakfast: boolean isOpen: boolean selectedAncillary: SelectedAncillary | null selectAncillary: (ancillary: SelectedAncillary) => void selectQuantity: VoidFunction selectDeliveryTime: VoidFunction selectQuantityAndDeliveryTime: VoidFunction } function findAncillaryByCategory( ancillaries: Ancillaries, selectedCategory: string ) { return ( ancillaries.find((ancillary) => ancillary.categoryName === selectedCategory) ?.ancillaryContent ?? [] ) } export const createAddAncillaryStore = ( booking: BookingConfirmation["booking"], ancillaries: Ancillaries ) => { const selectedCategory = ancillaries[0].categoryName const ancillariesBySelectedCategory = findAncillaryByCategory( ancillaries, selectedCategory ) const categories = ancillaries.map((ancillary) => ancillary.categoryName) const steps = { [AncillaryStepEnum.selectAncillary]: { step: AncillaryStepEnum.selectAncillary, isValid: true, }, [AncillaryStepEnum.selectQuantity]: { step: AncillaryStepEnum.selectQuantity, isValid: false, }, [AncillaryStepEnum.selectDelivery]: { step: AncillaryStepEnum.selectDelivery, isValid: false, }, [AncillaryStepEnum.confirmation]: { step: AncillaryStepEnum.confirmation, isValid: false, }, } return create((set) => ({ booking, ancillaries, categories, selectedCategory, ancillariesBySelectedCategory, currentStep: AncillaryStepEnum.selectAncillary, selectedAncillary: null, breakfastData: null, isBreakfast: false, isOpen: false, steps, openModal: () => set( produce((state: AddAncillaryState) => { state.isOpen = true state.currentStep = AncillaryStepEnum.selectAncillary }) ), closeModal: () => set( produce((state: AddAncillaryState) => { state.isOpen = false clearAncillarySessionData() state.selectedAncillary = null state.steps = steps }) ), selectCategory: (category) => set( produce((state: AddAncillaryState) => { state.selectedCategory = category state.ancillariesBySelectedCategory = findAncillaryByCategory( state.ancillaries, category ) }) ), selectQuantity: () => set( produce((state: AddAncillaryState) => { if (state.selectedAncillary?.requiresDeliveryTime) { state.currentStep = AncillaryStepEnum.selectDelivery } else { state.steps[AncillaryStepEnum.selectDelivery].isValid = true state.currentStep = AncillaryStepEnum.confirmation } state.steps[AncillaryStepEnum.selectQuantity].isValid = true }) ), selectQuantityAndDeliveryTime: () => set( produce((state: AddAncillaryState) => { state.steps[AncillaryStepEnum.selectQuantity].isValid = true state.steps[AncillaryStepEnum.selectDelivery].isValid = true state.currentStep = AncillaryStepEnum.confirmation }) ), selectDeliveryTime: () => set( produce((state: AddAncillaryState) => { state.steps[AncillaryStepEnum.selectDelivery].isValid = true state.currentStep = AncillaryStepEnum.confirmation }) ), prevStep: () => set( produce((state: AddAncillaryState) => { if ( state.currentStep === AncillaryStepEnum.selectAncillary || (state.currentStep === AncillaryStepEnum.selectQuantity && !state.steps[AncillaryStepEnum.selectAncillary]) ) { state.isOpen = false clearAncillarySessionData() state.selectedAncillary = null state.steps = steps } else { if ( !state.selectedAncillary?.requiresDeliveryTime && state.currentStep === AncillaryStepEnum.confirmation ) { state.currentStep = AncillaryStepEnum.selectQuantity } else if (state.currentStep === AncillaryStepEnum.selectQuantity) { state.currentStep = state.currentStep - 1 state.selectedAncillary = null } else { state.currentStep = state.currentStep - 1 } } }) ), selectAncillary: (ancillary) => set( produce((state: AddAncillaryState) => { if (state.isOpen) { state.steps[AncillaryStepEnum.selectAncillary]!.isValid = true } else { state.isOpen = true delete state.steps[AncillaryStepEnum.selectAncillary] } state.selectedAncillary = ancillary state.currentStep = AncillaryStepEnum.selectQuantity state.isBreakfast = ancillary.id === BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST }) ), setBreakfastData: (breakfastData) => set( produce((state: AddAncillaryState) => { state.breakfastData = breakfastData }) ), })) } export const useAddAncillaryStore = ( selector: (state: AddAncillaryState) => T ) => { const store = useContext(AddAncillaryContext) if (!store) { throw new Error( "useAddAncillaryStore must be used within AddAncillaryProvider" ) } return useStore(store, selector) }