Refactor password sign-up flow and improve form usability (#131)

- Extracted sign-up logic into a new `usePasswordSignUpFlow` hook
- Simplified `EmailPasswordSignUpContainer` component
- Added `autoComplete="new-password"` to password input for better UX
- Converted `PasswordSignUpForm` props to a TypeScript interface
This commit is contained in:
Giancarlo Buomprisco
2025-02-03 11:34:26 +07:00
committed by GitHub
parent 5622572f36
commit 001903ddac
3 changed files with 114 additions and 61 deletions

View File

@@ -1,16 +1,13 @@
'use client';
import { useCallback, useRef, useState } from 'react';
import { CheckCircledIcon } from '@radix-ui/react-icons';
import { useAppEvents } from '@kit/shared/events';
import { useSignUpWithEmailAndPassword } from '@kit/supabase/hooks/use-sign-up-with-email-password';
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
import { If } from '@kit/ui/if';
import { Trans } from '@kit/ui/trans';
import { useCaptchaToken } from '../captcha/client';
import { usePasswordSignUpFlow } from '../hooks/use-sign-up-flow';
import { AuthErrorAlert } from './auth-error-alert';
import { PasswordSignUpForm } from './password-sign-up-form';
@@ -19,7 +16,6 @@ interface EmailPasswordSignUpContainerProps {
defaultValues?: {
email: string;
};
onSignUp?: (userId?: string) => unknown;
emailRedirectTo: string;
}
@@ -32,54 +28,17 @@ export function EmailPasswordSignUpContainer({
}: EmailPasswordSignUpContainerProps) {
const { captchaToken, resetCaptchaToken } = useCaptchaToken();
const signUpMutation = useSignUpWithEmailAndPassword();
const redirecting = useRef(false);
const [showVerifyEmailAlert, setShowVerifyEmailAlert] = useState(false);
const appEvents = useAppEvents();
const loading = signUpMutation.isPending || redirecting.current;
const onSignupRequested = useCallback(
async (credentials: { email: string; password: string }) => {
if (loading) {
return;
}
try {
const data = await signUpMutation.mutateAsync({
...credentials,
emailRedirectTo,
captchaToken,
});
appEvents.emit({
type: 'user.signedUp',
payload: {
method: 'password',
},
});
setShowVerifyEmailAlert(true);
if (onSignUp) {
onSignUp(data.user?.id);
}
} catch (error) {
console.error(error);
} finally {
resetCaptchaToken();
}
},
[
appEvents,
captchaToken,
emailRedirectTo,
loading,
onSignUp,
resetCaptchaToken,
signUpMutation,
],
);
const {
signUp: onSignupRequested,
loading,
error,
showVerifyEmailAlert,
} = usePasswordSignUpFlow({
emailRedirectTo,
onSignUp,
captchaToken,
resetCaptchaToken,
});
return (
<>
@@ -88,7 +47,7 @@ export function EmailPasswordSignUpContainer({
</If>
<If condition={!showVerifyEmailAlert}>
<AuthErrorAlert error={signUpMutation.error} />
<AuthErrorAlert error={error} />
<PasswordSignUpForm
onSubmit={onSignupRequested}

View File

@@ -22,12 +22,7 @@ import { Trans } from '@kit/ui/trans';
import { PasswordSignUpSchema } from '../schemas/password-sign-up.schema';
import { TermsAndConditionsFormField } from './terms-and-conditions-form-field';
export function PasswordSignUpForm({
defaultValues,
displayTermsCheckbox,
onSubmit,
loading,
}: {
interface PasswordSignUpFormProps {
defaultValues?: {
email: string;
};
@@ -40,7 +35,14 @@ export function PasswordSignUpForm({
repeatPassword: string;
}) => unknown;
loading: boolean;
}) {
}
export function PasswordSignUpForm({
defaultValues,
displayTermsCheckbox,
onSubmit,
loading,
}: PasswordSignUpFormProps) {
const { t } = useTranslation();
const form = useForm({
@@ -96,6 +98,7 @@ export function PasswordSignUpForm({
required
data-test={'password-input'}
type="password"
autoComplete="new-password"
placeholder={''}
{...field}
/>

View File

@@ -0,0 +1,91 @@
'use client';
import { useCallback } from 'react';
import { useRouter } from 'next/navigation';
import { useAppEvents } from '@kit/shared/events';
import { useSignUpWithEmailAndPassword } from '@kit/supabase/hooks/use-sign-up-with-email-password';
type SignUpCredentials = {
email: string;
password: string;
};
type UseSignUpFlowProps = {
emailRedirectTo: string;
onSignUp?: (userId?: string) => unknown;
captchaToken?: string;
resetCaptchaToken?: () => void;
};
/**
* @name usePasswordSignUpFlow
* @description
* This hook is used to handle the sign up flow using the email and password method.
*/
export function usePasswordSignUpFlow({
emailRedirectTo,
onSignUp,
captchaToken,
resetCaptchaToken,
}: UseSignUpFlowProps) {
const router = useRouter();
const signUpMutation = useSignUpWithEmailAndPassword();
const appEvents = useAppEvents();
const signUp = useCallback(
async (credentials: SignUpCredentials) => {
if (signUpMutation.isPending) {
return;
}
try {
const data = await signUpMutation.mutateAsync({
...credentials,
emailRedirectTo,
captchaToken,
});
// emit event to track sign up
appEvents.emit({
type: 'user.signedUp',
payload: {
method: 'password',
},
});
// Update URL with success status. This is useful for password managers
// to understand that the form was submitted successfully.
const url = new URL(window.location.href);
url.searchParams.set('status', 'success');
router.replace(url.pathname + url.search);
if (onSignUp) {
onSignUp(data.user?.id);
}
} catch (error) {
console.error(error);
throw error;
} finally {
resetCaptchaToken?.();
}
},
[
signUpMutation,
emailRedirectTo,
captchaToken,
appEvents,
router,
onSignUp,
resetCaptchaToken,
],
);
return {
signUp,
loading: signUpMutation.isPending,
error: signUpMutation.error,
showVerifyEmailAlert: signUpMutation.isSuccess,
};
}