feat: booking confirmation page with hardcoded data

This commit is contained in:
Simon Emanuelsson
2024-10-22 11:43:08 +02:00
parent 445bde8e2e
commit 2d23f9bbf3
42 changed files with 859 additions and 533 deletions

View File

@@ -63,6 +63,10 @@ export const createBookingInput = z.object({
})
// Query
export const getBookingStatusInput = z.object({
const confirmationNumberInput = z.object({
confirmationNumber: z.string(),
})
export const bookingConfirmationInput = confirmationNumberInput
export const getBookingStatusInput = confirmationNumberInput

View File

@@ -35,96 +35,95 @@ async function getMembershipNumber(
}
export const bookingMutationRouter = router({
booking: router({
create: serviceProcedure
.input(createBookingInput)
.mutation(async function ({ ctx, input }) {
const { checkInDate, checkOutDate, hotelId } = input
create: serviceProcedure.input(createBookingInput).mutation(async function ({
ctx,
input,
}) {
const { checkInDate, checkOutDate, hotelId } = input
// TODO: add support for user token OR service token in procedure
// then we can fetch membership number if user token exists
const loggingAttributes = {
// membershipNumber: await getMembershipNumber(ctx.session),
checkInDate,
checkOutDate,
hotelId,
}
// TODO: add support for user token OR service token in procedure
// then we can fetch membership number if user token exists
const loggingAttributes = {
// membershipNumber: await getMembershipNumber(ctx.session),
checkInDate,
checkOutDate,
hotelId,
}
createBookingCounter.add(1, { hotelId, checkInDate, checkOutDate })
createBookingCounter.add(1, { hotelId, checkInDate, checkOutDate })
console.info(
"api.booking.booking.create start",
JSON.stringify({
query: loggingAttributes,
})
)
const headers = {
Authorization: `Bearer ${ctx.serviceToken}`,
}
console.info(
"api.booking.create start",
JSON.stringify({
query: loggingAttributes,
})
)
const headers = {
Authorization: `Bearer ${ctx.serviceToken}`,
}
const apiResponse = await api.post(api.endpoints.v1.booking, {
headers,
body: input,
const apiResponse = await api.post(api.endpoints.v1.booking, {
headers,
body: input,
})
if (!apiResponse.ok) {
const text = await apiResponse.text()
createBookingFailCounter.add(1, {
hotelId,
checkInDate,
checkOutDate,
error_type: "http_error",
error: JSON.stringify({
status: apiResponse.status,
}),
})
console.error(
"api.booking.create error",
JSON.stringify({
query: loggingAttributes,
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
error: text,
},
})
)
return null
}
if (!apiResponse.ok) {
const text = await apiResponse.text()
createBookingFailCounter.add(1, {
hotelId,
checkInDate,
checkOutDate,
error_type: "http_error",
error: JSON.stringify({
status: apiResponse.status,
}),
})
console.error(
"api.booking.booking.create error",
JSON.stringify({
query: loggingAttributes,
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
error: text,
},
})
)
return null
}
const apiJson = await apiResponse.json()
const verifiedData = createBookingSchema.safeParse(apiJson)
if (!verifiedData.success) {
createBookingFailCounter.add(1, {
hotelId,
checkInDate,
checkOutDate,
error_type: "validation_error",
})
const apiJson = await apiResponse.json()
const verifiedData = createBookingSchema.safeParse(apiJson)
if (!verifiedData.success) {
createBookingFailCounter.add(1, {
hotelId,
checkInDate,
checkOutDate,
error_type: "validation_error",
})
console.error(
"api.booking.booking.create validation error",
JSON.stringify({
query: loggingAttributes,
error: verifiedData.error,
})
)
return null
}
createBookingSuccessCounter.add(1, {
hotelId,
checkInDate,
checkOutDate,
console.error(
"api.booking.create validation error",
JSON.stringify({
query: loggingAttributes,
error: verifiedData.error,
})
)
return null
}
console.info(
"api.booking.booking.create success",
JSON.stringify({
query: loggingAttributes,
})
)
return verifiedData.data
}),
createBookingSuccessCounter.add(1, {
hotelId,
checkInDate,
checkOutDate,
})
console.info(
"api.booking.create success",
JSON.stringify({
query: loggingAttributes,
})
)
return verifiedData.data
}),
})

View File

@@ -1,5 +1,8 @@
import { z } from "zod"
import { BedTypeEnum } from "@/constants/booking"
// MUTATION
export const createBookingSchema = z
.object({
data: z.object({
@@ -32,4 +35,46 @@ export const createBookingSchema = z
paymentUrl: d.data.attributes.paymentUrl,
}))
type CreateBookingData = z.infer<typeof createBookingSchema>
// QUERY
const childrenAgesSchema = z.object({
age: z.number(),
bedType: z.nativeEnum(BedTypeEnum),
})
const guestSchema = z.object({
firstName: z.string(),
lastName: z.string(),
})
const packagesSchema = z.object({
accessibility: z.boolean(),
allergyFriendly: z.boolean(),
breakfast: z.boolean(),
petFriendly: z.boolean(),
})
export const bookingConfirmationSchema = z
.object({
data: z.object({
attributes: z.object({
adults: z.number(),
checkInDate: z.date({ coerce: true }),
checkOutDate: z.date({ coerce: true }),
createDateTime: z.date({ coerce: true }),
childrenAges: z.array(childrenAgesSchema),
computedReservationStatus: z.string(),
confirmationNumber: z.string(),
currencyCode: z.string(),
guest: guestSchema,
hasPayRouting: z.boolean(),
hotelId: z.string(),
packages: packagesSchema,
rateCode: z.string(),
reservationStatus: z.string(),
totalPrice: z.number(),
}),
id: z.string(),
type: z.literal("booking"),
}),
})
.transform(({ data }) => data.attributes)

View File

@@ -4,10 +4,20 @@ import * as api from "@/lib/api"
import { badRequestError, serverErrorByStatus } from "@/server/errors/trpc"
import { router, serviceProcedure } from "@/server/trpc"
import { getBookingStatusInput } from "./input"
import { createBookingSchema } from "./output"
import { bookingConfirmationInput, getBookingStatusInput } from "./input"
import { bookingConfirmationSchema, createBookingSchema } from "./output"
const meter = metrics.getMeter("trpc.booking")
const getBookingConfirmationCounter = meter.createCounter(
"trpc.booking.confirmation"
)
const getBookingConfirmationSuccessCounter = meter.createCounter(
"trpc.booking.confirmation-success"
)
const getBookingConfirmationFailCounter = meter.createCounter(
"trpc.booking.confirmation-fail"
)
const getBookingStatusCounter = meter.createCounter("trpc.booking.status")
const getBookingStatusSuccessCounter = meter.createCounter(
"trpc.booking.status-success"
@@ -17,6 +27,113 @@ const getBookingStatusFailCounter = meter.createCounter(
)
export const bookingQueryRouter = router({
confirmation: serviceProcedure
.input(bookingConfirmationInput)
.query(async function ({ ctx, input: { confirmationNumber } }) {
getBookingConfirmationCounter.add(1, { confirmationNumber })
const apiResponse = await api.get(
`${api.endpoints.v1.booking}/${confirmationNumber}`,
{
headers: {
Authorization: `Bearer ${ctx.serviceToken}`,
},
}
)
if (!apiResponse.ok) {
const responseMessage = await apiResponse.text()
getBookingConfirmationFailCounter.add(1, {
confirmationNumber,
error_type: "http_error",
error: responseMessage,
})
console.error(
"api.booking.confirmation error",
JSON.stringify({
query: { confirmationNumber },
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
text: responseMessage,
},
})
)
throw serverErrorByStatus(apiResponse.status, apiResponse)
}
const apiJson = await apiResponse.json()
const booking = bookingConfirmationSchema.safeParse(apiJson)
if (!booking.success) {
getBookingConfirmationFailCounter.add(1, {
confirmationNumber,
error_type: "validation_error",
error: JSON.stringify(booking.error),
})
console.error(
"api.booking.confirmation validation error",
JSON.stringify({
query: { confirmationNumber },
error: booking.error,
})
)
throw badRequestError()
}
getBookingConfirmationSuccessCounter.add(1, { confirmationNumber })
console.info(
"api.booking.confirmation success",
JSON.stringify({
query: { confirmationNumber },
})
)
return {
...booking.data,
temp: {
breakfastFrom: "06:30",
breakfastTo: "11:00",
cancelPolicy: "Free rebooking",
fromDate: "2024-10-21 14:00",
packages: [
{
name: "Breakfast buffet",
price: "150 SEK",
},
{
name: "Member discount",
price: "-297 SEK",
},
{
name: "Points used / remaining",
price: "0 / 1044",
},
],
payment: "2024-08-09 1:47",
room: {
price: "2 589 SEK",
type: "Cozy Cabin",
vat: "684,79 SEK",
},
toDate: "2024-10-22 11:00",
total: "2 739 SEK",
totalInEuro: "265 EUR",
},
guest: {
email: "sarah.obrian@gmail.com",
firstName: "Sarah",
lastName: "O'Brian",
memberbershipNumber: "19822",
phoneNumber: "+46702446688",
},
hotel: {
email: "bookings@scandichotels.com",
name: "Downtown Camper by Scandic",
phoneNumber: "+4689001350",
},
}
}),
status: serviceProcedure.input(getBookingStatusInput).query(async function ({
ctx,
input,