diff --git a/frontend/src/App.css b/frontend/src/App.css index b9d355d..5819d4b 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -1,8 +1,5 @@ #root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; + min-height: 100vh; } .logo { diff --git a/frontend/src/assets/styling/underworld.css b/frontend/src/assets/styling/underworld.css index 2deb7c0..2f4411d 100644 --- a/frontend/src/assets/styling/underworld.css +++ b/frontend/src/assets/styling/underworld.css @@ -1,19 +1,14 @@ /* Custom Underworld styling */ -:root { +/* :root { --und_background: linear-gradient(360deg, rgba(10, 10, 20, 1) 10%, rgba(50, 0, 0, 1) 60%, rgba(100, 0, 0, 1) 120%); --und_boss_container: linear-gradient(180deg, rgba(30, 30, 40, 0.8), rgba(10, 10, 20, 0.7)); --und_primary_color: #f93a33; --und_secondary_color: #d2d0d0; --und_font_primary: "Barlow", sans-serif; -} - +} */ -body { - background: var(--und_background); -} - .underworld_primary_text { color: var(--und_primary_color); diff --git a/frontend/src/components/Auth/LoginForm.jsx b/frontend/src/components/Auth/LoginForm.jsx index 654d86b..652338d 100644 --- a/frontend/src/components/Auth/LoginForm.jsx +++ b/frontend/src/components/Auth/LoginForm.jsx @@ -4,6 +4,9 @@ import { useAuth } from "../../context/AuthContext"; import { useNavigate, useLocation } from "react-router-dom"; import Modal from "../Layout/Modal"; +// Custom images +import heroAvatar from "../../assets/img/hero_avatar.png"; + function EyeIcon() { return ( @@ -28,7 +31,7 @@ const BrandLogo = () => ( - SkillForge + Skill Forge ); @@ -39,7 +42,7 @@ const features = [ ), - label: "500+ interactive courses across all levels", + label: "Variety of challenging quests to build real skills", }, { icon: ( @@ -47,7 +50,7 @@ const features = [ ), - label: "Real-world projects & coding challenges", + label: "Real time review and AI powered feedback on your code", }, { icon: ( @@ -55,7 +58,7 @@ const features = [ ), - label: "Vibrant community of 10,000+ developers", + label: "Progress tracking, achievements and leaderboards to keep you motivated", }, ]; @@ -113,13 +116,13 @@ export default function LoginForm() {
-

+

Master skills.
Build the future.

-

- Join thousands of learners advancing their careers through interactive, hands-on courses. +

+ Solve challenging coding quests, get real-time feedback, and track your progress. Join a community of learners and level up your skills with SkillForge.

@@ -129,32 +132,39 @@ export default function LoginForm() {
{feature.icon}
- {feature.label} + {feature.label}
))} -

© 2026 SkillForge. All rights reserved.

+

© 2026 Skill Forge. All rights reserved.

{/* Right form panel */}
-
+ {/* Hero avatar — decorative background, sits behind the form */} + +
{/* Mobile logo */}
-

Welcome back

-

Sign in to continue your learning journey.

+

Welcome back

+

Sign in to continue your learning journey.

{/* Email */} )} @@ -244,7 +254,7 @@ export default function LoginForm() { )} -

+

Don't have an account?{" "} Register here diff --git a/frontend/src/components/Auth/SignupForm.jsx b/frontend/src/components/Auth/SignupForm.jsx index e9895c6..afe5d67 100644 --- a/frontend/src/components/Auth/SignupForm.jsx +++ b/frontend/src/components/Auth/SignupForm.jsx @@ -2,6 +2,9 @@ import { useState } from "react"; import { signup } from "../../services/authService"; import { useNavigate } from "react-router-dom"; +// Custom images +import heroAvatar from "../../assets/img/hero_avatar.png"; + function EyeIcon() { return ( @@ -26,7 +29,7 @@ const BrandLogo = () => (

- SkillForge + Skill Forge
); @@ -37,7 +40,7 @@ const features = [ ), - label: "Free to start — no credit card required", + label: "Always free, forever. No credit card required.", }, { icon: ( @@ -53,7 +56,7 @@ const features = [ ), - label: "Earn certificates to showcase your skills", + label: "Earn badges and compete with others", }, ]; @@ -122,12 +125,12 @@ export default function SignupForm() {
-

+

Start your journey.
Level up fast.

-

+

Create your free account and unlock a world of skills, projects, and a community that grows with you.

@@ -138,32 +141,38 @@ export default function SignupForm() {
{feature.icon}
- {feature.label} + {feature.label}
))}
-

