import { z } from "zod" import { Lang } from "@scandic-hotels/common/constants/language" import { bookingSearchTypes } from "@/constants/booking" import { parseSearchParams, serializeSearchParams } from "./searchParams" import type { BookingWidgetSearchData } from "@/types/components/bookingWidget" import type { DetailsBooking } from "@/types/components/hotelReservation/enterDetails/details" import type { SelectHotelBooking } from "@/types/components/hotelReservation/selectHotel/selectHotel" import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter" import type { Room, SelectRateBooking, } from "@/types/components/hotelReservation/selectRate/selectRate" import { BreakfastPackageEnum } from "@/types/enums/breakfast" import type { NextSearchParams } from "@/types/params" export function removeMultipleSlashes(pathname: string) { return pathname.replaceAll(/\/\/+/g, "/") } export function removeTrailingSlash(pathname: string) { if (pathname.endsWith("/")) { // Remove the trailing slash return pathname.slice(0, -1) } return pathname } type PartialRoom = { rooms?: Partial[] } export type SelectHotelParams = Omit & { 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 const adultsSchema = z.coerce.number().min(1).max(6).catch(0) const childAgeSchema = z.coerce.number().catch(-1) 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) { console.log("[URL] Error parsing search params for booking widget:", error) 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) { console.log("[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) { console.log("[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) { console.log("[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, }) } /** * Returns the TLD (top-level domain) for a given language. * @param lang - The language to get the TLD for * @returns The TLD for the given language */ export function getTldForLanguage(lang: Lang): string { switch (lang) { case Lang.sv: return "se" case Lang.no: return "no" case Lang.da: return "dk" case Lang.fi: return "fi" case Lang.de: return "de" default: return "com" } } /** * Constructs a URL with the correct TLD (top-level domain) based on lang, for current web. * @param params - Object containing path, lang, and baseUrl * @param params.path - The path to append to the URL * @param params.lang - The language to use for TLD * @param params.baseUrl - The base URL to use (e.g. https://www.scandichotels.com) * @returns The complete URL with language-specific TLD */ export function getCurrentWebUrl({ path, lang, baseUrl = "https://www.scandichotels.com", // Fallback for ephemeral environments (e.g. deploy previews). }: { path: string lang: Lang baseUrl?: string }): string { const tld = getTldForLanguage(lang) const url = new URL(path, baseUrl) if (tld !== "com") { url.host = url.host.replace(".com", `.${tld}`) } return url.toString() }