From 3de4f9e40686faaaccdea94c4f17f63730668086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20J=C3=A4derberg?= Date: Tue, 20 May 2025 12:25:36 +0000 Subject: [PATCH] Merged in fix/SW-2827-availability-for-old-dates (pull request #2158) fix: add error handling for hotel room availability * fix: add error handling for hotel room availability * fix: update error codes * fix: have one error message to rule them all. them as in permutations of invalid dates Approved-by: Linus Flood --- .../RoomsContainer/index.module.css | 6 ++ .../SelectRate/RoomsContainer/index.tsx | 66 ++++++++++++++++--- .../server/routers/hotels/input.ts | 66 +++++++++++++++---- 3 files changed, 118 insertions(+), 20 deletions(-) create mode 100644 apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/index.module.css diff --git a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/index.module.css b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/index.module.css new file mode 100644 index 000000000..490c64c24 --- /dev/null +++ b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/index.module.css @@ -0,0 +1,6 @@ +.errorContainer { + margin: 0 auto; + padding: var(--Spacing-x-one-and-half) 0; + width: 100%; + max-width: var(--max-width-page); +} diff --git a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/index.tsx b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/index.tsx index 46fe36ec2..1c14d77ad 100644 --- a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/index.tsx +++ b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/index.tsx @@ -1,7 +1,10 @@ "use client" +import { useIntl } from "react-intl" + import { trpc } from "@/lib/trpc/client" +import Alert from "@/components/TempDesignSystem/Alert" import useLang from "@/hooks/useLang" import RatesProvider from "@/providers/RatesProvider" @@ -9,7 +12,10 @@ import RateSummary from "./RateSummary" import Rooms from "./Rooms" import { RoomsContainerSkeleton } from "./RoomsContainerSkeleton" +import styles from "./index.module.css" + import type { RoomsContainerProps } from "@/types/components/hotelReservation/selectRate/roomsContainer" +import { AlertTypeEnum } from "@/types/enums/alert" export function RoomsContainer({ booking, @@ -18,25 +24,45 @@ export function RoomsContainer({ vat, }: RoomsContainerProps) { const lang = useLang() + const intl = useIntl() - const roomsAvailability = trpc.hotel.availability.selectRate.rooms.useQuery({ - booking, - lang, - }) + const { data, isFetching, isError, error } = + trpc.hotel.availability.selectRate.rooms.useQuery( + { + booking, + lang, + }, + { + retry(failureCount, error) { + if (error.data?.code === "BAD_REQUEST") { + return false + } - if ( - (roomsAvailability.isFetching || !roomsAvailability.data) && - !roomsAvailability.isFetched - ) { + return failureCount <= 3 + }, + } + ) + + if (isFetching) { return } + if (isError) { + const errorMessage = getErrorMessage(error.data?.zodError?.formErrors, intl) + + return ( +
+ +
+ ) + } + return ( @@ -44,3 +70,25 @@ export function RoomsContainer({ ) } + +function getErrorMessage( + formErrors: string[] | undefined, + intl: ReturnType +) { + const firstError = formErrors?.at(0) + + switch (firstError) { + case "FROMDATE_INVALID": + case "TODATE_INVALID": + case "TODATE_MUST_BE_AFTER_FROMDATE": + case "FROMDATE_CANNOT_BE_IN_THE_PAST": { + return intl.formatMessage({ + defaultMessage: "Invalid dates", + }) + } + default: + return intl.formatMessage({ + defaultMessage: "Something went wrong", + }) + } +} diff --git a/apps/scandic-web/server/routers/hotels/input.ts b/apps/scandic-web/server/routers/hotels/input.ts index ea0ec7031..dfb0e4497 100644 --- a/apps/scandic-web/server/routers/hotels/input.ts +++ b/apps/scandic-web/server/routers/hotels/input.ts @@ -1,3 +1,4 @@ +import dayjs from "dayjs" import { z } from "zod" import { Lang } from "@/constants/languages" @@ -58,17 +59,60 @@ const baseBookingSchema = z.object({ toDate: z.string(), }) -export const selectRateRoomsAvailabilityInputSchema = z.object({ - booking: baseBookingSchema.extend({ - rooms: z.array( - baseRoomSchema.extend({ - rateCode: z.string().optional(), - roomTypeCode: z.string().optional(), - }) - ), - }), - lang: z.nativeEnum(Lang), -}) +export const selectRateRoomsAvailabilityInputSchema = z + .object({ + booking: baseBookingSchema.extend({ + rooms: z.array( + baseRoomSchema.extend({ + rateCode: z.string().optional(), + roomTypeCode: z.string().optional(), + }) + ), + }), + lang: z.nativeEnum(Lang), + }) + .refine( + (data) => { + const fromDate = dayjs(data.booking.fromDate) + + return fromDate.isValid() + }, + { + message: "FROMDATE_INVALID", + } + ) + .refine( + (data) => { + const toDate = dayjs(data.booking.toDate) + + return toDate.isValid() + }, + { + message: "TODATE_INVALID", + } + ) + .refine( + (data) => { + const fromDate = dayjs(data.booking.fromDate).startOf("day") + const toDate = dayjs(data.booking.toDate).startOf("day") + + return fromDate.isBefore(toDate) + }, + { + message: "FROMDATE_BEFORE_TODATE", + } + ) + .refine( + (data) => { + const fromDate = dayjs(data.booking.fromDate) + const today = dayjs().startOf("day") + + return fromDate.isSameOrAfter(today) + }, + { + message: "FROMDATE_CANNOT_BE_IN_THE_PAST", + } + ) export const selectRateRoomAvailabilityInputSchema = z.object({ booking: baseBookingSchema.extend({