feat: SW-1583 Implemented Reward nights on city search
This commit is contained in:
@@ -36,8 +36,9 @@ const hotelFacilitiesFilterNames = [
|
|||||||
export async function fetchAvailableHotels(
|
export async function fetchAvailableHotels(
|
||||||
input: AvailabilityInput
|
input: AvailabilityInput
|
||||||
): Promise<NullableHotelData[]> {
|
): Promise<NullableHotelData[]> {
|
||||||
const availableHotels =
|
const availableHotels = input.redemption
|
||||||
await serverClient().hotel.availability.hotelsByCity(input)
|
? await serverClient().hotel.availability.hotelsByCityWithRedemption(input)
|
||||||
|
: await serverClient().hotel.availability.hotelsByCity(input)
|
||||||
|
|
||||||
if (!availableHotels) return []
|
if (!availableHotels) return []
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ interface HotelSearchDetails<T> {
|
|||||||
childrenInRoomString?: string
|
childrenInRoomString?: string
|
||||||
childrenInRoom?: Child[]
|
childrenInRoom?: Child[]
|
||||||
bookingCode?: string
|
bookingCode?: string
|
||||||
|
redemption?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getHotelSearchDetails<
|
export async function getHotelSearchDetails<
|
||||||
@@ -105,5 +106,6 @@ export async function getHotelSearchDetails<
|
|||||||
childrenInRoomString,
|
childrenInRoomString,
|
||||||
childrenInRoom,
|
childrenInRoom,
|
||||||
bookingCode: selectHotelParams.bookingCode ?? undefined,
|
bookingCode: selectHotelParams.bookingCode ?? undefined,
|
||||||
|
redemption: selectHotelParams.searchType === "redemption",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ export async function GET(
|
|||||||
/** Record<string, any> is next-auth typings */
|
/** Record<string, any> is next-auth typings */
|
||||||
const params: Record<string, any> = {
|
const params: Record<string, any> = {
|
||||||
ui_locales: context.params.lang,
|
ui_locales: context.params.lang,
|
||||||
scope: ["openid", "profile", "booking", "profile_link"],
|
scope: ["openid", "profile", "booking", "profile_link", "availability"],
|
||||||
/**
|
/**
|
||||||
* The `acr_values` param is used to make Curity display the proper login
|
* The `acr_values` param is used to make Curity display the proper login
|
||||||
* page for Scandic. Without the parameter Curity presents some choices
|
* page for Scandic. Without the parameter Curity presents some choices
|
||||||
|
|||||||
@@ -65,7 +65,13 @@ export async function GET(
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
ui_locales: context.params.lang,
|
ui_locales: context.params.lang,
|
||||||
scope: ["openid", "profile"].join(" "),
|
scope: [
|
||||||
|
"openid",
|
||||||
|
"profile",
|
||||||
|
"booking",
|
||||||
|
"availability",
|
||||||
|
"profile_link",
|
||||||
|
].join(" "),
|
||||||
loginKey: loginKey,
|
loginKey: loginKey,
|
||||||
for_origin: publicURL,
|
for_origin: publicURL,
|
||||||
acr_values: "urn:com:scandichotels:scandic-email",
|
acr_values: "urn:com:scandichotels:scandic-email",
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
.poinstRow {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--Spacing-x1);
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||||
|
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||||
|
|
||||||
|
import styles from "./hotelPointsCard.module.css"
|
||||||
|
|
||||||
|
import type { PointsCardProps } from "@/types/components/hotelReservation/selectHotel/priceCardProps"
|
||||||
|
|
||||||
|
export default function HotelPointsCard({
|
||||||
|
productTypePoints,
|
||||||
|
}: PointsCardProps) {
|
||||||
|
const intl = useIntl()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.poinstRow}>
|
||||||
|
<Subtitle type="two" color="uiTextHighContrast">
|
||||||
|
{productTypePoints.localPrice.pointsPerStay}
|
||||||
|
</Subtitle>
|
||||||
|
<Caption color="uiTextHighContrast">
|
||||||
|
{intl.formatMessage({ id: "Points" })}
|
||||||
|
</Caption>
|
||||||
|
{productTypePoints.localPrice.pricePerStay ? (
|
||||||
|
<>
|
||||||
|
+
|
||||||
|
<Subtitle type="two" color="uiTextHighContrast">
|
||||||
|
{productTypePoints.localPrice.pricePerStay}
|
||||||
|
</Subtitle>
|
||||||
|
<Caption color="uiTextHighContrast">
|
||||||
|
{productTypePoints.localPrice.currency}
|
||||||
|
</Caption>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -96,6 +96,12 @@
|
|||||||
text-decoration: line-through;
|
text-decoration: line-through;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pointsCard {
|
||||||
|
background-color: var(--Base-Surface-Secondary-light-Normal);
|
||||||
|
padding: var(--Spacing-x-one-and-half);
|
||||||
|
border-radius: var(--Corner-radius-Medium);
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 768px) and (max-width: 1024px) {
|
@media screen and (min-width: 768px) and (max-width: 1024px) {
|
||||||
.imageContainer {
|
.imageContainer {
|
||||||
height: 180px;
|
height: 180px;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { getSingleDecimal } from "@/utils/numberFormatting"
|
|||||||
|
|
||||||
import ReadMore from "../ReadMore"
|
import ReadMore from "../ReadMore"
|
||||||
import TripAdvisorChip from "../TripAdvisorChip"
|
import TripAdvisorChip from "../TripAdvisorChip"
|
||||||
|
import HotelPointsCard from "./HotelPointsCard"
|
||||||
import HotelPriceCard from "./HotelPriceCard"
|
import HotelPriceCard from "./HotelPriceCard"
|
||||||
import NoPriceAvailableCard from "./NoPriceAvailableCard"
|
import NoPriceAvailableCard from "./NoPriceAvailableCard"
|
||||||
import { hotelCardVariants } from "./variants"
|
import { hotelCardVariants } from "./variants"
|
||||||
@@ -172,7 +173,9 @@ function HotelCard({
|
|||||||
{bookingCode}
|
{bookingCode}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{(!isUserLoggedIn || (bookingCode && !fullPrice)) &&
|
{(!isUserLoggedIn ||
|
||||||
|
!price.member ||
|
||||||
|
(bookingCode && !fullPrice)) &&
|
||||||
price.public && (
|
price.public && (
|
||||||
<HotelPriceCard productTypePrices={price.public} />
|
<HotelPriceCard productTypePrices={price.public} />
|
||||||
)}
|
)}
|
||||||
@@ -182,6 +185,20 @@ function HotelCard({
|
|||||||
isMemberPrice
|
isMemberPrice
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{price.redemption && (
|
||||||
|
<div className={styles.pointsCard}>
|
||||||
|
<Caption>
|
||||||
|
{intl.formatMessage({ id: "Available rates" })}
|
||||||
|
</Caption>
|
||||||
|
<HotelPointsCard productTypePoints={price.redemption} />
|
||||||
|
{price.redemptionA && (
|
||||||
|
<HotelPointsCard productTypePoints={price.redemptionA} />
|
||||||
|
)}
|
||||||
|
{price.redemptionB && (
|
||||||
|
<HotelPointsCard productTypePoints={price.redemptionB} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
asChild
|
asChild
|
||||||
theme="base"
|
theme="base"
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ export default async function SelectHotel({
|
|||||||
childrenInRoom,
|
childrenInRoom,
|
||||||
hotel: isAlternativeFor,
|
hotel: isAlternativeFor,
|
||||||
bookingCode,
|
bookingCode,
|
||||||
|
redemption,
|
||||||
} = searchDetails
|
} = searchDetails
|
||||||
|
|
||||||
if (!city) return notFound()
|
if (!city) return notFound()
|
||||||
@@ -89,6 +90,7 @@ export default async function SelectHotel({
|
|||||||
adults: adultsInRoom[0],
|
adults: adultsInRoom[0],
|
||||||
children: childrenInRoomString,
|
children: childrenInRoomString,
|
||||||
bookingCode,
|
bookingCode,
|
||||||
|
redemption,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
: bookingCode
|
: bookingCode
|
||||||
@@ -109,6 +111,7 @@ export default async function SelectHotel({
|
|||||||
roomStayEndDate: selectHotelParams.toDate,
|
roomStayEndDate: selectHotelParams.toDate,
|
||||||
adults: adultsInRoom[0],
|
adults: adultsInRoom[0],
|
||||||
children: childrenInRoomString,
|
children: childrenInRoomString,
|
||||||
|
redemption,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
"At latest": "Senest",
|
"At latest": "Senest",
|
||||||
"At the hotel": "På hotellet",
|
"At the hotel": "På hotellet",
|
||||||
"Attractions": "Attraktioner",
|
"Attractions": "Attraktioner",
|
||||||
|
"Available rates": "Tilgængelige priser",
|
||||||
"Average price per night": "gennemsnitspris pr. nat",
|
"Average price per night": "gennemsnitspris pr. nat",
|
||||||
"Away from elevator": "Væk fra elevator",
|
"Away from elevator": "Væk fra elevator",
|
||||||
"Back": "Tilbage",
|
"Back": "Tilbage",
|
||||||
|
|||||||
@@ -64,6 +64,7 @@
|
|||||||
"At the hotel": "Im Hotel",
|
"At the hotel": "Im Hotel",
|
||||||
"Attraction": "Attraktion",
|
"Attraction": "Attraktion",
|
||||||
"Attractions": "Attractions",
|
"Attractions": "Attractions",
|
||||||
|
"Available rates": "Verfügbare Preise",
|
||||||
"Average price per night": "Durchschnittspreis pro Nacht",
|
"Average price per night": "Durchschnittspreis pro Nacht",
|
||||||
"Away from elevator": "Weg vom Aufzug",
|
"Away from elevator": "Weg vom Aufzug",
|
||||||
"Back": "Zurück",
|
"Back": "Zurück",
|
||||||
|
|||||||
@@ -62,6 +62,7 @@
|
|||||||
"At latest": "At latest",
|
"At latest": "At latest",
|
||||||
"At the hotel": "At the hotel",
|
"At the hotel": "At the hotel",
|
||||||
"Attractions": "Attractions",
|
"Attractions": "Attractions",
|
||||||
|
"Available rates": "Available rates",
|
||||||
"Average price per night": "Average price per night",
|
"Average price per night": "Average price per night",
|
||||||
"Away from elevator": "Away from elevator",
|
"Away from elevator": "Away from elevator",
|
||||||
"Back": "Back",
|
"Back": "Back",
|
||||||
|
|||||||
@@ -62,6 +62,7 @@
|
|||||||
"At latest": "Viimeistään",
|
"At latest": "Viimeistään",
|
||||||
"At the hotel": "Hotellissa",
|
"At the hotel": "Hotellissa",
|
||||||
"Attractions": "Nähtävyydet",
|
"Attractions": "Nähtävyydet",
|
||||||
|
"Available rates": "Saatavilla olevat hinnat",
|
||||||
"Average price per night": "keskihinta per yö",
|
"Average price per night": "keskihinta per yö",
|
||||||
"Away from elevator": "Kaukana hissistä",
|
"Away from elevator": "Kaukana hissistä",
|
||||||
"Back": "Takaisin",
|
"Back": "Takaisin",
|
||||||
|
|||||||
@@ -62,6 +62,7 @@
|
|||||||
"At latest": "Senest",
|
"At latest": "Senest",
|
||||||
"At the hotel": "På hotellet",
|
"At the hotel": "På hotellet",
|
||||||
"Attractions": "Attraksjoner",
|
"Attractions": "Attraksjoner",
|
||||||
|
"Available rates": "Tilgjengelige priser",
|
||||||
"Average price per night": "gjennomsnittlig pris per natt",
|
"Average price per night": "gjennomsnittlig pris per natt",
|
||||||
"Away from elevator": "Bort fra heisen",
|
"Away from elevator": "Bort fra heisen",
|
||||||
"Back": "Tilbake",
|
"Back": "Tilbake",
|
||||||
|
|||||||
@@ -62,6 +62,7 @@
|
|||||||
"At latest": "Senast",
|
"At latest": "Senast",
|
||||||
"At the hotel": "På hotellet",
|
"At the hotel": "På hotellet",
|
||||||
"Attractions": "Sevärdheter",
|
"Attractions": "Sevärdheter",
|
||||||
|
"Available rates": "Tillgängliga priser",
|
||||||
"Average price per night": "Snittpris per natt",
|
"Average price per night": "Snittpris per natt",
|
||||||
"Away from elevator": "Bort från hissen",
|
"Away from elevator": "Bort från hissen",
|
||||||
"Back": "Tillbaka",
|
"Back": "Tillbaka",
|
||||||
|
|||||||
@@ -5,8 +5,34 @@ import { getDefaultRequestHeaders } from "./utils"
|
|||||||
import type { NextMiddleware } from "next/server"
|
import type { NextMiddleware } from "next/server"
|
||||||
|
|
||||||
import type { MiddlewareMatcher } from "@/types/middleware"
|
import type { MiddlewareMatcher } from "@/types/middleware"
|
||||||
|
import { auth } from "@/auth"
|
||||||
|
import { getPublicNextURL } from "@/server/utils"
|
||||||
|
import { login } from "@/constants/routes/handleAuth"
|
||||||
|
import { findLang } from "@/utils/languages"
|
||||||
|
|
||||||
export const middleware: NextMiddleware = async (request) => {
|
export const middleware: NextMiddleware = async (request) => {
|
||||||
|
// Redirect user to login if reward nights search and not logged in
|
||||||
|
const isRedemption =
|
||||||
|
request.nextUrl.searchParams.get("searchtype") === "redemption" ||
|
||||||
|
request.nextUrl.searchParams.get("searchType") === "redemption"
|
||||||
|
const session = await auth() // Check for user session
|
||||||
|
if (isRedemption && (!session || session?.error)) {
|
||||||
|
const lang = findLang(request.nextUrl.pathname)!
|
||||||
|
const nextUrlPublic = getPublicNextURL(request)
|
||||||
|
const headers = new Headers()
|
||||||
|
headers.append(
|
||||||
|
"set-cookie",
|
||||||
|
`redirectTo=${encodeURIComponent(nextUrlPublic.href)}; Path=/; HttpOnly; SameSite=Lax`
|
||||||
|
)
|
||||||
|
|
||||||
|
const loginUrl = login[lang]
|
||||||
|
const redirectUrl = new URL(loginUrl, nextUrlPublic)
|
||||||
|
const redirectOpts = {
|
||||||
|
headers,
|
||||||
|
}
|
||||||
|
return NextResponse.redirect(redirectUrl, redirectOpts)
|
||||||
|
}
|
||||||
|
|
||||||
const headers = getDefaultRequestHeaders(request)
|
const headers = getDefaultRequestHeaders(request)
|
||||||
return NextResponse.next({
|
return NextResponse.next({
|
||||||
request: {
|
request: {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export const hotelsAvailabilityInputSchema = z.object({
|
|||||||
adults: z.number(),
|
adults: z.number(),
|
||||||
children: z.string().optional(),
|
children: z.string().optional(),
|
||||||
bookingCode: z.string().optional().default(""),
|
bookingCode: z.string().optional().default(""),
|
||||||
|
redemption: z.boolean().optional().default(false),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const getHotelsByHotelIdsAvailabilityInputSchema = z.object({
|
export const getHotelsByHotelIdsAvailabilityInputSchema = z.object({
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { dt } from "@/lib/dt"
|
|||||||
import { badRequestError } from "@/server/errors/trpc"
|
import { badRequestError } from "@/server/errors/trpc"
|
||||||
import {
|
import {
|
||||||
contentStackBaseWithServiceProcedure,
|
contentStackBaseWithServiceProcedure,
|
||||||
|
protectedProcedure,
|
||||||
publicProcedure,
|
publicProcedure,
|
||||||
router,
|
router,
|
||||||
safeProtectedServiceProcedure,
|
safeProtectedServiceProcedure,
|
||||||
@@ -215,7 +216,7 @@ export const getHotel = cache(
|
|||||||
export const getHotelsAvailabilityByCity = async (
|
export const getHotelsAvailabilityByCity = async (
|
||||||
input: HotelsAvailabilityInputSchema,
|
input: HotelsAvailabilityInputSchema,
|
||||||
apiLang: string,
|
apiLang: string,
|
||||||
serviceToken: string
|
token: string // Either service token or user access token in case of redemption search
|
||||||
) => {
|
) => {
|
||||||
const {
|
const {
|
||||||
cityId,
|
cityId,
|
||||||
@@ -224,6 +225,7 @@ export const getHotelsAvailabilityByCity = async (
|
|||||||
adults,
|
adults,
|
||||||
children,
|
children,
|
||||||
bookingCode,
|
bookingCode,
|
||||||
|
redemption,
|
||||||
} = input
|
} = input
|
||||||
|
|
||||||
const params: Record<string, string | number> = {
|
const params: Record<string, string | number> = {
|
||||||
@@ -232,6 +234,7 @@ export const getHotelsAvailabilityByCity = async (
|
|||||||
adults,
|
adults,
|
||||||
...(children && { children }),
|
...(children && { children }),
|
||||||
...(bookingCode && { bookingCode }),
|
...(bookingCode && { bookingCode }),
|
||||||
|
...(redemption ? { isRedemption: "true" } : {}),
|
||||||
language: apiLang,
|
language: apiLang,
|
||||||
}
|
}
|
||||||
metrics.hotelsAvailability.counter.add(1, {
|
metrics.hotelsAvailability.counter.add(1, {
|
||||||
@@ -241,6 +244,7 @@ export const getHotelsAvailabilityByCity = async (
|
|||||||
adults,
|
adults,
|
||||||
children,
|
children,
|
||||||
bookingCode,
|
bookingCode,
|
||||||
|
redemption,
|
||||||
})
|
})
|
||||||
console.info(
|
console.info(
|
||||||
"api.hotels.hotelsAvailability start",
|
"api.hotels.hotelsAvailability start",
|
||||||
@@ -251,7 +255,7 @@ export const getHotelsAvailabilityByCity = async (
|
|||||||
{
|
{
|
||||||
cache: undefined,
|
cache: undefined,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${serviceToken}`,
|
Authorization: `Bearer ${token}`,
|
||||||
},
|
},
|
||||||
next: {
|
next: {
|
||||||
revalidate: env.CACHE_TIME_CITY_SEARCH,
|
revalidate: env.CACHE_TIME_CITY_SEARCH,
|
||||||
@@ -268,6 +272,7 @@ export const getHotelsAvailabilityByCity = async (
|
|||||||
adults,
|
adults,
|
||||||
children,
|
children,
|
||||||
bookingCode,
|
bookingCode,
|
||||||
|
redemption,
|
||||||
error_type: "http_error",
|
error_type: "http_error",
|
||||||
error: JSON.stringify({
|
error: JSON.stringify({
|
||||||
status: apiResponse.status,
|
status: apiResponse.status,
|
||||||
@@ -298,6 +303,7 @@ export const getHotelsAvailabilityByCity = async (
|
|||||||
adults,
|
adults,
|
||||||
children,
|
children,
|
||||||
bookingCode,
|
bookingCode,
|
||||||
|
redemption,
|
||||||
error_type: "validation_error",
|
error_type: "validation_error",
|
||||||
error: JSON.stringify(validateAvailabilityData.error),
|
error: JSON.stringify(validateAvailabilityData.error),
|
||||||
})
|
})
|
||||||
@@ -317,6 +323,7 @@ export const getHotelsAvailabilityByCity = async (
|
|||||||
adults,
|
adults,
|
||||||
children,
|
children,
|
||||||
bookingCode,
|
bookingCode,
|
||||||
|
redemption,
|
||||||
})
|
})
|
||||||
console.info(
|
console.info(
|
||||||
"api.hotels.hotelsAvailability success",
|
"api.hotels.hotelsAvailability success",
|
||||||
@@ -466,6 +473,17 @@ export const hotelQueryRouter = router({
|
|||||||
const apiLang = toApiLang(lang)
|
const apiLang = toApiLang(lang)
|
||||||
return getHotelsAvailabilityByCity(input, apiLang, ctx.serviceToken)
|
return getHotelsAvailabilityByCity(input, apiLang, ctx.serviceToken)
|
||||||
}),
|
}),
|
||||||
|
hotelsByCityWithRedemption: protectedProcedure
|
||||||
|
.input(hotelsAvailabilityInputSchema)
|
||||||
|
.query(async ({ input, ctx }) => {
|
||||||
|
const { lang } = ctx
|
||||||
|
const apiLang = toApiLang(lang)
|
||||||
|
return getHotelsAvailabilityByCity(
|
||||||
|
input,
|
||||||
|
apiLang,
|
||||||
|
ctx.session.token.access_token
|
||||||
|
)
|
||||||
|
}),
|
||||||
hotelsByHotelIds: serviceProcedure
|
hotelsByHotelIds: serviceProcedure
|
||||||
.input(getHotelsByHotelIdsAvailabilityInputSchema)
|
.input(getHotelsByHotelIdsAvailabilityInputSchema)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
import { productTypePriceSchema } from "../productTypePrice"
|
import {
|
||||||
|
productTypePriceSchema,
|
||||||
|
productTypePointsSchema,
|
||||||
|
} from "../productTypePrice"
|
||||||
|
|
||||||
export const productTypeSchema = z
|
export const productTypeSchema = z
|
||||||
.object({
|
.object({
|
||||||
public: productTypePriceSchema.optional(),
|
public: productTypePriceSchema.optional(),
|
||||||
member: productTypePriceSchema.optional(),
|
member: productTypePriceSchema.optional(),
|
||||||
|
redemption: productTypePointsSchema.optional(),
|
||||||
|
redemptionA: productTypePointsSchema.optional(),
|
||||||
|
redemptionB: productTypePointsSchema.optional(),
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
|
|||||||
@@ -10,9 +10,24 @@ export const priceSchema = z.object({
|
|||||||
regularPricePerStay: z.coerce.number().optional(),
|
regularPricePerStay: z.coerce.number().optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const productTypePriceSchema = z.object({
|
export const pointsSchema = z.object({
|
||||||
localPrice: priceSchema,
|
currency: z.nativeEnum(CurrencyEnum).optional(),
|
||||||
|
pricePerNight: z.coerce.number().optional(),
|
||||||
|
pricePerStay: z.coerce.number().optional(),
|
||||||
|
pointsPerNight: z.number(),
|
||||||
|
pointsPerStay: z.number(),
|
||||||
|
})
|
||||||
|
|
||||||
|
const partialPriceSchema = z.object({
|
||||||
rateCode: z.string(),
|
rateCode: z.string(),
|
||||||
rateType: z.string().optional(),
|
rateType: z.string().optional(),
|
||||||
requestedPrice: priceSchema.optional(),
|
requestedPrice: priceSchema.optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const productTypePriceSchema = partialPriceSchema.extend({
|
||||||
|
localPrice: priceSchema,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const productTypePointsSchema = partialPriceSchema.extend({
|
||||||
|
localPrice: pointsSchema,
|
||||||
|
})
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export type AvailabilityInput = {
|
|||||||
adults: number
|
adults: number
|
||||||
children?: string
|
children?: string
|
||||||
bookingCode?: string
|
bookingCode?: string
|
||||||
|
redemption?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AlternativeHotelsAvailabilityInput = {
|
export type AlternativeHotelsAvailabilityInput = {
|
||||||
@@ -13,4 +14,5 @@ export type AlternativeHotelsAvailabilityInput = {
|
|||||||
adults: number
|
adults: number
|
||||||
children?: string
|
children?: string
|
||||||
bookingCode?: string
|
bookingCode?: string
|
||||||
|
redemption?: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
import type { ProductTypePrices } from "@/types/trpc/routers/hotel/availability"
|
import type {
|
||||||
|
ProductTypePoints,
|
||||||
|
ProductTypePrices,
|
||||||
|
} from "@/types/trpc/routers/hotel/availability"
|
||||||
|
|
||||||
export type PriceCardProps = {
|
export type PriceCardProps = {
|
||||||
productTypePrices: ProductTypePrices
|
productTypePrices: ProductTypePrices
|
||||||
isMemberPrice?: boolean
|
isMemberPrice?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type PointsCardProps = {
|
||||||
|
productTypePoints: ProductTypePoints
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export interface SelectHotelSearchParams {
|
|||||||
toDate: string
|
toDate: string
|
||||||
rooms: Pick<Room, "adults" | "childrenInRoom">[]
|
rooms: Pick<Room, "adults" | "childrenInRoom">[]
|
||||||
bookingCode: string
|
bookingCode: string
|
||||||
|
searchType?: "redemption"
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AlternativeHotelsSearchParams {
|
export interface AlternativeHotelsSearchParams {
|
||||||
@@ -14,4 +15,5 @@ export interface AlternativeHotelsSearchParams {
|
|||||||
toDate: string
|
toDate: string
|
||||||
rooms: Pick<Room, "adults" | "childrenInRoom">[]
|
rooms: Pick<Room, "adults" | "childrenInRoom">[]
|
||||||
bookingCode: string
|
bookingCode: string
|
||||||
|
searchType?: "redemption"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export interface SelectRateSearchParams {
|
|||||||
hotelId: string
|
hotelId: string
|
||||||
rooms: Room[]
|
rooms: Room[]
|
||||||
toDate: string
|
toDate: string
|
||||||
|
searchType?: "redemption"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Rate = {
|
export type Rate = {
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ import type {
|
|||||||
} from "@/server/routers/hotels/input"
|
} from "@/server/routers/hotels/input"
|
||||||
import type { hotelsAvailabilitySchema } from "@/server/routers/hotels/output"
|
import type { hotelsAvailabilitySchema } from "@/server/routers/hotels/output"
|
||||||
import type { productTypeSchema } from "@/server/routers/hotels/schemas/availability/productType"
|
import type { productTypeSchema } from "@/server/routers/hotels/schemas/availability/productType"
|
||||||
import type { productTypePriceSchema } from "@/server/routers/hotels/schemas/productTypePrice"
|
import type {
|
||||||
|
productTypePointsSchema,
|
||||||
|
productTypePriceSchema,
|
||||||
|
} from "@/server/routers/hotels/schemas/productTypePrice"
|
||||||
|
|
||||||
export type HotelsAvailability = z.output<typeof hotelsAvailabilitySchema>
|
export type HotelsAvailability = z.output<typeof hotelsAvailabilitySchema>
|
||||||
export type HotelsAvailabilityInputSchema = z.output<
|
export type HotelsAvailabilityInputSchema = z.output<
|
||||||
@@ -17,6 +20,7 @@ export type HotelsByHotelIdsAvailabilityInputSchema = z.output<
|
|||||||
>
|
>
|
||||||
export type ProductType = z.output<typeof productTypeSchema>
|
export type ProductType = z.output<typeof productTypeSchema>
|
||||||
export type ProductTypePrices = z.output<typeof productTypePriceSchema>
|
export type ProductTypePrices = z.output<typeof productTypePriceSchema>
|
||||||
|
export type ProductTypePoints = z.output<typeof productTypePointsSchema>
|
||||||
|
|
||||||
export type HotelsAvailabilityItem =
|
export type HotelsAvailabilityItem =
|
||||||
HotelsAvailability["data"][number]["attributes"]
|
HotelsAvailability["data"][number]["attributes"]
|
||||||
|
|||||||
Reference in New Issue
Block a user