fix: fix logic for identifying single use ancillaries * fix: fix logic for identifying single use ancillaries * fix: filter out empty categories of ancillaries Approved-by: Erik Tiekstra
217 lines
6.2 KiB
TypeScript
217 lines
6.2 KiB
TypeScript
import { produce } from "immer"
|
|
import { useContext } from "react"
|
|
import { create, useStore } from "zustand"
|
|
|
|
import { BreakfastPackageEnum } from "@scandic-hotels/trpc/enums/breakfast"
|
|
|
|
import { clearAncillarySessionData } from "@/components/HotelReservation/MyStay/utils/ancillaries"
|
|
import { AddAncillaryContext } from "@/contexts/AddAncillary"
|
|
|
|
import type {
|
|
Ancillaries,
|
|
Ancillary,
|
|
SelectedAncillary,
|
|
} from "@/types/components/myPages/myStay/ancillaries"
|
|
import type { Room } from "@/types/stores/my-stay"
|
|
|
|
export enum AncillaryStepEnum {
|
|
selectQuantity = 0,
|
|
selectDelivery = 1,
|
|
confirmation = 2,
|
|
}
|
|
type Step = {
|
|
step: AncillaryStepEnum
|
|
isValid: boolean
|
|
}
|
|
type Steps = {
|
|
[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
|
|
totalPrice: number
|
|
}
|
|
|
|
interface AddAncillaryState {
|
|
currentStep: number
|
|
steps: Steps
|
|
booking: Room
|
|
ancillaries: Ancillaries
|
|
categories: Ancillary["translatedCategoryName"][]
|
|
selectedCategory: string
|
|
selectCategory: (category: string) => void
|
|
ancillariesBySelectedCategory: Ancillary["ancillaryContent"]
|
|
openModal: () => void
|
|
closeModal: () => void
|
|
prevStep: (isMobile: boolean) => void
|
|
breakfastData: BreakfastData | null
|
|
setBreakfastData: (breakfastData: BreakfastData | null) => void
|
|
isBreakfast: boolean
|
|
isOpen: boolean
|
|
selectedAncillary: SelectedAncillary | null
|
|
selectAncillary: (ancillary: SelectedAncillary) => void
|
|
selectQuantity: () => void
|
|
selectDeliveryTime: () => void
|
|
selectQuantityAndDeliveryTime: () => void
|
|
}
|
|
|
|
function findAncillaryByCategory(
|
|
ancillaries: Ancillaries,
|
|
selectedCategory: string
|
|
) {
|
|
return (
|
|
ancillaries.find(
|
|
(ancillary) => ancillary.translatedCategoryName === selectedCategory
|
|
)?.ancillaryContent ?? []
|
|
)
|
|
}
|
|
|
|
export const createAddAncillaryStore = (
|
|
booking: Room,
|
|
ancillaries: Ancillaries
|
|
) => {
|
|
const selectedCategory = ancillaries[0].translatedCategoryName
|
|
const ancillariesBySelectedCategory = findAncillaryByCategory(
|
|
ancillaries,
|
|
selectedCategory
|
|
)
|
|
const categories = ancillaries
|
|
.filter((anc) => !!anc.ancillaryContent.length)
|
|
.map((ancillary) => ancillary.translatedCategoryName)
|
|
const steps = {
|
|
[AncillaryStepEnum.selectQuantity]: {
|
|
step: AncillaryStepEnum.selectQuantity,
|
|
isValid: false,
|
|
},
|
|
[AncillaryStepEnum.selectDelivery]: {
|
|
step: AncillaryStepEnum.selectDelivery,
|
|
isValid: false,
|
|
},
|
|
[AncillaryStepEnum.confirmation]: {
|
|
step: AncillaryStepEnum.confirmation,
|
|
isValid: false,
|
|
},
|
|
}
|
|
return create<AddAncillaryState>((set) => ({
|
|
booking,
|
|
ancillaries,
|
|
categories,
|
|
selectedCategory,
|
|
ancillariesBySelectedCategory,
|
|
currentStep: AncillaryStepEnum.selectQuantity,
|
|
selectedAncillary: null,
|
|
breakfastData: null,
|
|
isBreakfast: false,
|
|
isOpen: false,
|
|
steps,
|
|
openModal: () =>
|
|
set(
|
|
produce((state: AddAncillaryState) => {
|
|
state.isOpen = true
|
|
})
|
|
),
|
|
closeModal: () =>
|
|
set(
|
|
produce((state: AddAncillaryState) => {
|
|
state.currentStep = AncillaryStepEnum.selectQuantity
|
|
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: (isMobile) =>
|
|
set(
|
|
produce((state: AddAncillaryState) => {
|
|
if (state.currentStep === AncillaryStepEnum.selectQuantity) {
|
|
state.isOpen = false
|
|
clearAncillarySessionData()
|
|
state.selectedAncillary = null
|
|
state.steps = steps
|
|
} else {
|
|
if (
|
|
(!state.selectedAncillary?.requiresDeliveryTime || isMobile) &&
|
|
state.currentStep === AncillaryStepEnum.confirmation
|
|
) {
|
|
state.currentStep = AncillaryStepEnum.selectQuantity
|
|
} else {
|
|
state.currentStep = state.currentStep - 1
|
|
}
|
|
}
|
|
})
|
|
),
|
|
selectAncillary: (ancillary) =>
|
|
set(
|
|
produce((state: AddAncillaryState) => {
|
|
state.isOpen = true
|
|
state.selectedAncillary = ancillary
|
|
state.isBreakfast =
|
|
ancillary.id === BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST
|
|
})
|
|
),
|
|
setBreakfastData: (breakfastData) =>
|
|
set(
|
|
produce((state: AddAncillaryState) => {
|
|
state.breakfastData = breakfastData
|
|
})
|
|
),
|
|
}))
|
|
}
|
|
export const useAddAncillaryStore = <T>(
|
|
selector: (state: AddAncillaryState) => T
|
|
) => {
|
|
const store = useContext(AddAncillaryContext)
|
|
if (!store) {
|
|
throw new Error(
|
|
"useAddAncillaryStore must be used within AddAncillaryProvider"
|
|
)
|
|
}
|
|
return useStore(store, selector)
|
|
}
|