Merged in chore/move-enter-details (pull request #2778)

Chore/move enter details

Approved-by: Anton Gunnarsson
This commit is contained in:
Joakim Jäderberg
2025-09-11 07:16:24 +00:00
parent 15711cb3a4
commit 7dee6d5083
238 changed files with 1656 additions and 1602 deletions

View File

@@ -1,606 +0,0 @@
import { parsePhoneNumberFromString } from "libphonenumber-js"
import { calculateRegularPrice } from "@scandic-hotels/booking-flow/utils/calculateRegularPrice"
import {
sumPackages,
sumPackagesRequestedPrice,
} from "@scandic-hotels/booking-flow/utils/SelectRate"
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import { RateTypeEnum } from "@scandic-hotels/common/constants/rateType"
import { logger } from "@scandic-hotels/common/logger"
import { detailsStorageName } from "."
import type { BreakfastPackage } from "@scandic-hotels/trpc/routers/hotels/schemas/packages"
import type { Packages } from "@scandic-hotels/trpc/types/packages"
import type {
CorporateChequeProduct,
PriceProduct,
RedemptionProduct,
VoucherProduct,
} from "@scandic-hotels/trpc/types/roomAvailability"
import { type RoomRate } from "@/types/components/hotelReservation/enterDetails/details"
import type { Price } from "@/types/components/hotelReservation/price"
import type { PersistedState, RoomState } from "@/types/stores/enter-details"
import type { SafeUser } from "@/types/user"
export function extractGuestFromUser(user: NonNullable<SafeUser>) {
let phoneNumberCC = ""
if (user.phoneNumber) {
const parsedPhoneNumber = parsePhoneNumberFromString(user.phoneNumber)
if (parsedPhoneNumber?.country) {
phoneNumberCC = parsedPhoneNumber.country.toLowerCase()
}
}
return {
countryCode: user.address.countryCode?.toString(),
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
join: false,
membershipNo: user.membership?.membershipNumber,
phoneNumber: user.phoneNumber ?? "",
phoneNumberCC,
}
}
export function add(...nums: (number | string | undefined)[]) {
return nums.reduce((total: number, num) => {
if (typeof num === "undefined") {
num = 0
}
total = total + parseInt(`${num}`)
return total
}, 0)
}
export function getRoomPrice(roomRate: RoomRate, isMember: boolean) {
if (isMember && "member" in roomRate && roomRate.member) {
let publicRate
if (
"public" in roomRate &&
roomRate.public?.rateType === RateTypeEnum.Regular
) {
publicRate = roomRate.public
}
return {
perNight: {
requested: roomRate.member.requestedPrice
? {
currency: roomRate.member.requestedPrice.currency,
price: roomRate.member.requestedPrice.pricePerNight,
}
: undefined,
local: {
currency: roomRate.member.localPrice.currency,
price: roomRate.member.localPrice.pricePerNight,
regularPrice:
publicRate?.localPrice.pricePerStay ||
roomRate.member.localPrice.regularPricePerNight,
},
},
perStay: {
requested: roomRate.member.requestedPrice
? {
currency: roomRate.member.requestedPrice.currency,
price: roomRate.member.requestedPrice.pricePerStay,
}
: undefined,
local: {
currency: roomRate.member.localPrice.currency,
price: roomRate.member.localPrice.pricePerStay,
regularPrice:
publicRate?.localPrice.pricePerStay ||
roomRate.member.localPrice.regularPricePerStay,
},
},
}
}
if ("public" in roomRate && roomRate.public) {
return {
perNight: {
requested: roomRate.public.requestedPrice
? {
currency: roomRate.public.requestedPrice.currency,
price: roomRate.public.requestedPrice.pricePerNight,
}
: undefined,
local: {
currency: roomRate.public.localPrice.currency,
price: roomRate.public.localPrice.pricePerNight,
regularPrice: roomRate.public.localPrice.regularPricePerNight,
},
},
perStay: {
requested: roomRate.public.requestedPrice
? {
currency: roomRate.public.requestedPrice.currency,
price: roomRate.public.requestedPrice.pricePerStay,
}
: undefined,
local: {
currency: roomRate.public.localPrice.currency,
price: roomRate.public.localPrice.pricePerStay,
regularPrice: roomRate.public.localPrice.regularPricePerStay,
},
},
}
}
if ("corporateCheque" in roomRate) {
return {
perNight: {
requested: roomRate.corporateCheque.requestedPrice
? {
currency: CurrencyEnum.CC,
price: roomRate.corporateCheque.requestedPrice.numberOfCheques,
additionalPrice:
roomRate.corporateCheque.requestedPrice.additionalPricePerStay,
additionalPriceCurrency:
roomRate.corporateCheque.requestedPrice.currency ?? undefined,
}
: undefined,
local: {
currency: CurrencyEnum.CC,
price: roomRate.corporateCheque.localPrice.numberOfCheques,
additionalPrice:
roomRate.corporateCheque.localPrice.additionalPricePerStay,
additionalPriceCurrency:
roomRate.corporateCheque.localPrice.currency ?? undefined,
},
},
perStay: {
requested: roomRate.corporateCheque.requestedPrice
? {
currency: CurrencyEnum.CC,
price: roomRate.corporateCheque.requestedPrice.numberOfCheques,
additionalPrice:
roomRate.corporateCheque.requestedPrice.additionalPricePerStay,
additionalPriceCurrency:
roomRate.corporateCheque.requestedPrice.currency ?? undefined,
}
: undefined,
local: {
currency: CurrencyEnum.CC,
price: roomRate.corporateCheque.localPrice.numberOfCheques,
additionalPrice:
roomRate.corporateCheque.localPrice.additionalPricePerStay,
additionalPriceCurrency:
roomRate.corporateCheque.localPrice.currency ?? undefined,
},
},
}
}
if ("voucher" in roomRate) {
return {
perNight: {
requested: undefined,
local: {
currency: CurrencyEnum.Voucher,
price: roomRate.voucher.numberOfVouchers,
},
},
perStay: {
requested: undefined,
local: {
currency: CurrencyEnum.Voucher,
price: roomRate.voucher.numberOfVouchers,
},
},
}
}
if ("redemption" in roomRate) {
return {
// ToDo Handle perNight as undefined
perNight: {
requested: undefined,
local: {
currency: CurrencyEnum.POINTS,
price: roomRate.redemption.localPrice.pointsPerStay,
additionalPrice:
roomRate.redemption.localPrice.additionalPricePerStay,
additionalPriceCurrency:
roomRate.redemption.localPrice.currency ?? undefined,
},
},
perStay: {
requested: undefined,
local: {
currency: CurrencyEnum.POINTS,
price: roomRate.redemption.localPrice.pointsPerStay,
additionalPrice:
roomRate.redemption.localPrice.additionalPricePerStay,
additionalPriceCurrency:
roomRate.redemption.localPrice.currency ?? undefined,
},
},
}
}
throw new Error(
`Unable to calculate RoomPrice since user is neither a member or memberRate is missing, or publicRate is missing`
)
}
export const checkRoomProgress = (steps: RoomState["steps"]) => {
return Object.values(steps)
.filter(Boolean)
.every((step) => step.isValid)
}
export function readFromSessionStorage(): PersistedState | undefined {
if (typeof window === "undefined") {
return undefined
}
try {
const storedData = sessionStorage.getItem(detailsStorageName)
if (!storedData) {
return undefined
}
const parsedData = JSON.parse(storedData) as PersistedState
if (!parsedData.booking || !parsedData.rooms) {
return undefined
}
return parsedData
} catch (error) {
logger.error("Error reading from session storage:", error)
return undefined
}
}
export function writeToSessionStorage(state: PersistedState) {
if (typeof window === "undefined") {
return
}
try {
sessionStorage.setItem(detailsStorageName, JSON.stringify(state))
} catch (error) {
logger.error("Error writing to session storage:", error)
}
}
export function clearSessionStorage() {
if (typeof window === "undefined") {
return
}
sessionStorage.removeItem(detailsStorageName)
}
function getAdditionalPrice(
total: Price,
adults: number,
breakfast: BreakfastPackage | false | undefined,
nights: number,
packages: Packages | null,
additionalPrice = 0,
additionalPriceCurrency?: CurrencyEnum | null | undefined
) {
const breakfastLocalPrice =
(breakfast ? breakfast.localPrice.price : 0) * nights * adults
const pkgsSum = sumPackages(packages || [])
total.local.additionalPrice = add(
total.local.additionalPrice,
additionalPrice,
breakfastLocalPrice,
pkgsSum.price
)
if (!total.local.additionalPriceCurrency) {
if (additionalPriceCurrency) {
total.local.additionalPriceCurrency = additionalPriceCurrency
} else if (breakfast && breakfast.localPrice.currency) {
total.local.additionalPriceCurrency = breakfast.localPrice.currency
} else if (pkgsSum.currency) {
total.local.additionalPriceCurrency = pkgsSum.currency
}
}
}
function getRequestedAdditionalPrice(
total: Price,
adults: number,
breakfast: BreakfastPackage | false | undefined,
nights: number,
packages: Packages | null,
cheques: number,
additionalPrice = 0,
additionalPriceCurrency: CurrencyEnum | null | undefined
) {
if (!total.requested) {
total.requested = {
currency: CurrencyEnum.CC,
price: 0,
}
}
total.requested.price = add(total.requested.price, cheques)
const breakfastRequestedPrice =
(breakfast ? breakfast.requestedPrice?.price || 0 : 0) * nights * adults
const pkgsSumRequested = sumPackagesRequestedPrice(packages)
total.requested.additionalPrice = add(
total.requested.additionalPrice,
additionalPrice,
breakfastRequestedPrice,
pkgsSumRequested.price
)
if (!total.requested.additionalPriceCurrency) {
if (additionalPriceCurrency) {
total.requested.additionalPriceCurrency = additionalPriceCurrency
} else if (pkgsSumRequested.currency) {
total.requested.additionalPriceCurrency = pkgsSumRequested.currency
} else if (breakfast && breakfast.requestedPrice) {
total.requested.additionalPriceCurrency =
breakfast.requestedPrice.currency
}
}
}
interface TRoom
extends Pick<
RoomState["room"],
"adults" | "breakfast" | "guest" | "roomFeatures" | "roomRate"
> {}
interface TRoomCorporateCheque extends TRoom {
roomRate: CorporateChequeProduct
}
export function getCorporateChequePrice(rooms: TRoom[], nights: number) {
return rooms
.filter(
(room): room is TRoomCorporateCheque => "corporateCheque" in room.roomRate
)
.reduce<Price>(
(total, room) => {
const corporateCheque = room.roomRate.corporateCheque
total.local.price = add(
total.local.price,
corporateCheque.localPrice.numberOfCheques
)
getAdditionalPrice(
total,
room.adults,
room.breakfast,
nights,
room.roomFeatures,
corporateCheque.localPrice.additionalPricePerStay,
corporateCheque.localPrice.currency
)
if (corporateCheque.requestedPrice) {
getRequestedAdditionalPrice(
total,
room.adults,
room.breakfast,
nights,
room.roomFeatures,
corporateCheque.requestedPrice.numberOfCheques,
corporateCheque.requestedPrice?.additionalPricePerStay,
corporateCheque.requestedPrice?.currency
)
}
return total
},
{
local: {
currency: CurrencyEnum.CC,
price: 0,
},
requested: undefined,
}
)
}
interface TRoomVoucher extends TRoom {
roomRate: VoucherProduct
}
export function getVoucherPrice(rooms: TRoom[], nights: number) {
return rooms
.filter((room): room is TRoomVoucher => "voucher" in room.roomRate)
.reduce<Price>(
(total, room) => {
const voucher = room.roomRate.voucher
total.local.price = add(total.local.price, voucher.numberOfVouchers)
getAdditionalPrice(
total,
room.adults,
room.breakfast,
nights,
room.roomFeatures
)
return total
},
{
local: {
currency: CurrencyEnum.Voucher,
price: 0,
},
requested: undefined,
}
)
}
interface TRoomRedemption extends TRoom {
roomRate: RedemptionProduct
}
export function getRedemptionPrice(rooms: TRoom[], nights: number) {
return rooms
.filter((room): room is TRoomRedemption => "redemption" in room.roomRate)
.reduce<Price>(
(total, room) => {
const redemption = room.roomRate.redemption
total.local.price = add(
total.local.price,
redemption.localPrice.pointsPerStay
)
getAdditionalPrice(
total,
room.adults,
room.breakfast,
nights,
room.roomFeatures,
redemption.localPrice.additionalPricePerStay,
redemption.localPrice.currency
)
return total
},
{
local: {
currency: CurrencyEnum.POINTS,
price: 0,
},
requested: undefined,
}
)
}
interface TRoomPriceProduct extends TRoom {
roomRate: PriceProduct
}
export function getRegularPrice(
rooms: TRoom[],
isMember: boolean,
nights: number
) {
const totalPrice = rooms
.filter(
(room): room is TRoomPriceProduct =>
"member" in room.roomRate || "public" in room.roomRate
)
.reduce<Price>(
(total, room, idx) => {
const isMainRoomAndMember = idx === 0 && isMember
const join = Boolean(room.guest.join || room.guest.membershipNo)
const memberRate = "member" in room.roomRate && room.roomRate.member
const publicRate = "public" in room.roomRate && room.roomRate.public
const useMemberRate = (isMainRoomAndMember || join) && memberRate
const rate = useMemberRate ? memberRate : publicRate
if (!rate) {
return total
}
const breakfastLocalPrice =
(room.breakfast ? room.breakfast.localPrice.price || 0 : 0) *
nights *
room.adults
const pkgsSum = sumPackages(room.roomFeatures || [])
const additionalCost = breakfastLocalPrice + pkgsSum.price
total.local.currency = rate.localPrice.currency
total.local.price = add(
total.local.price,
rate.localPrice.pricePerStay,
additionalCost
)
if (rate.requestedPrice) {
if (!total.requested) {
total.requested = {
currency: rate.requestedPrice.currency,
price: 0,
}
}
const breakfastRequestedPrice =
(room.breakfast ? (room.breakfast.requestedPrice?.price ?? 0) : 0) *
nights *
room.adults
const pkgsSumRequested = sumPackagesRequestedPrice(room.roomFeatures)
total.requested.price = add(
total.requested.price,
rate.requestedPrice.pricePerStay,
breakfastRequestedPrice,
pkgsSumRequested.price
)
}
return calculateRegularPrice({
total,
useMemberRate: !!useMemberRate,
regularMemberPrice: memberRate
? {
pricePerStay: memberRate.localPrice.pricePerNight,
regularPricePerStay: memberRate.localPrice.regularPricePerStay,
}
: undefined,
regularPublicPrice: publicRate
? {
pricePerStay: publicRate.localPrice.pricePerNight,
regularPricePerStay: publicRate.localPrice.regularPricePerStay,
}
: undefined,
additionalCost,
})
},
{
local: {
currency: CurrencyEnum.Unknown,
price: 0,
regularPrice: 0,
},
requested: undefined,
}
)
if (
totalPrice.local.regularPrice &&
totalPrice.local.price >= totalPrice.local.regularPrice
) {
totalPrice.local.regularPrice = 0
}
return totalPrice
}
export function getTotalPrice(
rooms: TRoom[],
isMember: boolean,
nights: number
) {
const hasCorpChqRates = rooms.some(
(room) => "corporateCheque" in room.roomRate
)
if (hasCorpChqRates) {
return getCorporateChequePrice(rooms, nights)
}
const hasRedemptionRates = rooms.some((room) => "redemption" in room.roomRate)
if (hasRedemptionRates) {
return getRedemptionPrice(rooms, nights)
}
const hasVoucherRates = rooms.some((room) => "voucher" in room.roomRate)
if (hasVoucherRates) {
return getVoucherPrice(rooms, nights)
}
return getRegularPrice(rooms, isMember, nights)
}

View File

@@ -1,396 +0,0 @@
import deepmerge from "deepmerge"
import { produce } from "immer"
import { useContext } from "react"
import { create, useStore } from "zustand"
import { dt } from "@scandic-hotels/common/dt"
import { getDefaultCountryFromLang } from "@scandic-hotels/common/utils/phone"
import { DetailsContext } from "@/contexts/Details"
import {
checkRoomProgress,
extractGuestFromUser,
getRoomPrice,
getTotalPrice,
writeToSessionStorage,
} from "./helpers"
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { BreakfastPackages } from "@scandic-hotels/trpc/routers/hotels/output"
import type { Price } from "@/types/components/hotelReservation/price"
import { StepEnum } from "@/types/enums/step"
import type {
DetailsState,
InitialState,
RoomState,
} from "@/types/stores/enter-details"
import type { SafeUser } from "@/types/user"
const defaultGuestState = {
countryCode: "",
dateOfBirth: "",
email: "",
firstName: "",
join: false,
lastName: "",
membershipNo: "",
phoneNumber: "",
phoneNumberCC: "",
zipCode: "",
}
export const detailsStorageName = "rooms-details-storage"
export function createDetailsStore(
initialState: InitialState,
searchParams: string,
user: SafeUser,
breakfastPackages: BreakfastPackages,
lang: Lang
) {
const isMember = !!user
const nights = dt(initialState.booking.toDate).diff(
initialState.booking.fromDate,
"days"
)
const initialRooms = initialState.rooms.map((room, idx) => {
return {
...room,
adults: initialState.booking.rooms[idx].adults,
childrenInRoom: initialState.booking.rooms[idx].childrenInRoom,
bedType: room.bedType,
breakfast:
!breakfastPackages.length || room.breakfastIncluded
? (false as const)
: undefined,
guest:
isMember && idx === 0
? deepmerge(defaultGuestState, extractGuestFromUser(user))
: {
...defaultGuestState,
phoneNumberCC: getDefaultCountryFromLang(lang),
},
roomPrice: getRoomPrice(room.roomRate, isMember && idx === 0),
specialRequest: {
comment: "",
},
}
})
const initialTotalPrice: Price = getTotalPrice(initialRooms, isMember, nights)
const availableBeds = initialState.rooms.reduce<
DetailsState["availableBeds"]
>((total, room) => {
for (const bed of room.bedTypes) {
if (!total[bed.value]) {
total[bed.value] = bed.roomsLeft
}
}
return total
}, {})
return create<DetailsState>()((set) => ({
availableBeds,
booking: initialState.booking,
roomCategories: initialState.roomCategories,
breakfastPackages,
canProceedToPayment: false,
isSubmitting: false,
isSummaryOpen: false,
lastRoom: initialState.booking.rooms.length - 1,
rooms: initialRooms.map((room, idx) => {
const steps: RoomState["steps"] = {
[StepEnum.selectBed]: {
step: StepEnum.selectBed,
isValid: !!room.bedType,
},
[StepEnum.breakfast]: {
step: StepEnum.breakfast,
isValid: false,
},
[StepEnum.details]: {
step: StepEnum.details,
isValid: isMember && idx === 0,
},
}
if (room.breakfastIncluded || !breakfastPackages.length) {
delete steps[StepEnum.breakfast]
}
return {
actions: {
setIncomplete() {
return set(
produce((state: DetailsState) => {
state.rooms[idx].isComplete = false
})
)
},
updateBedType(bedType) {
return set(
produce((state: DetailsState) => {
const currentlySelectedBed =
state.rooms[idx].room.bedType?.roomTypeCode
if (currentlySelectedBed) {
state.availableBeds[currentlySelectedBed] =
state.availableBeds[currentlySelectedBed] + 1
}
state.availableBeds[bedType.roomTypeCode] =
state.availableBeds[bedType.roomTypeCode] - 1
state.rooms[idx].steps[StepEnum.selectBed].isValid = true
state.rooms[idx].room.bedType = bedType
const isAllStepsCompleted = checkRoomProgress(
state.rooms[idx].steps
)
if (isAllStepsCompleted) {
state.rooms[idx].isComplete = true
}
writeToSessionStorage({
booking: state.booking,
rooms: state.rooms,
})
})
)
},
updateBreakfast(breakfast) {
return set(
produce((state: DetailsState) => {
const currentRoom = state.rooms[idx]
if (currentRoom.steps[StepEnum.breakfast]) {
currentRoom.steps[StepEnum.breakfast].isValid = true
}
currentRoom.room.breakfast = breakfast
const nights = dt(state.booking.toDate).diff(
state.booking.fromDate,
"days"
)
state.totalPrice = getTotalPrice(
state.rooms.map((r) => r.room),
isMember,
nights
)
const isAllStepsCompleted = checkRoomProgress(
state.rooms[idx].steps
)
if (isAllStepsCompleted) {
state.rooms[idx].isComplete = true
}
writeToSessionStorage({
booking: state.booking,
rooms: state.rooms,
})
})
)
},
updatePriceForMembershipNo(membershipNo, isValid) {
return set(
produce((state: DetailsState) => {
const currentRoom = state.rooms[idx].room
currentRoom.guest.join = false
currentRoom.guest.membershipNo = isValid ? membershipNo : ""
const isValidMembershipNo = isValid && !!membershipNo
currentRoom.roomPrice = getRoomPrice(
currentRoom.roomRate,
isValidMembershipNo
)
const nights = dt(state.booking.toDate).diff(
state.booking.fromDate,
"days"
)
state.totalPrice = getTotalPrice(
state.rooms.map((r) => r.room),
isMember,
nights
)
writeToSessionStorage({
booking: state.booking,
rooms: state.rooms,
})
})
)
},
updateJoin(join) {
return set(
produce((state: DetailsState) => {
const currentRoom = state.rooms[idx].room
currentRoom.guest.join = join
if (join) {
currentRoom.guest.membershipNo = ""
}
currentRoom.roomPrice = getRoomPrice(currentRoom.roomRate, join)
const nights = dt(state.booking.toDate).diff(
state.booking.fromDate,
"days"
)
state.totalPrice = getTotalPrice(
state.rooms.map((r) => r.room),
isMember,
nights
)
writeToSessionStorage({
booking: state.booking,
rooms: state.rooms,
})
})
)
},
updatePartialGuestData(data) {
return set(
produce((state: DetailsState) => {
const currentRoom = state.rooms[idx].room
//Update only the parts that are relevant for cross-validation
if (data.firstName !== undefined)
currentRoom.guest.firstName = data.firstName
if (data.lastName !== undefined)
currentRoom.guest.lastName = data.lastName
if (data.membershipNo !== undefined)
currentRoom.guest.membershipNo = data.membershipNo
})
)
},
updateDetails(data) {
return set(
produce((state: DetailsState) => {
state.rooms[idx].steps[StepEnum.details].isValid = true
const currentRoom = state.rooms[idx].room
currentRoom.guest.countryCode = data.countryCode
currentRoom.guest.email = data.email
currentRoom.guest.firstName = data.firstName
currentRoom.guest.join = data.join
currentRoom.guest.lastName = data.lastName
currentRoom.guest.phoneNumber = data.phoneNumber
currentRoom.guest.phoneNumberCC = data.phoneNumberCC
if (data.specialRequest?.comment) {
currentRoom.specialRequest.comment =
data.specialRequest.comment
}
if (data.join) {
currentRoom.guest.membershipNo = undefined
} else {
currentRoom.guest.membershipNo = data.membershipNo
}
// Only valid for room 1
if (idx === 0 && data.join && !isMember) {
if ("dateOfBirth" in currentRoom.guest) {
currentRoom.guest.dateOfBirth = data.dateOfBirth
}
if ("zipCode" in currentRoom.guest) {
currentRoom.guest.zipCode = data.zipCode
}
}
const isMemberAndRoomOne = idx === 0 && isMember
currentRoom.roomPrice = getRoomPrice(
currentRoom.roomRate,
Boolean(data.join || data.membershipNo || isMemberAndRoomOne)
)
const nights = dt(state.booking.toDate).diff(
state.booking.fromDate,
"days"
)
state.totalPrice = getTotalPrice(
state.rooms.map((r) => r.room),
isMember,
nights
)
const isAllStepsCompleted = checkRoomProgress(
state.rooms[idx].steps
)
if (isAllStepsCompleted) {
state.rooms[idx].isComplete = true
}
writeToSessionStorage({
booking: state.booking,
rooms: state.rooms,
})
})
)
},
},
room,
isComplete: false,
steps,
}
}),
searchParamString: searchParams,
totalPrice: initialTotalPrice,
vat: initialState.vat,
defaultCurrency: initialTotalPrice.local.currency,
preSubmitCallbacks: {},
actions: {
setIsSubmitting(isSubmitting) {
return set(
produce((state: DetailsState) => {
state.isSubmitting = isSubmitting
})
)
},
toggleSummaryOpen() {
return set(
produce((state: DetailsState) => {
state.isSummaryOpen = !state.isSummaryOpen
})
)
},
updateSeachParamString(searchParamString) {
return set(
produce((state: DetailsState) => {
state.searchParamString = searchParamString
})
)
},
addPreSubmitCallback(name, callback) {
return set(
produce((state: DetailsState) => {
state.preSubmitCallbacks[name] = callback
})
)
},
},
}))
}
export function useEnterDetailsStore<T>(selector: (store: DetailsState) => T) {
const store = useContext(DetailsContext)
if (!store) {
throw new Error("useEnterDetailsStore must be used within DetailsProvider")
}
return useStore(store, selector)
}