Skip to content

Commit a47cdee

Browse files
committed
wip
1 parent 1a5df37 commit a47cdee

8 files changed

Lines changed: 693 additions & 230 deletions

File tree

src/components/Auth/FormField.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import React from 'react';
2+
import { Eye, EyeOff } from 'lucide-react'; // Assuming lucide-react for icons
3+
4+
const FormField = ({
5+
id,
6+
name,
7+
type,
8+
value,
9+
onChange,
10+
placeholder,
11+
error,
12+
// icon: IconComponent, // Removed as Mail icon was removed from SignUp
13+
isPasswordToggle = false,
14+
showPassword = false,
15+
togglePasswordVisibility,
16+
inputClassName, // Class for the <input> element itself
17+
containerClassName, // Class for the outer <div> container
18+
...rest // For other props like 'required'
19+
}) => {
20+
// Determine the actual input type (text/password) based on toggle state
21+
const inputType = isPasswordToggle ? (showPassword ? "text" : "password") : type;
22+
23+
return (
24+
// Use containerClassName if provided, otherwise a generic one or empty string
25+
<div className={containerClassName || (isPasswordToggle ? "password-input-container" : "")}>
26+
<input
27+
id={id}
28+
name={name}
29+
type={inputType}
30+
value={value}
31+
onChange={onChange}
32+
placeholder={placeholder}
33+
className={inputClassName}
34+
{...rest}
35+
/>
36+
{/* Icon for password toggle */}
37+
{isPasswordToggle && (
38+
<button
39+
type="button" // Important: Prevents button from submitting the form
40+
onClick={togglePasswordVisibility}
41+
className="password-toggle-icon-button"
42+
aria-label={showPassword ? `Hide ${name} password` : `Show ${name} password`}
43+
data-testid={`${name}-toggle`} // Good for testing
44+
>
45+
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
46+
</button>
47+
)}
48+
{/* Error message */}
49+
{error && (
50+
<p className="mt-1 text-sm text-red-600">{error}</p>
51+
)}
52+
</div>
53+
);
54+
};
55+
56+
export default FormField;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from 'react';
2+
3+
const PasswordRequirementsList = ({ requirements }) => {
4+
return (
5+
<div className="password-composition-list mt-2">
6+
<p className={`text-sm flex items-center ${requirements.minLength ? "text-green-600" : "text-red-600"}`}>
7+
{requirements.minLength ? '✓' : '✗'}{" "}8 Characters
8+
</p>
9+
<p className={`text-sm flex items-center ${requirements.hasUppercase ? "text-green-600" : "text-red-600"}`}>
10+
{requirements.hasUppercase ? '✓' : '✗'}{" "}1 Uppercase Letter
11+
</p>
12+
<p className={`text-sm flex items-center ${requirements.hasLowercase ? "text-green-600" : "text-red-600"}`}>
13+
{requirements.hasLowercase ? '✓' : '✗'}{" "}1 Lowercase Letter
14+
</p>
15+
<p className={`text-sm flex items-center ${requirements.hasNumber ? "text-green-600" : "text-red-600"}`}>
16+
{requirements.hasNumber ? '✓' : '✗'}{" "}1 Number
17+
</p>
18+
<p className={`text-sm flex items-center ${requirements.hasSymbol ? "text-green-600" : "text-red-600"}`}>
19+
{requirements.hasSymbol ? '✓' : '✗'}{" "}1 Symbol
20+
</p>
21+
</div>
22+
);
23+
};
24+
25+
export default PasswordRequirementsList;

src/components/Auth/SignIn.js

Lines changed: 83 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
import React, { useState } from "react";
2-
import { Eye, EyeOff, Mail } from "lucide-react";
3-
import InputField from "./shared/InputField";
4-
import { useNavigate } from 'react-router-dom'; // <-- Import useNavigate
2+
import { FaEye, FaEyeSlash } from "react-icons/fa";
3+
import { Mail } from "lucide-react";
4+
import { useNavigate } from 'react-router-dom';
5+
import "../../styles/auth.css";
6+
7+
const isStrongPassword = (password) => {
8+
// At least one lowercase, uppercase, digit, symbol, min 8 char
9+
const regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\w\s]).{8,}$/;
10+
return regex.test(password);
11+
};
512

