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
@@ -1,14 +1,21 @@
|
||||
import type { z } from "zod"
|
||||
|
||||
import type { Product } from "@/types/trpc/routers/hotel/roomAvailability"
|
||||
import type { SafeUser } from "@/types/user"
|
||||
import type {
|
||||
guestDetailsSchema,
|
||||
signedInDetailsSchema,
|
||||
} from "@/components/HotelReservation/EnterDetails/Details/schema"
|
||||
import type { Price } from "../price"
|
||||
|
||||
export type DetailsSchema = z.output<typeof guestDetailsSchema>
|
||||
export type SignedInDetailsSchema = z.output<typeof signedInDetailsSchema>
|
||||
|
||||
export interface RoomPrice {
|
||||
perNight: Price
|
||||
perStay: Price
|
||||
}
|
||||
|
||||
type MemberPrice = {
|
||||
currency: string
|
||||
price: number
|
||||
@@ -23,3 +30,8 @@ export type JoinScandicFriendsCardProps = {
|
||||
name: string
|
||||
memberPrice?: MemberPrice
|
||||
}
|
||||
|
||||
export type RoomRate = {
|
||||
publicRate: Product["productType"]["public"]
|
||||
memberRate?: Product["productType"]["member"]
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { RoomConfiguration } from "@/types/trpc/routers/hotel/roomAvailabil
|
||||
|
||||
export interface SelectedRoomProps {
|
||||
hotelId: string
|
||||
roomType: string
|
||||
roomTypeCode: string
|
||||
rateDescription: string
|
||||
room: RoomConfiguration
|
||||
}
|
||||
|
||||
9
types/components/hotelReservation/price.ts
Normal file
9
types/components/hotelReservation/price.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
interface TPrice {
|
||||
currency: string
|
||||
price: number
|
||||
}
|
||||
|
||||
export interface Price {
|
||||
requested: TPrice | undefined
|
||||
local: TPrice
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Price } from "@/types/stores/enter-details"
|
||||
import type { RoomsAvailability } from "@/types/trpc/routers/hotel/roomAvailability"
|
||||
import type { Price } from "../price"
|
||||
import type { RoomPackages } from "./roomFilter"
|
||||
import type { SelectRateSearchParams } from "./selectRate"
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ export interface DetailsProps extends SectionProps {}
|
||||
|
||||
export interface PaymentProps {
|
||||
user: SafeUser
|
||||
roomPrice: { publicPrice: number; memberPrice: number | undefined }
|
||||
otherPaymentOptions: PaymentMethodEnum[]
|
||||
mustBeGuaranteed: boolean
|
||||
supportedCards: PaymentMethodEnum[]
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { StepEnum } from "@/types/enums/step"
|
||||
import type { StepEnum } from "@/types/enums/step"
|
||||
|
||||
export interface SectionAccordionProps {
|
||||
header: string
|
||||
label: string
|
||||
step: StepEnum
|
||||
roomIndex: number
|
||||
}
|
||||
|
||||
@@ -1,50 +1,46 @@
|
||||
import type { DetailsProviderProps } from "@/types/providers/enter-details"
|
||||
import type { Packages } from "@/types/requests/packages"
|
||||
import type {
|
||||
DetailsState,
|
||||
Price,
|
||||
RoomPrice,
|
||||
} from "@/types/stores/enter-details"
|
||||
import type { RoomAvailability } from "@/types/trpc/routers/hotel/roomAvailability"
|
||||
import type { BedTypeSchema } from "./enterDetails/bedType"
|
||||
import type { BreakfastPackage } from "./enterDetails/breakfast"
|
||||
import type { DetailsSchema } from "./enterDetails/details"
|
||||
import type { RoomState } from "@/types/stores/enter-details"
|
||||
import type { RoomPrice, RoomRate } from "./enterDetails/details"
|
||||
import type { Price } from "./price"
|
||||
import type { Child, SelectRateSearchParams } from "./selectRate/selectRate"
|
||||
|
||||
export type RoomsData = Pick<DetailsState, "roomPrice"> &
|
||||
Pick<RoomAvailability, "cancellationText" | "rateDetails"> &
|
||||
Pick<RoomAvailability["selectedRoom"], "roomType"> & {
|
||||
adults: number
|
||||
children?: Child[]
|
||||
packages: Packages | null
|
||||
}
|
||||
export type RoomsData = {
|
||||
rateDetails: string[] | undefined
|
||||
roomType: string
|
||||
cancellationText: string
|
||||
roomPrice: RoomPrice
|
||||
adults: number
|
||||
children?: Child[]
|
||||
packages: Packages | null
|
||||
}
|
||||
|
||||
export interface SummaryProps
|
||||
extends Pick<RoomAvailability, "cancellationText" | "rateDetails">,
|
||||
Pick<RoomAvailability["selectedRoom"], "roomType"> {
|
||||
export interface SummaryProps {
|
||||
isMember: boolean
|
||||
breakfastIncluded: boolean
|
||||
}
|
||||
|
||||
export interface SummaryUIProps {
|
||||
booking: SelectRateSearchParams
|
||||
isMember: boolean
|
||||
totalPrice: Price
|
||||
toggleSummaryOpen: () => void
|
||||
togglePriceDetailsModalOpen: () => void
|
||||
vat: number
|
||||
}
|
||||
|
||||
export interface EnterDetailsSummaryProps extends SummaryUIProps {
|
||||
breakfastIncluded: boolean
|
||||
rooms: RoomState[]
|
||||
}
|
||||
|
||||
export interface SelectRateSummaryProps extends SummaryUIProps {
|
||||
rooms: {
|
||||
adults: number
|
||||
childrenInRoom: Child[] | undefined
|
||||
bedType: BedTypeSchema | undefined
|
||||
breakfast: BreakfastPackage | false | undefined
|
||||
guest: DetailsSchema | undefined
|
||||
roomRate: DetailsProviderProps["roomRate"]
|
||||
roomPrice: RoomPrice
|
||||
roomType: string
|
||||
roomPrice: RoomPrice
|
||||
roomRate: RoomRate
|
||||
rateDetails: string[] | undefined
|
||||
cancellationText: string
|
||||
}[]
|
||||
isMember: boolean
|
||||
breakfastIncluded: boolean
|
||||
packages: Packages | null
|
||||
totalPrice: Price
|
||||
vat: number
|
||||
toggleSummaryOpen: () => void
|
||||
togglePriceDetailsModalOpen: () => void
|
||||
}
|
||||
|
||||
@@ -2,5 +2,4 @@ export enum StepEnum {
|
||||
selectBed = "select-bed",
|
||||
breakfast = "breakfast",
|
||||
details = "details",
|
||||
payment = "payment",
|
||||
}
|
||||
|
||||
@@ -2,17 +2,15 @@ import type { BedTypeSelection } from "@/types/components/hotelReservation/enter
|
||||
import type { StepEnum } from "@/types/enums/step"
|
||||
import type { RoomAvailability } from "@/types/trpc/routers/hotel/roomAvailability"
|
||||
import type { SafeUser } from "@/types/user"
|
||||
import type { RoomData } from "@/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page"
|
||||
import type { SelectRateSearchParams } from "../components/hotelReservation/selectRate/selectRate"
|
||||
import type { Packages } from "../requests/packages"
|
||||
|
||||
export interface DetailsProviderProps extends React.PropsWithChildren {
|
||||
booking: SelectRateSearchParams
|
||||
bedTypes: BedTypeSelection[]
|
||||
showBreakfastStep: boolean
|
||||
packages: Packages | null
|
||||
roomRate: Pick<RoomAvailability, "memberRate" | "publicRate">
|
||||
roomsData: RoomData[]
|
||||
searchParamsStr: string
|
||||
step: StepEnum
|
||||
user: SafeUser
|
||||
vat: number
|
||||
}
|
||||
|
||||
@@ -2,41 +2,47 @@ import type { BedTypeSchema } from "@/types/components/hotelReservation/enterDet
|
||||
import type { BreakfastPackage } from "@/types/components/hotelReservation/enterDetails/breakfast"
|
||||
import type {
|
||||
DetailsSchema,
|
||||
RoomPrice,
|
||||
RoomRate,
|
||||
SignedInDetailsSchema,
|
||||
} from "@/types/components/hotelReservation/enterDetails/details"
|
||||
import type { StepEnum } from "@/types/enums/step"
|
||||
import type { SelectRateSearchParams } from "../components/hotelReservation/selectRate/selectRate"
|
||||
import type { DetailsProviderProps } from "../providers/enter-details"
|
||||
import type { Price } from "../components/hotelReservation/price"
|
||||
import type {
|
||||
Child,
|
||||
SelectRateSearchParams,
|
||||
} from "../components/hotelReservation/selectRate/selectRate"
|
||||
import type { Packages } from "../requests/packages"
|
||||
|
||||
interface TPrice {
|
||||
currency: string
|
||||
price: number
|
||||
export interface InitialRoomData {
|
||||
roomRate: RoomRate
|
||||
roomType: string
|
||||
rateDetails: string[] | undefined
|
||||
cancellationText: string
|
||||
roomFeatures: Packages | null
|
||||
bedType?: BedTypeSchema // used when there is only one bedtype to preselect it
|
||||
}
|
||||
|
||||
export interface RoomPrice {
|
||||
perNight: Price
|
||||
perStay: Price
|
||||
}
|
||||
|
||||
export interface Price {
|
||||
requested: TPrice | undefined
|
||||
local: TPrice
|
||||
}
|
||||
|
||||
export interface FormValues {
|
||||
export interface RoomState extends InitialRoomData {
|
||||
adults: number
|
||||
childrenInRoom: Child[] | undefined
|
||||
bedType: BedTypeSchema | undefined
|
||||
booking: SelectRateSearchParams
|
||||
breakfast: BreakfastPackage | false | undefined
|
||||
guest: DetailsSchema | SignedInDetailsSchema
|
||||
roomPrice: RoomPrice
|
||||
}
|
||||
|
||||
export type InitialState = {
|
||||
booking: SelectRateSearchParams
|
||||
vat: number
|
||||
rooms: InitialRoomData[]
|
||||
breakfast?: false
|
||||
}
|
||||
|
||||
export interface DetailsState {
|
||||
actions: {
|
||||
completeStep: () => void
|
||||
navigate: (step: StepEnum) => void
|
||||
setStep: (step: StepEnum | null, roomIndex?: number) => void
|
||||
setIsSubmittingDisabled: (isSubmittingDisabled: boolean) => void
|
||||
setStep: (step: StepEnum) => void
|
||||
setTotalPrice: (totalPrice: Price) => void
|
||||
toggleSummaryOpen: () => void
|
||||
togglePriceDetailsModalOpen: () => void
|
||||
@@ -45,40 +51,42 @@ export interface DetailsState {
|
||||
updateDetails: (data: DetailsSchema) => void
|
||||
updateSeachParamString: (searchParamString: string) => void
|
||||
}
|
||||
bedType: BedTypeSchema | undefined
|
||||
booking: SelectRateSearchParams
|
||||
breakfast: BreakfastPackage | false | undefined
|
||||
currentStep: StepEnum
|
||||
formValues: FormValues
|
||||
guest: DetailsSchema
|
||||
isSubmittingDisabled: boolean
|
||||
isSummaryOpen: boolean
|
||||
isPriceDetailsModalOpen: boolean
|
||||
isValid: Record<StepEnum, boolean>
|
||||
packages: Packages | null
|
||||
roomRate: DetailsProviderProps["roomRate"]
|
||||
roomPrice: RoomPrice
|
||||
steps: StepEnum[]
|
||||
rooms: RoomState[]
|
||||
totalPrice: Price
|
||||
searchParamString: string
|
||||
vat: number
|
||||
bookingProgress: BookingProgress
|
||||
}
|
||||
|
||||
export type InitialState = Pick<DetailsState, "booking" | "packages"> &
|
||||
Pick<DetailsProviderProps, "roomRate" | "vat"> & {
|
||||
bedType?: BedTypeSchema
|
||||
breakfast?: false
|
||||
export type PersistedState = {
|
||||
booking: SelectRateSearchParams
|
||||
bookingProgress: BookingProgress
|
||||
rooms: RoomState[]
|
||||
}
|
||||
|
||||
export type RoomStep = {
|
||||
step: StepEnum
|
||||
isValid: boolean
|
||||
}
|
||||
|
||||
export type RoomStatus = {
|
||||
isComplete: boolean
|
||||
currentStep: StepEnum | null
|
||||
lastCompletedStep: StepEnum | undefined
|
||||
steps: {
|
||||
[StepEnum.selectBed]: RoomStep
|
||||
[StepEnum.breakfast]?: RoomStep
|
||||
[StepEnum.details]: RoomStep
|
||||
}
|
||||
}
|
||||
|
||||
export type RoomRate = DetailsProviderProps["roomRate"]
|
||||
|
||||
export type PersistedState = Pick<
|
||||
DetailsState,
|
||||
"bedType" | "booking" | "breakfast" | "guest"
|
||||
>
|
||||
|
||||
export type PersistedStatePart =
|
||||
| Pick<DetailsState, "bedType">
|
||||
| Pick<DetailsState, "booking">
|
||||
| Pick<DetailsState, "breakfast">
|
||||
| Pick<DetailsState, "guest">
|
||||
export type BookingProgress = {
|
||||
currentRoomIndex: number
|
||||
roomStatuses: RoomStatus[]
|
||||
canProceedToPayment: boolean
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user