Skip to content

Commit 6be6838

Browse files
authored
Merge pull request #68 from junaidkhan1723/feature/show-hide-password
feat(ui): add show/hide password toggle using Lucide React icons
2 parents 58e1937 + 808ebc5 commit 6be6838

File tree

2 files changed

+133
-67
lines changed

2 files changed

+133
-67
lines changed

src/components/auth/LoginForm.js

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,49 @@
11
import { useState } from 'react';
22
import { useAuth } from '@/context/AuthContext';
33
import { motion } from 'framer-motion';
4+
import { Eye, EyeOff } from 'lucide-react';
45

56
export default function LoginForm({ onSuccess, onBack, theme }) {
67
const [formData, setFormData] = useState({
78
email: '',
89
password: '',
9-
rememberMe: false
10+
rememberMe: false,
1011
});
1112
const [errors, setErrors] = useState({});
1213
const [isLoading, setIsLoading] = useState(false);
13-
14+
const [showPassword, setShowPassword] = useState(false);
15+
1416
const { login } = useAuth();
1517

1618
const validateForm = () => {
1719
const newErrors = {};
18-
20+
1921
if (!formData.email) {
2022
newErrors.email = 'Email is required';
2123
} else if (!/\S+@\S+\.\S+/.test(formData.email)) {
2224
newErrors.email = 'Email is invalid';
2325
}
24-
26+
2527
if (!formData.password) {
2628
newErrors.password = 'Password is required';
2729
}
28-
30+
2931
setErrors(newErrors);
3032
return Object.keys(newErrors).length === 0;
3133
};
3234

3335
const handleSubmit = async (e) => {
3436
e.preventDefault();
35-
37+
3638
if (!validateForm()) return;
37-
39+
3840
setIsLoading(true);
3941
try {
40-
const result = await login(formData.email, formData.password, formData.rememberMe);
42+
const result = await login(
43+
formData.email,
44+
formData.password,
45+
formData.rememberMe,
46+
);
4147
onSuccess(result);
4248
} catch (error) {
4349
setErrors({ submit: error.message });
@@ -48,13 +54,13 @@ export default function LoginForm({ onSuccess, onBack, theme }) {
4854

4955
const handleChange = (e) => {
5056
const { name, value, type, checked } = e.target;
51-
setFormData(prev => ({
57+
setFormData((prev) => ({
5258
...prev,
53-
[name]: type === 'checkbox' ? checked : value
59+
[name]: type === 'checkbox' ? checked : value,
5460
}));
55-
61+
5662
if (errors[name]) {
57-
setErrors(prev => ({ ...prev, [name]: '' }));
63+
setErrors((prev) => ({ ...prev, [name]: '' }));
5864
}
5965
};
6066

@@ -77,8 +83,11 @@ export default function LoginForm({ onSuccess, onBack, theme }) {
7783
<p className="text-white/70">Sign in to continue to OrbitOS</p>
7884
</div>
7985

86+
{/** Login Form */}
87+
8088
<form onSubmit={handleSubmit} className="space-y-6">
8189
<div className="space-y-4">
90+
{/** Email */}
8291
<div>
8392
<label className="block text-sm font-medium text-white/90 mb-2">
8493
Email Address
@@ -103,21 +112,39 @@ export default function LoginForm({ onSuccess, onBack, theme }) {
103112
</motion.p>
104113
)}
105114
</div>
106-
107-
<div>
115+
{/** Password */}
116+
<div className="relative mb-4">
108117
<label className="block text-sm font-medium text-white/90 mb-2">
109118
Password
110119
</label>
111-
<input
112-
type="password"
113-
name="password"
114-
value={formData.password}
115-
onChange={handleChange}
116-
className={`w-full px-4 py-3 rounded-xl bg-white/5 border ${
117-
errors.password ? 'border-red-400' : 'border-white/20'
118-
} text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-transparent transition-all backdrop-blur-sm`}
119-
placeholder="Enter your password"
120-
/>
120+
121+
{/* Input Wrapper with Icon */}
122+
<div className="relative">
123+
<input
124+
type={showPassword ? 'text' : 'password'}
125+
name="password"
126+
value={formData.password}
127+
onChange={handleChange}
128+
className={`w-full px-4 py-3 pr-10 rounded-xl bg-white/5 border ${
129+
errors.password ? 'border-red-400' : 'border-white/20'
130+
} text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-transparent transition-all backdrop-blur-sm`}
131+
placeholder="Enter your password"
132+
/>
133+
134+
{/* Eye Icon */}
135+
<span
136+
onClick={() => setShowPassword(!showPassword)}
137+
className="absolute right-4 top-1/2 -translate-y-1/2 text-white/70 cursor-pointer hover:text-white transition-colors"
138+
>
139+
{showPassword ? (
140+
<Eye size={18} strokeWidth={2} />
141+
) : (
142+
<EyeOff size={18} strokeWidth={2} />
143+
)}
144+
</span>
145+
</div>
146+
147+
{/* Error Message */}
121148
{errors.password && (
122149
<motion.p
123150
initial={{ opacity: 0, y: -10 }}
@@ -141,7 +168,10 @@ export default function LoginForm({ onSuccess, onBack, theme }) {
141168
/>
142169
<span className="ml-2 text-sm text-white/80">Remember me</span>
143170
</label>
144-
<button type="button" className="text-sm text-blue-400 hover:text-blue-300 transition-colors">
171+
<button
172+
type="button"
173+
className="text-sm text-blue-400 hover:text-blue-300 transition-colors"
174+
>
145175
Forgot password?
146176
</button>
147177
</div>
@@ -176,4 +206,4 @@ export default function LoginForm({ onSuccess, onBack, theme }) {
176206
</div>
177207
</motion.div>
178208
);
179-
}
209+
}

0 commit comments

Comments
 (0)