Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 95 additions & 42 deletions src/pages/Signup/Signup.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState } from "react";
import axios from "axios";
import { useNavigate ,Link } from "react-router-dom";
import { User, Mail, Lock } from "lucide-react";
import { User, Mail, Lock,Eye,EyeOff } from "lucide-react";
const backendUrl = import.meta.env.VITE_BACKEND_URL;
interface SignUpFormData {
username: string;
Expand All @@ -16,47 +16,74 @@ const SignUp: React.FC = () => {
password: ""
});
const [message, setMessage] = useState<string>("");
const [errors, setErrors] = useState({
username: "",
email: "",
password: "",
});
const [showPassword, setShowPassword] = useState(false);
const navigate = useNavigate();
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData({ ...formData, [name]: value });
};

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
const response = await axios.post(`${backendUrl}/api/auth/signup`,
formData // Include cookies for session
);
setMessage(response.data.message); // Show success message from backend

// Navigate to login page after successful signup
if (response.data.message === 'User created successfully') {
navigate("/login");}


// // Simulate API call (replace with your actual backend integration)
// try {
// // Mock successful signup
// setMessage("Account created successfully! Redirecting to login...");

// // In your actual implementation, integrate with your backend here:
// // const response = await fetch(`${backendUrl}/api/auth/signup`, {
// // method: 'POST',
// // headers: { 'Content-Type': 'application/json' },
// // body: JSON.stringify(formData)
// // });

