346 lines
11 KiB
JavaScript
346 lines
11 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
|
import {
|
|
Routes,
|
|
Route,
|
|
Navigate,
|
|
useNavigate,
|
|
useLocation,
|
|
Link,
|
|
} from 'react-router-dom';
|
|
|
|
// Import all components
|
|
import PremiumRoute from './components/PremiumRoute.js';
|
|
import SessionExpiredHandler from './components/SessionExpiredHandler.js';
|
|
import GettingStarted from './components/GettingStarted.js';
|
|
import SignIn from './components/SignIn.js';
|
|
import SignUp from './components/SignUp.js';
|
|
import InterestInventory from './components/InterestInventory.js';
|
|
import Dashboard from './components/Dashboard.js';
|
|
import UserProfile from './components/UserProfile.js';
|
|
import FinancialProfileForm from './components/FinancialProfileForm.js';
|
|
import MilestoneTracker from './components/MilestoneTracker.js';
|
|
import Paywall from './components/Paywall.js';
|
|
import OnboardingContainer from './components/PremiumOnboarding/OnboardingContainer.js';
|
|
import MultiScenarioView from './components/MultiScenarioView.js';
|
|
|
|
// 1) Import your ResumeRewrite component
|
|
import ResumeRewrite from './components/ResumeRewrite.js'; // adjust the path if needed
|
|
|
|
function App() {
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
|
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
const [user, setUser] = useState(null);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
|
|
// If you want Resume Optimizer to be considered a "premium path" too:
|
|
const premiumPaths = [
|
|
'/milestone-tracker',
|
|
'/paywall',
|
|
'/financial-profile',
|
|
'/multi-scenario',
|
|
'/premium-onboarding',
|
|
'/resume-optimizer', // add it here if you want
|
|
];
|
|
const showPremiumCTA = !premiumPaths.includes(location.pathname);
|
|
|
|
// 2) We'll define "canAccessPremium" to handle *both* is_premium or is_pro_premium
|
|
const canAccessPremium = user?.is_premium || user?.is_pro_premium;
|
|
|
|
// Rehydrate user if there's a token
|
|
useEffect(() => {
|
|
const token = localStorage.getItem('token');
|
|
if (!token) {
|
|
setIsLoading(false);
|
|
return;
|
|
}
|
|
|
|
// Validate token/fetch user
|
|
fetch('https://dev1.aptivaai.com/api/user-profile', {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
.then((res) => {
|
|
if (!res.ok) {
|
|
throw new Error('Token invalid/expired');
|
|
}
|
|
return res.json();
|
|
})
|
|
.then((profile) => {
|
|
setUser(profile);
|
|
setIsAuthenticated(true);
|
|
})
|
|
.catch((err) => {
|
|
console.error(err);
|
|
localStorage.removeItem('token');
|
|
})
|
|
.finally(() => {
|
|
setIsLoading(false);
|
|
});
|
|
}, []);
|
|
|
|
// Logout
|
|
const handleLogout = () => {
|
|
localStorage.removeItem('token');
|
|
setIsAuthenticated(false);
|
|
setUser(null);
|
|
navigate('/signin');
|
|
};
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="flex h-screen items-center justify-center">
|
|
<p>Loading...</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="flex min-h-screen flex-col bg-gray-50 text-gray-800">
|
|
{/* Header */}
|
|
<header className="flex items-center justify-between border-b bg-white px-6 py-4 shadow-sm">
|
|
<div className="flex items-center space-x-8">
|
|
<h1 className="text-lg font-semibold">
|
|
AptivaAI - Career Guidance Platform (beta)
|
|
</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 (still checking only user?.is_premium for these) */}
|
|
<li>
|
|
{user?.is_premium ? (
|
|
<Link
|
|
className="text-blue-600 hover:text-blue-800"
|
|
to="/milestone-tracker"
|
|
>
|
|
Career Planner
|
|
</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>
|
|
|
|
{/* 3) A new link for Resume Optimizer that checks canAccessPremium */}
|
|
<li>
|
|
{canAccessPremium ? (
|
|
<Link
|
|
className="text-blue-600 hover:text-blue-800"
|
|
to="/resume-optimizer"
|
|
>
|
|
Resume Optimizer
|
|
</Link>
|
|
) : (
|
|
<span className="text-gray-400 cursor-not-allowed">
|
|
Resume Optimizer{' '}
|
|
<span className="text-green-600">
|
|
(Premium or Pro 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/pro and on a free path */}
|
|
{showPremiumCTA && isAuthenticated && !canAccessPremium && (
|
|
<button
|
|
className="rounded bg-blue-600 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700"
|
|
onClick={() => navigate('/paywall')}
|
|
>
|
|
Upgrade to Premium
|
|
</button>
|
|
)}
|
|
</header>
|
|
|
|
{/* Main Content */}
|
|
<main className="flex-1 p-6">
|
|
<Routes>
|
|
{/* Default to /signin */}
|
|
<Route path="/" element={<Navigate to="/signin" />} />
|
|
|
|
{/* Public routes */}
|
|
<Route
|
|
path="/signin"
|
|
element={
|
|
<SignIn setIsAuthenticated={setIsAuthenticated} setUser={setUser} />
|
|
}
|
|
/>
|
|
<Route path="/signup" element={<SignUp />} />
|
|
|
|
{/* Paywall (public) */}
|
|
<Route path="/paywall" element={<Paywall />} />
|
|
|
|
{/* Authenticated routes */}
|
|
{isAuthenticated && (
|
|
<>
|
|
<Route path="/getting-started" element={<GettingStarted />} />
|
|
<Route path="/interest-inventory" element={<InterestInventory />} />
|
|
<Route path="/dashboard" element={<Dashboard />} />
|
|
<Route path="/profile" element={<UserProfile />} />
|
|
|
|
{/* Premium-only routes */}
|
|
<Route
|
|
path="/milestone-tracker"
|
|
element={
|
|
<PremiumRoute user={user}>
|
|
<MilestoneTracker />
|
|
</PremiumRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/financial-profile"
|
|
element={
|
|
<PremiumRoute user={user}>
|
|
<FinancialProfileForm />
|
|
</PremiumRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/multi-scenario"
|
|
element={
|
|
<PremiumRoute user={user}>
|
|
<MultiScenarioView />
|
|
</PremiumRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/premium-onboarding"
|
|
element={
|
|
<PremiumRoute user={user}>
|
|
<OnboardingContainer />
|
|
</PremiumRoute>
|
|
}
|
|
/>
|
|
|
|
{/* 4) The new Resume Optimizer route */}
|
|
<Route
|
|
path="/resume-optimizer"
|
|
element={
|
|
<PremiumRoute user={user}>
|
|
<ResumeRewrite />
|
|
</PremiumRoute>
|
|
}
|
|
/>
|
|
</>
|
|
)}
|
|
|
|
{/* 404 / Fallback */}
|
|
<Route path="*" element={<Navigate to="/signin" />} />
|
|
</Routes>
|
|
</main>
|
|
|
|
{/* Session Handler */}
|
|
<SessionExpiredHandler />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default App;
|