// src/components/RetirementPlanner.js import React, { useEffect, useState, useCallback, useContext } from 'react'; import authFetch from '../utils/authFetch.js'; import ScenarioContainer from './ScenarioContainer.js'; import { Button } from './ui/button.js'; import RetirementChatBar from './RetirementChatBar.js'; import ScenarioDiffDrawer from './ScenarioDiffDrawer.js'; import ChatCtx from '../contexts/ChatCtx.js'; /* ------------------------------------------------------------------ * tiny class‑name helper * ---------------------------------------------------------------- */ const cn = (...cls) => cls.filter(Boolean).join(' '); /* ------------------------------------------------------------------ * responsive helper – “mobile” = < 768px * ---------------------------------------------------------------- */ function useIsMobile () { const [mobile, setMobile] = useState(() => window.innerWidth < 768); useEffect(() => { const handler = () => setMobile(window.innerWidth < 768); window.addEventListener('resize', handler); return () => window.removeEventListener('resize', handler); }, []); return mobile; } /* ================================================================== * RetirementPlanner * ================================================================= */ export default function RetirementPlanner () { /* ---------------------------- state ----------------------------- */ const [loading, setLoading ] = useState(false); const [error, setError ] = useState(null); const [financialProfile, setFinancialProfile] = useState(null); const [scenarios, setScenarios] = useState([]); const [selectedScenario, setSelectedScenario] = useState(null); const [chatOpen, setChatOpen] = useState(false); // slide‑in flag const [diff, setDiff] = useState(null); const [simYearsMap, setSimYearsMap] = useState({}); const isMobile = useIsMobile(); const { openRetire } = useContext(ChatCtx); /* ----------------------- data loading -------------------------- */ const loadAll = useCallback(async () => { try { setLoading(true); setError(null); /* financial profile ------------------------------------------------ */ const finRes = await authFetch('/api/premium/financial-profile'); if (!finRes.ok) throw new Error(`Financial profile error (${finRes.status})`); const finJson = await finRes.json(); /* scenarios -------------------------------------------------------- */ const scRes = await authFetch('/api/premium/career-profile/all'); if (!scRes.ok) throw new Error(`Scenario error (${scRes.status})`); const scJson = await scRes.json(); setFinancialProfile(finJson); setScenarios(scJson.careerProfiles || []); } catch (e) { console.error('RetirementPlanner → loadAll', e); setError(e.message || 'Failed to load'); } finally { setLoading(false); } }, []); useEffect(() => { loadAll(); }, [loadAll]); /* ------------------ scenario CRUD helpers ---------------------- */ async function handleAddScenario () { try { const body = { career_name : `New Scenario ${new Date().toLocaleDateString()}`, status : 'planned', start_date : new Date().toISOString().slice(0, 10), college_enrollment_status : 'not_enrolled', currently_working : 'no' }; const r = await authFetch('/api/premium/career-profile', { method : 'POST', headers: { 'Content-Type': 'application/json' }, body : JSON.stringify(body) }); if (!r.ok) throw new Error(r.status); await loadAll(); } catch (e) { alert(`Add scenario failed (${e.message})`); } } async function handleRemoveScenario (id) { if (!window.confirm('Delete this scenario?')) return; const r = await authFetch(`/api/premium/career-profile/${id}`, { method: 'DELETE' }); if (!r.ok) return alert(`Delete error (${r.status})`); await loadAll(); } async function handleCloneScenario (src) { if (!src?.id) return; try { const r = await authFetch('/api/premium/career-profile/clone', { method : 'POST', headers: { 'Content-Type': 'application/json' }, body : JSON.stringify({ sourceId: src.id, overrides: {} }) }); if (!r.ok) throw new Error(`HTTP ${r.status}`); await loadAll(); // refresh scenarios list in state } catch (e) { alert(`Clone scenario failed (${e.message})`); } } /* ------------------ chat patch helper -------------------------- */ const applyPatch = (id, patch) => { setScenarios(prev => { const base = prev.find(s => s.id === id); const next = prev.map(s => (s.id === id ? { ...s, ...patch } : s)); setDiff({ base, patch }); return next; }); }; /* --------------------------- guards ---------------------------- */ if (loading) return
Loading scenarios…
; if (error) return{error}
; /* ----------------------- render body --------------------------- */ const visibleTwo = scenarios.slice(0, 2); const baselineId = visibleTwo[0]?.id; const baselineYears = simYearsMap[baselineId] ?? null; // renamed return (