Merged in feat/enter-details-multiroom (pull request #1280)
feat(SW-1259): Enter details multiroom * refactor: remove per-step URLs * WIP: map multiroom data * fix: lint errors in details page * fix: made useEnterDetailsStore tests pass * fix: WIP refactor enter details store * fix: WIP enter details store update * fix: added room index to select correct room * fix: added logic for navigating between steps and rooms * fix: update summary to work with store changes * fix: added room and total price calculation * fix: removed unused code and added test for breakfast included * refactor: move store selectors into helpers * refactor: session storage state for multiroom booking * feat: update enter details accordion navigation * fix: added room index to each form component so they select correct room * fix: added unique id to input to handle case when multiple inputs have same name * fix: update payment step with store changes * fix: rebase issues * fix: now you should only be able to go to a step if previous room is completed * refactor: cleanup * fix: if no availability just skip that room for now * fix: add select-rate Summary and adjust typings Approved-by: Arvid Norlin
This commit is contained in:
committed by
Arvid Norlin
parent
f43ee4a0e6
commit
b394d54c3f
@@ -3,136 +3,74 @@ import { useEffect, useRef } from "react"
|
||||
|
||||
import { createDetailsStore } from "@/stores/enter-details"
|
||||
import {
|
||||
calcTotalMemberPrice,
|
||||
calcTotalPublicPrice,
|
||||
navigate,
|
||||
writeToSessionStorage,
|
||||
checkIsSameRoom,
|
||||
clearSessionStorage,
|
||||
readFromSessionStorage,
|
||||
} from "@/stores/enter-details/helpers"
|
||||
|
||||
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 { DetailsContext } from "@/contexts/Details"
|
||||
|
||||
import type { DetailsStore } from "@/types/contexts/enter-details"
|
||||
import { StepEnum } from "@/types/enums/step"
|
||||
import type { DetailsProviderProps } from "@/types/providers/enter-details"
|
||||
import type { DetailsState, InitialState } from "@/types/stores/enter-details"
|
||||
import type { InitialState } from "@/types/stores/enter-details"
|
||||
|
||||
export default function EnterDetailsProvider({
|
||||
bedTypes,
|
||||
booking,
|
||||
showBreakfastStep,
|
||||
children,
|
||||
packages,
|
||||
roomRate,
|
||||
roomsData,
|
||||
searchParamsStr,
|
||||
step,
|
||||
user,
|
||||
vat,
|
||||
}: DetailsProviderProps) {
|
||||
const storeRef = useRef<DetailsStore>()
|
||||
|
||||
if (!storeRef.current) {
|
||||
const initialData: InitialState = { booking, packages, roomRate, vat }
|
||||
if (bedTypes.length === 1) {
|
||||
initialData.bedType = {
|
||||
description: bedTypes[0].description,
|
||||
roomTypeCode: bedTypes[0].value,
|
||||
}
|
||||
const initialData: InitialState = {
|
||||
booking,
|
||||
rooms: roomsData
|
||||
.filter((r) => r.bedTypes?.length) // TODO: how to handle room without bedtypes?
|
||||
.map((room) => ({
|
||||
roomFeatures: room.packages,
|
||||
roomRate: room.roomRate,
|
||||
roomType: room.roomType,
|
||||
cancellationText: room.cancellationText,
|
||||
rateDetails: room.rateDetails,
|
||||
bedType:
|
||||
room.bedTypes?.length === 1
|
||||
? {
|
||||
roomTypeCode: room.bedTypes[0].value,
|
||||
description: room.bedTypes[0].description,
|
||||
}
|
||||
: undefined,
|
||||
})),
|
||||
vat,
|
||||
}
|
||||
|
||||
if (!showBreakfastStep) {
|
||||
initialData.breakfast = false
|
||||
}
|
||||
|
||||
storeRef.current = createDetailsStore(
|
||||
initialData,
|
||||
step,
|
||||
searchParamsStr,
|
||||
user
|
||||
)
|
||||
storeRef.current = createDetailsStore(initialData, searchParamsStr, user)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (storeRef.current) {
|
||||
storeRef.current.setState((state) => {
|
||||
const newState: DetailsState = { ...state }
|
||||
|
||||
newState.bedType = state.formValues.bedType
|
||||
newState.breakfast = state.formValues.breakfast
|
||||
|
||||
if (state.formValues.guest && !user) {
|
||||
newState.guest = state.formValues.guest
|
||||
}
|
||||
|
||||
if (
|
||||
(newState.guest!.join || newState.guest!.membershipNo || user) &&
|
||||
state.roomRate.memberRate
|
||||
) {
|
||||
const memberPrice = calcTotalMemberPrice(newState)
|
||||
newState.roomPrice = memberPrice.roomPrice
|
||||
newState.totalPrice = memberPrice.totalPrice
|
||||
} else {
|
||||
const publicPrice = calcTotalPublicPrice(newState)
|
||||
newState.roomPrice = publicPrice.roomPrice
|
||||
newState.totalPrice = publicPrice.totalPrice
|
||||
}
|
||||
|
||||
const isValid = { ...newState.isValid }
|
||||
|
||||
const validateBooking = state.formValues
|
||||
const validPaths = [StepEnum.selectBed]
|
||||
const validatedBedType = bedTypeSchema.safeParse(validateBooking)
|
||||
if (validatedBedType.success) {
|
||||
isValid[StepEnum.selectBed] = true
|
||||
validPaths.push(state.steps[1])
|
||||
}
|
||||
|
||||
const validatedBreakfast =
|
||||
breakfastStoreSchema.safeParse(validateBooking)
|
||||
if (validatedBreakfast.success) {
|
||||
isValid[StepEnum.breakfast] = true
|
||||
validPaths.push(StepEnum.details)
|
||||
}
|
||||
|
||||
const detailsSchema = user ? signedInDetailsSchema : guestDetailsSchema
|
||||
const validatedDetails = detailsSchema.safeParse(validateBooking.guest)
|
||||
/**
|
||||
* Need to add the breakfast check here too since
|
||||
* when a member comes into the flow, their data is
|
||||
* already added and valid, and thus to avoid showing a
|
||||
* step the user hasn't been on yet as complete
|
||||
*/
|
||||
if (isValid.breakfast && validatedDetails.success) {
|
||||
isValid[StepEnum.details] = true
|
||||
validPaths.push(StepEnum.payment)
|
||||
}
|
||||
|
||||
if (!validPaths.includes(newState.currentStep!)) {
|
||||
newState.currentStep = validPaths.at(-1)!
|
||||
}
|
||||
|
||||
if (step !== newState.currentStep) {
|
||||
const stateCurrentStep = newState.currentStep!
|
||||
setTimeout(() => {
|
||||
navigate(stateCurrentStep, searchParamsStr)
|
||||
})
|
||||
}
|
||||
|
||||
writeToSessionStorage({
|
||||
bedType: newState.bedType,
|
||||
booking: newState.booking,
|
||||
breakfast: newState.breakfast,
|
||||
guest: newState.guest,
|
||||
})
|
||||
|
||||
return { ...newState, isValid }
|
||||
})
|
||||
const storedValues = readFromSessionStorage()
|
||||
if (!storedValues) {
|
||||
return
|
||||
}
|
||||
}, [searchParamsStr, step, user])
|
||||
const isSameRoom = checkIsSameRoom(storedValues.booking, booking)
|
||||
if (!isSameRoom) {
|
||||
clearSessionStorage()
|
||||
return
|
||||
}
|
||||
|
||||
const state = storeRef.current?.getState()
|
||||
storeRef.current?.setState({
|
||||
...state,
|
||||
rooms: storedValues.rooms,
|
||||
bookingProgress: storedValues.bookingProgress,
|
||||
})
|
||||
}, [booking])
|
||||
|
||||
return (
|
||||
<DetailsContext.Provider value={storeRef.current}>
|
||||
|
||||
Reference in New Issue
Block a user