Merged in fix/SW-2501-validation-trigger (pull request #1924)

fix(SW-2501): validation trigger

* fix(SW-2501): validation trigger

On enter details, when submitting we want to trigger the validation for
the details forms for each room. This will display error messages for
the form fields with errors if they are not already displayed, so the
user knows which fields has errors.


Approved-by: Tobias Johansson
This commit is contained in:
Niclas Edenvin
2025-05-02 13:59:23 +00:00
parent d49ecdae1f
commit fb44990777
5 changed files with 57 additions and 13 deletions

View File

@@ -24,7 +24,8 @@ const formID = "enter-details"
export default function Details() { export default function Details() {
const intl = useIntl() const intl = useIntl()
const { rooms } = useEnterDetailsStore((state) => ({ const { addPreSubmitCallback, rooms } = useEnterDetailsStore((state) => ({
addPreSubmitCallback: state.actions.addPreSubmitCallback,
rooms: state.rooms, rooms: state.rooms,
})) }))
@@ -71,13 +72,23 @@ export default function Details() {
}, },
}) })
const {
formState: { isValid },
handleSubmit,
trigger,
} = methods
useEffect(() => {
addPreSubmitCallback(`${idx}-details`, trigger)
}, [addPreSubmitCallback, idx, trigger])
const updateDetailsStore = useCallback(() => { const updateDetailsStore = useCallback(() => {
if (methods.formState.isValid) { if (isValid) {
methods.handleSubmit(updateDetails)() handleSubmit(updateDetails)()
} else { } else {
setIncomplete() setIncomplete()
} }
}, [methods, setIncomplete, updateDetails]) }, [handleSubmit, isValid, setIncomplete, updateDetails])
useEffect(updateDetailsStore, [methods.formState.isValid, updateDetailsStore]) useEffect(updateDetailsStore, [methods.formState.isValid, updateDetailsStore])

View File

