fix: booking confirmation validation
This commit is contained in:
@@ -45,8 +45,8 @@ export default async function BookingConfirmationPage({
|
||||
}
|
||||
)
|
||||
|
||||
const fromDate = dt(booking.temp.fromDate).locale(params.lang)
|
||||
const toDate = dt(booking.temp.toDate).locale(params.lang)
|
||||
const fromDate = dt(booking.checkInDate).locale(params.lang)
|
||||
const toDate = dt(booking.checkOutDate).locale(params.lang)
|
||||
const nights = intl.formatMessage(
|
||||
{ id: "booking.nights" },
|
||||
{
|
||||
@@ -77,7 +77,7 @@ export default async function BookingConfirmationPage({
|
||||
textTransform="regular"
|
||||
type="h1"
|
||||
>
|
||||
{booking.hotel.name}
|
||||
{booking.hotel?.data.attributes.name}
|
||||
</Title>
|
||||
</hgroup>
|
||||
<Body className={styles.body} textAlign="center">
|
||||
@@ -91,7 +91,7 @@ export default async function BookingConfirmationPage({
|
||||
<Subtitle color="burgundy" type="two">
|
||||
{intl.formatMessage(
|
||||
{ id: "Reference #{bookingNr}" },
|
||||
{ bookingNr: "A92320VV" }
|
||||
{ bookingNr: booking.confirmationNumber }
|
||||
)}
|
||||
</Subtitle>
|
||||
</header>
|
||||
@@ -183,11 +183,13 @@ export default async function BookingConfirmationPage({
|
||||
</Caption>
|
||||
<div>
|
||||
<Body color="burgundy" textTransform="bold">
|
||||
{booking.hotel.name}
|
||||
{booking.hotel?.data.attributes.name}
|
||||
</Body>
|
||||
<Body color="uiTextHighContrast">{booking.hotel.email}</Body>
|
||||
<Body color="uiTextHighContrast">
|
||||
{booking.hotel.phoneNumber}
|
||||
{booking.hotel?.data.attributes.contactInformation.email}
|
||||
</Body>
|
||||
<Body color="uiTextHighContrast">
|
||||
{booking.hotel?.data.attributes.contactInformation.phoneNumber}
|
||||
</Body>
|
||||
</div>
|
||||
</div>
|
||||
@@ -219,7 +221,16 @@ export default async function BookingConfirmationPage({
|
||||
<Body color="burgundy" textTransform="bold">
|
||||
{intl.formatMessage({ id: "Total cost" })}
|
||||
</Body>
|
||||
<Body color="uiTextHighContrast">{booking.temp.total}</Body>
|
||||
<Body color="uiTextHighContrast">
|
||||
{" "}
|
||||
{intl.formatMessage(
|
||||
{ id: "{amount} {currency}" },
|
||||
{
|
||||
amount: intl.formatNumber(booking.totalPrice),
|
||||
currency: booking.currencyCode,
|
||||
}
|
||||
)}
|
||||
</Body>
|
||||
<Caption color="uiTextPlaceholder">
|
||||
{`${intl.formatMessage({ id: "Approx." })} ${booking.temp.totalInEuro}`}
|
||||
</Caption>
|
||||
|
||||
@@ -44,14 +44,18 @@ const childrenAgesSchema = z.object({
|
||||
const guestSchema = z.object({
|
||||
firstName: z.string(),
|
||||
lastName: z.string(),
|
||||
email: z.string().nullable(),
|
||||
phoneNumber: z.string().nullable(),
|
||||
})
|
||||
|
||||
const packagesSchema = z.object({
|
||||
accessibility: z.boolean(),
|
||||
allergyFriendly: z.boolean(),
|
||||
breakfast: z.boolean(),
|
||||
petFriendly: z.boolean(),
|
||||
})
|
||||
const packagesSchema = z.array(
|
||||
z.object({
|
||||
accessibility: z.boolean().optional(),
|
||||
allergyFriendly: z.boolean().optional(),
|
||||
breakfast: z.boolean().optional(),
|
||||
petFriendly: z.boolean().optional(),
|
||||
})
|
||||
)
|
||||
|
||||
export const bookingConfirmationSchema = z
|
||||
.object({
|
||||
@@ -66,7 +70,7 @@ export const bookingConfirmationSchema = z
|
||||
confirmationNumber: z.string(),
|
||||
currencyCode: z.string(),
|
||||
guest: guestSchema,
|
||||
hasPayRouting: z.boolean(),
|
||||
hasPayRouting: z.boolean().optional(),
|
||||
hotelId: z.string(),
|
||||
packages: packagesSchema,
|
||||
rateCode: z.string(),
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as api from "@/lib/api"
|
||||
import { badRequestError, serverErrorByStatus } from "@/server/errors/trpc"
|
||||
import { router, serviceProcedure } from "@/server/trpc"
|
||||
|
||||
import { getHotelData } from "../hotels/query"
|
||||
import { bookingConfirmationInput, getBookingStatusInput } from "./input"
|
||||
import { bookingConfirmationSchema, createBookingSchema } from "./output"
|
||||
|
||||
@@ -81,6 +82,11 @@ export const bookingQueryRouter = router({
|
||||
throw badRequestError()
|
||||
}
|
||||
|
||||
const hotelData = await getHotelData(
|
||||
{ hotelId: booking.data.hotelId, language: ctx.lang },
|
||||
ctx.serviceToken
|
||||
)
|
||||
|
||||
getBookingConfirmationSuccessCounter.add(1, { confirmationNumber })
|
||||
console.info(
|
||||
"api.booking.confirmation success",
|
||||
@@ -91,6 +97,7 @@ export const bookingQueryRouter = router({
|
||||
|
||||
return {
|
||||
...booking.data,
|
||||
hotel: hotelData,
|
||||
temp: {
|
||||
breakfastFrom: "06:30",
|
||||
breakfastTo: "11:00",
|
||||
@@ -127,11 +134,6 @@ export const bookingQueryRouter = router({
|
||||
memberbershipNumber: "19822",
|
||||
phoneNumber: "+46702446688",
|
||||
},
|
||||
hotel: {
|
||||
email: "bookings@scandichotels.com",
|
||||
name: "Downtown Camper by Scandic",
|
||||
phoneNumber: "+4689001350",
|
||||
},
|
||||
}
|
||||
}),
|
||||
status: serviceProcedure.input(getBookingStatusInput).query(async function ({
|
||||
|
||||
@@ -68,6 +68,8 @@ export const getHotelDataInputSchema = z.object({
|
||||
include: z.array(z.nativeEnum(HotelIncludeEnum)).optional(),
|
||||
})
|
||||
|
||||
export type HotelDataInput = z.input<typeof getHotelDataInputSchema>
|
||||
|
||||
export const getBreakfastPackageInputSchema = z.object({
|
||||
adults: z.number().min(1, { message: "at least one adult is required" }),
|
||||
fromDate: z
|
||||
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
getRatesInputSchema,
|
||||
getRoomsAvailabilityInputSchema,
|
||||
getSelectedRoomAvailabilityInputSchema,
|
||||
type HotelDataInput,
|
||||
} from "./input"
|
||||
import {
|
||||
breakfastPackagesSchema,
|
||||
@@ -162,6 +163,110 @@ async function getContentstackData(lang: Lang, uid?: string | null) {
|
||||
return hotelPageData.data.hotel_page
|
||||
}
|
||||
|
||||
export async function getHotelData(
|
||||
input: HotelDataInput,
|
||||
serviceToken: string
|
||||
) {
|
||||
const { hotelId, language, include, isCardOnlyPayment } = input
|
||||
|
||||
const params: Record<string, string> = {
|
||||
hotelId,
|
||||
language,
|
||||
}
|
||||
|
||||
if (include) {
|
||||
params.include = include.join(",")
|
||||
}
|
||||
|
||||
getHotelCounter.add(1, {
|
||||
hotelId,
|
||||
language,
|
||||
include,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.hotelData start",
|
||||
JSON.stringify({ query: { hotelId, params } })
|
||||
)
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Hotel.Hotels.hotel(hotelId),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`,
|
||||
},
|
||||
},
|
||||
params
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
getHotelFailCounter.add(1, {
|
||||
hotelId,
|
||||
language,
|
||||
include,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.hotels.hotelData error",
|
||||
JSON.stringify({
|
||||
query: { hotelId, params },
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const validateHotelData = getHotelDataSchema.safeParse(apiJson)
|
||||
|
||||
if (!validateHotelData.success) {
|
||||
getHotelFailCounter.add(1, {
|
||||
hotelId,
|
||||
language,
|
||||
include,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validateHotelData.error),
|
||||
})
|
||||
|
||||
console.error(
|
||||
"api.hotels.hotelData validation error",
|
||||
JSON.stringify({
|
||||
query: { hotelId, params },
|
||||
error: validateHotelData.error,
|
||||
})
|
||||
)
|
||||
throw badRequestError()
|
||||
}
|
||||
|
||||
getHotelSuccessCounter.add(1, {
|
||||
hotelId,
|
||||
language,
|
||||
include,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.hotelData success",
|
||||
JSON.stringify({
|
||||
query: { hotelId, params: params },
|
||||
})
|
||||
)
|
||||
|
||||
if (isCardOnlyPayment) {
|
||||
validateHotelData.data.data.attributes.merchantInformationData.alternatePaymentOptions =
|
||||
[]
|
||||
}
|
||||
|
||||
return validateHotelData.data
|
||||
}
|
||||
|
||||
export const hotelQueryRouter = router({
|
||||
get: contentStackUidWithServiceProcedure
|
||||
.input(getHotelInputSchema)
|
||||
@@ -753,104 +858,7 @@ export const hotelQueryRouter = router({
|
||||
get: serviceProcedure
|
||||
.input(getHotelDataInputSchema)
|
||||
.query(async ({ ctx, input }) => {
|
||||
const { hotelId, language, include, isCardOnlyPayment } = input
|
||||
|
||||
const params: Record<string, string> = {
|
||||
hotelId,
|
||||
language,
|
||||
}
|
||||
|
||||
if (include) {
|
||||
params.include = include.join(",")
|
||||
}
|
||||
|
||||
getHotelCounter.add(1, {
|
||||
hotelId,
|
||||
language,
|
||||
include,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.hotelData start",
|
||||
JSON.stringify({ query: { hotelId, params } })
|
||||
)
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Hotel.Hotels.hotel(hotelId),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
},
|
||||
},
|
||||
params
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
getHotelFailCounter.add(1, {
|
||||
hotelId,
|
||||
language,
|
||||
include,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.hotels.hotelData error",
|
||||
JSON.stringify({
|
||||
query: { hotelId, params },
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const validateHotelData = getHotelDataSchema.safeParse(apiJson)
|
||||
|
||||
if (!validateHotelData.success) {
|
||||
getHotelFailCounter.add(1, {
|
||||
hotelId,
|
||||
language,
|
||||
include,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validateHotelData.error),
|
||||
})
|
||||
|
||||
console.error(
|
||||
"api.hotels.hotelData validation error",
|
||||
JSON.stringify({
|
||||
query: { hotelId, params },
|
||||
error: validateHotelData.error,
|
||||
})
|
||||
)
|
||||
throw badRequestError()
|
||||
}
|
||||
|
||||
getHotelSuccessCounter.add(1, {
|
||||
hotelId,
|
||||
language,
|
||||
include,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.hotelData success",
|
||||
JSON.stringify({
|
||||
query: { hotelId, params: params },
|
||||
})
|
||||
)
|
||||
|
||||
if (isCardOnlyPayment) {
|
||||
validateHotelData.data.data.attributes.merchantInformationData.alternatePaymentOptions =
|
||||
[]
|
||||
}
|
||||
|
||||
return validateHotelData.data
|
||||
return getHotelData(input, ctx.serviceToken)
|
||||
}),
|
||||
}),
|
||||
locations: router({
|
||||
|
||||
Reference in New Issue
Block a user