Files
web/apps/scandic-web/stores/my-stay/add-ancillary-flow.ts
Christel Westerberg 001000a56d Merged in fix/STAY-131-hide-ancillaries (pull request #3299)
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
2025-12-05 12:25:12 +00:00

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)
}