© 2026 SkillForge. All rights reserved.

+

© 2026 Skill Forge. All rights reserved.

{/* Right form panel */}
-
+
+ {/* Mobile logo */}
-

Create an account

-

It's free and only takes a minute.

+

Create an account

+

It's free and only takes a minute.

{/* Username */}
-
-
@@ -260,7 +269,7 @@ export default function SignupForm() { required className="w-4 h-4 mt-0.5 rounded accent-[#03e9f4] cursor-pointer flex-shrink-0" /> - Terms of Service @@ -301,7 +310,7 @@ export default function SignupForm() { )} -

+

Already have an account?{" "} Sign in here diff --git a/frontend/src/components/Layout/Navbar.jsx b/frontend/src/components/Layout/Navbar.jsx index 18f9811..274d679 100644 --- a/frontend/src/components/Layout/Navbar.jsx +++ b/frontend/src/components/Layout/Navbar.jsx @@ -1,60 +1,351 @@ -import { Link } from "react-router-dom"; -import { useEffect, useState } from "react"; -import { getAvatarUrl } from "../../services/useAvatarUrl"; -import "../../assets/styling/navbar.css"; -import { FiSettings } from "react-icons/fi"; -import skillForgeLogo from "../../assets/img/skill_forge_logo.png"; +import { Link, useLocation } from "react-router-dom"; +import { useEffect, useRef, useState } from "react"; import { useAuth } from "../../context/AuthContext"; +import { getUserById, getAvatarUrl } from "../../services/usersService"; +import skillForgeLogo from "../../assets/img/skill_forge_logo.png"; -const USER_API = import.meta.env.VITE_USERS_SERVICE_URL; +const LANGUAGES = [ + { name: "Python", path: "/quests/Python" }, + { name: "JavaScript", path: "/quests/JavaScript" }, + { name: "Java", path: "/quests/Java" }, + { name: "C#", path: "/quests/Csharp" }, +]; export default function Navbar() { const { user, logout } = useAuth(); - const [avatarUrl, setAvatarUrl] = useState(null); + const location = useLocation(); + + const [avatarUrl, setAvatarUrl] = useState(null); + const [stats, setStats] = useState(null); + const [userDropOpen, setUserDropOpen] = useState(false); + const [mobileMenuOpen, setMobileMenuOpen] = useState(false); + const [questsDropOpen, setQuestsDropOpen] = useState(false); + + const userDropRef = useRef(null); + + // Fetch avatar + stats whenever the logged-in user changes + useEffect(() => { + if (!user?.id) return; + let alive = true; + + getAvatarUrl(user.id).then((url) => { if (alive) setAvatarUrl(url); }); + getUserById(user.id) + .then((data) => { + if (alive) + setStats({ level: data.level, level_percentage: data.level_percentage }); + }) + .catch(() => {}); + + return () => { alive = false; }; + }, [user?.id]); + + // Close everything on route change + useEffect(() => { + setMobileMenuOpen(false); + setUserDropOpen(false); + }, [location.pathname]); + // Close user dropdown when clicking outside useEffect(() => { - if (user?.id) { - getAvatarUrl(user.id, null, USER_API).then((url) => { - setAvatarUrl(url); - }); - } - }, [user]); + if (!userDropOpen) return; + const handler = (e) => { + if (userDropRef.current && !userDropRef.current.contains(e.target)) + setUserDropOpen(false); + }; + document.addEventListener("mousedown", handler); + return () => document.removeEventListener("mousedown", handler); + }, [userDropOpen]); - const handleLogout = async () => { - await logout(); - }; + const isActive = (path) => location.pathname === path; + const isQuestsActive = location.pathname.startsWith("/quests") || location.pathname.startsWith("/quest/"); + const initials = user?.username?.[0]?.toUpperCase() ?? "?"; + + const desktopLinkClass = (active) => + `text-sm font-medium transition-colors duration-150 normal_text ${ + active ? "text-[#03e9f4]" : "text-white/50 hover:text-white/90" + }`; + + const mobileLinkClass = (active) => + `block px-3 py-2.5 rounded-lg text-sm font-medium transition-colors duration-150 ${ + active + ? "text-[#03e9f4] bg-[#03e9f4]/[0.08]" + : "text-white/60 hover:text-white hover:bg-white/[0.04]" + }`; return ( -