From 7ba5b29b34f445c50f463e2954bd2c7a1aeb38cf Mon Sep 17 00:00:00 2001 From: Uyoxy Date: Thu, 5 Mar 2026 00:49:13 +0000 Subject: [PATCH] feat: add Navbar, Footer, and MainLayout with responsive routing --- package-lock.json | 22 +- src/components/layout/Footer.jsx | 226 +++++++++++++++ src/components/layout/MainLayout.jsx | 92 +++--- src/components/layout/Navbar.jsx | 419 +++++++++++++++++++++++++++ src/routes/AppRouter.jsx | 46 +-- 5 files changed, 737 insertions(+), 68 deletions(-) create mode 100644 src/components/layout/Footer.jsx create mode 100644 src/components/layout/Navbar.jsx diff --git a/package-lock.json b/package-lock.json index bac82be..d92eacd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -83,6 +83,7 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1804,6 +1805,7 @@ "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -1851,6 +1853,7 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2013,6 +2016,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -2179,7 +2183,8 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/debug": { "version": "4.4.3", @@ -2376,6 +2381,7 @@ "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -3556,6 +3562,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -3583,6 +3590,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -3650,6 +3658,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -3659,6 +3668,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -3671,6 +3681,7 @@ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.71.2.tgz", "integrity": "sha512-1CHvcDYzuRUNOflt4MOq3ZM46AronNJtQ1S7tnX6YN4y72qhgiUItpacZUAQ0TyWYci3yz1X+rXaSxiuEm86PA==", "license": "MIT", + "peer": true, "engines": { "node": ">=18.0.0" }, @@ -3713,6 +3724,7 @@ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", + "peer": true, "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" @@ -3783,7 +3795,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/redux-persist": { "version": "6.0.0", @@ -3950,7 +3963,8 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz", "integrity": "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/tapable": { "version": "2.3.0", @@ -4083,6 +4097,7 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -4216,6 +4231,7 @@ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/src/components/layout/Footer.jsx b/src/components/layout/Footer.jsx new file mode 100644 index 0000000..37600df --- /dev/null +++ b/src/components/layout/Footer.jsx @@ -0,0 +1,226 @@ +import { Link } from 'react-router-dom'; + +const PLATFORM_LINKS = [ + { label: 'How it works', to: '/how-it-works' }, + { label: 'Projects', to: '/explore' }, + { label: 'About', to: '/about' }, +]; + +const LEGAL_LINKS = [ + { label: 'Privacy', to: '/privacy' }, + { label: 'Terms', to: '/terms' }, + { label: 'Contact', to: '/contact' }, +]; + +/* Simple inline SVGs — no external icon library required */ +const TwitterIcon = () => ( + +); + +const DiscordIcon = () => ( + +); + +const GitHubIcon = () => ( + +); + +export default function Footer() { + return ( + <> + + + + + ); +} \ No newline at end of file diff --git a/src/components/layout/MainLayout.jsx b/src/components/layout/MainLayout.jsx index e619acb..a2d2dfc 100644 --- a/src/components/layout/MainLayout.jsx +++ b/src/components/layout/MainLayout.jsx @@ -1,9 +1,33 @@ -import { Outlet, Link } from 'react-router-dom'; +import { Outlet } from 'react-router-dom'; import { Toaster } from 'react-hot-toast'; +import Navbar from './Navbar'; +import Footer from './Footer'; -const MainLayout = () => { +/** + * MainLayout + * Wraps every public page with the sticky Navbar and Footer. + * Usage in AppRouter.jsx: + * + * }> + * } /> + * } /> + * … + * + */ +export default function MainLayout() { return ( -
+ <> + + { background: '#ffffff', color: '#0F172A', borderRadius: '8px', + fontFamily: "'Poppins', sans-serif", + fontSize: '.875rem', boxShadow: - '0 10px 15px -3px rgba(15,23,42,0.1), 0 4px 6px -4px rgba(15,23,42,0.1)', + '0 10px 15px -3px rgba(15,23,42,.1), 0 4px 6px -4px rgba(15,23,42,.1)', }, success: { - iconTheme: { - primary: '#10B981', - secondary: '#ffffff', - }, - style: { - borderColor: '#10B981', - }, + iconTheme: { primary: '#10B981', secondary: '#ffffff' }, + style: { borderColor: '#10B981' }, }, error: { - iconTheme: { - primary: '#EF4444', - secondary: '#ffffff', - }, - style: { - borderColor: '#EF4444', - }, + iconTheme: { primary: '#EF4444', secondary: '#ffffff' }, + style: { borderColor: '#EF4444' }, }, loading: { - iconTheme: { - primary: '#6366F1', - secondary: '#ffffff', - }, - style: { - borderColor: '#6366F1', - }, + iconTheme: { primary: '#6366F1', secondary: '#ffffff' }, + style: { borderColor: '#6366F1' }, }, }} /> -
- -
- -
- -
-
-

