96 lines
3.3 KiB
JavaScript
96 lines
3.3 KiB
JavaScript
// 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);
|
||
|
||
// Human‑readable 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'
|
||
};
|
||
|
||
/**
|
||
* 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
|
||
if (!hasValue(financial.current_salary)) missing.push('current_salary');
|
||
|
||
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 they’re enrolled / prospective
|
||
if (requireCollegeData) {
|
||
if (!hasValue(college.tuition)) missing.push('tuition');
|
||
|
||
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 they’ve 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;
|
||
}
|