@@ -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,6 +33,7 @@ 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 ) ;
34
37
const navigate = useNavigate ( ) ;
35
38
36
39
// Username availability check with debounce
@@ -157,26 +160,57 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
157
160
< Label className = "sr-only" htmlFor = "username" >
158
161
Username
159
162
</ 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 > }
163
+
164
+ { /* Container for input and icon */ }
165
+ < div className = "relative flex items-center" >
166
+ < Input
167
+ id = "username"
168
+ placeholder = "Username"
169
+ type = "text"
170
+ autoCapitalize = "none"
171
+ autoComplete = "username"
172
+ autoCorrect = "off"
173
+ disabled = { isLoading }
174
+ value = { username }
175
+ onChange = { ( e ) => setUsername ( e . target . value ) }
176
+ className = "dark:bg-zinc-900 pr-10" // Add padding to the right for the icon
177
+ required
178
+ />
179
+
180
+ { /* HoverCard for the green tick */ }
181
+ { isUsernameAvailable && (
182
+ < HoverCard >
183
+ < HoverCardTrigger asChild >
184
+ < Check className = "absolute right-3 text-green-500" />
185
+ </ HoverCardTrigger >
186
+ < HoverCardContent className = "text-green-500 text-sm bg-zinc-900" >
187
+ Username is available
188
+ </ HoverCardContent >
189
+ </ HoverCard >
190
+ ) }
191
+ { isUsernameAvailable === false && (
192
+ < HoverCard >
193
+ < HoverCardTrigger asChild >
194
+ < X className = "absolute right-3 text-red-500" />
195
+ </ HoverCardTrigger >
196
+ < HoverCardContent className = "text-red-500 text-sm bg-zinc-900" >
197
+ Username is not available
198
+ Username is not available
199
+ </ HoverCardContent >
200
+ </ HoverCard >
201
+ ) }
202
+
203
+ { usernameError && (
204
+ < HoverCard >
205
+ < HoverCardTrigger asChild >
206
+ < X className = "absolute right-3 text-red-500" />
207
+ </ HoverCardTrigger >
208
+ < HoverCardContent className = "text-red-500 text-sm bg-zinc-900" >
209
+ { usernameError }
210
+ </ HoverCardContent >
211
+ </ HoverCard >
212
+ ) }
213
+ </ div >
180
214
</ div >
181
215
< div className = "grid gap-1" >
182
216
< Label className = "sr-only" htmlFor = "email" >
@@ -200,26 +234,50 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
200
234
< Label className = "sr-only" htmlFor = "password" >
201
235
Password
202
236
</ 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
- ) ) }
237
+ < div className = "relative flex items-center" >
238
+ < Input
239
+ id = "password"
240
+ placeholder = "Password"
241
+ type = "password"
242
+ autoComplete = "current-password"
243
+ disabled = { isLoading }
244
+ value = { password }
245
+ onChange = { handlePasswordChange }
246
+ className = "dark:bg-zinc-900 pr-10"
247
+ required
248
+ />
249
+ { allRulesPassed && (
250
+ < HoverCard >
251
+ < HoverCardTrigger asChild >
252
+ < Check className = "absolute right-3 text-green-500" />
253
+ </ HoverCardTrigger >
254
+ < HoverCardContent className = "text-green-500 text-sm bg-zinc-900" >
255
+ { passwordRules &&
256
+ Object . entries ( passwordRules ) . map ( ( [ rule , passed ] ) => (
257
+ < p key = { rule } className = { passed ? "text-green-500" : "text-red-500" } >
258
+ { passwordRuleMessages [ rule as keyof typeof passwordRuleMessages ] }
259
+ </ p >
260
+ ) ) }
261
+ </ HoverCardContent >
262
+ </ HoverCard >
263
+ ) }
264
+ { ! allRulesPassed && (
265
+ < HoverCard >
266
+ < HoverCardTrigger asChild >
267
+ < X className = "absolute right-3 text-red-500" />
268
+ </ HoverCardTrigger >
269
+ < HoverCardContent className = "text-red-500 text-sm bg-zinc-900" >
270
+ { passwordRules &&
271
+ Object . entries ( passwordRules ) . map ( ( [ rule , passed ] ) => (
272
+ < p key = { rule } className = { passed ? "text-green-500" : "text-red-500" } >
273
+ { passwordRuleMessages [ rule as keyof typeof passwordRuleMessages ] }
274
+ </ p >
275
+ ) ) }
276
+ </ HoverCardContent >
277
+ </ HoverCard >
278
+ ) }
279
+ </ div >
280
+
223
281
</ div >
224
282
< Button
225
283
disabled = {
0 commit comments