Added navigation menu in header.

This commit is contained in:
Josh 2025-05-02 15:39:51 +00:00
parent 8917e4890e
commit 34fda5760d
2 changed files with 168 additions and 38 deletions

View File

@ -1,5 +1,14 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Routes, Route, Navigate, useNavigate, useLocation } from 'react-router-dom'; import {
Routes,
Route,
Navigate,
useNavigate,
useLocation,
Link,
} from 'react-router-dom';
// Import all components from the components folder
import PremiumRoute from './components/PremiumRoute.js'; import PremiumRoute from './components/PremiumRoute.js';
import SessionExpiredHandler from './components/SessionExpiredHandler.js'; import SessionExpiredHandler from './components/SessionExpiredHandler.js';
import GettingStarted from './components/GettingStarted.js'; import GettingStarted from './components/GettingStarted.js';
@ -18,16 +27,10 @@ function App() {
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
// Track whether user is authenticated
const [isAuthenticated, setIsAuthenticated] = useState(false); const [isAuthenticated, setIsAuthenticated] = useState(false);
// Track the user object, including is_premium
const [user, setUser] = useState(null); const [user, setUser] = useState(null);
// We might also track a "loading" state so we don't redirect prematurely
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
// Hide the "Upgrade to Premium" CTA on certain premium routes
const premiumPaths = [ const premiumPaths = [
'/milestone-tracker', '/milestone-tracker',
'/paywall', '/paywall',
@ -37,35 +40,30 @@ function App() {
]; ];
const showPremiumCTA = !premiumPaths.includes(location.pathname); const showPremiumCTA = !premiumPaths.includes(location.pathname);
// On first mount, rehydrate user if there's a token // Rehydrate user if there's a token
useEffect(() => { useEffect(() => {
const token = localStorage.getItem('token'); const token = localStorage.getItem('token');
if (!token) { if (!token) {
setIsLoading(false); setIsLoading(false);
return; // No token => not authenticated return;
} }
// If we have a token, let's validate it and fetch user data // Validate token/fetch user
fetch('https://dev1.aptivaai.com/api/user-profile', { fetch('https://dev1.aptivaai.com/api/user-profile', {
headers: { headers: { Authorization: `Bearer ${token}` },
Authorization: `Bearer ${token}`,
},
}) })
.then((res) => { .then((res) => {
if (!res.ok) { if (!res.ok) {
// e.g. 401 means token invalid/expired throw new Error('Token invalid/expired');
throw new Error('Token invalid or expired');
} }
return res.json(); return res.json();
}) })
.then((profile) => { .then((profile) => {
// We have a valid user profile -> set states
setUser(profile); setUser(profile);
setIsAuthenticated(true); setIsAuthenticated(true);
}) })
.catch((err) => { .catch((err) => {
console.error(err); console.error(err);
// If invalid, remove the token to avoid loops
localStorage.removeItem('token'); localStorage.removeItem('token');
}) })
.finally(() => { .finally(() => {
@ -73,8 +71,15 @@ function App() {
}); });
}, []); }, []);
// Logout
const handleLogout = () => {
localStorage.removeItem('token');
setIsAuthenticated(false);
setUser(null);
navigate('/signin');
};
if (isLoading) { if (isLoading) {
// Optionally show a loading spinner or placeholder
return ( return (
<div className="flex h-screen items-center justify-center"> <div className="flex h-screen items-center justify-center">
<p>Loading...</p> <p>Loading...</p>
@ -86,10 +91,138 @@ function App() {
<div className="flex min-h-screen flex-col bg-gray-50 text-gray-800"> <div className="flex min-h-screen flex-col bg-gray-50 text-gray-800">
{/* Header */} {/* Header */}
<header className="flex items-center justify-between border-b bg-white px-6 py-4 shadow-sm"> <header className="flex items-center justify-between border-b bg-white px-6 py-4 shadow-sm">
<h1 className="text-lg font-semibold"> <div className="flex items-center space-x-8">
AptivaAI - Career Guidance Platform (beta) <h1 className="text-lg font-semibold">
</h1> AptivaAI - Career Guidance Platform (beta)
{showPremiumCTA && ( </h1>
{/* Navigation Menu */}
{isAuthenticated && (
<nav>
<ul className="flex space-x-4">
{/* Free sections */}
<li>
<Link
className="text-blue-600 hover:text-blue-800"
to="/getting-started"
>
Getting Started
</Link>
</li>
<li>
<Link
className="text-blue-600 hover:text-blue-800"
to="/interest-inventory"
>
Interest Inventory
</Link>
</li>
<li>
<Link
className="text-blue-600 hover:text-blue-800"
to="/dashboard"
>
Dashboard
</Link>
</li>
<li>
<Link
className="text-blue-600 hover:text-blue-800"
to="/profile"
>
Profile
</Link>
</li>
{/* Premium sections (greyed out if not is_premium) */}
<li>
{user?.is_premium ? (
<Link
className="text-blue-600 hover:text-blue-800"
to="/milestone-tracker"
>
Milestone Tracker
</Link>
) : (
<span className="text-gray-400 cursor-not-allowed">
Milestone Tracker{' '}
<span className="text-green-600">
(Premium Subscribers Only)
</span>
</span>
)}
</li>
<li>
{user?.is_premium ? (
<Link
className="text-blue-600 hover:text-blue-800"
to="/financial-profile"
>
Financial Profile
</Link>
) : (
<span className="text-gray-400 cursor-not-allowed">
Financial Profile{' '}
<span className="text-green-600">
(Premium Subscribers Only)
</span>
</span>
)}
</li>
<li>
{user?.is_premium ? (
<Link
className="text-blue-600 hover:text-blue-800"
to="/multi-scenario"
>
Multi Scenario
</Link>
) : (
<span className="text-gray-400 cursor-not-allowed">
Multi Scenario{' '}
<span className="text-green-600">
(Premium Subscribers Only)
</span>
</span>
)}
</li>
<li>
{user?.is_premium ? (
<Link
className="text-blue-600 hover:text-blue-800"
to="/premium-onboarding"
>
Premium Onboarding
</Link>
) : (
<span className="text-gray-400 cursor-not-allowed">
Premium Onboarding{' '}
<span className="text-green-600">
(Premium Subscribers Only)
</span>
</span>
)}
</li>
{/* Logout */}
<li>
<button
className="text-red-600 hover:text-red-800"
onClick={handleLogout}
>
Logout
</button>
</li>
</ul>
</nav>
)}
</div>
{/* "Upgrade to Premium" button if not premium and on a free path */}
{showPremiumCTA && isAuthenticated && !user?.is_premium && (
<button <button
className="rounded bg-blue-600 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700" className="rounded bg-blue-600 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700"
onClick={() => navigate('/paywall')} onClick={() => navigate('/paywall')}
@ -102,22 +235,19 @@ function App() {
{/* Main Content */} {/* Main Content */}
<main className="flex-1 p-6"> <main className="flex-1 p-6">
<Routes> <Routes>
{/* Default to /signin if no path */} {/* Default to /signin */}
<Route path="/" element={<Navigate to="/signin" />} /> <Route path="/" element={<Navigate to="/signin" />} />
{/* Public routes */} {/* Public routes */}
<Route <Route
path="/signin" path="/signin"
element={ element={
<SignIn <SignIn setIsAuthenticated={setIsAuthenticated} setUser={setUser} />
setIsAuthenticated={setIsAuthenticated}
setUser={setUser}
/>
} }
/> />
<Route path="/signup" element={<SignUp />} /> <Route path="/signup" element={<SignUp />} />
{/* Paywall (accessible to all) */} {/* Paywall (public) */}
<Route path="/paywall" element={<Paywall />} /> <Route path="/paywall" element={<Paywall />} />
{/* Authenticated routes */} {/* Authenticated routes */}
@ -128,7 +258,7 @@ function App() {
<Route path="/dashboard" element={<Dashboard />} /> <Route path="/dashboard" element={<Dashboard />} />
<Route path="/profile" element={<UserProfile />} /> <Route path="/profile" element={<UserProfile />} />
{/* Premium-only routes use <PremiumRoute> */} {/* Premium-only routes */}
<Route <Route
path="/milestone-tracker" path="/milestone-tracker"
element={ element={
@ -145,14 +275,6 @@ function App() {
</PremiumRoute> </PremiumRoute>
} }
/> />
<Route
path="/premium-onboarding"
element={
<PremiumRoute user={user}>
<OnboardingContainer />
</PremiumRoute>
}
/>
<Route <Route
path="/multi-scenario" path="/multi-scenario"
element={ element={
@ -161,10 +283,18 @@ function App() {
</PremiumRoute> </PremiumRoute>
} }
/> />
<Route
path="/premium-onboarding"
element={
<PremiumRoute user={user}>
<OnboardingContainer />
</PremiumRoute>
}
/>
</> </>
)} )}
{/* Fallback if route not found */} {/* 404 / Fallback */}
<Route path="*" element={<Navigate to="/signin" />} /> <Route path="*" element={<Navigate to="/signin" />} />
</Routes> </Routes>
</main> </main>

Binary file not shown.