Add visible password requirements with live validation checklist
- Add PasswordRequirements component showing rules with live checkmarks - Add PASSWORD_RULES and PASSWORD_VALIDATION constants - Update sign-up and reset-password forms to show requirements
This commit is contained in:
parent
e06802738c
commit
f2d72d8825
|
|
@ -7,9 +7,11 @@ import { toast } from 'sonner';
|
|||
|
||||
import FooterLink from '@/components/forms/FooterLink';
|
||||
import InputField from '@/components/forms/InputField';
|
||||
import PasswordRequirements from '@/components/forms/PasswordRequirements';
|
||||
import OpenDevSocietyBranding from '@/components/OpenDevSocietyBranding';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { resetPasswordWithToken } from '@/lib/actions/auth.actions';
|
||||
import { PASSWORD_VALIDATION } from '@/lib/constants';
|
||||
|
||||
type ResetPasswordFormData = {
|
||||
newPassword: string;
|
||||
|
|
@ -86,8 +88,9 @@ const ResetPasswordForm = () => {
|
|||
type="password"
|
||||
register={register}
|
||||
error={errors.newPassword}
|
||||
validation={{ required: 'New password is required', minLength: 8 }}
|
||||
validation={PASSWORD_VALIDATION}
|
||||
/>
|
||||
<PasswordRequirements password={newPassword ?? ''} />
|
||||
|
||||
<InputField
|
||||
name="confirmPassword"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ import { useForm } from "react-hook-form";
|
|||
import { Button } from "@/components/ui/button";
|
||||
import InputField from "@/components/forms/InputField";
|
||||
import SelectField from "@/components/forms/SelectField";
|
||||
import { INVESTMENT_GOALS, PREFERRED_INDUSTRIES, RISK_TOLERANCE_OPTIONS } from "@/lib/constants";
|
||||
import PasswordRequirements from "@/components/forms/PasswordRequirements";
|
||||
import { INVESTMENT_GOALS, PASSWORD_VALIDATION, PREFERRED_INDUSTRIES, RISK_TOLERANCE_OPTIONS } from "@/lib/constants";
|
||||
import { CountrySelectField } from "@/components/forms/CountrySelectField";
|
||||
import FooterLink from "@/components/forms/FooterLink";
|
||||
import { signUpWithEmail } from "@/lib/actions/auth.actions";
|
||||
|
|
@ -19,6 +20,7 @@ const SignUp = () => {
|
|||
register,
|
||||
handleSubmit,
|
||||
control,
|
||||
watch,
|
||||
formState: { errors, isSubmitting },
|
||||
} = useForm<SignUpFormData>({
|
||||
defaultValues: {
|
||||
|
|
@ -33,6 +35,8 @@ const SignUp = () => {
|
|||
mode: 'onBlur'
|
||||
},);
|
||||
|
||||
const passwordValue = watch('password');
|
||||
|
||||
const onSubmit = async (data: SignUpFormData) => {
|
||||
try {
|
||||
const result = await signUpWithEmail(data);
|
||||
|
|
@ -87,8 +91,9 @@ const SignUp = () => {
|
|||
type="password"
|
||||
register={register}
|
||||
error={errors.password}
|
||||
validation={{ required: 'Password is required', minLength: 8 }}
|
||||
validation={PASSWORD_VALIDATION}
|
||||
/>
|
||||
<PasswordRequirements password={passwordValue ?? ''} />
|
||||
|
||||
<CountrySelectField
|
||||
name="country"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { PASSWORD_RULES } from '@/lib/constants';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Check, X } from 'lucide-react';
|
||||
|
||||
const PasswordRequirements = ({ password }: { password: string }) => {
|
||||
return (
|
||||
<ul className="space-y-1.5 mt-2">
|
||||
{PASSWORD_RULES.map((rule) => {
|
||||
const passed = rule.test(password);
|
||||
return (
|
||||
<li key={rule.label} className="flex items-center gap-2 text-xs">
|
||||
{password.length === 0 ? (
|
||||
<span className="size-3.5 rounded-full border border-gray-500" />
|
||||
) : passed ? (
|
||||
<Check className="size-3.5 text-green-500" />
|
||||
) : (
|
||||
<X className="size-3.5 text-red-500" />
|
||||
)}
|
||||
<span
|
||||
className={cn(
|
||||
'transition-colors',
|
||||
password.length === 0 && 'text-gray-500',
|
||||
passed ? 'text-green-500' : password.length > 0 && 'text-red-500',
|
||||
)}
|
||||
>
|
||||
{rule.label}
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
export default PasswordRequirements;
|
||||
|
|
@ -338,3 +338,19 @@ export const WATCHLIST_TABLE_HEADER = [
|
|||
'Alert',
|
||||
'Action',
|
||||
];
|
||||
|
||||
export const PASSWORD_RULES = [
|
||||
{ label: 'At least 8 characters', test: (pw: string) => pw.length >= 8 },
|
||||
{ label: 'At least 1 uppercase letter', test: (pw: string) => /[A-Z]/.test(pw) },
|
||||
{ label: 'At least 1 lowercase letter', test: (pw: string) => /[a-z]/.test(pw) },
|
||||
{ label: 'At least 1 number', test: (pw: string) => /[0-9]/.test(pw) },
|
||||
] as const;
|
||||
|
||||
export const PASSWORD_VALIDATION = {
|
||||
required: 'Password is required',
|
||||
minLength: { value: 8, message: 'Password must be at least 8 characters' },
|
||||
pattern: {
|
||||
value: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/,
|
||||
message: 'Password must include uppercase, lowercase, and a number',
|
||||
},
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue