Merged in fix/BOOK-323-enter-details-scroll-error (pull request #2986)
Fix/BOOK-323 enter details scroll error * fix(BOOK-323): scroll to invalid element on submit on enter details * fix(BOOK-323): update error message design * fix(BOOK-323): clean up * fix(BOOK-323): scroll to fields in room in right order * fix(BOOK-323): add id to translations * fix(BOOK-323): remove undefined * fix(BOOK-323): fix submitting state * fix(BOOK-323): use ref in multiroom for scrolling to right element, add membershipNo * fix(BOOK-323): fix invalid border country * fix(BOOK-323): use error message component * fix(BOOK-323): fix invalid focused styling on mobile * fix(BOOK-323): remove redundant dependency in callback Approved-by: Erik Tiekstra
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useCallback, useEffect, useMemo } from "react"
|
||||
import { useCallback, useEffect, useMemo, useRef } from "react"
|
||||
import { FormProvider, useForm } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
@@ -32,6 +32,7 @@ export default function Details() {
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
const config = useBookingFlowConfig()
|
||||
const refs = useRef<Record<string, HTMLElement | null>>({})
|
||||
|
||||
const { addPreSubmitCallback, rooms } = useEnterDetailsStore((state) => ({
|
||||
addPreSubmitCallback: state.actions.addPreSubmitCallback,
|
||||
@@ -106,12 +107,32 @@ export default function Details() {
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
function callback() {
|
||||
trigger()
|
||||
async function callback() {
|
||||
await trigger()
|
||||
trackFormSubmit()
|
||||
const fieldOrder = [
|
||||
"firstName",
|
||||
"lastName",
|
||||
"countryCode",
|
||||
"email",
|
||||
"phoneNumber",
|
||||
"membershipNo",
|
||||
]
|
||||
for (const name of fieldOrder) {
|
||||
const fieldError =
|
||||
methods.formState.errors[
|
||||
name as keyof typeof methods.formState.errors
|
||||
]
|
||||
if (fieldError && refs.current[name]) {
|
||||
return refs.current[name] ?? undefined
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
addPreSubmitCallback(`${idx}-details`, callback)
|
||||
}, [addPreSubmitCallback, idx, trigger, trackFormSubmit])
|
||||
}, [addPreSubmitCallback, idx, trigger, trackFormSubmit, methods])
|
||||
|
||||
const updateDetailsStore = useCallback(() => {
|
||||
if (isValid) {
|
||||
@@ -188,88 +209,124 @@ export default function Details() {
|
||||
defaultMessage: "Guest information",
|
||||
})}
|
||||
</Footnote>
|
||||
<BookingFlowInput
|
||||
label={intl.formatMessage({
|
||||
id: "common.firstName",
|
||||
defaultMessage: "First name",
|
||||
})}
|
||||
maxLength={30}
|
||||
name="firstName"
|
||||
registerOptions={{
|
||||
required: true,
|
||||
deps: "lastName",
|
||||
onBlur: updateDetailsStore,
|
||||
<div
|
||||
ref={(el) => {
|
||||
refs.current.firstName = el
|
||||
}}
|
||||
/>
|
||||
<BookingFlowInput
|
||||
label={intl.formatMessage({
|
||||
id: "common.lastName",
|
||||
defaultMessage: "Last name",
|
||||
})}
|
||||
maxLength={30}
|
||||
name="lastName"
|
||||
registerOptions={{
|
||||
required: true,
|
||||
deps: "firstName",
|
||||
onBlur: updateDetailsStore,
|
||||
}}
|
||||
/>
|
||||
<CountrySelect
|
||||
className={styles.fullWidth}
|
||||
countries={getFormattedCountryList(intl)}
|
||||
errorMessage={getErrorMessage(
|
||||
intl,
|
||||
config.variant,
|
||||
errors.countryCode?.message
|
||||
)}
|
||||
label={intl.formatMessage({
|
||||
id: "common.country",
|
||||
defaultMessage: "Country",
|
||||
})}
|
||||
lang={lang}
|
||||
name="countryCode"
|
||||
registerOptions={{ required: true, onBlur: updateDetailsStore }}
|
||||
/>
|
||||
<BookingFlowInput
|
||||
className={styles.fullWidth}
|
||||
label={intl.formatMessage({
|
||||
id: "common.emailAddress",
|
||||
defaultMessage: "Email address",
|
||||
})}
|
||||
name="email"
|
||||
registerOptions={{ required: true, onBlur: updateDetailsStore }}
|
||||
/>
|
||||
<Phone
|
||||
countryLabel={intl.formatMessage({
|
||||
id: "common.countryCode",
|
||||
defaultMessage: "Country code",
|
||||
})}
|
||||
countriesWithTranslatedName={getFormattedCountryList(intl)}
|
||||
defaultCountryCode={getDefaultCountryFromLang(lang)}
|
||||
errorMessage={getErrorMessage(
|
||||
intl,
|
||||
config.variant,
|
||||
errors.phoneNumber?.message
|
||||
)}
|
||||
className={styles.fullWidth}
|
||||
label={intl.formatMessage({
|
||||
id: "common.phoneNumber",
|
||||
defaultMessage: "Phone number",
|
||||
})}
|
||||
name="phoneNumber"
|
||||
registerOptions={{ required: true, onBlur: updateDetailsStore }}
|
||||
/>
|
||||
{showMembershipIdInput ? (
|
||||
>
|
||||
<BookingFlowInput
|
||||
className={styles.fullWidth}
|
||||
label={intl.formatMessage({
|
||||
id: "common.membershipId",
|
||||
defaultMessage: "Membership ID",
|
||||
id: "common.firstName",
|
||||
defaultMessage: "First name",
|
||||
})}
|
||||
name="membershipNo"
|
||||
type="tel"
|
||||
registerOptions={{ onBlur: updateDetailsStore }}
|
||||
maxLength={30}
|
||||
name="firstName"
|
||||
registerOptions={{
|
||||
required: true,
|
||||
deps: "lastName",
|
||||
onBlur: updateDetailsStore,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
ref={(el) => {
|
||||
refs.current.lastName = el
|
||||
}}
|
||||
>
|
||||
<BookingFlowInput
|
||||
label={intl.formatMessage({
|
||||
id: "common.lastName",
|
||||
defaultMessage: "Last name",
|
||||
})}
|
||||
maxLength={30}
|
||||
name="lastName"
|
||||
registerOptions={{
|
||||
required: true,
|
||||
deps: "firstName",
|
||||
onBlur: updateDetailsStore,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
ref={(el) => {
|
||||
refs.current.countryCode = el
|
||||
}}
|
||||
className={styles.fullWidth}
|
||||
>
|
||||
<CountrySelect
|
||||
countries={getFormattedCountryList(intl)}
|
||||
errorMessage={getErrorMessage(
|
||||
intl,
|
||||
config.variant,
|
||||
errors.countryCode?.message
|
||||
)}
|
||||
label={intl.formatMessage({
|
||||
id: "common.country",
|
||||
defaultMessage: "Country",
|
||||
})}
|
||||
lang={lang}
|
||||
name="countryCode"
|
||||
registerOptions={{ required: true, onBlur: updateDetailsStore }}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
ref={(el) => {
|
||||
refs.current.email = el
|
||||
}}
|
||||
className={styles.fullWidth}
|
||||
>
|
||||
<BookingFlowInput
|
||||
label={intl.formatMessage({
|
||||
id: "common.emailAddress",
|
||||
defaultMessage: "Email address",
|
||||
})}
|
||||
name="email"
|
||||
registerOptions={{ required: true, onBlur: updateDetailsStore }}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
ref={(el) => {
|
||||
refs.current.phoneNumber = el
|
||||
}}
|
||||
className={styles.fullWidth}
|
||||
>
|
||||
<Phone
|
||||
countryLabel={intl.formatMessage({
|
||||
id: "common.countryCode",
|
||||
defaultMessage: "Country code",
|
||||
})}
|
||||
countriesWithTranslatedName={getFormattedCountryList(intl)}
|
||||
defaultCountryCode={getDefaultCountryFromLang(lang)}
|
||||
errorMessage={getErrorMessage(
|
||||
intl,
|
||||
config.variant,
|
||||
errors.phoneNumber?.message
|
||||
)}
|
||||
label={intl.formatMessage({
|
||||
id: "common.phoneNumber",
|
||||
defaultMessage: "Phone number",
|
||||
})}
|
||||
name="phoneNumber"
|
||||
registerOptions={{ required: true, onBlur: updateDetailsStore }}
|
||||
/>
|
||||
</div>
|
||||
{showMembershipIdInput ? (
|
||||
<div
|
||||
ref={(el) => {
|
||||
refs.current.membershipNo = el
|
||||
}}
|
||||
className={styles.fullWidth}
|
||||
>
|
||||
<BookingFlowInput
|
||||
label={intl.formatMessage({
|
||||
id: "common.membershipId",
|
||||
defaultMessage: "Membership ID",
|
||||
})}
|
||||
name="membershipNo"
|
||||
type="tel"
|
||||
registerOptions={{ onBlur: updateDetailsStore }}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<SpecialRequests registerOptions={{ onBlur: updateDetailsStore }} />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user