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,54 @@
import { z } from "zod"
import { nullableArrayObjectValidator } from "@scandic-hotels/common/utils/zod/arrayValidator"
import { nullableStringValidator } from "@scandic-hotels/common/utils/zod/stringValidator"
import { displayWebPageSchema } from "./additionalData/displayWebPage"
import { facilitySchema } from "./additionalData/facility"
import { gallerySchema } from "./additionalData/gallery"
import { restaurantsOverviewPageSchema } from "./additionalData/restaurantsOverviewPage"
import { specialNeedGroupSchema } from "./additionalData/specialNeedGroups"
export const extraPageSchema = z.object({
elevatorPitch: nullableStringValidator,
mainBody: nullableStringValidator,
nameInUrl: nullableStringValidator,
})
export const additionalDataAttributesSchema = z.object({
accessibility: facilitySchema.nullish(),
conferencesAndMeetings: facilitySchema.nullish(),
displayWebPage: displayWebPageSchema,
gallery: gallerySchema.nullish(),
healthAndFitness: extraPageSchema,
healthAndWellness: facilitySchema.nullish(),
hotelParking: extraPageSchema,
hotelRoomElevatorPitchText: nullableStringValidator,
hotelSpecialNeeds: extraPageSchema,
id: nullableStringValidator,
meetingRooms: extraPageSchema.merge(
z.object({
meetingOnlineLink: z.string().nullish(),
})
),
name: nullableStringValidator,
parkingImages: facilitySchema.nullish(),
restaurantImages: facilitySchema.nullish(),
restaurantsOverviewPage: restaurantsOverviewPageSchema,
specialNeedGroups: nullableArrayObjectValidator(specialNeedGroupSchema),
})
export const additionalDataSchema = z.object({
attributes: additionalDataAttributesSchema,
type: z.literal("additionalData"),
})
export function transformAdditionalData(
data: z.output<typeof additionalDataSchema>
) {
return {
...data.attributes,
id: data.attributes.id,
type: data.type,
}
}

View File

@@ -0,0 +1,19 @@
import { z } from "zod"
export const displayWebPageSchema = z
.object({
healthGym: z.boolean().default(false),
meetingRoom: z.boolean().default(false),
parking: z.boolean().default(false),
specialNeeds: z.boolean().default(false),
})
.nullish()
.transform(
(object) =>
object ?? {
healthGym: false,
meetingRoom: false,
parking: false,
specialNeeds: false,
}
)

View File

@@ -0,0 +1,13 @@
import { z } from "zod"
import { nullableStringValidator } from "@scandic-hotels/common/utils/zod/stringValidator"
import { imageSchema } from "../../../image"
export const facilitySchema = z.object({
headingText: nullableStringValidator,
heroImages: z
.array(imageSchema)
.nullish()
.transform((arr) => (arr ? arr.filter(Boolean) : [])),
})

View File

@@ -0,0 +1,13 @@
import { z } from "zod"
import { imageSchema } from "../../../image"
const imagesSchema = z
.array(imageSchema)
.nullish()
.transform((arr) => (arr ? arr.filter(Boolean) : []))
export const gallerySchema = z.object({
heroImages: imagesSchema,
smallerImages: imagesSchema,
})

View File

@@ -0,0 +1,10 @@
import { z } from "zod"
import { nullableStringValidator } from "@scandic-hotels/common/utils/zod/stringValidator"
export const restaurantsOverviewPageSchema = z.object({
restaurantsContentDescriptionMedium: nullableStringValidator,
restaurantsContentDescriptionShort: nullableStringValidator,
restaurantsOverviewPageLink: nullableStringValidator,
restaurantsOverviewPageLinkText: nullableStringValidator,
})

View File

@@ -0,0 +1,14 @@
import { z } from "zod"
import { nullableArrayObjectValidator } from "@scandic-hotels/common/utils/zod/arrayValidator"
import { nullableStringValidator } from "@scandic-hotels/common/utils/zod/stringValidator"
const specialNeedSchema = z.object({
details: nullableStringValidator,
name: nullableStringValidator,
})
export const specialNeedGroupSchema = z.object({
name: nullableStringValidator,
specialNeeds: nullableArrayObjectValidator(specialNeedSchema),
})

View File

@@ -0,0 +1,23 @@
import { z } from "zod"
import { attributesSchema } from "../../hotel"
export const nearbyHotelsSchema = z.object({
attributes: z.lazy(() =>
attributesSchema.pick({
address: true,
cityId: true,
cityName: true,
detailedFacilities: true,
hotelContent: true,
isActive: true,
isPublished: true,
location: true,
name: true,
operaId: true,
ratings: true,
})
),
id: z.string(),
type: z.literal("hotels"),
})

View File

