Merged in feat/sw-2861-move-autocomplete-router-to-trpc-package (pull request #2417)

feat(SW-2861): Move autocomplete router 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

* Move booking router to trpc package

* Move partners router to trpc package

* Move autocomplete router to trpc package

* Merge branch 'master' into feat/sw-2861-move-autocomplete-router-to-trpc-package


Approved-by: Linus Flood
This commit is contained in:
Anton Gunnarsson
2025-06-26 12:40:45 +00:00
parent 7e4ed93c97
commit 5f8ac8cdeb
29 changed files with 92 additions and 70 deletions

View File

@@ -3,8 +3,9 @@ import Body from "@/components/TempDesignSystem/Text/Body"
import { listItemVariants } from "./variants" import { listItemVariants } from "./variants"
import type { AutoCompleteLocation } from "@scandic-hotels/trpc/routers/autocomplete/schema"
import type { SearchListProps } from "@/types/components/search" import type { SearchListProps } from "@/types/components/search"
import type { AutoCompleteLocation } from "@/server/routers/autocomplete/schema"
export interface ListItemProps export interface ListItemProps
extends Pick<SearchListProps, "getItemProps" | "highlightedIndex"> { extends Pick<SearchListProps, "getItemProps" | "highlightedIndex"> {

View File

@@ -5,8 +5,9 @@ import ListItem, { ListItemSkeleton } from "./ListItem"
import styles from "./list.module.css" import styles from "./list.module.css"
import type { AutoCompleteLocation } from "@scandic-hotels/trpc/routers/autocomplete/schema"
import type { SearchListProps } from "@/types/components/search" import type { SearchListProps } from "@/types/components/search"
import type { AutoCompleteLocation } from "@/server/routers/autocomplete/schema"
interface ListProps interface ListProps
extends Pick<SearchListProps, "getItemProps" | "highlightedIndex"> { extends Pick<SearchListProps, "getItemProps" | "highlightedIndex"> {

View File

@@ -10,8 +10,6 @@ import { Button } from "@scandic-hotels/design-system/Button"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon" import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography" import { Typography } from "@scandic-hotels/design-system/Typography"
import { type AutoCompleteLocation } from "@/server/routers/autocomplete/schema"
import SkeletonShimmer from "@/components/SkeletonShimmer" import SkeletonShimmer from "@/components/SkeletonShimmer"
import { useSearchHistory } from "@/hooks/useSearchHistory" import { useSearchHistory } from "@/hooks/useSearchHistory"
@@ -20,6 +18,8 @@ import SearchList from "./SearchList"
import styles from "./search.module.css" import styles from "./search.module.css"
import type { AutoCompleteLocation } from "@scandic-hotels/trpc/routers/autocomplete/schema"
interface SearchProps { interface SearchProps {
autoFocus?: boolean autoFocus?: boolean
alwaysShowResults?: boolean alwaysShowResults?: boolean

View File

@@ -2,11 +2,10 @@
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { isDefined } from "@scandic-hotels/common/utils/isDefined"
import { Typography } from "@scandic-hotels/design-system/Typography" import { Typography } from "@scandic-hotels/design-system/Typography"
import { HotelTypeEnum } from "@scandic-hotels/trpc/enums/hotelType" import { HotelTypeEnum } from "@scandic-hotels/trpc/enums/hotelType"
import { isDefined } from "@/server/utils"
import { IconName } from "@/components/Icons/iconName" import { IconName } from "@/components/Icons/iconName"
import OpeningHours from "@/components/OpeningHours" import OpeningHours from "@/components/OpeningHours"
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem" import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"

View File

@@ -3,7 +3,7 @@ import { useCallback, useEffect, useState } from "react"
import { import {
type AutoCompleteLocation, type AutoCompleteLocation,
autoCompleteLocationSchema, autoCompleteLocationSchema,
} from "@/server/routers/autocomplete/schema" } from "@scandic-hotels/trpc/routers/autocomplete/schema"
import useLang from "@/hooks/useLang" import useLang from "@/hooks/useLang"

View File

@@ -1,7 +1,8 @@
import { redirect } from "next/navigation" import { redirect } from "next/navigation"
import { isDefined } from "@/server/utils" import { isDefined } from "@scandic-hotels/common/utils/isDefined"
// import { isDefined } from "@/server/utils"
import { getLang } from "@/i18n/serverContext" import { getLang } from "@/i18n/serverContext"
import { cache } from "@/utils/cache" import { cache } from "@/utils/cache"

View File

@@ -1,10 +1,10 @@
/** Routers */ /** Routers */
import { router } from "@scandic-hotels/trpc" import { router } from "@scandic-hotels/trpc"
import { autocompleteRouter } from "@scandic-hotels/trpc/routers/autocomplete"
import { contentstackRouter } from "@scandic-hotels/trpc/routers/contentstack" import { contentstackRouter } from "@scandic-hotels/trpc/routers/contentstack"
import { hotelsRouter } from "@scandic-hotels/trpc/routers/hotels" import { hotelsRouter } from "@scandic-hotels/trpc/routers/hotels"
import { partnerRouter } from "@scandic-hotels/trpc/routers/partners" import { partnerRouter } from "@scandic-hotels/trpc/routers/partners"
import { autocompleteRouter } from "./routers/autocomplete"
import { bookingRouter } from "./routers/booking" import { bookingRouter } from "./routers/booking"
import { navigationRouter } from "./routers/navigation" import { navigationRouter } from "./routers/navigation"
import { userRouter } from "./routers/user" import { userRouter } from "./routers/user"

View File

@@ -2,10 +2,6 @@ import { NextRequest } from "next/server"
import { env } from "@/env/server" import { env } from "@/env/server"
export function isDefined<T>(argument: T | undefined | null): argument is T {
return argument !== undefined && argument !== null
}
/** /**
* Use this function when you want to create URLs that are public facing, for * Use this function when you want to create URLs that are public facing, for
* example for redirects or redirectTo query parameters. * example for redirects or redirectTo query parameters.

View File

@@ -1,6 +1,5 @@
import { getServiceToken } from "@scandic-hotels/common/tokenManager" import { getServiceToken } from "@scandic-hotels/common/tokenManager"
import { getAutoCompleteDestinationsData } from "@scandic-hotels/trpc/routers/autocomplete/destinations"
import { getAutoCompleteDestinationsData } from "@/server/routers/autocomplete/destinations"
import type { Lang } from "@scandic-hotels/common/constants/language" import type { Lang } from "@scandic-hotels/common/constants/language"

View File

@@ -1,8 +1,9 @@
import type { AutoCompleteLocation } from "@scandic-hotels/trpc/routers/autocomplete/schema"
import type { VariantProps } from "class-variance-authority" import type { VariantProps } from "class-variance-authority"
import type { PropGetters } from "downshift" import type { PropGetters } from "downshift"
import type { dialogVariants } from "@/components/Forms/BookingWidget/FormContent/Search/SearchList/Dialog/variants" import type { dialogVariants } from "@/components/Forms/BookingWidget/FormContent/Search/SearchList/Dialog/variants"
import type { AutoCompleteLocation } from "@/server/routers/autocomplete/schema" // import type { AutoCompleteLocation } from "@/server/routers/autocomplete/schema"
export interface SearchProps { export interface SearchProps {
handlePressEnter: () => void handlePressEnter: () => void

View File

@@ -21,6 +21,7 @@
"./utils/url": "./utils/url.ts", "./utils/url": "./utils/url.ts",
"./utils/languages": "./utils/languages.ts", "./utils/languages": "./utils/languages.ts",
"./utils/chunk": "./utils/chunk.ts", "./utils/chunk": "./utils/chunk.ts",
"./utils/isDefined": "./utils/isDefined.ts",
"./utils/zod/stringValidator": "./utils/zod/stringValidator.ts", "./utils/zod/stringValidator": "./utils/zod/stringValidator.ts",
"./utils/zod/numberValidator": "./utils/zod/numberValidator.ts", "./utils/zod/numberValidator": "./utils/zod/numberValidator.ts",
"./utils/zod/arrayValidator": "./utils/zod/arrayValidator.ts", "./utils/zod/arrayValidator": "./utils/zod/arrayValidator.ts",

View File

@@ -0,0 +1,3 @@
export function isDefined<T>(argument: T | undefined | null): argument is T {
return argument !== undefined && argument !== null
}

0
packages/trpc/.env.test Normal file
View File

View File

@@ -2,20 +2,19 @@ import { z } from "zod"
import { Lang } from "@scandic-hotels/common/constants/language" import { Lang } from "@scandic-hotels/common/constants/language"
import { getCacheClient } from "@scandic-hotels/common/dataCache" import { getCacheClient } from "@scandic-hotels/common/dataCache"
import { isDefined } from "@scandic-hotels/common/utils/isDefined"
import { safeTry } from "@scandic-hotels/common/utils/safeTry" import { safeTry } from "@scandic-hotels/common/utils/safeTry"
import { safeProtectedServiceProcedure } from "@scandic-hotels/trpc/procedures"
import { getCityPageUrls } from "@scandic-hotels/trpc/routers/contentstack/destinationCityPage/utils" import { safeProtectedServiceProcedure } from "../../procedures"
import { getCountryPageUrls } from "@scandic-hotels/trpc/routers/contentstack/destinationCountryPage/utils" import { getCityPageUrls } from "../../routers/contentstack/destinationCityPage/utils"
import { getHotelPageUrls } from "@scandic-hotels/trpc/routers/contentstack/hotelPage/utils" import { getCountryPageUrls } from "../../routers/contentstack/destinationCountryPage/utils"
import { getHotelPageUrls } from "../../routers/contentstack/hotelPage/utils"
import { import {
getCitiesByCountry, getCitiesByCountry,
getCountries, getCountries,
getLocations, getLocations,
} from "@scandic-hotels/trpc/routers/hotels/utils" } from "../../routers/hotels/utils"
import { ApiCountry, type Country } from "@scandic-hotels/trpc/types/country" import { ApiCountry, type Country } from "../../types/country"
import { isDefined } from "@/server/utils"
import { filterAndCategorizeAutoComplete } from "./util/filterAndCategorizeAutoComplete" import { filterAndCategorizeAutoComplete } from "./util/filterAndCategorizeAutoComplete"
import { mapLocationToAutoCompleteLocation } from "./util/mapLocationToAutoCompleteLocation" import { mapLocationToAutoCompleteLocation } from "./util/mapLocationToAutoCompleteLocation"

View File

@@ -1,5 +1,4 @@
import { router } from "@scandic-hotels/trpc" import { router } from "../.."
import { getDestinationsAutoCompleteRoute } from "./destinations" import { getDestinationsAutoCompleteRoute } from "./destinations"
export const autocompleteRouter = router({ export const autocompleteRouter = router({

View File

@@ -1,8 +1,8 @@
import { describe, expect, it } from "@jest/globals" import { describe, expect, it } from "vitest"
import { filterAutoCompleteLocations } from "./filterAutoCompleteLocations" import { filterAutoCompleteLocations } from "./filterAutoCompleteLocations"
import type { DeepPartial } from "@/types/DeepPartial" import type { DeepPartial } from "../../../types/deepPartial"
import type { AutoCompleteLocation } from "../schema" import type { AutoCompleteLocation } from "../schema"
describe("rankAutoCompleteLocations", () => { describe("rankAutoCompleteLocations", () => {

View File

@@ -1,10 +1,10 @@
import { describe, expect, it } from "@jest/globals" import { describe, expect, it } from "vitest"
import { getSearchTokens } from "./getSearchTokens" import { getSearchTokens } from "./getSearchTokens"
import type { Location } from "@scandic-hotels/trpc/types/locations" import type { Location } from "@scandic-hotels/trpc/types/locations"
import type { DeepPartial } from "@/types/DeepPartial" import type { DeepPartial } from "../../../types/deepPartial"
describe("getSearchTokens", () => { describe("getSearchTokens", () => {
it("should return lowercased tokens for a hotel location", () => { it("should return lowercased tokens for a hotel location", () => {
@@ -28,16 +28,18 @@ describe("getSearchTokens", () => {
} }
const result = getSearchTokens(location as Location) const result = getSearchTokens(location as Location)
expect(result).toEqual([ expect(result.toSorted()).toEqual(
"ångström", [
"café", "ångström",
"münchen", "café",
"frånce", "münchen",
"angstrom", "frånce",
"cafe", "angstrom",
"munchen", "cafe",
"france", "munchen",
]) "france",
].toSorted()
)
}) })
it("should filter out empty or falsey tokens", () => { it("should filter out empty or falsey tokens", () => {

View File

@@ -1,6 +1,6 @@
import { normalizeAumlauts } from "./normalizeAumlauts" import { normalizeAumlauts } from "./normalizeAumlauts"
import type { Location } from "@scandic-hotels/trpc/types/locations" import type { Location } from "../../../types/locations"
export function getSearchTokens(location: Location) { export function getSearchTokens(location: Location) {
const tokens = [ const tokens = [

View File

@@ -1,7 +1,6 @@
import { getSearchTokens } from "./getSearchTokens" import { getSearchTokens } from "./getSearchTokens"
import type { Location } from "@scandic-hotels/trpc/types/locations" import type { Location } from "../../../types/locations"
import type { AutoCompleteLocation } from "../schema" import type { AutoCompleteLocation } from "../schema"
export function mapLocationToAutoCompleteLocation( export function mapLocationToAutoCompleteLocation(

View File

@@ -2,6 +2,24 @@ import { Lang } from "@scandic-hotels/common/constants/language"
import { getCacheClient } from "@scandic-hotels/common/dataCache" import { getCacheClient } from "@scandic-hotels/common/dataCache"
import { dt } from "@scandic-hotels/common/dt" import { dt } from "@scandic-hotels/common/dt"
import { createCounter } from "@scandic-hotels/common/telemetry" import { createCounter } from "@scandic-hotels/common/telemetry"
import { env } from "../../../env/server"
import { router } from "../.."
import * as api from "../../api"
import { REDEMPTION } from "../../constants/booking"
import { BreakfastPackageEnum } from "../../enums/breakfast"
import { RateEnum } from "../../enums/rate"
import { RateTypeEnum } from "../../enums/rateType"
import { AvailabilityEnum } from "../../enums/selectHotel"
import { badRequestError, unauthorizedError } from "../../errors"
import {
contentStackBaseWithServiceProcedure,
publicProcedure,
safeProtectedServiceProcedure,
serviceProcedure,
} from "../../procedures"
import { getCityPageUrls } from "../../routers/contentstack/destinationCityPage/utils"
import { getHotelPageUrls } from "../../routers/contentstack/hotelPage/utils"
import { import {
ancillaryPackageInputSchema, ancillaryPackageInputSchema,
breakfastPackageInputSchema, breakfastPackageInputSchema,
@@ -23,46 +41,30 @@ import {
roomPackagesInputSchema, roomPackagesInputSchema,
selectRateRoomAvailabilityInputSchema, selectRateRoomAvailabilityInputSchema,
selectRateRoomsAvailabilityInputSchema, selectRateRoomsAvailabilityInputSchema,
} from "@scandic-hotels/trpc/routers/hotels/input" } from "../../routers/hotels/input"
import { import {
ancillaryPackagesSchema, ancillaryPackagesSchema,
breakfastPackagesSchema, breakfastPackagesSchema,
getNearbyHotelIdsSchema, getNearbyHotelIdsSchema,
} from "@scandic-hotels/trpc/routers/hotels/output" } from "../../routers/hotels/output"
import { additionalDataSchema } from "@scandic-hotels/trpc/routers/hotels/schemas/hotel/include/additionalData" import { additionalDataSchema } from "../../routers/hotels/schemas/hotel/include/additionalData"
import { env } from "../../../env/server"
import { router } from "../.."
import * as api from "../../api"
import { REDEMPTION } from "../../constants/booking"
import { BreakfastPackageEnum } from "../../enums/breakfast"
import { RateEnum } from "../../enums/rate"
import { RateTypeEnum } from "../../enums/rateType"
import { AvailabilityEnum } from "../../enums/selectHotel"
import { badRequestError, unauthorizedError } from "../../errors"
import {
contentStackBaseWithServiceProcedure,
publicProcedure,
safeProtectedServiceProcedure,
serviceProcedure,
} from "../../procedures"
import { getCityPageUrls } from "../../routers/contentstack/destinationCityPage/utils"
import { getHotelPageUrls } from "../../routers/contentstack/hotelPage/utils"
import { toApiLang } from "../../utils" import { toApiLang } from "../../utils"
import { getVerifiedUser } from "../user/utils" import { getVerifiedUser } from "../user/utils"
import { meetingRoomsSchema } from "./schemas/meetingRoom" import { meetingRoomsSchema } from "./schemas/meetingRoom"
import { import {
getBedTypes,
getCitiesByCountry, getCitiesByCountry,
getCountries, getCountries,
getHotel, getHotel,
getHotelIdsByCityId, getHotelIdsByCityId,
getHotelIdsByCityIdentifier, getHotelIdsByCityIdentifier,
getHotelIdsByCountry, getHotelIdsByCountry,
getHotelsAvailabilityByCity,
getHotelsAvailabilityByHotelIds,
getHotelsByHotelIds, getHotelsByHotelIds,
getLocations, getLocations,
} from "./utils"
import {
getBedTypes,
getHotelsAvailabilityByCity,
getHotelsAvailabilityByHotelIds,
getPackages, getPackages,
getRoomsAvailability, getRoomsAvailability,
getSelectedRoomAvailability, getSelectedRoomAvailability,

View File

@@ -6,7 +6,9 @@
"scripts": { "scripts": {
"check-types": "tsc --noEmit", "check-types": "tsc --noEmit",
"lint": "eslint . --max-warnings 0 && tsc --noEmit", "lint": "eslint . --max-warnings 0 && tsc --noEmit",
"lint:fix": "eslint . --fix && tsc --noEmit" "lint:fix": "eslint . --fix && tsc --noEmit",
"test": "vitest run",
"test:watch": "vitest"
}, },
"exports": { "exports": {
".": "./lib/index.ts", ".": "./lib/index.ts",
@@ -24,6 +26,7 @@
"./routers/booking/*": "./lib/routers/booking/*.ts", "./routers/booking/*": "./lib/routers/booking/*.ts",
"./routers/user/*": "./lib/routers/user/*.ts", "./routers/user/*": "./lib/routers/user/*.ts",
"./routers/partners/*": "./lib/routers/partners/*.ts", "./routers/partners/*": "./lib/routers/partners/*.ts",
"./routers/autocomplete/*": "./lib/routers/autocomplete/*.ts",
"./enums/*": "./lib/enums/*.ts", "./enums/*": "./lib/enums/*.ts",
"./types/*": "./lib/types/*.ts", "./types/*": "./lib/types/*.ts",
"./constants/*": "./lib/constants/*.ts", "./constants/*": "./lib/constants/*.ts",
@@ -46,6 +49,7 @@
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"deepmerge": "^4.3.1", "deepmerge": "^4.3.1",
"fetch-retry": "^6.0.0", "fetch-retry": "^6.0.0",
"fuse.js": "^7.1.0",
"graphql": "^16.11.0", "graphql": "^16.11.0",
"graphql-request": "^7.1.2", "graphql-request": "^7.1.2",
"json-stable-stringify-without-jsonify": "^1.0.1", "json-stable-stringify-without-jsonify": "^1.0.1",
@@ -68,9 +72,11 @@
"@types/react": "19.1.0", "@types/react": "19.1.0",
"@typescript-eslint/eslint-plugin": "^8.32.0", "@typescript-eslint/eslint-plugin": "^8.32.0",
"@typescript-eslint/parser": "^8.32.0", "@typescript-eslint/parser": "^8.32.0",
"dotenv": "^16.5.0",
"eslint": "^9", "eslint": "^9",
"eslint-plugin-import": "^2.31.0", "eslint-plugin-import": "^2.31.0",
"eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-simple-import-sort": "^12.1.1",
"typescript": "5.8.3" "typescript": "5.8.3",
"vitest": "^3.2.3"
} }
} }

View File

@@ -0,0 +1,3 @@
import { config } from "dotenv"
config({ path: "./.env.test" })

View File

@@ -0,0 +1,7 @@
export default {
test: {
globals: true,
environment: "jsdom",
setupFiles: ["./vitest-setup.ts"],
},
}

View File

@@ -7254,10 +7254,12 @@ __metadata:
"@typescript-eslint/parser": "npm:^8.32.0" "@typescript-eslint/parser": "npm:^8.32.0"
dayjs: "npm:^1.11.13" dayjs: "npm:^1.11.13"
deepmerge: "npm:^4.3.1" deepmerge: "npm:^4.3.1"
dotenv: "npm:^16.5.0"
eslint: "npm:^9" eslint: "npm:^9"
eslint-plugin-import: "npm:^2.31.0" eslint-plugin-import: "npm:^2.31.0"
eslint-plugin-simple-import-sort: "npm:^12.1.1" eslint-plugin-simple-import-sort: "npm:^12.1.1"
fetch-retry: "npm:^6.0.0" fetch-retry: "npm:^6.0.0"
fuse.js: "npm:^7.1.0"
graphql: "npm:^16.11.0" graphql: "npm:^16.11.0"
graphql-request: "npm:^7.1.2" graphql-request: "npm:^7.1.2"
json-stable-stringify-without-jsonify: "npm:^1.0.1" json-stable-stringify-without-jsonify: "npm:^1.0.1"
@@ -7266,6 +7268,7 @@ __metadata:
slugify: "npm:^1.6.6" slugify: "npm:^1.6.6"
superjson: "npm:^2.2.2" superjson: "npm:^2.2.2"
typescript: "npm:5.8.3" typescript: "npm:5.8.3"
vitest: "npm:^3.2.3"
zod: "npm:^3.24.4" zod: "npm:^3.24.4"
peerDependencies: peerDependencies:
next: ^15 next: ^15