Skip to content

Commit 465eda3

Browse files
Merge pull request #616 from harendra-godara/fix-username-validation
fix: allow valid GitHub usernames with hyphens and numbers
2 parents b7d6351 + 560e42b commit 465eda3

1 file changed

Lines changed: 188 additions & 107 deletions

File tree

src/pages/Signup/Signup.tsx

Lines changed: 188 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ const SignUp: React.FC = () => {
5151
if (name === "username") {
5252
if (!value.trim()) {
5353
errorMessage = "Username is required";
54-
} else if (!/^[A-Za-z\s]+$/.test(value)) {
55-
errorMessage = "Only letters are allowed";
54+
} else if (!/^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$/.test(value)) {
55+
errorMessage = "Enter a valid GitHub username";
5656
}
5757
}
5858

@@ -67,8 +67,11 @@ const SignUp: React.FC = () => {
6767
if (name === "password") {
6868
if (!value.trim()) {
6969
errorMessage = "Password is required";
70-
} else if (!/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/.test(value)) {
71-
errorMessage = "Password must contain uppercase, lowercase, number, and special character";
70+
} else if (
71+
!/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$/.test(value)
72+
) {
73+
errorMessage =
74+
"Password must be 8+ characters with letters and numbers";
7275
}
7376
}
7477

@@ -80,8 +83,8 @@ const SignUp: React.FC = () => {
8083

8184
const usernameError = !formData.username.trim()
8285
? "Username is required"
83-
: !/^[A-Za-z\s]+$/.test(formData.username)
84-
? "Only letters are allowed"
86+
: !/^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$/.test(formData.username)
87+
? "Enter a valid GitHub username"
8588
: "";
8689
const emailError = !formData.email.trim()
8790
? "Email is required"
@@ -90,30 +93,37 @@ const SignUp: React.FC = () => {
9093
: "";
9194
const passwordError = !formData.password.trim()
9295
? "Password is required"
93-
: !/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$/.test(formData.password)
96+
: !/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$/.test(
97+
formData.password,
98+
)
9499
? "Password must be 8+ characters with letters and numbers"
95100
: "";
96-
97101
if (usernameError || emailError || passwordError) {
98-
setErrors({ username: usernameError, email: emailError, password: passwordError });
102+
setErrors({
103+
username: usernameError,
104+
email: emailError,
105+
password: passwordError,
106+
});
99107
return;
100108
}
101109

102110
setIsLoading(true);
103111
try {
104-
const response = await axios.post(`${backendUrl}/api/auth/signup`, formData, { withCredentials: true });
105-
setMessage(response.data.message);
112+
const response = await axios.post(
113+
`${backendUrl}/api/auth/signup`,
114+
formData, // Include cookies for session
115+
);
116+
setMessage(response.data.message); // Show success message from backend
106117

107118
// Navigate to login page after successful signup
108-
if (response.status === 201) {
119+
if (response.data.message === "User created successfully") {
109120
navigate("/login");
110121
}
111-
} catch (error: unknown) {
112-
if (axios.isAxiosError(error)) {
113-
setMessage(error.response?.data?.message || "Something went wrong. Please try again.");
114-
} else {
115-
setMessage("Something went wrong. Please try again.");
116-
}
122+
} catch (error: any) {
123+
setMessage(
124+
error.response?.data?.message ||
125+
"Something went wrong. Please try again.",
126+
);
117127
} finally {
118128
setIsLoading(false);
119129
}
@@ -135,105 +145,176 @@ const SignUp: React.FC = () => {
135145
</p>
136146
}
137147
>
138-
<div className="space-y-8">
139-
<div className="space-y-3 text-center sm:text-left">
140-
<div className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/5 px-3 py-1.5 text-xs font-semibold uppercase tracking-[0.22em] text-cyan-300/90">
141-
<Sparkles className="h-3.5 w-3.5" />
142-
New account
143-
</div>
144-
<div>
145-
<h2 className={`text-3xl font-semibold tracking-tight ${mode === "dark" ? "text-white" : "text-slate-950"}`}>
146-
Build your profile
147-
</h2>
148-
<p className={`mt-2 text-sm leading-6 ${mode === "dark" ? "text-slate-300" : "text-slate-600"}`}>
149-
Use a simple, guided signup flow with instant feedback while you type.
150-
</p>
148+
<div className="absolute inset-0">
149+
<div
150+
className={`absolute -top-40 -right-40 w-96 h-96 ${
151+
mode === "dark" ? "bg-purple-500" : "bg-purple-300"
152+
} rounded-full blur-3xl opacity-30 animate-pulse`}
153+
/>
154+
<div
155+
className={`absolute -bottom-40 -left-40 w-96 h-96 ${
156+
mode === "dark" ? "bg-blue-500" : "bg-blue-300"
157+
} rounded-full blur-3xl opacity-30 animate-pulse`}
158+
/>
159+
<div
160+
className={`absolute top-40 left-40 w-96 h-96 ${
161+
mode === "dark" ? "bg-pink-500" : "bg-pink-300"
162+
} rounded-full blur-3xl opacity-30 animate-pulse`}
163+
/>
164+
<div
165+
className={`absolute top-1/2 right-1/4 w-64 h-64 ${
166+
mode === "dark" ? "bg-indigo-500" : "bg-indigo-300"
167+
} rounded-full blur-2xl opacity-20 animate-pulse delay-1000`}
168+
/>
169+
</div>
170+
171+
<div className="relative w-full max-w-md px-4 sm:px-6">
172+
<motion.div
173+
initial={{ opacity: 0, y: -20 }}
174+
animate={{ opacity: 1, y: 0 }}
175+
transition={{ duration: 0.6 }}
176+
className="text-center mb-10"
177+
>
178+
<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">
179+
<img
180+
src="/crl-icon.png"
181+
alt="Logo"
182+
className="w-14 h-14 object-contain"
183+
/>
151184
</div>
152-
</div>
153-
154-
<form onSubmit={handleSubmit} className="space-y-5">
155-
<label className="block space-y-2">
156-
<span className={`text-sm font-medium ${mode === "dark" ? "text-slate-200" : "text-slate-700"}`}>Username</span>
157-
<div className={`flex items-center gap-3 rounded-2xl border px-4 py-3 transition focus-within:ring-2 ${mode === "dark" ? "border-white/10 bg-white/5 focus-within:ring-cyan-400/50" : "border-slate-200 bg-slate-50 focus-within:ring-cyan-500/30"}`}>
158-
<User className={`h-5 w-5 shrink-0 ${mode === "dark" ? "text-slate-400" : "text-slate-500"}`} />
159-
<input
160-
type="text"
161-
name="username"
162-
placeholder="Your display name"
163-
value={formData.username}
164-
onChange={handleChange}
165-
required
166-
className={`w-full bg-transparent text-sm outline-none ${mode === "dark" ? "text-white placeholder-slate-500" : "text-slate-900 placeholder-slate-400"}`}
167-
/>
185+
<h1
186+
className={`text-4xl font-bold mb-2 ${mode === "dark" ? "text-white" : "text-black"}`}
187+
>
188+
GitHubTracker
189+
</h1>
190+
<p
191+
className={`text-lg font-medium ${mode === "dark" ? "text-slate-300" : "text-gray-700"}`}
192+
>
193+
Join your GitHub journey
194+
</p>
195+
</motion.div>
196+
197+
<motion.div
198+
initial={{ opacity: 0, y: 30 }}
199+
animate={{ opacity: 1, y: 0 }}
200+
transition={{ duration: 0.6, delay: 0.2 }}
201+
className={`rounded-3xl p-6 sm:p-10 shadow-2xl border ${
202+
mode === "dark"
203+
? "bg-white/10 backdrop-blur-xl border-white/20 text-white"
204+
: "bg-white border-gray-200 text-black"
205+
}`}
206+
>
207+
<h2
208+
className={`text-2xl font-bold text-center mb-8 ${
209+
mode === "dark" ? "text-white" : "text-gray-800"
210+
}`}
211+
>
212+
Create Account
213+
</h2>
214+
215+
<form onSubmit={handleSubmit} className="space-y-6">
216+
<div>
217+
<div className="relative">
218+
<div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
219+
<User className="h-5 w-5 text-gray-400" />
220+
</div>
221+
<input
222+
type="text"
223+
name="username"
224+
placeholder="Enter your username"
225+
value={formData.username}
226+
onChange={handleChange}
227+
required
228+
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"}`}
229+
/>
230+
</div>
231+
{errors.username && (
232+
<p className="text-red-500 text-sm mt-2">{errors.username}</p>
233+
)}
168234
</div>
169-
{errors.username && <p className="text-sm text-rose-600 dark:text-rose-300">{errors.username}</p>}
170-
</label>
171235

172-
<label className="block space-y-2">
173-
<span className={`text-sm font-medium ${mode === "dark" ? "text-slate-200" : "text-slate-700"}`}>Email address</span>
174-
<div className={`flex items-center gap-3 rounded-2xl border px-4 py-3 transition focus-within:ring-2 ${mode === "dark" ? "border-white/10 bg-white/5 focus-within:ring-cyan-400/50" : "border-slate-200 bg-slate-50 focus-within:ring-cyan-500/30"}`}>
175-
<Mail className={`h-5 w-5 shrink-0 ${mode === "dark" ? "text-slate-400" : "text-slate-500"}`} />
176-
<input
177-
type="email"
178-
name="email"
179-
placeholder="name@example.com"
180-
value={formData.email}
181-
onChange={handleChange}
182-
required
183-
className={`w-full bg-transparent text-sm outline-none ${mode === "dark" ? "text-white placeholder-slate-500" : "text-slate-900 placeholder-slate-400"}`}
184-
/>
236+
<div>
237+
<div className="relative">
238+
<div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
239+
<Mail className="h-5 w-5 text-gray-400" />
240+
</div>
241+
<input
242+
type="email"
243+
name="email"
244+
placeholder="Enter your email"
245+
value={formData.email}
246+
onChange={handleChange}
247+
required
248+
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"}`}
249+
/>
250+
</div>
251+
{errors.email && (
252+
<p className="text-red-500 text-sm mt-2">{errors.email}</p>
253+
)}
185254
</div>
186-
{errors.email && <p className="text-sm text-rose-600 dark:text-rose-300">{errors.email}</p>}
187-
</label>
188255

189-
<label className="block space-y-2">
190-
<span className={`text-sm font-medium ${mode === "dark" ? "text-slate-200" : "text-slate-700"}`}>Password</span>
191-
<div className={`flex items-center gap-3 rounded-2xl border px-4 py-3 transition focus-within:ring-2 ${mode === "dark" ? "border-white/10 bg-white/5 focus-within:ring-cyan-400/50" : "border-slate-200 bg-slate-50 focus-within:ring-cyan-500/30"}`}>
192-
<Lock className={`h-5 w-5 shrink-0 ${mode === "dark" ? "text-slate-400" : "text-slate-500"}`} />
193-
<input
194-
type={showPassword ? "text" : "password"}
195-
name="password"
196-
placeholder="At least 8 characters"
197-
value={formData.password}
198-
onChange={handleChange}
199-
required
200-
className={`w-full bg-transparent text-sm outline-none ${mode === "dark" ? "text-white placeholder-slate-500" : "text-slate-900 placeholder-slate-400"}`}
201-
/>
202-
<button
203-
type="button"
204-
onClick={() => setShowPassword((previous) => !previous)}
205-
aria-label={showPassword ? "Hide password" : "Show password"}
206-
aria-pressed={showPassword}
207-
className={`shrink-0 transition-colors ${mode === "dark" ? "text-slate-400 hover:text-white" : "text-slate-500 hover:text-slate-900"}`}
208-
>
209-
{showPassword ? <EyeOff className="h-5 w-5" /> : <Eye className="h-5 w-5" />}
210-
</button>
256+
<div>
257+
<div className="relative">
258+
<div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
259+
<Lock className="h-5 w-5 text-gray-400" />
260+
</div>
261+
<input
262+
type={showPassword ? "text" : "password"}
263+
name="password"
264+
placeholder="Enter your password"
265+
value={formData.password}
266+
onChange={handleChange}
267+
required
268+
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"}`}
269+
/>
270+
<button
271+
type="button"
272+
onClick={() => setShowPassword(!showPassword)}
273+
aria-label={showPassword ? "Hide password" : "Show password"}
274+
aria-pressed={showPassword}
275+
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"}`}
276+
>
277+
{showPassword ? (
278+
<EyeOff className="h-5 w-5" />
279+
) : (
280+
<Eye className="h-5 w-5" />
281+
)}
282+
</button>
283+
</div>
284+
{errors.password && (
285+
<p className="text-red-500 text-sm mt-2">{errors.password}</p>
286+
)}
211287
</div>
212288
{errors.password && <p className="text-sm text-rose-600 dark:text-rose-300">{errors.password}</p>}
213289
</label>
214290

215-
<button
216-
type="submit"
217-
disabled={isLoading}
218-
className="inline-flex w-full items-center justify-center gap-2 rounded-2xl bg-gradient-to-r from-cyan-500 via-blue-500 to-indigo-600 px-6 py-4 text-sm font-semibold text-white shadow-lg shadow-cyan-500/20 transition duration-300 hover:-translate-y-0.5 hover:shadow-xl disabled:cursor-not-allowed disabled:opacity-60"
219-
>
220-
{isLoading ? "Creating account..." : "Create account"}
221-
{!isLoading && <ArrowRight className="h-4 w-4" />}
222-
</button>
223-
</form>
224-
225-
{message && (
226-
<div
227-
className={`rounded-2xl border px-4 py-3 text-sm ${
228-
message.includes("successfully")
229-
? "border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-500/30 dark:bg-emerald-500/10 dark:text-emerald-300"
230-
: "border-rose-200 bg-rose-50 text-rose-700 dark:border-rose-500/30 dark:bg-rose-500/10 dark:text-rose-300"
231-
}`}
232-
>
233-
<div className="flex items-start gap-2">
234-
<ShieldCheck className="mt-0.5 h-4 w-4 shrink-0" />
235-
<p>{message}</p>
291+
<button
292+
type="submit"
293+
disabled={isLoading}
294+
className="w-full bg-gradient-to-r from-purple-600 via-pink-600 to-indigo-600 text-white py-4 px-6 rounded-2xl font-semibold focus:ring-4 focus:ring-purple-500/50 transition-all duration-300 hover:scale-[1.02] hover:shadow-xl disabled:opacity-50 disabled:cursor-not-allowed"
295+
>
296+
{isLoading ? "Creating account..." : "Create Account"}
297+
</button>
298+
</form>
299+
300+
{message && (
301+
<div
302+
className={`text-center mt-6 p-3 rounded-xl ${message.includes("successfully") ? "text-green-600 bg-green-100" : "text-red-600 bg-red-100"}`}
303+
>
304+
{message}
236305
</div>
306+
)}
307+
308+
<div className="text-center mt-8">
309+
<p className={mode === "dark" ? "text-gray-300" : "text-gray-600"}>
310+
Already have an account?{" "}
311+
<Link
312+
to="/login"
313+
className={`font-medium hover:underline transition-colors duration-300 ${mode === "dark" ? "text-white" : "text-black"}`}
314+
>
315+
Sign in here
316+
</Link>
317+
</p>
237318
</div>
238319
)}
239320
</div>

0 commit comments

Comments
 (0)