// src/components/MilestoneTracker.js import React, { useState, useEffect } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import { v4 as uuidv4 } from 'uuid'; import { Line } from 'react-chartjs-2'; import { Chart as ChartJS, LineElement, CategoryScale, LinearScale, PointElement, Tooltip, Legend } from 'chart.js'; import annotationPlugin from 'chartjs-plugin-annotation'; import { Filler } from 'chart.js'; import authFetch from '../utils/authFetch.js'; import CareerSelectDropdown from './CareerSelectDropdown.js'; import CareerSearch from './CareerSearch.js'; import MilestoneTimeline from './MilestoneTimeline.js'; import AISuggestedMilestones from './AISuggestedMilestones.js'; import './MilestoneTracker.css'; import './MilestoneTimeline.css'; // Ensure this file contains styles for timeline-line and milestone-dot import { simulateFinancialProjection } from '../utils/FinancialProjectionService.js'; ChartJS.register( LineElement, CategoryScale, LinearScale, Filler, PointElement, Tooltip, Legend, annotationPlugin ); const MilestoneTracker = ({ selectedCareer: initialCareer }) => { const location = useLocation(); const navigate = useNavigate(); const [selectedCareer, setSelectedCareer] = useState(initialCareer || null); const [careerPathId, setCareerPathId] = useState(null); const [existingCareerPaths, setExistingCareerPaths] = useState([]); const [pendingCareerForModal, setPendingCareerForModal] = useState(null); const [activeView, setActiveView] = useState("Career"); const [financialProfile, setFinancialProfile] = useState(null); // Store the financial profile const { projectionData: initialProjectionData = [], loanPayoffMonth: initialLoanPayoffMonth = null, } = location.state || {}; const [loanPayoffMonth, setLoanPayoffMonth] = useState(initialLoanPayoffMonth); const [projectionData, setProjectionData] = useState(initialProjectionData); const apiURL = process.env.REACT_APP_API_URL; useEffect(() => { const fetchCareerPaths = async () => { const res = await authFetch(`${apiURL}/premium/career-profile/all`); if (!res) return; const data = await res.json(); const { careerPaths } = data; setExistingCareerPaths(careerPaths); const fromPopout = location.state?.selectedCareer; if (fromPopout) { setSelectedCareer(fromPopout); setCareerPathId(fromPopout.career_path_id); } else if (!selectedCareer) { const latest = await authFetch(`${apiURL}/premium/career-profile/latest`); if (latest) { const latestData = await latest.json(); if (latestData?.id) { setSelectedCareer(latestData); setCareerPathId(latestData.id); } } } }; const fetchFinancialProfile = async () => { const res = await authFetch(`${apiURL}/premium/financial-profile`); if (res && res.ok) { const data = await res.json(); setFinancialProfile(data); // Set the financial profile in state } }; fetchCareerPaths(); fetchFinancialProfile(); }, []); useEffect(() => { if (financialProfile && selectedCareer) { const { projectionData, loanPaidOffMonth, emergencySavings } = simulateFinancialProjection({ currentSalary: financialProfile.current_salary, monthlyExpenses: financialProfile.monthly_expenses, monthlyDebtPayments: financialProfile.monthly_debt_payments || 0, studentLoanAmount: financialProfile.college_loan_total, interestRate: financialProfile.interest_rate || 5.5, loanTerm: financialProfile.loan_term || 10, extraPayment: financialProfile.extra_payment || 0, expectedSalary: financialProfile.expected_salary || financialProfile.current_salary, emergencySavings: financialProfile.emergency_fund, retirementSavings: financialProfile.retirement_savings, monthlyRetirementContribution: financialProfile.retirement_contribution, monthlyEmergencyContribution: 0, gradDate: financialProfile.expected_graduation, fullTimeCollegeStudent: financialProfile.in_college, partTimeIncome: financialProfile.part_time_income, startDate: new Date(), programType: financialProfile.program_type, isFullyOnline: financialProfile.is_online, creditHoursPerYear: financialProfile.credit_hours_per_year, calculatedTuition: financialProfile.tuition, hoursCompleted: financialProfile.hours_completed, loanDeferralUntilGraduation: financialProfile.loan_deferral_until_graduation, programLength: financialProfile.program_length, }); let cumulativeSavings = emergencySavings || 0; const cumulativeProjectionData = projectionData.map(month => { cumulativeSavings += month.netSavings || 0; return { ...month, cumulativeNetSavings: cumulativeSavings }; }); // Only update if we have real projection data if (cumulativeProjectionData.length > 0) { setProjectionData(cumulativeProjectionData); setLoanPayoffMonth(loanPaidOffMonth); } } }, [financialProfile, selectedCareer]); const handleCareerChange = (selected) => { if (selected && selected.id && selected.career_name) { setSelectedCareer(selected); setCareerPathId(selected.id); } else { console.warn('Invalid career object received in handleCareerChange:', selected); } }; console.log( 'First 5 items of projectionData:', Array.isArray(projectionData) ? projectionData.slice(0, 5) : 'projectionData not yet available' ); const handleConfirmCareerSelection = async () => { const newId = uuidv4(); const body = { career_path_id: newId, career_name: pendingCareerForModal, start_date: new Date().toISOString().split('T')[0] }; const res = await authFetch(`${apiURL}/premium/career-profile`, { method: 'POST', body: JSON.stringify(body) }); if (!res || !res.ok) return; const result = await res.json(); setCareerPathId(result.career_path_id); setSelectedCareer({ career_name: pendingCareerForModal, id: result.career_path_id }); setPendingCareerForModal(null); }; return (
{console.log('Passing careerPathId to MilestoneTimeline:', careerPathId)} {projectionData && (

Financial Projection

p.month), datasets: [ { label: 'Total Savings', // ✅ Changed label to clarify data: projectionData.map(p => p.cumulativeNetSavings), borderColor: 'rgba(54, 162, 235, 1)', backgroundColor: 'rgba(54, 162, 235, 0.2)', tension: 0.4, fill: true }, { label: 'Loan Balance', data: projectionData.map(p => p.loanBalance), borderColor: 'rgba(255, 99, 132, 1)', backgroundColor: 'rgba(255, 99, 132, 0.2)', tension: 0.4, fill: { target: 'origin', above: 'rgba(255,99,132,0.3)', // loan debt below: 'transparent' // don't show below 0 } }, { label: 'Retirement Savings', data: projectionData.map(p => p.totalRetirementSavings), borderColor: 'rgba(75, 192, 192, 1)', backgroundColor: 'rgba(75, 192, 192, 0.2)', tension: 0.4, fill: true } ] }} options={{ responsive: true, plugins: { legend: { position: 'bottom' }, tooltip: { mode: 'index', intersect: false }, annotation: loanPayoffMonth ? { annotations: { loanPaidOffLine: { type: 'line', xMin: loanPayoffMonth, xMax: loanPayoffMonth, borderColor: 'rgba(255, 206, 86, 1)', borderWidth: 2, borderDash: [6, 6], label: { display: true, content: 'Loan Paid Off', position: 'end', backgroundColor: 'rgba(255, 206, 86, 0.8)', color: '#000', font: { size: 12 }, rotation: 0, yAdjust: -10 } } } } : undefined }, scales: { y: { beginAtZero: false, ticks: { callback: (value) => `$${value.toLocaleString()}` } } } }} />
)} setPendingCareerForModal(careerName)} setPendingCareerForModal={setPendingCareerForModal} authFetch={authFetch} /> {pendingCareerForModal && ( )}
); }; export default MilestoneTracker;