feat: refactor onboarding into a multi-step flow with dedicated routes and session state management

This commit is contained in:
Jose Selesan
2026-04-17 16:17:19 -03:00
parent 4588e63fa5
commit 20b70c06c1
12 changed files with 617 additions and 382 deletions

View File

@@ -0,0 +1,79 @@
import { OnboardingLayout } from '@/features/onboard/components/onboarding-layout';
import { OnboardingStartStep } from '@/features/onboard/components/onboarding-start-step';
import {
OTP_MAX_ATTEMPTS,
defaultCompleteFormValues,
defaultVerifyOtpFormValues,
getOnboardingSessionState,
updateOnboardingSessionState,
} from '@/features/onboard/onboarding-session';
import type { StartValues } from '@/features/onboard/onboarding.types';
import { apiClient } from '@/lib/api-client';
import { zodResolver } from '@hookform/resolvers/zod';
import { onboardingStartSchema } from '@repo/api-contract';
import { useNavigate } from '@tanstack/react-router';
import { useEffect, useMemo, useState } from 'react';
import { useForm, useWatch } from 'react-hook-form';
export function OnboardStartPage() {
const navigate = useNavigate();
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [infoMessage, setInfoMessage] = useState<string | null>(null);
const initialState = useMemo(() => getOnboardingSessionState(), []);
const form = useForm<StartValues>({
resolver: zodResolver(onboardingStartSchema),
mode: 'onChange',
defaultValues: initialState.startForm,
});
const values = useWatch({ control: form.control });
useEffect(() => {
updateOnboardingSessionState((current) => ({
...current,
startForm: {
fullName: values.fullName ?? '',
email: values.email ?? '',
},
}));
}, [values.email, values.fullName]);
const onSubmit = async (payload: StartValues) => {
setErrorMessage(null);
setInfoMessage(null);
try {
const result = await apiClient.onboarding.start(payload);
updateOnboardingSessionState((current) => ({
...current,
requestId: result.requestId,
onboardingEmail: result.email,
verifiedEmail: null,
remainingAttempts: OTP_MAX_ATTEMPTS,
resendCooldownSeconds: result.cooldownSeconds,
startForm: {
fullName: payload.fullName,
email: payload.email,
},
verifyOtpForm: defaultVerifyOtpFormValues,
completeForm: defaultCompleteFormValues,
}));
await navigate({ to: '/onboard/verify' });
} catch {
setErrorMessage('No pudimos iniciar el onboarding. Intenta nuevamente.');
}
};
return (
<OnboardingLayout title="Onboarding" description="Ingresa tu nombre y email para comenzar.">
<OnboardingStartStep
form={form}
errorMessage={errorMessage}
infoMessage={infoMessage}
onSubmit={onSubmit}
/>
</OnboardingLayout>
);
}