diff --git a/backend/models/User.js b/backend/models/User.js index eb506ed5..5f834714 100644 --- a/backend/models/User.js +++ b/backend/models/User.js @@ -16,6 +16,13 @@ const UserSchema = new mongoose.Schema({ type: String, required: true, }, + bookmarks: [ + { + githubUsername: { type: String, required: true }, + avatarUrl: { type: String }, + savedAt: { type: Date, default: Date.now }, + }, + ], }); // ✅ FIXED: no next() diff --git a/backend/routes/bookmarks.js b/backend/routes/bookmarks.js new file mode 100644 index 00000000..9dc37d85 --- /dev/null +++ b/backend/routes/bookmarks.js @@ -0,0 +1,75 @@ +const express = require('express'); +const router = express.Router(); +const User = require('../models/User'); + +// Simple auth check middleware (session-based via passport) +function ensureAuth(req, res, next) { + if (req.isAuthenticated && req.isAuthenticated()) return next(); + return res.status(401).json({ message: 'Authentication required' }); +} + +// GET /api/bookmarks - returns current user's bookmarks +router.get('/', ensureAuth, async (req, res) => { + try { + console.log('GET /api/bookmarks req.isAuthenticated=', req.isAuthenticated && req.isAuthenticated(), 'user=', req.user && req.user.id); + const user = await User.findById(req.user._id).select('bookmarks'); + return res.json({ bookmarks: user?.bookmarks || [] }); + } catch (err) { + console.error('Error fetching bookmarks', err); + return res.status(500).json({ message: 'Failed to fetch bookmarks', error: err.message }); + } +}); + +// POST /api/bookmarks - add a bookmark +router.post('/', ensureAuth, async (req, res) => { + try { + console.log('POST /api/bookmarks req.isAuthenticated=', req.isAuthenticated && req.isAuthenticated(), 'user=', req.user && req.user.id, 'body=', req.body); + const { githubUsername, avatarUrl } = req.body; + if (!githubUsername || !githubUsername.trim()) { + return res.status(400).json({ message: 'githubUsername is required' }); + } + + const user = await User.findById(req.user._id); + if (!user) return res.status(404).json({ message: 'User not found' }); + + const exists = user.bookmarks?.some( + (b) => b.githubUsername.toLowerCase() === githubUsername.toLowerCase() + ); + if (exists) return res.status(409).json({ message: 'Bookmark already exists' }); + + user.bookmarks = user.bookmarks || []; + user.bookmarks.unshift({ githubUsername, avatarUrl }); + await user.save(); + + return res.status(201).json({ message: 'Bookmark saved', bookmark: user.bookmarks[0] }); + } catch (err) { + console.error('Error saving bookmark', err); + return res.status(500).json({ message: 'Failed to save bookmark', error: err.message }); + } +}); + +// DELETE /api/bookmarks/:username - remove bookmark +router.delete('/:username', ensureAuth, async (req, res) => { + try { + console.log('DELETE /api/bookmarks/:username req.isAuthenticated=', req.isAuthenticated && req.isAuthenticated(), 'user=', req.user && req.user.id, 'params=', req.params); + const username = req.params.username; + const user = await User.findById(req.user._id); + if (!user) return res.status(404).json({ message: 'User not found' }); + + const before = (user.bookmarks || []).length; + user.bookmarks = (user.bookmarks || []).filter( + (b) => b.githubUsername.toLowerCase() !== username.toLowerCase() + ); + + if (user.bookmarks.length === before) { + return res.status(404).json({ message: 'Bookmark not found' }); + } + + await user.save(); + return res.json({ message: 'Bookmark removed' }); + } catch (err) { + return res.status(500).json({ message: 'Failed to remove bookmark', error: err.message }); + } +}); + +module.exports = router; diff --git a/backend/server.js b/backend/server.js index 48d6ccfb..f69cd471 100644 --- a/backend/server.js +++ b/backend/server.js @@ -13,14 +13,20 @@ const logger = require('./logger'); const app = express(); -// CORS configuration -const allowedOrigins = ['http://localhost:5173', 'https://github-spy.etlify.app']; +// CORS configuration - allow common local dev origins +const allowedOrigins = [ + 'http://localhost:5173', + 'http://127.0.0.1:5173', + 'http://localhost:5174', + 'https://github-spy.etlify.app' +]; app.use(cors({ origin: function (origin, callback) { + // Allow requests with no origin like curl/postman if (!origin || allowedOrigins.indexOf(origin) !== -1) { callback(null, true); - } else{ - callback(new Error('Blocked by CORS policy')); + } else { + callback(new Error('Blocked by CORS policy: ' + origin)); } }, credentials: true @@ -29,9 +35,12 @@ app.use(cors({ // Middleware app.use(bodyParser.json()); app.use(session({ - secret: process.env.SESSION_SECRET, + secret: process.env.SESSION_SECRET || 'dev-secret', resave: false, saveUninitialized: false, + cookie: { + sameSite: 'lax', // help ensure cross-port cookies work in dev + }, })); app.use(passport.initialize()); app.use(passport.session()); @@ -39,6 +48,9 @@ app.use(passport.session()); // Routes const authRoutes = require('./routes/auth'); app.use('/api/auth', authRoutes); +// Bookmarks +const bookmarkRoutes = require('./routes/bookmarks'); +app.use('/api/bookmarks', bookmarkRoutes); // Connect to MongoDB mongoose.connect(process.env.MONGO_URI, {}).then(() => { diff --git a/src/Routes/Router.tsx b/src/Routes/Router.tsx index 874ef7e7..a1abacff 100644 --- a/src/Routes/Router.tsx +++ b/src/Routes/Router.tsx @@ -7,7 +7,8 @@ import Signup from "../pages/Signup/Signup.tsx"; import Login from "../pages/Login/Login.tsx"; import ContributorProfile from "../pages/ContributorProfile/ContributorProfile.tsx"; import Home from "../pages/Home/Home.tsx"; -import Activity from "../pages/Activity.tsx"; +import Activity from "../pages/Activity.tsx"; +import Bookmarks from "../pages/Bookmarks"; import PrivacyPolicy from "../pages/Privacy/PrivacyPolicy.tsx"; // ✅ Updated import path to match your new folder structure const Router = () => { @@ -22,6 +23,7 @@ const Router = () => { } /> } /> } /> + } /> {/* Privacy Policy page route */} } /> diff --git a/src/components/ActivityFeed.tsx b/src/components/ActivityFeed.tsx index d770dfee..823db8a0 100644 --- a/src/components/ActivityFeed.tsx +++ b/src/components/ActivityFeed.tsx @@ -12,8 +12,12 @@ interface EventType { export default function ActivityFeed({ username }: { username: string }) { const [events, setEvents] = useState([]); const [loading, setLoading] = useState(true); + const [error, setError] = useState(""); + const [isBookmarked, setIsBookmarked] = useState(false); + const [bookmarkLoading, setBookmarkLoading] = useState(false); + + const backendUrl = import.meta.env.VITE_BACKEND_URL || 'http://localhost:5000'; - // 🕒 time ago function const getTimeAgo = (dateString: string) => { const diff = Math.floor( (Date.now() - new Date(dateString).getTime()) / 1000 @@ -26,19 +30,69 @@ export default function ActivityFeed({ username }: { username: string }) { }; useEffect(() => { + // check bookmark state for this username + const checkBookmark = async () => { + if (!username.trim()) return setIsBookmarked(false); + try { + const res = await fetch(`${backendUrl}/api/bookmarks`, { credentials: 'include' }); + if (!res.ok) return setIsBookmarked(false); + const data = await res.json(); + const found = (data.bookmarks || []).some( + (b: any) => b.githubUsername.toLowerCase() === username.toLowerCase() + ); + setIsBookmarked(!!found); + } catch (err) { + // ignore + } + }; + + checkBookmark(); + const fetchEvents = async () => { + if (!username.trim()) { + setEvents([]); + setError("Please enter a GitHub username to get started."); + setLoading(false); + return; + } + try { setLoading(true); + setError(""); const res = await fetch( `https://api.github.com/users/${username}/events` ); + + if (!res.ok) { + let message = "Unable to load activity. Please try again."; + if (res.status === 404) { + message = "GitHub user not found. Please check the username."; + } else if (res.status === 403) { + message = + "GitHub rate limit exceeded. Wait a moment and try again."; + } + setEvents([]); + setError(message); + setLoading(false); + return; + } + const data = await res.json(); + if (!Array.isArray(data)) { + setError("Unexpected response from GitHub. Please try again."); + setEvents([]); + setLoading(false); + return; + } + setEvents(data); - setLoading(false); } catch (err) { console.error(err); + setError("Unable to fetch activity. Check your connection and try again."); + setEvents([]); + } finally { setLoading(false); } }; @@ -49,40 +103,106 @@ export default function ActivityFeed({ username }: { username: string }) { return () => clearInterval(interval); }, [username]); + const handleAddBookmark = async () => { + if (!username.trim()) return; + setBookmarkLoading(true); + try { + const res = await fetch(`${backendUrl}/api/bookmarks`, { + method: 'POST', + credentials: 'include', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ githubUsername: username }), + }); + if (!res.ok) throw new Error('Failed to save'); + setIsBookmarked(true); + } catch (err) { + console.error(err); + } finally { + setBookmarkLoading(false); + } + }; + + const handleRemoveBookmark = async () => { + if (!username.trim()) return; + setBookmarkLoading(true); + try { + const res = await fetch(`${backendUrl}/api/bookmarks/${encodeURIComponent(username)}`, { + method: 'DELETE', + credentials: 'include', + }); + if (!res.ok) throw new Error('Failed to remove'); + setIsBookmarked(false); + } catch (err) { + console.error(err); + } finally { + setBookmarkLoading(false); + } + }; + + const currentEvents = events.slice(0, 10); + return ( -
-

