refactor: di chuyển các trường nhập liệu sang sử dụng Controller của React Hook Form.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { z } from 'zod';
|
||||
import { useRouter } from 'next/navigation';
|
||||
@@ -12,6 +12,7 @@ import { Input } from '@/features/shared/components/ui/input';
|
||||
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from '@/features/shared/components/ui/card';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { AuthControls } from '@/features/shared/components/layout/auth-controls';
|
||||
import { cn } from '@/shared/utils';
|
||||
|
||||
/**
|
||||
* EN: Create register schema with translated messages
|
||||
@@ -151,10 +152,9 @@ export default function RegisterPage() {
|
||||
// EN: React Hook Form setup with Zod resolver
|
||||
// VI: Setup React Hook Form với Zod resolver
|
||||
const {
|
||||
register,
|
||||
control,
|
||||
handleSubmit,
|
||||
watch,
|
||||
setValue,
|
||||
formState: { errors, isSubmitting },
|
||||
} = useForm<RegisterFormData>({
|
||||
resolver: zodResolver(registerSchema),
|
||||
@@ -291,52 +291,64 @@ export default function RegisterPage() {
|
||||
|
||||
<div className="space-y-4">
|
||||
{/* EN: Full name input field / VI: Trường nhập họ tên */}
|
||||
<Input
|
||||
type="text"
|
||||
label={t('auth.register.fullName')}
|
||||
placeholder="John Doe"
|
||||
isInvalid={!!errors.fullName}
|
||||
errorMessage={errors.fullName?.message}
|
||||
{...register('fullName')}
|
||||
onChange={(e) => {
|
||||
register('fullName').onChange(e);
|
||||
setValue('fullName', e.target.value);
|
||||
}}
|
||||
autoComplete="name"
|
||||
aria-required="true"
|
||||
<Controller
|
||||
name="fullName"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
type="text"
|
||||
label={t('auth.register.fullName')}
|
||||
placeholder="John Doe"
|
||||
isInvalid={!!errors.fullName}
|
||||
errorMessage={errors.fullName?.message}
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
onBlur={field.onBlur}
|
||||
autoComplete="name"
|
||||
aria-required="true"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* EN: Email input field / VI: Trường nhập email */}
|
||||
<Input
|
||||
type="email"
|
||||
label={t('auth.register.email')}
|
||||
placeholder="you@example.com"
|
||||
isInvalid={!!errors.email}
|
||||
errorMessage={errors.email?.message}
|
||||
{...register('email')}
|
||||
onChange={(e) => {
|
||||
register('email').onChange(e);
|
||||
setValue('email', e.target.value);
|
||||
}}
|
||||
autoComplete="email"
|
||||
aria-required="true"
|
||||
<Controller
|
||||
name="email"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
type="email"
|
||||
label={t('auth.register.email')}
|
||||
placeholder="you@example.com"
|
||||
isInvalid={!!errors.email}
|
||||
errorMessage={errors.email?.message}
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
onBlur={field.onBlur}
|
||||
autoComplete="email"
|
||||
aria-required="true"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* EN: Password input field / VI: Trường nhập mật khẩu */}
|
||||
<div className="space-y-2">
|
||||
<Input
|
||||
type="password"
|
||||
label={t('auth.register.password')}
|
||||
placeholder={t('auth.register.createStrongPassword')}
|
||||
isInvalid={!!errors.password}
|
||||
errorMessage={errors.password?.message}
|
||||
{...register('password')}
|
||||
onChange={(e) => {
|
||||
register('password').onChange(e);
|
||||
setValue('password', e.target.value);
|
||||
}}
|
||||
autoComplete="new-password"
|
||||
aria-required="true"
|
||||
<Controller
|
||||
name="password"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
type="password"
|
||||
label={t('auth.register.password')}
|
||||
placeholder={t('auth.register.createStrongPassword')}
|
||||
isInvalid={!!errors.password}
|
||||
errorMessage={errors.password?.message}
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
onBlur={field.onBlur}
|
||||
autoComplete="new-password"
|
||||
aria-required="true"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* EN: Password strength indicator / VI: Chỉ báo độ mạnh mật khẩu */}
|
||||
@@ -372,52 +384,64 @@ export default function RegisterPage() {
|
||||
</div>
|
||||
|
||||
{/* EN: Confirm password input field / VI: Trường xác nhận mật khẩu */}
|
||||
<Input
|
||||
type="password"
|
||||
label={t('auth.register.confirmPassword')}
|
||||
placeholder={t('auth.register.reEnterPassword')}
|
||||
isInvalid={!!errors.confirmPassword}
|
||||
errorMessage={errors.confirmPassword?.message}
|
||||
{...register('confirmPassword')}
|
||||
onChange={(e) => {
|
||||
register('confirmPassword').onChange(e);
|
||||
setValue('confirmPassword', e.target.value);
|
||||
}}
|
||||
autoComplete="new-password"
|
||||
aria-required="true"
|
||||
<Controller
|
||||
name="confirmPassword"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
type="password"
|
||||
label={t('auth.register.confirmPassword')}
|
||||
placeholder={t('auth.register.reEnterPassword')}
|
||||
isInvalid={!!errors.confirmPassword}
|
||||
errorMessage={errors.confirmPassword?.message}
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
onBlur={field.onBlur}
|
||||
autoComplete="new-password"
|
||||
aria-required="true"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* EN: Terms and conditions checkbox / VI: Checkbox điều khoản và điều kiện */}
|
||||
<div className="space-y-2 pt-2">
|
||||
<label className="flex items-start gap-2 cursor-pointer group">
|
||||
<input
|
||||
type="checkbox"
|
||||
{...register('terms')}
|
||||
className="mt-1 w-4 h-4 rounded border-glass bg-glass-subtle text-accent-primary focus:ring-2 focus:ring-accent-primary focus:ring-offset-2 focus:ring-offset-bg-primary cursor-pointer flex-shrink-0 transition-all"
|
||||
aria-required="true"
|
||||
aria-invalid={errors.terms ? 'true' : 'false'}
|
||||
/>
|
||||
<span className="text-sm text-text-secondary group-hover:text-text-primary transition-colors">
|
||||
{t('auth.register.agreeToTerms')}{' '}
|
||||
<Link
|
||||
href="/terms"
|
||||
className="text-accent-primary hover:text-accent-primary-hover font-medium transition-colors"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{t('auth.register.termsAndConditions')}
|
||||
</Link>
|
||||
</span>
|
||||
</label>
|
||||
{errors.terms && (
|
||||
<p
|
||||
className="text-sm text-accent-error flex items-center gap-1 ml-6 animate-in fade-in duration-quick"
|
||||
role="alert"
|
||||
>
|
||||
<span>{errors.terms.message}</span>
|
||||
</p>
|
||||
<Controller
|
||||
name="terms"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<div className="space-y-2 pt-2">
|
||||
<label className="flex items-start gap-2 cursor-pointer group">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={field.value}
|
||||
onChange={field.onChange}
|
||||
onBlur={field.onBlur}
|
||||
className="mt-1 w-4 h-4 rounded border-glass bg-glass-subtle text-accent-primary focus:ring-2 focus:ring-accent-primary focus:ring-offset-2 focus:ring-offset-bg-primary cursor-pointer flex-shrink-0 transition-all"
|
||||
aria-required="true"
|
||||
aria-invalid={errors.terms ? 'true' : 'false'}
|
||||
/>
|
||||
<span className="text-sm text-text-secondary group-hover:text-text-primary transition-colors">
|
||||
{t('auth.register.agreeToTerms')}{' '}
|
||||
<Link
|
||||
href="/terms"
|
||||
className="text-accent-primary hover:text-accent-primary-hover font-medium transition-colors"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{t('auth.register.termsAndConditions')}
|
||||
</Link>
|
||||
</span>
|
||||
</label>
|
||||
{errors.terms && (
|
||||
<p
|
||||
className="text-sm text-accent-error flex items-center gap-1 ml-6 animate-in fade-in duration-quick"
|
||||
role="alert"
|
||||
>
|
||||
<span>{errors.terms.message}</span>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* EN: Submit button with loading state / VI: Nút submit với trạng thái loading */}
|
||||
|
||||
Reference in New Issue
Block a user