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
146 lines
4.3 KiB
TypeScript
146 lines
4.3 KiB
TypeScript
import { z } from "zod"
|
|
|
|
import { countriesMap } from "../../constants/countries"
|
|
import { getFriendsMembership } from "./helpers"
|
|
|
|
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}`,
|
|
}
|
|
})
|
|
|
|
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),
|
|
})
|