feat: get breakfast package from API
This commit is contained in:
@@ -148,7 +148,7 @@ export const editProfile = protectedServerActionProcedure
|
||||
)
|
||||
}
|
||||
|
||||
const apiResponse = await api.patch(api.endpoints.v1.profile, {
|
||||
const apiResponse = await api.patch(api.endpoints.v1.Profile.profile, {
|
||||
body,
|
||||
cache: "no-store",
|
||||
headers: {
|
||||
|
||||
@@ -55,7 +55,7 @@ export const registerUser = serviceServerActionProcedure
|
||||
|
||||
let apiResponse
|
||||
try {
|
||||
apiResponse = await api.post(api.endpoints.v1.profile, {
|
||||
apiResponse = await api.post(api.endpoints.v1.Profile.profile, {
|
||||
body: parsedPayload.data,
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
|
||||
@@ -33,7 +33,7 @@ export const registerUserBookingFlow = serviceServerActionProcedure
|
||||
// TODO: Consume the API to register the user as soon as passwordless signup is enabled.
|
||||
// let apiResponse
|
||||
// try {
|
||||
// apiResponse = await api.post(api.endpoints.v1.profile, {
|
||||
// apiResponse = await api.post(api.endpoints.v1.Profile.profile, {
|
||||
// body: payload,
|
||||
// headers: {
|
||||
// Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "../page"
|
||||
@@ -0,0 +1,5 @@
|
||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||
|
||||
export default function LoadingHotelHeader() {
|
||||
return <LoadingSpinner />
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { redirect } from "next/navigation"
|
||||
|
||||
import { getHotelData } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import HotelSelectionHeader from "@/components/HotelReservation/HotelSelectionHeader"
|
||||
|
||||
import type { LangParams, PageArgs } from "@/types/params"
|
||||
|
||||
export default async function HotelHeader({
|
||||
params,
|
||||
searchParams,
|
||||
}: PageArgs<LangParams, { hotel: string }>) {
|
||||
const home = `/${params.lang}`
|
||||
if (!searchParams.hotel) {
|
||||
redirect(home)
|
||||
}
|
||||
const hotel = await getHotelData(searchParams.hotel, params.lang)
|
||||
if (!hotel?.data) {
|
||||
redirect(home)
|
||||
}
|
||||
return <HotelSelectionHeader hotel={hotel.data.attributes} />
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "../page"
|
||||
@@ -0,0 +1,5 @@
|
||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||
|
||||
export default function LoadingHotelSidePeek() {
|
||||
return <LoadingSpinner />
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { redirect } from "next/navigation"
|
||||
|
||||
import { getHotelData } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import SidePeek from "@/components/HotelReservation/EnterDetails/SidePeek"
|
||||
|
||||
import type { LangParams, PageArgs } from "@/types/params"
|
||||
|
||||
export default async function HotelSidePeek({
|
||||
params,
|
||||
searchParams,
|
||||
}: PageArgs<LangParams, { hotel: string }>) {
|
||||
if (!searchParams.hotel) {
|
||||
redirect(`/${params.lang}`)
|
||||
}
|
||||
const hotel = await getHotelData(searchParams.hotel, params.lang)
|
||||
if (!hotel?.data) {
|
||||
redirect(`/${params.lang}`)
|
||||
}
|
||||
return <SidePeek hotel={hotel.data.attributes} />
|
||||
}
|
||||
@@ -1,46 +1,32 @@
|
||||
import { redirect } from "next/navigation"
|
||||
|
||||
import {
|
||||
getCreditCardsSafely,
|
||||
getHotelData,
|
||||
getProfileSafely,
|
||||
} from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import EnterDetailsProvider from "@/components/HotelReservation/EnterDetails/Provider"
|
||||
import SelectedRoom from "@/components/HotelReservation/EnterDetails/SelectedRoom"
|
||||
import SidePeek from "@/components/HotelReservation/EnterDetails/SidePeek"
|
||||
import Summary from "@/components/HotelReservation/EnterDetails/Summary"
|
||||
import HotelSelectionHeader from "@/components/HotelReservation/HotelSelectionHeader"
|
||||
import { setLang } from "@/i18n/serverContext"
|
||||
|
||||
import { preload } from "./page"
|
||||
|
||||
import styles from "./layout.module.css"
|
||||
|
||||
import { StepEnum } from "@/types/components/enterDetails/step"
|
||||
import type { LangParams, LayoutArgs } from "@/types/params"
|
||||
|
||||
function preload(id: string, lang: string) {
|
||||
void getHotelData(id, lang)
|
||||
void getProfileSafely()
|
||||
void getCreditCardsSafely()
|
||||
}
|
||||
|
||||
export default async function StepLayout({
|
||||
children,
|
||||
hotelHeader,
|
||||
params,
|
||||
}: React.PropsWithChildren<LayoutArgs<LangParams & { step: StepEnum }>>) {
|
||||
setLang(params.lang)
|
||||
preload("811", params.lang)
|
||||
|
||||
const hotel = await getHotelData("811", params.lang)
|
||||
|
||||
if (!hotel?.data) {
|
||||
redirect(`/${params.lang}`)
|
||||
sidePeek,
|
||||
}: React.PropsWithChildren<
|
||||
LayoutArgs<LangParams & { step: StepEnum }> & {
|
||||
hotelHeader: React.ReactNode
|
||||
sidePeek: React.ReactNode
|
||||
}
|
||||
|
||||
>) {
|
||||
setLang(params.lang)
|
||||
preload()
|
||||
return (
|
||||
<EnterDetailsProvider step={params.step}>
|
||||
<main className={styles.layout}>
|
||||
<HotelSelectionHeader hotel={hotel.data.attributes} />
|
||||
{hotelHeader}
|
||||
<div className={styles.content}>
|
||||
<SelectedRoom />
|
||||
{children}
|
||||
@@ -48,7 +34,7 @@ export default async function StepLayout({
|
||||
<Summary />
|
||||
</aside>
|
||||
</div>
|
||||
<SidePeek hotel={hotel.data.attributes} />
|
||||
{sidePeek}
|
||||
</main>
|
||||
</EnterDetailsProvider>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { notFound } from "next/navigation"
|
||||
import { notFound, redirect } from "next/navigation"
|
||||
|
||||
import {
|
||||
getBreakfastPackages,
|
||||
getCreditCardsSafely,
|
||||
getHotelData,
|
||||
getProfileSafely,
|
||||
@@ -17,22 +18,32 @@ import { getIntl } from "@/i18n"
|
||||
import { StepEnum } from "@/types/components/enterDetails/step"
|
||||
import type { LangParams, PageArgs } from "@/types/params"
|
||||
|
||||
export function preload() {
|
||||
void getProfileSafely()
|
||||
void getCreditCardsSafely()
|
||||
}
|
||||
|
||||
function isValidStep(step: string): step is StepEnum {
|
||||
return Object.values(StepEnum).includes(step as StepEnum)
|
||||
}
|
||||
|
||||
export default async function StepPage({
|
||||
params,
|
||||
}: PageArgs<LangParams & { step: StepEnum }>) {
|
||||
const { step, lang } = params
|
||||
searchParams,
|
||||
}: PageArgs<LangParams & { step: StepEnum }, { hotel: string }>) {
|
||||
if (!searchParams.hotel) {
|
||||
redirect(`/${params.lang}`)
|
||||
}
|
||||
void getBreakfastPackages(searchParams.hotel)
|
||||
|
||||
const intl = await getIntl()
|
||||
|
||||
const hotel = await getHotelData("811", lang)
|
||||
const hotel = await getHotelData(searchParams.hotel, params.lang)
|
||||
const user = await getProfileSafely()
|
||||
const savedCreditCards = await getCreditCardsSafely()
|
||||
const breakfastPackages = await getBreakfastPackages(searchParams.hotel)
|
||||
|
||||
if (!isValidStep(step) || !hotel) {
|
||||
if (!isValidStep(params.step) || !hotel) {
|
||||
return notFound()
|
||||
}
|
||||
|
||||
@@ -51,7 +62,7 @@ export default async function StepPage({
|
||||
step={StepEnum.breakfast}
|
||||
label={intl.formatMessage({ id: "Select breakfast options" })}
|
||||
>
|
||||
<Breakfast />
|
||||
<Breakfast packages={breakfastPackages} />
|
||||
</SectionAccordion>
|
||||
<SectionAccordion
|
||||
header="Details"
|
||||
|
||||
@@ -15,7 +15,7 @@ import { bedTypeSchema } from "./schema"
|
||||
import styles from "./bedOptions.module.css"
|
||||
|
||||
import type { BedTypeSchema } from "@/types/components/enterDetails/bedType"
|
||||
import { bedTypeEnum } from "@/types/enums/bedType"
|
||||
import { BedTypeEnum } from "@/types/enums/bedType"
|
||||
|
||||
export default function BedType() {
|
||||
const intl = useIntl()
|
||||
@@ -61,7 +61,7 @@ export default function BedType() {
|
||||
<RadioCard
|
||||
Icon={KingBedIcon}
|
||||
iconWidth={46}
|
||||
id={bedTypeEnum.KING}
|
||||
id={BedTypeEnum.KING}
|
||||
name="bedType"
|
||||
subtitle={intl.formatMessage(
|
||||
{ id: "{width} cm × {length} cm" },
|
||||
@@ -72,12 +72,12 @@ export default function BedType() {
|
||||
)}
|
||||
text={text}
|
||||
title={intl.formatMessage({ id: "King bed" })}
|
||||
value={bedTypeEnum.KING}
|
||||
value={BedTypeEnum.KING}
|
||||
/>
|
||||
<RadioCard
|
||||
Icon={KingBedIcon}
|
||||
iconWidth={46}
|
||||
id={bedTypeEnum.QUEEN}
|
||||
id={BedTypeEnum.QUEEN}
|
||||
name="bedType"
|
||||
subtitle={intl.formatMessage(
|
||||
{ id: "{width} cm × {length} cm" },
|
||||
@@ -88,7 +88,7 @@ export default function BedType() {
|
||||
)}
|
||||
text={text}
|
||||
title={intl.formatMessage({ id: "Queen bed" })}
|
||||
value={bedTypeEnum.QUEEN}
|
||||
value={BedTypeEnum.QUEEN}
|
||||
/>
|
||||
</form>
|
||||
</FormProvider>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import { bedTypeEnum } from "@/types/enums/bedType"
|
||||
import { BedTypeEnum } from "@/types/enums/bedType"
|
||||
|
||||
export const bedTypeSchema = z.object({
|
||||
bedType: z.nativeEnum(bedTypeEnum),
|
||||
bedType: z.nativeEnum(BedTypeEnum),
|
||||
})
|
||||
|
||||
@@ -7,36 +7,50 @@ import { useIntl } from "react-intl"
|
||||
|
||||
import { useEnterDetailsStore } from "@/stores/enter-details"
|
||||
|
||||
import { BreakfastIcon, NoBreakfastIcon } from "@/components/Icons"
|
||||
import { Highlight } from "@/components/TempDesignSystem/Form/ChoiceCard/_Card"
|
||||
import RadioCard from "@/components/TempDesignSystem/Form/ChoiceCard/Radio"
|
||||
|
||||
import { breakfastSchema } from "./schema"
|
||||
import { breakfastFormSchema } from "./schema"
|
||||
|
||||
import styles from "./breakfast.module.css"
|
||||
|
||||
import type { BreakfastSchema } from "@/types/components/enterDetails/breakfast"
|
||||
import { breakfastEnum } from "@/types/enums/breakfast"
|
||||
import type {
|
||||
BreakfastFormSchema,
|
||||
BreakfastProps,
|
||||
} from "@/types/components/enterDetails/breakfast"
|
||||
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
|
||||
|
||||
export default function Breakfast() {
|
||||
export default function Breakfast({ packages }: BreakfastProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
const breakfast = useEnterDetailsStore((state) => state.data.breakfast)
|
||||
|
||||
const methods = useForm<BreakfastSchema>({
|
||||
defaultValues: breakfast ? { breakfast } : undefined,
|
||||
let defaultValues = undefined
|
||||
if (breakfast === BreakfastPackageEnum.NO_BREAKFAST) {
|
||||
defaultValues = { breakfast: BreakfastPackageEnum.NO_BREAKFAST }
|
||||
} else if (breakfast?.code) {
|
||||
defaultValues = { breakfast: breakfast.code }
|
||||
}
|
||||
const methods = useForm<BreakfastFormSchema>({
|
||||
defaultValues,
|
||||
criteriaMode: "all",
|
||||
mode: "all",
|
||||
resolver: zodResolver(breakfastSchema),
|
||||
resolver: zodResolver(breakfastFormSchema),
|
||||
reValidateMode: "onChange",
|
||||
})
|
||||
|
||||
const completeStep = useEnterDetailsStore((state) => state.completeStep)
|
||||
|
||||
const onSubmit = useCallback(
|
||||
(values: BreakfastSchema) => {
|
||||
completeStep(values)
|
||||
(values: BreakfastFormSchema) => {
|
||||
const pkg = packages?.find((p) => p.code === values.breakfast)
|
||||
if (pkg) {
|
||||
completeStep({ breakfast: pkg })
|
||||
} else {
|
||||
completeStep({ breakfast: BreakfastPackageEnum.NO_BREAKFAST })
|
||||
}
|
||||
},
|
||||
[completeStep]
|
||||
[completeStep, packages]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -47,30 +61,46 @@ export default function Breakfast() {
|
||||
return () => subscription.unsubscribe()
|
||||
}, [methods, onSubmit])
|
||||
|
||||
if (!packages) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<FormProvider {...methods}>
|
||||
<form className={styles.form} onSubmit={methods.handleSubmit(onSubmit)}>
|
||||
<RadioCard
|
||||
Icon={BreakfastIcon}
|
||||
id={breakfastEnum.BREAKFAST}
|
||||
name="breakfast"
|
||||
subtitle={intl.formatMessage<React.ReactNode>(
|
||||
{ id: "<b>{amount} {currency}</b>/night per adult" },
|
||||
{
|
||||
amount: "150",
|
||||
b: (str) => <b>{str}</b>,
|
||||
currency: "SEK",
|
||||
{packages.map((pkg) => (
|
||||
<RadioCard
|
||||
key={pkg.code}
|
||||
id={pkg.code}
|
||||
name="breakfast"
|
||||
subtitle={
|
||||
pkg.code === BreakfastPackageEnum.FREE_MEMBER_BREAKFAST
|
||||
? intl.formatMessage<React.ReactNode>(
|
||||
{ id: "breakfast.price.free" },
|
||||
{
|
||||
amount: pkg.originalPrice,
|
||||
currency: pkg.currency,
|
||||
free: (str) => <Highlight>{str}</Highlight>,
|
||||
strikethrough: (str) => <s>{str}</s>,
|
||||
}
|
||||
)
|
||||
: intl.formatMessage(
|
||||
{ id: "breakfast.price" },
|
||||
{
|
||||
amount: pkg.packagePrice,
|
||||
currency: pkg.currency,
|
||||
}
|
||||
)
|
||||
}
|
||||
)}
|
||||
text={intl.formatMessage({
|
||||
id: "All our breakfast buffets offer gluten free, vegan, and allergy-friendly options.",
|
||||
})}
|
||||
title={intl.formatMessage({ id: "Breakfast buffet" })}
|
||||
value={breakfastEnum.BREAKFAST}
|
||||
/>
|
||||
text={intl.formatMessage({
|
||||
id: "All our breakfast buffets offer gluten free, vegan, and allergy-friendly options.",
|
||||
})}
|
||||
title={intl.formatMessage({ id: "Breakfast buffet" })}
|
||||
value={pkg.code}
|
||||
/>
|
||||
))}
|
||||
<RadioCard
|
||||
Icon={NoBreakfastIcon}
|
||||
id={breakfastEnum.NO_BREAKFAST}
|
||||
id={BreakfastPackageEnum.NO_BREAKFAST}
|
||||
name="breakfast"
|
||||
subtitle={intl.formatMessage(
|
||||
{ id: "{amount} {currency}" },
|
||||
@@ -83,7 +113,7 @@ export default function Breakfast() {
|
||||
id: "You can always change your mind later and add breakfast at the hotel.",
|
||||
})}
|
||||
title={intl.formatMessage({ id: "No breakfast" })}
|
||||
value={breakfastEnum.NO_BREAKFAST}
|
||||
value={BreakfastPackageEnum.NO_BREAKFAST}
|
||||
/>
|
||||
</form>
|
||||
</FormProvider>
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import { breakfastEnum } from "@/types/enums/breakfast"
|
||||
import { breakfastPackageSchema } from "@/server/routers/hotels/output"
|
||||
|
||||
export const breakfastSchema = z.object({
|
||||
breakfast: z.nativeEnum(breakfastEnum),
|
||||
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
|
||||
|
||||
export const breakfastStoreSchema = z.object({
|
||||
breakfast: breakfastPackageSchema.or(
|
||||
z.literal(BreakfastPackageEnum.NO_BREAKFAST)
|
||||
),
|
||||
})
|
||||
|
||||
export const breakfastFormSchema = z.object({
|
||||
breakfast: z.string().or(z.literal(BreakfastPackageEnum.NO_BREAKFAST)),
|
||||
})
|
||||
|
||||
@@ -70,3 +70,7 @@
|
||||
.listItem:nth-of-type(n + 2) {
|
||||
margin-top: var(--Spacing-x-quarter);
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: var(--Scandic-Brand-Scandic-Red);
|
||||
}
|
||||
|
||||
@@ -34,3 +34,12 @@ export type CheckboxProps =
|
||||
export type RadioProps =
|
||||
| Omit<ListCardProps, "type">
|
||||
| Omit<TextCardProps, "type">
|
||||
|
||||
export interface ListProps extends Pick<ListCardProps, "declined"> {
|
||||
list?: ListCardProps["list"]
|
||||
}
|
||||
|
||||
export interface SubtitleProps
|
||||
extends Pick<BaseCardProps, "highlightSubtitle" | "subtitle"> {}
|
||||
|
||||
export interface TextProps extends Pick<TextCardProps, "text"> {}
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
|
||||
import { useFormContext } from "react-hook-form"
|
||||
|
||||
import { CheckIcon, CloseIcon, HeartIcon } from "@/components/Icons"
|
||||
import { CheckIcon, CloseIcon } from "@/components/Icons"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
||||
|
||||
import styles from "./card.module.css"
|
||||
|
||||
import type { CardProps } from "./card"
|
||||
import type { CardProps, ListProps, SubtitleProps, TextProps } from "./card"
|
||||
|
||||
export default function Card({
|
||||
Icon = HeartIcon,
|
||||
Icon,
|
||||
iconHeight = 32,
|
||||
iconWidth = 32,
|
||||
declined = false,
|
||||
@@ -26,56 +26,79 @@ export default function Card({
|
||||
value,
|
||||
}: CardProps) {
|
||||
const { register } = useFormContext()
|
||||
|
||||
return (
|
||||
<label className={styles.label} data-declined={declined} tabIndex={0}>
|
||||
<Caption className={styles.title} type="label" uppercase>
|
||||
<Caption className={styles.title} color="burgundy" type="label" uppercase>
|
||||
{title}
|
||||
</Caption>
|
||||
{subtitle ? (
|
||||
<Caption
|
||||
className={styles.subtitle}
|
||||
color={highlightSubtitle ? "baseTextAccent" : "uiTextHighContrast"}
|
||||
type="regular"
|
||||
>
|
||||
{subtitle}
|
||||
</Caption>
|
||||
) : null}
|
||||
<Icon
|
||||
className={styles.icon}
|
||||
color="uiTextHighContrast"
|
||||
height={iconHeight}
|
||||
width={iconWidth}
|
||||
/>
|
||||
{list
|
||||
? list.map((listItem) => (
|
||||
<span key={listItem.title} className={styles.listItem}>
|
||||
{declined ? (
|
||||
<CloseIcon
|
||||
color="uiTextMediumContrast"
|
||||
height={20}
|
||||
width={20}
|
||||
/>
|
||||
) : (
|
||||
<CheckIcon color="baseIconLowContrast" height={20} width={20} />
|
||||
)}
|
||||
<Footnote color="uiTextMediumContrast">{listItem.title}</Footnote>
|
||||
</span>
|
||||
))
|
||||
: null}
|
||||
{text ? (
|
||||
<Footnote className={styles.text} color="uiTextMediumContrast">
|
||||
{text}
|
||||
</Footnote>
|
||||
<Subtitle highlightSubtitle={highlightSubtitle} subtitle={subtitle} />
|
||||
{Icon ? (
|
||||
<Icon
|
||||
className={styles.icon}
|
||||
color="uiTextHighContrast"
|
||||
height={iconHeight}
|
||||
width={iconWidth}
|
||||
/>
|
||||
) : null}
|
||||
<List declined={declined} list={list} />
|
||||
<Text text={text} />
|
||||
<input
|
||||
{...register(name)}
|
||||
aria-hidden
|
||||
id={id || name}
|
||||
hidden
|
||||
type={type}
|
||||
value={value}
|
||||
{...register(name)}
|
||||
/>
|
||||
</label>
|
||||
)
|
||||
}
|
||||
|
||||
function List({ declined, list }: ListProps) {
|
||||
if (!list) {
|
||||
return null
|
||||
}
|
||||
|
||||
return list.map((listItem) => (
|
||||
<span key={listItem.title} className={styles.listItem}>
|
||||
{declined ? (
|
||||
<CloseIcon color="uiTextMediumContrast" height={20} width={20} />
|
||||
) : (
|
||||
<CheckIcon color="baseIconLowContrast" height={20} width={20} />
|
||||
)}
|
||||
<Footnote color="uiTextMediumContrast">{listItem.title}</Footnote>
|
||||
</span>
|
||||
))
|
||||
}
|
||||
|
||||
function Subtitle({ highlightSubtitle, subtitle }: SubtitleProps) {
|
||||
if (!subtitle) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Caption
|
||||
className={styles.subtitle}
|
||||
color={highlightSubtitle ? "baseTextAccent" : "uiTextMediumContrast"}
|
||||
type="label"
|
||||
uppercase
|
||||
>
|
||||
{subtitle}
|
||||
</Caption>
|
||||
)
|
||||
}
|
||||
|
||||
function Text({ text }: TextProps) {
|
||||
if (!text) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<Footnote className={styles.text} color="uiTextMediumContrast">
|
||||
{text}
|
||||
</Footnote>
|
||||
)
|
||||
}
|
||||
|
||||
export function Highlight({ children }: React.PropsWithChildren) {
|
||||
return <span className={styles.highlight}>{children}</span>
|
||||
}
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
export enum BookingStatusEnum {
|
||||
CreatedInOhip = "CreatedInOhip",
|
||||
PaymentRegistered = "PaymentRegistered",
|
||||
BookingCompleted = "BookingCompleted",
|
||||
Cancelled = "Cancelled",
|
||||
CheckedOut = "CheckedOut",
|
||||
ConfirmedInScorpio = "ConfirmedInScorpio",
|
||||
CreatedInOhip = "CreatedInOhip",
|
||||
PaymentAuthorized = "PaymentAuthorized",
|
||||
PaymentCancelled = "PaymentCancelled",
|
||||
PaymentError = "PaymentError",
|
||||
PaymentFailed = "PaymentFailed",
|
||||
PaymentRegistered = "PaymentRegistered",
|
||||
PaymentSucceeded = "PaymentSucceeded",
|
||||
PendingAcceptPriceChange = "PendingAcceptPriceChange",
|
||||
PendingGuarantee = "PendingGuarantee",
|
||||
PendingPayment = "PendingPayment",
|
||||
}
|
||||
|
||||
export enum BedTypeEnum {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"<b>Included</b> (based on availability)": "<b>Inkluderet</b> (baseret på tilgængelighed)",
|
||||
"<b>{amount} {currency}</b>/night per adult": "<b>{amount} {currency}</b>/nat pr. voksen",
|
||||
"A destination or hotel name is needed to be able to search for a hotel room.": "Et destinations- eller hotelnavn er nødvendigt for at kunne søge efter et hotelværelse.",
|
||||
"A photo of the room": "Et foto af værelset",
|
||||
"ACCE": "Tilgængelighed",
|
||||
@@ -366,6 +365,8 @@
|
||||
"booking.rooms": "{totalRooms, plural, one {# værelse} other {# værelser}}",
|
||||
"booking.terms": "Ved at betale med en af de tilgængelige betalingsmetoder, accepterer jeg vilkårene for denne booking og de generelle <termsLink>Vilkår og betingelser</termsLink>, og forstår, at Scandic vil behandle min personlige data i forbindelse med denne booking i henhold til <privacyLink>Scandics Privatlivspolitik</privacyLink>. Jeg accepterer, at Scandic kræver et gyldigt kreditkort under min besøg i tilfælde af, at noget er tilbagebetalt.",
|
||||
"booking.thisRoomIsEquippedWith": "Dette værelse er udstyret med",
|
||||
"breakfast.price": "{amount} {currency}/nat",
|
||||
"breakfast.price.free": "<strikethrough>{amount} {currency}</strikethrough> <free>0 {currency}</free>/nat",
|
||||
"by": "inden",
|
||||
"characters": "tegn",
|
||||
"guest": "gæst",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"<b>Included</b> (based on availability)": "<b>Inbegriffen</b> (je nach Verfügbarkeit)",
|
||||
"<b>{amount} {currency}</b>/night per adult": "<b>{amount} {currency}</b>/Nacht pro Erwachsener",
|
||||
"A destination or hotel name is needed to be able to search for a hotel room.": "Ein Reiseziel oder Hotelname wird benötigt, um nach einem Hotelzimmer suchen zu können.",
|
||||
"A photo of the room": "Ein Foto des Zimmers",
|
||||
"ACCE": "Zugänglichkeit",
|
||||
@@ -367,6 +366,8 @@
|
||||
"booking.rooms": "{totalRooms, plural, one {# zimmer} other {# räume}}",
|
||||
"booking.terms": "Ved at betale med en af de tilgængelige betalingsmetoder, accepterer jeg vilkårene for denne booking og de generelle <termsLink>Vilkår og betingelser</termsLink>, og forstår, at Scandic vil behandle min personlige data i forbindelse med denne booking i henhold til <privacyLink>Scandics Privatlivspolitik</privacyLink>. Jeg accepterer, at Scandic kræver et gyldigt kreditkort under min besøg i tilfælde af, at noget er tilbagebetalt.",
|
||||
"booking.thisRoomIsEquippedWith": "Dieses Zimmer ist ausgestattet mit",
|
||||
"breakfast.price": "{amount} {currency}/Nacht",
|
||||
"breakfast.price.free": "<strikethrough>{amount} {currency}</strikethrough> <free>0 {currency}</free>/Nacht",
|
||||
"by": "bis",
|
||||
"characters": "figuren",
|
||||
"guest": "gast",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"<b>Included</b> (based on availability)": "<b>Included</b> (based on availability)",
|
||||
"<b>{amount} {currency}</b>/night per adult": "<b>{amount} {currency}</b>/night per adult",
|
||||
"A destination or hotel name is needed to be able to search for a hotel room.": "A destination or hotel name is needed to be able to search for a hotel room.",
|
||||
"A photo of the room": "A photo of the room",
|
||||
"ACCE": "Accessibility",
|
||||
@@ -385,6 +384,8 @@
|
||||
"booking.rooms": "{totalRooms, plural, one {# room} other {# rooms}}",
|
||||
"booking.terms": "By paying with any of the payment methods available, I accept the terms for this booking and the general <termsLink>Terms & Conditions</termsLink>, and understand that Scandic will process my personal data for this booking in accordance with <privacyLink>Scandic's Privacy policy</privacyLink>. I also accept that Scandic require a valid credit card during my visit in case anything is left unpaid.",
|
||||
"booking.thisRoomIsEquippedWith": "This room is equipped with",
|
||||
"breakfast.price": "{amount} {currency}/night",
|
||||
"breakfast.price.free": "<strikethrough>{amount} {currency}</strikethrough> <free>0 {currency}</free>/night",
|
||||
"by": "by",
|
||||
"characters": "characters",
|
||||
"from": "from",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"<b>Included</b> (based on availability)": "<b>Sisältyy</b> (saatavuuden mukaan)",
|
||||
"<b>{amount} {currency}</b>/night per adult": "<b>{amount} {currency}</b>/yö per aikuinen",
|
||||
"A destination or hotel name is needed to be able to search for a hotel room.": "Kohteen tai hotellin nimi tarvitaan, jotta hotellihuonetta voidaan hakea.",
|
||||
"A photo of the room": "Kuva huoneesta",
|
||||
"ACCE": "Saavutettavuus",
|
||||
@@ -367,6 +366,8 @@
|
||||
"booking.rooms": "{totalRooms, plural, one {# huone} other {# sviitti}}",
|
||||
"booking.terms": "Maksamalla minkä tahansa saatavilla olevan maksutavan avulla hyväksyn tämän varauksen ehdot ja yleiset <termsLink>ehdot ja ehtoja</termsLink>, ja ymmärrän, että Scandic käsittelee minun henkilötietoni tässä varauksessa mukaisesti <privacyLink>Scandicin tietosuojavaltuuden</privacyLink> mukaisesti. Hyväksyn myös, että Scandic vaatii validin luottokortin majoituksen ajan, jos jokin jää maksamatta.",
|
||||
"booking.thisRoomIsEquippedWith": "Tämä huone on varustettu",
|
||||
"breakfast.price": "{amount} {currency}/yö",
|
||||
"breakfast.price.free": "<strikethrough>{amount} {currency}</strikethrough> <free>0 {currency}</free>/yö",
|
||||
"by": "mennessä",
|
||||
"characters": "hahmoja",
|
||||
"guest": "Vieras",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"<b>Included</b> (based on availability)": "<b>Inkludert</b> (basert på tilgjengelighet)",
|
||||
"<b>{amount} {currency}</b>/night per adult": "<b>{amount} {currency}</b>/natt per voksen",
|
||||
"A destination or hotel name is needed to be able to search for a hotel room.": "Et reisemål eller hotellnavn er nødvendig for å kunne søke etter et hotellrom.",
|
||||
"A photo of the room": "Et bilde av rommet",
|
||||
"ACCE": "Tilgjengelighet",
|
||||
@@ -363,6 +362,8 @@
|
||||
"booking.nights": "{totalNights, plural, one {# natt} other {# netter}}",
|
||||
"booking.rooms": "{totalRooms, plural, one {# rom} other {# rom}}",
|
||||
"booking.thisRoomIsEquippedWith": "Dette rommet er utstyrt med",
|
||||
"breakfast.price": "{amount} {currency}/natt",
|
||||
"breakfast.price.free": "<strikethrough>{amount} {currency}</strikethrough> <free>0 {currency}</free>/natt",
|
||||
"by": "innen",
|
||||
"characters": "tegn",
|
||||
"guest": "gjest",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"<b>Included</b> (based on availability)": "<b>Ingår</b> (baserat på tillgänglighet)",
|
||||
"<b>{amount} {currency}</b>/night per adult": "<b>{amount} {currency}</b>/natt per vuxen",
|
||||
"A destination or hotel name is needed to be able to search for a hotel room.": "Ett destinations- eller hotellnamn behövs för att kunna söka efter ett hotellrum.",
|
||||
"A photo of the room": "Ett foto av rummet",
|
||||
"ACCE": "Tillgänglighet",
|
||||
@@ -364,6 +363,8 @@
|
||||
"booking.rooms": "{totalRooms, plural, one {# rum} other {# rum}}",
|
||||
"booking.terms": "Genom att betala med någon av de tillgängliga betalningsmetoderna accepterar jag villkoren för denna bokning och de generella <termsLink>Villkoren och villkoren</termsLink>, och förstår att Scandic kommer att behandla min personliga data i samband med denna bokning i enlighet med <privacyLink>Scandics integritetspolicy</privacyLink>. Jag accepterar att Scandic kräver ett giltigt kreditkort under min besök i fall att något är tillbaka betalt.",
|
||||
"booking.thisRoomIsEquippedWith": "Detta rum är utrustat med",
|
||||
"breakfast.price": "{amount} {currency}/natt",
|
||||
"breakfast.price.free": "<strikethrough>{amount} {currency}</strikethrough> <free>0 {currency}</free>/natt",
|
||||
"by": "innan",
|
||||
"characters": "tecken",
|
||||
"guest": "gäst",
|
||||
|
||||
@@ -2,29 +2,190 @@
|
||||
* Nested enum requires namespace
|
||||
*/
|
||||
export namespace endpoints {
|
||||
export const enum v0 {
|
||||
profile = "profile/v0/Profile",
|
||||
namespace base {
|
||||
export const enum path {
|
||||
availability = "availability",
|
||||
booking = "booking",
|
||||
hotel = "hotel",
|
||||
package = "package",
|
||||
profile = "profile",
|
||||
}
|
||||
|
||||
export const enum enitity {
|
||||
Ancillary = "Ancillary",
|
||||
Availabilities = "availabilities",
|
||||
Bookings = "Bookings",
|
||||
Breakfast = "breakfast",
|
||||
Cities = "Cities",
|
||||
Countries = "Countries",
|
||||
Hotels = "Hotels",
|
||||
Locations = "Locations",
|
||||
Packages = "packages",
|
||||
Profile = "Profile",
|
||||
Reward = "Reward",
|
||||
Stays = "Stays",
|
||||
Transaction = "Transaction",
|
||||
}
|
||||
}
|
||||
export const enum v1 {
|
||||
hotelsAvailability = "availability/v1/availabilities/city",
|
||||
roomsAvailability = "availability/v1/availabilities/hotel",
|
||||
profile = "profile/v1/Profile",
|
||||
booking = "booking/v1/Bookings",
|
||||
creditCards = `${profile}/creditCards`,
|
||||
city = "hotel/v1/Cities",
|
||||
citiesCountry = `${city}/country`,
|
||||
countries = "hotel/v1/Countries",
|
||||
friendTransactions = "profile/v1/Transaction/friendTransactions",
|
||||
hotels = "hotel/v1/Hotels",
|
||||
initiateSaveCard = `${creditCards}/initiateSaveCard`,
|
||||
locations = "hotel/v1/Locations",
|
||||
previousStays = "booking/v1/Stays/past",
|
||||
upcomingStays = "booking/v1/Stays/future",
|
||||
rewards = `${profile}/reward`,
|
||||
tierRewards = `${profile}/TierRewards`,
|
||||
subscriberId = `${profile}/SubscriberId`,
|
||||
packages = "package/v1/packages/hotel",
|
||||
|
||||
export namespace v1 {
|
||||
const version = "v1"
|
||||
/**
|
||||
* availability (Swagger)
|
||||
* https://tstapi.scandichotels.com/availability/swagger/v1/index.html
|
||||
*/
|
||||
export namespace Availability {
|
||||
export function city(cityId: string) {
|
||||
return `${base.path.availability}/${version}/${base.enitity.Availabilities}/city/${cityId}`
|
||||
}
|
||||
export function hotel(hotelId: string) {
|
||||
return `${base.path.availability}/${version}/${base.enitity.Availabilities}/hotel/${hotelId}`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* booking (Swagger)
|
||||
* https://tstapi.scandichotels.com/booking/swagger/v1/index.html
|
||||
*/
|
||||
export namespace Booking {
|
||||
export const bookings = `${base.path.booking}/${version}/${base.enitity.Bookings}`
|
||||
|
||||
export function booking(confirmationNumber: string) {
|
||||
return `${bookings}/${confirmationNumber}`
|
||||
}
|
||||
export function cancel(confirmationNumber: string) {
|
||||
return `${bookings}/${confirmationNumber}/cancel`
|
||||
}
|
||||
export function status(confirmationNumber: string) {
|
||||
return `${bookings}/${confirmationNumber}/status`
|
||||
}
|
||||
|
||||
export const enum Stays {
|
||||
future = `${base.path.booking}/${version}/${base.enitity.Stays}/future`,
|
||||
past = `${base.path.booking}/${version}/${base.enitity.Stays}/past`,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* hotel (Swagger)
|
||||
* https://tstapi.scandichotels.com/hotel/swagger/v1/index.html
|
||||
*/
|
||||
export namespace Hotel {
|
||||
export const cities = `${base.path.hotel}/${version}/${base.enitity.Cities}`
|
||||
export namespace Cities {
|
||||
export function city(cityId: string) {
|
||||
return `${cities}/${cityId}`
|
||||
}
|
||||
export function country(countryId: string) {
|
||||
return `${cities}/country/${countryId}`
|
||||
}
|
||||
export function hotel(hotelId: string) {
|
||||
return `${cities}/hotel/${hotelId}`
|
||||
}
|
||||
}
|
||||
|
||||
export const countries = `${base.path.hotel}/${version}/${base.enitity.Countries}`
|
||||
export namespace Countries {
|
||||
export function country(countryId: string) {
|
||||
return `${countries}/${countryId}`
|
||||
}
|
||||
}
|
||||
|
||||
export const hotels = `${base.path.hotel}/${version}/${base.enitity.Hotels}`
|
||||
export namespace Hotels {
|
||||
export function hotel(hotelId: string) {
|
||||
return `${hotels}/${hotelId}`
|
||||
}
|
||||
export function meetingRooms(hotelId: string) {
|
||||
return `${hotels}/${hotelId}/meetingRooms`
|
||||
}
|
||||
export function merchantInformation(hotelId: string) {
|
||||
return `${hotels}/${hotelId}/merchantInformation`
|
||||
}
|
||||
export function nearbyHotels(hotelId: string) {
|
||||
return `${hotels}/${hotelId}/nearbyHotels`
|
||||
}
|
||||
export function restaurants(hotelId: string) {
|
||||
return `${hotels}/${hotelId}/restaurants`
|
||||
}
|
||||
export function roomCategories(hotelId: string) {
|
||||
return `${hotels}/${hotelId}/roomCategories`
|
||||
}
|
||||
}
|
||||
|
||||
export const locations = `${base.path.hotel}/${version}/${base.enitity.Locations}`
|
||||
}
|
||||
|
||||
/**
|
||||
* package (Swagger)
|
||||
* https://tstapi.scandichotels.com/package/swagger/v1/index.html
|
||||
*/
|
||||
export namespace Package {
|
||||
export namespace Ancillary {
|
||||
export function hotel(hotelId: string) {
|
||||
return `${base.path.package}/${version}/${base.enitity.Ancillary}/hotel/${hotelId}`
|
||||
}
|
||||
export function hotelAncillaries(hotelId: string) {
|
||||
return `${base.path.package}/${version}/${base.enitity.Ancillary}/hotel/${hotelId}/ancillaries`
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Breakfast {
|
||||
export function hotel(hotelId: string) {
|
||||
return `${base.path.package}/${version}/${base.enitity.Breakfast}/hotel/${hotelId}`
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Packages {
|
||||
export function hotel(hotelId: string) {
|
||||
return `${base.path.package}/${version}/${base.enitity.Packages}/hotel/${hotelId}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* profile (Swagger)
|
||||
* https://tstapi.scandichotels.com/profile/swagger/v1/index.html
|
||||
*/
|
||||
export namespace Profile {
|
||||
export const invalidateSessions = `${base.path.profile}/${version}/${base.enitity.Profile}/invalidateSessions`
|
||||
export const membership = `${base.path.profile}/${version}/${base.enitity.Profile}/membership`
|
||||
export const profile = `${base.path.profile}/${version}/${base.enitity.Profile}`
|
||||
export const reward = `${base.path.profile}/${version}/${base.enitity.Profile}/reward`
|
||||
export const subscriberId = `${base.path.profile}/${version}/${base.enitity.Profile}/SubscriberId`
|
||||
export const tierRewards = `${base.path.profile}/${version}/${base.enitity.Profile}/tierRewards`
|
||||
|
||||
export function deleteProfile(profileId: string) {
|
||||
return `${profile}/${profileId}`
|
||||
}
|
||||
|
||||
export const creditCards = `${base.path.profile}/${version}/${base.enitity.Profile}/creditCards`
|
||||
export namespace CreditCards {
|
||||
export const initiateSaveCard = `${creditCards}/initiateSaveCard`
|
||||
|
||||
export function deleteCreditCard(creditCardId: string) {
|
||||
return `${creditCards}/${creditCardId}`
|
||||
}
|
||||
export function transaction(transactionId: string) {
|
||||
return `${creditCards}/${transactionId}`
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Reward {
|
||||
export const allTiers = `${base.path.profile}/${version}/${base.enitity.Reward}/AllTiers`
|
||||
export const reward = `${base.path.profile}/${version}/${base.enitity.Reward}`
|
||||
export const unwrap = `${base.path.profile}/${version}/${base.enitity.Reward}/Unwrap`
|
||||
|
||||
export function claim(rewardId: string) {
|
||||
return `${base.path.profile}/${version}/${base.enitity.Reward}/Claim/${rewardId}`
|
||||
}
|
||||
}
|
||||
|
||||
export const enum Transaction {
|
||||
friendTransactions = `${base.path.profile}/${version}/${base.enitity.Transaction}/friendTransactions`,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type Endpoint = endpoints.v0 | endpoints.v1
|
||||
export type Endpoint = string
|
||||
|
||||
@@ -28,7 +28,7 @@ const wrappedFetch = fetchRetry(fetch, {
|
||||
})
|
||||
|
||||
export async function get(
|
||||
endpoint: Endpoint | `${Endpoint}/${string}`,
|
||||
endpoint: Endpoint,
|
||||
options: RequestOptionsWithOutBody,
|
||||
params = {}
|
||||
) {
|
||||
|
||||
@@ -89,3 +89,9 @@ export const getLanguageSwitcher = cache(
|
||||
export const getSiteConfig = cache(async function getMemoizedSiteConfig() {
|
||||
return serverClient().contentstack.base.siteConfig()
|
||||
})
|
||||
|
||||
export const getBreakfastPackages = cache(async function getMemoizedPackages(
|
||||
hotelId: string
|
||||
) {
|
||||
return serverClient().hotel.packages.breakfast({ hotelId })
|
||||
})
|
||||
|
||||
@@ -6,7 +6,6 @@ import { login } from "@/constants/routes/handleAuth"
|
||||
import { webviews } from "@/constants/routes/webviews"
|
||||
import { appRouter } from "@/server"
|
||||
import { createContext } from "@/server/context"
|
||||
import { internalServerError } from "@/server/errors/next"
|
||||
import { createCallerFactory } from "@/server/trpc"
|
||||
|
||||
const createCaller = createCallerFactory(appRouter)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { cookies, headers } from "next/headers"
|
||||
import { type Session } from "next-auth"
|
||||
import { cache } from "react"
|
||||
|
||||
import { Lang } from "@/constants/languages"
|
||||
|
||||
@@ -37,7 +38,7 @@ export function createContextInner(opts: CreateContextOptions) {
|
||||
* This is the actual context you'll use in your router
|
||||
* @link https://trpc.io/docs/context
|
||||
**/
|
||||
export function createContext() {
|
||||
export const createContext = cache(function () {
|
||||
const h = headers()
|
||||
|
||||
const cookie = cookies()
|
||||
@@ -66,6 +67,6 @@ export function createContext() {
|
||||
webToken: webviewTokenCookie?.value,
|
||||
contentType: h.get("x-contenttype")!,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
export type Context = ReturnType<typeof createContext>
|
||||
|
||||
@@ -62,7 +62,7 @@ export const bookingMutationRouter = router({
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
}
|
||||
|
||||
const apiResponse = await api.post(api.endpoints.v1.booking, {
|
||||
const apiResponse = await api.post(api.endpoints.v1.Booking.bookings, {
|
||||
headers,
|
||||
body: input,
|
||||
})
|
||||
|
||||
@@ -33,7 +33,7 @@ export const bookingQueryRouter = router({
|
||||
getBookingConfirmationCounter.add(1, { confirmationNumber })
|
||||
|
||||
const apiResponse = await api.get(
|
||||
`${api.endpoints.v1.booking}/${confirmationNumber}`,
|
||||
api.endpoints.v1.Booking.booking(confirmationNumber),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
@@ -142,7 +142,7 @@ export const bookingQueryRouter = router({
|
||||
getBookingStatusCounter.add(1, { confirmationNumber })
|
||||
|
||||
const apiResponse = await api.get(
|
||||
`${api.endpoints.v1.booking}/${confirmationNumber}/status`,
|
||||
api.endpoints.v1.Booking.status(confirmationNumber),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
|
||||
@@ -75,7 +75,7 @@ function getUniqueRewardIds(rewardIds: string[]) {
|
||||
|
||||
const getAllCachedApiRewards = unstable_cache(
|
||||
async function (token) {
|
||||
const apiResponse = await api.get(api.endpoints.v1.tierRewards, {
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.tierRewards, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
@@ -194,7 +194,7 @@ export const rewardQueryRouter = router({
|
||||
|
||||
const { limit, cursor } = input
|
||||
|
||||
const apiResponse = await api.get(api.endpoints.v1.rewards, {
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.reward, {
|
||||
cache: undefined, // override defaultOptions
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
@@ -393,7 +393,7 @@ export const rewardQueryRouter = router({
|
||||
surprises: contentStackBaseWithProtectedProcedure.query(async ({ ctx }) => {
|
||||
getCurrentRewardCounter.add(1)
|
||||
|
||||
const apiResponse = await api.get(api.endpoints.v1.rewards, {
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.reward, {
|
||||
cache: undefined, // override defaultOptions
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
|
||||
@@ -39,3 +39,7 @@ export const getlHotelDataInputSchema = z.object({
|
||||
.array(z.enum(["RoomCategories", "NearbyHotels", "Restaurants", "City"]))
|
||||
.optional(),
|
||||
})
|
||||
|
||||
export const getBreakfastPackageInput = z.object({
|
||||
hotelId: z.string().min(1, { message: "hotelId is required" }),
|
||||
})
|
||||
|
||||
@@ -9,7 +9,9 @@ import { getPoiGroupByCategoryName } from "./utils"
|
||||
|
||||
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||
import { AlertTypeEnum } from "@/types/enums/alert"
|
||||
import { CurrencyEnum } from "@/types/enums/currency"
|
||||
import { FacilityEnum } from "@/types/enums/facilities"
|
||||
import { PackageTypeEnum } from "@/types/enums/packages"
|
||||
import { PointOfInterestCategoryNameEnum } from "@/types/hotel"
|
||||
|
||||
const ratingsSchema = z
|
||||
@@ -653,7 +655,7 @@ export const apiCountriesSchema = z.object({
|
||||
name: z.string(),
|
||||
}),
|
||||
hotelInformationSystemId: z.number().optional(),
|
||||
id: z.string().optional(),
|
||||
id: z.string().optional().default(""),
|
||||
language: z.string().optional(),
|
||||
type: z.literal("countries"),
|
||||
})
|
||||
@@ -794,3 +796,30 @@ export const apiLocationsSchema = z.object({
|
||||
})
|
||||
),
|
||||
})
|
||||
|
||||
export const breakfastPackageSchema = z.object({
|
||||
code: z.string(),
|
||||
currency: z.nativeEnum(CurrencyEnum),
|
||||
description: z.string(),
|
||||
originalPrice: z.number().default(0),
|
||||
packagePrice: z.number(),
|
||||
packageType: z.enum([
|
||||
PackageTypeEnum.BreakfastAdult,
|
||||
PackageTypeEnum.BreakfastChildren,
|
||||
]),
|
||||
totalPrice: z.number(),
|
||||
})
|
||||
|
||||
export const breakfastPackagesSchema = z
|
||||
.object({
|
||||
data: z.object({
|
||||
attributes: z.object({
|
||||
hotelId: z.number(),
|
||||
packages: z.array(breakfastPackageSchema),
|
||||
}),
|
||||
type: z.literal("breakfastpackage"),
|
||||
}),
|
||||
})
|
||||
.transform(({ data }) =>
|
||||
data.attributes.packages.filter((pkg) => pkg.code.match(/^(BRF\d+)$/gm))
|
||||
)
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
contentStackUidWithServiceProcedure,
|
||||
publicProcedure,
|
||||
router,
|
||||
safeProtectedServiceProcedure,
|
||||
serviceProcedure,
|
||||
} from "@/server/trpc"
|
||||
import { toApiLang } from "@/server/utils"
|
||||
@@ -24,11 +25,13 @@ import {
|
||||
getHotelPageCounter,
|
||||
validateHotelPageRefs,
|
||||
} from "../contentstack/hotelPage/utils"
|
||||
import { getVerifiedUser, parsedUser } from "../user/query"
|
||||
import {
|
||||
getRoomPackagesInputSchema,
|
||||
getRoomPackagesSchema,
|
||||
} from "./schemas/packages"
|
||||
import {
|
||||
getBreakfastPackageInput,
|
||||
getHotelInputSchema,
|
||||
getHotelsAvailabilityInputSchema,
|
||||
getlHotelDataInputSchema,
|
||||
@@ -36,6 +39,7 @@ import {
|
||||
getRoomsAvailabilityInputSchema,
|
||||
} from "./input"
|
||||
import {
|
||||
breakfastPackagesSchema,
|
||||
getHotelDataSchema,
|
||||
getHotelsAvailabilitySchema,
|
||||
getRatesSchema,
|
||||
@@ -51,6 +55,7 @@ import {
|
||||
|
||||
import { FacilityCardTypeEnum } from "@/types/components/hotelPage/facilities"
|
||||
import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel"
|
||||
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
|
||||
import type { RequestOptionsWithOutBody } from "@/types/fetch"
|
||||
import type { Facility } from "@/types/hotel"
|
||||
import type { GetHotelPageData } from "@/types/trpc/routers/contentstack/hotelPage"
|
||||
@@ -88,6 +93,14 @@ const roomsAvailabilityFailCounter = meter.createCounter(
|
||||
"trpc.hotel.availability.rooms-fail"
|
||||
)
|
||||
|
||||
const breakfastPackagesCounter = meter.createCounter("trpc.package.breakfast")
|
||||
const breakfastPackagesSuccessCounter = meter.createCounter(
|
||||
"trpc.package.breakfast-success"
|
||||
)
|
||||
const breakfastPackagesFailCounter = meter.createCounter(
|
||||
"trpc.package.breakfast-fail"
|
||||
)
|
||||
|
||||
async function getContentstackData(lang: Lang, uid?: string | null) {
|
||||
if (!uid) {
|
||||
return null
|
||||
@@ -169,7 +182,7 @@ export const hotelQueryRouter = router({
|
||||
})
|
||||
)
|
||||
const apiResponse = await api.get(
|
||||
`${api.endpoints.v1.hotels}/${hotelId}`,
|
||||
api.endpoints.v1.Hotel.Hotels.hotel(hotelId),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
@@ -320,7 +333,7 @@ export const hotelQueryRouter = router({
|
||||
JSON.stringify({ query: { cityId, params } })
|
||||
)
|
||||
const apiResponse = await api.get(
|
||||
`${api.endpoints.v1.hotelsAvailability}/${cityId}`,
|
||||
api.endpoints.v1.Availability.city(cityId),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
@@ -444,7 +457,7 @@ export const hotelQueryRouter = router({
|
||||
JSON.stringify({ query: { hotelId, params } })
|
||||
)
|
||||
const apiResponse = await api.get(
|
||||
`${api.endpoints.v1.roomsAvailability}/${hotelId}`,
|
||||
api.endpoints.v1.Availability.hotel(hotelId.toString()),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
@@ -587,7 +600,7 @@ export const hotelQueryRouter = router({
|
||||
)
|
||||
|
||||
const apiResponse = await api.get(
|
||||
`${api.endpoints.v1.hotels}/${hotelId}`,
|
||||
api.endpoints.v1.Hotel.Hotels.hotel(hotelId),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
@@ -734,7 +747,7 @@ export const hotelQueryRouter = router({
|
||||
)
|
||||
|
||||
const apiResponse = await api.get(
|
||||
`${api.endpoints.v1.packages}/${hotelId}`,
|
||||
api.endpoints.v1.Package.Packages.hotel(hotelId),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
@@ -789,5 +802,114 @@ export const hotelQueryRouter = router({
|
||||
|
||||
return validatedPackagesData.data
|
||||
}),
|
||||
breakfast: safeProtectedServiceProcedure
|
||||
.input(getBreakfastPackageInput)
|
||||
.query(async function ({ ctx, input }) {
|
||||
const params = {
|
||||
Adults: 2,
|
||||
EndDate: "2024-10-28",
|
||||
StartDate: "2024-10-25",
|
||||
}
|
||||
const metricsData = { ...input, ...params }
|
||||
breakfastPackagesCounter.add(1, metricsData)
|
||||
console.info(
|
||||
"api.package.breakfast start",
|
||||
JSON.stringify({ query: metricsData })
|
||||
)
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Package.Breakfast.hotel(input.hotelId),
|
||||
{
|
||||
cache: undefined,
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
},
|
||||
next: {
|
||||
revalidate: 60,
|
||||
},
|
||||
},
|
||||
params
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
breakfastPackagesFailCounter.add(1, {
|
||||
...metricsData,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.hotels.hotelsAvailability error",
|
||||
JSON.stringify({
|
||||
query: metricsData,
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const breakfastPackages = breakfastPackagesSchema.safeParse(apiJson)
|
||||
if (!breakfastPackages.success) {
|
||||
hotelsAvailabilityFailCounter.add(1, {
|
||||
...metricsData,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(breakfastPackages.error),
|
||||
})
|
||||
console.error(
|
||||
"api.package.breakfast validation error",
|
||||
JSON.stringify({
|
||||
query: metricsData,
|
||||
error: breakfastPackages.error,
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
breakfastPackagesSuccessCounter.add(1, metricsData)
|
||||
console.info(
|
||||
"api.package.breakfast success",
|
||||
JSON.stringify({
|
||||
query: metricsData,
|
||||
})
|
||||
)
|
||||
|
||||
if (ctx.session?.token) {
|
||||
const apiUser = await getVerifiedUser({ session: ctx.session })
|
||||
if (apiUser && !("error" in apiUser)) {
|
||||
const user = parsedUser(apiUser.data, false)
|
||||
if (
|
||||
user.membership &&
|
||||
["L6", "L7"].includes(user.membership.membershipLevel)
|
||||
) {
|
||||
const originalBreakfastPackage = breakfastPackages.data.find(
|
||||
(pkg) => pkg.code === BreakfastPackageEnum.REGULAR_BREAKFAST
|
||||
)
|
||||
const freeBreakfastPackage = breakfastPackages.data.find(
|
||||
(pkg) => pkg.code === BreakfastPackageEnum.FREE_MEMBER_BREAKFAST
|
||||
)
|
||||
if (freeBreakfastPackage) {
|
||||
if (originalBreakfastPackage) {
|
||||
freeBreakfastPackage.originalPrice =
|
||||
originalBreakfastPackage.packagePrice
|
||||
}
|
||||
return [freeBreakfastPackage]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return breakfastPackages.data.filter(
|
||||
(pkg) => pkg.code !== BreakfastPackageEnum.FREE_MEMBER_BREAKFAST
|
||||
)
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -96,7 +96,7 @@ export async function getCountries(
|
||||
return unstable_cache(
|
||||
async function (searchParams) {
|
||||
const countryResponse = await api.get(
|
||||
api.endpoints.v1.countries,
|
||||
api.endpoints.v1.Hotel.countries,
|
||||
options,
|
||||
searchParams
|
||||
)
|
||||
@@ -136,7 +136,7 @@ export async function getCitiesByCountry(
|
||||
await Promise.all(
|
||||
searchedCountries.data.map(async (country) => {
|
||||
const countryResponse = await api.get(
|
||||
`${api.endpoints.v1.citiesCountry}/${country.name}`,
|
||||
api.endpoints.v1.Hotel.Cities.country(country.name),
|
||||
options,
|
||||
searchParams
|
||||
)
|
||||
@@ -182,7 +182,7 @@ export async function getLocations(
|
||||
groupedCitiesByCountry: CitiesGroupedByCountry | null
|
||||
) {
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.locations,
|
||||
api.endpoints.v1.Hotel.locations,
|
||||
options,
|
||||
searchParams
|
||||
)
|
||||
|
||||
@@ -35,16 +35,19 @@ export const userMutationRouter = router({
|
||||
"api.user.creditCard.add start",
|
||||
JSON.stringify({ query: { language: input.language } })
|
||||
)
|
||||
const apiResponse = await api.post(api.endpoints.v1.initiateSaveCard, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
},
|
||||
body: {
|
||||
language: input.language,
|
||||
mobileToken: false,
|
||||
redirectUrl: `api/web/add-card-callback/${input.language}`,
|
||||
},
|
||||
})
|
||||
const apiResponse = await api.post(
|
||||
api.endpoints.v1.Profile.CreditCards.initiateSaveCard,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
},
|
||||
body: {
|
||||
language: input.language,
|
||||
mobileToken: false,
|
||||
redirectUrl: `api/web/add-card-callback/${input.language}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
@@ -85,7 +88,7 @@ export const userMutationRouter = router({
|
||||
.mutation(async function ({ ctx, input }) {
|
||||
console.info("api.user.creditCard.save start", JSON.stringify({}))
|
||||
const apiResponse = await api.post(
|
||||
`${api.endpoints.v1.creditCards}/${input.transactionId}`,
|
||||
api.endpoints.v1.Profile.CreditCards.transaction(input.transactionId),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
@@ -118,7 +121,9 @@ export const userMutationRouter = router({
|
||||
JSON.stringify({ query: {} })
|
||||
)
|
||||
const apiResponse = await api.remove(
|
||||
`${api.endpoints.v1.creditCards}/${input.creditCardId}`,
|
||||
api.endpoints.v1.Profile.CreditCards.deleteCreditCard(
|
||||
input.creditCardId
|
||||
),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
@@ -149,7 +154,7 @@ export const userMutationRouter = router({
|
||||
ctx,
|
||||
}) {
|
||||
generatePreferencesLinkCounter.add(1)
|
||||
const apiResponse = await api.get(api.endpoints.v1.subscriberId, {
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.subscriberId, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
},
|
||||
|
||||
@@ -89,7 +89,7 @@ export const getVerifiedUser = cache(
|
||||
}
|
||||
getVerifiedUserCounter.add(1)
|
||||
console.info("api.user.profile getVerifiedUser start", JSON.stringify({}))
|
||||
const apiResponse = await api.get(api.endpoints.v1.profile, {
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.profile, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${session.token.access_token}`,
|
||||
},
|
||||
@@ -163,7 +163,7 @@ export const getVerifiedUser = cache(
|
||||
}
|
||||
)
|
||||
|
||||
function parsedUser(data: User, isMFA: boolean) {
|
||||
export function parsedUser(data: User, isMFA: boolean) {
|
||||
const country = countries.find((c) => c.code === data.address.countryCode)
|
||||
|
||||
const user = {
|
||||
@@ -211,7 +211,7 @@ function parsedUser(data: User, isMFA: boolean) {
|
||||
async function getCreditCards(session: Session) {
|
||||
getCreditCardsCounter.add(1)
|
||||
console.info("api.profile.creditCards start", JSON.stringify({}))
|
||||
const apiResponse = await api.get(api.endpoints.v1.creditCards, {
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.creditCards, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${session.token.access_token}`,
|
||||
},
|
||||
@@ -354,7 +354,7 @@ export const userQueryRouter = router({
|
||||
JSON.stringify({ query: { params } })
|
||||
)
|
||||
const previousStaysResponse = await api.get(
|
||||
api.endpoints.v1.previousStays,
|
||||
api.endpoints.v1.Booking.Stays.past,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
@@ -430,7 +430,7 @@ export const userQueryRouter = router({
|
||||
)
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.previousStays,
|
||||
api.endpoints.v1.Booking.Stays.past,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
@@ -492,7 +492,7 @@ export const userQueryRouter = router({
|
||||
)
|
||||
const nextCursor =
|
||||
verifiedData.data.links &&
|
||||
verifiedData.data.links.offset < verifiedData.data.links.totalCount
|
||||
verifiedData.data.links.offset < verifiedData.data.links.totalCount
|
||||
? verifiedData.data.links.offset
|
||||
: undefined
|
||||
|
||||
@@ -525,7 +525,7 @@ export const userQueryRouter = router({
|
||||
JSON.stringify({ query: { params } })
|
||||
)
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.upcomingStays,
|
||||
api.endpoints.v1.Booking.Stays.future,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
@@ -585,7 +585,7 @@ export const userQueryRouter = router({
|
||||
})
|
||||
const nextCursor =
|
||||
verifiedData.data.links &&
|
||||
verifiedData.data.links.offset < verifiedData.data.links.totalCount
|
||||
verifiedData.data.links.offset < verifiedData.data.links.totalCount
|
||||
? verifiedData.data.links.offset
|
||||
: undefined
|
||||
|
||||
@@ -611,13 +611,16 @@ export const userQueryRouter = router({
|
||||
"api.transaction.friendTransactions start",
|
||||
JSON.stringify({})
|
||||
)
|
||||
const apiResponse = await api.get(api.endpoints.v1.friendTransactions, {
|
||||
cache: undefined, // override defaultOptions
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
},
|
||||
next: { revalidate: 30 * 60 * 1000 },
|
||||
})
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Profile.Transaction.friendTransactions,
|
||||
{
|
||||
cache: undefined, // override defaultOptions
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
},
|
||||
next: { revalidate: 30 * 60 * 1000 },
|
||||
}
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
// switch (apiResponse.status) {
|
||||
@@ -740,7 +743,7 @@ export const userQueryRouter = router({
|
||||
membershipCards: protectedProcedure.query(async function ({ ctx }) {
|
||||
getProfileCounter.add(1)
|
||||
console.info("api.profile start", JSON.stringify({}))
|
||||
const apiResponse = await api.get(api.endpoints.v1.profile, {
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.profile, {
|
||||
cache: "no-store",
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
|
||||
@@ -35,7 +35,7 @@ async function updateStaysBookingUrl(
|
||||
// Temporary API call needed till we have user name in ctx session data
|
||||
getProfileCounter.add(1)
|
||||
console.info("api.user.profile updatebookingurl start", JSON.stringify({}))
|
||||
const apiResponse = await api.get(api.endpoints.v1.profile, {
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.profile, {
|
||||
cache: "no-store",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
|
||||
@@ -176,7 +176,7 @@ export const protectedServerActionProcedure = serverActionProcedure.use(
|
||||
}
|
||||
)
|
||||
|
||||
// NOTE: This is actually save to use, just the implementation could change
|
||||
// NOTE: This is actually safe to use, just the implementation could change
|
||||
// in minor version bumps. Please read: https://trpc.io/docs/faq#unstable
|
||||
export const contentStackUidWithServiceProcedure =
|
||||
contentstackExtendedProcedureUID.unstable_concat(serviceProcedure)
|
||||
@@ -186,3 +186,6 @@ export const contentStackBaseWithServiceProcedure =
|
||||
|
||||
export const contentStackBaseWithProtectedProcedure =
|
||||
contentstackBaseProcedure.unstable_concat(protectedProcedure)
|
||||
|
||||
export const safeProtectedServiceProcedure =
|
||||
safeProtectedProcedure.unstable_concat(serviceProcedure)
|
||||
|
||||
@@ -3,21 +3,22 @@ import { createContext, useContext } from "react"
|
||||
import { create, useStore } from "zustand"
|
||||
|
||||
import { bedTypeSchema } from "@/components/HotelReservation/EnterDetails/BedType/schema"
|
||||
import { breakfastSchema } from "@/components/HotelReservation/EnterDetails/Breakfast/schema"
|
||||
import { breakfastStoreSchema } from "@/components/HotelReservation/EnterDetails/Breakfast/schema"
|
||||
import { detailsSchema } from "@/components/HotelReservation/EnterDetails/Details/schema"
|
||||
|
||||
import { BreakfastPackage } from "@/types/components/enterDetails/breakfast"
|
||||
import { DetailsSchema } from "@/types/components/enterDetails/details"
|
||||
import { SidePeekEnum } from "@/types/components/enterDetails/sidePeek"
|
||||
import { StepEnum } from "@/types/components/enterDetails/step"
|
||||
import { bedTypeEnum } from "@/types/enums/bedType"
|
||||
import { breakfastEnum } from "@/types/enums/breakfast"
|
||||
import { BedTypeEnum } from "@/types/enums/bedType"
|
||||
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
|
||||
|
||||
const SESSION_STORAGE_KEY = "enterDetails"
|
||||
|
||||
interface EnterDetailsState {
|
||||
data: {
|
||||
bedType: bedTypeEnum | undefined
|
||||
breakfast: breakfastEnum | undefined
|
||||
bedType: BedTypeEnum | undefined
|
||||
breakfast: BreakfastPackage | BreakfastPackageEnum.NO_BREAKFAST | undefined
|
||||
} & DetailsSchema
|
||||
steps: StepEnum[]
|
||||
currentStep: StepEnum
|
||||
@@ -26,7 +27,7 @@ interface EnterDetailsState {
|
||||
completeStep: (updatedData: Partial<EnterDetailsState["data"]>) => void
|
||||
navigate: (
|
||||
step: StepEnum,
|
||||
updatedData?: Record<string, string | boolean>
|
||||
updatedData?: Record<string, string | boolean | BreakfastPackage>
|
||||
) => void
|
||||
setCurrentStep: (step: StepEnum) => void
|
||||
openSidePeek: (key: SidePeekEnum | null) => void
|
||||
@@ -75,7 +76,7 @@ export function initEditDetailsState(currentStep: StepEnum) {
|
||||
initialData = { ...initialData, ...validatedBedType.data }
|
||||
isValid[StepEnum.selectBed] = true
|
||||
}
|
||||
const validatedBreakfast = breakfastSchema.safeParse(inputData)
|
||||
const validatedBreakfast = breakfastStoreSchema.safeParse(inputData)
|
||||
if (validatedBreakfast.success) {
|
||||
validPaths.push(StepEnum.details)
|
||||
initialData = { ...initialData, ...validatedBreakfast.data }
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import { breakfastSchema } from "@/components/HotelReservation/EnterDetails/Breakfast/schema"
|
||||
import {
|
||||
breakfastPackageSchema,
|
||||
breakfastPackagesSchema,
|
||||
} from "@/server/routers/hotels/output"
|
||||
|
||||
export interface BreakfastSchema extends z.output<typeof breakfastSchema> {}
|
||||
import { breakfastFormSchema } from "@/components/HotelReservation/EnterDetails/Breakfast/schema"
|
||||
|
||||
export interface BreakfastFormSchema
|
||||
extends z.output<typeof breakfastFormSchema> {}
|
||||
|
||||
export interface BreakfastPackages
|
||||
extends z.output<typeof breakfastPackagesSchema> {}
|
||||
|
||||
export interface BreakfastPackage
|
||||
extends z.output<typeof breakfastPackageSchema> {}
|
||||
|
||||
export interface BreakfastProps {
|
||||
packages: BreakfastPackages | null
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export enum bedTypeEnum {
|
||||
export enum BedTypeEnum {
|
||||
KING = "KING",
|
||||
QUEEN = "QUEEN",
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export enum breakfastEnum {
|
||||
BREAKFAST = "BREAKFAST",
|
||||
export enum BreakfastPackageEnum {
|
||||
FREE_MEMBER_BREAKFAST = "BRF0",
|
||||
REGULAR_BREAKFAST = "BRF1",
|
||||
NO_BREAKFAST = "NO_BREAKFAST",
|
||||
}
|
||||
|
||||
7
types/enums/currency.ts
Normal file
7
types/enums/currency.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export enum CurrencyEnum {
|
||||
DKK = "DKK",
|
||||
EUR = "EUR",
|
||||
NOK = "NOK",
|
||||
PLN = "PLN",
|
||||
SEK = "SEK",
|
||||
}
|
||||
7
types/enums/packages.ts
Normal file
7
types/enums/packages.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export enum PackageTypeEnum {
|
||||
AccessibleFriendlyRoom = "AccessibleFriendlyRoom",
|
||||
AllergyRoom = "AllergyRoom",
|
||||
BreakfastAdult = "BreakfastAdult",
|
||||
BreakfastChildren = "BreakfastChildren",
|
||||
PetRoom = "PetRoom",
|
||||
}
|
||||
Reference in New Issue
Block a user