- Activity Feed -

+
+
+
+

Activity Feed

+

+ Tracking {username} +

+
+ +
+

Refreshes every 30s

+ + +
+
{loading ? ( -

Loading...

- ) : events.length === 0 ? ( -

No activity found

+
+ Loading GitHub activity... +
+ ) : error ? ( +
+ {error} +
+ ) : currentEvents.length === 0 ? ( +
+ No recent public activity found for this user. +
) : ( - events.slice(0, 10).map((event) => ( -
-

- {event.type === "PushEvent" && "🚀 Commit pushed"} - {event.type === "PullRequestEvent" && "🔀 Pull Request"} - {event.type === "IssuesEvent" && "🐛 Issue"} - {event.type === "WatchEvent" && "⭐ Starred repo"} - {![ - "PushEvent", - "PullRequestEvent", - "IssuesEvent", - "WatchEvent", - ].includes(event.type) && event.type} -

- -

- {event.repo?.name} • {getTimeAgo(event.created_at)} -

-
- )) +
+ {currentEvents.map((event) => ( +
+

+ {event.type === "PushEvent" && "🚀 Commit pushed"} + {event.type === "PullRequestEvent" && "🔀 Pull request event"} + {event.type === "IssuesEvent" && "🐛 Issue event"} + {event.type === "WatchEvent" && "⭐ Starred repository"} + {![ + "PushEvent", + "PullRequestEvent", + "IssuesEvent", + "WatchEvent", + ].includes(event.type) && event.type} +

+

+ {event.repo?.name || "Unknown repository"} • {getTimeAgo(event.created_at)} +

+
+ ))} +
)}
); diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index a606d777..a05a6364 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -7,17 +7,17 @@ const MATRIX_CELLS = Array.from({ length: 21 }, (_, i) => i); const Hero = () => { return (
- + {/* 1. Cyber Grid Overlay */}
- + {/* Ambient Radial Glow Elements */}
- + {/* LEFT COLUMN: Typography & CTA */}

@@ -26,9 +26,9 @@ const Hero = () => { Like Never Before

- +

- Monitor and analyze GitHub user activity with powerful insights. Perfect for developers, + Monitor and analyze GitHub user activity with powerful insights. Perfect for developers, project managers, and teams who want to understand contribution patterns and repository engagement.

@@ -40,12 +40,19 @@ const Hero = () => { Start Tracking + + + Explore Features +
{/* RIGHT COLUMN: Dashboard & Floating Git Elements */}
- + {/* The Main Dashboard Mockup Card */}
@@ -78,14 +85,14 @@ const Hero = () => { // Fully qualified class names to ensure they aren't removed by Tailwind's compilation process const intensitiesLight = ['bg-slate-200', 'bg-emerald-200', 'bg-emerald-300', 'bg-emerald-500', 'bg-emerald-600']; const intensitiesDark = ['dark:bg-slate-800', 'dark:bg-emerald-900/60', 'dark:bg-emerald-700', 'dark:bg-emerald-500', 'dark:bg-emerald-400']; - + const lightClass = intensitiesLight[cellIndex % intensitiesLight.length]; const darkClass = intensitiesDark[cellIndex % intensitiesDark.length]; - + return ( -
); })} @@ -95,21 +102,21 @@ const Hero = () => {
{/* Floating Git Icon Nodes using native inline styles for correct staggered delays */} -
-
-
diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index fd5eac86..115383bf 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -1,10 +1,21 @@ -import { NavLink, Link } from "react-router-dom"; -import { useState, useContext } from "react"; +import { Link, useLocation } from "react-router-dom"; +import { useState, useContext, useEffect } from "react"; import { ThemeContext } from "../context/ThemeContext"; -import { Moon, Sun, Menu, X, Github } from "lucide-react"; +import { Moon, Sun, Menu, X } from "lucide-react"; + +const navItems = [ + { label: "Home", to: "/", section: null }, + { label: "Features", to: "/#features", section: "features" }, + { label: "How It Works", to: "/#how-it-works", section: "how-it-works" }, + { label: "Tracker", to: "/track", section: null }, + { label: "Contributors", to: "/contributors", section: null }, + { label: "Login", to: "/login", section: null }, +]; const Navbar: React.FC = () => { const [isOpen, setIsOpen] = useState(false); + const [activeSection, setActiveSection] = useState(""); + const location = useLocation(); const themeContext = useContext(ThemeContext); @@ -12,15 +23,60 @@ const Navbar: React.FC = () => { const { toggleTheme, mode } = themeContext; - const navLinkStyles = ({ isActive }: { isActive: boolean }) => - `px-4 py-2 rounded-xl text-sm lg:text-base font-semibold transition-all duration-300 ${ - isActive - ? "text-blue-600 bg-blue-100 dark:bg-blue-900/40 shadow-sm" - : "text-slate-700 dark:text-gray-300 hover:text-blue-500" + const navLinkStyles = (isActive: boolean) => + `px-4 py-2 rounded-xl text-sm lg:text-base font-semibold transition-all duration-300 ${isActive + ? "text-blue-600 bg-blue-100 dark:bg-blue-900/40 shadow-sm" + : "text-slate-700 dark:text-gray-300 hover:text-blue-500" }`; const closeMenu = () => setIsOpen(false); + useEffect(() => { + if (location.pathname !== "/") { + setActiveSection(""); + return; + } + + const sectionIds = ["features", "how-it-works"]; + const observer = new IntersectionObserver( + (entries) => { + const visibleSection = entries + .filter((entry) => entry.isIntersecting) + .sort((a, b) => (a.target as HTMLElement).offsetTop - (b.target as HTMLElement).offsetTop)[0]; + + if (visibleSection?.target?.id) { + setActiveSection(visibleSection.target.id); + } + }, + { + threshold: 0.4, + rootMargin: "-30% 0px -55% 0px", + } + ); + + sectionIds.forEach((id) => { + const element = document.getElementById(id); + if (element) observer.observe(element); + }); + + return () => observer.disconnect(); + }, [location.pathname]); + + const getLinkActive = (item: { to: string; section: string | null }) => { + if (item.section) { + return ( + location.pathname === "/" && + (activeSection === item.section || location.hash === `#${item.section}`) + ); + } + + if (item.to === "/") { + return location.pathname === "/" && !location.hash; + } + + return location.pathname === item.to; + }; + return (
); diff --git a/src/pages/Bookmarks.tsx b/src/pages/Bookmarks.tsx new file mode 100644 index 00000000..d3329572 --- /dev/null +++ b/src/pages/Bookmarks.tsx @@ -0,0 +1,90 @@ +import React, { useEffect, useState, useContext } from 'react'; +import { ThemeContext } from '../context/ThemeContext'; +import type { ThemeContextType } from '../context/ThemeContext'; + +const backendUrl = import.meta.env.VITE_BACKEND_URL || 'http://localhost:5000'; + +interface Bookmark { + githubUsername: string; + avatarUrl?: string; + savedAt?: string; +} + +export default function Bookmarks() { + const [bookmarks, setBookmarks] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); + + const themeContext = useContext(ThemeContext) as ThemeContextType; + const { mode } = themeContext; + + const fetchBookmarks = async () => { + setLoading(true); + setError(''); + try { + const res = await fetch(`${backendUrl}/api/bookmarks`, { credentials: 'include' }); + if (!res.ok) throw new Error(await res.text()); + const data = await res.json(); + setBookmarks(data.bookmarks || []); + } catch (err: unknown) { + setError('Failed to load bookmarks'); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchBookmarks(); + }, []); + + const handleRemove = async (username: string) => { + try { + const res = await fetch(`${backendUrl}/api/bookmarks/${encodeURIComponent(username)}`, { + method: 'DELETE', + credentials: 'include', + }); + if (!res.ok) throw new Error('Failed'); + setBookmarks((prev) => prev.filter((b) => b.githubUsername.toLowerCase() !== username.toLowerCase())); + } catch (err) { + setError('Unable to remove bookmark'); + } + }; + + return ( +
+
+

Saved Bookmarks

+ +
+ {loading ? ( +

Loading...

+ ) : error ? ( +

{error}

+ ) : bookmarks.length === 0 ? ( +

No bookmarks yet.

+ ) : ( +
    + {bookmarks.map((b) => ( +
  • +
    + avatar +
    +
    {b.githubUsername}
    +
    Saved {b.savedAt ? new Date(b.savedAt).toLocaleString() : ''}
    +
    +
    + +
    + +
    +
  • + ))} +
+ )} +
+
+
+ ); +} diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index 03759ab4..52ead281 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -1,16 +1,29 @@ +import { useEffect } from "react"; +import { useLocation } from "react-router-dom"; import Hero from "../../components/Hero"; import HowItWorks from "../../components/HowItWorks"; import Features from "../../components/Features"; function Home() { + const location = useLocation(); + + useEffect(() => { + if (!location.hash) return; + + const id = location.hash.replace("#", ""); + const element = document.getElementById(id); + if (element) { + element.scrollIntoView({ behavior: "smooth", block: "start" }); + } + }, [location.hash]); + return (
- - - - + + +
- ) + ); } -export default Home +export default Home; diff --git a/src/pages/Login/Login.tsx b/src/pages/Login/Login.tsx index 92b7073e..643b1287 100644 --- a/src/pages/Login/Login.tsx +++ b/src/pages/Login/Login.tsx @@ -4,7 +4,7 @@ import { useNavigate, Link } from "react-router-dom"; import { ThemeContext } from "../../context/ThemeContext"; import type { ThemeContextType } from "../../context/ThemeContext"; -const backendUrl = import.meta.env.VITE_BACKEND_URL; +const backendUrl = import.meta.env.VITE_BACKEND_URL || 'http://localhost:5000'; interface LoginFormData { email: string; @@ -30,7 +30,9 @@ const Login: React.FC = () => { setIsLoading(true); try { - const response = await axios.post(`${backendUrl}/api/auth/login`, formData); + const response = await axios.post(`${backendUrl}/api/auth/login`, formData, { + withCredentials: true, + }); setMessage(response.data.message); if (response.data.message === 'Login successful') { @@ -49,11 +51,10 @@ const Login: React.FC = () => { return (
{/* Animated background elements */}
@@ -70,11 +71,10 @@ const Login: React.FC = () => { Logo
-

+ }`}> GitHubTracker

@@ -98,11 +98,10 @@ const Login: React.FC = () => { onChange={handleChange} autoComplete="username" required - className={`w-full pl-4 pr-4 py-4 rounded-2xl focus:outline-none transition-all ${ - mode === "dark" + className={`w-full pl-4 pr-4 py-4 rounded-2xl focus:outline-none transition-all ${mode === "dark" ? "bg-white/5 border border-white/10 text-white placeholder-slate-400 focus:ring-2 focus:ring-purple-500" : "bg-gray-100 border border-gray-300 text-gray-900 placeholder-gray-500 focus:ring-2 focus:ring-purple-400" - }`} + }`} />

@@ -115,11 +114,10 @@ const Login: React.FC = () => { value={formData.password} onChange={handleChange} required - className={`w-full pl-4 pr-4 py-4 rounded-2xl focus:outline-none transition-all ${ - mode === "dark" + className={`w-full pl-4 pr-4 py-4 rounded-2xl focus:outline-none transition-all ${mode === "dark" ? "bg-white/5 border border-white/10 text-white placeholder-slate-400 focus:ring-2 focus:ring-purple-500" : "bg-gray-100 border border-gray-300 text-gray-900 placeholder-gray-500 focus:ring-2 focus:ring-purple-400" - }`} + }`} />
@@ -134,11 +132,10 @@ const Login: React.FC = () => { {/* Message */} {message && ( -
+ }`}> {message}
)} diff --git a/src/pages/Signup/Signup.tsx b/src/pages/Signup/Signup.tsx index 2ac51dcc..76957683 100644 --- a/src/pages/Signup/Signup.tsx +++ b/src/pages/Signup/Signup.tsx @@ -6,7 +6,7 @@ import { User, Mail, Lock, Eye, EyeOff } from "lucide-react"; import { ThemeContext } from "../../context/ThemeContext"; import type { ThemeContextType } from "../../context/ThemeContext"; -const backendUrl = import.meta.env.VITE_BACKEND_URL; +const backendUrl = import.meta.env.VITE_BACKEND_URL || 'http://localhost:5000'; interface SignUpFormData { username: string; @@ -39,8 +39,8 @@ const SignUp: React.FC = () => { if (name === "username") { if (!value.trim()) { errorMessage = "Username is required"; - } else if (!/^[A-Za-z\s]+$/.test(value)) { - errorMessage = "Only letters are allowed"; + } else if (!/^[a-zA-Z0-9_]+$/.test(value)) { + errorMessage = "Username can contain letters, numbers and underscores only"; } } if (name === "email") { @@ -53,8 +53,8 @@ const SignUp: React.FC = () => { 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"; + } else if (!/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/.test(value)) { + errorMessage = "Password must be 8+ chars and include uppercase, lowercase, number, and special char"; } } setErrors((prev) => ({ ...prev, [name]: errorMessage })); @@ -64,28 +64,28 @@ const SignUp: React.FC = () => { e.preventDefault(); const usernameError = !formData.username.trim() ? "Username is required" - : !/^[A-Za-z\s]+$/.test(formData.username) - ? "Only letters are allowed" - : ""; + : !/^[a-zA-Z0-9_]+$/.test(formData.username) + ? "Username can contain letters, numbers and underscores only" + : ""; const emailError = !formData.email.trim() ? "Email is required" : !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email.trim()) - ? "Enter a valid email" - : ""; + ? "Enter a valid email" + : ""; const passwordError = !formData.password.trim() ? "Password is required" - : !/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$/.test(formData.password) - ? "Password must be 8+ characters with letters and numbers" - : ""; + : !/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/.test(formData.password) + ? "Password must be 8+ chars and include uppercase, lowercase, number, and special char" + : ""; if (usernameError || emailError || passwordError) { setErrors({ username: usernameError, email: emailError, password: passwordError }); return; } setIsLoading(true); try { - const response = await axios.post(`${backendUrl}/api/auth/signup`, - formData // Include cookies for session - ); + const response = await axios.post(`${backendUrl}/api/auth/signup`, formData, { + withCredentials: true, + }); setMessage(response.data.message); // Show success message from backend // Navigate to login page after successful signup @@ -101,32 +101,27 @@ const SignUp: React.FC = () => { return (
@@ -148,17 +143,15 @@ const SignUp: React.FC = () => { initial={{ opacity: 0, y: 30 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.6, delay: 0.2 }} - className={`rounded-3xl p-6 sm:p-10 shadow-2xl border ${ - mode === "dark" - ? "bg-white/10 backdrop-blur-xl border-white/20 text-white" - : "bg-white border-gray-200 text-black" - }`} + className={`rounded-3xl p-6 sm:p-10 shadow-2xl border ${mode === "dark" + ? "bg-white/10 backdrop-blur-xl border-white/20 text-white" + : "bg-white border-gray-200 text-black" + }`} > - +

Create Account

@@ -196,7 +189,7 @@ const SignUp: React.FC = () => { - @@ -231,9 +224,8 @@ const SignUp: React.FC = () => {
);