import React, { useState, useEffect, useMemo } from 'react'; import { Routes, Route, Navigate, useNavigate, useLocation, Link, } from 'react-router-dom'; import { Button } from './components/ui/button.js'; import { cn } from './utils/cn.js'; import PromptModal from './components/ui/PromptModal.js'; import PremiumRoute from './components/PremiumRoute.js'; import SessionExpiredHandler from './components/SessionExpiredHandler.js'; import SignInLanding from './components/SignInLanding.js'; import SignIn from './components/SignIn.js'; import SignUp from './components/SignUp.js'; import PlanningLanding from './components/PlanningLanding.js'; import CareerExplorer from './components/CareerExplorer.js'; import PreparingLanding from './components/PreparingLanding.js'; import EducationalProgramsPage from './components/EducationalProgramsPage.js'; import EnhancingLanding from './components/EnhancingLanding.js'; import RetirementLanding from './components/RetirementLanding.js'; import InterestInventory from './components/InterestInventory.js'; import UserProfile from './components/UserProfile.js'; import FinancialProfileForm from './components/FinancialProfileForm.js'; import CareerProfileList from './components/CareerProfileList.js'; import CareerProfileForm from './components/CareerProfileForm.js'; import CollegeProfileList from './components/CollegeProfileList.js'; import CollegeProfileForm from './components/CollegeProfileForm.js'; import CareerRoadmap from './components/CareerRoadmap.js'; import Paywall from './components/Paywall.js'; import OnboardingContainer from './components/PremiumOnboarding/OnboardingContainer.js'; import RetirementPlanner from './components/RetirementPlanner.js'; import ResumeRewrite from './components/ResumeRewrite.js'; import LoanRepaymentPage from './components/LoanRepaymentPage.js'; import usePageContext from './utils/usePageContext.js'; import ChatDrawer from './components/ChatDrawer.js'; import ChatCtx from './contexts/ChatCtx.js'; import BillingResult from './components/BillingResult.js'; import SupportModal from './components/SupportModal.js'; import ForgotPassword from './components/ForgotPassword.js'; import ResetPassword from './components/ResetPassword.js'; import { clearToken } from './auth/authMemory.js'; import api from './auth/apiClient.js'; import * as safeLocal from './utils/safeLocal.js'; export const ProfileCtx = React.createContext(); function ResetPasswordGate() { const location = useLocation(); useEffect(() => { clearToken(); try { localStorage.removeItem('id'); } catch {} }, [location.pathname]); return ; } function App() { const navigate = useNavigate(); const location = useLocation(); const { pageContext, snapshot: routeSnapshot } = usePageContext(); const [drawerOpen, setDrawerOpen] = useState(false); const [drawerPane, setDrawerPane] = useState('support'); const [retireProps, setRetireProps] = useState(null); const [supportOpen, setSupportOpen] = useState(false); const [userEmail, setUserEmail] = useState(''); const [loggingOut, setLoggingOut] = useState(false); const AUTH_HOME = '/signin-landing'; /* ------------------------------------------ ChatDrawer – route-aware tool handlers ------------------------------------------ */ const uiToolHandlers = useMemo(() => { if (pageContext === "CareerExplorer") { return { // __tool:addCareerToComparison:{"socCode":"15-2051","careerName":"Data Scientist"} addCareerToComparison: ({ socCode, careerName }) => { console.log('[dispatch]', socCode, careerName); window.dispatchEvent( new CustomEvent("add-career", { detail: { socCode, careerName } }) ); }, // __tool:openCareerModal:{"socCode":"15-2051"} openCareerModal: ({ socCode }) => { window.dispatchEvent( new CustomEvent("open-career", { detail: { socCode } }) ); } }; } return {}; // every other page exposes no UI tools }, [pageContext]); // Retirement bot is only relevant on these pages const canShowRetireBot = pageContext === 'RetirementPlanner' || pageContext === 'RetirementLanding'; // Auth states const [isAuthenticated, setIsAuthenticated] = useState(false); const [user, setUser] = useState(null); const [chatSnapshot, setChatSnapshot] = useState(null); // Loading state while verifying token const [isLoading, setIsLoading] = useState(true); // User states const [financialProfile, setFinancialProfile] = useState(null); const [scenario, setScenario] = useState(null); // Logout warning modal const [showLogoutWarning, setShowLogoutWarning] = useState(false); // Check if user can access premium const canAccessPremium = user?.is_premium || user?.is_pro_premium; const isAuthScreen = React.useMemo(() => { const p = location.pathname; return ( p === '/signin' || p === '/signup' || p === '/forgot-password' || p.startsWith('/reset-password') ); }, [location.pathname]); const showAuthedNav = isAuthenticated && !isAuthScreen; // List of premium paths for your CTA logic const premiumPaths = [ '/career-roadmap', '/paywall', '/financial-profile', '/retirement-planner', '/premium-onboarding', '/enhancing', '/retirement', '/resume-optimizer', ]; const showPremiumCTA = !premiumPaths.some(p => location.pathname.startsWith(p) ); // Helper to see if user is mid–premium-onboarding function isOnboardingInProgress() { try { const stored = JSON.parse(localStorage.getItem('premiumOnboardingState') || '{}'); // If step < 4 (example), user is in progress return stored.step && stored.step < 4; } catch (e) { return false; } } // ============================== // 1) Single Rehydrate UseEffect // ============================== useEffect(() => { let cancelled = false; if (loggingOut) return; // Skip auth probe on all public auth routes if ( location.pathname.startsWith('/reset-password') || location.pathname === '/signin' || location.pathname === '/signup' || location.pathname === '/forgot-password' ) { try { localStorage.removeItem('id'); } catch {} setIsAuthenticated(false); setUser(null); setIsLoading(false); return; } (async () => { setIsLoading(true); try { // axios client already: withCredentials + Bearer from authMemory const { data } = await api.get('/api/user-profile'); if (cancelled) return; setUser(data); setIsAuthenticated(true); } catch (err) { if (cancelled) return; clearToken(); setIsAuthenticated(false); setUser(null); // Only kick to /signin if you’re not already on a public page const p = location.pathname; const onPublic = p === '/signin' || p === '/signup' || p === '/forgot-password' || p.startsWith('/reset-password') || p === '/paywall'; if (!onPublic) navigate('/signin?session=expired', { replace: true }); } finally { if (!cancelled) setIsLoading(false); } })(); return () => { cancelled = true; }; // include isAuthScreen if you prefer, but this local check avoids a dep loop // eslint-disable-next-line react-hooks/exhaustive-deps }, [location.pathname, navigate, loggingOut]); /* ===================== Support Modal Email ===================== */ useEffect(() => { setUserEmail(user?.email || ''); }, [user]); // ========================== // 2) Logout Handler + Modal // ========================== const handleLogoutClick = () => { if (isOnboardingInProgress()) { // Show a modal to confirm losing onboarding data setShowLogoutWarning(true); } else { // No onboarding => just logout confirmLogout(); } }; const confirmLogout = async () => { setLoggingOut(true); // 1) Ask the server to clear the session cookie try { // If you created /logout (no /api prefix): await api.post('/logout'); // axios client is withCredentials: true // If your route is /api/signout instead, use: // await api.post('/api/signout'); } catch (e) { console.warn('Server logout failed (continuing client-side):', e?.message || e); } // 2) Clear client-side state/caches clearToken(); // in-memory bearer (if any, for legacy flows) safeLocal.clearMany([ 'id', 'careerSuggestionsCache', 'lastSelectedCareerProfileId', 'selectedCareer', 'aiClickCount', 'aiClickDate', 'aiRecommendations', 'premiumOnboardingState', 'financialProfile', 'selectedScenario', ]); // 3) Reset React state setFinancialProfile(null); setScenario(null); setIsAuthenticated(false); setUser(null); setShowLogoutWarning(false); // 4) Back to sign-in navigate('/signin', { replace: true }); setLoggingOut(false); }; const cancelLogout = () => { setShowLogoutWarning(false); }; // ==================================== // 3) If still verifying the token, show loading // ==================================== if (isLoading) { return (

Loading...

); } // ===================== // Main Render / Layout // ===================== return ( { if (!isAuthenticated) return; setDrawerPane('support'); setDrawerOpen(true); }, openRetire : (props) => { if (!isAuthenticated || !canShowRetireBot) return; if (!canShowRetireBot) { console.warn('Retirement bot disabled on this page'); return; } setRetireProps(props); setDrawerPane('retire'); setDrawerOpen(true); } }}>
{/* Header */}

AptivaAI - Career Guidance Platform

{showAuthedNav && ( <> {/* NAV MENU */} {/* LOGOUT + UPGRADE BUTTONS */}
{showPremiumCTA && !canAccessPremium && ( )} setSupportOpen(false)} userEmail={userEmail} /> {/* LOGOUT BUTTON */} {/* SHOW WARNING MODAL IF needed */} {showLogoutWarning && ( )}
)}
{/* MAIN CONTENT */}
{/* Default */} } /> } /> {/* Public (guest-only) routes */} ) : ( ) } /> : } /> : } /> } /> {/* Authenticated routes */} {isAuthenticated && ( <> } /> } /> } /> } /> } /> } /> } /> } /> } /> {/* Premium-wrapped */} } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> )} {/* 404 / Fallback */} } />
{isAuthenticated && ( )} {/* Session Handler (Optional) */}
); } export default App;