Added navigation menu in header.
This commit is contained in:
parent
8917e4890e
commit
34fda5760d
206
src/App.js
206
src/App.js
@ -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>
|
||||||
|
BIN
user_profile.db
BIN
user_profile.db
Binary file not shown.
Loading…
Reference in New Issue
Block a user