// src/components/ScenarioContainer.js import React, { useState, useEffect } from 'react'; import { Line } from 'react-chartjs-2'; import { simulateFinancialProjection } from '../utils/FinancialProjectionService.js'; import ScenarioEditModal from './ScenarioEditModal.js'; import MilestoneTimeline from './MilestoneTimeline.js'; import AISuggestedMilestones from './AISuggestedMilestones.js'; import authFetch from '../utils/authFetch.js'; export default function ScenarioContainer({ scenario, // from career_paths row financialProfile, // single row, shared across user onClone, onRemove, onScenarioUpdated }) { const [localScenario, setLocalScenario] = useState(scenario); const [collegeProfile, setCollegeProfile] = useState(null); const [milestones, setMilestones] = useState([]); const [universalMilestones, setUniversalMilestones] = useState([]); const [projectionData, setProjectionData] = useState([]); const [loanPaidOffMonth, setLoanPaidOffMonth] = useState(null); const [editOpen, setEditOpen] = useState(false); // Re-sync if parent updates scenario useEffect(() => { setLocalScenario(scenario); }, [scenario]); // 1) Fetch the college profile for this scenario useEffect(() => { if (!localScenario?.id) return; async function loadCollegeProfile() { try { const res = await authFetch( `/api/premium/college-profile?careerPathId=${localScenario.id}` ); if (res.ok) { const data = await res.json(); setCollegeProfile(data); } else { console.warn('No college profile found or error:', res.status); setCollegeProfile({}); } } catch (err) { console.error('Failed fetching college profile:', err); } } loadCollegeProfile(); }, [localScenario]); // 2) Fetch scenario’s milestones (and universal) useEffect(() => { if (!localScenario?.id) return; async function loadMilestones() { try { const [scenRes, uniRes] = await Promise.all([ authFetch(`/api/premium/milestones?careerPathId=${localScenario.id}`), authFetch(`/api/premium/milestones?careerPathId=universal`) // if you have that route ]); let scenarioData = scenRes.ok ? (await scenRes.json()) : { milestones: [] }; let universalData = uniRes.ok ? (await uniRes.json()) : { milestones: [] }; setMilestones(scenarioData.milestones || []); setUniversalMilestones(universalData.milestones || []); } catch (err) { console.error('Failed to load milestones:', err); } } loadMilestones(); }, [localScenario]); // 3) Merge real snapshot + scenario overrides => run simulation useEffect(() => { if (!financialProfile || !collegeProfile) return; // Merge the scenario's planned overrides if not null, // else fallback to the real snapshot in financialProfile const mergedProfile = { currentSalary: financialProfile.current_salary || 0, monthlyExpenses: localScenario.planned_monthly_expenses ?? financialProfile.monthly_expenses ?? 0, monthlyDebtPayments: localScenario.planned_monthly_debt_payments ?? financialProfile.monthly_debt_payments ?? 0, retirementSavings: financialProfile.retirement_savings ?? 0, emergencySavings: financialProfile.emergency_fund ?? 0, monthlyRetirementContribution: localScenario.planned_monthly_retirement_contribution ?? financialProfile.retirement_contribution ?? 0, monthlyEmergencyContribution: localScenario.planned_monthly_emergency_contribution ?? financialProfile.emergency_contribution ?? 0, surplusEmergencyAllocation: localScenario.planned_surplus_emergency_pct ?? financialProfile.extra_cash_emergency_pct ?? 50, surplusRetirementAllocation: localScenario.planned_surplus_retirement_pct ?? financialProfile.extra_cash_retirement_pct ?? 50, additionalIncome: localScenario.planned_additional_income ?? financialProfile.additional_income ?? 0, // College fields studentLoanAmount: collegeProfile.existing_college_debt || 0, interestRate: collegeProfile.interest_rate || 5, loanTerm: collegeProfile.loan_term || 10, loanDeferralUntilGraduation: !!collegeProfile.loan_deferral_until_graduation, academicCalendar: collegeProfile.academic_calendar || 'semester', annualFinancialAid: collegeProfile.annual_financial_aid || 0, calculatedTuition: collegeProfile.tuition || 0, extraPayment: collegeProfile.extra_payment || 0, gradDate: collegeProfile.expected_graduation || null, programType: collegeProfile.program_type || '', creditHoursPerYear: collegeProfile.credit_hours_per_year || 0, hoursCompleted: collegeProfile.hours_completed || 0, programLength: collegeProfile.program_length || 0, inCollege: collegeProfile.college_enrollment_status === 'currently_enrolled' || collegeProfile.college_enrollment_status === 'prospective_student', expectedSalary: collegeProfile.expected_salary || financialProfile.current_salary || 0, // Flatten scenario + universal milestoneImpacts milestoneImpacts: buildAllImpacts([...milestones, ...universalMilestones]) }; const { projectionData, loanPaidOffMonth } = simulateFinancialProjection(mergedProfile); setProjectionData(projectionData); setLoanPaidOffMonth(loanPaidOffMonth); }, [financialProfile, collegeProfile, localScenario, milestones, universalMilestones]); function buildAllImpacts(allMilestones) { let impacts = []; for (let m of allMilestones) { if (m.impacts) { impacts.push(...m.impacts); } // If new_salary logic is relevant, handle it here } return impacts; } // Edit => open modal return (
Status: {localScenario.status}