Files
web/packages/booking-flow/lib/utils/url.ts
Anton Gunnarsson 6ee262ad89 Merged in chore/booking-flow-cleaning (pull request #3354)
chore: Clean booking-flow

* Clean booking-flow

* Fix type issue


Approved-by: Joakim Jäderberg
Approved-by: Linus Flood
2025-12-17 13:04:26 +00:00

283 lines
7.3 KiB
TypeScript

import { z } from "zod"
import { logger } from "@scandic-hotels/common/logger"
import { BreakfastPackageEnum } from "@scandic-hotels/trpc/enums/breakfast"
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
import { type BookingWidgetSearchData } from "../components/BookingWidget"
import { type BookingSearchType, bookingSearchTypes } from "../misc/searchType"
import { parseSearchParams, serializeSearchParams } from "./searchParams"
import type { Child } from "@scandic-hotels/trpc/types/child"
import type { PackageEnum } from "@scandic-hotels/trpc/types/packages"
import type { NextSearchParams } from "../types"
import type { SelectRateBooking } from "../types/components/selectRate/selectRate"
type PartialRoom = { rooms?: Partial<Room>[] }
export type SelectHotelParams<T> = Omit<T, "hotel"> & {
hotelId: string
} & PartialRoom
export function searchParamsToRecord(searchParams: URLSearchParams) {
return Object.fromEntries(searchParams.entries())
}
const keyRenameMap = {
room: "rooms",
ratecode: "rateCode",
counterratecode: "counterRateCode",
roomtype: "roomTypeCode",
fromdate: "fromDate",
todate: "toDate",
hotel: "hotelId",
child: "childrenInRoom",
searchtype: "searchType",
}
const typeHints = {
filters: "COMMA_SEPARATED_ARRAY",
packages: "COMMA_SEPARATED_ARRAY",
} as const
export const adultsSchema = z.coerce.number().min(1).max(6).catch(0)
export const childAgeSchema = z.coerce.number().catch(-1)
export const childBedSchema = z.coerce.number().catch(-1)
const searchTypeSchema = z.enum(bookingSearchTypes).optional().catch(undefined)
export function parseBookingWidgetSearchParams(
searchParams: NextSearchParams
): BookingWidgetSearchData {
try {
const result = parseSearchParams(searchParams, {
keyRenameMap,
typeHints,
schema: z.object({
city: z.string().optional(),
hotelId: z.string().optional(),
fromDate: z.string().optional(),
toDate: z.string().optional(),
bookingCode: z.string().optional(),
searchType: searchTypeSchema,
rooms: z
.array(
z.object({
adults: adultsSchema,
childrenInRoom: z
.array(
z.object({
bed: childBedSchema,
age: childAgeSchema,
})
)
.optional()
.default([]),
})
)
.optional(),
}),
})
return result
} catch (error) {
logger.error(
"[URL] Error parsing search params for booking widget:",
error,
searchParams
)
return {}
}
}
export function parseSelectHotelSearchParams(
searchParams: NextSearchParams
): SelectHotelBooking | null {
try {
const result = parseSearchParams(searchParams, {
keyRenameMap,
typeHints,
schema: z.object({
city: z.string().optional(),
hotelId: z.string().optional(),
fromDate: z.string(),
toDate: z.string(),
bookingCode: z.string().optional(),
searchType: searchTypeSchema,
rooms: z.array(
z.object({
adults: adultsSchema,
childrenInRoom: z
.array(
z.object({
bed: childBedSchema,
age: childAgeSchema,
})
)
.optional(),
})
),
}),
})
return result
} catch (error) {
logger.error("[URL] Error parsing search params for select hotel:", error)
return null
}
}
export function parseSelectRateSearchParams(
searchParams: NextSearchParams
): SelectRateBooking | null {
try {
const result = parseSearchParams(searchParams, {
keyRenameMap,
typeHints,
schema: z.object({
city: z.string().optional(),
hotelId: z.string(),
fromDate: z.string(),
toDate: z.string(),
searchType: searchTypeSchema,
bookingCode: z.string().optional(),
rooms: z.array(
z.object({
adults: adultsSchema,
bookingCode: z.string().optional(),
counterRateCode: z.string().optional(),
rateCode: z.string().optional(),
roomTypeCode: z.string().optional(),
packages: z
.array(
z.nativeEnum({
...BreakfastPackageEnum,
...RoomPackageCodeEnum,
})
)
.optional(),
childrenInRoom: z
.array(
z.object({
bed: childBedSchema,
age: childAgeSchema,
})
)
.optional(),
})
),
}),
})
return result
} catch (error) {
logger.error("[URL] Error parsing search params for select rate:", error)
return null
}
}
export function parseDetailsSearchParams(
searchParams: NextSearchParams
): DetailsBooking | null {
const packageEnum = {
...BreakfastPackageEnum,
...RoomPackageCodeEnum,
} as const
try {
const result = parseSearchParams(searchParams, {
keyRenameMap,
typeHints,
schema: z.object({
city: z.string().optional(),
hotelId: z.string(),
fromDate: z.string(),
toDate: z.string(),
searchType: searchTypeSchema,
bookingCode: z.string().optional(),
rooms: z.array(
z.object({
adults: adultsSchema,
bookingCode: z.string().optional(),
counterRateCode: z.string().optional(),
rateCode: z.string(),
roomTypeCode: z.string(),
packages: z.array(z.nativeEnum(packageEnum)).optional(),
childrenInRoom: z
.array(
z.object({
bed: childBedSchema,
age: childAgeSchema,
})
)
.optional(),
})
),
}),
})
return result
} catch (error) {
logger.error("[URL] Error parsing search params for details:", error)
return null
}
}
const reversedKeyRenameMap = Object.fromEntries(
Object.entries(keyRenameMap).map(([key, value]) => [value, key])
)
export function serializeBookingSearchParams(
obj:
| BookingWidgetSearchData
| SelectHotelBooking
| SelectRateBooking
| DetailsBooking,
{ initialSearchParams }: { initialSearchParams?: URLSearchParams } = {}
) {
return serializeSearchParams(obj, {
keyRenameMap: reversedKeyRenameMap,
initialSearchParams,
typeHints,
})
}
export type DetailsBooking = {
hotelId: string
fromDate: string
toDate: string
city?: string
bookingCode?: string
searchType?: BookingSearchType
rooms: {
adults: number
rateCode: string
roomTypeCode: string
bookingCode?: string
childrenInRoom?: Child[]
counterRateCode?: string
packages?: PackageEnum[]
}[]
}
export type SelectHotelBooking = {
hotelId?: string
city?: string
fromDate: string
toDate: string
rooms: {
adults: number
childrenInRoom?: Child[]
}[]
bookingCode?: string
searchType?: BookingSearchType
}
export interface Room {
adults: number
childrenInRoom?: Child[]
bookingCode?: string | null
counterRateCode?: string | null
packages?: PackageEnum[] | null
rateCode?: string | null
roomTypeCode?: string | null
}