feat(SW-1773): add proper validation to form and query
This commit is contained in:
@@ -14,6 +14,7 @@ import Link from "@/components/TempDesignSystem/Link"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { toast } from "@/components/TempDesignSystem/Toasts"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
import { type FindMyBookingFormSchema, findMyBookingFormSchema } from "./schema"
|
||||
@@ -25,12 +26,6 @@ export default function Form() {
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
const form = useForm<FindMyBookingFormSchema>({
|
||||
defaultValues: {
|
||||
reservationNumber: "",
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
email: "",
|
||||
},
|
||||
resolver: zodResolver(findMyBookingFormSchema),
|
||||
mode: "all",
|
||||
criteriaMode: "all",
|
||||
@@ -39,23 +34,30 @@ export default function Form() {
|
||||
|
||||
const update = trpc.booking.createRefId.useMutation({
|
||||
onSuccess: (result) => {
|
||||
const values = form.getValues()
|
||||
const value = new URLSearchParams(values).toString()
|
||||
document.cookie = `bv=${value}; Path=/; Max-Age=30; Secure; SameSite=Strict`
|
||||
router.push(
|
||||
`/${lang}/hotelreservation/my-stay/${encodeURIComponent(result.refId)}`
|
||||
)
|
||||
},
|
||||
onError: (error) => {
|
||||
console.log("Failed to create ref id", error)
|
||||
console.error("Failed to create ref id", error)
|
||||
toast.error(
|
||||
intl.formatMessage({
|
||||
id: "Failed to submit form, please try again later.",
|
||||
})
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
async function onSubmit(data: FindMyBookingFormSchema) {
|
||||
const value = new URLSearchParams(data).toString()
|
||||
document.cookie = `bv=${value}; Path=/; Max-Age=30; Secure; SameSite=Strict`
|
||||
update.mutate({
|
||||
confirmationNumber: data.reservationNumber,
|
||||
bookingNumber: data.bookingNumber,
|
||||
lastName: data.lastName,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className={styles.form}>
|
||||
@@ -71,8 +73,8 @@ export default function Form() {
|
||||
</div>
|
||||
<div className={styles.inputs}>
|
||||
<Input
|
||||
label="Reservation number"
|
||||
name="reservationNumber"
|
||||
label="Booking number"
|
||||
name="bookingNumber"
|
||||
placeholder="XXXXXX"
|
||||
registerOptions={{ required: true }}
|
||||
/>
|
||||
@@ -123,7 +125,7 @@ export default function Form() {
|
||||
type="submit"
|
||||
intent="primary"
|
||||
theme="base"
|
||||
disabled={form.formState.isSubmitting}
|
||||
disabled={form.formState.isSubmitting || update.isPending}
|
||||
>
|
||||
{intl.formatMessage({ id: "Find" })}
|
||||
</Button>
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
import { z } from "zod"
|
||||
|
||||
export const findMyBookingFormSchema = z.object({
|
||||
reservationNumber: z.string(),
|
||||
firstName: z.string().max(250).trim().min(1, {
|
||||
bookingNumber: z
|
||||
.string()
|
||||
.trim()
|
||||
.regex(/^[0-9]+(-[0-9])?$/, {
|
||||
message: "Invalid booking number",
|
||||
})
|
||||
.min(1, {
|
||||
message: "Booking number is required",
|
||||
}),
|
||||
firstName: z.string().trim().max(250).min(1, {
|
||||
message: "First name is required",
|
||||
}),
|
||||
lastName: z.string().max(250).trim().min(1, {
|
||||
lastName: z.string().trim().max(250).min(1, {
|
||||
message: "Last name is required",
|
||||
}),
|
||||
email: z.string().max(250).email(),
|
||||
email: z.string().max(250).email({ message: "Email address is required" }),
|
||||
})
|
||||
|
||||
export interface FindMyBookingFormSchema
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client"
|
||||
import { Text, TextField } from "react-aria-components"
|
||||
import { Controller, useFormContext } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { CheckIcon, InfoCircleIcon } from "@/components/Icons"
|
||||
import AriaInputWithLabel from "@/components/TempDesignSystem/Form/Input/AriaInputWithLabel"
|
||||
@@ -25,6 +26,7 @@ export default function Input({
|
||||
registerOptions = {},
|
||||
type = "text",
|
||||
}: InputProps) {
|
||||
const intl = useIntl()
|
||||
const { control } = useFormContext()
|
||||
let numberAttributes: HTMLAttributes<HTMLInputElement> = {}
|
||||
if (type === "number") {
|
||||
@@ -73,7 +75,7 @@ export default function Input({
|
||||
{fieldState.error ? (
|
||||
<Caption className={styles.error} fontOnly>
|
||||
<InfoCircleIcon color="red" />
|
||||
{fieldState.error.message}
|
||||
{intl.formatMessage({ id: fieldState.error.message })}
|
||||
</Caption>
|
||||
) : null}
|
||||
</TextField>
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
"Booking code": "Bookingkode",
|
||||
"Booking confirmation": "Booking bekræftelse",
|
||||
"Booking number": "Bookingnummer",
|
||||
"Booking number is required": "Bookingnummer er påkrævet",
|
||||
"Booking policy": "Booking politik",
|
||||
"Booking summary": "Opsummering",
|
||||
"Breakfast": "Morgenmad",
|
||||
@@ -209,7 +210,7 @@
|
||||
"Elevator preference": "Elevatorpræference",
|
||||
"Email": "E-mail",
|
||||
"Email address": "E-mailadresse",
|
||||
"Email address is required": "Email address is required",
|
||||
"Email address is required": "E-mailadresse er påkrævet",
|
||||
"Enjoy relaxed restaurant experiences": "Enjoy relaxed restaurant experiences",
|
||||
"Enter destination or hotel": "Indtast destination eller hotel",
|
||||
"Enter your details": "Indtast dine oplysninger",
|
||||
@@ -225,6 +226,7 @@
|
||||
"FAQ": "Ofte stillede spørgsmål",
|
||||
"Failed to add to calendar": "Fejl ved tilføjelse til kalender",
|
||||
"Failed to delete credit card, please try again later.": "Kunne ikke slette kreditkort. Prøv venligst igen senere.",
|
||||
"Failed to submit form, please try again later.": "Failed to submit form, please try again later.",
|
||||
"Failed to unlink account": "Failed to unlink account",
|
||||
"Failed to upgrade level": "Failed to upgrade level",
|
||||
"Failed to verify membership": "Medlemskab ikke verificeret",
|
||||
@@ -318,6 +320,7 @@
|
||||
"Indoor windows facing the hotel": "Indoor windows facing the hotel",
|
||||
"Insufficient points": "Utilstrækkelige point",
|
||||
"Invalid booking code": "Ugyldig reservationskode",
|
||||
"Invalid booking number": "Ugyldigt reservationsnummer",
|
||||
"Is there anything else you would like us to know before your arrival?": "Er der andet, du gerne vil have os til at vide, før din ankomst?",
|
||||
"It is not posible to manage your communication preferences right now, please try again later or contact support if the problem persists.": "Det er ikke muligt at administrere dine kommunikationspræferencer lige nu, prøv venligst igen senere eller kontakt support, hvis problemet fortsætter.",
|
||||
"It looks like no hotels match your filters. Try adjusting your search to find the perfect stay.": "Det ser ud til, at ingen hoteller matcher dine filtre. Prøv at justere din søgning for at finde det perfekte ophold.",
|
||||
@@ -466,9 +469,9 @@
|
||||
"Password": "Adgangskode",
|
||||
"Pay later": "Betal senere",
|
||||
"Pay now": "Betal nu",
|
||||
"Pay the member price of {amount} for Room {roomNr}": "Betal medlemsprisen på {amount} til værelse {roomNr}",
|
||||
"Pay with card": "Betal med kort",
|
||||
"Pay with points": "Betal med point",
|
||||
"Pay the member price of {amount} for Room {roomNr}": "Betal medlemsprisen på {amount} til værelse {roomNr}",
|
||||
"Payment": "Betaling",
|
||||
"Payment Guarantee": "Garanti betaling",
|
||||
"Payment details": "Payment details",
|
||||
|
||||
@@ -90,6 +90,7 @@
|
||||
"Booking code": "Buchungscode",
|
||||
"Booking confirmation": "Buchungsbestätigung",
|
||||
"Booking number": "Buchungsnummer",
|
||||
"Booking number is required": "Buchungsnummer ist erforderlich",
|
||||
"Booking policy": "Buchungsbedingungen",
|
||||
"Booking summary": "Zusammenfassung",
|
||||
"Breakfast": "Frühstück",
|
||||
@@ -226,6 +227,7 @@
|
||||
"FAQ": "Häufig gestellte Fragen",
|
||||
"Failed to add to calendar": "Fehler beim Hinzufügen zum Kalender",
|
||||
"Failed to delete credit card, please try again later.": "Kreditkarte konnte nicht gelöscht werden. Bitte versuchen Sie es später noch einmal.",
|
||||
"Failed to submit form, please try again later.": "Failed to submit form, please try again later.",
|
||||
"Failed to unlink account": "Failed to unlink account",
|
||||
"Failed to upgrade level": "Failed to upgrade level",
|
||||
"Failed to verify membership": "Medlemskab nicht verifiziert",
|
||||
@@ -319,6 +321,7 @@
|
||||
"Indoor windows facing the hotel": "Indoor windows facing the hotel",
|
||||
"Insufficient points": "Zu wenig Punkte",
|
||||
"Invalid booking code": "Ungültiger Buchungscode",
|
||||
"Invalid booking number": "Ungültige Buchungsnummer",
|
||||
"Is there anything else you would like us to know before your arrival?": "Gibt es noch etwas, das Sie uns vor Ihrer Ankunft mitteilen möchten?",
|
||||
"It is not posible to manage your communication preferences right now, please try again later or contact support if the problem persists.": "Es ist derzeit nicht möglich, Ihre Kommunikationseinstellungen zu verwalten. Bitte versuchen Sie es später erneut oder wenden Sie sich an den Support, wenn das Problem weiterhin besteht.",
|
||||
"It looks like no hotels match your filters. Try adjusting your search to find the perfect stay.": "Es scheint, dass keine Hotels Ihren Filtern entsprechen. Versuchen Sie, Ihre Suche anzupassen, um den perfekten Aufenthalt zu finden.",
|
||||
@@ -468,9 +471,9 @@
|
||||
"Password": "Passwort",
|
||||
"Pay later": "Später bezahlen",
|
||||
"Pay now": "Jetzt bezahlen",
|
||||
"Pay the member price of {amount} for Room {roomNr}": "Zahlen Sie den Mitgliedspreis von {amount} für Zimmer {roomNr}",
|
||||
"Pay with Card": "Mit Karte bezahlen",
|
||||
"Pay with points": "Mit Punkten bezahlen",
|
||||
"Pay the member price of {amount} for Room {roomNr}": "Zahlen Sie den Mitgliedspreis von {amount} für Zimmer {roomNr}",
|
||||
"Payment": "Zahlung",
|
||||
"Payment Guarantee": "Zahlungsgarantie",
|
||||
"Payment details": "Payment details",
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
"Booking code": "Booking code",
|
||||
"Booking confirmation": "Booking confirmation",
|
||||
"Booking number": "Booking number",
|
||||
"Booking number is required": "Booking number is required",
|
||||
"Booking policy": "Booking policy",
|
||||
"Booking summary": "Booking summary",
|
||||
"Breakfast": "Breakfast",
|
||||
@@ -227,6 +228,7 @@
|
||||
"FAQ": "FAQ",
|
||||
"Failed to add to calendar": "Failed to add to calendar",
|
||||
"Failed to delete credit card, please try again later.": "Failed to delete credit card, please try again later.",
|
||||
"Failed to submit form, please try again later.": "Failed to submit form, please try again later.",
|
||||
"Failed to unlink account": "Failed to unlink account",
|
||||
"Failed to upgrade level": "Failed to upgrade level",
|
||||
"Failed to verify membership": "Failed to verify membership",
|
||||
@@ -320,6 +322,7 @@
|
||||
"Indoor windows facing the hotel": "Indoor windows facing the hotel",
|
||||
"Insufficient points": "Insufficient points",
|
||||
"Invalid booking code": "Invalid booking code",
|
||||
"Invalid booking number": "Invalid booking number",
|
||||
"Is there anything else you would like us to know before your arrival?": "Is there anything else you would like us to know before your arrival?",
|
||||
"It is not posible to manage your communication preferences right now, please try again later or contact support if the problem persists.": "It is not posible to manage your communication preferences right now, please try again later or contact support if the problem persists.",
|
||||
"It looks like no hotels match your filters. Try adjusting your search to find the perfect stay.": "It looks like no hotels match your filters. Try adjusting your search to find the perfect stay.",
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
"Booking code": "Varauskoodi",
|
||||
"Booking confirmation": "Varausvahvistus",
|
||||
"Booking number": "Varausnumero",
|
||||
"Booking number is required": "Varausnumero vaaditaan",
|
||||
"Booking policy": "Varauskäytäntö",
|
||||
"Booking summary": "Yhteenveto",
|
||||
"Breakfast": "Aamiainen",
|
||||
@@ -225,6 +226,7 @@
|
||||
"FAQ": "Usein kysytyt kysymykset",
|
||||
"Failed to add to calendar": "Virhe kalenteriin lisäämisessä",
|
||||
"Failed to delete credit card, please try again later.": "Luottokortin poistaminen epäonnistui, yritä myöhemmin uudelleen.",
|
||||
"Failed to submit form, please try again later.": "Failed to submit form, please try again later.",
|
||||
"Failed to unlink account": "Failed to unlink account",
|
||||
"Failed to upgrade level": "Failed to upgrade level",
|
||||
"Failed to verify membership": "Jäsenyys ei verifioitu",
|
||||
@@ -318,6 +320,7 @@
|
||||
"Indoor windows facing the hotel": "Indoor windows facing the hotel",
|
||||
"Insufficient points": "Riittämättä pisteitä",
|
||||
"Invalid booking code": "Virheellinen varauskoodi",
|
||||
"Invalid booking number": "Virheellinen varausnumero",
|
||||
"Is there anything else you would like us to know before your arrival?": "Onko jotain muuta, mitä haluaisit meidän tietävän ennen saapumistasi?",
|
||||
"It is not posible to manage your communication preferences right now, please try again later or contact support if the problem persists.": "Viestintäasetuksiasi ei voi hallita juuri nyt. Yritä myöhemmin uudelleen tai ota yhteyttä tukeen, jos ongelma jatkuu.",
|
||||
"It looks like no hotels match your filters. Try adjusting your search to find the perfect stay.": "Näyttää siltä, että mikään hotelli ei vastaa suodattimiasi. Yritä muokata hakuasi löytääksesi täydellisen oleskelun.",
|
||||
@@ -467,9 +470,9 @@
|
||||
"Password": "Salasana",
|
||||
"Pay later": "Maksa myöhemmin",
|
||||
"Pay now": "Maksa nyt",
|
||||
"Pay the member price of {amount} for Room {roomNr}": "Maksa jäsenhinta {amount} varten Huone {roomNr}",
|
||||
"Pay with Card": "Maksa kortilla",
|
||||
"Pay with points": "Maksa pisteillä",
|
||||
"Pay the member price of {amount} for Room {roomNr}": "Maksa jäsenhinta {amount} varten Huone {roomNr}",
|
||||
"Payment": "Maksu",
|
||||
"Payment Guarantee": "Varmistusmaksu",
|
||||
"Payment details": "Payment details",
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
"Booking code": "Bestillingskode",
|
||||
"Booking confirmation": "Bestillingsbekreftelse",
|
||||
"Booking number": "Bestillingsnummer",
|
||||
"Booking number is required": "Bookingnummer er påkrevd",
|
||||
"Booking policy": "Bestillingsbetingelser",
|
||||
"Booking summary": "Sammendrag",
|
||||
"Breakfast": "Frokost",
|
||||
@@ -224,6 +225,7 @@
|
||||
"FAQ": "Ofte stilte spørsmål",
|
||||
"Failed to add to calendar": "Feil ved tilføyelse til kalender",
|
||||
"Failed to delete credit card, please try again later.": "Kunne ikke slette kredittkortet, prøv igjen senere.",
|
||||
"Failed to submit form, please try again later.": "Failed to submit form, please try again later.",
|
||||
"Failed to unlink account": "Failed to unlink account",
|
||||
"Failed to upgrade level": "Failed to upgrade level",
|
||||
"Failed to verify membership": "Medlemskap ikke verifisert",
|
||||
@@ -317,6 +319,7 @@
|
||||
"Indoor windows facing the hotel": "Indoor windows facing the hotel",
|
||||
"Insufficient points": "Utilstrekklig poeng",
|
||||
"Invalid booking code": "Ugyldig bookingkode",
|
||||
"Invalid booking number": "Ugyldig bestillingsnummer",
|
||||
"Is there anything else you would like us to know before your arrival?": "Er det noe annet du vil at vi skal vite før ankomsten din?",
|
||||
"It is not posible to manage your communication preferences right now, please try again later or contact support if the problem persists.": "Det er ikke mulig å administrere kommunikasjonspreferansene dine akkurat nå, prøv igjen senere eller kontakt support hvis problemet vedvarer.",
|
||||
"It looks like no hotels match your filters. Try adjusting your search to find the perfect stay.": "Det ser ut til at ingen hoteller samsvarer med filtrene dine. Prøv å justere søket for å finne det perfekte oppholdet.",
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
"Booking code": "Bokningskod",
|
||||
"Booking confirmation": "Bokningsbekräftelse",
|
||||
"Booking number": "Bokningsnummer",
|
||||
"Booking number is required": "Bokningsnummer krävs",
|
||||
"Booking policy": "Bokningsvillkor",
|
||||
"Booking summary": "Sammanfattning",
|
||||
"Breakfast": "Frukost",
|
||||
@@ -224,6 +225,7 @@
|
||||
"FAQ": "FAQ",
|
||||
"Failed to add to calendar": "Misslyckades att lägga till i kalender",
|
||||
"Failed to delete credit card, please try again later.": "Det gick inte att ta bort kreditkortet, försök igen senare.",
|
||||
"Failed to submit form, please try again later.": "Failed to submit form, please try again later.",
|
||||
"Failed to unlink account": "Failed to unlink account",
|
||||
"Failed to upgrade level": "Failed to upgrade level",
|
||||
"Failed to verify membership": "Medlemskap inte verifierat",
|
||||
@@ -317,6 +319,7 @@
|
||||
"Indoor windows facing the hotel": "Inomhusfönster mot hotellet",
|
||||
"Insufficient points": "Otillräckliga poäng",
|
||||
"Invalid booking code": "Ogiltig bokningskod",
|
||||
"Invalid booking number": "Ogiltigt bokningsnummer",
|
||||
"Is there anything else you would like us to know before your arrival?": "Är det något mer du vill att vi ska veta innan din ankomst?",
|
||||
"It is not posible to manage your communication preferences right now, please try again later or contact support if the problem persists.": "Det gick inte att hantera dina kommunikationsinställningar just nu, försök igen senare eller kontakta supporten om problemet kvarstår.",
|
||||
"It looks like no hotels match your filters. Try adjusting your search to find the perfect stay.": "Det verkar som att inga hotell matchar dina filter. Prova att justera din sökning för att hitta den perfekta vistelsen.",
|
||||
|
||||
@@ -40,6 +40,14 @@ export function notFound(cause?: unknown) {
|
||||
})
|
||||
}
|
||||
|
||||
export function unprocessableContent(cause?: unknown) {
|
||||
return new TRPCError({
|
||||
code: "UNPROCESSABLE_CONTENT",
|
||||
message: "Unprocessable content",
|
||||
cause,
|
||||
})
|
||||
}
|
||||
|
||||
export function internalServerError(cause?: unknown) {
|
||||
return new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
@@ -78,6 +86,8 @@ export function serverErrorByStatus(status: number, cause?: unknown) {
|
||||
return notFound(cause)
|
||||
case 409:
|
||||
return conflictError(cause)
|
||||
case 422:
|
||||
return unprocessableContent(cause)
|
||||
case 500:
|
||||
default:
|
||||
return internalServerError(cause)
|
||||
|
||||
@@ -109,8 +109,12 @@ export const cancelBookingInput = z.object({
|
||||
})
|
||||
|
||||
export const createRefIdInput = z.object({
|
||||
confirmationNumber: z.string(),
|
||||
lastName: z.string(),
|
||||
bookingNumber: z
|
||||
.string()
|
||||
.trim()
|
||||
.regex(/^\s*[0-9]+(-[0-9])?\s*$/)
|
||||
.min(1),
|
||||
lastName: z.string().trim().max(250).min(1),
|
||||
})
|
||||
|
||||
// Query
|
||||
|
||||
@@ -236,8 +236,12 @@ export const bookingQueryRouter = router({
|
||||
createRefId: serviceProcedure
|
||||
.input(createRefIdInput)
|
||||
.mutation(async function ({ input }) {
|
||||
const { confirmationNumber, lastName } = input
|
||||
const encryptedRefId = encryptValue(`${confirmationNumber},${lastName}`)
|
||||
const { bookingNumber, lastName } = input
|
||||
const encryptedRefId = encryptValue(`${bookingNumber},${lastName}`)
|
||||
|
||||
if (!encryptedRefId) {
|
||||
throw serverErrorByStatus(422, "Was not able to encrypt ref id")
|
||||
}
|
||||
|
||||
return {
|
||||
refId: encryptedRefId,
|
||||
|
||||
Reference in New Issue
Block a user