import { produce } from "immer" import { createContext, useContext } from "react" import { create, useStore } from "zustand" import { bedTypeSchema } from "@/components/HotelReservation/EnterDetails/BedType/schema" import { breakfastSchema } from "@/components/HotelReservation/EnterDetails/Breakfast/schema" import { detailsSchema } from "@/components/HotelReservation/EnterDetails/Details/schema" import { DetailsSchema } from "@/types/components/enterDetails/details" import { SidePeekEnum } from "@/types/components/enterDetails/sidePeek" import { StepEnum } from "@/types/components/enterDetails/step" import { bedTypeEnum } from "@/types/enums/bedType" import { breakfastEnum } from "@/types/enums/breakfast" interface EnterDetailsState { data: { bedType: bedTypeEnum | undefined breakfast: breakfastEnum | undefined } & DetailsSchema steps: StepEnum[] currentStep: StepEnum activeSidePeek: SidePeekEnum | null isValid: Record completeStep: (updatedData: Partial) => void navigate: (step: StepEnum, searchParams?: Record) => void openSidePeek: (key: SidePeekEnum | null) => void closeSidePeek: () => void } export function initEditDetailsState(currentStep: StepEnum) { const isBrowser = typeof window !== "undefined" const sessionData = isBrowser ? sessionStorage.getItem("editDetails") : null const search = isBrowser ? new URLSearchParams(window.location.search) : null const defaultData: EnterDetailsState["data"] = { bedType: undefined, breakfast: undefined, countryCode: "", email: "", firstname: "", lastname: "", phoneNumber: "", } let inputData = {} if (search?.size) { const searchParams: Record = {} search.forEach((value, key) => { searchParams[key] = value }) inputData = searchParams } else if (sessionData) { inputData = JSON.parse(sessionData) } const validPaths = [StepEnum.selectBed] let initialData = defaultData const isValid = { [StepEnum.selectBed]: false, [StepEnum.breakfast]: false, [StepEnum.details]: false, [StepEnum.payment]: false, } const validatedBedType = bedTypeSchema.safeParse(inputData) if (validatedBedType.success) { validPaths.push(StepEnum.breakfast) initialData = { ...initialData, ...validatedBedType.data } isValid[StepEnum.selectBed] = true } const validatedBreakfast = breakfastSchema.safeParse(inputData) if (validatedBreakfast.success) { validPaths.push(StepEnum.details) initialData = { ...initialData, ...validatedBreakfast.data } isValid[StepEnum.breakfast] = true } const validatedDetails = detailsSchema.safeParse(inputData) if (validatedDetails.success) { validPaths.push(StepEnum.payment) initialData = { ...initialData, ...validatedDetails.data } isValid[StepEnum.details] = true } if (!validPaths.includes(currentStep)) { currentStep = validPaths.pop()! // We will always have at least one valid path if (isBrowser) { window.history.pushState({}, "", currentStep + window.location.search) } } return create()((set, get) => ({ data: initialData, steps: Object.values(StepEnum), navigate: (step, searchParams) => set( produce((state) => { const query = new URLSearchParams(window.location.search) if (searchParams) { Object.entries(searchParams).forEach(([key, value]) => { query.set(key, value) }) } state.currentStep = step window.history.pushState({}, "", step + "?" + query.toString()) }) ), openSidePeek: (key) => set({ activeSidePeek: key }), closeSidePeek: () => set({ activeSidePeek: null }), currentStep, activeSidePeek: null, isValid, completeStep: (updatedData) => set( produce((state) => { state.isValid[state.currentStep] = true const nextStep = state.steps[state.steps.indexOf(state.currentStep) + 1] state.data = { ...state.data, ...updatedData } state.currentStep = nextStep get().navigate(nextStep, updatedData) }) ), })) } export type EnterDetailsStore = ReturnType export const EnterDetailsContext = createContext(null) export const useEnterDetailsStore = ( selector: (store: EnterDetailsState) => T ): T => { const enterDetailsContextStore = useContext(EnterDetailsContext) if (!enterDetailsContextStore) { throw new Error( `useEnterDetailsStore must be used within EnterDetailsContextProvider` ) } return useStore(enterDetailsContextStore, selector) }