Skip to content

Commit 5f744b6

Browse files
authored
Merge pull request #60 from deepraj21/main
Update UI of Signup
2 parents 4184121 + 11d0a86 commit 5f744b6

File tree

4 files changed

+168
-40
lines changed

4 files changed

+168
-40
lines changed

client/package-lock.json

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@radix-ui/react-collapsible": "^1.1.1",
1717
"@radix-ui/react-dialog": "^1.1.2",
1818
"@radix-ui/react-dropdown-menu": "^2.1.2",
19+
"@radix-ui/react-hover-card": "^1.1.2",
1920
"@radix-ui/react-icons": "^1.3.0",
2021
"@radix-ui/react-label": "^2.1.0",
2122
"@radix-ui/react-navigation-menu": "^1.2.0",

client/src/components/Auth/user-auth-form-signup.tsx

Lines changed: 109 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { Icons } from "@/components/ui/icons";
99
import { Button } from "@/components/ui/button";
1010
import { Input } from "@/components/ui/input";
1111
import { Label } from "@/components/ui/label";
12+
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card";
13+
import { Check, X } from "lucide-react";
1214

1315
interface UserAuthFormProps extends React.HTMLAttributes<HTMLDivElement> { }
1416
interface PasswordRules {
@@ -31,8 +33,15 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
3133
const [usernameError, setUsernameError] = useState<string>("");
3234
const [passwordRules, setPasswordRules] = useState<PasswordRules | null>(null);
3335
const [isPasswordValid, setIsPasswordValid] = useState<boolean>(false);
36+
const allRulesPassed = passwordRules && Object.values(passwordRules).every((passed) => passed === true);
37+
const [hasStartedTyping, setHasStartedTyping] = useState(false);
3438
const navigate = useNavigate();
3539

40+
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
41+
setHasStartedTyping(true);
42+
handlePasswordChange(e);
43+
};
44+
3645
// Username availability check with debounce
3746
useEffect(() => {
3847
const checkUsernameAvailability = async () => {
@@ -157,26 +166,57 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
157166
<Label className="sr-only" htmlFor="username">
158167
Username
159168
</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>
180220
</div>
181221
<div className="grid gap-1">
182222
<Label className="sr-only" htmlFor="email">
@@ -200,26 +240,55 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
200240
<Label className="sr-only" htmlFor="password">
201241
Password
202242
</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>
223292
</div>
224293
<Button
225294
disabled={
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import * as React from "react"
2+
import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
3+
4+
import { cn } from "@/lib/utils"
5+
6+
const HoverCard = HoverCardPrimitive.Root
7+
8+
const HoverCardTrigger = HoverCardPrimitive.Trigger
9+
10+
const HoverCardContent = React.forwardRef<
11+
React.ElementRef<typeof HoverCardPrimitive.Content>,
12+
React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
13+
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
14+
<HoverCardPrimitive.Content
15+
ref={ref}
16+
align={align}
17+
sideOffset={sideOffset}
18+
className={cn(
19+
"z-50 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
20+
className
21+
)}
22+
{...props}
23+
/>
24+
))
25+
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName
26+
27+
export { HoverCard, HoverCardTrigger, HoverCardContent }

0 commit comments

Comments
 (0)