// 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; }