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,
|
body,
|
||||||
cache: "no-store",
|
cache: "no-store",
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export const registerUser = serviceServerActionProcedure
|
|||||||
|
|
||||||
let apiResponse
|
let apiResponse
|
||||||
try {
|
try {
|
||||||
apiResponse = await api.post(api.endpoints.v1.profile, {
|
apiResponse = await api.post(api.endpoints.v1.Profile.profile, {
|
||||||
body: parsedPayload.data,
|
body: parsedPayload.data,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
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.
|
// TODO: Consume the API to register the user as soon as passwordless signup is enabled.
|
||||||
// let apiResponse
|
// let apiResponse
|
||||||
// try {
|
// try {
|
||||||
// apiResponse = await api.post(api.endpoints.v1.profile, {
|
// apiResponse = await api.post(api.endpoints.v1.Profile.profile, {
|
||||||
// body: payload,
|
// body: payload,
|
||||||
// headers: {
|
// headers: {
|
||||||
// Authorization: `Bearer ${ctx.serviceToken}`,
|
// 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 EnterDetailsProvider from "@/components/HotelReservation/EnterDetails/Provider"
|
||||||
import SelectedRoom from "@/components/HotelReservation/EnterDetails/SelectedRoom"
|
import SelectedRoom from "@/components/HotelReservation/EnterDetails/SelectedRoom"
|
||||||
import SidePeek from "@/components/HotelReservation/EnterDetails/SidePeek"
|
|
||||||
import Summary from "@/components/HotelReservation/EnterDetails/Summary"
|
import Summary from "@/components/HotelReservation/EnterDetails/Summary"
|
||||||
import HotelSelectionHeader from "@/components/HotelReservation/HotelSelectionHeader"
|
|
||||||
import { setLang } from "@/i18n/serverContext"
|
import { setLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
|
import { preload } from "./page"
|
||||||
|
|
||||||
import styles from "./layout.module.css"
|
import styles from "./layout.module.css"
|
||||||
|
|
||||||
import { StepEnum } from "@/types/components/enterDetails/step"
|
import { StepEnum } from "@/types/components/enterDetails/step"
|
||||||
import type { LangParams, LayoutArgs } from "@/types/params"
|
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({
|
export default async function StepLayout({
|
||||||
children,
|
children,
|
||||||
|
hotelHeader,
|
||||||
params,
|
params,
|
||||||
}: React.PropsWithChildren<LayoutArgs<LangParams & { step: StepEnum }>>) {
|
sidePeek,
|
||||||
setLang(params.lang)
|
}: React.PropsWithChildren<
|
||||||
preload("811", params.lang)
|
LayoutArgs<LangParams & { step: StepEnum }> & {
|
||||||
|
hotelHeader: React.ReactNode
|
||||||
const hotel = await getHotelData("811", params.lang)
|
sidePeek: React.ReactNode
|
||||||
|
|
||||||
if (!hotel?.data) {
|
|
||||||
redirect(`/${params.lang}`)
|
|
||||||
}
|
}
|
||||||
|
>) {
|
||||||
|
setLang(params.lang)
|
||||||
|
preload()
|
||||||
return (
|
return (
|
||||||
<EnterDetailsProvider step={params.step}>
|
<EnterDetailsProvider step={params.step}>
|
||||||
<main className={styles.layout}>
|
<main className={styles.layout}>
|
||||||
<HotelSelectionHeader hotel={hotel.data.attributes} />
|
{hotelHeader}
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<SelectedRoom />
|
<SelectedRoom />
|
||||||
{children}
|
{children}
|
||||||
@@ -48,7 +34,7 @@ export default async function StepLayout({
|
|||||||
<Summary />
|
<Summary />
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
<SidePeek hotel={hotel.data.attributes} />
|
{sidePeek}
|
||||||
</main>
|
</main>
|
||||||
</EnterDetailsProvider>
|
</EnterDetailsProvider>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { notFound } from "next/navigation"
|
import { notFound, redirect } from "next/navigation"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
getBreakfastPackages,
|
||||||
getCreditCardsSafely,
|
getCreditCardsSafely,
|
||||||
getHotelData,
|
getHotelData,
|
||||||
getProfileSafely,
|
getProfileSafely,
|
||||||
@@ -17,22 +18,32 @@ import { getIntl } from "@/i18n"
|
|||||||
import { StepEnum } from "@/types/components/enterDetails/step"
|
import { StepEnum } from "@/types/components/enterDetails/step"
|
||||||
import type { LangParams, PageArgs } from "@/types/params"
|
import type { LangParams, PageArgs } from "@/types/params"
|
||||||
|
|
||||||
|
export function preload() {
|
||||||
|
void getProfileSafely()
|
||||||
|
void getCreditCardsSafely()
|
||||||
|
}
|
||||||
|
|
||||||
function isValidStep(step: string): step is StepEnum {
|
function isValidStep(step: string): step is StepEnum {
|
||||||
return Object.values(StepEnum).includes(step as StepEnum)
|
return Object.values(StepEnum).includes(step as StepEnum)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function StepPage({
|
export default async function StepPage({
|
||||||
params,
|
params,
|
||||||
}: PageArgs<LangParams & { step: StepEnum }>) {
|
searchParams,
|
||||||
const { step, lang } = params
|
}: PageArgs<LangParams & { step: StepEnum }, { hotel: string }>) {
|
||||||
|
if (!searchParams.hotel) {
|
||||||
|
redirect(`/${params.lang}`)
|
||||||
|
}
|
||||||
|
void getBreakfastPackages(searchParams.hotel)
|
||||||
|
|
||||||
const intl = await getIntl()
|
const intl = await getIntl()
|
||||||
|
|
||||||
const hotel = await getHotelData("811", lang)
|
const hotel = await getHotelData(searchParams.hotel, params.lang)
|
||||||
const user = await getProfileSafely()
|
const user = await getProfileSafely()
|
||||||
const savedCreditCards = await getCreditCardsSafely()
|
const savedCreditCards = await getCreditCardsSafely()
|
||||||
|
const breakfastPackages = await getBreakfastPackages(searchParams.hotel)
|
||||||
|
|
||||||
if (!isValidStep(step) || !hotel) {
|
if (!isValidStep(params.step) || !hotel) {
|
||||||
return notFound()
|
return notFound()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +62,7 @@ export default async function StepPage({
|
|||||||
step={StepEnum.breakfast}
|
step={StepEnum.breakfast}
|
||||||
label={intl.formatMessage({ id: "Select breakfast options" })}
|
label={intl.formatMessage({ id: "Select breakfast options" })}
|
||||||
>
|
>
|
||||||
<Breakfast />
|
<Breakfast packages={breakfastPackages} />
|
||||||
</SectionAccordion>
|
</SectionAccordion>
|
||||||
<SectionAccordion
|
<SectionAccordion
|
||||||
header="Details"
|
header="Details"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { bedTypeSchema } from "./schema"
|
|||||||
import styles from "./bedOptions.module.css"
|
import styles from "./bedOptions.module.css"
|
||||||
|
|
||||||
import type { BedTypeSchema } from "@/types/components/enterDetails/bedType"
|
import type { BedTypeSchema } from "@/types/components/enterDetails/bedType"
|
||||||
import { bedTypeEnum } from "@/types/enums/bedType"
|
import { BedTypeEnum } from "@/types/enums/bedType"
|
||||||
|
|
||||||
export default function BedType() {
|
export default function BedType() {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
@@ -61,7 +61,7 @@ export default function BedType() {
|
|||||||
<RadioCard
|
<RadioCard
|
||||||
Icon={KingBedIcon}
|
Icon={KingBedIcon}
|
||||||
iconWidth={46}
|
iconWidth={46}
|
||||||
id={bedTypeEnum.KING}
|
id={BedTypeEnum.KING}
|
||||||
name="bedType"
|
name="bedType"
|
||||||
subtitle={intl.formatMessage(
|
subtitle={intl.formatMessage(
|
||||||
{ id: "{width} cm × {length} cm" },
|
{ id: "{width} cm × {length} cm" },
|
||||||
@@ -72,12 +72,12 @@ export default function BedType() {
|
|||||||
)}
|
)}
|
||||||
text={text}
|
text={text}
|
||||||
title={intl.formatMessage({ id: "King bed" })}
|
title={intl.formatMessage({ id: "King bed" })}
|
||||||
value={bedTypeEnum.KING}
|
value={BedTypeEnum.KING}
|
||||||
/>
|
/>
|
||||||
<RadioCard
|
<RadioCard
|
||||||
Icon={KingBedIcon}
|
Icon={KingBedIcon}
|
||||||
iconWidth={46}
|
iconWidth={46}
|
||||||
id={bedTypeEnum.QUEEN}
|
id={BedTypeEnum.QUEEN}
|
||||||
name="bedType"
|
name="bedType"
|
||||||
subtitle={intl.formatMessage(
|
subtitle={intl.formatMessage(
|
||||||
{ id: "{width} cm × {length} cm" },
|
{ id: "{width} cm × {length} cm" },
|
||||||
@@ -88,7 +88,7 @@ export default function BedType() {
|
|||||||
)}
|
)}
|
||||||
text={text}
|
text={text}
|
||||||
title={intl.formatMessage({ id: "Queen bed" })}
|
title={intl.formatMessage({ id: "Queen bed" })}
|
||||||
value={bedTypeEnum.QUEEN}
|
value={BedTypeEnum.QUEEN}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
import { bedTypeEnum } from "@/types/enums/bedType"
|
import { BedTypeEnum } from "@/types/enums/bedType"
|
||||||
|
|
||||||
export const bedTypeSchema = z.object({
|
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 { 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 RadioCard from "@/components/TempDesignSystem/Form/ChoiceCard/Radio"
|
||||||
|
|
||||||
import { breakfastSchema } from "./schema"
|
import { breakfastFormSchema } from "./schema"
|
||||||
|
|
||||||
import styles from "./breakfast.module.css"
|
import styles from "./breakfast.module.css"
|
||||||
|
|
||||||
import type { BreakfastSchema } from "@/types/components/enterDetails/breakfast"
|
import type {
|
||||||
import { breakfastEnum } from "@/types/enums/breakfast"
|
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 intl = useIntl()
|
||||||
|
|
||||||
const breakfast = useEnterDetailsStore((state) => state.data.breakfast)
|
const breakfast = useEnterDetailsStore((state) => state.data.breakfast)
|
||||||
|
|
||||||
const methods = useForm<BreakfastSchema>({
|
let defaultValues = undefined
|
||||||
defaultValues: breakfast ? { breakfast } : 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",
|
criteriaMode: "all",
|
||||||
mode: "all",
|
mode: "all",
|
||||||
resolver: zodResolver(breakfastSchema),
|
resolver: zodResolver(breakfastFormSchema),
|
||||||
reValidateMode: "onChange",
|
reValidateMode: "onChange",
|
||||||
})
|
})
|
||||||
|
|
||||||
const completeStep = useEnterDetailsStore((state) => state.completeStep)
|
const completeStep = useEnterDetailsStore((state) => state.completeStep)
|
||||||
|
|
||||||
const onSubmit = useCallback(
|
const onSubmit = useCallback(
|
||||||
(values: BreakfastSchema) => {
|
(values: BreakfastFormSchema) => {
|
||||||
completeStep(values)
|
const pkg = packages?.find((p) => p.code === values.breakfast)
|
||||||
|
if (pkg) {
|
||||||
|
completeStep({ breakfast: pkg })
|
||||||
|
} else {
|
||||||
|
completeStep({ breakfast: BreakfastPackageEnum.NO_BREAKFAST })
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[completeStep]
|
[completeStep, packages]
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -47,30 +61,46 @@ export default function Breakfast() {
|
|||||||
return () => subscription.unsubscribe()
|
return () => subscription.unsubscribe()
|
||||||
}, [methods, onSubmit])
|
}, [methods, onSubmit])
|
||||||
|
|
||||||
|
if (!packages) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...methods}>
|
<FormProvider {...methods}>
|
||||||
<form className={styles.form} onSubmit={methods.handleSubmit(onSubmit)}>
|
<form className={styles.form} onSubmit={methods.handleSubmit(onSubmit)}>
|
||||||
<RadioCard
|
{packages.map((pkg) => (
|
||||||
Icon={BreakfastIcon}
|
<RadioCard
|
||||||
id={breakfastEnum.BREAKFAST}
|
key={pkg.code}
|
||||||
name="breakfast"
|
id={pkg.code}
|
||||||
subtitle={intl.formatMessage<React.ReactNode>(
|
name="breakfast"
|
||||||
{ id: "<b>{amount} {currency}</b>/night per adult" },
|
subtitle={
|
||||||
{
|
pkg.code === BreakfastPackageEnum.FREE_MEMBER_BREAKFAST
|
||||||
amount: "150",
|
? intl.formatMessage<React.ReactNode>(
|
||||||
b: (str) => <b>{str}</b>,
|
{ id: "breakfast.price.free" },
|
||||||
currency: "SEK",
|
{
|
||||||
|
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({
|
||||||
text={intl.formatMessage({
|
id: "All our breakfast buffets offer gluten free, vegan, and allergy-friendly options.",
|
||||||
id: "All our breakfast buffets offer gluten free, vegan, and allergy-friendly options.",
|
})}
|
||||||
})}
|
title={intl.formatMessage({ id: "Breakfast buffet" })}
|
||||||
title={intl.formatMessage({ id: "Breakfast buffet" })}
|
value={pkg.code}
|
||||||
value={breakfastEnum.BREAKFAST}
|
/>
|
||||||
/>
|
))}
|
||||||
<RadioCard
|
<RadioCard
|
||||||
Icon={NoBreakfastIcon}
|
id={BreakfastPackageEnum.NO_BREAKFAST}
|
||||||
id={breakfastEnum.NO_BREAKFAST}
|
|
||||||
name="breakfast"
|
name="breakfast"
|
||||||
subtitle={intl.formatMessage(
|
subtitle={intl.formatMessage(
|
||||||
{ id: "{amount} {currency}" },
|
{ 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.",
|
id: "You can always change your mind later and add breakfast at the hotel.",
|
||||||
})}
|
})}
|
||||||
title={intl.formatMessage({ id: "No breakfast" })}
|
title={intl.formatMessage({ id: "No breakfast" })}
|
||||||
value={breakfastEnum.NO_BREAKFAST}
|
value={BreakfastPackageEnum.NO_BREAKFAST}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
import { breakfastEnum } from "@/types/enums/breakfast"
|
import { breakfastPackageSchema } from "@/server/routers/hotels/output"
|
||||||
|
|
||||||
export const breakfastSchema = z.object({
|
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
|
||||||
breakfast: z.nativeEnum(breakfastEnum),
|
|
||||||
|
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) {
|
.listItem:nth-of-type(n + 2) {
|
||||||
margin-top: var(--Spacing-x-quarter);
|
margin-top: var(--Spacing-x-quarter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
color: var(--Scandic-Brand-Scandic-Red);
|
||||||
|
}
|
||||||
|
|||||||
@@ -34,3 +34,12 @@ export type CheckboxProps =
|
|||||||
export type RadioProps =
|
export type RadioProps =
|
||||||
| Omit<ListCardProps, "type">
|
| Omit<ListCardProps, "type">
|
||||||
| Omit<TextCardProps, "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 { 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 Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||||
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
||||||
|
|
||||||
import styles from "./card.module.css"
|
import styles from "./card.module.css"
|
||||||
|
|
||||||
import type { CardProps } from "./card"
|
import type { CardProps, ListProps, SubtitleProps, TextProps } from "./card"
|
||||||
|
|
||||||
export default function Card({
|
export default function Card({
|
||||||
Icon = HeartIcon,
|
Icon,
|
||||||
iconHeight = 32,
|
iconHeight = 32,
|
||||||
iconWidth = 32,
|
iconWidth = 32,
|
||||||
declined = false,
|
declined = false,
|
||||||
@@ -26,56 +26,79 @@ export default function Card({
|
|||||||
value,
|
value,
|
||||||
}: CardProps) {
|
}: CardProps) {
|
||||||
const { register } = useFormContext()
|
const { register } = useFormContext()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<label className={styles.label} data-declined={declined} tabIndex={0}>
|
<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}
|
{title}
|
||||||
</Caption>
|
</Caption>
|
||||||
{subtitle ? (
|
<Subtitle highlightSubtitle={highlightSubtitle} subtitle={subtitle} />
|
||||||
<Caption
|
{Icon ? (
|
||||||
className={styles.subtitle}
|
<Icon
|
||||||
color={highlightSubtitle ? "baseTextAccent" : "uiTextHighContrast"}
|
className={styles.icon}
|
||||||
type="regular"
|
color="uiTextHighContrast"
|
||||||
>
|
height={iconHeight}
|
||||||
{subtitle}
|
width={iconWidth}
|
||||||
</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>
|
|
||||||
) : null}
|
) : null}
|
||||||
|
<List declined={declined} list={list} />
|
||||||
|
<Text text={text} />
|
||||||
<input
|
<input
|
||||||
|
{...register(name)}
|
||||||
aria-hidden
|
aria-hidden
|
||||||
id={id || name}
|
id={id || name}
|
||||||
hidden
|
hidden
|
||||||
type={type}
|
type={type}
|
||||||
value={value}
|
value={value}
|
||||||
{...register(name)}
|
|
||||||
/>
|
/>
|
||||||
</label>
|
</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 {
|
export enum BookingStatusEnum {
|
||||||
CreatedInOhip = "CreatedInOhip",
|
|
||||||
PaymentRegistered = "PaymentRegistered",
|
|
||||||
BookingCompleted = "BookingCompleted",
|
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 {
|
export enum BedTypeEnum {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"<b>Included</b> (based on availability)": "<b>Inkluderet</b> (baseret på tilgængelighed)",
|
"<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 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",
|
"A photo of the room": "Et foto af værelset",
|
||||||
"ACCE": "Tilgængelighed",
|
"ACCE": "Tilgængelighed",
|
||||||
@@ -366,6 +365,8 @@
|
|||||||
"booking.rooms": "{totalRooms, plural, one {# værelse} other {# værelser}}",
|
"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.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",
|
"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",
|
"by": "inden",
|
||||||
"characters": "tegn",
|
"characters": "tegn",
|
||||||
"guest": "gæst",
|
"guest": "gæst",
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"<b>Included</b> (based on availability)": "<b>Inbegriffen</b> (je nach Verfügbarkeit)",
|
"<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 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",
|
"A photo of the room": "Ein Foto des Zimmers",
|
||||||
"ACCE": "Zugänglichkeit",
|
"ACCE": "Zugänglichkeit",
|
||||||
@@ -367,6 +366,8 @@
|
|||||||
"booking.rooms": "{totalRooms, plural, one {# zimmer} other {# räume}}",
|
"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.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",
|
"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",
|
"by": "bis",
|
||||||
"characters": "figuren",
|
"characters": "figuren",
|
||||||
"guest": "gast",
|
"guest": "gast",
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"<b>Included</b> (based on availability)": "<b>Included</b> (based on availability)",
|
"<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 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",
|
"A photo of the room": "A photo of the room",
|
||||||
"ACCE": "Accessibility",
|
"ACCE": "Accessibility",
|
||||||
@@ -385,6 +384,8 @@
|
|||||||
"booking.rooms": "{totalRooms, plural, one {# room} other {# rooms}}",
|
"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.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",
|
"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",
|
"by": "by",
|
||||||
"characters": "characters",
|
"characters": "characters",
|
||||||
"from": "from",
|
"from": "from",
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"<b>Included</b> (based on availability)": "<b>Sisältyy</b> (saatavuuden mukaan)",
|
"<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 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",
|
"A photo of the room": "Kuva huoneesta",
|
||||||
"ACCE": "Saavutettavuus",
|
"ACCE": "Saavutettavuus",
|
||||||
@@ -367,6 +366,8 @@
|
|||||||
"booking.rooms": "{totalRooms, plural, one {# huone} other {# sviitti}}",
|
"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.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",
|
"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ä",
|
"by": "mennessä",
|
||||||
"characters": "hahmoja",
|
"characters": "hahmoja",
|
||||||
"guest": "Vieras",
|
"guest": "Vieras",
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"<b>Included</b> (based on availability)": "<b>Inkludert</b> (basert på tilgjengelighet)",
|
"<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 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",
|
"A photo of the room": "Et bilde av rommet",
|
||||||
"ACCE": "Tilgjengelighet",
|
"ACCE": "Tilgjengelighet",
|
||||||
@@ -363,6 +362,8 @@
|
|||||||
"booking.nights": "{totalNights, plural, one {# natt} other {# netter}}",
|
"booking.nights": "{totalNights, plural, one {# natt} other {# netter}}",
|
||||||
"booking.rooms": "{totalRooms, plural, one {# rom} other {# rom}}",
|
"booking.rooms": "{totalRooms, plural, one {# rom} other {# rom}}",
|
||||||
"booking.thisRoomIsEquippedWith": "Dette rommet er utstyrt med",
|
"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",
|
"by": "innen",
|
||||||
"characters": "tegn",
|
"characters": "tegn",
|
||||||
"guest": "gjest",
|
"guest": "gjest",
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"<b>Included</b> (based on availability)": "<b>Ingår</b> (baserat på tillgänglighet)",
|
"<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 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",
|
"A photo of the room": "Ett foto av rummet",
|
||||||
"ACCE": "Tillgänglighet",
|
"ACCE": "Tillgänglighet",
|
||||||
@@ -364,6 +363,8 @@
|
|||||||
"booking.rooms": "{totalRooms, plural, one {# rum} other {# rum}}",
|
"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.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",
|
"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",
|
"by": "innan",
|
||||||
"characters": "tecken",
|
"characters": "tecken",
|
||||||
"guest": "gäst",
|
"guest": "gäst",
|
||||||
|
|||||||
@@ -2,29 +2,190 @@
|
|||||||
* Nested enum requires namespace
|
* Nested enum requires namespace
|
||||||
*/
|
*/
|
||||||
export namespace endpoints {
|
export namespace endpoints {
|
||||||
export const enum v0 {
|
namespace base {
|
||||||
profile = "profile/v0/Profile",
|
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",
|
export namespace v1 {
|
||||||
roomsAvailability = "availability/v1/availabilities/hotel",
|
const version = "v1"
|
||||||
profile = "profile/v1/Profile",
|
/**
|
||||||
booking = "booking/v1/Bookings",
|
* availability (Swagger)
|
||||||
creditCards = `${profile}/creditCards`,
|
* https://tstapi.scandichotels.com/availability/swagger/v1/index.html
|
||||||
city = "hotel/v1/Cities",
|
*/
|
||||||
citiesCountry = `${city}/country`,
|
export namespace Availability {
|
||||||
countries = "hotel/v1/Countries",
|
export function city(cityId: string) {
|
||||||
friendTransactions = "profile/v1/Transaction/friendTransactions",
|
return `${base.path.availability}/${version}/${base.enitity.Availabilities}/city/${cityId}`
|
||||||
hotels = "hotel/v1/Hotels",
|
}
|
||||||
initiateSaveCard = `${creditCards}/initiateSaveCard`,
|
export function hotel(hotelId: string) {
|
||||||
locations = "hotel/v1/Locations",
|
return `${base.path.availability}/${version}/${base.enitity.Availabilities}/hotel/${hotelId}`
|
||||||
previousStays = "booking/v1/Stays/past",
|
}
|
||||||
upcomingStays = "booking/v1/Stays/future",
|
}
|
||||||
rewards = `${profile}/reward`,
|
|
||||||
tierRewards = `${profile}/TierRewards`,
|
/**
|
||||||
subscriberId = `${profile}/SubscriberId`,
|
* booking (Swagger)
|
||||||
packages = "package/v1/packages/hotel",
|
* 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(
|
export async function get(
|
||||||
endpoint: Endpoint | `${Endpoint}/${string}`,
|
endpoint: Endpoint,
|
||||||
options: RequestOptionsWithOutBody,
|
options: RequestOptionsWithOutBody,
|
||||||
params = {}
|
params = {}
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -89,3 +89,9 @@ export const getLanguageSwitcher = cache(
|
|||||||
export const getSiteConfig = cache(async function getMemoizedSiteConfig() {
|
export const getSiteConfig = cache(async function getMemoizedSiteConfig() {
|
||||||
return serverClient().contentstack.base.siteConfig()
|
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 { webviews } from "@/constants/routes/webviews"
|
||||||
import { appRouter } from "@/server"
|
import { appRouter } from "@/server"
|
||||||
import { createContext } from "@/server/context"
|
import { createContext } from "@/server/context"
|
||||||
import { internalServerError } from "@/server/errors/next"
|
|
||||||
import { createCallerFactory } from "@/server/trpc"
|
import { createCallerFactory } from "@/server/trpc"
|
||||||
|
|
||||||
const createCaller = createCallerFactory(appRouter)
|
const createCaller = createCallerFactory(appRouter)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { cookies, headers } from "next/headers"
|
import { cookies, headers } from "next/headers"
|
||||||
import { type Session } from "next-auth"
|
import { type Session } from "next-auth"
|
||||||
|
import { cache } from "react"
|
||||||
|
|
||||||
import { Lang } from "@/constants/languages"
|
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
|
* This is the actual context you'll use in your router
|
||||||
* @link https://trpc.io/docs/context
|
* @link https://trpc.io/docs/context
|
||||||
**/
|
**/
|
||||||
export function createContext() {
|
export const createContext = cache(function () {
|
||||||
const h = headers()
|
const h = headers()
|
||||||
|
|
||||||
const cookie = cookies()
|
const cookie = cookies()
|
||||||
@@ -66,6 +67,6 @@ export function createContext() {
|
|||||||
webToken: webviewTokenCookie?.value,
|
webToken: webviewTokenCookie?.value,
|
||||||
contentType: h.get("x-contenttype")!,
|
contentType: h.get("x-contenttype")!,
|
||||||
})
|
})
|
||||||
}
|
})
|
||||||
|
|
||||||
export type Context = ReturnType<typeof createContext>
|
export type Context = ReturnType<typeof createContext>
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export const bookingMutationRouter = router({
|
|||||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiResponse = await api.post(api.endpoints.v1.booking, {
|
const apiResponse = await api.post(api.endpoints.v1.Booking.bookings, {
|
||||||
headers,
|
headers,
|
||||||
body: input,
|
body: input,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export const bookingQueryRouter = router({
|
|||||||
getBookingConfirmationCounter.add(1, { confirmationNumber })
|
getBookingConfirmationCounter.add(1, { confirmationNumber })
|
||||||
|
|
||||||
const apiResponse = await api.get(
|
const apiResponse = await api.get(
|
||||||
`${api.endpoints.v1.booking}/${confirmationNumber}`,
|
api.endpoints.v1.Booking.booking(confirmationNumber),
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||||
@@ -142,7 +142,7 @@ export const bookingQueryRouter = router({
|
|||||||
getBookingStatusCounter.add(1, { confirmationNumber })
|
getBookingStatusCounter.add(1, { confirmationNumber })
|
||||||
|
|
||||||
const apiResponse = await api.get(
|
const apiResponse = await api.get(
|
||||||
`${api.endpoints.v1.booking}/${confirmationNumber}/status`,
|
api.endpoints.v1.Booking.status(confirmationNumber),
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ function getUniqueRewardIds(rewardIds: string[]) {
|
|||||||
|
|
||||||
const getAllCachedApiRewards = unstable_cache(
|
const getAllCachedApiRewards = unstable_cache(
|
||||||
async function (token) {
|
async function (token) {
|
||||||
const apiResponse = await api.get(api.endpoints.v1.tierRewards, {
|
const apiResponse = await api.get(api.endpoints.v1.Profile.tierRewards, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
},
|
},
|
||||||
@@ -194,7 +194,7 @@ export const rewardQueryRouter = router({
|
|||||||
|
|
||||||
const { limit, cursor } = input
|
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
|
cache: undefined, // override defaultOptions
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||||
@@ -393,7 +393,7 @@ export const rewardQueryRouter = router({
|
|||||||
surprises: contentStackBaseWithProtectedProcedure.query(async ({ ctx }) => {
|
surprises: contentStackBaseWithProtectedProcedure.query(async ({ ctx }) => {
|
||||||
getCurrentRewardCounter.add(1)
|
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
|
cache: undefined, // override defaultOptions
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||||
|
|||||||
@@ -39,3 +39,7 @@ export const getlHotelDataInputSchema = z.object({
|
|||||||
.array(z.enum(["RoomCategories", "NearbyHotels", "Restaurants", "City"]))
|
.array(z.enum(["RoomCategories", "NearbyHotels", "Restaurants", "City"]))
|
||||||
.optional(),
|
.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 { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||||
import { AlertTypeEnum } from "@/types/enums/alert"
|
import { AlertTypeEnum } from "@/types/enums/alert"
|
||||||
|
import { CurrencyEnum } from "@/types/enums/currency"
|
||||||
import { FacilityEnum } from "@/types/enums/facilities"
|
import { FacilityEnum } from "@/types/enums/facilities"
|
||||||
|
import { PackageTypeEnum } from "@/types/enums/packages"
|
||||||
import { PointOfInterestCategoryNameEnum } from "@/types/hotel"
|
import { PointOfInterestCategoryNameEnum } from "@/types/hotel"
|
||||||
|
|
||||||
const ratingsSchema = z
|
const ratingsSchema = z
|
||||||
@@ -653,7 +655,7 @@ export const apiCountriesSchema = z.object({
|
|||||||
name: z.string(),
|
name: z.string(),
|
||||||
}),
|
}),
|
||||||
hotelInformationSystemId: z.number().optional(),
|
hotelInformationSystemId: z.number().optional(),
|
||||||
id: z.string().optional(),
|
id: z.string().optional().default(""),
|
||||||
language: z.string().optional(),
|
language: z.string().optional(),
|
||||||
type: z.literal("countries"),
|
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,
|
contentStackUidWithServiceProcedure,
|
||||||
publicProcedure,
|
publicProcedure,
|
||||||
router,
|
router,
|
||||||
|
safeProtectedServiceProcedure,
|
||||||
serviceProcedure,
|
serviceProcedure,
|
||||||
} from "@/server/trpc"
|
} from "@/server/trpc"
|
||||||
import { toApiLang } from "@/server/utils"
|
import { toApiLang } from "@/server/utils"
|
||||||
@@ -24,11 +25,13 @@ import {
|
|||||||
getHotelPageCounter,
|
getHotelPageCounter,
|
||||||
validateHotelPageRefs,
|
validateHotelPageRefs,
|
||||||
} from "../contentstack/hotelPage/utils"
|
} from "../contentstack/hotelPage/utils"
|
||||||
|
import { getVerifiedUser, parsedUser } from "../user/query"
|
||||||
import {
|
import {
|
||||||
getRoomPackagesInputSchema,
|
getRoomPackagesInputSchema,
|
||||||
getRoomPackagesSchema,
|
getRoomPackagesSchema,
|
||||||
} from "./schemas/packages"
|
} from "./schemas/packages"
|
||||||
import {
|
import {
|
||||||
|
getBreakfastPackageInput,
|
||||||
getHotelInputSchema,
|
getHotelInputSchema,
|
||||||
getHotelsAvailabilityInputSchema,
|
getHotelsAvailabilityInputSchema,
|
||||||
getlHotelDataInputSchema,
|
getlHotelDataInputSchema,
|
||||||
@@ -36,6 +39,7 @@ import {
|
|||||||
getRoomsAvailabilityInputSchema,
|
getRoomsAvailabilityInputSchema,
|
||||||
} from "./input"
|
} from "./input"
|
||||||
import {
|
import {
|
||||||
|
breakfastPackagesSchema,
|
||||||
getHotelDataSchema,
|
getHotelDataSchema,
|
||||||
getHotelsAvailabilitySchema,
|
getHotelsAvailabilitySchema,
|
||||||
getRatesSchema,
|
getRatesSchema,
|
||||||
@@ -51,6 +55,7 @@ import {
|
|||||||
|
|
||||||
import { FacilityCardTypeEnum } from "@/types/components/hotelPage/facilities"
|
import { FacilityCardTypeEnum } from "@/types/components/hotelPage/facilities"
|
||||||
import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel"
|
import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel"
|
||||||
|
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
|
||||||
import type { RequestOptionsWithOutBody } from "@/types/fetch"
|
import type { RequestOptionsWithOutBody } from "@/types/fetch"
|
||||||
import type { Facility } from "@/types/hotel"
|
import type { Facility } from "@/types/hotel"
|
||||||
import type { GetHotelPageData } from "@/types/trpc/routers/contentstack/hotelPage"
|
import type { GetHotelPageData } from "@/types/trpc/routers/contentstack/hotelPage"
|
||||||
@@ -88,6 +93,14 @@ const roomsAvailabilityFailCounter = meter.createCounter(
|
|||||||
"trpc.hotel.availability.rooms-fail"
|
"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) {
|
async function getContentstackData(lang: Lang, uid?: string | null) {
|
||||||
if (!uid) {
|
if (!uid) {
|
||||||
return null
|
return null
|
||||||
@@ -169,7 +182,7 @@ export const hotelQueryRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
const apiResponse = await api.get(
|
const apiResponse = await api.get(
|
||||||
`${api.endpoints.v1.hotels}/${hotelId}`,
|
api.endpoints.v1.Hotel.Hotels.hotel(hotelId),
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||||
@@ -320,7 +333,7 @@ export const hotelQueryRouter = router({
|
|||||||
JSON.stringify({ query: { cityId, params } })
|
JSON.stringify({ query: { cityId, params } })
|
||||||
)
|
)
|
||||||
const apiResponse = await api.get(
|
const apiResponse = await api.get(
|
||||||
`${api.endpoints.v1.hotelsAvailability}/${cityId}`,
|
api.endpoints.v1.Availability.city(cityId),
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||||
@@ -444,7 +457,7 @@ export const hotelQueryRouter = router({
|
|||||||
JSON.stringify({ query: { hotelId, params } })
|
JSON.stringify({ query: { hotelId, params } })
|
||||||
)
|
)
|
||||||
const apiResponse = await api.get(
|
const apiResponse = await api.get(
|
||||||
`${api.endpoints.v1.roomsAvailability}/${hotelId}`,
|
api.endpoints.v1.Availability.hotel(hotelId.toString()),
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||||
@@ -587,7 +600,7 @@ export const hotelQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
const apiResponse = await api.get(
|
const apiResponse = await api.get(
|
||||||
`${api.endpoints.v1.hotels}/${hotelId}`,
|
api.endpoints.v1.Hotel.Hotels.hotel(hotelId),
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||||
@@ -734,7 +747,7 @@ export const hotelQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
const apiResponse = await api.get(
|
const apiResponse = await api.get(
|
||||||
`${api.endpoints.v1.packages}/${hotelId}`,
|
api.endpoints.v1.Package.Packages.hotel(hotelId),
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||||
@@ -789,5 +802,114 @@ export const hotelQueryRouter = router({
|
|||||||
|
|
||||||
return validatedPackagesData.data
|
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(
|
return unstable_cache(
|
||||||
async function (searchParams) {
|
async function (searchParams) {
|
||||||
const countryResponse = await api.get(
|
const countryResponse = await api.get(
|
||||||
api.endpoints.v1.countries,
|
api.endpoints.v1.Hotel.countries,
|
||||||
options,
|
options,
|
||||||
searchParams
|
searchParams
|
||||||
)
|
)
|
||||||
@@ -136,7 +136,7 @@ export async function getCitiesByCountry(
|
|||||||
await Promise.all(
|
await Promise.all(
|
||||||
searchedCountries.data.map(async (country) => {
|
searchedCountries.data.map(async (country) => {
|
||||||
const countryResponse = await api.get(
|
const countryResponse = await api.get(
|
||||||
`${api.endpoints.v1.citiesCountry}/${country.name}`,
|
api.endpoints.v1.Hotel.Cities.country(country.name),
|
||||||
options,
|
options,
|
||||||
searchParams
|
searchParams
|
||||||
)
|
)
|
||||||
@@ -182,7 +182,7 @@ export async function getLocations(
|
|||||||
groupedCitiesByCountry: CitiesGroupedByCountry | null
|
groupedCitiesByCountry: CitiesGroupedByCountry | null
|
||||||
) {
|
) {
|
||||||
const apiResponse = await api.get(
|
const apiResponse = await api.get(
|
||||||
api.endpoints.v1.locations,
|
api.endpoints.v1.Hotel.locations,
|
||||||
options,
|
options,
|
||||||
searchParams
|
searchParams
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -35,16 +35,19 @@ export const userMutationRouter = router({
|
|||||||
"api.user.creditCard.add start",
|
"api.user.creditCard.add start",
|
||||||
JSON.stringify({ query: { language: input.language } })
|
JSON.stringify({ query: { language: input.language } })
|
||||||
)
|
)
|
||||||
const apiResponse = await api.post(api.endpoints.v1.initiateSaveCard, {
|
const apiResponse = await api.post(
|
||||||
headers: {
|
api.endpoints.v1.Profile.CreditCards.initiateSaveCard,
|
||||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
{
|
||||||
},
|
headers: {
|
||||||
body: {
|
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||||
language: input.language,
|
},
|
||||||
mobileToken: false,
|
body: {
|
||||||
redirectUrl: `api/web/add-card-callback/${input.language}`,
|
language: input.language,
|
||||||
},
|
mobileToken: false,
|
||||||
})
|
redirectUrl: `api/web/add-card-callback/${input.language}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
const text = await apiResponse.text()
|
||||||
@@ -85,7 +88,7 @@ export const userMutationRouter = router({
|
|||||||
.mutation(async function ({ ctx, input }) {
|
.mutation(async function ({ ctx, input }) {
|
||||||
console.info("api.user.creditCard.save start", JSON.stringify({}))
|
console.info("api.user.creditCard.save start", JSON.stringify({}))
|
||||||
const apiResponse = await api.post(
|
const apiResponse = await api.post(
|
||||||
`${api.endpoints.v1.creditCards}/${input.transactionId}`,
|
api.endpoints.v1.Profile.CreditCards.transaction(input.transactionId),
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||||
@@ -118,7 +121,9 @@ export const userMutationRouter = router({
|
|||||||
JSON.stringify({ query: {} })
|
JSON.stringify({ query: {} })
|
||||||
)
|
)
|
||||||
const apiResponse = await api.remove(
|
const apiResponse = await api.remove(
|
||||||
`${api.endpoints.v1.creditCards}/${input.creditCardId}`,
|
api.endpoints.v1.Profile.CreditCards.deleteCreditCard(
|
||||||
|
input.creditCardId
|
||||||
|
),
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||||
@@ -149,7 +154,7 @@ export const userMutationRouter = router({
|
|||||||
ctx,
|
ctx,
|
||||||
}) {
|
}) {
|
||||||
generatePreferencesLinkCounter.add(1)
|
generatePreferencesLinkCounter.add(1)
|
||||||
const apiResponse = await api.get(api.endpoints.v1.subscriberId, {
|
const apiResponse = await api.get(api.endpoints.v1.Profile.subscriberId, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export const getVerifiedUser = cache(
|
|||||||
}
|
}
|
||||||
getVerifiedUserCounter.add(1)
|
getVerifiedUserCounter.add(1)
|
||||||
console.info("api.user.profile getVerifiedUser start", JSON.stringify({}))
|
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: {
|
headers: {
|
||||||
Authorization: `Bearer ${session.token.access_token}`,
|
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 country = countries.find((c) => c.code === data.address.countryCode)
|
||||||
|
|
||||||
const user = {
|
const user = {
|
||||||
@@ -211,7 +211,7 @@ function parsedUser(data: User, isMFA: boolean) {
|
|||||||
async function getCreditCards(session: Session) {
|
async function getCreditCards(session: Session) {
|
||||||
getCreditCardsCounter.add(1)
|
getCreditCardsCounter.add(1)
|
||||||
console.info("api.profile.creditCards start", JSON.stringify({}))
|
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: {
|
headers: {
|
||||||
Authorization: `Bearer ${session.token.access_token}`,
|
Authorization: `Bearer ${session.token.access_token}`,
|
||||||
},
|
},
|
||||||
@@ -354,7 +354,7 @@ export const userQueryRouter = router({
|
|||||||
JSON.stringify({ query: { params } })
|
JSON.stringify({ query: { params } })
|
||||||
)
|
)
|
||||||
const previousStaysResponse = await api.get(
|
const previousStaysResponse = await api.get(
|
||||||
api.endpoints.v1.previousStays,
|
api.endpoints.v1.Booking.Stays.past,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||||
@@ -430,7 +430,7 @@ export const userQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
const apiResponse = await api.get(
|
const apiResponse = await api.get(
|
||||||
api.endpoints.v1.previousStays,
|
api.endpoints.v1.Booking.Stays.past,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||||
@@ -492,7 +492,7 @@ export const userQueryRouter = router({
|
|||||||
)
|
)
|
||||||
const nextCursor =
|
const nextCursor =
|
||||||
verifiedData.data.links &&
|
verifiedData.data.links &&
|
||||||
verifiedData.data.links.offset < verifiedData.data.links.totalCount
|
verifiedData.data.links.offset < verifiedData.data.links.totalCount
|
||||||
? verifiedData.data.links.offset
|
? verifiedData.data.links.offset
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
@@ -525,7 +525,7 @@ export const userQueryRouter = router({
|
|||||||
JSON.stringify({ query: { params } })
|
JSON.stringify({ query: { params } })
|
||||||
)
|
)
|
||||||
const apiResponse = await api.get(
|
const apiResponse = await api.get(
|
||||||
api.endpoints.v1.upcomingStays,
|
api.endpoints.v1.Booking.Stays.future,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||||
@@ -585,7 +585,7 @@ export const userQueryRouter = router({
|
|||||||
})
|
})
|
||||||
const nextCursor =
|
const nextCursor =
|
||||||
verifiedData.data.links &&
|
verifiedData.data.links &&
|
||||||
verifiedData.data.links.offset < verifiedData.data.links.totalCount
|
verifiedData.data.links.offset < verifiedData.data.links.totalCount
|
||||||
? verifiedData.data.links.offset
|
? verifiedData.data.links.offset
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
@@ -611,13 +611,16 @@ export const userQueryRouter = router({
|
|||||||
"api.transaction.friendTransactions start",
|
"api.transaction.friendTransactions start",
|
||||||
JSON.stringify({})
|
JSON.stringify({})
|
||||||
)
|
)
|
||||||
const apiResponse = await api.get(api.endpoints.v1.friendTransactions, {
|
const apiResponse = await api.get(
|
||||||
cache: undefined, // override defaultOptions
|
api.endpoints.v1.Profile.Transaction.friendTransactions,
|
||||||
headers: {
|
{
|
||||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
cache: undefined, // override defaultOptions
|
||||||
},
|
headers: {
|
||||||
next: { revalidate: 30 * 60 * 1000 },
|
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||||
})
|
},
|
||||||
|
next: { revalidate: 30 * 60 * 1000 },
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
// switch (apiResponse.status) {
|
// switch (apiResponse.status) {
|
||||||
@@ -740,7 +743,7 @@ export const userQueryRouter = router({
|
|||||||
membershipCards: protectedProcedure.query(async function ({ ctx }) {
|
membershipCards: protectedProcedure.query(async function ({ ctx }) {
|
||||||
getProfileCounter.add(1)
|
getProfileCounter.add(1)
|
||||||
console.info("api.profile start", JSON.stringify({}))
|
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",
|
cache: "no-store",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
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
|
// Temporary API call needed till we have user name in ctx session data
|
||||||
getProfileCounter.add(1)
|
getProfileCounter.add(1)
|
||||||
console.info("api.user.profile updatebookingurl start", JSON.stringify({}))
|
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",
|
cache: "no-store",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${token}`,
|
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
|
// in minor version bumps. Please read: https://trpc.io/docs/faq#unstable
|
||||||
export const contentStackUidWithServiceProcedure =
|
export const contentStackUidWithServiceProcedure =
|
||||||
contentstackExtendedProcedureUID.unstable_concat(serviceProcedure)
|
contentstackExtendedProcedureUID.unstable_concat(serviceProcedure)
|
||||||
@@ -186,3 +186,6 @@ export const contentStackBaseWithServiceProcedure =
|
|||||||
|
|
||||||
export const contentStackBaseWithProtectedProcedure =
|
export const contentStackBaseWithProtectedProcedure =
|
||||||
contentstackBaseProcedure.unstable_concat(protectedProcedure)
|
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 { create, useStore } from "zustand"
|
||||||
|
|
||||||
import { bedTypeSchema } from "@/components/HotelReservation/EnterDetails/BedType/schema"
|
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 { detailsSchema } from "@/components/HotelReservation/EnterDetails/Details/schema"
|
||||||
|
|
||||||
|
import { BreakfastPackage } from "@/types/components/enterDetails/breakfast"
|
||||||
import { DetailsSchema } from "@/types/components/enterDetails/details"
|
import { DetailsSchema } from "@/types/components/enterDetails/details"
|
||||||
import { SidePeekEnum } from "@/types/components/enterDetails/sidePeek"
|
import { SidePeekEnum } from "@/types/components/enterDetails/sidePeek"
|
||||||
import { StepEnum } from "@/types/components/enterDetails/step"
|
import { StepEnum } from "@/types/components/enterDetails/step"
|
||||||
import { bedTypeEnum } from "@/types/enums/bedType"
|
import { BedTypeEnum } from "@/types/enums/bedType"
|
||||||
import { breakfastEnum } from "@/types/enums/breakfast"
|
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
|
||||||
|
|
||||||
const SESSION_STORAGE_KEY = "enterDetails"
|
const SESSION_STORAGE_KEY = "enterDetails"
|
||||||
|
|
||||||
interface EnterDetailsState {
|
interface EnterDetailsState {
|
||||||
data: {
|
data: {
|
||||||
bedType: bedTypeEnum | undefined
|
bedType: BedTypeEnum | undefined
|
||||||
breakfast: breakfastEnum | undefined
|
breakfast: BreakfastPackage | BreakfastPackageEnum.NO_BREAKFAST | undefined
|
||||||
} & DetailsSchema
|
} & DetailsSchema
|
||||||
steps: StepEnum[]
|
steps: StepEnum[]
|
||||||
currentStep: StepEnum
|
currentStep: StepEnum
|
||||||
@@ -26,7 +27,7 @@ interface EnterDetailsState {
|
|||||||
completeStep: (updatedData: Partial<EnterDetailsState["data"]>) => void
|
completeStep: (updatedData: Partial<EnterDetailsState["data"]>) => void
|
||||||
navigate: (
|
navigate: (
|
||||||
step: StepEnum,
|
step: StepEnum,
|
||||||
updatedData?: Record<string, string | boolean>
|
updatedData?: Record<string, string | boolean | BreakfastPackage>
|
||||||
) => void
|
) => void
|
||||||
setCurrentStep: (step: StepEnum) => void
|
setCurrentStep: (step: StepEnum) => void
|
||||||
openSidePeek: (key: SidePeekEnum | null) => void
|
openSidePeek: (key: SidePeekEnum | null) => void
|
||||||
@@ -75,7 +76,7 @@ export function initEditDetailsState(currentStep: StepEnum) {
|
|||||||
initialData = { ...initialData, ...validatedBedType.data }
|
initialData = { ...initialData, ...validatedBedType.data }
|
||||||
isValid[StepEnum.selectBed] = true
|
isValid[StepEnum.selectBed] = true
|
||||||
}
|
}
|
||||||
const validatedBreakfast = breakfastSchema.safeParse(inputData)
|
const validatedBreakfast = breakfastStoreSchema.safeParse(inputData)
|
||||||
if (validatedBreakfast.success) {
|
if (validatedBreakfast.success) {
|
||||||
validPaths.push(StepEnum.details)
|
validPaths.push(StepEnum.details)
|
||||||
initialData = { ...initialData, ...validatedBreakfast.data }
|
initialData = { ...initialData, ...validatedBreakfast.data }
|
||||||
|
|||||||
@@ -1,5 +1,21 @@
|
|||||||
import { z } from "zod"
|
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",
|
KING = "KING",
|
||||||
QUEEN = "QUEEN",
|
QUEEN = "QUEEN",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export enum breakfastEnum {
|
export enum BreakfastPackageEnum {
|
||||||
BREAKFAST = "BREAKFAST",
|
FREE_MEMBER_BREAKFAST = "BRF0",
|
||||||
|
REGULAR_BREAKFAST = "BRF1",
|
||||||
NO_BREAKFAST = "NO_BREAKFAST",
|
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