157 lines
4.2 KiB
TypeScript
157 lines
4.2 KiB
TypeScript
"use client"
|
|
import merge from "deepmerge"
|
|
import { produce } from "immer"
|
|
import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime"
|
|
import { useContext } from "react"
|
|
import { create, useStore } from "zustand"
|
|
|
|
import { bedTypeSchema } from "@/components/HotelReservation/EnterDetails/BedType/schema"
|
|
import { breakfastStoreSchema } from "@/components/HotelReservation/EnterDetails/Breakfast/schema"
|
|
import {
|
|
guestDetailsSchema,
|
|
signedInDetailsSchema,
|
|
} from "@/components/HotelReservation/EnterDetails/Details/schema"
|
|
import { StepsContext } from "@/contexts/Steps"
|
|
|
|
import { detailsStorageName as detailsStorageName } from "./details"
|
|
|
|
import { StepEnum } from "@/types/enums/step"
|
|
import type { DetailsState } from "@/types/stores/details"
|
|
import type { StepState } from "@/types/stores/steps"
|
|
|
|
export function createStepsStore(
|
|
currentStep: StepEnum,
|
|
isMember: boolean,
|
|
noBedChoices: boolean,
|
|
noBreakfast: boolean,
|
|
searchParams: string,
|
|
push: AppRouterInstance["push"]
|
|
) {
|
|
const isBrowser = typeof window !== "undefined"
|
|
const steps = [
|
|
StepEnum.selectBed,
|
|
StepEnum.breakfast,
|
|
StepEnum.details,
|
|
StepEnum.payment,
|
|
]
|
|
|
|
/**
|
|
* TODO:
|
|
* - when included in rate, can packages still be received?
|
|
* - no hotels yet with breakfast included in the rate so
|
|
* impossible to build for atm.
|
|
*
|
|
* matching breakfast first so the steps array is altered
|
|
* before the bedTypes possible step altering
|
|
*/
|
|
if (noBreakfast) {
|
|
steps.splice(1, 1)
|
|
if (currentStep === StepEnum.breakfast) {
|
|
currentStep = steps[1]
|
|
push(`${currentStep}?${searchParams}`)
|
|
}
|
|
}
|
|
|
|
if (noBedChoices) {
|
|
if (currentStep === StepEnum.selectBed) {
|
|
currentStep = steps[1]
|
|
push(`${currentStep}?${searchParams}`)
|
|
}
|
|
}
|
|
|
|
const detailsStorageUnparsed = isBrowser
|
|
? sessionStorage.getItem(detailsStorageName)
|
|
: null
|
|
if (detailsStorageUnparsed) {
|
|
const detailsStorage: Record<
|
|
"state",
|
|
Pick<DetailsState, "data">
|
|
> = JSON.parse(detailsStorageUnparsed)
|
|
|
|
const validPaths = [StepEnum.selectBed]
|
|
|
|
const validatedBedType = bedTypeSchema.safeParse(detailsStorage.state.data)
|
|
if (validatedBedType.success) {
|
|
validPaths.push(steps[1])
|
|
}
|
|
|
|
const validatedBreakfast = breakfastStoreSchema.safeParse(
|
|
detailsStorage.state.data
|
|
)
|
|
if (validatedBreakfast.success) {
|
|
validPaths.push(StepEnum.details)
|
|
}
|
|
|
|
const detailsSchema = isMember ? signedInDetailsSchema : guestDetailsSchema
|
|
const validatedDetails = detailsSchema.safeParse(detailsStorage.state.data)
|
|
if (validatedDetails.success) {
|
|
validPaths.push(StepEnum.payment)
|
|
}
|
|
|
|
if (!validPaths.includes(currentStep) && isBrowser) {
|
|
// We will always have at least one valid path
|
|
currentStep = validPaths.pop()!
|
|
push(`${currentStep}?${searchParams}`)
|
|
}
|
|
}
|
|
|
|
const initalData = {
|
|
currentStep,
|
|
steps,
|
|
}
|
|
|
|
return create<StepState>()((set) =>
|
|
merge(
|
|
{
|
|
currentStep: StepEnum.selectBed,
|
|
steps: [],
|
|
|
|
completeStep() {
|
|
return set(
|
|
produce((state: StepState) => {
|
|
const currentStepIndex = state.steps.indexOf(state.currentStep)
|
|
const nextStep = state.steps[currentStepIndex + 1]
|
|
state.currentStep = nextStep
|
|
window.history.pushState(
|
|
{ step: nextStep },
|
|
"",
|
|
nextStep + window.location.search
|
|
)
|
|
})
|
|
)
|
|
},
|
|
navigate(step: StepEnum) {
|
|
return set(
|
|
produce((state) => {
|
|
state.currentStep = step
|
|
window.history.pushState(
|
|
{ step },
|
|
"",
|
|
step + window.location.search
|
|
)
|
|
})
|
|
)
|
|
},
|
|
setStep(step: StepEnum) {
|
|
return set(
|
|
produce((state: StepState) => {
|
|
state.currentStep = step
|
|
})
|
|
)
|
|
},
|
|
},
|
|
initalData
|
|
)
|
|
)
|
|
}
|
|
|
|
export function useStepsStore<T>(selector: (store: StepState) => T) {
|
|
const store = useContext(StepsContext)
|
|
|
|
if (!store) {
|
|
throw new Error(`useStepsStore must be used within StepsProvider`)
|
|
}
|
|
|
|
return useStore(store, selector)
|
|
}
|