feat: add multiroom signup
This commit is contained in:
@@ -3,6 +3,8 @@ import { produce } from "immer"
|
||||
import { useContext } from "react"
|
||||
import { create, useStore } from "zustand"
|
||||
|
||||
import { dt } from "@/lib/dt"
|
||||
|
||||
import { DetailsContext } from "@/contexts/Details"
|
||||
|
||||
import {
|
||||
@@ -10,22 +12,20 @@ import {
|
||||
calcTotalPrice,
|
||||
checkRoomProgress,
|
||||
extractGuestFromUser,
|
||||
findNextInvalidStep,
|
||||
getRoomPrice,
|
||||
getTotalPrice,
|
||||
handleStepProgression,
|
||||
selectPreviousSteps,
|
||||
selectRoom,
|
||||
selectRoomStatus,
|
||||
writeToSessionStorage,
|
||||
} from "./helpers"
|
||||
|
||||
import type { BreakfastPackages } from "@/types/components/hotelReservation/enterDetails/breakfast"
|
||||
import { StepEnum } from "@/types/enums/step"
|
||||
import type {
|
||||
DetailsState,
|
||||
InitialState,
|
||||
RoomState,
|
||||
RoomStatus,
|
||||
RoomStep,
|
||||
} from "@/types/stores/enter-details"
|
||||
import type { SafeUser } from "@/types/user"
|
||||
|
||||
@@ -46,7 +46,8 @@ export const detailsStorageName = "rooms-details-storage"
|
||||
export function createDetailsStore(
|
||||
initialState: InitialState,
|
||||
searchParams: string,
|
||||
user: SafeUser
|
||||
user: SafeUser,
|
||||
breakfastPackages: BreakfastPackages | null
|
||||
) {
|
||||
const isMember = !!user
|
||||
|
||||
@@ -73,21 +74,6 @@ export function createDetailsStore(
|
||||
})
|
||||
|
||||
const rooms: RoomState[] = initialState.rooms.map((room, idx) => {
|
||||
return {
|
||||
...room,
|
||||
adults: initialState.booking.rooms[idx].adults,
|
||||
childrenInRoom: initialState.booking.rooms[idx].childrenInRoom,
|
||||
bedType: room.bedType,
|
||||
breakfast:
|
||||
initialState.breakfast === false ? initialState.breakfast : undefined,
|
||||
guest: isMember
|
||||
? deepmerge(defaultGuestState, extractGuestFromUser(user))
|
||||
: defaultGuestState,
|
||||
roomPrice: getRoomPrice(room.roomRate, isMember && idx === 0),
|
||||
}
|
||||
})
|
||||
|
||||
const roomStatuses: RoomStatus[] = initialState.rooms.map((room, idx) => {
|
||||
const steps: RoomStatus["steps"] = {
|
||||
[StepEnum.selectBed]: {
|
||||
step: StepEnum.selectBed,
|
||||
@@ -103,69 +89,112 @@ export function createDetailsStore(
|
||||
},
|
||||
}
|
||||
|
||||
if (initialState.breakfast === false) {
|
||||
if (room.breakfastIncluded || !breakfastPackages?.length) {
|
||||
delete steps[StepEnum.breakfast]
|
||||
}
|
||||
|
||||
const currentStep =
|
||||
idx === 0
|
||||
? Object.values(steps).find((step) => !step.isValid)?.step ??
|
||||
StepEnum.selectBed
|
||||
: null
|
||||
Object.values(steps).find((step) => !step.isValid)?.step ??
|
||||
StepEnum.selectBed
|
||||
|
||||
return {
|
||||
room: {
|
||||
...room,
|
||||
adults: initialState.booking.rooms[idx].adults,
|
||||
childrenInRoom: initialState.booking.rooms[idx].childrenInRoom,
|
||||
bedType: room.bedType,
|
||||
breakfast:
|
||||
!breakfastPackages?.length || room.breakfastIncluded
|
||||
? false
|
||||
: undefined,
|
||||
guest:
|
||||
isMember && idx === 0
|
||||
? deepmerge(defaultGuestState, extractGuestFromUser(user))
|
||||
: defaultGuestState,
|
||||
roomPrice: getRoomPrice(room.roomRate, isMember && idx === 0),
|
||||
},
|
||||
|
||||
currentStep,
|
||||
isComplete: false,
|
||||
currentStep: currentStep,
|
||||
lastCompletedStep: undefined,
|
||||
steps,
|
||||
}
|
||||
})
|
||||
|
||||
return create<DetailsState>()((set, get) => ({
|
||||
searchParamString: searchParams,
|
||||
return create<DetailsState>()((set) => ({
|
||||
activeRoom: 0,
|
||||
booking: initialState.booking,
|
||||
breakfast:
|
||||
initialState.breakfast === false ? initialState.breakfast : undefined,
|
||||
breakfastPackages,
|
||||
canProceedToPayment: false,
|
||||
isSubmittingDisabled: false,
|
||||
isSummaryOpen: false,
|
||||
isPriceDetailsModalOpen: false,
|
||||
lastRoom: initialState.booking.rooms.length - 1,
|
||||
rooms,
|
||||
searchParamString: searchParams,
|
||||
totalPrice: initialTotalPrice,
|
||||
vat: initialState.vat,
|
||||
rooms,
|
||||
bookingProgress: {
|
||||
currentRoomIndex: 0,
|
||||
roomStatuses,
|
||||
canProceedToPayment: false,
|
||||
},
|
||||
|
||||
actions: {
|
||||
setStep(step: StepEnum | null, roomIndex?: number) {
|
||||
if (step === null) {
|
||||
return
|
||||
}
|
||||
|
||||
return set(
|
||||
produce((state: DetailsState) => {
|
||||
const currentRoomIndex =
|
||||
roomIndex ?? state.bookingProgress.currentRoomIndex
|
||||
const previousSteps = selectPreviousSteps(state, roomIndex)
|
||||
const arePreviousStepsCompleted = Object.values(
|
||||
previousSteps
|
||||
).every((step: RoomStep) => step.isValid)
|
||||
const arePreviousRoomsCompleted = state.bookingProgress.roomStatuses
|
||||
.slice(0, currentRoomIndex)
|
||||
.every((room) => room.isComplete)
|
||||
const roomStatus = selectRoomStatus(state, roomIndex)
|
||||
|
||||
if (arePreviousRoomsCompleted && arePreviousStepsCompleted) {
|
||||
roomStatus.currentStep = step
|
||||
|
||||
if (roomIndex !== undefined) {
|
||||
state.bookingProgress.currentRoomIndex = roomIndex
|
||||
setStep(idx) {
|
||||
return function (step) {
|
||||
return set(
|
||||
produce((state: DetailsState) => {
|
||||
const isSameRoom = idx === state.activeRoom
|
||||
const room = state.rooms[idx]
|
||||
if (isSameRoom) {
|
||||
// Closed same accordion as was open
|
||||
if (step === room.currentStep) {
|
||||
if (room.isComplete) {
|
||||
// Room is complete, move to next room or payment
|
||||
const nextRoomIdx = state.rooms.findIndex(
|
||||
(r) => !r.isComplete
|
||||
)
|
||||
state.activeRoom = nextRoomIdx
|
||||
// Done, proceed to payment
|
||||
if (nextRoomIdx === -1) {
|
||||
room.currentStep = null
|
||||
} else {
|
||||
const nextRoom = state.rooms[nextRoomIdx]
|
||||
const nextInvalidStep = findNextInvalidStep(nextRoom)
|
||||
nextRoom.currentStep = nextInvalidStep
|
||||
}
|
||||
} else {
|
||||
room.currentStep = findNextInvalidStep(room)
|
||||
}
|
||||
} else {
|
||||
if (room.steps[step]?.isValid) {
|
||||
room.currentStep = step
|
||||
} else {
|
||||
room.currentStep = findNextInvalidStep(room)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const arePreviousRoomsCompleted = state.rooms
|
||||
.slice(0, idx)
|
||||
.every((room) => room.isComplete)
|
||||
if (arePreviousRoomsCompleted) {
|
||||
state.activeRoom = idx
|
||||
if (room.steps[step]?.isValid) {
|
||||
room.currentStep = step
|
||||
} else {
|
||||
room.currentStep = findNextInvalidStep(room)
|
||||
}
|
||||
} else {
|
||||
const firstIncompleteRoom = state.rooms.findIndex(
|
||||
(r) => !r.isComplete
|
||||
)
|
||||
state.activeRoom = firstIncompleteRoom
|
||||
if (firstIncompleteRoom === -1) {
|
||||
// All rooms are done, proceed to payment
|
||||
room.currentStep = null
|
||||
} else {
|
||||
const nextRoom = state.rooms[firstIncompleteRoom]
|
||||
nextRoom.currentStep = findNextInvalidStep(nextRoom)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
setIsSubmittingDisabled(isSubmittingDisabled) {
|
||||
return set(
|
||||
@@ -189,170 +218,240 @@ export function createDetailsStore(
|
||||
})
|
||||
)
|
||||
},
|
||||
togglePriceDetailsModalOpen() {
|
||||
return set(
|
||||
produce((state: DetailsState) => {
|
||||
state.isPriceDetailsModalOpen = !state.isPriceDetailsModalOpen
|
||||
})
|
||||
)
|
||||
},
|
||||
updateBedType(bedType) {
|
||||
return set(
|
||||
produce((state: DetailsState) => {
|
||||
const roomStatus = selectRoomStatus(state)
|
||||
roomStatus.steps[StepEnum.selectBed].isValid = true
|
||||
updateBedType(idx) {
|
||||
return function (bedType) {
|
||||
return set(
|
||||
produce((state: DetailsState) => {
|
||||
state.rooms[idx].steps[StepEnum.selectBed].isValid = true
|
||||
state.rooms[idx].room.bedType = bedType
|
||||
|
||||
const room = selectRoom(state)
|
||||
room.bedType = bedType
|
||||
handleStepProgression(state.rooms[idx], state)
|
||||
|
||||
handleStepProgression(state)
|
||||
|
||||
writeToSessionStorage({
|
||||
booking: state.booking,
|
||||
rooms: state.rooms,
|
||||
bookingProgress: state.bookingProgress,
|
||||
writeToSessionStorage({
|
||||
activeRoom: state.activeRoom,
|
||||
booking: state.booking,
|
||||
rooms: state.rooms,
|
||||
})
|
||||
})
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
updateBreakfast(breakfast) {
|
||||
return set(
|
||||
produce((state: DetailsState) => {
|
||||
const roomStatus = selectRoomStatus(state)
|
||||
if (roomStatus.steps[StepEnum.breakfast]) {
|
||||
roomStatus.steps[StepEnum.breakfast].isValid = true
|
||||
}
|
||||
updateBreakfast(idx) {
|
||||
return function (breakfast) {
|
||||
return set(
|
||||
produce((state: DetailsState) => {
|
||||
const currentRoom = state.rooms[idx]
|
||||
if (currentRoom.steps[StepEnum.breakfast]) {
|
||||
currentRoom.steps[StepEnum.breakfast].isValid = true
|
||||
}
|
||||
|
||||
const stateTotalRequestedPrice =
|
||||
state.totalPrice.requested?.price || 0
|
||||
const currentTotalPriceRequested = state.totalPrice.requested
|
||||
let stateTotalRequestedPrice = 0
|
||||
if (currentTotalPriceRequested) {
|
||||
stateTotalRequestedPrice = currentTotalPriceRequested.price
|
||||
}
|
||||
|
||||
const stateTotalLocalPrice = state.totalPrice.local.price
|
||||
const stateTotalLocalPrice = state.totalPrice.local.price
|
||||
|
||||
const addToTotalPrice =
|
||||
(state.breakfast === undefined || state.breakfast === false) &&
|
||||
!!breakfast
|
||||
const addToTotalPrice =
|
||||
(currentRoom.room.breakfast === undefined ||
|
||||
currentRoom.room.breakfast === false) &&
|
||||
!!breakfast
|
||||
|
||||
const subtractFromTotalPrice =
|
||||
(state.breakfast === undefined || state.breakfast) &&
|
||||
breakfast === false
|
||||
const subtractFromTotalPrice =
|
||||
currentRoom.room.breakfast && breakfast === false
|
||||
|
||||
if (addToTotalPrice) {
|
||||
const breakfastTotalRequestedPrice = parseInt(
|
||||
breakfast.requestedPrice.totalPrice
|
||||
)
|
||||
const breakfastTotalPrice = parseInt(
|
||||
breakfast.localPrice.totalPrice
|
||||
const nights = dt(state.booking.toDate).diff(
|
||||
state.booking.fromDate,
|
||||
"days"
|
||||
)
|
||||
|
||||
state.totalPrice = {
|
||||
requested: state.totalPrice.requested && {
|
||||
currency: state.totalPrice.requested.currency,
|
||||
price:
|
||||
stateTotalRequestedPrice + breakfastTotalRequestedPrice,
|
||||
},
|
||||
local: {
|
||||
currency: breakfast.localPrice.currency,
|
||||
price: stateTotalLocalPrice + breakfastTotalPrice,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (subtractFromTotalPrice) {
|
||||
let currency = state.totalPrice.local.currency
|
||||
let currentBreakfastTotalPrice = 0
|
||||
let currentBreakfastTotalRequestedPrice = 0
|
||||
if (state.breakfast) {
|
||||
currentBreakfastTotalPrice = parseInt(
|
||||
state.breakfast.localPrice.totalPrice
|
||||
)
|
||||
currentBreakfastTotalRequestedPrice = parseInt(
|
||||
state.breakfast.requestedPrice.totalPrice
|
||||
)
|
||||
currency = state.breakfast.localPrice.currency
|
||||
if (addToTotalPrice) {
|
||||
const breakfastTotalRequestedPrice =
|
||||
parseInt(breakfast.requestedPrice.price) *
|
||||
currentRoom.room.adults *
|
||||
nights
|
||||
const breakfastTotalPrice =
|
||||
parseInt(breakfast.localPrice.price) *
|
||||
currentRoom.room.adults *
|
||||
nights
|
||||
state.totalPrice = {
|
||||
requested: state.totalPrice.requested && {
|
||||
currency: state.totalPrice.requested.currency,
|
||||
price:
|
||||
stateTotalRequestedPrice + breakfastTotalRequestedPrice,
|
||||
},
|
||||
local: {
|
||||
currency: breakfast.localPrice.currency,
|
||||
price: stateTotalLocalPrice + breakfastTotalPrice,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let requestedPrice =
|
||||
stateTotalRequestedPrice - currentBreakfastTotalRequestedPrice
|
||||
if (requestedPrice < 0) {
|
||||
requestedPrice = 0
|
||||
}
|
||||
let localPrice = stateTotalLocalPrice - currentBreakfastTotalPrice
|
||||
if (localPrice < 0) {
|
||||
localPrice = 0
|
||||
if (subtractFromTotalPrice) {
|
||||
let currency = state.totalPrice.local.currency
|
||||
let currentBreakfastTotalPrice = 0
|
||||
let currentBreakfastTotalRequestedPrice = 0
|
||||
if (currentRoom.room.breakfast) {
|
||||
currentBreakfastTotalPrice =
|
||||
parseInt(currentRoom.room.breakfast.localPrice.price) *
|
||||
currentRoom.room.adults *
|
||||
nights
|
||||
currentBreakfastTotalRequestedPrice =
|
||||
parseInt(
|
||||
currentRoom.room.breakfast.requestedPrice.totalPrice
|
||||
) *
|
||||
currentRoom.room.adults *
|
||||
nights
|
||||
currency = currentRoom.room.breakfast.localPrice.currency
|
||||
}
|
||||
|
||||
let requestedPrice =
|
||||
stateTotalRequestedPrice - currentBreakfastTotalRequestedPrice
|
||||
if (requestedPrice < 0) {
|
||||
requestedPrice = 0
|
||||
}
|
||||
let localPrice =
|
||||
stateTotalLocalPrice - currentBreakfastTotalPrice
|
||||
if (localPrice < 0) {
|
||||
localPrice = 0
|
||||
}
|
||||
|
||||
state.totalPrice = {
|
||||
requested: state.totalPrice.requested && {
|
||||
currency: state.totalPrice.requested.currency,
|
||||
price: requestedPrice,
|
||||
},
|
||||
local: {
|
||||
currency,
|
||||
price: localPrice,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
state.totalPrice = {
|
||||
requested: state.totalPrice.requested && {
|
||||
currency: state.totalPrice.requested.currency,
|
||||
price: requestedPrice,
|
||||
},
|
||||
local: {
|
||||
currency,
|
||||
price: localPrice,
|
||||
},
|
||||
}
|
||||
}
|
||||
currentRoom.room.breakfast = breakfast
|
||||
|
||||
const room = selectRoom(state)
|
||||
room.breakfast = breakfast
|
||||
handleStepProgression(currentRoom, state)
|
||||
|
||||
handleStepProgression(state)
|
||||
|
||||
writeToSessionStorage({
|
||||
booking: state.booking,
|
||||
rooms: state.rooms,
|
||||
bookingProgress: state.bookingProgress,
|
||||
writeToSessionStorage({
|
||||
activeRoom: state.activeRoom,
|
||||
booking: state.booking,
|
||||
rooms: state.rooms,
|
||||
})
|
||||
})
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
updateDetails(data) {
|
||||
return set(
|
||||
produce((state: DetailsState) => {
|
||||
const roomStatus = selectRoomStatus(state)
|
||||
roomStatus.steps[StepEnum.details].isValid = true
|
||||
updateDetails(idx) {
|
||||
return function (data) {
|
||||
return set(
|
||||
produce((state: DetailsState) => {
|
||||
state.rooms[idx].steps[StepEnum.details].isValid = true
|
||||
|
||||
const room = selectRoom(state)
|
||||
room.guest.countryCode = data.countryCode
|
||||
room.guest.dateOfBirth = data.dateOfBirth
|
||||
room.guest.email = data.email
|
||||
room.guest.firstName = data.firstName
|
||||
room.guest.join = data.join
|
||||
room.guest.lastName = data.lastName
|
||||
state.rooms[idx].room.guest.countryCode = data.countryCode
|
||||
state.rooms[idx].room.guest.dateOfBirth = data.dateOfBirth
|
||||
state.rooms[idx].room.guest.email = data.email
|
||||
state.rooms[idx].room.guest.firstName = data.firstName
|
||||
state.rooms[idx].room.guest.join = data.join
|
||||
state.rooms[idx].room.guest.lastName = data.lastName
|
||||
|
||||
if (data.join) {
|
||||
room.guest.membershipNo = undefined
|
||||
} else {
|
||||
room.guest.membershipNo = data.membershipNo
|
||||
}
|
||||
room.guest.phoneNumber = data.phoneNumber
|
||||
room.guest.zipCode = data.zipCode
|
||||
if (data.join) {
|
||||
state.rooms[idx].room.guest.membershipNo = undefined
|
||||
} else {
|
||||
state.rooms[idx].room.guest.membershipNo = data.membershipNo
|
||||
}
|
||||
state.rooms[idx].room.guest.phoneNumber = data.phoneNumber
|
||||
state.rooms[idx].room.guest.zipCode = data.zipCode
|
||||
|
||||
room.roomPrice = getRoomPrice(
|
||||
room.roomRate,
|
||||
Boolean(data.join || data.membershipNo || isMember)
|
||||
)
|
||||
state.rooms[idx].room.roomPrice = getRoomPrice(
|
||||
state.rooms[idx].room.roomRate,
|
||||
Boolean(data.join || data.membershipNo || isMember)
|
||||
)
|
||||
|
||||
state.totalPrice = calcTotalPrice(
|
||||
state.rooms,
|
||||
state.totalPrice,
|
||||
isMember
|
||||
)
|
||||
const nights = dt(state.booking.toDate).diff(
|
||||
state.booking.fromDate,
|
||||
"days"
|
||||
)
|
||||
|
||||
const isAllStepsCompleted = checkRoomProgress(state)
|
||||
if (isAllStepsCompleted) {
|
||||
roomStatus.isComplete = true
|
||||
}
|
||||
state.totalPrice = calcTotalPrice(
|
||||
state.rooms,
|
||||
state.totalPrice.local.currency,
|
||||
isMember,
|
||||
nights
|
||||
)
|
||||
|
||||
handleStepProgression(state)
|
||||
const isAllStepsCompleted = checkRoomProgress(
|
||||
state.rooms[idx].steps
|
||||
)
|
||||
if (isAllStepsCompleted) {
|
||||
state.rooms[idx].isComplete = true
|
||||
}
|
||||
|
||||
writeToSessionStorage({
|
||||
booking: state.booking,
|
||||
rooms: state.rooms,
|
||||
bookingProgress: state.bookingProgress,
|
||||
handleStepProgression(state.rooms[idx], state)
|
||||
|
||||
writeToSessionStorage({
|
||||
activeRoom: state.activeRoom,
|
||||
booking: state.booking,
|
||||
rooms: state.rooms,
|
||||
})
|
||||
})
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
updateMultiroomDetails(idx) {
|
||||
return function (data) {
|
||||
return set(
|
||||
produce((state: DetailsState) => {
|
||||
state.rooms[idx].steps[StepEnum.details].isValid = true
|
||||
|
||||
state.rooms[idx].room.guest.countryCode = data.countryCode
|
||||
state.rooms[idx].room.guest.email = data.email
|
||||
state.rooms[idx].room.guest.firstName = data.firstName
|
||||
state.rooms[idx].room.guest.join = data.join
|
||||
state.rooms[idx].room.guest.lastName = data.lastName
|
||||
|
||||
if (data.join) {
|
||||
state.rooms[idx].room.guest.membershipNo = undefined
|
||||
} else {
|
||||
state.rooms[idx].room.guest.membershipNo = data.membershipNo
|
||||
}
|
||||
state.rooms[idx].room.guest.phoneNumber = data.phoneNumber
|
||||
|
||||
const getMemberPrice = Boolean(data.join || data.membershipNo)
|
||||
state.rooms[idx].room.roomPrice = getRoomPrice(
|
||||
state.rooms[idx].room.roomRate,
|
||||
getMemberPrice
|
||||
)
|
||||
|
||||
const nights = dt(state.booking.toDate).diff(
|
||||
state.booking.fromDate,
|
||||
"days"
|
||||
)
|
||||
|
||||
state.totalPrice = calcTotalPrice(
|
||||
state.rooms,
|
||||
state.totalPrice.local.currency,
|
||||
getMemberPrice,
|
||||
nights
|
||||
)
|
||||
|
||||
const isAllStepsCompleted = checkRoomProgress(
|
||||
state.rooms[idx].steps
|
||||
)
|
||||
if (isAllStepsCompleted) {
|
||||
state.rooms[idx].isComplete = true
|
||||
}
|
||||
|
||||
handleStepProgression(state.rooms[idx], state)
|
||||
|
||||
writeToSessionStorage({
|
||||
activeRoom: state.activeRoom,
|
||||
booking: state.booking,
|
||||
rooms: state.rooms,
|
||||
})
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
updateSeachParamString(searchParamString) {
|
||||
return set(
|
||||
|
||||
Reference in New Issue
Block a user