Merged in feat/sw-2864-move-hotels-router-to-trpc-package (pull request #2410)

feat (SW-2864): Move booking router to trpc package

* 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

* Move booking router to trpc package

* Merge branch 'master' into feat/sw-2864-move-hotels-router-to-trpc-package


Approved-by: Linus Flood
This commit is contained in:
Anton Gunnarsson
2025-06-26 09:02:59 +00:00
parent 002d093af4
commit bbcabfa0ba
135 changed files with 1403 additions and 1387 deletions

View File

@@ -3,8 +3,8 @@
import { z } from "zod"
import * as api from "@scandic-hotels/trpc/api"
import { countriesMap } from "@scandic-hotels/trpc/constants/countries"
import { countriesMap } from "@/constants/countries"
import { ApiLang } from "@/constants/languages"
import { getProfile } from "@/lib/trpc/memoizedRequests"
import { protectedServerActionProcedure } from "@/server/trpc"

View File

@@ -1,11 +1,9 @@
import { notFound } from "next/navigation"
import { myStay } from "@scandic-hotels/common/constants/routes/myStay"
import { BookingErrorCodeEnum } from "@scandic-hotels/trpc/enums/bookingErrorCode"
import {
BookingErrorCodeEnum,
PaymentCallbackStatusEnum,
} from "@/constants/booking"
import { PaymentCallbackStatusEnum } from "@/constants/booking"
import { serverClient } from "@/lib/trpc/server"
import GuaranteeCallback from "@/components/HotelReservation/MyStay/Ancillaries/GuaranteeCallback"

View File

@@ -1,11 +1,9 @@
import { notFound } from "next/navigation"
import { getServiceToken } from "@scandic-hotels/common/tokenManager"
import { BookingErrorCodeEnum } from "@scandic-hotels/trpc/enums/bookingErrorCode"
import {
BookingErrorCodeEnum,
PaymentCallbackStatusEnum,
} from "@/constants/booking"
import { PaymentCallbackStatusEnum } from "@/constants/booking"
import {
bookingConfirmation,
details,

View File

@@ -1,6 +1,8 @@
import { notFound } from "next/navigation"
import { combineRegExps, rateTypeRegex, REDEMPTION } from "@/constants/booking"
import { REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
import { combineRegExps, rateTypeRegex } from "@/constants/booking"
import SelectRate from "@/components/HotelReservation/SelectRate"
import { parseSelectRateSearchParams } from "@/utils/url"

View File

@@ -1,10 +1,10 @@
import NextAuth, { type NextAuthConfig, type User } from "next-auth"
import { LoginTypeEnum } from "@scandic-hotels/trpc/types/loginType"
import { PRE_REFRESH_TIME_IN_SECONDS } from "@/constants/auth"
import { env } from "@/env/server"
import { LoginTypeEnum } from "./types/components/tracking"
import type { JWT } from "next-auth/jwt"
import type { OIDCConfig } from "next-auth/providers"

View File

@@ -1,10 +1,10 @@
import { Lang } from "@scandic-hotels/common/constants/language"
import { dt } from "@scandic-hotels/common/dt"
import { getFriendsMembership } from "@scandic-hotels/trpc/routers/user/helpers"
import Body from "@/components/TempDesignSystem/Text/Body"
import { getIntl } from "@/i18n"
import { getLang } from "@/i18n/serverContext"
import { getFriendsMembership } from "@/utils/user"
import type { UserProps } from "@/types/components/myPages/user"

View File

@@ -1,9 +1,9 @@
import { MembershipLevelEnum } from "@scandic-hotels/common/constants/membershipLevels"
import { getFriendsMembership } from "@scandic-hotels/trpc/routers/user/helpers"
import { serverClient } from "@/lib/trpc/server"
import { getIntl } from "@/i18n"
import { getFriendsMembership } from "@/utils/user"
import PointsContainer from "./Container"
import { PointsColumn } from "./PointsColumn"

View File

@@ -23,7 +23,7 @@ import { UnlinkSAS } from "./UnlinkSAS"
import styles from "./linkedAccounts.module.css"
import type { UserLoyalty } from "@/types/user"
import type { UserLoyalty } from "@scandic-hotels/trpc/types/user"
type Props = {
title?: string

View File

@@ -7,8 +7,8 @@ import { FormProvider, useForm } from "react-hook-form"
import { dt } from "@scandic-hotels/common/dt"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
import { REDEMPTION } from "@/constants/booking"
import { trpc } from "@/lib/trpc/client"
import { StickyElementNameEnum } from "@/stores/sticky-position"

View File

@@ -6,8 +6,7 @@ import { useMediaQuery } from "usehooks-ts"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { REDEMPTION } from "@/constants/booking"
import { REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
import Modal from "@/components/Modal"
import Button from "@/components/TempDesignSystem/Button"

View File

@@ -7,8 +7,7 @@ import { useMediaQuery } from "usehooks-ts"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { REDEMPTION } from "@/constants/booking"
import { REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
import Modal from "@/components/Modal"
import Button from "@/components/TempDesignSystem/Button"

View File

@@ -7,8 +7,8 @@ import { useIntl } from "react-intl"
import { dt } from "@scandic-hotels/common/dt"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
import { REDEMPTION } from "@/constants/booking"
import { hotelreservation } from "@/constants/routes/hotelReservation"
import DatePicker from "@/components/DatePicker"

View File

@@ -5,11 +5,12 @@ import { useTransition } from "react"
import { Form as FormRAC } from "react-aria-components"
import { useFormContext } from "react-hook-form"
import { REDEMPTION } from "@/constants/booking"
import { selectRate } from "@scandic-hotels/common/constants/routes/hotelReservation"
import { REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
import {
selectHotel,
selectHotelMap,
selectRate,
} from "@/constants/routes/hotelReservation"
import useLang from "@/hooks/useLang"

View File

@@ -1,9 +1,8 @@
import { z } from "zod"
import { REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
import { ChildBedMapEnum } from "@scandic-hotels/trpc/enums/childBedMapEnum"
import { REDEMPTION } from "@/constants/booking"
export const bookingWidgetErrors = {
AGE_REQUIRED: "AGE_REQUIRED",
BED_CHOICE_REQUIRED: "BED_CHOICE_REQUIRED",

View File

@@ -6,8 +6,7 @@ import { useIntl } from "react-intl"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { REDEMPTION } from "@/constants/booking"
import { REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
import Button from "../TempDesignSystem/Button"
import { Tooltip } from "../TempDesignSystem/Tooltip"

View File

@@ -20,9 +20,9 @@ import MyPagesMenuContent, { useMyPagesNavigation } from "../MyPagesMenuContent"
import styles from "./myPagesMenu.module.css"
import type { LoyaltyLevel } from "@scandic-hotels/trpc/routers/contentstack/loyaltyLevel/output"
import type { FriendsMembership, User } from "@scandic-hotels/trpc/types/user"
import { DropdownTypeEnum } from "@/types/components/dropdown/dropdown"
import type { FriendsMembership, User } from "@/types/user"
export type MyPagesMenuProps = {
user: Pick<User, "firstName" | "lastName">

View File

@@ -6,8 +6,9 @@ import {
packageSchema,
} from "@scandic-hotels/trpc/routers/hotels/schemas/packages"
import type { Package } from "@scandic-hotels/trpc/types/packages"
import type { BreakfastPackage } from "@/types/components/hotelReservation/breakfast"
import type { Package } from "@/types/requests/packages"
import type { Room } from "@/types/stores/booking-confirmation"
export function mapToPrice(rooms: (Room | null)[], nights: number) {

View File

@@ -4,11 +4,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
import { useCallback, useEffect, useState } from "react"
import { FormProvider, useForm } from "react-hook-form"
import {
BED_TYPE_ICONS,
type BedTypeEnum,
type ExtraBedTypeEnum,
} from "@/constants/booking"
import { BED_TYPE_ICONS } from "@/constants/booking"
import { useEnterDetailsStore } from "@/stores/enter-details"
import RadioCard from "@/components/TempDesignSystem/Form/RadioCard"
@@ -20,6 +16,10 @@ import { bedTypeFormSchema } from "./schema"
import styles from "./bedOptions.module.css"
import type { IconProps } from "@scandic-hotels/design-system/Icons"
import type {
BedTypeEnum,
ExtraBedTypeEnum,
} from "@scandic-hotels/trpc/enums/bedType"
import type { BedTypeFormSchema } from "@/types/components/hotelReservation/enterDetails/bedType"

View File

@@ -23,7 +23,7 @@ import PaymentOptionsGroup from "../../Payment/PaymentOptionsGroup"
import styles from "./guarantee.module.css"
import type { CreditCard } from "@/types/user"
import type { CreditCard } from "@scandic-hotels/trpc/types/user"
interface GuaranteeProps {
savedCreditCards: CreditCard[] | null

View File

@@ -11,7 +11,7 @@ import Guarantee from "./Guarantee"
import styles from "./confirm.module.css"
import type { CreditCard } from "@/types/user"
import type { CreditCard } from "@scandic-hotels/trpc/types/user"
interface ConfirmBookingProps {
savedCreditCards: CreditCard[] | null

View File

@@ -4,10 +4,10 @@ import { usePathname, useSearchParams } from "next/navigation"
import { useEffect, useRef, useState } from "react"
import { useIntl } from "react-intl"
import { selectRate } from "@scandic-hotels/common/constants/routes/hotelReservation"
import { BookingErrorCodeEnum } from "@scandic-hotels/trpc/enums/bookingErrorCode"
import { AlertTypeEnum } from "@scandic-hotels/trpc/types/alertType"
import { BookingErrorCodeEnum } from "@/constants/booking"
import { selectRate } from "@/constants/routes/hotelReservation"
import { useEnterDetailsStore } from "@/stores/enter-details"
import Alert from "@/components/TempDesignSystem/Alert"

View File

@@ -9,19 +9,15 @@ import { FormProvider, useForm } from "react-hook-form"
import { useIntl } from "react-intl"
import { PaymentMethodEnum } from "@scandic-hotels/common/constants/paymentMethod"
import { selectRate } from "@scandic-hotels/common/constants/routes/hotelReservation"
import { Button } from "@scandic-hotels/design-system/Button"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { bedTypeMap } from "@scandic-hotels/trpc/constants/bedTypeMap"
import { REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
import {
BookingStatusEnum,
PAYMENT_METHOD_TITLES,
REDEMPTION,
} from "@/constants/booking"
import {
bookingConfirmation,
selectRate,
} from "@/constants/routes/hotelReservation"
import { BookingStatusEnum, PAYMENT_METHOD_TITLES } from "@/constants/booking"
import { bookingConfirmation } from "@/constants/routes/hotelReservation"
import { env } from "@/env/client"
import { trpc } from "@/lib/trpc/client"
import { useEnterDetailsStore } from "@/stores/enter-details"
@@ -38,7 +34,6 @@ import { trackPaymentEvent } from "@/utils/tracking"
import { trackEvent } from "@/utils/tracking/base"
import { trackGlaSaveCardAttempt } from "@/utils/tracking/myStay"
import { bedTypeMap } from "../../utils"
import ConfirmBooking, { ConfirmBookingRedemption } from "../Confirm"
import PriceChangeDialog from "../PriceChangeDialog"
import { writeGlaToSessionStorage } from "./PaymentCallback/helpers"

View File

@@ -4,10 +4,10 @@ import { useRouter } from "next/navigation"
import { useTransition } from "react"
import { useIntl } from "react-intl"
import { selectRate } from "@scandic-hotels/common/constants/routes/hotelReservation"
import { Button } from "@scandic-hotels/design-system/Button"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { selectRate } from "@/constants/routes/hotelReservation"
import { useEnterDetailsStore } from "@/stores/enter-details"
import Footnote from "@/components/TempDesignSystem/Text/Footnote"

View File

@@ -9,9 +9,9 @@ import { getTracking } from "./tracking"
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { Hotel } from "@scandic-hotels/trpc/types/hotel"
import type { Room } from "@scandic-hotels/trpc/types/room"
import type { DetailsBooking } from "@/types/components/hotelReservation/enterDetails/details"
import type { Room } from "@/types/providers/details/room"
interface TrackingWrapperProps {
booking: DetailsBooking

View File

@@ -1,17 +1,17 @@
import { differenceInCalendarDays, format, isWeekend } from "date-fns"
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import { REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
import { ChildBedMapEnum } from "@scandic-hotels/trpc/enums/childBedMapEnum"
import { PackageTypeEnum } from "@scandic-hotels/trpc/enums/packages"
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
import { REDEMPTION } from "@/constants/booking"
import { sumPackages } from "@/components/HotelReservation/utils"
import { getSpecialRoomType } from "@/utils/specialRoomType"
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { Hotel } from "@scandic-hotels/trpc/types/hotel"
import type { Room } from "@scandic-hotels/trpc/types/room"
import type {
PriceProduct,
Product,
@@ -28,7 +28,6 @@ import {
type TrackingSDKHotelInfo,
type TrackingSDKPageData,
} from "@/types/components/tracking"
import type { Room } from "@/types/providers/details/room"
import type { RoomState } from "@/types/stores/enter-details"
export function getTracking(

View File

@@ -7,7 +7,7 @@ import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import styles from "./hotelChequeCard.module.css"
import type { ProductTypeCheque } from "@/types/trpc/routers/hotel/availability"
import type { ProductTypeCheque } from "@scandic-hotels/trpc/types/availability"
export default function HotelChequeCard({
productTypeCheque,

View File

@@ -7,7 +7,7 @@ import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import styles from "./hotelVoucherCard.module.css"
import type { ProductTypeVoucher } from "@/types/trpc/routers/hotel/availability"
import type { ProductTypeVoucher } from "@scandic-hotels/trpc/types/availability"
export default function HotelVoucherCard({
productTypeVoucher,

View File

@@ -10,11 +10,12 @@ import {
import { memo } from "react"
import { useIntl } from "react-intl"
import { selectRate } from "@scandic-hotels/common/constants/routes/hotelReservation"
import { Divider } from "@scandic-hotels/design-system/Divider"
import HotelLogoIcon from "@scandic-hotels/design-system/Icons/HotelLogoIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { selectHotelMap, selectRate } from "@/constants/routes/hotelReservation"
import { selectHotelMap } from "@/constants/routes/hotelReservation"
import { useHotelsMapStore } from "@/stores/hotels-map"
import BookingCodeChip from "@/components/BookingCodeChip"

View File

@@ -3,12 +3,11 @@ import { useSession } from "next-auth/react"
import { useState } from "react"
import { useIntl } from "react-intl"
import { selectRate } from "@scandic-hotels/common/constants/routes/hotelReservation"
import { IconButton } from "@scandic-hotels/design-system/IconButton"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { selectRate } from "@/constants/routes/hotelReservation"
import { FacilityToIcon } from "@/components/ContentType/HotelPage/data"
import Button from "@/components/TempDesignSystem/Button"
import Link from "@/components/TempDesignSystem/Link"

View File

@@ -3,12 +3,11 @@ import { useSession } from "next-auth/react"
import { useState } from "react"
import { useIntl } from "react-intl"
import { selectRate } from "@scandic-hotels/common/constants/routes/hotelReservation"
import { IconButton } from "@scandic-hotels/design-system/IconButton"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { selectRate } from "@/constants/routes/hotelReservation"
import { FacilityToIcon } from "@/components/ContentType/HotelPage/data"
import Button from "@/components/TempDesignSystem/Button"
import Link from "@/components/TempDesignSystem/Link"

View File

@@ -8,8 +8,7 @@ import PaymentOption from "../PaymentOption"
import styles from "./mySavedCards.module.css"
import type { PaymentMethodEnum } from "@scandic-hotels/common/constants/paymentMethod"
import type { CreditCard } from "@/types/user"
import type { CreditCard } from "@scandic-hotels/trpc/types/user"
interface MySavedCardsProps {
savedCreditCards: CreditCard[] | null

View File

@@ -1,9 +1,10 @@
import type { User } from "@scandic-hotels/trpc/types/user"
import type {
Ancillaries,
Ancillary,
SelectedAncillary,
} from "@/types/components/myPages/myStay/ancillaries"
import type { User } from "@/types/user"
function filterPoints(ancillaries: Ancillaries, user: User | null) {
return ancillaries.map((ancillary) => {

View File

@@ -7,8 +7,9 @@ import BoldRow from "./Row/Bold"
import RegularRow from "./Row/Regular"
import Tbody from "./Tbody"
import type { Child } from "@scandic-hotels/trpc/types/child"
import type { BreakfastPackage } from "@/types/components/hotelReservation/breakfast"
import type { Child } from "@/types/components/hotelReservation/selectRate/selectRate"
interface BreakfastProps {
adults: number

View File

@@ -5,7 +5,7 @@ import { formatPrice } from "@/utils/numberFormatting"
import RegularRow from "../Regular"
import type { Packages as PackagesType } from "@/types/requests/packages"
import type { Packages as PackagesType } from "@scandic-hotels/trpc/types/packages"
interface PackagesProps {
packages: PackagesType | null

View File

@@ -1,5 +1,6 @@
import type { Packages } from "@scandic-hotels/trpc/types/packages"
import type { BedTypeSchema } from "@/types/components/hotelReservation/enterDetails/bedType"
import type { Packages } from "@/types/requests/packages"
export interface SharedPriceRowProps {
bedType: BedTypeSchema | undefined

View File

@@ -27,13 +27,13 @@ import Tbody from "./Tbody"
import styles from "./priceDetailsTable.module.css"
import type { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import type { Child } from "@scandic-hotels/trpc/types/child"
import type { Packages } from "@scandic-hotels/trpc/types/packages"
import type { RateDefinition } from "@scandic-hotels/trpc/types/roomAvailability"
import type { BreakfastPackage } from "@/types/components/hotelReservation/breakfast"
import type { BedTypeSchema } from "@/types/components/hotelReservation/enterDetails/bedType"
import type { Price } from "@/types/components/hotelReservation/price"
import type { Child } from "@/types/components/hotelReservation/selectRate/selectRate"
import type { Packages } from "@/types/requests/packages"
type RoomPrice =
| CorporateChequePriceType

View File

@@ -1,17 +1,21 @@
import { AvailabilityEnum } from "@scandic-hotels/trpc/enums/selectHotel"
import { generateChildrenString } from "@scandic-hotels/trpc/routers/hotels/helpers"
import {
type AdditionalData,
type Hotel,
} from "@scandic-hotels/trpc/types/hotel"
import {
type HotelLocation,
type Location,
} from "@scandic-hotels/trpc/types/locations"
import { getHotel } from "@/lib/trpc/memoizedRequests"
import { serverClient } from "@/lib/trpc/server"
import { getLang } from "@/i18n/serverContext"
import { generateChildrenString } from "../utils"
import type { AdditionalData, Hotel } from "@scandic-hotels/trpc/types/hotel"
import type {
HotelLocation,
Location,
} from "@scandic-hotels/trpc/types/locations"
import type { HotelsAvailabilityItem } from "@scandic-hotels/trpc/types/availability"
import type { Child } from "@scandic-hotels/trpc/types/child"
import type {
AlternativeHotelsAvailabilityInput,
@@ -21,8 +25,6 @@ import type {
CategorizedHotelFilters,
HotelFilter,
} from "@/types/components/hotelReservation/selectHotel/hotelFilters"
import type { Child } from "@/types/components/hotelReservation/selectRate/selectRate"
import type { HotelsAvailabilityItem } from "@/types/trpc/routers/hotel/availability"
interface AvailabilityResponse {
availability: HotelsAvailabilityItem[]

View File

@@ -4,7 +4,7 @@ import { usePathname, useSearchParams } from "next/navigation"
import { useEffect } from "react"
import { useIntl } from "react-intl"
import { BookingErrorCodeEnum } from "@/constants/booking"
import { BookingErrorCodeEnum } from "@scandic-hotels/trpc/enums/bookingErrorCode"
import { toast } from "@/components/TempDesignSystem/Toasts"

View File

@@ -14,12 +14,13 @@ import { getMemberPrice, isBookingCodeRate } from "../utils"
import styles from "./room.module.css"
import type { Child } from "@scandic-hotels/trpc/types/child"
import type { Packages } from "@scandic-hotels/trpc/types/packages"
import type {
RoomPrice,
RoomRate,
} from "@/types/components/hotelReservation/enterDetails/details"
import type { Child } from "@/types/components/hotelReservation/selectRate/selectRate"
import type { Packages } from "@/types/requests/packages"
interface RoomProps {
room: {

View File

@@ -1,11 +1,12 @@
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import type { Packages } from "@scandic-hotels/trpc/types/packages"
import type { Price } from "@/types/components/hotelReservation/price"
import type {
Rate,
Room,
} from "@/types/components/hotelReservation/selectRate/selectRate"
import type { Packages } from "@/types/requests/packages"
export function mapRate(
room: Rate,

View File

@@ -3,11 +3,11 @@ import { RateTypeEnum } from "@scandic-hotels/trpc/enums/rateType"
import { sumPackages } from "@/components/HotelReservation/utils"
import type { Packages } from "@scandic-hotels/trpc/types/packages"
import type { RedemptionProduct } from "@scandic-hotels/trpc/types/roomAvailability"
import type { Price } from "@/types/components/hotelReservation/price"
import type { Rate } from "@/types/components/hotelReservation/selectRate/selectRate"
import type { Packages } from "@/types/requests/packages"
export function calculateTotalPrice(
selectedRateSummary: Rate[],

View File

@@ -1,6 +1,6 @@
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
import type { PackageEnum } from "@/types/requests/packages"
import type { PackageEnum } from "@scandic-hotels/trpc/types/packages"
export function includesAllergyRoom(codes: PackageEnum[]) {
return codes.includes(RoomPackageCodeEnum.ALLERGY_ROOM)

View File

@@ -1,4 +1,4 @@
import type { PackageEnum } from "@/types/requests/packages"
import type { PackageEnum } from "@scandic-hotels/trpc/types/packages"
export type FormValues = {
selectedPackages: PackageEnum[]

View File

@@ -16,7 +16,8 @@ import Checkboxes from "./Checkboxes"
import styles from "./form.module.css"
import type { PackageEnum } from "@/types/requests/packages"
import type { PackageEnum } from "@scandic-hotels/trpc/types/packages"
import type { FormValues } from "./formValues"
export default function Form({ close }: { close: () => void }) {

View File

@@ -17,7 +17,7 @@ import { getIconNameByPackageCode } from "./utils"
import styles from "./roomPackageFilter.module.css"
import type { PackageEnum } from "@/types/requests/packages"
import type { PackageEnum } from "@scandic-hotels/trpc/types/packages"
export default function RoomPackageFilter() {
const lang = useLang()

View File

@@ -1,8 +1,7 @@
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
import type { MaterialSymbolProps } from "@scandic-hotels/design-system/Icons/MaterialIcon/MaterialSymbol"
import type { PackageEnum } from "@/types/requests/packages"
import type { PackageEnum } from "@scandic-hotels/trpc/types/packages"
export function getIconNameByPackageCode(
packageCode: PackageEnum

View File

@@ -3,7 +3,7 @@
import { useSearchParams } from "next/navigation"
import React from "react"
import { REDEMPTION } from "@/constants/booking"
import { REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
import TrackingSDK from "@/components/TrackingSDK"
import useLang from "@/hooks/useLang"

View File

@@ -1,6 +1,7 @@
import { ChildBedMapEnum } from "@scandic-hotels/trpc/enums/childBedMapEnum"
import type { Child } from "@/types/components/hotelReservation/selectRate/selectRate"
import type { Child } from "@scandic-hotels/trpc/types/child"
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
export function convertToChildType(

View File

@@ -6,14 +6,10 @@ import { ChildBedMapEnum } from "@scandic-hotels/trpc/enums/childBedMapEnum"
import { ChildBedTypeEnum } from "@scandic-hotels/trpc/enums/childBedTypeEnum"
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
import type { Packages } from "@scandic-hotels/trpc/types/packages"
import type { JSX } from "react"
import {
// RoomPackageCodeEnum,
type RoomPackageCodes,
} from "@/types/components/hotelReservation/selectRate/roomFilter"
import type { Child } from "@/types/components/hotelReservation/selectRate/selectRate"
import type { Packages } from "@/types/requests/packages"
import { type RoomPackageCodes } from "@/types/components/hotelReservation/selectRate/roomFilter"
interface IconForFeatureCodeProps {
featureCode: RoomPackageCodes
@@ -33,13 +29,6 @@ export function IconForFeatureCode({
}
}
export const bedTypeMap: Record<number, ChildBedTypeEnum> = {
[ChildBedMapEnum.IN_ADULTS_BED]: ChildBedTypeEnum.ParentsBed,
[ChildBedMapEnum.IN_CRIB]: ChildBedTypeEnum.Crib,
[ChildBedMapEnum.IN_EXTRA_BED]: ChildBedTypeEnum.ExtraBed,
[ChildBedMapEnum.UNKNOWN]: ChildBedTypeEnum.Unknown,
}
export const invertedBedTypeMap: Record<ChildBedTypeEnum, string> = {
[ChildBedTypeEnum.ParentsBed]: ChildBedMapEnum[ChildBedMapEnum.IN_ADULTS_BED],
[ChildBedTypeEnum.Crib]: ChildBedMapEnum[ChildBedMapEnum.IN_CRIB],
@@ -47,16 +36,6 @@ export const invertedBedTypeMap: Record<ChildBedTypeEnum, string> = {
[ChildBedTypeEnum.Unknown]: ChildBedMapEnum[ChildBedMapEnum.UNKNOWN],
}
export function generateChildrenString(children: Child[]): string {
return `[${children
.map((child) => {
const age = child.age
const bedType = bedTypeMap[parseInt(child.bed.toString())]
return `${age}:${bedType}`
})
.join(",")}]`
}
export function sumPackages(packages: Packages | null) {
if (!packages || !packages.length) {
return {

View File

@@ -2,7 +2,7 @@ import { getInitials } from "@/utils/user"
import styles from "./avatar.module.css"
import type { User } from "@/types/user"
import type { User } from "@scandic-hotels/trpc/types/user"
export default function Avatar({
firstName,

View File

@@ -15,7 +15,7 @@ import DigitalTeamMemberCardContent from "./Content"
import styles from "./digitalTeamMemberCard.module.css"
import type { User } from "@/types/user"
import type { User } from "@scandic-hotels/trpc/types/user"
interface DigitalTeamMemberCardClientProps {
user: User

View File

@@ -9,7 +9,7 @@ import { debounce } from "@/utils/debounce"
import styles from "./digitalTeamMemberCard.module.css"
import type { User } from "@/types/user"
import type { User } from "@scandic-hotels/trpc/types/user"
interface DigitalTeamMemberCardCardProps {
user: User

View File

@@ -2,7 +2,7 @@ import { env } from "@/env/server"
import DigitalTeamMemberCardClient from "./Client"
import type { User } from "@/types/user"
import type { User } from "@scandic-hotels/trpc/types/user"
interface DigitalTeamMemberCardProps {
user: User

View File

@@ -3,8 +3,8 @@ import { isValidLang } from "@scandic-hotels/common/utils/languages"
import { Divider } from "@scandic-hotels/design-system/Divider"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { countriesMap } from "@scandic-hotels/trpc/constants/countries"
import { countriesMap } from "@/constants/countries"
import { languages } from "@/constants/languages"
import { profileEdit } from "@/constants/routes/myPages"
import { getProfile } from "@/lib/trpc/memoizedRequests"

View File

@@ -8,7 +8,7 @@ import CreditCardRow from "../CreditCardRow"
import styles from "./CreditCardList.module.css"
import type { CreditCard } from "@/types/user"
import type { CreditCard } from "@scandic-hotels/trpc/types/user"
export default function CreditCardList({
initialData,

View File

@@ -25,13 +25,13 @@ import RoomDetails from "./RoomDetails"
import styles from "./bookedRoomSidePeek.module.css"
import type { Child } from "@scandic-hotels/trpc/types/child"
import type { Room as HotelRoom } from "@scandic-hotels/trpc/types/hotel"
import type { Packages } from "@scandic-hotels/trpc/types/packages"
import type { BreakfastPackage } from "@/types/components/hotelReservation/breakfast"
import type { BedTypeSchema } from "@/types/components/hotelReservation/enterDetails/bedType"
import type { PriceTypeEnum } from "@/types/components/hotelReservation/myStay/myStay"
import type { Child } from "@/types/components/hotelReservation/selectRate/selectRate"
import type { Packages } from "@/types/requests/packages"
import type { BookingConfirmationSchema } from "@/types/trpc/routers/booking/confirmation"
import type { SafeUser } from "@/types/user"

View File

@@ -16,8 +16,7 @@ import { useIntl } from "react-intl"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { countries } from "@/constants/countries"
import { countries } from "@scandic-hotels/trpc/constants/countries"
import useLang from "@/hooks/useLang"

View File

@@ -5,8 +5,7 @@ import { useController } from "react-hook-form"
import { useIntl } from "react-intl"
import { Select } from "@scandic-hotels/design-system/Select"
import { countries } from "@/constants/countries"
import { countries } from "@scandic-hotels/trpc/constants/countries"
import useLang from "@/hooks/useLang"

View File

@@ -7,9 +7,14 @@ import BedSingleIcon from "@scandic-hotels/design-system/Icons/BedSingleIcon"
import BedSofaExtraIcon from "@scandic-hotels/design-system/Icons/BedSofaExtraIcon"
import BedTwinIcon from "@scandic-hotels/design-system/Icons/BedTwinIcon"
import BedWallExtraIcon from "@scandic-hotels/design-system/Icons/BedWallExtraIcon"
import { REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
import type { PaymentMethodEnum } from "@scandic-hotels/common/constants/paymentMethod"
import type { IconProps } from "@scandic-hotels/design-system/Icons"
import type {
BedTypeEnum,
ExtraBedTypeEnum,
} from "@scandic-hotels/trpc/enums/bedType"
import type { JSX } from "react"
export enum BookingStatusEnum {
@@ -31,25 +36,11 @@ export enum BookingStatusEnum {
export const FamilyAndFriendsCodes = ["D000029555", "D000029271", "D000029195"]
export const REDEMPTION = "redemption"
export const bookingSearchTypes = [REDEMPTION] as const
export const SEARCHTYPE = "searchtype"
export const MEMBERSHIP_FAILED_ERROR = "MembershipFailedError"
export enum BookingErrorCodeEnum {
InternalError = "InternalError",
ReservationError = "ReservationError",
AvailabilityError = "AvailabilityError",
BookingStatusNotFound = "BookingStatusNotFound",
TransactionAbandoned = "TransactionAbandoned",
TransactionCancelled = "TransactionCancelled",
TransactionFailed = "TransactionFailed",
BookingStateError = "BookingStateError",
MembershipFailedError = "MembershipFailedError",
NoAvailabilityForRateAndRoomType = "NoAvailabilityForRateAndRoomType",
}
export const PAYMENT_METHOD_TITLES: Record<
keyof typeof PaymentMethodEnum,
string
@@ -100,21 +91,6 @@ export const PAYMENT_METHOD_ICONS: Record<
discover: "/_static/icons/payment/discover.svg",
}
export enum BedTypeEnum {
King = "King",
Queen = "Queen",
Single = "Single",
Twin = "Twin",
Other = "Other",
}
export enum ExtraBedTypeEnum {
SofaBed = "SofaBed",
WallBed = "WallBed",
PullOutBed = "PullOutBed",
BunkBed = "BunkBed",
}
export type BedTypes = keyof typeof BedTypeEnum | keyof typeof ExtraBedTypeEnum
export const BED_TYPE_ICONS: Record<

View File

@@ -1,257 +0,0 @@
export const countriesMap = {
Afghanistan: "AF",
Albania: "AL",
Algeria: "DZ",
"American Samoa": "AS",
Andorra: "AD",
Angola: "AO",
Anguilla: "AI",
Antarctica: "AQ",
"Antigua and Barbuda": "AG",
Argentina: "AR",
Armenia: "AM",
Aruba: "AW",
Australia: "AU",
Austria: "AT",
Azerbaijan: "AZ",
Bahamas: "BS",
Bahrain: "BH",
Bangladesh: "BD",
Barbados: "BB",
Belarus: "BY",
Belgium: "BE",
Belize: "BZ",
Benin: "BJ",
Bermuda: "BM",
Bhutan: "BT",
Bolivia: "BO",
Bonaire: "BQ",
"Bosnia and Herzegovina": "BA",
Botswana: "BW",
"Bouvet Island": "BV",
Brazil: "BR",
"British Indian Ocean Territory": "IO",
"Brunei Darussalam": "BN",
Bulgaria: "BG",
"Burkina Faso": "BF",
Burundi: "BI",
Cambodia: "KH",
Cameroon: "CM",
Canada: "CA",
"Cape Verde": "CV",
"Cayman Islands": "KY",
"Central African Republic": "CF",
Chad: "TD",
Chile: "CL",
China: "CN",
"Christmas Island": "CX",
"Cocos (Keeling) Islands": "CC",
Colombia: "CO",
Comoros: "KM",
Congo: "CG",
"Congo, The Democratic Republic of the": "CD",
"Cook Islands": "CK",
"Costa Rica": "CR",
"Côte d'Ivoire": "CI",
Croatia: "HR",
Cuba: "CU",
Curacao: "CW",
Cyprus: "CY",
"Czech Republic": "CZ",
Denmark: "DK",
Djibouti: "DJ",
Dominica: "DM",
"Dominican Republic": "DO",
Ecuador: "EC",
Egypt: "EG",
"El Salvador": "SV",
"Equatorial Guinea": "GQ",
Eritrea: "ER",
Estonia: "EE",
Eswatini: "SZ",
Ethiopia: "ET",
"Falkland Islands (Malvinas)": "FK",
"Faroe Islands": "FO",
Fiji: "FJ",
Finland: "FI",
France: "FR",
"French Guiana": "GF",
"French Polynesia": "PF",
"French Southern Territories": "TF",
Gabon: "GA",
Gambia: "GM",
Georgia: "GE",
Germany: "DE",
Ghana: "GH",
Gibraltar: "GI",
Greece: "GR",
Greenland: "GL",
Grenada: "GD",
Guadeloupe: "GP",
Guam: "GU",
Guatemala: "GT",
Guernsey: "GG",
Guinea: "GN",
"Guinea-Bissau": "GW",
Guyana: "GY",
Haiti: "HT",
"Heard Island and Mcdonald Islands": "HM",
"Holy See (Vatican City State)": "VA",
Honduras: "HN",
"Hong Kong": "HK",
Hungary: "HU",
Iceland: "IS",
India: "IN",
Indonesia: "ID",
"Iran, Islamic Republic Of": "IR",
Iraq: "IQ",
Ireland: "IE",
"Isle of Man": "IM",
Israel: "IL",
Italy: "IT",
Jamaica: "JM",
Japan: "JP",
Jersey: "JE",
Jordan: "JO",
Kazakhstan: "KZ",
Kenya: "KE",
Kiribati: "KI",
'Korea, Democratic People"S Republic of': "KP",
"Korea, Republic of": "KR",
Kuwait: "KW",
Kyrgyzstan: "KG",
Laos: "LA",
Latvia: "LV",
Lebanon: "LB",
Lesotho: "LS",
Liberia: "LR",
"Libyan Arab Jamahiriya": "LY",
Liechtenstein: "LI",
Lithuania: "LT",
Luxembourg: "LU",
Macao: "MO",
"Macedonia, The Former Yugoslav Republic of": "MK",
Madagascar: "MG",
Malawi: "MW",
Malaysia: "MY",
Maldives: "MV",
Mali: "ML",
Malta: "MT",
"Marshall Islands": "MH",
Martinique: "MQ",
Mauritania: "MR",
Mauritius: "MU",
Mayotte: "YT",
Mexico: "MX",
"Micronesia, Federated States of": "FM",
"Moldova, Republic of": "MD",
Monaco: "MC",
Mongolia: "MN",
Montenegro: "ME",
Montserrat: "MS",
Morocco: "MA",
Mozambique: "MZ",
Myanmar: "MM",
Namibia: "NA",
Nauru: "NR",
Nepal: "NP",
Netherlands: "NL",
"Netherlands Antilles": "AN",
"New Caledonia": "NC",
"New Zealand": "NZ",
Nicaragua: "NI",
Niger: "NE",
Nigeria: "NG",
Niue: "NU",
"Norfolk Island": "NF",
"Northern Mariana Islands": "MP",
Norway: "NO",
Oman: "OM",
Pakistan: "PK",
Palau: "PW",
Palestine: "PS",
Panama: "PA",
"Papua New Guinea": "PG",
Paraguay: "PY",
Peru: "PE",
Philippines: "PH",
Pitcairn: "PN",
Poland: "PL",
Portugal: "PT",
"Puerto Rico": "PR",
Qatar: "QA",
RWANDA: "RW",
Reunion: "RE",
Romania: "RO",
"Russian Federation": "RU",
"Saint Barthelemy": "BL",
"Saint Helena": "SH",
"Saint Kitts and Nevis": "KN",
"Saint Lucia": "LC",
"Saint Martin": "MF",
"Saint Pierre and Miquelon": "PM",
"Saint Vincent and the Grenadines": "VC",
Samoa: "WS",
"San Marino": "SM",
"Sao Tome and Principe": "ST",
"Saudi Arabia": "SA",
Senegal: "SN",
Serbia: "RS",
Seychelles: "SC",
"Sierra Leone": "SL",
Singapore: "SG",
"Sint Maarten": "SX",
Slovakia: "SK",
Slovenia: "SI",
"Solomon Islands": "SB",
Somalia: "SO",
"South Africa": "ZA",
"South Georgia and the South Sandwich Islands": "GS",
"South Sudan": "SS",
Spain: "ES",
"Sri Lanka": "LK",
Sudan: "SD",
Suriname: "SR",
"Svalbard and Jan Mayen": "SJ",
Sweden: "SE",
Switzerland: "CH",
"Syrian Arab Republic": "SY",
Taiwan: "TW",
Tajikistan: "TJ",
"Tanzania, United Republic of": "TZ",
Thailand: "TH",
"Timor-Leste": "TL",
Togo: "TG",
Tokelau: "TK",
Tonga: "TO",
"Trinidad and Tobago": "TT",
Tunisia: "TN",
Turkey: "TR",
Turkmenistan: "TM",
"Turks and Caicos Islands": "TC",
Tuvalu: "TV",
Uganda: "UG",
Ukraine: "UA",
"United Arab Emirates": "AE",
"United Kingdom": "GB",
"United States": "US",
"United States Minor Outlying Islands": "UM",
Uruguay: "UY",
Uzbekistan: "UZ",
Vanuatu: "VU",
Venezuela: "VE",
Vietnam: "VN",
"Virgin Islands, British": "VG",
"Virgin Islands, U.S.": "VI",
"Wallis and Futuna": "WF",
"Western Sahara": "EH",
Yemen: "YE",
Zambia: "ZM",
Zimbabwe: "ZW",
"Åland Islands": "AX",
} as const
export const countries = Object.keys(countriesMap).map((country) => ({
code: countriesMap[country as keyof typeof countriesMap],
name: country as keyof typeof countriesMap,
}))

View File

@@ -1,6 +1,6 @@
import { MembershipLevelEnum } from "@scandic-hotels/common/constants/membershipLevels"
import type { EurobonusTier } from "@/types/user"
import type { EurobonusTier } from "@scandic-hotels/trpc/types/user"
export const FriendsMembershipLevels = [
"L1",

View File

@@ -37,13 +37,6 @@ export function selectHotelMap(lang) {
return `${hotelreservation(lang)}/select-hotel/map`
}
/**
* @param {Lang} lang
*/
export function selectRate(lang) {
return `${hotelreservation(lang)}/select-rate`
}
/**
* @param {Lang} lang
* @param {string} hotelId

View File

@@ -1,2 +1,4 @@
import "@scandic-hotels/common/global.d.ts"
import "@scandic-hotels/trpc/types.d.ts"
import "@scandic-hotels/trpc/auth.d.ts"
import "@scandic-hotels/trpc/jwt.d.ts"

View File

@@ -4,7 +4,7 @@ import { usePathname, useRouter, useSearchParams } from "next/navigation"
import { useCallback, useEffect } from "react"
import { useIntl } from "react-intl"
import { BookingErrorCodeEnum } from "@/constants/booking"
import { BookingErrorCodeEnum } from "@scandic-hotels/trpc/enums/bookingErrorCode"
import { toast } from "@/components/TempDesignSystem/Toasts"

View File

@@ -9,18 +9,18 @@ import { serverClient } from "../server"
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { GetHotelsByCSFilterInput } from "@scandic-hotels/trpc/routers/hotels/input"
import type { RoomsAvailabilityExtendedInputSchema } from "@scandic-hotels/trpc/types/availability"
import type { Country } from "@scandic-hotels/trpc/types/country"
import type {
CityCoordinatesInput,
HotelInput,
} from "@scandic-hotels/trpc/types/hotel"
import type {
AncillaryPackagesInput,
BreackfastPackagesInput,
PackagesInput,
} from "@/types/requests/packages"
import type { RoomsAvailabilityExtendedInputSchema } from "@/types/trpc/routers/hotel/availability"
} from "@scandic-hotels/trpc/types/packages"
import type { GetSavedPaymentCardsInput } from "@/server/routers/user/input"
export const getLocations = cache(async function getMemoizedLocations() {

View File

@@ -1,11 +1,13 @@
import { type NextMiddleware, NextResponse } from "next/server"
import { REDEMPTION, SEARCHTYPE } from "@/constants/booking"
import { findLang } from "@scandic-hotels/common/utils/languages"
import { REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
import { SEARCHTYPE } from "@/constants/booking"
import { login } from "@/constants/routes/handleAuth"
import { getPublicNextURL } from "@/server/utils"
import { auth } from "@/auth"
import { findLang } from "@scandic-hotels/common/utils/languages"
import { isValidSession } from "@/utils/session"
import { getDefaultRequestHeaders } from "./utils"

View File

@@ -3,6 +3,8 @@ import { notFound } from "next/navigation"
import { use, useRef } from "react"
import { useIntl } from "react-intl"
import { type RoomCategories } from "@scandic-hotels/trpc/types/hotel"
import { trpc } from "@/lib/trpc/client"
import { createMyStayStore } from "@/stores/my-stay"
@@ -10,15 +12,14 @@ import { MyStaySkeleton } from "@/components/HotelReservation/MyStay/myStaySkele
import { MyStayContext } from "@/contexts/MyStay"
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { CreditCard } from "@scandic-hotels/trpc/types/user"
import type { Packages } from "@/types/components/myPages/myStay/ancillaries"
import type { MyStayStore } from "@/types/contexts/my-stay"
import type { RoomCategories } from "@scandic-hotels/trpc/types/hotel"
import type {
BookingConfirmation,
BookingConfirmationSchema,
} from "@/types/trpc/routers/booking/confirmation"
import type { CreditCard } from "@/types/user"
interface MyStayProviderProps {
bookingConfirmation: BookingConfirmation

View File

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

View File

@@ -1,5 +0,0 @@
import { mergeRouters } from "@scandic-hotels/trpc"
import { hotelQueryRouter } from "./query"
export const hotelsRouter = mergeRouters(hotelQueryRouter)

File diff suppressed because it is too large Load Diff

View File

@@ -1,49 +0,0 @@
import { z } from "zod"
import { imageSchema } from "@scandic-hotels/trpc/routers/hotels/schemas/image"
export const meetingRoomsSchema = z.object({
data: z.array(
z.object({
attributes: z.object({
name: z.string(),
email: z.string().optional(),
phoneNumber: z.string(),
size: z.number(),
doorWidth: z.number(),
doorHeight: z.number(),
length: z.number(),
width: z.number(),
height: z.number(),
floorNumber: z.number(),
content: z.object({
images: z.array(imageSchema),
texts: z.object({
facilityInformation: z.string().optional(),
surroundingInformation: z.string().optional(),
descriptions: z.object({
short: z.string().optional(),
medium: z.string().optional(),
}),
meetingDescription: z
.object({
short: z.string().optional(),
medium: z.string().optional(),
})
.optional(),
}),
}),
seatings: z.array(
z.object({
type: z.string(),
capacity: z.number(),
})
),
lighting: z.string(),
sortOrder: z.number().optional(),
}),
id: z.string(),
type: z.string(),
})
),
})

View File

@@ -1,751 +0,0 @@
import stringify from "json-stable-stringify-without-jsonify"
import { getCacheClient } from "@scandic-hotels/common/dataCache"
import { createCounter } from "@scandic-hotels/common/telemetry"
import * as api from "@scandic-hotels/trpc/api"
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
import { AvailabilityEnum } from "@scandic-hotels/trpc/enums/selectHotel"
import { badRequestError } from "@scandic-hotels/trpc/errors"
import { type RoomFeaturesInput } from "@scandic-hotels/trpc/routers/hotels/input"
import {
hotelsAvailabilitySchema,
packagesSchema,
roomFeaturesSchema,
roomsAvailabilitySchema,
} from "@scandic-hotels/trpc/routers/hotels/output"
import { toApiLang } from "@scandic-hotels/trpc/utils"
import { sortRoomConfigs } from "@scandic-hotels/trpc/utils/sortRoomConfigs"
import { BookingErrorCodeEnum, REDEMPTION } from "@/constants/booking"
import { selectRate } from "@/constants/routes/hotelReservation"
import { env } from "@/env/server"
import { generateChildrenString } from "@/components/HotelReservation/utils"
import type { Room as RoomCategory } from "@scandic-hotels/trpc/types/hotel"
import type {
Product,
Products,
RateDefinition,
RedemptionsProduct,
RoomConfiguration,
} from "@scandic-hotels/trpc/types/roomAvailability"
import type { BedTypeSelection } from "@/types/components/hotelReservation/enterDetails/bedType"
import type { PackagesOutput } from "@/types/requests/packages"
import type {
HotelsAvailabilityInputSchema,
HotelsByHotelIdsAvailabilityInputSchema,
RoomsAvailabilityExtendedInputSchema,
RoomsAvailabilityInputRoom,
RoomsAvailabilityOutputSchema,
} from "@/types/trpc/routers/hotel/availability"
export const TWENTYFOUR_HOURS = 60 * 60 * 24
function findProduct(product: Products, rateDefinition: RateDefinition) {
if ("corporateCheque" in product) {
return product.corporateCheque.rateCode === rateDefinition.rateCode
}
if (("member" in product && product.member) || "public" in product) {
let isMemberRate = false
if (product.member) {
isMemberRate = product.member.rateCode === rateDefinition.rateCode
}
let isPublicRate = false
if (product.public) {
isPublicRate = product.public.rateCode === rateDefinition.rateCode
}
return isMemberRate || isPublicRate
}
if ("voucher" in product) {
return product.voucher.rateCode === rateDefinition.rateCode
}
if (Array.isArray(product)) {
return product.find(
(r) => r.redemption.rateCode === rateDefinition.rateCode
)
}
}
export async function getHotelsAvailabilityByCity(
input: HotelsAvailabilityInputSchema,
apiLang: string,
token: string, // Either service token or user access token in case of redemption search
userPoints: number = 0
) {
const {
cityId,
roomStayStartDate,
roomStayEndDate,
adults,
children,
bookingCode,
redemption,
} = input
const params: Record<string, string | number> = {
roomStayStartDate,
roomStayEndDate,
adults,
...(children && { children }),
...(bookingCode && { bookingCode }),
...(redemption ? { isRedemption: "true" } : {}),
language: apiLang,
}
const getHotelsAvailabilityByCityCounter = createCounter(
"hotel",
"getHotelsAvailabilityByCity"
)
const metricsGetHotelsAvailabilityByCity =
getHotelsAvailabilityByCityCounter.init({
apiLang,
cityId,
roomStayStartDate,
roomStayEndDate,
adults,
children,
bookingCode,
redemption,
})
metricsGetHotelsAvailabilityByCity.start()
const apiResponse = await api.get(
api.endpoints.v1.Availability.city(cityId),
{
headers: {
Authorization: `Bearer ${token}`,
},
},
params
)
if (!apiResponse.ok) {
await metricsGetHotelsAvailabilityByCity.httpError(apiResponse)
throw new Error("Failed to fetch hotels availability by city")
}
const apiJson = await apiResponse.json()
const validateAvailabilityData = hotelsAvailabilitySchema.safeParse(apiJson)
if (!validateAvailabilityData.success) {
metricsGetHotelsAvailabilityByCity.validationError(
validateAvailabilityData.error
)
throw badRequestError()
}
if (redemption) {
validateAvailabilityData.data.data.forEach((data) => {
data.attributes.productType?.redemptions?.forEach((r) => {
r.hasEnoughPoints = userPoints >= r.localPrice.pointsPerStay
})
})
}
const result = {
availability: validateAvailabilityData.data.data.flatMap(
(hotels) => hotels.attributes
),
}
metricsGetHotelsAvailabilityByCity.success()
return result
}
export async function getHotelsAvailabilityByHotelIds(
input: HotelsByHotelIdsAvailabilityInputSchema,
apiLang: string,
serviceToken: string
) {
const {
hotelIds,
roomStayStartDate,
roomStayEndDate,
adults,
children,
bookingCode,
} = input
const params = new URLSearchParams([
["roomStayStartDate", roomStayStartDate],
["roomStayEndDate", roomStayEndDate],
["adults", adults.toString()],
["children", children ?? ""],
["bookingCode", bookingCode],
["language", apiLang],
])
const getHotelsAvailabilityByHotelIdsCounter = createCounter(
"hotel",
"getHotelsAvailabilityByHotelIds"
)
const metricsGetHotelsAvailabilityByHotelIds =
getHotelsAvailabilityByHotelIdsCounter.init({
apiLang,
hotelIds,
roomStayStartDate,
roomStayEndDate,
adults,
children,
bookingCode,
})
metricsGetHotelsAvailabilityByHotelIds.start()
const cacheClient = await getCacheClient()
const result = cacheClient.cacheOrGet(
`${apiLang}:hotels:availability:${hotelIds.join(",")}:${roomStayStartDate}:${roomStayEndDate}:${adults}:${children}:${bookingCode}`,
async () => {
/**
* Since API expects the params appended and not just
* a comma separated string we need to initialize the
* SearchParams with a sequence of pairs
* (hotelIds=810&hotelIds=879&hotelIds=222 etc.)
**/
hotelIds.forEach((hotelId) =>
params.append("hotelIds", hotelId.toString())
)
const apiResponse = await api.get(
api.endpoints.v1.Availability.hotels(),
{
headers: {
Authorization: `Bearer ${serviceToken}`,
},
},
params
)
if (!apiResponse.ok) {
await metricsGetHotelsAvailabilityByHotelIds.httpError(apiResponse)
throw new Error("Failed to fetch hotels availability by hotelIds")
}
const apiJson = await apiResponse.json()
const validateAvailabilityData =
hotelsAvailabilitySchema.safeParse(apiJson)
if (!validateAvailabilityData.success) {
metricsGetHotelsAvailabilityByHotelIds.validationError(
validateAvailabilityData.error
)
throw badRequestError()
}
return {
availability: validateAvailabilityData.data.data.flatMap(
(hotels) => hotels.attributes
),
}
},
env.CACHE_TIME_CITY_SEARCH
)
metricsGetHotelsAvailabilityByHotelIds.success()
return result
}
async function getRoomFeaturesInventory(
input: RoomFeaturesInput,
token: string
) {
const {
adults,
childrenInRoom,
endDate,
hotelId,
roomFeatureCodes,
startDate,
} = input
const params = {
adults,
hotelId,
roomFeatureCode: roomFeatureCodes,
roomStayEndDate: endDate,
roomStayStartDate: startDate,
...(childrenInRoom?.length && {
children: generateChildrenString(childrenInRoom),
}),
}
const getRoomFeaturesInventoryCounter = createCounter(
"hotel",
"getRoomFeaturesInventory"
)
const metricsGetRoomFeaturesInventory =
getRoomFeaturesInventoryCounter.init(params)
metricsGetRoomFeaturesInventory.start()
const cacheClient = await getCacheClient()
const result = cacheClient.cacheOrGet(
stringify(input),
async function () {
const apiResponse = await api.get(
api.endpoints.v1.Availability.roomFeatures(hotelId),
{
headers: {
Authorization: `Bearer ${token}`,
},
},
params
)
if (!apiResponse.ok) {
await metricsGetRoomFeaturesInventory.httpError(apiResponse)
return null
}
const data = await apiResponse.json()
const validatedRoomFeaturesData = roomFeaturesSchema.safeParse(data)
if (!validatedRoomFeaturesData.success) {
metricsGetRoomFeaturesInventory.validationError(
validatedRoomFeaturesData.error
)
return null
}
return validatedRoomFeaturesData.data
},
"5m"
)
metricsGetRoomFeaturesInventory.success()
return result
}
export async function getPackages(input: PackagesOutput, serviceToken: string) {
const { adults, children, endDate, hotelId, lang, packageCodes, startDate } =
input
const getPackagesCounter = createCounter("hotel", "getPackages")
const metricsGetPackages = getPackagesCounter.init({
input,
})
metricsGetPackages.start()
const cacheClient = await getCacheClient()
const result = cacheClient.cacheOrGet(
stringify(input),
async function () {
const apiLang = toApiLang(lang)
const searchParams = new URLSearchParams({
adults: adults.toString(),
children: children.toString(),
endDate,
language: apiLang,
startDate,
})
packageCodes.forEach((code) => {
searchParams.append("packageCodes", code)
})
const apiResponse = await api.get(
api.endpoints.v1.Package.Packages.hotel(hotelId),
{
headers: {
Authorization: `Bearer ${serviceToken}`,
},
},
searchParams
)
if (!apiResponse.ok) {
await metricsGetPackages.httpError(apiResponse)
return null
}
const apiJson = await apiResponse.json()
const validatedPackagesData = packagesSchema.safeParse(apiJson)
if (!validatedPackagesData.success) {
metricsGetPackages.validationError(validatedPackagesData.error)
return null
}
return validatedPackagesData.data
},
"3h"
)
metricsGetPackages.success()
return result
}
export async function getRoomsAvailability(
input: RoomsAvailabilityOutputSchema,
token: string,
serviceToken: string,
userPoints: number | undefined
) {
const {
booking: { bookingCode, fromDate, hotelId, rooms, searchType, toDate },
lang,
} = input
const redemption = searchType === REDEMPTION
const getRoomsAvailabilityCounter = createCounter(
"hotel",
"getRoomsAvailability"
)
const metricsGetRoomsAvailability = getRoomsAvailabilityCounter.init({
input,
redemption,
})
metricsGetRoomsAvailability.start()
const apiLang = toApiLang(lang)
const baseCacheKey = {
bookingCode,
fromDate,
hotelId,
lang,
searchType,
toDate,
}
const cacheClient = await getCacheClient()
const availabilityResponses = await Promise.allSettled(
rooms.map((room: RoomsAvailabilityInputRoom) => {
const cacheKey = {
...baseCacheKey,
room,
}
const result = cacheClient.cacheOrGet(
stringify(cacheKey),
async function () {
{
const params = {
adults: room.adults,
language: apiLang,
roomStayStartDate: fromDate,
roomStayEndDate: toDate,
...(room.childrenInRoom?.length && {
children: generateChildrenString(room.childrenInRoom),
}),
...(room.bookingCode && { bookingCode: room.bookingCode }),
...(redemption && { isRedemption: "true" }),
}
const apiResponse = await api.get(
api.endpoints.v1.Availability.hotel(hotelId),
{
cache: undefined, // overwrite default
headers: {
Authorization: `Bearer ${token}`,
},
},
params
)
if (!apiResponse.ok) {
await metricsGetRoomsAvailability.httpError(apiResponse)
const text = await apiResponse.text()
return { error: "http_error", details: text }
}
const apiJson = await apiResponse.json()
const validateAvailabilityData =
roomsAvailabilitySchema.safeParse(apiJson)
if (!validateAvailabilityData.success) {
metricsGetRoomsAvailability.validationError(
validateAvailabilityData.error
)
return {
error: "validation_error",
details: validateAvailabilityData.error,
}
}
if (redemption) {
for (const roomConfig of validateAvailabilityData.data
.roomConfigurations) {
for (const product of roomConfig.redemptions) {
if (userPoints) {
product.redemption.hasEnoughPoints =
userPoints >= product.redemption.localPrice.pointsPerStay
}
}
}
}
const roomFeatures = await getPackages(
{
adults: room.adults,
children: room.childrenInRoom?.length || 0,
endDate: input.booking.toDate,
hotelId: input.booking.hotelId,
lang,
packageCodes: [
RoomPackageCodeEnum.ACCESSIBILITY_ROOM,
RoomPackageCodeEnum.ALLERGY_ROOM,
RoomPackageCodeEnum.PET_ROOM,
],
startDate: input.booking.fromDate,
},
serviceToken
)
if (roomFeatures) {
validateAvailabilityData.data.packages = roomFeatures
}
// Fetch packages
if (room.packages?.length) {
const roomFeaturesInventory = await getRoomFeaturesInventory(
{
adults: room.adults,
childrenInRoom: room.childrenInRoom,
endDate: input.booking.toDate,
hotelId: input.booking.hotelId,
lang,
roomFeatureCodes: room.packages,
startDate: input.booking.fromDate,
},
serviceToken
)
if (roomFeaturesInventory) {
const features = roomFeaturesInventory.reduce<
Record<string, number>
>((fts, feat) => {
fts[feat.roomTypeCode] = feat.features?.[0]?.inventory ?? 0
return fts
}, {})
const updatedRoomConfigurations =
validateAvailabilityData.data.roomConfigurations
// This filter is needed since we can get availability
// back from roomFeatures yet the availability call
// says there are no rooms left...
.filter((rc) => rc.roomsLeft)
.filter((rc) => features?.[rc.roomTypeCode])
.map((rc) => ({
...rc,
roomsLeft: features[rc.roomTypeCode],
status: AvailabilityEnum.Available,
}))
validateAvailabilityData.data.roomConfigurations =
updatedRoomConfigurations
}
}
return validateAvailabilityData.data
}
},
"1m"
)
return result
})
)
const data = availabilityResponses.map((availability) => {
if (availability.status === "fulfilled") {
return availability.value
}
return {
details: availability.reason,
error: "request_failure",
}
})
metricsGetRoomsAvailability.success()
return data
}
export function getSelectedRoomAvailability(
rateCode: string,
rateDefinitions: RateDefinition[],
roomConfigurations: RoomConfiguration[],
roomTypeCode: string,
userPoints: number | undefined
) {
const rateDefinition = rateDefinitions.find((rd) => rd.rateCode === rateCode)
if (!rateDefinition) {
return null
}
const selectedRoom = roomConfigurations.find(
(room) =>
room.roomTypeCode === roomTypeCode &&
room.products.find((product) => findProduct(product, rateDefinition))
)
if (!selectedRoom) {
return null
}
let product: Product | RedemptionsProduct | undefined =
selectedRoom.products.find((product) =>
findProduct(product, rateDefinition)
)
if (!product) {
return null
}
if (Array.isArray(product)) {
const redemptionProduct = userPoints
? product.find(
(r) =>
r.redemption.rateCode === rateDefinition.rateCode &&
r.redemption.localPrice.pointsPerStay <= userPoints
)
: undefined
if (!redemptionProduct) {
return null
}
product = redemptionProduct
}
return {
rateDefinition,
rateDefinitions,
rooms: roomConfigurations,
product,
selectedRoom,
}
}
export function getBedTypes(
rooms: RoomConfiguration[],
roomType: string,
roomCategories?: RoomCategory[]
) {
if (!roomCategories) {
return []
}
return rooms
.filter(
(room) => room.status === AvailabilityEnum.Available || room.roomsLeft > 0
)
.filter((room) => room.roomType === roomType)
.map((availRoom) => {
const matchingRoom = roomCategories
?.find((room) =>
room.roomTypes
.map((roomType) => roomType.code)
.includes(availRoom.roomTypeCode)
)
?.roomTypes.find((roomType) => roomType.code === availRoom.roomTypeCode)
if (matchingRoom) {
return {
description: matchingRoom.description,
size: matchingRoom.mainBed.widthRange,
value: matchingRoom.code,
type: matchingRoom.mainBed.type,
roomsLeft: availRoom.roomsLeft,
extraBed: matchingRoom.fixedExtraBed
? {
type: matchingRoom.fixedExtraBed.type,
description: matchingRoom.fixedExtraBed.description,
}
: undefined,
}
}
})
.filter((bed): bed is BedTypeSelection => Boolean(bed))
}
export function mergeRoomTypes(roomConfigurations: RoomConfiguration[]) {
// Initial sort to guarantee if one bed is NotAvailable and whereas
// the other is Available to make sure data is added to the correct
// roomConfig
roomConfigurations.sort(sortRoomConfigs)
const roomConfigs = new Map<string, RoomConfiguration>()
for (const roomConfig of roomConfigurations) {
if (roomConfigs.has(roomConfig.roomType)) {
const currentRoomConf = roomConfigs.get(roomConfig.roomType)
if (currentRoomConf) {
currentRoomConf.features = roomConfig.features.reduce(
(feats, feature) => {
const currentFeatureIndex = feats.findIndex(
(f) => f.code === feature.code
)
if (currentFeatureIndex !== -1) {
feats[currentFeatureIndex].inventory =
feats[currentFeatureIndex].inventory + feature.inventory
} else {
feats.push(feature)
}
return feats
},
currentRoomConf.features
)
currentRoomConf.roomsLeft =
currentRoomConf.roomsLeft + roomConfig.roomsLeft
roomConfigs.set(currentRoomConf.roomType, currentRoomConf)
}
} else {
roomConfigs.set(roomConfig.roomType, roomConfig)
}
}
return Array.from(roomConfigs.values())
}
export function selectRateRedirectURL(
input: RoomsAvailabilityExtendedInputSchema,
selectedRooms: boolean[]
) {
const searchParams = new URLSearchParams({
errorCode: BookingErrorCodeEnum.AvailabilityError,
fromdate: input.booking.fromDate,
hotel: input.booking.hotelId,
todate: input.booking.toDate,
})
if (input.booking.searchType) {
searchParams.set("searchtype", input.booking.searchType)
}
for (const [idx, room] of input.booking.rooms.entries()) {
searchParams.set(`room[${idx}].adults`, room.adults.toString())
if (selectedRooms[idx]) {
if (room.counterRateCode) {
searchParams.set(`room[${idx}].counterratecode`, room.counterRateCode)
}
searchParams.set(`room[${idx}].ratecode`, room.rateCode)
searchParams.set(`room[${idx}].roomtype`, room.roomTypeCode)
} else {
if (!searchParams.has("modifyRateIndex")) {
searchParams.set("modifyRateIndex", idx.toString())
}
}
if (room.bookingCode) {
searchParams.set(`room[${idx}].bookingCode`, room.bookingCode)
}
if (room.packages) {
searchParams.set(`room[${idx}].packages`, room.packages.join(","))
}
if (room.childrenInRoom?.length) {
for (const [i, kid] of room.childrenInRoom.entries()) {
searchParams.set(`room[${idx}].child[${i}].age`, kid.age.toString())
searchParams.set(`room[${idx}].child[${i}].bed`, kid.bed.toString())
}
}
}
return `${selectRate(input.lang)}?${searchParams.toString()}`
}

View File

@@ -9,8 +9,8 @@ import { getIntl } from "@/i18n"
import { getEurobonusMembership } from "@/utils/user"
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { UserLoyalty } from "@scandic-hotels/trpc/types/user"
import type { UserLoyalty } from "@/types/user"
import type { MyPagesLink } from "./MyPagesLink"
export const getPrimaryLinks = cache(

View File

@@ -3,8 +3,7 @@ import { z } from "zod"
import { Lang } from "@scandic-hotels/common/constants/language"
import { safeProtectedProcedure } from "@scandic-hotels/trpc/procedures"
import { getVerifiedUser } from "@/server/routers/user/utils"
import { getVerifiedUser } from "@scandic-hotels/trpc/routers/user/utils"
import { isValidSession } from "@/utils/session"

View File

@@ -4,13 +4,12 @@ import { z } from "zod"
import * as api from "@scandic-hotels/trpc/api"
import { protectedProcedure } from "@scandic-hotels/trpc/procedures"
import { getUserSchema } from "@scandic-hotels/trpc/routers/user/output"
import { getVerifiedUser } from "@scandic-hotels/trpc/routers/user/utils"
import { FriendsMembershipLevels } from "@/constants/membershipLevels"
import { getUserSchema } from "../../user/output"
import { getVerifiedUser } from "../../user/utils"
import type { FriendsTier } from "@/types/user"
import type { FriendsTier } from "@scandic-hotels/trpc/types/user"
const matchedSchema = z.object({
tierMatchState: z.enum(["matched"]),

View File

@@ -2,122 +2,6 @@ import { z } from "zod"
import { imageSchema } from "@scandic-hotels/trpc/routers/hotels/schemas/image"
import { countriesMap } from "@/constants/countries"
import { getFriendsMembership } from "@/utils/user"
const scandicFriendsTier = z.enum(["L1", "L2", "L3", "L4", "L5", "L6", "L7"])
const sasEurobonusTier = z.enum(["EBB", "EBS", "EBG", "EBD", "EBP"])
const commonMembershipSchema = z.object({
membershipNumber: z.string(),
tierExpires: z.string().nullish().default(null),
memberSince: z.string().nullish(),
})
// This prevents validation errors if the API returns an unhandled membership type
const otherMembershipSchema = z
.object({
// This ensures that `type` won't widen into "string", losing the literal types, when used in a union
type: z.string().refine((val): val is string & {} => true),
})
.merge(commonMembershipSchema)
export const sasMembershipSchema = z
.object({
type: z.literal("SAS_EB"),
tier: sasEurobonusTier,
nextTier: sasEurobonusTier.nullish(),
spendablePoints: z.number().nullish(),
boostedByScandic: z.boolean().nullish(),
boostedTier: sasEurobonusTier.nullish(),
boostedTierExpires: z.string().nullish().default(null),
})
.merge(commonMembershipSchema)
.transform((response) => {
return {
...response,
tierExpires:
// SAS API returns 1900-01-01 for non-expiring tiers
response.tierExpires === "1900-01-01" ? null : response.tierExpires,
}
})
export const friendsMembershipSchema = z
.object({
type: z.literal("SCANDIC_NATIVE"),
tier: scandicFriendsTier,
nextTier: scandicFriendsTier.nullish(),
pointsToNextTier: z.number().nullish(),
nightsToTopTier: z.number().nullish(),
})
.merge(commonMembershipSchema)
export const membershipSchema = z.union([
friendsMembershipSchema,
sasMembershipSchema,
otherMembershipSchema,
])
const pointExpirationSchema = z.object({
points: z.number().int(),
expires: z.string(),
})
export const userLoyaltySchema = z.object({
memberships: z.array(membershipSchema),
points: z.object({
spendable: z.number().int(),
earned: z.number().int(),
spent: z.number().int(),
}),
tier: scandicFriendsTier,
tierExpires: z.string(),
tierBoostedBy: z.string().nullish(),
pointExpirations: z.array(pointExpirationSchema),
})
export const getUserSchema = z
.object({
data: z.object({
attributes: z.object({
dateOfBirth: z.string().optional().default("1900-01-01"),
email: z.string().email(),
firstName: z.string(),
language: z
.string()
// Preserve Profile v1 formatting for now so it matches ApiLang enum
.transform((s) => s.charAt(0).toUpperCase() + s.slice(1))
.optional(),
lastName: z.string(),
phoneNumber: z.string().optional(),
profileId: z.string(),
membershipNumber: z.string(),
address: z
.object({
city: z.string().optional(),
country: z.string().optional(),
countryCode: z.nativeEnum(countriesMap).optional(),
streetAddress: z.string().optional(),
zipCode: z.string().optional(),
})
.optional()
.nullable(),
loyalty: userLoyaltySchema.optional(),
}),
type: z.string(),
}),
})
.transform((apiResponse) => {
return {
...apiResponse.data.attributes,
membership: apiResponse.data.attributes.loyalty
? getFriendsMembership(apiResponse.data.attributes.loyalty)
: null,
name: `${apiResponse.data.attributes.firstName} ${apiResponse.data.attributes.lastName}`,
}
})
// Schema is the same for upcoming and previous stays endpoints
export const getStaysSchema = z.object({
data: z.array(
@@ -234,35 +118,6 @@ type GetFriendTransactionsData = z.infer<typeof getFriendTransactionsSchema>
export type FriendTransaction = GetFriendTransactionsData["data"][number]
export const creditCardSchema = z
.object({
attribute: z.object({
cardName: z.string().optional(),
alias: z.string(),
truncatedNumber: z.string().transform((s) => s.slice(-4)),
expirationDate: z.string(),
cardType: z
.string()
.transform((s) => s.charAt(0).toLowerCase() + s.slice(1)),
}),
id: z.string(),
type: z.string(),
})
.transform((apiResponse) => {
return {
id: apiResponse.id,
type: apiResponse.attribute.cardType,
truncatedNumber: apiResponse.attribute.truncatedNumber,
alias: apiResponse.attribute.alias,
expirationDate: apiResponse.attribute.expirationDate,
cardType: apiResponse.attribute.cardType,
}
})
export const creditCardsSchema = z.object({
data: z.array(creditCardSchema),
})
export const initiateSaveCardSchema = z.object({
data: z.object({
attribute: z.object({

View File

@@ -6,10 +6,12 @@ import {
protectedProcedure,
safeProtectedProcedure,
} from "@scandic-hotels/trpc/procedures"
import { getFriendsMembership } from "@scandic-hotels/trpc/routers/user/helpers"
import { getVerifiedUser } from "@scandic-hotels/trpc/routers/user/utils"
import { toApiLang } from "@scandic-hotels/trpc/utils"
import { isValidSession } from "@/utils/session"
import { getFriendsMembership, getMembershipCards } from "@/utils/user"
import { getMembershipCards } from "@/utils/user"
import {
friendTransactionsInput,
@@ -22,13 +24,14 @@ import {
getCreditCards,
getPreviousStays,
getUpcomingStays,
getVerifiedUser,
parsedUser,
updateStaysBookingUrl,
} from "./utils"
import type { LoginType } from "@scandic-hotels/trpc/types/loginType"
import type {
LoginType,
// LoginType,
TrackingSDKUserData,
} from "@/types/components/tracking"
import { Transactions } from "@/types/enums/transactions"

View File

@@ -2,9 +2,12 @@ import { myStay } from "@scandic-hotels/common/constants/routes/myStay"
import { dt } from "@scandic-hotels/common/dt"
import { createCounter } from "@scandic-hotels/common/telemetry"
import * as api from "@scandic-hotels/trpc/api"
import { countries } from "@scandic-hotels/trpc/constants/countries"
import { getFriendsMembership } from "@scandic-hotels/trpc/routers/user/helpers"
import { creditCardsSchema } from "@scandic-hotels/trpc/routers/user/output"
import { getVerifiedUser } from "@scandic-hotels/trpc/routers/user/utils"
import { toApiLang } from "@scandic-hotels/trpc/utils"
import { countries } from "@/constants/countries"
import { myBookingPath } from "@/constants/myBooking"
import { env } from "@/env/server"
@@ -13,93 +16,13 @@ import { encrypt } from "@/utils/encryption"
import * as maskValue from "@/utils/maskValue"
import { isValidSession } from "@/utils/session"
import { getCurrentWebUrl } from "@/utils/url"
import { getFriendsMembership } from "@/utils/user"
import {
creditCardsSchema,
type FriendTransaction,
getStaysSchema,
getUserSchema,
type Stay,
} from "./output"
import { type FriendTransaction, getStaysSchema, type Stay } from "./output"
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { User } from "@scandic-hotels/trpc/types/user"
import type { Session } from "next-auth"
import type { User } from "@/types/user"
export const getVerifiedUser = cache(
async ({
session,
includeExtendedPartnerData,
}: {
session: Session
includeExtendedPartnerData?: boolean
}) => {
const getVerifiedUserCounter = createCounter("user", "getVerifiedUser")
const metricsGetVerifiedUser = getVerifiedUserCounter.init()
metricsGetVerifiedUser.start()
const now = Date.now()
if (session.token.expires_at && session.token.expires_at < now) {
metricsGetVerifiedUser.dataError(`Token expired`)
return { error: true, cause: "token_expired" } as const
}
const apiResponse = await api.get(
api.endpoints.v2.Profile.profile,
{
headers: {
Authorization: `Bearer ${session.token.access_token}`,
},
},
includeExtendedPartnerData
? { includes: "extendedPartnerInformation" }
: {}
)
if (!apiResponse.ok) {
await metricsGetVerifiedUser.httpError(apiResponse)
if (apiResponse.status === 401) {
return { error: true, cause: "unauthorized" } as const
} else if (apiResponse.status === 403) {
return { error: true, cause: "forbidden" } as const
} else if (apiResponse.status === 404) {
return { error: true, cause: "notfound" } as const
}
return {
error: true,
cause: "unknown",
status: apiResponse.status,
} as const
}
const apiJson = await apiResponse.json()
if (!apiJson.data?.attributes) {
metricsGetVerifiedUser.dataError(
`Missing data attributes in API response`,
{
data: apiJson,
}
)
return null
}
const verifiedData = getUserSchema.safeParse(apiJson)
if (!verifiedData.success) {
metricsGetVerifiedUser.validationError(verifiedData.error)
return null
}
metricsGetVerifiedUser.success()
return verifiedData
}
)
export async function getMembershipNumber(
session: Session | null
): Promise<string | undefined> {

View File

@@ -11,6 +11,7 @@ import {
import { detailsStorageName } from "."
import type { Packages } from "@scandic-hotels/trpc/types/packages"
import type {
CorporateChequeProduct,
PriceProduct,
@@ -22,7 +23,6 @@ import type { BreakfastPackage } from "@/types/components/hotelReservation/break
import { type RoomRate } from "@/types/components/hotelReservation/enterDetails/details"
import type { Price } from "@/types/components/hotelReservation/price"
import type { SelectRateBooking } from "@/types/components/hotelReservation/selectRate/selectRate"
import type { Packages } from "@/types/requests/packages"
import type { PersistedState, RoomState } from "@/types/stores/enter-details"
import type { SafeUser } from "@/types/user"

View File

@@ -2,11 +2,10 @@ import { produce } from "immer"
import { useContext } from "react"
import { create, useStore } from "zustand"
import { REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
import { AvailabilityEnum } from "@scandic-hotels/trpc/enums/selectHotel"
import { REDEMPTION } from "@/constants/booking"
import { RatesContext } from "@/contexts/Rates"
import { serializeBookingSearchParams } from "@/utils/url"
@@ -16,10 +15,10 @@ import {
findSelectedRate,
} from "./helpers"
import type { Package, Packages } from "@scandic-hotels/trpc/types/packages"
import type { PriceProduct } from "@scandic-hotels/trpc/types/roomAvailability"
import { BookingCodeFilterEnum } from "@/types/enums/bookingCodeFilter"
import type { Package, Packages } from "@/types/requests/packages"
import type { InitialState, RatesState } from "@/types/stores/rates"
export function createRatesStore({

View File

@@ -1,32 +0,0 @@
import type { JWT } from "next-auth/jwt"
import type { RefreshTokenError } from "./authError"
// Module augmentation
// https://authjs.dev/getting-started/typescript#popular-interfaces-to-augment
declare module "next-auth" {
/**
* The shape of the account object returned in the OAuth providers' `account` callback,
* Usually contains information about the provider being used, like OAuth tokens (`access_token`, etc).
*/
interface Account {}
/**
* Returned by `useSession`, `auth`, contains information about the active session.
*/
interface Session extends RefreshTokenError {
token: JWT
employeeId?: string | null
}
/**
* The shape of the user object returned in the OAuth providers' `profile` callback,
* or the second parameter of the `session` callback, when using a database.
*/
interface User {
given_name: string
sub: string
email?: string
login_with: string
}
}

View File

@@ -1,3 +0,0 @@
export interface RefreshTokenError {
error?: "RefreshAccessTokenError"
}

View File

@@ -1,4 +1,4 @@
import type { Child } from "../hotelReservation/selectRate/selectRate"
import type { Child } from "@scandic-hotels/trpc/types/child"
export type ChildBed = {
label: string

View File

@@ -1,10 +1,10 @@
import type { Image } from "@scandic-hotels/trpc/types/image"
import type { User } from "@scandic-hotels/trpc/types/user"
import type {
CurrentHeaderLink,
TopMenuHeaderLink,
} from "@/types/requests/currentHeader"
import type { User } from "@/types/user"
export type MainMenuProps = {
frontpageLinkText: string

View File

@@ -1,7 +1,6 @@
import type { meetingRoomsSchema } from "@scandic-hotels/trpc/routers/hotels/schemas/meetingRoom"
import type { z } from "zod"
import type { meetingRoomsSchema } from "@/server/routers/hotels/schemas/meetingRoom"
export type MeetingRoomData = z.output<typeof meetingRoomsSchema>
export type MeetingRooms = MeetingRoomData["data"]
export type MeetingRoom = MeetingRooms[number]["attributes"]

View File

@@ -4,24 +4,6 @@ import type {
bedTypeFormSchema,
bedTypeSchema,
} from "@/components/HotelReservation/EnterDetails/BedType/schema"
import type { BedTypeEnum, ExtraBedTypeEnum } from "@/constants/booking"
export type BedTypeSelection = {
description: string
size: {
min: number
max: number
}
value: string
type: BedTypeEnum
roomsLeft: number
extraBed:
| {
description: string
type: ExtraBedTypeEnum
}
| undefined
}
export interface BedTypeFormSchema extends z.output<typeof bedTypeFormSchema> {}

View File

@@ -1,17 +1,17 @@
import type { productTypePointsSchema } from "@scandic-hotels/trpc/routers/hotels/schemas/productTypePrice"
import type { Child } from "@scandic-hotels/trpc/types/child"
import type { PackageEnum } from "@scandic-hotels/trpc/types/packages"
import type { Product } from "@scandic-hotels/trpc/types/roomAvailability"
import type { z } from "zod"
import type { PackageEnum } from "@/types/requests/packages"
import type { Product } from "@scandic-hotels/trpc/types/roomAvailability"
import type { SafeUser } from "@/types/user"
import type { getMultiroomDetailsSchema } from "@/components/HotelReservation/EnterDetails/Details/Multiroom/schema"
import type {
guestDetailsSchema,
signedInDetailsSchema,
} from "@/components/HotelReservation/EnterDetails/Details/RoomOne/schema"
import type { productTypePointsSchema } from "@scandic-hotels/trpc/routers/hotels/schemas/productTypePrice"
import type { BookingSearchType } from "../booking"
import type { Price } from "../price"
import type { Child } from "../selectRate/selectRate"
export type DetailsSchema = z.output<typeof guestDetailsSchema>
export type MultiroomDetailsSchema = z.output<

View File

@@ -1,6 +1,5 @@
import type { PaymentMethodEnum } from "@scandic-hotels/common/constants/paymentMethod"
import type { CreditCard } from "@/types/user"
import type { CreditCard } from "@scandic-hotels/trpc/types/user"
export interface PaymentProps {
otherPaymentOptions: PaymentMethodEnum[]

View File

@@ -1,5 +1,6 @@
import type { ProductType } from "@scandic-hotels/trpc/types/availability"
import type { Hotel } from "@scandic-hotels/trpc/types/hotel"
import type { ProductType } from "@/types/trpc/routers/hotel/availability"
import type { HotelResponse } from "@/components/HotelReservation/SelectHotel/helpers"
export enum HotelCardListingTypeEnum {

View File

@@ -1,9 +1,9 @@
import type { imageSchema } from "@scandic-hotels/trpc/routers/hotels/schemas/image"
import type { ProductTypeCheque } from "@scandic-hotels/trpc/types/availability"
import type { Amenities } from "@scandic-hotels/trpc/types/hotel"
import type { z } from "zod"
import type { Coordinates } from "@/types/components/maps/coordinates"
import type { ProductTypeCheque } from "@/types/trpc/routers/hotel/availability"
import type { HotelResponse } from "@/components/HotelReservation/SelectHotel/helpers"
import type { CategorizedHotelFilters } from "./hotelFilters"
import type { SelectHotelBooking } from "./selectHotel"

View File

@@ -2,7 +2,7 @@ import type {
ProductTypeCheque,
ProductTypePrices,
ProductTypeVoucher,
} from "@/types/trpc/routers/hotel/availability"
} from "@scandic-hotels/trpc/types/availability"
export type PriceCardProps = {
productTypePrices: ProductTypePrices

View File

@@ -1,6 +1,7 @@
import type { Child } from "@scandic-hotels/trpc/types/child"
import type { Hotel } from "@scandic-hotels/trpc/types/hotel"
import type { BookingSearchType } from "../booking"
import type { Child } from "../selectRate/selectRate"
import type { SidePeekEnum } from "../sidePeek"
export interface ReadMoreProps {

View File

@@ -1,4 +1,4 @@
import type { Package } from "@/types/requests/packages"
import type { Package } from "@scandic-hotels/trpc/types/packages"
import type { RoomConfiguration } from "@scandic-hotels/trpc/types/roomAvailability"
export type RoomListItemProps = {

View File

@@ -1,18 +1,13 @@
import type { ChildBedMapEnum } from "@scandic-hotels/trpc/enums/childBedMapEnum"
import type { RateEnum } from "@scandic-hotels/trpc/enums/rate"
import type { Child } from "@scandic-hotels/trpc/types/child"
import type { PackageEnum, Packages } from "@scandic-hotels/trpc/types/packages"
import type {
Product,
RoomConfiguration,
} from "@scandic-hotels/trpc/types/roomAvailability"
import type { PackageEnum, Packages } from "@/types/requests/packages"
import type { BookingSearchType } from "../booking"
export interface Child {
bed: ChildBedMapEnum
age: number
}
export interface Room {
adults: number
childrenInRoom?: Child[]

View File

@@ -1,5 +1,7 @@
import type { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import type { Packages } from "@/types/requests/packages"
import type { Child } from "@scandic-hotels/trpc/types/child"
import type { Packages } from "@scandic-hotels/trpc/types/packages"
import type { RoomState } from "@/types/stores/enter-details"
import type {
DetailsBooking,
@@ -7,7 +9,7 @@ import type {
RoomRate,
} from "./enterDetails/details"
import type { Price } from "./price"
import type { Child, SelectRateBooking } from "./selectRate/selectRate"
import type { SelectRateBooking } from "./selectRate/selectRate"
export type RoomsData = {
rateDetails: string[] | undefined

View File

@@ -1,4 +1,4 @@
import type { User } from "@/types/user"
import type { User } from "@scandic-hotels/trpc/types/user"
export interface FriendProps
extends React.PropsWithChildren<Pick<User, "membership" | "name">> {}

View File

@@ -1,6 +1,6 @@
import type { User } from "@scandic-hotels/trpc/types/user"
import type { VariantProps } from "class-variance-authority"
import type { User } from "@/types/user"
import type { membershipNumberVariants } from "@/components/Blocks/DynamicContent/Overview/Friend/MembershipNumber/membershipNumberVariants"
export interface MembershipNumberProps

View File

@@ -1,4 +1,4 @@
import type { CreditCard } from "@/types/user"
import type { CreditCard } from "@scandic-hotels/trpc/types/user"
export type CreditCardRowProps = {
card: CreditCard

View File

@@ -1,4 +1,4 @@
import type { User } from "@/types/user"
import type { User } from "@scandic-hotels/trpc/types/user"
export type EditFormProps = {
user: User

View File

@@ -1,11 +1,11 @@
import type { z } from "zod"
import type { Room } from "@/types/stores/my-stay"
import type { CreditCard, User } from "@/types/user"
import type {
ancillaryPackagesSchema,
packagesSchema,
} from "@scandic-hotels/trpc/routers/hotels/output"
import type { CreditCard, User } from "@scandic-hotels/trpc/types/user"
import type { z } from "zod"
import type { Room } from "@/types/stores/my-stay"
export type Ancillaries = z.output<typeof ancillaryPackagesSchema>
export type Ancillary = Ancillaries[number]

Some files were not shown because too many files have changed in this diff Show More