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:
committed by
GitHub
parent
5622572f36
commit
001903ddac
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
91
packages/features/auth/src/hooks/use-sign-up-flow.ts
Normal file
91
packages/features/auth/src/hooks/use-sign-up-flow.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user