@@ -9,6 +9,8 @@ import { Icons } from "@/components/ui/icons";
9
9
import { Button } from "@/components/ui/button" ;
10
10
import { Input } from "@/components/ui/input" ;
11
11
import { Label } from "@/components/ui/label" ;
12
+ import { HoverCard , HoverCardContent , HoverCardTrigger } from "@/components/ui/hover-card" ;
13
+ import { Check , X } from "lucide-react" ;
12
14
13
15
interface UserAuthFormProps extends React . HTMLAttributes < HTMLDivElement > { }
14
16
interface PasswordRules {
@@ -31,8 +33,15 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
31
33
const [ usernameError , setUsernameError ] = useState < string > ( "" ) ;
32
34
const [ passwordRules , setPasswordRules ] = useState < PasswordRules | null > ( null ) ;
33
35
const [ isPasswordValid , setIsPasswordValid ] = useState < boolean > ( false ) ;
36
+ const allRulesPassed = passwordRules && Object . values ( passwordRules ) . every ( ( passed ) => passed === true ) ;
37
+ const [ hasStartedTyping , setHasStartedTyping ] = useState ( false ) ;
34
38
const navigate = useNavigate ( ) ;
35
39
40
+ const handleInputChange = ( e : React . ChangeEvent < HTMLInputElement > ) => {
41
+ setHasStartedTyping ( true ) ;
42
+ handlePasswordChange ( e ) ;
43
+ } ;
44
+
36
45
// Username availability check with debounce
37
46
useEffect ( ( ) => {
38
47
const checkUsernameAvailability = async ( ) => {
@@ -157,26 +166,57 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
157
166
< Label className = "sr-only" htmlFor = "username" >
158
167
Username
159
168
</ Label >
160
- < Input
161
- id = "username"
162
- placeholder = "Username"
163
- type = "text"
164
- autoCapitalize = "none"
165
- autoComplete = "username"
166
- autoCorrect = "off"
167
- disabled = { isLoading }
168
- value = { username }
169
- onChange = { ( e ) => setUsername ( e . target . value ) }
170
- className = 'dark:bg-zinc-900'
171
- required
172
- />
173
- { isUsernameAvailable === false && (
174
- < p className = "text-red-500" > Username is not available</ p >
175
- ) }
176
- { isUsernameAvailable === true && (
177
- < p className = "text-green-500" > Username is available</ p >
178
- ) }
179
- { usernameError && < p className = "text-red-500" > { usernameError } </ p > }
169
+
170
+ { /* Container for input and icon */ }
171
+ < div className = "relative flex items-center" >
172
+ < Input
173
+ id = "username"
174
+ placeholder = "Username"
175
+ type = "text"
176
+ autoCapitalize = "none"
177
+ autoComplete = "username"
178
+ autoCorrect = "off"
179
+ disabled = { isLoading }
180
+ value = { username }
181
+ onChange = { ( e ) => setUsername ( e . target . value ) }
182
+ className = "dark:bg-zinc-900 pr-10" // Add padding to the right for the icon
183
+ required
184
+ />
185
+
186
+ { /* HoverCard for the green tick */ }
187
+ { isUsernameAvailable && (
188
+ < HoverCard >
189
+ < HoverCardTrigger asChild >
190
+ < Check className = "absolute right-3 text-green-500" />
191
+ </ HoverCardTrigger >
192
+ < HoverCardContent className = "text-green-500 text-sm bg-zinc-900" >
193
+ Username is available
194
+ </ HoverCardContent >
195
+ </ HoverCard >
196
+ ) }
197
+ { isUsernameAvailable === false && (
198
+ < HoverCard >
199
+ < HoverCardTrigger asChild >
200
+ < X className = "absolute right-3 text-red-500" />
201
+ </ HoverCardTrigger >
202
+ < HoverCardContent className = "text-red-500 text-sm bg-zinc-900" >
203
+ Username is not available
204
+ Username is not available
205
+ </ HoverCardContent >
206
+ </ HoverCard >
207
+ ) }
208
+
209
+ { usernameError && (
210
+ < HoverCard >
211
+ < HoverCardTrigger asChild >
212
+ < X className = "absolute right-3 text-red-500" />
213
+ </ HoverCardTrigger >
214
+ < HoverCardContent className = "text-red-500 text-sm bg-zinc-900" >
215
+ { usernameError }
216
+ </ HoverCardContent >
217
+ </ HoverCard >
218
+ ) }
219
+ </ div >
180
220
</ div >
181
221
< div className = "grid gap-1" >
182
222
< Label className = "sr-only" htmlFor = "email" >
@@ -200,26 +240,55 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
200
240
< Label className = "sr-only" htmlFor = "password" >
201
241
Password
202
242
</ Label >
203
- < Input
204
- id = "password"
205
- placeholder = "Password"
206
- type = "password"
207
- autoComplete = "current-password"
208
- disabled = { isLoading }
209
- value = { password }
210
- onChange = { handlePasswordChange }
211
- className = 'dark:bg-zinc-900'
212
- required
213
- />
214
- { passwordRules &&
215
- Object . entries ( passwordRules ) . map ( ( [ rule , passed ] ) => (
216
- < p
217
- key = { rule }
218
- className = { passed ? "text-green-500" : "text-red-500" }
219
- >
220
- { passwordRuleMessages [ rule as keyof PasswordRules ] }
221
- </ p >
222
- ) ) }
243
+
244
+ < div className = "relative flex items-center" >
245
+ < Input
246
+ id = "password"
247
+ placeholder = "Password"
248
+ type = "password"
249
+ autoComplete = "current-password"
250
+ disabled = { isLoading }
251
+ value = { password }
252
+ onChange = { handleInputChange }
253
+ className = "dark:bg-zinc-900 pr-10"
254
+ required
255
+ />
256
+
257
+ { /* Show the validation only after user has started typing */ }
258
+ { hasStartedTyping && (
259
+ < >
260
+ { allRulesPassed ? (
261
+ < HoverCard >
262
+ < HoverCardTrigger asChild >
263
+ < Check className = "absolute right-3 text-green-500" />
264
+ </ HoverCardTrigger >
265
+ < HoverCardContent className = "text-green-500 text-sm bg-zinc-900" >
266
+ { passwordRules &&
267
+ Object . entries ( passwordRules ) . map ( ( [ rule , passed ] ) => (
268
+ < p key = { rule } className = { passed ? "text-green-500" : "text-red-500" } >
269
+ { passwordRuleMessages [ rule as keyof typeof passwordRuleMessages ] }
270
+ </ p >
271
+ ) ) }
272
+ </ HoverCardContent >
273
+ </ HoverCard >
274
+ ) : (
275
+ < HoverCard >
276
+ < HoverCardTrigger asChild >
277
+ < X className = "absolute right-3 text-red-500" />
278
+ </ HoverCardTrigger >
279
+ < HoverCardContent className = "text-red-500 text-sm bg-zinc-900" >
280
+ { passwordRules &&
281
+ Object . entries ( passwordRules ) . map ( ( [ rule , passed ] ) => (
282
+ < p key = { rule } className = { passed ? "text-green-500" : "text-red-500" } >
283
+ { passwordRuleMessages [ rule as keyof typeof passwordRuleMessages ] }
284
+ </ p >
285
+ ) ) }
286
+ </ HoverCardContent >
287
+ </ HoverCard >
288
+ ) }
289
+ </ >
290
+ ) }
291
+ </ div >
223
292
</ div >
224
293
< Button
225
294
disabled = {
0 commit comments