@@ -0,0 +1,95 @@
import { z } from "zod"
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import {
nullableIntValidator,
nullableNumberValidator,
} from "@scandic-hotels/common/utils/zod/numberValidator"
import {
nullableStringUrlValidator,
nullableStringValidator,
} from "@scandic-hotels/common/utils/zod/stringValidator"
import { imageSchema } from "../../image"
import { specialAlertsSchema } from "../specialAlerts"
const descriptionSchema = z.object({
medium: nullableStringValidator,
short: nullableStringValidator,
})
const textSchema = z.object({
descriptions: descriptionSchema,
facilityInformation: nullableStringValidator,
meetingDescription: descriptionSchema.optional(),
surroundingInformation: nullableStringValidator,
})
const contentSchema = z.object({
images: z.array(imageSchema).default([]),
texts: textSchema,
})
const restaurantPriceSchema = z.object({
amount: nullableNumberValidator,
currency: z.nativeEnum(CurrencyEnum).default(CurrencyEnum.SEK),
})
const externalBreakfastSchema = z.object({
isAvailable: z.boolean().default(false),
localPriceForExternalGuests: restaurantPriceSchema.optional(),
requestedPriceForExternalGuests: restaurantPriceSchema.optional(),
})
const menuItemSchema = z.object({
name: nullableStringValidator,
url: nullableStringUrlValidator,
})
export const openingHoursDetailsSchema = z.object({
alwaysOpen: z.boolean().default(false),
closingTime: nullableStringValidator,
isClosed: z.boolean().default(false),
openingTime: nullableStringValidator,
sortOrder: nullableIntValidator,
})
export const openingHoursSchema = z.object({
friday: openingHoursDetailsSchema.optional(),
isActive: z.boolean().default(false),
monday: openingHoursDetailsSchema.optional(),
name: nullableStringValidator,
saturday: openingHoursDetailsSchema.optional(),
sunday: openingHoursDetailsSchema.optional(),
thursday: openingHoursDetailsSchema.optional(),
tuesday: openingHoursDetailsSchema.optional(),
wednesday: openingHoursDetailsSchema.optional(),
})
const openingDetailsSchema = z.object({
alternateOpeningHours: openingHoursSchema.optional(),
openingHours: openingHoursSchema,
ordinary: openingHoursSchema.optional(),
weekends: openingHoursSchema.optional(),
})
export const restaurantsSchema = z.object({
attributes: z.object({
bookTableUrl: nullableStringValidator,
content: contentSchema,
email: z.string().email().optional(),
externalBreakfast: externalBreakfastSchema,
isPublished: z.boolean().default(false),
menus: z.array(menuItemSchema).default([]),
name: z.string().default(""),
openingDetails: z.array(openingDetailsSchema).default([]),
phoneNumber: z.string().optional(),
restaurantPage: z.boolean().default(false),
elevatorPitch: z.string().optional(),
nameInUrl: z.string().optional(),
mainBody: z.string().optional(),
specialAlerts: specialAlertsSchema,
}),
id: z.string(),
type: z.literal("restaurants"),
})

View File

@@ -0,0 +1,107 @@
import { z } from "zod"
import { nullableArrayObjectValidator } from "@scandic-hotels/common/utils/zod/arrayValidator"
import { nullableStringValidator } from "@scandic-hotels/common/utils/zod/stringValidator"
import { imageSchema } from "../../image"
const minMaxSchema = z.object({
max: z.number(),
min: z.number(),
})
const bedTypeSchema = z.object({
description: nullableStringValidator,
type: nullableStringValidator,
widthRange: minMaxSchema,
})
const occupancySchema = z.object({
adults: z.number(),
children: z.number(),
total: z.number(),
})
const roomContentSchema = z.object({
images: z
.array(imageSchema)
.nullish()
.transform((arr) => (arr ? arr.filter(Boolean) : [])),
texts: z.object({
descriptions: z.object({
medium: nullableStringValidator,
short: nullableStringValidator,
}),
}),
})
const roomTypesSchema = z.object({
code: nullableStringValidator,
description: nullableStringValidator,
fixedExtraBed: bedTypeSchema.optional(),
isLackingCribs: z.boolean(),
isLackingExtraBeds: z.boolean(),
mainBed: bedTypeSchema,
name: nullableStringValidator,
occupancy: occupancySchema,
roomCount: z.number(),
roomSize: minMaxSchema,
})
const roomFacilitiesSchema = z.object({
availableInAllRooms: z.boolean(),
icon: z.string().optional(),
isUniqueSellingPoint: z.boolean(),
name: z.string(),
sortOrder: z.number(),
})
export const roomCategoriesSchema = z.object({
attributes: z.object({
content: roomContentSchema,
name: nullableStringValidator,
occupancy: minMaxSchema,
roomFacilities: nullableArrayObjectValidator(roomFacilitiesSchema),
roomSize: minMaxSchema,
roomTypes: nullableArrayObjectValidator(roomTypesSchema),
sortOrder: z.number(),
}),
id: z.string(),
type: z.literal("roomcategories"),
})
export function transformRoomCategories(
data: z.output<typeof roomCategoriesSchema>
) {
return {
descriptions: data.attributes.content.texts.descriptions,
id: data.id,
images: data.attributes.content.images,
name: data.attributes.name,
occupancy: data.attributes.occupancy,
roomFacilities: data.attributes.roomFacilities,
roomSize: data.attributes.roomSize,
roomTypes: data.attributes.roomTypes.map((roomType) => {
if (!roomType || roomType.fixedExtraBed?.type.toLowerCase() === "none") {
return {
...roomType,
fixedExtraBed: undefined,
}
}
return roomType
}),
sortOrder: data.attributes.sortOrder,
type: data.type,
totalOccupancy:
data.attributes.occupancy.min === data.attributes.occupancy.max
? {
max: data.attributes.occupancy.max,
range: `${data.attributes.occupancy.max}`,
}
: {
max: data.attributes.occupancy.max,
range: `${data.attributes.occupancy.min}-${data.attributes.occupancy.max}`,
},
}
}