@@ -4,6 +4,8 @@ import { useCallback, useEffect } from "react"
import { FormProvider, useForm } from "react-hook-form" import { FormProvider, useForm } from "react-hook-form"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { useEnterDetailsStore } from "@/stores/enter-details"
import SpecialRequests from "@/components/HotelReservation/EnterDetails/Details/SpecialRequests" import SpecialRequests from "@/components/HotelReservation/EnterDetails/Details/SpecialRequests"
import CountrySelect from "@/components/TempDesignSystem/Form/Country" import CountrySelect from "@/components/TempDesignSystem/Form/Country"
import Input from "@/components/TempDesignSystem/Form/Input" import Input from "@/components/TempDesignSystem/Form/Input"
@@ -27,10 +29,15 @@ const formID = "enter-details"
export default function Details({ user }: DetailsProps) { export default function Details({ user }: DetailsProps) {
const intl = useIntl() const intl = useIntl()
const { addPreSubmitCallback } = useEnterDetailsStore((state) => ({
addPreSubmitCallback: state.actions.addPreSubmitCallback,
}))
const { const {
actions: { updateDetails, setIncomplete }, actions: { updateDetails, setIncomplete },
room, room,
roomNr, roomNr,
idx,
} = useRoomContext() } = useRoomContext()
const initialData = room.guest const initialData = room.guest
@@ -58,6 +65,16 @@ export default function Details({ user }: DetailsProps) {
}, },
}) })
const {
formState: { isValid },
handleSubmit,
trigger,
} = methods
useEffect(() => {
addPreSubmitCallback(`${idx}-details`, trigger)
}, [addPreSubmitCallback, idx, trigger])
const onSubmit = useCallback( const onSubmit = useCallback(
(values: DetailsSchema) => { (values: DetailsSchema) => {
updateDetails(values) updateDetails(values)
@@ -66,12 +83,12 @@ export default function Details({ user }: DetailsProps) {
) )
const updateDetailsStore = useCallback(() => { const updateDetailsStore = useCallback(() => {
if (methods.formState.isValid) { if (isValid) {
methods.handleSubmit(onSubmit)() handleSubmit(onSubmit)()
} else { } else {
setIncomplete() setIncomplete()
} }
}, [methods, onSubmit, setIncomplete]) }, [handleSubmit, isValid, onSubmit, setIncomplete])
useEffect(updateDetailsStore, [methods.formState.isValid, updateDetailsStore]) useEffect(updateDetailsStore, [methods.formState.isValid, updateDetailsStore])

View File

@@ -76,11 +76,13 @@ export default function PaymentClient({
const [showBookingAlert, setShowBookingAlert] = useState(false) const [showBookingAlert, setShowBookingAlert] = useState(false)
const { booking, rooms, totalPrice } = useEnterDetailsStore((state) => ({ const { booking, rooms, totalPrice, preSubmitCallbacks } =
booking: state.booking, useEnterDetailsStore((state) => ({
rooms: state.rooms, booking: state.booking,
totalPrice: state.totalPrice, rooms: state.rooms,
})) totalPrice: state.totalPrice,
preSubmitCallbacks: state.preSubmitCallbacks,
}))
const bookingMustBeGuaranteed = rooms.some(({ room }, idx) => { const bookingMustBeGuaranteed = rooms.some(({ room }, idx) => {
if (idx === 0 && isUserLoggedIn && room.memberMustBeGuaranteed) { if (idx === 0 && isUserLoggedIn && room.memberMustBeGuaranteed) {
@@ -282,6 +284,9 @@ export default function PaymentClient({
const handleSubmit = useCallback( const handleSubmit = useCallback(
(data: PaymentFormData) => { (data: PaymentFormData) => {
Object.values(preSubmitCallbacks).forEach((callback) => {
callback()
})
const firstIncompleteRoomIndex = rooms.findIndex( const firstIncompleteRoomIndex = rooms.findIndex(
(room) => !room.isComplete (room) => !room.isComplete
) )
@@ -445,10 +450,11 @@ export default function PaymentClient({
fromDate, fromDate,
toDate, toDate,
rooms, rooms,
booking, booking.rooms,
getPaymentMethod, getPaymentMethod,
hasOnlyFlexRates, hasOnlyFlexRates,
bookingMustBeGuaranteed, bookingMustBeGuaranteed,
preSubmitCallbacks,
isUserLoggedIn, isUserLoggedIn,
getTopOffset, getTopOffset,
] ]

View File

@@ -355,6 +355,7 @@ export function createDetailsStore(
searchParamString: searchParams, searchParamString: searchParams,
totalPrice: initialTotalPrice, totalPrice: initialTotalPrice,
vat: initialState.vat, vat: initialState.vat,
preSubmitCallbacks: {},
actions: { actions: {
setIsSubmittingDisabled(isSubmittingDisabled) { setIsSubmittingDisabled(isSubmittingDisabled) {
@@ -386,6 +387,13 @@ export function createDetailsStore(
}) })
) )
}, },
addPreSubmitCallback(name, callback) {
return set(
produce((state: DetailsState) => {
state.preSubmitCallbacks[name] = callback
})
)
},
}, },
})) }))
} }

View File

@@ -87,6 +87,7 @@ export interface DetailsState {
setTotalPrice: (totalPrice: Price) => void setTotalPrice: (totalPrice: Price) => void
toggleSummaryOpen: () => void toggleSummaryOpen: () => void
updateSeachParamString: (searchParamString: string) => void updateSeachParamString: (searchParamString: string) => void
addPreSubmitCallback: (name: string, callback: () => void) => void
} }
booking: SelectRateSearchParams booking: SelectRateSearchParams
breakfastPackages: BreakfastPackages breakfastPackages: BreakfastPackages
@@ -98,6 +99,7 @@ export interface DetailsState {
searchParamString: string searchParamString: string
totalPrice: Price totalPrice: Price
vat: number vat: number
preSubmitCallbacks: Record<string, () => void>
} }
export type PersistedState = { export type PersistedState = {