dev1/src/utils/getMissingFields.js
2025-08-25 13:14:09 +00:00

104 lines
3.8 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.

// utils/getMissingFields.js
// Treat 0 / "0" as present; only null/undefined/"" are missing.
export const hasValue = (v) => {
if (v === null || v === undefined) return false;
if (typeof v === 'string') return v.trim().length > 0;
return true; // numbers, booleans, objects…
};
// helper: pass if any of these have a value
const any = (...vals) => vals.some(hasValue);
// Humanreadable labels for the banner
export const MISSING_LABELS = {
start_date: 'Start date',
retirement_start_date: 'Planned retirement date',
current_salary: 'Current salary',
monthly_expenses: 'Monthly expenses (or scenario override)',
monthly_debt_payments: 'Monthly debt payments (or scenario override)',
savings_plan: 'Savings plan (either contributions or surplus %)',
tuition: 'Yearly tuition (can be 0)',
interest_rate: 'Loan interest rate',
loan_term: 'Loan term',
expected_graduation: 'Expected graduation date',
program_type: 'Program type',
academic_calendar: 'Academic calendar',
expected_salary: 'Expected salary after graduation'
};
/**
* Return a list of *blocking* missing fields.
* Keep this minimal so the yellow banner only appears when
* the projection truly cannot run.
*/
export default function getMissingFields(
{ scenario = {}, financial = {}, college = {} },
{ requireCollegeData = false } = {}
) {
const missing = [];
// ── Scenario (only what the simulator truly needs)
if (!hasValue(scenario.start_date)) missing.push('start_date');
if (!hasValue(scenario.retirement_start_date)) missing.push('retirement_start_date');
// ── Financial base
// Intentionally do NOT require current salary.
// Missing/0 income is allowed; we only require expected salary (below) for students.
if (!any(scenario.planned_monthly_expenses, financial.monthly_expenses)) {
missing.push('monthly_expenses');
}
if (!any(scenario.planned_monthly_debt_payments, financial.monthly_debt_payments)) {
missing.push('monthly_debt_payments');
}
// ── Savings plan: need at least one way to save
const hasRetirementPlan = any(
scenario.planned_monthly_retirement_contribution,
financial.retirement_contribution,
scenario.planned_surplus_retirement_pct
);
const hasEmergencyPlan = any(
scenario.planned_monthly_emergency_contribution,
financial.emergency_contribution,
scenario.planned_surplus_emergency_pct
);
if (!(hasRetirementPlan || hasEmergencyPlan)) {
missing.push('savings_plan');
}
// ── College only if theyre enrolled / prospective
if (requireCollegeData) {
if (!hasValue(college.tuition)) missing.push('tuition');
// If user has no income (0 or missing), require an expected post-grad salary
const incomeNum = Number(financial.current_salary);
const noIncome = !hasValue(financial.current_salary) || !Number.isFinite(incomeNum) || incomeNum <= 0;
if (noIncome && !hasValue(college.expected_salary)) {
missing.push('expected_salary');
}
const plansToBorrow =
(Number(college.existing_college_debt) || 0) > 0 ||
(Number(college.tuition) || 0) > (Number(college.annual_financial_aid) || 0);
if (plansToBorrow) {
if (!hasValue(college.interest_rate)) missing.push('interest_rate');
if (!hasValue(college.loan_term)) missing.push('loan_term');
if (college.loan_deferral_until_graduation && !hasValue(college.expected_graduation)) {
missing.push('expected_graduation');
}
}
// Only insist on these if theyve actually chosen a school/program
const hasSchoolOrProgram = any(college.selected_school, college.selected_program);
if (hasSchoolOrProgram) {
if (!hasValue(college.program_type)) missing.push('program_type');
if (!hasValue(college.academic_calendar)) missing.push('academic_calendar');
}
}
return missing;
}