fix: SW-1102 show error on invalid entry

This commit is contained in:
Hrishikesh Vaipurkar
2024-12-05 23:07:45 +01:00
parent abf6ed1fcc
commit e3f93e0de6
3 changed files with 80 additions and 35 deletions

View File

@@ -29,23 +29,17 @@ export default function SearchList({
}: SearchListProps) {
const intl = useIntl()
const [hasMounted, setHasMounted] = useState(false)
const [isFormSubmitted, setIsFormSubmitted] = useState(false)
const {
clearErrors,
formState: { errors, isSubmitted },
} = useFormContext()
const searchError = errors["search"]
useEffect(() => {
setIsFormSubmitted(isSubmitted)
}, [isSubmitted])
useEffect(() => {
let timeoutID: ReturnType<typeof setTimeout> | null = null
if (searchError && searchError.message === "Required") {
if (searchError) {
timeoutID = setTimeout(() => {
clearErrors("search")
setIsFormSubmitted(false)
// magic number originates from animation
// 5000ms delay + 120ms exectuion
}, 5120)
@@ -66,7 +60,7 @@ export default function SearchList({
return null
}
if (searchError && isFormSubmitted) {
if (searchError && isSubmitted) {
if (typeof searchError.message === "string") {
if (!isOpen) {
if (searchError.message === "Required") {
@@ -87,6 +81,24 @@ export default function SearchList({
</Body>
</Dialog>
)
} else if (searchError.type === "custom") {
return (
<Dialog
className={styles.fadeOut}
getMenuProps={getMenuProps}
variant="error"
>
<Caption className={styles.heading} color="red">
<ErrorCircleIcon color="red" />
{intl.formatMessage({ id: "No results" })}
</Caption>
<Body>
{intl.formatMessage({
id: "We couldn't find a matching location for your search.",
})}
</Body>
</Dialog>
)
}
}
}

View File

@@ -27,7 +27,8 @@ import type { Location } from "@/types/trpc/routers/hotel/locations"
const name = "search"
export default function Search({ locations }: SearchProps) {
const { register, setValue, trigger } = useFormContext<BookingWidgetSchema>()
const { register, setValue, trigger, unregister } =
useFormContext<BookingWidgetSchema>()
const intl = useIntl()
const value = useWatch({ name })
const [state, dispatch] = useReducer(
@@ -135,6 +136,27 @@ export default function Search({ locations }: SearchProps) {
}
}, [dispatch])
const stayType = state.searchData?.type === "cities" ? "city" : "hotel"
const stayValue =
(value === state.searchData?.name &&
((state.searchData?.type === "cities" && state.searchData?.name) ||
state.searchData?.id)) ||
""
useEffect(() => {
if (stayType === "city") {
unregister("hotel")
setValue(stayType, stayValue, {
shouldValidate: true,
})
} else {
unregister("city")
setValue(stayType, Number(stayValue), {
shouldValidate: true,
})
}
}, [stayType, stayValue, unregister, setValue])
return (
<Downshift
initialSelectedItem={state.searchData}
@@ -155,6 +177,10 @@ export default function Search({ locations }: SearchProps) {
openMenu,
}) => (
<div className={styles.container}>
{value ? (
// Adding hidden input to define hotel or city based on destination selection for basic form submit.
<input type="hidden" {...register(stayType)} />
) : null}
<label {...getLabelProps({ htmlFor: name })} className={styles.label}>
<Caption
type="bold"

View File

@@ -35,30 +35,37 @@ export const guestRoomSchema = z
export const guestRoomsSchema = z.array(guestRoomSchema)
export const bookingWidgetSchema = z.object({
bookingCode: z.string(), // Update this as required when working with booking codes component
date: z.object({
// Update this as required once started working with Date picker in Nights component
fromDate: z.string(),
toDate: z.string(),
}),
location: z.string().refine(
(value) => {
if (value) {
const parsedValue: Location = JSON.parse(decodeURIComponent(value))
switch (parsedValue?.type) {
case "cities":
case "hotels":
return true
default:
return false
export const bookingWidgetSchema = z
.object({
bookingCode: z.string(), // Update this as required when working with booking codes component
date: z.object({
// Update this as required once started working with Date picker in Nights component
fromDate: z.string(),
toDate: z.string(),
}),
location: z.string().refine(
(value) => {
if (value) {
const parsedValue: Location = JSON.parse(decodeURIComponent(value))
switch (parsedValue?.type) {
case "cities":
case "hotels":
return true
default:
return false
}
}
}
},
{ message: "Required" }
),
redemption: z.boolean().default(false),
rooms: guestRoomsSchema,
search: z.string({ coerce: true }).min(1, "Required"),
voucher: z.boolean().default(false),
})
},
{ message: "Required" }
),
redemption: z.boolean().default(false),
rooms: guestRoomsSchema,
search: z.string({ coerce: true }).min(1, "Required"),
voucher: z.boolean().default(false),
hotel: z.number().optional(),
city: z.string().optional(),
})
.refine((value) => value.hotel || value.city, {
message: "Destination required",
path: ["search"],
})