6-
// Remove onSwitchToSignUp from props
713
const SignIn = ({ onAuthSuccess }) => {
814
const [formData, setFormData] = useState({ email: "", password: "" });
915
const [showPassword, setShowPassword] = useState(false);
1016
const [errors, setErrors] = useState({});
1117
const [isLoading, setIsLoading] = useState(false);
12-
13-
const navigate = useNavigate(); // <-- Initialize useNavigate
18+
const navigate = useNavigate();
1419

1520
const handleInputChange = (e) => {
1621
const { name, value } = e.target;
@@ -20,12 +25,19 @@ const SignIn = ({ onAuthSuccess }) => {
2025
}
2126
};
2227

28+
const validateEmail = (email) =>
29+
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
30+
2331
const handleSubmit = async () => {
2432
const newErrors = {};
2533
if (!formData.email) newErrors.email = 'Email is required';
26-
else if (!formData.email.includes('@')) newErrors.email = 'Invalid email';
34+
else if (!validateEmail(formData.email)) newErrors.email = 'Invalid email';
2735

2836
if (!formData.password) newErrors.password = 'Password is required';
37+
else if (!isStrongPassword(formData.password)) {
38+
newErrors.password =
39+
'Password must be at least 8 characters, include uppercase, lowercase, number, and symbol.';
40+
}
2941

3042
setErrors(newErrors);
3143

@@ -34,100 +46,103 @@ const SignIn = ({ onAuthSuccess }) => {
3446
setTimeout(() => {
3547
setIsLoading(false);
3648
alert('Sign in successful!');
37-
onAuthSuccess();
38-
// The App.js's handleAuthSuccess now calls navigate('/')
39-
// So you don't need navigate here unless you want to override it.
40-
// For clarity, keep the navigation in App.js.
49+
onAuthSuccess && onAuthSuccess();
4150
}, 1500);
4251
}
4352
};
4453

4554
return (
46-
<div className="auth-wrapper min-h-screen bg-white flex items-center justify-center p-4">
47-
<div className="auth-card bg-white rounded-xl shadow-2xl p-8 max-w-md w-full">
55+
<div className="auth-wrapper">
56+
<div className="auth-card">
4857
<div className="text-center mb-8">
49-
<h1 className="text-3xl font-bold text-gray-900 mb-2">
50-
Welcome Back
51-
</h1>
52-
<p className="text-gray-600">Sign in to your account</p>
58+
<h1>Welcome Back</h1>
59+
<p>Please enter your email and password to Login</p>
5360
</div>
5461

5562
<form
63+
className="auth-form"
5664
onSubmit={(e) => {
5765
e.preventDefault();
5866
handleSubmit();
5967
}}
6068
>
6169
<div className="space-y-6">
62-
{/* Email Input */}
63-
<InputField
64-
icon={Mail}
65-
id="signin-email"
66-
name="email"
67-
type="email"
68-
value={formData.email}
69-
onChange={handleInputChange}
70-
error={errors.email}
71-
placeholder="Email Address"
72-
/>
73-
74-
{/* Password Field with Toggle Button */}
75-
<div>
76-
<label
77-
htmlFor="signin-password"
78-
className="block text-sm font-medium text-gray-700 mb-2"
79-
>
80-
Password
81-
</label>
82-
<div className="flex items-center">
83-
<input
84-
id="signin-password"
85-
name="password"
86-
type={showPassword ? "text" : "password"}
87-
value={formData.password}
88-
onChange={handleInputChange}
89-
placeholder="Enter your password"
90-
className="flex-1 h-12 px-4 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
91-
/>
92-
<button
93-
type="button"
94-
onClick={() => setShowPassword(!showPassword)}
95-
className="w-10 h-10 px-2 bg-green-700 text-white rounded-md ml-2 hover:bg-green-800 focus:outline-none focus:ring-2 focus:ring-green-500 flex items-center justify-center"
96-
aria-label={showPassword ? "Hide password" : "Show password"}
97-
>
98-
{showPassword ? (
99-
<EyeOff className="h-4 w-4" />
100-
) : (
101-
<Eye className="h-4 w-4" />
102-
)}
103-
</button>
104-
</div>
10570

71+
{/* EMAIL INPUT */}
72+
<div className="email-input-container">
73+
<input
74+
id="signin-email"
75+
name="email"
76+
type="email"
77+
value={formData.email}
78+
onChange={handleInputChange}
79+
placeholder="Email"
80+
className="login-email"
81+
required
82+
/>
83+
<Mail className="email-icon-right" />
84+
{errors.email && (
85+
<p className="mt-1 text-sm text-red-600">{errors.email}</p>
86+
)}
87+
</div>
88+
89+
{/* PASSWORD INPUT */}
90+
<div className="password-input-container">
91+
<input
92+
id="signin-password"
93+
name="password"
94+
type={showPassword ? "text" : "password"}
95+
value={formData.password}
96+
onChange={handleInputChange}
97+
placeholder="Password"
98+
className="login-password"
99+
required
100+
/>
101+
<span
102+
className="password-icon"
103+
onClick={() => setShowPassword(!showPassword)}
104+
role="button"
105+
aria-label="Toggle password visibility"
106+
tabIndex={0}
107+
onKeyDown={(e) =>
108+
e.key === 'Enter' && setShowPassword(!showPassword)
109+
}
110+
data-testid="password-toggle"
111+
>
112+
{showPassword ? <FaEyeSlash size={18} /> : <FaEye size={18} />}
113+
</span>
106114
{errors.password && (
107115
<p className="mt-1 text-sm text-red-600">{errors.password}</p>
108116
)}
109117
</div>
110118

111-
{/* Submit Button */}
112119
<button
113120
type="submit"
114121
disabled={isLoading}
115-
className="w-full bg-green-700 text-white py-3 px-4 rounded-lg hover:bg-green-800 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors font-medium"
122+
className="auth-submit-button"
116123
>
117-
{isLoading ? "Signing in..." : "Sign In"}
124+
{isLoading ? "Signing in..." : "Login"}
118125
</button>
119126
</div>
120127
</form>
121128

122129
<div className="mt-8 text-center">
130+
<button
131+
type="button"
132+
onClick={() => navigate('/forgotpassword')}
133+
className="forgot-password"
134+
>
135+
Forgot password?
136+
</button>
137+
123138
<p className="text-sm text-gray-600">
124-
Don&#39;t have an account?{" "}
139+
Don&#39;t have an account?{' '}
125140
<button
126141
type="button"
127-
onClick={() => navigate('/signup')} // <-- Use navigate
128-
className="text-green-700 hover:text-green-600 font-medium focus:outline-none"
142+
onClick={() => navigate('/signup')}
143+
className="link-button"
129144
>
130-
Sign up
145+
Register
131146
</button>
132147
</p>
133148
</div>
@@ -136,4 +151,4 @@ const SignIn = ({ onAuthSuccess }) => {
136151
);
137152
};
138153

139-
export default SignIn;
154+
export default SignIn;

0 commit comments

Comments
 (0)