dev1/src/components/MilestoneTracker.js

816 lines
28 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect } from 'react';
import { useLocation } from 'react-router-dom';
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 { simulateFinancialProjection } from '../utils/FinancialProjectionService.js';
import { Button } from './ui/button.js';
import CareerSelectDropdown from './CareerSelectDropdown.js';
import CareerSearch from './CareerSearch.js';
import MilestoneTimeline from './MilestoneTimeline.js';
import AISuggestedMilestones from './AISuggestedMilestones.js';
import ScenarioEditModal from './ScenarioEditModal.js';
import parseFloatOrZero from '../utils/ParseFloatorZero.js';
import './MilestoneTracker.css';
import './MilestoneTimeline.css';
// Register Chart + annotation plugin
ChartJS.register(
LineElement,
CategoryScale,
LinearScale,
Filler,
PointElement,
Tooltip,
Legend,
annotationPlugin
);
const MilestoneTracker = ({ selectedCareer: initialCareer }) => {
const location = useLocation();
const apiURL = process.env.REACT_APP_API_URL;
// --------------------------------------------------
// State
// --------------------------------------------------
// User and Financial Profile Data
const [userProfile, setUserProfile] = useState(null);
const [financialProfile, setFinancialProfile] = useState(null);
// Career & Scenario Data
const [selectedCareer, setSelectedCareer] = useState(initialCareer || null);
const [careerProfileId, setCareerProfileId] = useState(null);
const [existingCareerProfiles, setExistingCareerProfiles] = useState([]);
const [scenarioRow, setScenarioRow] = useState(null);
const [collegeProfile, setCollegeProfile] = useState(null);
// Milestones & Simulation
const [scenarioMilestones, setScenarioMilestones] = useState([]);
const [projectionData, setProjectionData] = useState([]);
const [loanPayoffMonth, setLoanPayoffMonth] = useState(null);
const [simulationYearsInput, setSimulationYearsInput] = useState('20');
const simulationYears = parseInt(simulationYearsInput, 10) || 20;
// Salary Data & Economic Projections
const [salaryData, setSalaryData] = useState(null);
const [economicProjections, setEconomicProjections] = useState(null);
// UI Toggles
const [showEditModal, setShowEditModal] = useState(false);
const [pendingCareerForModal, setPendingCareerForModal] = useState(null);
const [showAISuggestions, setShowAISuggestions] = useState(false);
// If coming from location.state
const {
projectionData: initialProjectionData = [],
loanPayoffMonth: initialLoanPayoffMonth = null
} = location.state || {};
// --------------------------------------------------
// 1) Fetch User Profile & Financial Profile
// --------------------------------------------------
useEffect(() => {
const fetchUserProfile = async () => {
try {
const res = await authFetch('/api/user-profile'); // or wherever user profile is fetched
if (res.ok) {
const data = await res.json();
setUserProfile(data);
} else {
console.error('Failed to fetch user profile:', res.status);
}
} catch (error) {
console.error('Error fetching user profile:', error);
}
};
const fetchFinancialProfile = async () => {
try {
const res = await authFetch(`${apiURL}/premium/financial-profile`);
if (res.ok) {
const data = await res.json();
setFinancialProfile(data);
} else {
console.error('Failed to fetch financial profile:', res.status);
}
} catch (error) {
console.error('Error fetching financial profile:', error);
}
};
fetchUserProfile();
fetchFinancialProfile();
}, [apiURL]);
const userLocation = userProfile?.area || '';
const userSalary = financialProfile?.current_salary ?? 0;
// --------------------------------------------------
// 2) Fetch users Career Profiles => set initial scenario
// --------------------------------------------------
useEffect(() => {
const fetchCareerProfiles = async () => {
const res = await authFetch(`${apiURL}/premium/career-profile/all`);
if (!res || !res.ok) return;
const data = await res.json();
setExistingCareerProfiles(data.careerProfiles);
// If there's a career in location.state, pick that
const fromPopout = location.state?.selectedCareer;
if (fromPopout) {
setSelectedCareer(fromPopout);
setCareerProfileId(fromPopout.career_profile_id);
} else {
// Else try localStorage
const storedCareerProfileId = localStorage.getItem('lastSelectedCareerProfileId');
if (storedCareerProfileId) {
const matchingCareer = data.careerProfiles.find((p) => p.id === storedCareerProfileId);
if (matchingCareer) {
setSelectedCareer(matchingCareer);
setCareerProfileId(storedCareerProfileId);
return;
}
}
// Fallback to the "latest" scenario
const latest = await authFetch(`${apiURL}/premium/career-profile/latest`);
if (latest && latest.ok) {
const latestData = await latest.json();
if (latestData?.id) {
setSelectedCareer(latestData);
setCareerProfileId(latestData.id);
}
}
}
};
fetchCareerProfiles();
}, [apiURL, location.state]);
// --------------------------------------------------
// 3) Fetch scenarioRow + collegeProfile for chosen careerProfileId
// --------------------------------------------------
useEffect(() => {
if (!careerProfileId) {
setScenarioRow(null);
setCollegeProfile(null);
setScenarioMilestones([]);
return;
}
localStorage.setItem('lastSelectedCareerProfileId', careerProfileId);
const fetchScenario = async () => {
const scenRes = await authFetch(`${apiURL}/premium/career-profile/${careerProfileId}`);
if (scenRes.ok) {
const data = await scenRes.json();
setScenarioRow(data);
} else {
console.error('Failed to fetch scenario row:', scenRes.status);
setScenarioRow(null);
}
};
const fetchCollege = async () => {
const colRes = await authFetch(
`${apiURL}/premium/college-profile?careerProfileId=${careerProfileId}`
);
if (colRes.ok) {
const data = await colRes.json();
setCollegeProfile(data);
} else {
setCollegeProfile(null);
}
};
fetchScenario();
fetchCollege();
}, [careerProfileId, apiURL]);
// --------------------------------------------------
// 4) Fetch Salary Data for selectedCareer + userLocation
// --------------------------------------------------
useEffect(() => {
if (!selectedCareer?.soc_code) {
setSalaryData(null);
return;
}
const areaParam = userLocation || 'U.S.';
const fetchSalaryData = async () => {
try {
const queryParams = new URLSearchParams({
socCode: selectedCareer.soc_code,
area: areaParam
}).toString();
const res = await fetch(`/api/salary?${queryParams}`);
if (!res.ok) {
console.error('Error fetching salary data:', res.status);
setSalaryData(null);
return;
}
const data = await res.json();
if (data.error) {
console.log('No salary data found for these params:', data.error);
}
setSalaryData(data);
} catch (err) {
console.error('Exception fetching salary data:', err);
setSalaryData(null);
}
};
fetchSalaryData();
}, [selectedCareer, userLocation]);
// --------------------------------------------------
// 5) (Optional) Fetch Economic Projections
// --------------------------------------------------
useEffect(() => {
if (!selectedCareer?.career_name) {
setEconomicProjections(null);
return;
}
const fetchEconomicProjections = async () => {
try {
const encodedCareer = encodeURIComponent(selectedCareer.career_name);
const res = await authFetch('/api/projections/:socCode');
if (res.ok) {
const data = await res.json();
setEconomicProjections(data);
}
} catch (err) {
console.error('Error fetching economic projections:', err);
setEconomicProjections(null);
}
};
fetchEconomicProjections();
}, [selectedCareer, apiURL]);
// --------------------------------------------------
// 6) Once we have scenario + financial + college => run simulation
// --------------------------------------------------
useEffect(() => {
if (!financialProfile || !scenarioRow || !collegeProfile) return;
(async () => {
try {
// 1) Fetch milestones for this scenario
const milRes = await authFetch(`${apiURL}/premium/milestones?careerProfileId=${careerProfileId}`);
if (!milRes.ok) {
console.error('Failed to fetch milestones for scenario', careerProfileId);
return;
}
const milestonesData = await milRes.json();
const allMilestones = milestonesData.milestones || [];
setScenarioMilestones(allMilestones);
// 2) Fetch impacts for each milestone
const impactPromises = allMilestones.map((m) =>
authFetch(`${apiURL}/premium/milestone-impacts?milestone_id=${m.id}`)
.then((r) => (r.ok ? r.json() : null))
.then((data) => data?.impacts || [])
.catch((err) => {
console.warn('Error fetching impacts for milestone', m.id, err);
return [];
})
);
const impactsForEach = await Promise.all(impactPromises);
// Flatten all milestone impacts
const allImpacts = allMilestones.map((m, i) => ({
...m,
impacts: impactsForEach[i] || [],
})).flatMap((m) => m.impacts);
/*******************************************************
* A) Parse numeric "financialProfile" fields
*******************************************************/
const financialBase = {
currentSalary: parseFloatOrZero(financialProfile.current_salary, 0),
additionalIncome: parseFloatOrZero(financialProfile.additional_income, 0),
monthlyExpenses: parseFloatOrZero(financialProfile.monthly_expenses, 0),
monthlyDebtPayments: parseFloatOrZero(financialProfile.monthly_debt_payments, 0),
retirementSavings: parseFloatOrZero(financialProfile.retirement_savings, 0),
emergencySavings: parseFloatOrZero(financialProfile.emergency_fund, 0),
retirementContribution: parseFloatOrZero(financialProfile.retirement_contribution, 0),
emergencyContribution: parseFloatOrZero(financialProfile.emergency_contribution, 0),
extraCashEmergencyPct: parseFloatOrZero(financialProfile.extra_cash_emergency_pct, 50),
extraCashRetirementPct: parseFloatOrZero(financialProfile.extra_cash_retirement_pct, 50),
};
/*******************************************************
* B) Parse scenario overrides from "scenarioRow"
*******************************************************/
const scenarioOverrides = {
monthlyExpenses: parseFloatOrZero(
scenarioRow.planned_monthly_expenses,
financialBase.monthlyExpenses
),
monthlyDebtPayments: parseFloatOrZero(
scenarioRow.planned_monthly_debt_payments,
financialBase.monthlyDebtPayments
),
monthlyRetirementContribution: parseFloatOrZero(
scenarioRow.planned_monthly_retirement_contribution,
financialBase.retirementContribution
),
monthlyEmergencyContribution: parseFloatOrZero(
scenarioRow.planned_monthly_emergency_contribution,
financialBase.emergencyContribution
),
surplusEmergencyAllocation: parseFloatOrZero(
scenarioRow.planned_surplus_emergency_pct,
financialBase.extraCashEmergencyPct
),
surplusRetirementAllocation: parseFloatOrZero(
scenarioRow.planned_surplus_retirement_pct,
financialBase.extraCashRetirementPct
),
additionalIncome: parseFloatOrZero(
scenarioRow.planned_additional_income,
financialBase.additionalIncome
),
};
/*******************************************************
* C) Parse numeric "collegeProfile" fields
*******************************************************/
const collegeData = {
studentLoanAmount: parseFloatOrZero(collegeProfile.existing_college_debt, 0),
interestRate: parseFloatOrZero(collegeProfile.interest_rate, 5),
loanTerm: parseFloatOrZero(collegeProfile.loan_term, 10),
loanDeferralUntilGraduation: !!collegeProfile.loan_deferral_until_graduation,
academicCalendar: collegeProfile.academic_calendar || 'monthly',
annualFinancialAid: parseFloatOrZero(collegeProfile.annual_financial_aid, 0),
calculatedTuition: parseFloatOrZero(collegeProfile.tuition, 0),
extraPayment: parseFloatOrZero(collegeProfile.extra_payment, 0),
inCollege:
collegeProfile.college_enrollment_status === 'currently_enrolled' ||
collegeProfile.college_enrollment_status === 'prospective_student',
gradDate: collegeProfile.expected_graduation || null,
programType: collegeProfile.program_type || null,
creditHoursPerYear: parseFloatOrZero(collegeProfile.credit_hours_per_year, 0),
hoursCompleted: parseFloatOrZero(collegeProfile.hours_completed, 0),
programLength: parseFloatOrZero(collegeProfile.program_length, 0),
expectedSalary:
parseFloatOrZero(collegeProfile.expected_salary) ||
parseFloatOrZero(financialProfile.current_salary, 0),
};
/*******************************************************
* D) Combine them into a single mergedProfile
*******************************************************/
const mergedProfile = {
// Financial base
currentSalary: financialBase.currentSalary,
// scenario overrides (with scenario > financial precedence)
monthlyExpenses: scenarioOverrides.monthlyExpenses,
monthlyDebtPayments: scenarioOverrides.monthlyDebtPayments,
// big items from financialProfile that had no scenario override
retirementSavings: financialBase.retirementSavings,
emergencySavings: financialBase.emergencySavings,
// scenario overrides for monthly contributions
monthlyRetirementContribution: scenarioOverrides.monthlyRetirementContribution,
monthlyEmergencyContribution: scenarioOverrides.monthlyEmergencyContribution,
// scenario overrides for surplus distribution
surplusEmergencyAllocation: scenarioOverrides.surplusEmergencyAllocation,
surplusRetirementAllocation: scenarioOverrides.surplusRetirementAllocation,
// scenario override for additionalIncome
additionalIncome: scenarioOverrides.additionalIncome,
// college fields
studentLoanAmount: collegeData.studentLoanAmount,
interestRate: collegeData.interestRate,
loanTerm: collegeData.loanTerm,
loanDeferralUntilGraduation: collegeData.loanDeferralUntilGraduation,
academicCalendar: collegeData.academicCalendar,
annualFinancialAid: collegeData.annualFinancialAid,
calculatedTuition: collegeData.calculatedTuition,
extraPayment: collegeData.extraPayment,
inCollege: collegeData.inCollege,
gradDate: collegeData.gradDate,
programType: collegeData.programType,
creditHoursPerYear: collegeData.creditHoursPerYear,
hoursCompleted: collegeData.hoursCompleted,
programLength: collegeData.programLength,
expectedSalary: collegeData.expectedSalary,
// scenario horizon + milestone impacts
startDate: new Date().toISOString(),
simulationYears,
milestoneImpacts: allImpacts
};
// 3) Run the simulation
const { projectionData: pData, loanPaidOffMonth: payoff } =
simulateFinancialProjection(mergedProfile);
// 4) Add cumulative net savings
let cumu = mergedProfile.emergencySavings || 0;
const finalData = pData.map((mo) => {
cumu += mo.netSavings || 0;
return { ...mo, cumulativeNetSavings: cumu };
});
setProjectionData(finalData);
setLoanPayoffMonth(payoff);
} catch (err) {
console.error('Error in scenario simulation:', err);
}
})();
}, [
financialProfile,
scenarioRow,
collegeProfile,
careerProfileId,
apiURL,
simulationYears
]);
// --------------------------------------------------
// Handlers & Chart Setup
// --------------------------------------------------
const handleSimulationYearsChange = (e) => setSimulationYearsInput(e.target.value);
const handleSimulationYearsBlur = () => {
if (!simulationYearsInput.trim()) {
setSimulationYearsInput('20');
}
};
// Build chart annotations from scenarioMilestones
const milestoneAnnotationLines = {};
scenarioMilestones.forEach((m) => {
if (!m.date) return;
const d = new Date(m.date);
if (isNaN(d)) return;
const year = d.getUTCFullYear();
const month = String(d.getUTCMonth() + 1).padStart(2, '0');
const short = `${year}-${month}`;
if (!projectionData.some((p) => p.month === short)) return;
milestoneAnnotationLines[`milestone_${m.id}`] = {
type: 'line',
xMin: short,
xMax: short,
borderColor: 'orange',
borderWidth: 2,
label: {
display: true,
content: m.title || 'Milestone',
color: 'orange',
position: 'end'
}
};
});
// Loan payoff line
const annotationConfig = {};
if (loanPayoffMonth) {
annotationConfig.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
}
};
}
const allAnnotations = { ...milestoneAnnotationLines, ...annotationConfig };
// Salary Gauge
function getRelativePosition(userSal, p10, p90) {
if (!p10 || !p90) return 0; // avoid NaN
if (userSal < p10) return 0;
if (userSal > p90) return 1;
return (userSal - p10) / (p90 - p10);
}
const SalaryGauge = ({ userSalary, percentileRow, prefix = 'regional' }) => {
if (!percentileRow) return null;
const p10 = percentileRow[`${prefix}_PCT10`];
const p90 = percentileRow[`${prefix}_PCT90`];
if (!p10 || !p90) return null;
const fraction = getRelativePosition(userSalary, p10, p90) * 100;
return (
<div className="mb-4">
<div
style={{
border: '1px solid #ccc',
width: '100%',
height: '12px',
position: 'relative'
}}
>
<div
style={{
position: 'absolute',
left: `${fraction}%`,
transform: 'translateX(-50%)',
top: 0,
bottom: 0,
width: '2px',
backgroundColor: 'red'
}}
></div>
</div>
<p>
You are at <strong>{Math.round(fraction)}%</strong> between the 10th and 90th percentiles (
{prefix}).
</p>
</div>
);
};
return (
<div className="milestone-tracker max-w-screen-lg mx-auto px-4 py-6 space-y-6">
{/* 1) Career dropdown */}
<CareerSelectDropdown
existingCareerProfiles={existingCareerProfiles}
selectedCareer={selectedCareer}
onChange={(selected) => {
setSelectedCareer(selected);
setCareerProfileId(selected?.id || null);
}}
loading={!existingCareerProfiles.length}
authFetch={authFetch}
/>
{/* 2) Salary Data Display */}
{salaryData && (
<div className="salary-display-container bg-white p-4 rounded shadow">
<h3 className="text-lg font-semibold mb-2">Salary Overview</h3>
{/* Regional Salaries */}
{salaryData.regional && (
<div className="mb-4">
<h4 className="font-medium">Regional Salaries (Area: {userLocation || 'U.S.'})</h4>
<p>
<strong>10th percentile:</strong>{' '}
${salaryData.regional.regional_PCT10?.toLocaleString() ?? 'N/A'}
</p>
<p>
<strong>25th percentile:</strong>{' '}
${salaryData.regional.regional_PCT25?.toLocaleString() ?? 'N/A'}
</p>
<p>
<strong>Median:</strong>{' '}
${salaryData.regional.regional_MEDIAN?.toLocaleString() ?? 'N/A'}
</p>
<p>
<strong>75th percentile:</strong>{' '}
${salaryData.regional.regional_PCT75?.toLocaleString() ?? 'N/A'}
</p>
<p>
<strong>90th percentile:</strong>{' '}
${salaryData.regional.regional_PCT90?.toLocaleString() ?? 'N/A'}
</p>
<SalaryGauge
userSalary={userSalary}
percentileRow={salaryData.regional}
prefix="regional"
/>
</div>
)}
{/* National Salaries */}
{salaryData.national && (
<div>
<h4 className="font-medium">National Salaries</h4>
<p>
<strong>10th percentile:</strong>{' '}
${salaryData.national.national_PCT10?.toLocaleString() ?? 'N/A'}
</p>
<p>
<strong>25th percentile:</strong>{' '}
${salaryData.national.national_PCT25?.toLocaleString() ?? 'N/A'}
</p>
<p>
<strong>Median:</strong>{' '}
${salaryData.national.national_MEDIAN?.toLocaleString() ?? 'N/A'}
</p>
<p>
<strong>75th percentile:</strong>{' '}
${salaryData.national.national_PCT75?.toLocaleString() ?? 'N/A'}
</p>
<p>
<strong>90th percentile:</strong>{' '}
${salaryData.national.national_PCT90?.toLocaleString() ?? 'N/A'}
</p>
<SalaryGauge
userSalary={userSalary}
percentileRow={salaryData.national}
prefix="national"
/>
</div>
)}
<p className="mt-2">
Your current salary: <strong>${userSalary.toLocaleString()}</strong>
</p>
</div>
)}
{/* 3) Milestone Timeline */}
<MilestoneTimeline
careerProfileId={careerProfileId}
authFetch={authFetch}
activeView="Career"
onMilestoneUpdated={() => {}}
/>
{/* 4) AI Suggestions Button */}
{!showAISuggestions && (
<Button
onClick={() => setShowAISuggestions(true)}
className="bg-green-500 hover:bg-green-600 text-white font-semibold px-4 py-2 rounded"
>
Show AI Suggestions
</Button>
)}
{/* 5) AI-Suggested Milestones */}
{showAISuggestions && (
<AISuggestedMilestones
career={selectedCareer?.career_name}
careerProfileId={careerProfileId}
authFetch={authFetch}
activeView="Career"
projectionData={projectionData}
/>
)}
{/* 6) Financial Projection Chart */}
{projectionData.length > 0 && (
<div className="bg-white p-4 rounded shadow space-y-4">
<h3 className="text-lg font-semibold">Financial Projection</h3>
<Line
data={{
labels: projectionData.map((p) => p.month),
datasets: [
{
label: 'Total Savings',
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)',
below: 'transparent'
}
},
{
label: 'Retirement Savings',
data: projectionData.map((p) => p.retirementSavings),
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: {
annotations: allAnnotations
}
},
scales: {
y: {
beginAtZero: false,
ticks: {
callback: (value) => `$${value.toLocaleString()}`
}
}
}
}}
/>
<div>
{loanPayoffMonth && (
<p className="font-semibold text-sm">
Loan Paid Off at: <span className="text-yellow-600">{loanPayoffMonth}</span>
</p>
)}
</div>
</div>
)}
{/* 7) Simulation length + "Edit" => open ScenarioEditModal */}
<div className="space-x-2">
<label className="font-medium">Simulation Length (years):</label>
<input
type="text"
value={simulationYearsInput}
onChange={handleSimulationYearsChange}
onBlur={handleSimulationYearsBlur}
className="border rounded p-1 w-16"
/>
<Button onClick={() => setShowEditModal(true)} className="ml-2">
Edit
</Button>
</div>
{/* 8) Economic Projections Section */}
{economicProjections && (
<div className="bg-white p-4 rounded shadow">
<h3 className="text-lg font-semibold mb-2">Economic Projections</h3>
<p>
<strong>Growth Outlook:</strong> {economicProjections.growthOutlook || 'N/A'}
</p>
<p>
<strong>AI Automation Risk:</strong> {economicProjections.aiRisk || 'N/A'}
</p>
{economicProjections.chatGPTAnalysis && (
<div className="mt-2 border border-gray-200 p-2 rounded">
<h4 className="font-semibold">ChatGPT Analysis:</h4>
<p>{economicProjections.chatGPTAnalysis}</p>
</div>
)}
</div>
)}
{/* 9) Career Search & Potential new scenario creation */}
<CareerSearch
onCareerSelected={(careerObj) => {
setPendingCareerForModal(careerObj.title);
}}
/>
{pendingCareerForModal && (
<Button
onClick={() => {
console.log('User confirmed new career path:', pendingCareerForModal);
setPendingCareerForModal(null);
}}
className="bg-blue-500 hover:bg-blue-600 text-white font-semibold px-4 py-2 rounded mt-2"
>
Confirm Career Change to {pendingCareerForModal}
</Button>
)}
{/* 10) Scenario Edit Modal */}
<ScenarioEditModal
show={showEditModal}
onClose={() => {
setShowEditModal(false);
window.location.reload();
}}
scenario={scenarioRow}
financialProfile={financialProfile}
setFinancialProfile={setFinancialProfile}
collegeProfile={collegeProfile}
setCollegeProfile={setCollegeProfile}
apiURL={apiURL}
authFetch={authFetch}
/>
</div>
);
};
export default MilestoneTracker;