© {new Date().getFullYear()} StellarAid. All rights reserved.

-
-
+
+ +
+ +
+
+
+ ); -}; - -export default MainLayout; +} \ No newline at end of file diff --git a/src/components/layout/Navbar.jsx b/src/components/layout/Navbar.jsx new file mode 100644 index 0000000..a06fc4a --- /dev/null +++ b/src/components/layout/Navbar.jsx @@ -0,0 +1,419 @@ +import { useState, useRef, useEffect } from 'react'; +import { Link, NavLink, useNavigate } from 'react-router-dom'; + +// ─── Replace with your real auth hook / context ─────────────────────────────── +// e.g. import { useAuth } from '../../contexts/AuthContext'; +const useAuth = () => ({ + user: null, // set to a user object when logged in, e.g. { name: 'Alice', avatarUrl: null } + logout: () => {}, +}); +// ───────────────────────────────────────────────────────────────────────────── + +const NAV_LINKS = [ + { label: 'How it works', to: '/how-it-works' }, + { label: 'Projects', to: '/explore' }, + { label: 'About', to: '/about' }, +]; + +function initials(name = '') { + return name + .split(' ') + .map((w) => w[0]) + .join('') + .toUpperCase() + .slice(0, 2); +} + +export default function Navbar() { + const { user, logout } = useAuth(); + const navigate = useNavigate(); + + const [menuOpen, setMenuOpen] = useState(false); + const [avatarOpen, setAvatarOpen] = useState(false); + const avatarRef = useRef(null); + + /* close avatar dropdown on outside click */ + useEffect(() => { + function handler(e) { + if (avatarRef.current && !avatarRef.current.contains(e.target)) { + setAvatarOpen(false); + } + } + document.addEventListener('mousedown', handler); + return () => document.removeEventListener('mousedown', handler); + }, []); + + /* close mobile menu on route change */ + const handleNavClick = () => setMenuOpen(false); + + const handleLogout = () => { + setAvatarOpen(false); + logout(); + navigate('/'); + }; + + return ( + <> + + + + + ); +} \ No newline at end of file diff --git a/src/routes/AppRouter.jsx b/src/routes/AppRouter.jsx index 027afad..fc72258 100644 --- a/src/routes/AppRouter.jsx +++ b/src/routes/AppRouter.jsx @@ -13,12 +13,12 @@ import Register from '../pages/Register'; import ForgotPasswordPage from '../pages/auth/ForgotPasswordPage'; // Main pages — lazy-loaded for code splitting -const Home = lazy(() => import('../pages/Home')); -const Explore = lazy(() => import('../pages/Explore')); +const Home = lazy(() => import('../pages/Home')); +const Explore = lazy(() => import('../pages/Explore')); const CampaignDetails = lazy(() => import('../pages/CampaignDetails')); -const CreateCampaign = lazy(() => import('../pages/CreateCampaign')); -const Dashboard = lazy(() => import('../pages/Dashboard')); -const Admin = lazy(() => import('../pages/Admin')); +const CreateCampaign = lazy(() => import('../pages/CreateCampaign')); +const Dashboard = lazy(() => import('../pages/Dashboard')); +const Admin = lazy(() => import('../pages/Admin')); const SuspenseFallback = () => (
@@ -32,18 +32,28 @@ const AppRouter = () => { }> - } /> - } /> - } /> - } /> - } /> - } /> - }> - } /> - } /> - } /> - } /> - } /> + {/* Every route lives inside MainLayout — Navbar + Footer always present */} + }> + + {/* Auth & utility pages */} + } /> + } /> + } /> + } /> + } /> + + {/* Main public pages */} + + } /> + } /> + } /> + } /> + } /> + + + {/* 404 — also gets Navbar + Footer */} + } /> + @@ -52,4 +62,4 @@ const AppRouter = () => { ); }; -export default AppRouter; +export default AppRouter; \ No newline at end of file