// src/components/LoanRepaymentDrawer.js import React, { useState, useEffect, useMemo, useCallback } from 'react'; import { X } from 'lucide-react'; import LoanRepayment from './LoanRepayment.js'; import { Button } from './ui/button.js'; import { getNearbySchools } from '../utils/getNearbySchools.js'; import UpsellSummary from './UpsellSummary.js'; import { useNavigate } from 'react-router-dom'; /* ───────────────────────── CONSTANTS ───────────────────────── */ const DEGREE_OPTS = [ "Associate's Degree", "Bachelor's Degree", "Master's Degree", 'Graduate / Professional Certificate', 'First Professional Degree', 'Doctoral Degree', ]; /* ──────────────────────── COMPONENT ────────────────────────── */ export default function LoanRepaymentDrawer({ open, onClose, schools, // comes from PreparingLanding setSchools, results, setResults, user, cipCodes = [], userZip = '', userState='', }) { /* Hooks must always run – return null later if !open */ /* ── Remote data for auto-suggest ─ */ const [cipData, setCipData] = useState([]); const [schoolSearch, setSchoolSearch] = useState(''); /* ── Simple form fields ─ */ const [degree, setDegree] = useState(''); const [tuition, setTuition] = useState(''); const [err, setErr] = useState(''); /* ── When “Continue” is pressed show true calculator ─ */ const [showCalc, setShowCalc] = useState(false); const navigate = useNavigate(); /* ▒▒▒ NEW auto-seed effect ▒▒▒ */ useEffect(() => { // run once every time the drawer opens if (!open) return; // drawer closed if (schools.length) return; // already have data if (!cipCodes.length) return; // no career → nothing to seed (async () => { try { const seed = await getNearbySchools(cipCodes, userZip, userState); if (seed.length) setSchools(seed); } catch (e) { console.warn('auto-seed schools failed:', e); } })(); }, [open, schools.length, cipCodes.join('-'), userZip, userState, setSchools]); /* ════════════════════════════════════════════════════ FETCH CIP DATA (only once the drawer is ever opened) ════════════════════════════════════════════════════ */ useEffect(() => { if (!open || cipData.length) return; fetch('/cip_institution_mapping_new.json') .then(r => r.text()) .then(text => text .split('\n') .map(l => { try { return JSON.parse(l); } catch { return null; } }) .filter(Boolean) ) .then(arr => setCipData(arr)) .catch(e => console.error('CIP fetch error', e)); }, [open, cipData.length]); /* ════════════════════════════════════════════════════ SCHOOL AUTOCOMPLETE LIST (memoised) ════════════════════════════════════════════════════ */ const suggestions = useMemo(() => { if (!schoolSearch.trim()) return []; const low = schoolSearch.toLowerCase(); const set = new Set( cipData .filter(r => r.INSTNM.toLowerCase().includes(low)) .map(r => r.INSTNM) ); return [...set].slice(0, 10); }, [schoolSearch, cipData]); /* ════════════════════════════════════════════════════ ESC --> close convenience ════════════════════════════════════════════════════ */ const escHandler = useCallback(e => { if (e.key === 'Escape') onClose(); }, [onClose]); useEffect(() => { if (open) window.addEventListener('keydown', escHandler); return () => window.removeEventListener('keydown', escHandler); }, [open, escHandler]); /* ════════════════════════════════════════════════════ HANDLE “CONTINUE” ════════════════════════════════════════════════════ */ const handleContinue = () => { // Tuition is the only truly required field if (!tuition.trim()) { setErr('Please enter an annual tuition estimate.'); return; } setErr(''); // Build a stub “school” object so LoanRepayment // can work with the same shape it expects const stub = { name : schoolSearch || 'Unknown School', degreeType : degree || 'Unspecified', programLength : 4, // sensible default inState : parseFloat(tuition), outOfState : parseFloat(tuition), inStateGraduate : parseFloat(tuition), outStateGraduate: parseFloat(tuition), }; setSchools([stub]); // overwrite – single-school calc setShowCalc(true); }; const showUpsell = user && !user.is_premium && !user.is_pro_premium; /* ════════════════════════════════════════════════════ RENDER ════════════════════════════════════════════════════ */ if (!open) return null; return (
{/* Header */}

Estimate Student-Loan Payments

{/* Content */}
{/* Free money first */}

Free money first

  1. Submit the FAFSA (it’s free).
  2. Check state grant / Promise / HOPE programs.
  3. Apply for scholarships (FastWeb, Bold.org, local).
  4. Take gift-aid first, loans last.
{/* STEP 1: QUICK FORM */} {!showCalc && (
{ e.preventDefault(); handleContinue(); }} > {/* School name (optional) */}
setSchoolSearch(e.target.value)} list="school-suggestions" placeholder="Start typing…" className="mt-1 w-full rounded border px-3 py-2 text-sm" /> {suggestions.map((s, i) => (
{/* Degree */}
{/* Tuition */}
setTuition(e.target.value)} placeholder="e.g. 28000" className="mt-1 w-full rounded border px-3 py-2 text-sm" />
{/* Error */} {err &&

{err}

}
)} {/* STEP 2: REAL CALCULATOR */} {showCalc && ( <> {/* small separator */}
{/* PREMIUM CTA */} {showUpsell ? (

Want to see how this loan fits into a full financial plan?

Premium subscribers can model salary growth, living costs, retirement goals, and more.

) : ( /* If already premium just show a helpful note */

You’re on Premium — open the ​College Planning wizard to store this tuition in your plan (Profile ▸ Premium Onboarding ▸ College Details).

)} )} {/* STEP 3 : results table */} {results?.length > 0 && (

Estimated payments

{results.map((r, i) => ( ))}
School Monthly Total Cost Net Gain
{r.name} ${r.totalMonthlyPayment} ${r.totalLoanCost} ${r.netGain}
)}
); }