Merged in feat/sw-2863-move-contentstack-router-to-trpc-package (pull request #2389)

feat(SW-2863): Move contentstack router to trpc package

* Add exports to packages and lint rule to prevent relative imports

* Add env to trpc package

* Add eslint to trpc package

* Apply lint rules

* Use direct imports from trpc package

* Add lint-staged config to trpc

* Move lang enum to common

* Restructure trpc package folder structure

* WIP first step

* update internal imports in trpc

* Fix most errors in scandic-web

Just 100 left...

* Move Props type out of trpc

* Fix CategorizedFilters types

* Move more schemas in hotel router

* Fix deps

* fix getNonContentstackUrls

* Fix import error

* Fix entry error handling

* Fix generateMetadata metrics

* Fix alertType enum

* Fix duplicated types

* lint:fix

* Merge branch 'master' into feat/sw-2863-move-contentstack-router-to-trpc-package

* Fix broken imports

* Merge branch 'master' into feat/sw-2863-move-contentstack-router-to-trpc-package


Approved-by: Linus Flood
This commit is contained in:
Anton Gunnarsson
2025-06-26 07:53:01 +00:00
parent 0263ab8c87
commit 002d093af4
921 changed files with 3112 additions and 3008 deletions

View File

@@ -0,0 +1,13 @@
// TODO move to better location?
export enum CurrencyEnum {
DKK = "DKK",
EUR = "EUR",
NOK = "NOK",
PLN = "PLN",
SEK = "SEK",
POINTS = "Points",
Voucher = "Voucher",
CC = "CC",
Unknown = "Unknown",
}

View File

@@ -0,0 +1,11 @@
export enum MembershipLevelEnum {
L1 = "L1",
L2 = "L2",
L3 = "L3",
L4 = "L4",
L5 = "L5",
L6 = "L6",
L7 = "L7",
}
export type MembershipLevel = keyof typeof MembershipLevelEnum

View File

@@ -0,0 +1,23 @@
// TODO move to better location?
export enum PaymentMethodEnum {
card = "card",
swish = "swish",
vipps = "vipps",
mobilePay = "mobilePay",
applePay = "applePay",
googlePay = "googlePay",
alipayPlus = "alipayPlus",
weChatPay = "weChatPay",
payPal = "payPal",
klarna = "klarna",
americanExpress = "americanExpress",
dankort = "dankort",
dinersClub = "dinersClub",
jcb = "jcb",
masterCard = "masterCard",
visa = "visa",
maestro = "maestro",
chinaUnionPay = "chinaUnionPay",
discover = "discover",
}

View File

@@ -0,0 +1,19 @@
import type { LangRoute } from "./langRoute"
export const findMyBooking: LangRoute = {
da: "/da/hotelreservation/hent-booking",
de: "/de/hotelreservation/mein-bereich",
en: "/en/hotelreservation/get-booking",
fi: "/fi/hotelreservation/hae-varaus",
no: "/no/hotelreservation/get-booking",
sv: "/sv/hotelreservation/hitta-bokning",
}
export const findMyBookingCurrentWebPath: LangRoute = {
da: "/hotelreservation/hent-booking",
de: "/hotelreservation/mein-bereich",
en: "/hotelreservation/get-booking",
fi: "/varaa-hotelli/hae-varaus",
no: "/hotelreservation/get-booking",
sv: "/hotelreservation/hitta-bokning",
}

View File

@@ -0,0 +1,3 @@
import type { Lang } from "@scandic-hotels/common/constants/language"
export type LangRoute = Record<Lang, string>

View File

@@ -0,0 +1,19 @@
import type { LangRoute } from "@scandic-hotels/common/constants/routes/langRoute"
export const myStay: LangRoute = {
da: "/da/hotelreservation/min-ophold",
de: "/de/hotelreservation/meine-aufenthalte",
en: "/en/hotelreservation/my-stay",
fi: "/fi/hotelreservation/min-vistelma",
no: "/no/hotelreservation/min-ophold",
sv: "/sv/hotelreservation/min-vistelse",
}
export const preliminaryReceipt: LangRoute = {
da: `${myStay.da}/receipt`,
de: `${myStay.de}/receipt`,
en: `${myStay.en}/receipt`,
fi: `${myStay.fi}/receipt`,
no: `${myStay.no}/receipt`,
sv: `${myStay.sv}/receipt`,
}

41
packages/common/dt/dt.ts Normal file
View File

@@ -0,0 +1,41 @@
import "dayjs/locale/da"
import "dayjs/locale/de"
import "dayjs/locale/fi"
import "dayjs/locale/sv"
import d from "dayjs"
import nb from "dayjs/locale/nb"
import advancedFormat from "dayjs/plugin/advancedFormat"
import customParseFormat from "dayjs/plugin/customParseFormat"
import duration from "dayjs/plugin/duration"
import isBetween from "dayjs/plugin/isBetween"
import isSameOrAfter from "dayjs/plugin/isSameOrAfter"
import isSameOrBefore from "dayjs/plugin/isSameOrBefore"
import isToday from "dayjs/plugin/isToday"
import relativeTime from "dayjs/plugin/relativeTime"
import timezone from "dayjs/plugin/timezone"
import utc from "dayjs/plugin/utc"
/**
* dayjs export Norwegian as nb [Norwegian Bokmål] so here we create the same
* setup as nb has.
* https://day.js.org/docs/en/customization/customization
* https://github.com/iamkun/dayjs/blob/dev/src/locale/nb.js
*/
d.locale("no", { ...nb, name: "no" }, true)
/**
* If more plugins are needed https://day.js.org/docs/en/plugin/plugin
*/
d.extend(advancedFormat)
d.extend(isToday)
d.extend(relativeTime)
d.extend(timezone)
d.extend(utc)
d.extend(isSameOrAfter)
d.extend(isSameOrBefore)
d.extend(duration)
d.extend(customParseFormat)
d.extend(isBetween)
export const dt = d

View File

@@ -15,9 +15,20 @@
"./dataCache": "./dataCache/index.ts",
"./telemetry": "./telemetry/index.ts",
"./tokenManager": "./tokenManager/index.ts",
"./dt": "./dt/dt.ts",
"./utils/isEdge": "./utils/isEdge.ts",
"./utils/safeTry": "./utils/safeTry.ts",
"./constants/language": "./constants/language.ts"
"./utils/url": "./utils/url.ts",
"./utils/languages": "./utils/languages.ts",
"./utils/chunk": "./utils/chunk.ts",
"./utils/zod/stringValidator": "./utils/zod/stringValidator.ts",
"./utils/zod/numberValidator": "./utils/zod/numberValidator.ts",
"./utils/zod/arrayValidator": "./utils/zod/arrayValidator.ts",
"./constants/language": "./constants/language.ts",
"./constants/membershipLevels": "./constants/membershipLevels.ts",
"./constants/paymentMethod": "./constants/paymentMethod.ts",
"./constants/currency": "./constants/currency.ts",
"./constants/routes/*": "./constants/routes/*.ts"
},
"dependencies": {
"@opentelemetry/api": "^1.9.0",

View File

@@ -0,0 +1,25 @@
import { describe, expect } from "vitest"
import { chunk } from "./chunk"
describe("chunk", () => {
it("should split an array into equally sized chunks of specified size", () => {
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9]
const size = 3
const expected = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
expect(chunk(array, size)).toEqual(expected)
})
it("should split an array into equally sized chunks of specified size", () => {
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9]
const size = 4
const expected = [[1, 2, 3, 4], [5, 6, 7, 8], [9]]
expect(chunk(array, size)).toEqual(expected)
})
})