// setTimeout(() => {
// // Navigate to login page in your actual implementation
// console.log("Redirecting to login page...");
// }, 2000);

} catch (error) {
setMessage("Something went wrong. Please try again.");
const { name, value } = e.target;

setFormData({ ...formData, [name]: value });

let errorMessage = "";

if (name === "username") {
if (!value.trim()) {
errorMessage = "Username is required";
} else if (!/^[A-Za-z\s]+$/.test(value)) {
errorMessage = "Only letters are allowed";
}
}

if (name === "email") {
if (!value.trim()) {
errorMessage = "Email is required";
} else if (!/\S+@\S+\.\S+/.test(value)) {
errorMessage = "Enter a valid email";
Comment on lines +41 to +45
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Tighten email validation to avoid false positives.

At Line 44, /\S+@\S+\.\S+/ is unanchored and used against untrimmed input, so malformed values can still pass validation.

Proposed fix
   if (name === "email") {
-    if (!value.trim()) {
+    const email = value.trim();
+    if (!email) {
       errorMessage = "Email is required";
-    } else if (!/\S+@\S+\.\S+/.test(value)) {
+    } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
       errorMessage = "Enter a valid email";
     }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (name === "email") {
if (!value.trim()) {
errorMessage = "Email is required";
} else if (!/\S+@\S+\.\S+/.test(value)) {
errorMessage = "Enter a valid email";
if (name === "email") {
const email = value.trim();
if (!email) {
errorMessage = "Email is required";
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
errorMessage = "Enter a valid email";
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/Signup/Signup.tsx` around lines 41 - 45, The email validation in
the Signup component uses an unanchored regex against untrimmed input which
permits malformed emails; update the logic in the block where name === "email"
to first use const trimmed = value.trim() and validate trimmed (not raw value),
and replace the regex /\S+@\S+\.\S+/ with an anchored, stricter pattern (e.g.
start/end anchors and better local/domain checks) when setting errorMessage so
malformed values are rejected; ensure errorMessage is set based on trimmed input
and the function/handler (the code that references name, value, and
errorMessage) uses the trimmed, validated value thereafter.

}
};
}

if (name === "password") {
if (!value.trim()) {
errorMessage = "Password is required";
} else if (
!/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$/.test(value)
) {
errorMessage =
"Password must be 8+ characters with letters and numbers";
}
}

setErrors((prev) => ({
...prev,
[name]: errorMessage,
}));
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();

if (errors.username || errors.email || errors.password) {
return;
}
Comment on lines +65 to +70
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Revalidate from formData on submit; current gate misses untouched invalid fields.

At Line 68, submission is blocked only when existing errors entries are set. If a user clicks submit without typing, errors remains empty and invalid payload can still be posted.

Proposed fix
+const validateField = (name: keyof SignUpFormData, rawValue: string): string => {
+  const value = rawValue.trim();
+  if (name === "username") {
+    if (!value) return "Username is required";
+    if (!/^[A-Za-z\s]+$/.test(value)) return "Only letters are allowed";
+  }
+  if (name === "email") {
+    if (!value) return "Email is required";
+    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) return "Enter a valid email";
+  }
+  if (name === "password") {
+    if (!value) return "Password is required";
+    if (!/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$/.test(rawValue)) {
+      return "Password must be 8+ characters with letters and numbers";
+    }
+  }
+  return "";
+};
+
 const handleSubmit = async (e: React.FormEvent) => {
   e.preventDefault();
-
-  if (errors.username || errors.email || errors.password) {
+  const nextErrors = {
+    username: validateField("username", formData.username),
+    email: validateField("email", formData.email),
+    password: validateField("password", formData.password),
+  };
+  setErrors(nextErrors);
+  if (Object.values(nextErrors).some(Boolean)) {
     return;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (errors.username || errors.email || errors.password) {
return;
}
const validateField = (name: keyof SignUpFormData, rawValue: string): string => {
const value = rawValue.trim();
if (name === "username") {
if (!value) return "Username is required";
if (!/^[A-Za-z\s]+$/.test(value)) return "Only letters are allowed";
}
if (name === "email") {
if (!value) return "Email is required";
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) return "Enter a valid email";
}
if (name === "password") {
if (!value) return "Password is required";
if (!/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$/.test(rawValue)) {
return "Password must be 8+ characters with letters and numbers";
}
}
return "";
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const nextErrors = {
username: validateField("username", formData.username),
email: validateField("email", formData.email),
password: validateField("password", formData.password),
};
setErrors(nextErrors);
if (Object.values(nextErrors).some(Boolean)) {
return;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/Signup/Signup.tsx` around lines 65 - 70, The submit handler
handleSubmit currently only checks the existing errors object, so untouched
invalid fields let bad payloads through; update handleSubmit to re-run
validation against current formData on submit (e.g., call your
validate/validateAll function or validateField for each field with
formData.username, formData.email, formData.password), update/set the errors
state with any returned issues, and abort submission if any validation errors
are present before posting the payload; ensure this revalidation handles async
validators by awaiting the validator if needed and then returning early when
errors exist.


try {
const response = await axios.post(
`${backendUrl}/api/auth/signup`,
formData
);

setMessage(response.data.message);

if (response.data.message === "User created successfully") {
navigate("/login");
}
} catch (error) {
setMessage("Something went wrong. Please try again.");
}
};
return (
<div className="relative h-screen w-screen bg-gradient-to-br from-indigo-900 via-purple-800 to-pink-700 flex items-center justify-center px-4 overflow-hidden">
{/* Background decorative elements */}
Expand All @@ -78,8 +105,8 @@ const navigate = useNavigate();
{/* Sign Up Form */}
<div className="bg-white/10 backdrop-blur-lg rounded-3xl p-8 border border-white/20 shadow-2xl">
<h2 className="text-2xl font-semibold text-white text-center mb-8">Create Account</h2>

<div className="space-y-6">
<div>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
<User className="h-5 w-5 text-purple-300" />
Expand All @@ -93,8 +120,14 @@ const navigate = useNavigate();
required
className="w-full pl-12 pr-4 py-4 bg-white/10 border border-white/20 rounded-2xl text-white placeholder-purple-300 focus:outline-none focus:ring-2 focus:ring-purple-400 focus:border-transparent backdrop-blur-sm transition-all duration-300"
/>
</div>
{errors.username && (
<p className="text-red-300 text-sm mt-2">
{errors.username}
</p>
)}
</div>

<div>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
<Mail className="h-5 w-5 text-purple-300" />
Expand All @@ -108,21 +141,41 @@ const navigate = useNavigate();
required
className="w-full pl-12 pr-4 py-4 bg-white/10 border border-white/20 rounded-2xl text-white placeholder-purple-300 focus:outline-none focus:ring-2 focus:ring-purple-400 focus:border-transparent backdrop-blur-sm transition-all duration-300"
/>
</div>
{errors.email && (
<p className="text-red-300 text-sm mt-2">
{errors.email}
</p>
)}
</div>


<div>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
<Lock className="h-5 w-5 text-purple-300" />
</div>
<input
type="password"
type={showPassword ? "text" : "password"}
name="password"
placeholder="Enter your password"
value={formData.password}
onChange={handleChange}
required
className="w-full pl-12 pr-4 py-4 bg-white/10 border border-white/20 rounded-2xl text-white placeholder-purple-300 focus:outline-none focus:ring-2 focus:ring-purple-400 focus:border-transparent backdrop-blur-sm transition-all duration-300"
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute inset-y-0 right-0 pr-4 flex items-center text-purple-300"
>
{showPassword ? <EyeOff size={20} /> : <Eye size={20} />}
</button>
Comment on lines +166 to +172
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add accessible label/state to password visibility toggle.

The icon-only button at Line 166 has no accessible name, which makes the control unclear for screen-reader users.

Proposed fix
               <button
               type="button"
               onClick={() => setShowPassword(!showPassword)}
+              aria-label={showPassword ? "Hide password" : "Show password"}
+              aria-pressed={showPassword}
               className="absolute inset-y-0 right-0 pr-4 flex items-center text-purple-300"
               >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute inset-y-0 right-0 pr-4 flex items-center text-purple-300"
>
{showPassword ? <EyeOff size={20} /> : <Eye size={20} />}
</button>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
aria-label={showPassword ? "Hide password" : "Show password"}
aria-pressed={showPassword}
className="absolute inset-y-0 right-0 pr-4 flex items-center text-purple-300"
>
{showPassword ? <EyeOff size={20} /> : <Eye size={20} />}
</button>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/Signup/Signup.tsx` around lines 166 - 172, The password visibility
toggle button is icon-only and lacks an accessible name; update the button (the
element using setShowPassword and showPassword that renders Eye/EyeOff) to
provide an accessible label and state, e.g., add an aria-label that changes with
showPassword (e.g., "Show password" vs "Hide password") and consider adding
aria-pressed or title for additional context so screen readers announce its
purpose and current state.

</div>
{errors.password && (
<p className="text-red-300 text-sm mt-2">
{errors.password}
</p>
)}
</div>

<button
Expand Down
Loading