diff --git a/src/pages/JoinPage/OnboardingPage/Steps/PersonalInfoStep.tsx b/src/pages/JoinPage/OnboardingPage/Steps/PersonalInfoStep.tsx index 30c23d490..d8599ba40 100644 --- a/src/pages/JoinPage/OnboardingPage/Steps/PersonalInfoStep.tsx +++ b/src/pages/JoinPage/OnboardingPage/Steps/PersonalInfoStep.tsx @@ -9,8 +9,15 @@ import { Span, XL, } from '@appquality/unguess-design-system'; -import { Field, FieldProps, Form, Formik, FormikHelpers } from 'formik'; -import { useMemo, useRef } from 'react'; +import { + Field, + FieldProps, + Form, + Formik, + FormikHelpers, + useFormikContext, +} from 'formik'; +import { useCallback, useEffect, useMemo, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate, useSearchParams } from 'react-router-dom'; import { appTheme } from 'src/app/theme'; @@ -46,6 +53,67 @@ interface PersonalInfoFormValues { companySizeId: string; } +const TEXT_FIELDS: (keyof PersonalInfoFormValues)[] = ['name', 'surname']; + +/** + * Syncs browser-autofilled input values into Formik state. + * Chrome autofill often skips React synthetic events, leaving Formik + * unaware of pre-filled values. This component listens for native DOM + * change events (which Chrome does fire for autofill on user interaction) + * and pushes them into Formik. + */ +const AutofillSync = () => { + const { setFieldValue, values } = useFormikContext(); + const valuesRef = useRef(values); + valuesRef.current = values; + + const syncAutofill = useCallback(() => { + TEXT_FIELDS.forEach((fieldName) => { + const input = document.querySelector( + `input[name="${fieldName}"]` + ); + if ( + input && + input.value && + input.value !== valuesRef.current[`${fieldName}`] + ) { + setFieldValue(fieldName, input.value, true); + } + }); + }, [setFieldValue]); + + // Catch Chrome's delayed native change events for autofilled inputs + useEffect(() => { + const handleChange = (e: Event) => { + const target = e.target as HTMLInputElement; + if ( + target.tagName === 'INPUT' && + target.name && + TEXT_FIELDS.includes(target.name as keyof PersonalInfoFormValues) + ) { + if ( + target.value !== + valuesRef.current[target.name as keyof PersonalInfoFormValues] + ) { + setFieldValue(target.name, target.value, true); + } + } + }; + + document.addEventListener('change', handleChange, true); + return () => document.removeEventListener('change', handleChange, true); + }, [setFieldValue]); + + // Also check once shortly after mount for autofill that happened before + // any user interaction + useEffect(() => { + const timer = setTimeout(syncAutofill, 300); + return () => clearTimeout(timer); + }, [syncAutofill]); + + return null; +}; + export const PersonalInfoStep = () => { const { t } = useTranslation(); const { data, userData, updateData, setStep } = useOnboarding(); @@ -202,9 +270,11 @@ export const PersonalInfoStep = () => { initialValues={initialValues} validationSchema={getPersonalInfoValidationSchema(t)} onSubmit={handleSubmit} + validateOnMount > - {({ isSubmitting, isValid, dirty, setFieldValue }) => ( + {({ isSubmitting, isValid, setFieldValue }) => (
+ {({ field, meta }: FieldProps) => { @@ -274,7 +344,6 @@ export const PersonalInfoStep = () => { { } onSelect={(sizeId) => { - setFieldValue('companySizeId', Number(sizeId)); + setFieldValue('companySizeId', Number(sizeId), true); ( selectRef.current?.querySelector( '[role="combobox"]' @@ -377,7 +445,7 @@ export const PersonalInfoStep = () => { isAccent isStretched size="medium" - disabled={isSubmitting || !isValid || !dirty} + disabled={isSubmitting || !isValid} > {isSubmitting ? t('LOADING') : t('SIGNUP_FORM_NEXT_STEP')} diff --git a/src/pages/JoinPage/OnboardingPage/Steps/WorkspaceStep.tsx b/src/pages/JoinPage/OnboardingPage/Steps/WorkspaceStep.tsx index 31468e7db..2ba3d6007 100644 --- a/src/pages/JoinPage/OnboardingPage/Steps/WorkspaceStep.tsx +++ b/src/pages/JoinPage/OnboardingPage/Steps/WorkspaceStep.tsx @@ -155,8 +155,9 @@ export const WorkspaceStep = () => { initialValues={initialValues} validationSchema={getWorkspaceValidationSchema(t)} onSubmit={handleSubmit} + validateOnMount > - {({ isSubmitting, isValid, dirty }) => ( + {({ isSubmitting, isValid }) => ( @@ -196,7 +197,7 @@ export const WorkspaceStep = () => { isAccent isStretched size="medium" - disabled={isSubmitting || !isValid || !dirty} + disabled={isSubmitting || !isValid} > {isSubmitting ? t('LOADING') : t('SIGNUP_FORM_SUBMIT')} diff --git a/src/pages/Profile/parts/MfaAccordion/steps/AuthenticatorStep.tsx b/src/pages/Profile/parts/MfaAccordion/steps/AuthenticatorStep.tsx index ea1f74822..d1381689d 100644 --- a/src/pages/Profile/parts/MfaAccordion/steps/AuthenticatorStep.tsx +++ b/src/pages/Profile/parts/MfaAccordion/steps/AuthenticatorStep.tsx @@ -63,8 +63,8 @@ export const AuthenticatorStep = ({ setSecretKey(totpDetails.sharedSecret); const totpUri = totpDetails.getSetupUri( - userEmail || 'UNGUESS', - 'UNGUESS' + 'UNGUESS', + userEmail || 'UNGUESS' ); const generatedQrCodeUrl = await QRCode.toDataURL(totpUri.href); setQrCodeUrl(generatedQrCodeUrl);