11"use client" ;
22
3- import { useState } from "react" ;
3+ import { useState , useMemo } from "react" ;
44import { Button } from "@/components/ui/button" ;
55import { Input } from "@/components/ui/input" ;
66import { Label } from "@/components/ui/label" ;
@@ -13,7 +13,7 @@ import {
1313} from "@/components/ui/card" ;
1414import { useObjectState } from "@/hooks/use-object-state" ;
1515import { cn } from "lib/utils" ;
16- import { ChevronLeft , Loader } from "lucide-react" ;
16+ import { ChevronLeft , Loader , Check , X } from "lucide-react" ;
1717import { toast } from "sonner" ;
1818import { safe } from "ts-safe" ;
1919import { UserZodSchema } from "app-types/user" ;
@@ -42,6 +42,16 @@ export default function EmailSignUp({
4242 t ( "Auth.SignUp.step3" ) ,
4343 ] ;
4444
45+ // Password validation checklist
46+ const passwordValidation = useMemo ( ( ) => {
47+ const password = formData . password ;
48+ return {
49+ hasMinLength : password . length >= 8 && password . length <= 20 ,
50+ hasLetter : / [ a - z A - Z ] / . test ( password ) ,
51+ hasNumber : / \d / . test ( password ) ,
52+ } ;
53+ } , [ formData . password ] ) ;
54+
4555 const safeProcessWithLoading = function < T > ( fn : ( ) => Promise < T > ) {
4656 setIsLoading ( true ) ;
4757 return safe ( ( ) => fn ( ) ) . watch ( ( ) => setIsLoading ( false ) ) ;
@@ -195,6 +205,58 @@ export default function EmailSignUp({
195205 onChange = { ( e ) => setFormData ( { password : e . target . value } ) }
196206 required
197207 />
208+ { formData . password && (
209+ < div className = "space-y-1 mt-2" >
210+ < div className = "flex items-center gap-2 text-xs" >
211+ { passwordValidation . hasMinLength ? (
212+ < Check className = "size-3 text-primary" />
213+ ) : (
214+ < X className = "size-3 text-destructive" />
215+ ) }
216+ < span
217+ className = {
218+ passwordValidation . hasMinLength
219+ ? "text-primary"
220+ : "text-muted-foreground"
221+ }
222+ >
223+ 8-20 characters
224+ </ span >
225+ </ div >
226+ < div className = "flex items-center gap-2 text-xs" >
227+ { passwordValidation . hasLetter ? (
228+ < Check className = "size-3 text-primary" />
229+ ) : (
230+ < X className = "size-3 text-destructive" />
231+ ) }
232+ < span
233+ className = {
234+ passwordValidation . hasLetter
235+ ? "text-primary"
236+ : "text-muted-foreground"
237+ }
238+ >
239+ At least one letter
240+ </ span >
241+ </ div >
242+ < div className = "flex items-center gap-2 text-xs" >
243+ { passwordValidation . hasNumber ? (
244+ < Check className = "size-3 text-primary" />
245+ ) : (
246+ < X className = "size-3 text-destructive" />
247+ ) }
248+ < span
249+ className = {
250+ passwordValidation . hasNumber
251+ ? "text-primary"
252+ : "text-muted-foreground"
253+ }
254+ >
255+ At least one number
256+ </ span >
257+ </ div >
258+ </ div >
259+ ) }
198260 </ div >
199261 ) }
200262 < p className = "text-muted-foreground text-xs mb-6" >
0 commit comments