feat(SW-2068): Link to Correct Public URL from within My Stays Overview Page * feat(SW-2068): Link to Correct Public URL from within My Stays Overview Page - Added language parameter to previous and upcoming stays queries. - Updated Client components to utilize the new language hook. - Refactored EmptyUpcomingStays component to dynamically generate links based on the current language. - Adjusted user input validation to include optional language parameter. * refactor(SW-2068): Update EmptyUpcomingStays components to use PUBLIC_URL + add utility to handle TLD based on language * chore(SW-2068): Clarify TLD * feat(SW-2068): documentation for getTldForLanguage * refactor(SW-2068): Simplify booking URL construction in updateStaysBookingUrl * refactor(SW-2068): Remove incorrect TLD update logic from booking URL construction * refactor(SW-2068): Centralize booking URL paths using myBookingPath constant * refactor(SW-2068): Streamline search params in booking URL construction logic in updateStaysBookingUrl Approved-by: Christian Andolf
210 lines
6.0 KiB
TypeScript
210 lines
6.0 KiB
TypeScript
import { Lang } from "@/constants/languages"
|
|
import { env } from "@/env/server"
|
|
|
|
import type { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
|
import type {
|
|
Child,
|
|
Room,
|
|
} from "@/types/components/hotelReservation/selectRate/selectRate"
|
|
|
|
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<Room>[] }
|
|
|
|
const keyedSearchParams = new Map([
|
|
["room", "rooms"],
|
|
["ratecode", "rateCode"],
|
|
["counterratecode", "counterRateCode"],
|
|
["roomtype", "roomTypeCode"],
|
|
["fromdate", "fromDate"],
|
|
["todate", "toDate"],
|
|
["hotel", "hotelId"],
|
|
["child", "childrenInRoom"],
|
|
["searchtype", "searchType"],
|
|
])
|
|
|
|
export type SelectHotelParams<T> = Omit<T, "hotel"> & {
|
|
hotelId: string
|
|
} & PartialRoom
|
|
|
|
export function getKeyFromSearchParam(key: string): string {
|
|
return keyedSearchParams.get(key) || key
|
|
}
|
|
|
|
export function getSearchParamFromKey(key: string): string {
|
|
for (const [mapKey, mapValue] of keyedSearchParams.entries()) {
|
|
if (mapValue === key) {
|
|
return mapKey
|
|
}
|
|
}
|
|
return key
|
|
}
|
|
|
|
export function searchParamsToRecord(searchParams: URLSearchParams) {
|
|
return Object.fromEntries(searchParams.entries())
|
|
}
|
|
|
|
export function convertSearchParamsToObj<T extends PartialRoom>(
|
|
searchParams: Record<string, string>
|
|
) {
|
|
const searchParamsObject = Object.entries(searchParams).reduce<
|
|
SelectHotelParams<T>
|
|
>((acc, [key, value]) => {
|
|
// The params are sometimes indexed with a number (for ex: `room[0].adults`),
|
|
// so we need to split them by . or []
|
|
const keys = key.replace(/\]/g, "").split(/\[|\./)
|
|
const firstKey = getKeyFromSearchParam(keys[0])
|
|
|
|
// Room is a special case since it is an array, so we need to handle it separately
|
|
if (firstKey === "rooms") {
|
|
// Rooms are always indexed with a number, so we need to extract the index
|
|
const index = Number(keys[1])
|
|
const roomObject =
|
|
acc.rooms && Array.isArray(acc.rooms) ? acc.rooms : (acc.rooms = [])
|
|
|
|
const roomObjectKey = getKeyFromSearchParam(keys[2]) as keyof Room
|
|
|
|
if (!roomObject[index]) {
|
|
roomObject[index] = {}
|
|
}
|
|
|
|
// Adults should be converted to a number
|
|
if (roomObjectKey === "adults") {
|
|
roomObject[index].adults = Number(value)
|
|
|
|
// Child is an array, so we need to handle it separately
|
|
} else if (roomObjectKey === "childrenInRoom") {
|
|
const childIndex = Number(keys[3])
|
|
const childKey = keys[4] as keyof Child
|
|
|
|
if (
|
|
!("childrenInRoom" in roomObject[index]) ||
|
|
!Array.isArray(roomObject[index].childrenInRoom)
|
|
) {
|
|
roomObject[index].childrenInRoom = []
|
|
}
|
|
|
|
roomObject[index].childrenInRoom![childIndex] = {
|
|
...roomObject[index].childrenInRoom![childIndex],
|
|
[childKey]: Number(value),
|
|
}
|
|
} else if (roomObjectKey === "packages") {
|
|
roomObject[index].packages = value.split(",") as RoomPackageCodeEnum[]
|
|
} else {
|
|
roomObject[index][roomObjectKey] = value
|
|
}
|
|
} else {
|
|
return { ...acc, [firstKey]: value }
|
|
}
|
|
|
|
return acc
|
|
}, {} as SelectHotelParams<T>)
|
|
|
|
return searchParamsObject
|
|
}
|
|
|
|
export function convertObjToSearchParams<T>(
|
|
bookingData: T & PartialRoom,
|
|
intitalSearchParams = {} as URLSearchParams
|
|
) {
|
|
const bookingSearchParams = new URLSearchParams(intitalSearchParams)
|
|
Object.entries(bookingData).forEach(([key, value]) => {
|
|
if (key === "rooms") {
|
|
value.forEach((item, index) => {
|
|
if (item?.adults) {
|
|
bookingSearchParams.set(
|
|
`room[${index}].adults`,
|
|
item.adults.toString()
|
|
)
|
|
}
|
|
if (item?.childrenInRoom) {
|
|
item.childrenInRoom.forEach((child, childIndex) => {
|
|
bookingSearchParams.set(
|
|
`room[${index}].child[${childIndex}].age`,
|
|
child.age.toString()
|
|
)
|
|
bookingSearchParams.set(
|
|
`room[${index}].child[${childIndex}].bed`,
|
|
child.bed.toString()
|
|
)
|
|
})
|
|
}
|
|
if (item?.roomTypeCode) {
|
|
bookingSearchParams.set(`room[${index}].roomtype`, item.roomTypeCode)
|
|
}
|
|
if (item?.rateCode) {
|
|
bookingSearchParams.set(`room[${index}].ratecode`, item.rateCode)
|
|
}
|
|
|
|
if (item?.counterRateCode) {
|
|
bookingSearchParams.set(
|
|
`room[${index}].counterratecode`,
|
|
item.counterRateCode
|
|
)
|
|
}
|
|
|
|
if (item.packages && item.packages.length > 0) {
|
|
bookingSearchParams.set(
|
|
`room[${index}].packages`,
|
|
item.packages.join(",")
|
|
)
|
|
}
|
|
})
|
|
} else {
|
|
bookingSearchParams.set(getSearchParamFromKey(key), value.toString())
|
|
}
|
|
})
|
|
|
|
return bookingSearchParams
|
|
}
|
|
|
|
/**
|
|
* 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 path - The path to append to the URL
|
|
* @param lang - The language to use for TLD
|
|
* @returns The complete URL with language-specific TLD
|
|
*/
|
|
export function getCurrentWebUrl(path: string, lang: Lang): string {
|
|
const baseUrl = env.PUBLIC_URL || "https://www.scandichotels.com" // Fallback for ephemeral environments (e.g. deploy previews).
|
|
const tld = getTldForLanguage(lang)
|
|
const url = new URL(path, baseUrl)
|
|
|
|
if (tld !== "com") {
|
|
url.host = url.host.replace(".com", `.${tld}`)
|
|
}
|
|
|
|
return url.toString()
|
|
}
|