View File

@@ -0,0 +1,11 @@
/**
* Splits an array into chunks of a specified size
*/
export function chunk<T>(array: T[], size: number): T[][] {
const result: T[][] = []
for (let i = 0; i < array.length; i += size) {
result.push(array.slice(i, i + size))
}
return result
}

View File

@@ -0,0 +1,29 @@
import { z } from "zod"
import { Lang } from "../constants/language"
export const languageSchema = z.preprocess(
(arg) => (typeof arg === "string" ? arg.toLowerCase() : arg),
z.nativeEnum(Lang)
)
export function isValidLang(lang?: string): lang is Lang {
const result = languageSchema.safeParse(lang)
return result.success
}
export function findLang(pathname: string): Lang | undefined {
const langFromPath = Object.values(Lang).find(
(l) => pathname.startsWith(`/${l}/`) || pathname === `/${l}`
)
return isValidLang(langFromPath) ? langFromPath : undefined
}
/**
* Helper function to convert lang string to Lang enum.
*/
export function toLang(lang: string): Lang | undefined {
const lowerCaseLang = lang.toLowerCase()
return Object.values(Lang).find((l) => l === lowerCaseLang)
}

View File

@@ -0,0 +1,11 @@
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
}

View File

@@ -0,0 +1,17 @@
import { nullableStringValidator } from "./stringValidator"
import type { ZodObject, ZodRawShape } from "zod"
export function nullableArrayObjectValidator<T extends ZodRawShape>(
schema: ZodObject<T>
) {
return schema
.array()
.nullish()
.transform((arr) => (arr ? arr.filter(Boolean) : []))
}
export const nullableArrayStringValidator = nullableStringValidator
.array()
.nullish()
.transform((arr) => (arr ? arr.filter(Boolean) : []))

View File

@@ -0,0 +1,12 @@
import { z } from "zod"
export const nullableNumberValidator = z
.number()
.nullish()
.transform((num) => (typeof num === "number" ? num : 0))
export const nullableIntValidator = z
.number()
.int()
.nullish()
.transform((num) => (typeof num === "number" ? num : 0))

View File

@@ -0,0 +1,18 @@
import { z } from "zod"
export const nullableStringValidator = z
.string()
.nullish()
.transform((str) => (str ? str : ""))
export const nullableStringEmailValidator = z
.string()
.email()
.nullish()
.transform((str) => (str ? str : ""))
export const nullableStringUrlValidator = z
.string()
.url()
.nullish()
.transform((str) => (str ? str : ""))