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';
export const ProfileCtx = React.createContext();
function ResetPasswordGate() {
const location = useLocation();
useEffect(() => {
try {
localStorage.removeItem('token');
localStorage.removeItem('id');
// If you cache other auth-ish flags, clear them here too
} catch {}
// no navigate here; we want to render the reset UI
}, [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 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;
}
}
/* =====================
Support Modal Email
===================== */
useEffect(() => {
setUserEmail(user?.email || '');
}, [user]);
/* Multi-tab signout listener */
useEffect(() => {
const onStorage = (e) => {
if (e.key === 'token' && !e.newValue) {
// another tab cleared the token
clearToken();
setIsAuthenticated(false);
setUser(null);
navigate('/signin?session=expired');
}
};
window.addEventListener('storage', onStorage);
return () => window.removeEventListener('storage', onStorage);
}, [navigate]);
// ==============================
// 1) Single Rehydrate UseEffect (cookie mode)
// ==============================
useEffect(() => {
let cancelled = false;
(async () => {
const isAuthRoute =
location.pathname === '/signin' ||
location.pathname === '/signup' ||
location.pathname === '/forgot-password' ||
location.pathname.startsWith('/reset-password');
if (isAuthRoute) {
try { localStorage.removeItem('token'); localStorage.removeItem('id'); } catch {}
if (!cancelled) {
setIsAuthenticated(false);
setUser(null);
setIsLoading(false);
}
return;
}
try {
// Cookie goes automatically; shim sends credentials:'include'
const res = await fetch('/api/user-profile', { credentials: 'include' });
if (!res.ok) throw new Error('unauthorized');
const profile = await res.json();
if (cancelled) return;
setUser(profile);
setFinancialProfile(profile);
setIsAuthenticated(true);
} catch {
if (cancelled) return;
setIsAuthenticated(false);
setUser(null);
} finally {
if (!cancelled) setIsLoading(false);
}
})();
return () => { cancelled = true; };
}, [location.pathname]);
// ==========================
// 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 () => {
// Clear any sensitive values from Web Storage
[
'token',
'id',
'careerSuggestionsCache',
'lastSelectedCareerProfileId',
'selectedCareer',
'aiClickCount',
'aiClickDate',
'aiRecommendations',
'premiumOnboardingState',
'financialProfile'
].forEach(k => {
try { localStorage.removeItem(k); } catch {}
});
// Clear in-memory token
try { await fetch('/api/logout', { method: 'POST', credentials: 'include' }); } catch {}
try { clearToken(); } catch {}
// Reset React state/context
setFinancialProfile(null);
setScenario(null);
setIsAuthenticated(false);
setUser(null);
setShowLogoutWarning(false);
// Navigate to Sign In
navigate('/signin');
};
const cancelLogout = () => {
setShowLogoutWarning(false);
};
// ====================================
// 3) If still verifying the token, show loading
// ====================================
if (isLoading) {
return (
);
}
// =====================
// Main Render / Layout
// =====================
return (
{ setDrawerPane('support'); setDrawerOpen(true); },
openRetire : (props) => {
if (!canShowRetireBot) {
console.warn('Retirement bot disabled on this page');
return;
}
setRetireProps(props);
setDrawerPane('retire');
setDrawerOpen(true);
}
}}>
{/* Header */}
{/* MAIN CONTENT */}
{/* Default */}
}
/>
} />
{/* Public (guest-only) routes */}
) : (
)
}
/>
: }
/>
: }
/>
} />
{/* Authenticated routes */}
{isAuthenticated && (
<>
} />
} />
} />
} />
} />
} />
} />
} />
} />
{/* Premium-wrapped */}
}
/>
}
/>
}
/>
} />
} />
} />
} />
}
/>
}
/>
}
/>
}
/>
>
)}
{/* 404 / Fallback */}
}
/>
{/* Session Handler (Optional) */}
);
}
export default App;