diff --git a/backend/server3.js b/backend/server3.js
index 25fe759..0beb851 100644
--- a/backend/server3.js
+++ b/backend/server3.js
@@ -165,7 +165,7 @@ app.post('/api/premium/career-profile', authenticatePremiumUser, async (req, res
}
try {
- const newId = uuidv4();
+ const finalId = req.body.id || uuidv4();
// 1) Insert includes career_goals
const sql = `
@@ -207,7 +207,7 @@ app.post('/api/premium/career-profile', authenticatePremiumUser, async (req, res
`;
await pool.query(sql, [
- newId,
+ finalId,
req.id,
scenario_title || null,
career_name,
@@ -231,12 +231,12 @@ app.post('/api/premium/career-profile', authenticatePremiumUser, async (req, res
`SELECT id
FROM career_profiles
WHERE id = ?`,
- [newId]
+ [finalId]
);
return res.status(200).json({
message: 'Career profile upserted.',
- career_profile_id: rows[0]?.id || newId
+ career_profile_id: finalId
});
} catch (error) {
console.error('Error upserting career profile:', error);
@@ -1383,6 +1383,7 @@ app.post('/api/premium/financial-profile', authenticatePremiumUser, async (req,
app.post('/api/premium/college-profile', authenticatePremiumUser, async (req, res) => {
const {
+ id, // <-- Accept this in body
career_profile_id,
selected_school,
selected_program,
@@ -1395,6 +1396,7 @@ app.post('/api/premium/college-profile', authenticatePremiumUser, async (req, re
credit_hours_required,
hours_completed,
program_length,
+ enrollment_date,
expected_graduation,
existing_college_debt,
interest_rate,
@@ -1409,7 +1411,8 @@ app.post('/api/premium/college-profile', authenticatePremiumUser, async (req, re
} = req.body;
try {
- const newId = uuidv4();
+ // If the request includes an existing id, use it; otherwise generate a new one
+ const finalId = id || uuidv4();
const sql = `
INSERT INTO college_profiles (
@@ -1428,6 +1431,7 @@ app.post('/api/premium/college-profile', authenticatePremiumUser, async (req, re
hours_completed,
program_length,
credit_hours_required,
+ enrollment_date,
expected_graduation,
existing_college_debt,
interest_rate,
@@ -1444,7 +1448,7 @@ app.post('/api/premium/college-profile', authenticatePremiumUser, async (req, re
?, ?, ?, ?, ?,
?, ?, ?, ?, ?,
?, ?, ?, ?, ?,
- ?, ?, ?, ?
+ ?, ?, ?, ?, ?
)
ON DUPLICATE KEY UPDATE
is_in_state = VALUES(is_in_state),
@@ -1456,6 +1460,7 @@ app.post('/api/premium/college-profile', authenticatePremiumUser, async (req, re
hours_completed = VALUES(hours_completed),
program_length = VALUES(program_length),
credit_hours_required = VALUES(credit_hours_required),
+ enrollment_date = VALUES(enrollment_date),
expected_graduation = VALUES(expected_graduation),
existing_college_debt = VALUES(existing_college_debt),
interest_rate = VALUES(interest_rate),
@@ -1470,8 +1475,8 @@ app.post('/api/premium/college-profile', authenticatePremiumUser, async (req, re
`;
await pool.query(sql, [
- newId,
- req.id,
+ finalId,
+ req.id, // user_id
career_profile_id,
selected_school,
selected_program,
@@ -1485,6 +1490,7 @@ app.post('/api/premium/college-profile', authenticatePremiumUser, async (req, re
hours_completed || 0,
program_length || 0,
credit_hours_required || 0,
+ enrollment_date || null,
expected_graduation || null,
existing_college_debt || 0,
interest_rate || 0,
@@ -1497,7 +1503,7 @@ app.post('/api/premium/college-profile', authenticatePremiumUser, async (req, re
tuition_paid || 0
]);
- res.status(201).json({ message: 'College profile upsert done.' });
+ res.status(201).json({ message: 'College profile upsert done.', id: finalId });
} catch (error) {
console.error('Error saving college profile:', error);
res.status(500).json({ error: 'Failed to save college profile.' });
diff --git a/src/components/CareerRoadmap.js b/src/components/CareerRoadmap.js
index 35aca43..e32d53b 100644
--- a/src/components/CareerRoadmap.js
+++ b/src/components/CareerRoadmap.js
@@ -20,8 +20,6 @@ import parseFloatOrZero from '../utils/ParseFloatorZero.js';
import { getFullStateName } from '../utils/stateUtils.js';
import { Button } from './ui/button.js';
-import CareerSelectDropdown from './CareerSelectDropdown.js';
-import MilestoneTimeline from './MilestoneTimeline.js';
import ScenarioEditModal from './ScenarioEditModal.js';
import './CareerRoadmap.css';
@@ -634,6 +632,7 @@ export default function CareerRoadmap({ selectedCareer: initialCareer }) {
annualFinancialAid: collegeData.annualFinancialAid,
calculatedTuition: collegeData.calculatedTuition,
extraPayment: collegeData.extraPayment,
+ enrollmentDate: collegeProfile.enrollmentDate || null,
inCollege: collegeData.inCollege,
gradDate: collegeData.gradDate,
programType: collegeData.programType,
@@ -653,6 +652,8 @@ export default function CareerRoadmap({ selectedCareer: initialCareer }) {
randomRangeMax
};
+ console.log('Merged profile to simulate =>', mergedProfile);
+
const { projectionData: pData, loanPaidOffMonth } =
simulateFinancialProjection(mergedProfile);
diff --git a/src/components/PremiumOnboarding/CollegeOnboarding.js b/src/components/PremiumOnboarding/CollegeOnboarding.js
index 97f2b59..820adf4 100644
--- a/src/components/PremiumOnboarding/CollegeOnboarding.js
+++ b/src/components/PremiumOnboarding/CollegeOnboarding.js
@@ -23,6 +23,7 @@ function CollegeOnboarding({ nextStep, prevStep, data, setData, careerProfileId
annual_financial_aid = '',
is_online = false,
existing_college_debt = '',
+ enrollment_date = '',
expected_graduation = '',
interest_rate = 5.5,
loan_term = 10,
@@ -494,11 +495,24 @@ function CollegeOnboarding({ nextStep, prevStep, data, setData, careerProfileId
/>
+ {college_enrollment_status === 'prospective_student' && (
+
+
+
+
+ )}
+
{/* If "currently_enrolled" show Hours Completed + Program Length */}
{college_enrollment_status === 'currently_enrolled' && (
<>
-
+
{
localStorage.removeItem('premiumOnboardingState');
// 5) Navigate away
- navigate('/milestone-tracker');
+ navigate('/career-roadmap');
} catch (err) {
console.error(err);
// Optionally show error to user
diff --git a/src/components/PremiumOnboarding/ReviewPage.js b/src/components/PremiumOnboarding/ReviewPage.js
index c39ad7d..5696934 100644
--- a/src/components/PremiumOnboarding/ReviewPage.js
+++ b/src/components/PremiumOnboarding/ReviewPage.js
@@ -39,7 +39,7 @@ function ReviewPage({
Career Info
Career Name: {careerData.career_name || 'N/A'}
Currently Working: {careerData.currently_working || 'N/A'}
-
Enrollment Status: {careerData.college_enrollment_status || 'N/A'}
+
College enrollment Status: {careerData.college_enrollment_status || 'N/A'}
Status: {careerData.status || 'N/A'}
Start Date: {careerData.start_date || 'N/A'}
Projected End Date: {careerData.projected_end_date || 'N/A'}
@@ -90,21 +90,21 @@ function ReviewPage({
College Info
-
College Name
-
Major
-
Program Type
-
Tuition (calculated)
-
Program Length (years)
-
Credit Hours Per Year
-
Credit Hours Required
-
Hours Completed
-
Is In State?
-
Loan Deferral Until Graduation?
-
Annual Financial Aid
-
Existing College Debt
-
Extra Monthly Payment
-
Expected Graduation
-
Expected Salary
+
College Name: {formatNum(collegeData.selected_school)}
+
Major {formatNum(collegeData.selected_program)}
+
Program Type {formatNum(collegeData.program_type)}
+
Yearly Tuition {formatNum(collegeData.tuition)}
+
Program Length (years) {formatNum(collegeData.program_length)}
+
Credit Hours Per Year {formatNum(collegeData.credit_hours_per_year)}
+
Credit Hours Required {formatNum(collegeData.credit_hours_required)}
+
Hours Completed {formatNum(collegeData.hours_completed)}
+
Is In State? {formatNum(collegeData.is_in_state)}
+
Loan Deferral Until Graduation? {formatNum(collegeData.loan_deferral_until_graduation)}
+
Annual Financial Aid {formatNum(collegeData.annual_financial_aid)}
+
Existing College Debt {formatNum(collegeData.existing_college_debt)}
+
Extra Monthly Payment {formatNum(collegeData.extra_payment)}
+
Expected Graduation {formatNum(collegeData.expected_graduation)}
+
Expected Salary {formatNum(collegeData.expected_salary)}
)}
diff --git a/src/components/ScenarioEditModal.js b/src/components/ScenarioEditModal.js
index 6d2f478..ced0d01 100644
--- a/src/components/ScenarioEditModal.js
+++ b/src/components/ScenarioEditModal.js
@@ -2,6 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
import authFetch from '../utils/authFetch.js';
import { simulateFinancialProjection } from '../utils/FinancialProjectionService.js';
import { Button } from './ui/button.js';
+import parseFloatOrZero from '../utils/ParseFloatorZero.js';
// Data paths
const CIP_URL = '/cip_institution_mapping_new.json';
@@ -137,17 +138,18 @@ export default function ScenarioEditModal({
college_enrollment_status: s.college_enrollment_status || 'not_enrolled',
currently_working: s.currently_working || 'no',
- planned_monthly_expenses: s.planned_monthly_expenses ?? '',
- planned_monthly_debt_payments: s.planned_monthly_debt_payments ?? '',
+ planned_monthly_expenses: s.planned_monthly_expenses ?? null,
+ planned_monthly_debt_payments: s.planned_monthly_debt_payments ?? null,
planned_monthly_retirement_contribution:
- s.planned_monthly_retirement_contribution ?? '',
+ s.planned_monthly_retirement_contribution ?? null,
planned_monthly_emergency_contribution:
- s.planned_monthly_emergency_contribution ?? '',
- planned_surplus_emergency_pct: s.planned_surplus_emergency_pct ?? '',
- planned_surplus_retirement_pct: s.planned_surplus_retirement_pct ?? '',
- planned_additional_income: s.planned_additional_income ?? '',
+ s.planned_monthly_emergency_contribution ?? null,
+ planned_surplus_emergency_pct: s.planned_surplus_emergency_pct ?? null,
+ planned_surplus_retirement_pct: s.planned_surplus_retirement_pct ?? null,
+ planned_additional_income: s.planned_additional_income ?? null,
// college portion
+ college_profile_id: c.id || null,
selected_school: c.selected_school || '',
selected_program: c.selected_program || '',
program_type: c.program_type || '',
@@ -169,7 +171,8 @@ export default function ScenarioEditModal({
hours_completed: c.hours_completed ?? '',
program_length: c.program_length ?? '',
credit_hours_required: c.credit_hours_required ?? '',
- expected_graduation: c.expected_graduation || '',
+ enrollment_date: c.enrollment_date ? c.enrollment_date.substring(0, 10): '',
+ expected_graduation: c.expected_graduation ? c.expected_graduation.substring(0, 10): '',
expected_salary: c.expected_salary ?? ''
});
@@ -415,7 +418,7 @@ export default function ScenarioEditModal({
annualFinancialAid: collegeRow.annual_financial_aid || 0,
calculatedTuition: collegeRow.tuition || 0,
extraPayment: collegeRow.extra_payment || 0,
-
+ enrollmentDate: collegeRow.enrollment_date || null,
gradDate: collegeRow.expected_graduation || null,
programType: collegeRow.program_type || null,
hoursCompleted: collegeRow.hours_completed || 0,
@@ -467,6 +470,12 @@ export default function ScenarioEditModal({
// Build scenario payload
const scenarioPayload = {};
+
+ // If scenario already has an id, include it:
+ if (scenario?.id) {
+ scenarioPayload.id = scenario.id;
+ }
+
scenarioPayload.college_enrollment_status = finalCollegeStatus;
scenarioPayload.currently_working = formData.currently_working || 'no';
@@ -486,8 +495,7 @@ export default function ScenarioEditModal({
formData.projected_end_date &&
formData.projected_end_date.trim() !== ''
) {
- scenarioPayload.projected_end_date =
- formData.projected_end_date.trim();
+ scenarioPayload.projected_end_date = formData.projected_end_date.trim();
}
const pme = parseNumberIfGiven(formData.planned_monthly_expenses);
@@ -537,6 +545,7 @@ export default function ScenarioEditModal({
// 2) Build college payload
const collegePayload = {
+ id: formData.college_profile_id || null,
career_profile_id: updatedScenarioId,
college_enrollment_status: finalCollegeStatus,
is_in_state: formData.is_in_state ? 1 : 0,
@@ -556,12 +565,16 @@ export default function ScenarioEditModal({
const acCal = parseStringIfGiven(formData.academic_calendar);
if (acCal !== undefined) collegePayload.academic_calendar = acCal;
- if (
- formData.expected_graduation &&
- formData.expected_graduation.trim() !== ''
- ) {
- collegePayload.expected_graduation =
- formData.expected_graduation.trim();
+ if (formData.expected_graduation && formData.expected_graduation.trim() !== '') {
+ collegePayload.expected_graduation = formData.expected_graduation
+ .trim()
+ .substring(0, 10);
+ }
+
+ if (formData.enrollment_date && formData.enrollment_date.trim() !== '') {
+ collegePayload.enrollment_date = formData.enrollment_date
+ .trim()
+ .substring(0, 10);
}
const afa = parseNumberIfGiven(formData.annual_financial_aid);
@@ -607,30 +620,31 @@ export default function ScenarioEditModal({
}
// 3) Upsert or skip
- if (finalCollegeStatus === 'currently_enrolled' ||
- finalCollegeStatus === 'prospective_student')
- {
- const colRes = await authFetch('/api/premium/college-profile', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(collegePayload),
- });
- if (!colRes.ok) {
- const msg2 = await colRes.text();
- throw new Error(`College upsert failed: ${msg2}`);
- }
- } else {
- console.log('Skipping college-profile upsert in EditScenarioModal because user not enrolled');
- // Optionally: if you want to delete an existing college profile:
- // await authFetch(`/api/premium/college-profile/delete/${updatedScenarioId}`, { method: 'DELETE' });
+ if (
+ finalCollegeStatus === 'currently_enrolled' ||
+ finalCollegeStatus === 'prospective_student'
+ ) {
+ const colRes = await authFetch('/api/premium/college-profile', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(collegePayload)
+ });
+ if (!colRes.ok) {
+ const msg2 = await colRes.text();
+ throw new Error(`College upsert failed: ${msg2}`);
}
+ } else {
+ console.log(
+ 'Skipping college-profile upsert in EditScenarioModal because user not enrolled'
+ );
+ // Optionally: if you want to delete an existing college profile:
+ // await authFetch(`/api/premium/college-profile/delete/${updatedScenarioId}`, { method: 'DELETE' });
+ }
// 4) Re-fetch scenario, college, financial => aggregator => simulate
const [scenResp2, colResp2, finResp] = await Promise.all([
authFetch(`/api/premium/career-profile/${updatedScenarioId}`),
- authFetch(
- `/api/premium/college-profile?careerProfileId=${updatedScenarioId}`
- ),
+ authFetch(`/api/premium/college-profile?careerProfileId=${updatedScenarioId}`),
authFetch(`/api/premium/financial-profile`)
]);
if (!scenResp2.ok || !colResp2.ok || !finResp.ok) {
@@ -643,24 +657,139 @@ export default function ScenarioEditModal({
return;
}
- const [finalScenarioRow, finalCollegeRaw, finalFinancial] =
- await Promise.all([scenResp2.json(), colResp2.json(), finResp.json()]);
+ const [finalScenarioRow, finalCollegeRaw, finalFinancial] = await Promise.all([
+ scenResp2.json(),
+ colResp2.json(),
+ finResp.json()
+ ]);
let finalCollegeRow = Array.isArray(finalCollegeRaw)
? finalCollegeRaw[0] || {}
: finalCollegeRaw;
- // 5) Simulate
+ // -------------------------------------------
+ // 5) Before simulate: parse numeric fields
+ // to avoid .toFixed errors
+ // -------------------------------------------
+ // scenario planned_ fields
+ if (finalScenarioRow.planned_monthly_expenses != null) {
+ finalScenarioRow.planned_monthly_expenses = parseFloatOrZero(
+ finalScenarioRow.planned_monthly_expenses,
+ null
+ );
+ }
+ if (finalScenarioRow.planned_monthly_debt_payments != null) {
+ finalScenarioRow.planned_monthly_debt_payments = parseFloatOrZero(
+ finalScenarioRow.planned_monthly_debt_payments,
+ null
+ );
+ }
+ if (finalScenarioRow.planned_monthly_retirement_contribution != null) {
+ finalScenarioRow.planned_monthly_retirement_contribution = parseFloatOrZero(
+ finalScenarioRow.planned_monthly_retirement_contribution,
+ null
+ );
+ }
+ if (finalScenarioRow.planned_monthly_emergency_contribution != null) {
+ finalScenarioRow.planned_monthly_emergency_contribution = parseFloatOrZero(
+ finalScenarioRow.planned_monthly_emergency_contribution,
+ null
+ );
+ }
+ if (finalScenarioRow.planned_surplus_emergency_pct != null) {
+ finalScenarioRow.planned_surplus_emergency_pct = parseFloatOrZero(
+ finalScenarioRow.planned_surplus_emergency_pct,
+ null
+ );
+ }
+ if (finalScenarioRow.planned_surplus_retirement_pct != null) {
+ finalScenarioRow.planned_surplus_retirement_pct = parseFloatOrZero(
+ finalScenarioRow.planned_surplus_retirement_pct,
+ null
+ );
+ }
+ if (finalScenarioRow.planned_additional_income != null) {
+ finalScenarioRow.planned_additional_income = parseFloatOrZero(
+ finalScenarioRow.planned_additional_income,
+ null
+ );
+ }
+
+ // college numeric fields (force all to numbers or 0)
+ const numericFields = [
+ 'existing_college_debt',
+ 'extra_payment',
+ 'tuition',
+ 'tuition_paid',
+ 'interest_rate',
+ 'loan_term',
+ 'credit_hours_per_year',
+ 'hours_completed',
+ 'program_length',
+ 'expected_salary',
+ 'annual_financial_aid',
+ 'credit_hours_required'
+ ];
+ for (const field of numericFields) {
+ if (finalCollegeRow[field] != null) {
+ finalCollegeRow[field] = parseFloatOrZero(finalCollegeRow[field], 0);
+ } else {
+ finalCollegeRow[field] = 0;
+ }
+ }
+
+ // Also ensure all scenario/financial fields used in buildMergedUserProfile are numbers
+ const scenarioNumericFields = [
+ 'planned_monthly_expenses',
+ 'planned_monthly_debt_payments',
+ 'planned_monthly_retirement_contribution',
+ 'planned_monthly_emergency_contribution',
+ 'planned_surplus_emergency_pct',
+ 'planned_surplus_retirement_pct',
+ 'planned_additional_income'
+ ];
+ for (const field of scenarioNumericFields) {
+ if (finalScenarioRow[field] != null) {
+ finalScenarioRow[field] = parseFloatOrZero(finalScenarioRow[field], 0);
+ } else {
+ finalScenarioRow[field] = 0;
+ }
+ }
+ if (finalFinancial) {
+ const financialNumericFields = [
+ 'current_salary',
+ 'monthly_expenses',
+ 'monthly_debt_payments',
+ 'additional_income',
+ 'emergency_fund',
+ 'retirement_savings',
+ 'retirement_contribution',
+ 'emergency_contribution',
+ 'extra_cash_emergency_pct',
+ 'extra_cash_retirement_pct'
+ ];
+ for (const field of financialNumericFields) {
+ if (finalFinancial[field] != null) {
+ finalFinancial[field] = parseFloatOrZero(finalFinancial[field], 0);
+ } else {
+ finalFinancial[field] = 0;
+ }
+ }
+ }
+
+ // 6) Now simulate
const userProfile = buildMergedUserProfile(
finalScenarioRow,
finalCollegeRow,
finalFinancial
);
+ console.log('UserProfile from Modal:', userProfile);
+
const results = simulateFinancialProjection(userProfile);
setProjectionData(results.projectionData);
setLoanPayoffMonth(results.loanPaidOffMonth);
- // 6) Close or reload
+ // 7) Close or reload
onClose();
window.location.reload();
} catch (err) {
@@ -797,7 +926,7 @@ export default function ScenarioEditModal({
{/* College Enrollment Status */}
@@ -1169,6 +1298,20 @@ export default function ScenarioEditModal({
>
)}
+ {/* Enrollment Date */}
+
+
+
+
+
{/* Expected Graduation */}