Skip to content

Commit fbbffc9

Browse files
fix: allow valid GitHub usernames with hyphens and numbers
1 parent 4ae0ef6 commit fbbffc9

1 file changed

Lines changed: 94 additions & 32 deletions

File tree

src/pages/Signup/Signup.tsx

Lines changed: 94 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ const SignUp: React.FC = () => {
3939
if (name === "username") {
4040
if (!value.trim()) {
4141
errorMessage = "Username is required";
42-
} else if (!/^[A-Za-z\s]+$/.test(value)) {
43-
errorMessage = "Only letters are allowed";
42+
} else if (!/^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$/.test(value)) {
43+
errorMessage = "Enter a valid GitHub username";
4444
}
4545
}
4646
if (name === "email") {
@@ -53,8 +53,11 @@ const SignUp: React.FC = () => {
5353
if (name === "password") {
5454
if (!value.trim()) {
5555
errorMessage = "Password is required";
56-
} else if (!/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$/.test(value)) {
57-
errorMessage = "Password must be 8+ characters with letters and numbers";
56+
} else if (
57+
!/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$/.test(value)
58+
) {
59+
errorMessage =
60+
"Password must be 8+ characters with letters and numbers";
5861
}
5962
}
6063
setErrors((prev) => ({ ...prev, [name]: errorMessage }));
@@ -64,36 +67,46 @@ const SignUp: React.FC = () => {
6467
e.preventDefault();
6568
const usernameError = !formData.username.trim()
6669
? "Username is required"
67-
: !/^[A-Za-z\s]+$/.test(formData.username)
68-
? "Only letters are allowed"
69-
: "";
70+
: !/^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$/.test(formData.username)
71+
? "Enter a valid GitHub username"
72+
: "";
7073
const emailError = !formData.email.trim()
7174
? "Email is required"
7275
: !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email.trim())
73-
? "Enter a valid email"
74-
: "";
76+
? "Enter a valid email"
77+
: "";
7578
const passwordError = !formData.password.trim()
7679
? "Password is required"
77-
: !/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$/.test(formData.password)
78-
? "Password must be 8+ characters with letters and numbers"
79-
: "";
80+
: !/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$/.test(
81+
formData.password,
82+
)
83+
? "Password must be 8+ characters with letters and numbers"
84+
: "";
8085
if (usernameError || emailError || passwordError) {
81-
setErrors({ username: usernameError, email: emailError, password: passwordError });
86+
setErrors({
87+
username: usernameError,
88+
email: emailError,
89+
password: passwordError,
90+
});
8291
return;
8392
}
8493
setIsLoading(true);
8594
try {
86-
const response = await axios.post(`${backendUrl}/api/auth/signup`,
87-
formData // Include cookies for session
95+
const response = await axios.post(
96+
`${backendUrl}/api/auth/signup`,
97+
formData, // Include cookies for session
8898
);
8999
setMessage(response.data.message); // Show success message from backend
90100

91101
// Navigate to login page after successful signup
92-
if (response.data.message === 'User created successfully') {
102+
if (response.data.message === "User created successfully") {
93103
navigate("/login");
94104
}
95105
} catch (error: any) {
96-
setMessage(error.response?.data?.message || "Something went wrong. Please try again.");
106+
setMessage(
107+
error.response?.data?.message ||
108+
"Something went wrong. Please try again.",
109+
);
97110
} finally {
98111
setIsLoading(false);
99112
}
@@ -138,10 +151,22 @@ const SignUp: React.FC = () => {
138151
className="text-center mb-10"
139152
>
140153
<div className="inline-flex items-center justify-center w-20 h-20 bg-white rounded-3xl mb-6 shadow-2xl transform hover:scale-105 transition-transform duration-300 overflow-hidden">
141-
<img src="/crl-icon.png" alt="Logo" className="w-14 h-14 object-contain" />
154+
<img
155+
src="/crl-icon.png"
156+
alt="Logo"
157+
className="w-14 h-14 object-contain"
158+
/>
142159
</div>
143-
<h1 className={`text-4xl font-bold mb-2 ${mode === "dark" ? "text-white" : "text-black"}`}>GitHubTracker</h1>
144-
<p className={`text-lg font-medium ${mode === "dark" ? "text-slate-300" : "text-gray-700"}`}>Join your GitHub journey</p>
160+
<h1
161+
className={`text-4xl font-bold mb-2 ${mode === "dark" ? "text-white" : "text-black"}`}
162+
>
163+
GitHubTracker
164+
</h1>
165+
<p
166+
className={`text-lg font-medium ${mode === "dark" ? "text-slate-300" : "text-gray-700"}`}
167+
>
168+
Join your GitHub journey
169+
</p>
145170
</motion.div>
146171

147172
<motion.div
@@ -154,7 +179,6 @@ const SignUp: React.FC = () => {
154179
: "bg-white border-gray-200 text-black"
155180
}`}
156181
>
157-
158182
<h2
159183
className={`text-2xl font-bold text-center mb-8 ${
160184
mode === "dark" ? "text-white" : "text-gray-800"
@@ -169,39 +193,72 @@ const SignUp: React.FC = () => {
169193
<div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
170194
<User className="h-5 w-5 text-gray-400" />
171195
</div>
172-
<input type="text" name="username" placeholder="Enter your username" value={formData.username} onChange={handleChange} required
196+
<input
197+
type="text"
198+
name="username"
199+
placeholder="Enter your username"
200+
value={formData.username}
201+
onChange={handleChange}
202+
required
173203
className={`w-full pl-12 pr-4 py-4 rounded-2xl border focus:outline-none focus:ring-2 focus:ring-gray-400 focus:border-transparent transition-all duration-300 ${mode === "dark" ? "bg-white/10 border-white/20 text-white placeholder-gray-400" : "bg-gray-100 border-gray-300 text-black placeholder-gray-400"}`}
174204
/>
175205
</div>
176-
{errors.username && <p className="text-red-500 text-sm mt-2">{errors.username}</p>}
206+
{errors.username && (
207+
<p className="text-red-500 text-sm mt-2">{errors.username}</p>
208+
)}
177209
</div>
178210

179211
<div>
180212
<div className="relative">
181213
<div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
182214
<Mail className="h-5 w-5 text-gray-400" />
183215
</div>
184-
<input type="email" name="email" placeholder="Enter your email" value={formData.email} onChange={handleChange} required
216+
<input
217+
type="email"
218+
name="email"
219+
placeholder="Enter your email"
220+
value={formData.email}
221+
onChange={handleChange}
222+
required
185223
className={`w-full pl-12 pr-4 py-4 rounded-2xl border focus:outline-none focus:ring-2 focus:ring-gray-400 focus:border-transparent transition-all duration-300 ${mode === "dark" ? "bg-white/10 border-white/20 text-white placeholder-gray-400" : "bg-gray-100 border-gray-300 text-black placeholder-gray-400"}`}
186224
/>
187225
</div>
188-
{errors.email && <p className="text-red-500 text-sm mt-2">{errors.email}</p>}
226+
{errors.email && (
227+
<p className="text-red-500 text-sm mt-2">{errors.email}</p>
228+
)}
189229
</div>
190230

191231
<div>
192232
<div className="relative">
193233
<div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
194234
<Lock className="h-5 w-5 text-gray-400" />
195235
</div>
196-
<input type={showPassword ? "text" : "password"} name="password" placeholder="Enter your password" value={formData.password} onChange={handleChange} required
236+
<input
237+
type={showPassword ? "text" : "password"}
238+
name="password"
239+
placeholder="Enter your password"
240+
value={formData.password}
241+
onChange={handleChange}
242+
required
197243
className={`w-full pl-12 pr-12 py-4 rounded-2xl border focus:outline-none focus:ring-2 focus:ring-gray-400 focus:border-transparent transition-all duration-300 ${mode === "dark" ? "bg-white/10 border-white/20 text-white placeholder-gray-400" : "bg-gray-100 border-gray-300 text-black placeholder-gray-400"}`}
198244
/>
199-
<button type="button" onClick={() => setShowPassword(!showPassword)} aria-label={showPassword ? "Hide password" : "Show password"} aria-pressed={showPassword}
200-
className={`absolute inset-y-0 right-0 pr-4 flex items-center transition-colors duration-200 ${mode === "dark" ? "text-slate-400 hover:text-white" : "text-gray-500 hover:text-gray-800"}`}>
201-
{showPassword ? <EyeOff className="h-5 w-5" /> : <Eye className="h-5 w-5" />}
245+
<button
246+
type="button"
247+
onClick={() => setShowPassword(!showPassword)}
248+
aria-label={showPassword ? "Hide password" : "Show password"}
249+
aria-pressed={showPassword}
250+
className={`absolute inset-y-0 right-0 pr-4 flex items-center transition-colors duration-200 ${mode === "dark" ? "text-slate-400 hover:text-white" : "text-gray-500 hover:text-gray-800"}`}
251+
>
252+
{showPassword ? (
253+
<EyeOff className="h-5 w-5" />
254+
) : (
255+
<Eye className="h-5 w-5" />
256+
)}
202257
</button>
203258
</div>
204-
{errors.password && <p className="text-red-500 text-sm mt-2">{errors.password}</p>}
259+
{errors.password && (
260+
<p className="text-red-500 text-sm mt-2">{errors.password}</p>
261+
)}
205262
</div>
206263

207264
<button
@@ -214,15 +271,20 @@ const SignUp: React.FC = () => {
214271
</form>
215272

216273
{message && (
217-
<div className={`text-center mt-6 p-3 rounded-xl ${message.includes("successfully") ? "text-green-600 bg-green-100" : "text-red-600 bg-red-100"}`}>
274+
<div
275+
className={`text-center mt-6 p-3 rounded-xl ${message.includes("successfully") ? "text-green-600 bg-green-100" : "text-red-600 bg-red-100"}`}
276+
>
218277
{message}
219278
</div>
220279
)}
221280

222281
<div className="text-center mt-8">
223282
<p className={mode === "dark" ? "text-gray-300" : "text-gray-600"}>
224283
Already have an account?{" "}
225-
<Link to="/login" className={`font-medium hover:underline transition-colors duration-300 ${mode === "dark" ? "text-white" : "text-black"}`}>
284+
<Link
285+
to="/login"
286+
className={`font-medium hover:underline transition-colors duration-300 ${mode === "dark" ? "text-white" : "text-black"}`}
287+
>
226288
Sign in here
227289
</Link>
228290
</p>

0 commit comments

Comments
 (0)