dev1/src/components/ScenarioEditModal.js

972 lines
32 KiB
JavaScript

// src/components/ScenarioEditModal.js
import React, { useState, useEffect, useRef } from 'react';
import authFetch from '../utils/authFetch.js';
// Paths to your JSON/CSV data. Adjust if needed:
const CIP_URL = '/cip_institution_mapping_new.json';
const IPEDS_URL = '/ic2023_ay.csv';
const CAREER_CLUSTERS_URL = '/career_clusters.json';
export default function ScenarioEditModal({
show,
onClose, // onClose(updatedScenario, updatedCollege)
scenario,
collegeProfile
}) {
const [formData, setFormData] = useState({});
// CIP & IPEDS data
const [schoolData, setSchoolData] = useState([]);
const [icTuitionData, setIcTuitionData] = useState([]);
// suggestions
const [schoolSuggestions, setSchoolSuggestions] = useState([]);
const [programSuggestions, setProgramSuggestions] = useState([]);
const [availableProgramTypes, setAvailableProgramTypes] = useState([]);
// manual vs auto for tuition & program length
const [manualTuition, setManualTuition] = useState('');
const [autoTuition, setAutoTuition] = useState(0);
const [manualProgLength, setManualProgLength] = useState('');
const [autoProgLength, setAutoProgLength] = useState('0.00');
// career auto-suggest
const [allCareers, setAllCareers] = useState([]);
const [careerSearchInput, setCareerSearchInput] = useState('');
const [careerMatches, setCareerMatches] = useState([]);
const careerDropdownRef = useRef(null);
// ---------- Load CIP/iPEDS/career data once ----------
useEffect(() => {
async function loadCIP() {
try {
const res = await fetch(CIP_URL);
const text = await res.text();
const lines = text.split('\n');
const parsed = lines
.map((line) => {
try {
return JSON.parse(line);
} catch {
return null;
}
})
.filter(Boolean);
setSchoolData(parsed);
} catch (err) {
console.error('Failed to load CIP data:', err);
}
}
async function loadIPEDS() {
try {
const res = await fetch(IPEDS_URL);
const text = await res.text();
const rows = text.split('\n').map((line) => line.split(','));
const headers = rows[0];
const dataRows = rows.slice(1).map((row) =>
Object.fromEntries(row.map((val, idx) => [headers[idx], val]))
);
setIcTuitionData(dataRows);
} catch (err) {
console.error('Failed to load iPEDS data:', err);
}
}
async function loadCareers() {
try {
const res = await fetch(CAREER_CLUSTERS_URL);
const data = await res.json();
const titles = new Set();
for (const cluster of Object.keys(data)) {
for (const subdiv of Object.keys(data[cluster])) {
const arr = data[cluster][subdiv];
arr.forEach((obj) => {
if (obj.title) titles.add(obj.title);
});
}
}
setAllCareers([...titles]);
} catch (err) {
console.error('Failed to load career_clusters:', err);
}
}
loadCIP();
loadIPEDS();
loadCareers();
}, []);
// ---------- career auto-suggest logic ----------
useEffect(() => {
if (!careerSearchInput) {
setCareerMatches([]);
return;
}
const lower = careerSearchInput.toLowerCase();
const partials = allCareers
.filter((title) => title.toLowerCase().includes(lower))
.slice(0, 15);
setCareerMatches(partials);
}, [careerSearchInput, allCareers]);
function handleCareerInputChange(e) {
const val = e.target.value;
setCareerSearchInput(val);
if (allCareers.includes(val)) {
setFormData((prev) => ({ ...prev, career_name: val }));
}
}
function handleSelectCareer(title) {
setCareerSearchInput(title);
setFormData((prev) => ({ ...prev, career_name: title }));
setCareerMatches([]);
}
// ---------- Show => populate formData from scenario & college ----------
useEffect(() => {
if (!show) return;
if (!scenario) return;
const s = scenario || {};
const c = collegeProfile || {};
setFormData({
// Scenario
scenario_title: s.scenario_title || '',
career_name: s.career_name || '',
status: s.status || 'planned',
start_date: s.start_date || '',
projected_end_date: s.projected_end_date || '',
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_retirement_contribution:
s.planned_monthly_retirement_contribution ?? '',
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 ?? '',
// College
selected_school: c.selected_school || '',
selected_program: c.selected_program || '',
program_type: c.program_type || '',
academic_calendar: c.academic_calendar || 'semester',
is_in_state: !!c.is_in_state,
is_in_district: !!c.is_in_district,
is_online: !!c.is_online,
college_enrollment_status_db: c.college_enrollment_status || 'not_enrolled',
annual_financial_aid: c.annual_financial_aid ?? '',
existing_college_debt: c.existing_college_debt ?? '',
tuition: c.tuition ?? 0,
tuition_paid: c.tuition_paid ?? 0,
loan_deferral_until_graduation: !!c.loan_deferral_until_graduation,
loan_term: c.loan_term ?? 10,
interest_rate: c.interest_rate ?? 5,
extra_payment: c.extra_payment ?? 0,
credit_hours_per_year: c.credit_hours_per_year ?? '',
hours_completed: c.hours_completed ?? '',
program_length: c.program_length ?? '',
credit_hours_required: c.credit_hours_required ?? '',
expected_graduation: c.expected_graduation || '',
expected_salary: c.expected_salary ?? ''
});
// set up manual vs auto
setManualTuition('');
setAutoTuition(0);
setManualProgLength('');
setAutoProgLength('0.00');
// career input
setCareerSearchInput(s.career_name || '');
}, [show, scenario, collegeProfile]);
// ---------- handle form changes ----------
function handleFormChange(e) {
const { name, type, checked, value } = e.target;
let val = value;
if (type === 'checkbox') {
val = checked;
}
setFormData((prev) => ({ ...prev, [name]: val }));
}
// ---------- school / program changes ----------
function handleSchoolChange(e) {
const val = e.target.value;
setFormData((prev) => ({
...prev,
selected_school: val,
selected_program: '',
program_type: '',
credit_hours_required: ''
}));
if (!val) {
setSchoolSuggestions([]);
return;
}
const filtered = schoolData.filter((s) =>
s.INSTNM.toLowerCase().includes(val.toLowerCase())
);
const uniqueSchools = [...new Set(filtered.map((s) => s.INSTNM))];
setSchoolSuggestions(uniqueSchools.slice(0, 10));
setProgramSuggestions([]);
setAvailableProgramTypes([]);
}
function handleSchoolSelect(schoolName) {
setFormData((prev) => ({
...prev,
selected_school: schoolName,
selected_program: '',
program_type: '',
credit_hours_required: ''
}));
setSchoolSuggestions([]);
setProgramSuggestions([]);
setAvailableProgramTypes([]);
}
function handleProgramChange(e) {
const val = e.target.value;
setFormData((prev) => ({ ...prev, selected_program: val }));
if (!val) {
setProgramSuggestions([]);
return;
}
const filtered = schoolData.filter(
(row) =>
row.INSTNM.toLowerCase() ===
formData.selected_school.toLowerCase() &&
row.CIPDESC.toLowerCase().includes(val.toLowerCase())
);
const uniquePrograms = [...new Set(filtered.map((r) => r.CIPDESC))];
setProgramSuggestions(uniquePrograms.slice(0, 10));
}
function handleProgramSelect(prog) {
setFormData((prev) => ({ ...prev, selected_program: prog }));
setProgramSuggestions([]);
}
function handleProgramTypeSelect(e) {
setFormData((prev) => ({
...prev,
program_type: e.target.value,
credit_hours_required: ''
}));
setManualProgLength('');
setAutoProgLength('0.00');
}
// ---------- manual tuition & program length ----------
function handleManualTuitionChange(e) {
setManualTuition(e.target.value);
}
function handleManualProgLengthChange(e) {
setManualProgLength(e.target.value);
}
// ---------- auto-calc tuition ----------
useEffect(() => {
const {
selected_school,
program_type,
credit_hours_per_year,
is_in_state,
is_in_district
} = formData;
if (!icTuitionData.length) return;
if (!selected_school || !program_type || !credit_hours_per_year) return;
// find
const found = schoolData.find(
(s) => s.INSTNM.toLowerCase() === selected_school.toLowerCase()
);
if (!found?.UNITID) return;
const match = icTuitionData.find((row) => row.UNITID === found.UNITID);
if (!match) return;
const isGradOrProf = [
"Master's Degree",
"Doctoral Degree",
"Graduate/Professional Certificate",
"First Professional Degree"
].includes(program_type);
let partTimeRate = 0;
let fullTimeTuition = 0;
if (isGradOrProf) {
if (is_in_district) {
partTimeRate = parseFloat(match.HRCHG5 || 0);
fullTimeTuition = parseFloat(match.TUITION5 || 0);
} else if (is_in_state) {
partTimeRate = parseFloat(match.HRCHG6 || 0);
fullTimeTuition = parseFloat(match.TUITION6 || 0);
} else {
partTimeRate = parseFloat(match.HRCHG7 || 0);
fullTimeTuition = parseFloat(match.TUITION7 || 0);
}
} else {
// undergrad
if (is_in_district) {
partTimeRate = parseFloat(match.HRCHG1 || 0);
fullTimeTuition = parseFloat(match.TUITION1 || 0);
} else if (is_in_state) {
partTimeRate = parseFloat(match.HRCHG2 || 0);
fullTimeTuition = parseFloat(match.TUITION2 || 0);
} else {
partTimeRate = parseFloat(match.HRCHG3 || 0);
fullTimeTuition = parseFloat(match.TUITION3 || 0);
}
}
const chpy = parseFloat(credit_hours_per_year) || 0;
let estimate = 0;
if (chpy < 24 && partTimeRate) {
estimate = partTimeRate * chpy;
} else {
estimate = fullTimeTuition;
}
setAutoTuition(Math.round(estimate));
}, [
icTuitionData,
formData.selected_school,
formData.program_type,
formData.credit_hours_per_year,
formData.is_in_district,
formData.is_in_state,
schoolData
]);
// ---------- auto-calc program length ----------
useEffect(() => {
const { program_type, hours_completed, credit_hours_per_year, credit_hours_required } =
formData;
if (!program_type) return;
if (!hours_completed || !credit_hours_per_year) return;
let required = 0;
switch (program_type) {
case "Associate's Degree":
required = 60;
break;
case "Bachelor's Degree":
required = 120;
break;
case "Master's Degree":
required = 60;
break;
case "Doctoral Degree":
required = 120;
break;
case "First Professional Degree":
required = 180;
break;
case "Graduate/Professional Certificate":
required = parseInt(credit_hours_required, 10) || 0;
break;
default:
required = parseInt(credit_hours_required, 10) || 0;
break;
}
const remain = required - (parseInt(hours_completed, 10) || 0);
const yrs = remain / (parseFloat(credit_hours_per_year) || 1);
setAutoProgLength(yrs.toFixed(2));
}, [
formData.program_type,
formData.hours_completed,
formData.credit_hours_per_year,
formData.credit_hours_required
]);
// ---------- handleSave => PUT scenario & college => onClose(...)
async function handleSave() {
try {
// chosen tuition
const chosenTuition =
manualTuition.trim() === '' ? autoTuition : parseFloat(manualTuition);
const chosenProgLen =
manualProgLength.trim() === '' ? autoProgLength : manualProgLength;
// scenario payload
const scenarioPayload = {
scenario_title: formData.scenario_title || '',
career_name: formData.career_name || '',
status: formData.status || 'planned',
start_date: formData.start_date || null,
projected_end_date: formData.projected_end_date || null,
college_enrollment_status: formData.college_enrollment_status || 'not_enrolled',
currently_working: formData.currently_working || 'no',
planned_monthly_expenses:
formData.planned_monthly_expenses === '' ? null : Number(formData.planned_monthly_expenses),
planned_monthly_debt_payments:
formData.planned_monthly_debt_payments === '' ? null : Number(formData.planned_monthly_debt_payments),
planned_monthly_retirement_contribution:
formData.planned_monthly_retirement_contribution === '' ? null : Number(formData.planned_monthly_retirement_contribution),
planned_monthly_emergency_contribution:
formData.planned_monthly_emergency_contribution === '' ? null : Number(formData.planned_monthly_emergency_contribution),
planned_surplus_emergency_pct:
formData.planned_surplus_emergency_pct === '' ? null : Number(formData.planned_surplus_emergency_pct),
planned_surplus_retirement_pct:
formData.planned_surplus_retirement_pct === '' ? null : Number(formData.planned_surplus_retirement_pct),
planned_additional_income:
formData.planned_additional_income === '' ? null : Number(formData.planned_additional_income)
};
// 1) Put scenario
const scenRes = await authFetch(`/api/premium/career-profile/${scenario.id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(scenarioPayload)
});
if (!scenRes.ok) {
const eText = await scenRes.text();
throw new Error('Scenario update failed: ' + eText);
}
const updatedScenario = await scenRes.json(); // updated scenario row
// 2) Put college
const colId = collegeProfile?.id; // or handle no ID
const collegePayload = {
selected_school: formData.selected_school || null,
selected_program: formData.selected_program || null,
program_type: formData.program_type || null,
academic_calendar: formData.academic_calendar || 'semester',
is_in_state: formData.is_in_state ? 1 : 0,
is_in_district: formData.is_in_district ? 1 : 0,
is_online: formData.is_online ? 1 : 0,
college_enrollment_status: formData.college_enrollment_status_db || 'not_enrolled',
annual_financial_aid:
formData.annual_financial_aid === '' ? 0 : Number(formData.annual_financial_aid),
existing_college_debt:
formData.existing_college_debt === '' ? 0 : Number(formData.existing_college_debt),
tuition: chosenTuition,
tuition_paid:
formData.tuition_paid === '' ? 0 : Number(formData.tuition_paid),
loan_deferral_until_graduation:
formData.loan_deferral_until_graduation ? 1 : 0,
loan_term:
formData.loan_term === '' ? 10 : Number(formData.loan_term),
interest_rate:
formData.interest_rate === '' ? 5 : Number(formData.interest_rate),
extra_payment:
formData.extra_payment === '' ? 0 : Number(formData.extra_payment),
credit_hours_per_year:
formData.credit_hours_per_year === '' ? 0 : Number(formData.credit_hours_per_year),
hours_completed:
formData.hours_completed === '' ? 0 : Number(formData.hours_completed),
program_length: chosenProgLen,
credit_hours_required:
formData.credit_hours_required === '' ? 0 : Number(formData.credit_hours_required),
expected_graduation: formData.expected_graduation || null,
expected_salary:
formData.expected_salary === '' ? 0 : Number(formData.expected_salary)
};
const colRes = await authFetch(`/api/premium/college-profile/${colId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(collegePayload)
});
if (!colRes.ok) {
const colText = await colRes.text();
throw new Error('College update failed: ' + colText);
}
const updatedCollege = await colRes.json();
onClose(updatedScenario, updatedCollege);
} catch (err) {
console.error('Error in handleSave:', err);
alert(err.message || 'Failed to save scenario changes');
}
}
if (!show) return null;
// displayed tuition/programLength
const displayedTuition =
manualTuition.trim() === '' ? autoTuition : manualTuition;
const displayedProgLen =
manualProgLength.trim() === '' ? autoProgLength : manualProgLength;
return (
<div
className="modal-backdrop"
style={{
position: 'fixed',
top: 0,
left: 0,
width: '100vw',
height: '100vh',
background: 'rgba(0,0,0,0.6)',
zIndex: 9999
}}
>
<div
className="modal-container"
style={{
background: '#fff',
width: '90%',
maxWidth: '900px',
maxHeight: '85vh',
margin: '30px auto',
padding: '1rem',
borderRadius: '6px',
overflowY: 'auto', // allow scroll
position: 'relative'
}}
>
<h2>
Edit Scenario: {scenario?.scenario_title || scenario?.career_name}
</h2>
{/* ============ SCENARIO (CAREER) SECTION ============ */}
<h3 style={{ marginTop: '1rem' }}>Scenario (Career Paths)</h3>
<hr />
<div style={{ marginTop: '0.5rem' }}>
<label>Scenario Title</label>
<input
type="text"
name="scenario_title"
value={formData.scenario_title}
onChange={handleFormChange}
style={{ width: '100%', marginBottom: '0.5rem' }}
/>
<label>Career Search</label>
<input
type="text"
value={careerSearchInput}
onChange={handleCareerInputChange}
style={{ width: '100%' }}
/>
{careerMatches.length > 0 && (
<ul
ref={careerDropdownRef}
style={{
border: '1px solid #ccc',
padding: '4px',
maxHeight: '150px',
overflowY: 'auto',
background: '#fff',
position: 'absolute'
}}
>
{careerMatches.map((c, idx) => (
<li
key={idx}
style={{ cursor: 'pointer' }}
onClick={() => handleSelectCareer(c)}
>
{c}
</li>
))}
</ul>
)}
<p>
<em>Current Career:</em> {formData.career_name || '(none)'}
</p>
<label>Status</label>
<select
name="status"
value={formData.status}
onChange={handleFormChange}
style={{ width: '100%', marginBottom: '0.5rem' }}
>
<option value="planned">Planned</option>
<option value="current">Current</option>
<option value="completed">Completed</option>
<option value="exploring">Exploring</option>
</select>
<label>Start Date</label>
<input
type="date"
name="start_date"
value={formData.start_date || ''}
onChange={handleFormChange}
style={{ width: '100%', marginBottom: '0.5rem' }}
/>
<label>Projected End Date</label>
<input
type="date"
name="projected_end_date"
value={formData.projected_end_date || ''}
onChange={handleFormChange}
style={{ width: '100%', marginBottom: '0.5rem' }}
/>
<label>College Enrollment (scenario)</label>
<select
name="college_enrollment_status"
value={formData.college_enrollment_status}
onChange={handleFormChange}
style={{ width: '100%', marginBottom: '0.5rem' }}
>
<option value="not_enrolled">Not Enrolled</option>
<option value="currently_enrolled">Currently Enrolled</option>
<option value="prospective_student">Prospective</option>
</select>
<label>Currently Working?</label>
<select
name="currently_working"
value={formData.currently_working}
onChange={handleFormChange}
style={{ width: '100%', marginBottom: '0.5rem' }}
>
<option value="yes">Yes</option>
<option value="no">No</option>
</select>
</div>
{/* ============ SCENARIO (FINANCIAL) SECTION ============ */}
<h3 style={{ marginTop: '1rem' }}>Scenario Overwrites (financial)</h3>
<hr />
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fill,minmax(220px,1fr))',
gap: '1rem',
marginTop: '0.5rem'
}}
>
<div>
<label>Monthly Expenses</label>
<input
type="number"
name="planned_monthly_expenses"
value={formData.planned_monthly_expenses}
onChange={handleFormChange}
style={{ width: '100%' }}
/>
</div>
<div>
<label>Monthly Debt Pmts</label>
<input
type="number"
name="planned_monthly_debt_payments"
value={formData.planned_monthly_debt_payments}
onChange={handleFormChange}
style={{ width: '100%' }}
/>
</div>
<div>
<label>Retirement Contrib</label>
<input
type="number"
name="planned_monthly_retirement_contribution"
value={formData.planned_monthly_retirement_contribution}
onChange={handleFormChange}
style={{ width: '100%' }}
/>
</div>
<div>
<label>Emergency Contrib</label>
<input
type="number"
name="planned_monthly_emergency_contribution"
value={formData.planned_monthly_emergency_contribution}
onChange={handleFormChange}
style={{ width: '100%' }}
/>
</div>
<div>
<label>Surplus % Emer</label>
<input
type="number"
name="planned_surplus_emergency_pct"
value={formData.planned_surplus_emergency_pct}
onChange={handleFormChange}
style={{ width: '100%' }}
/>
</div>
<div>
<label>Surplus % Ret</label>
<input
type="number"
name="planned_surplus_retirement_pct"
value={formData.planned_surplus_retirement_pct}
onChange={handleFormChange}
style={{ width: '100%' }}
/>
</div>
<div>
<label>Additional Income</label>
<input
type="number"
name="planned_additional_income"
value={formData.planned_additional_income}
onChange={handleFormChange}
style={{ width: '100%' }}
/>
</div>
</div>
{/* ============ COLLEGE SECTION ============ */}
<h3 style={{ marginTop: '1rem' }}>College Profile</h3>
<hr />
{(formData.college_enrollment_status === 'currently_enrolled' ||
formData.college_enrollment_status === 'prospective_student') ? (
<div style={{ marginTop: '0.5rem' }}>
<div>
<label style={{ marginRight: '1rem' }}>
<input
type="checkbox"
name="is_in_district"
checked={!!formData.is_in_district}
onChange={handleFormChange}
/>
In District
</label>
<label style={{ marginRight: '1rem' }}>
<input
type="checkbox"
name="is_in_state"
checked={!!formData.is_in_state}
onChange={handleFormChange}
/>
In State
</label>
<label>
<input
type="checkbox"
name="is_online"
checked={!!formData.is_online}
onChange={handleFormChange}
/>
Fully Online
</label>
</div>
<label>
<input
type="checkbox"
name="loan_deferral_until_graduation"
checked={!!formData.loan_deferral_until_graduation}
onChange={handleFormChange}
/>
{' '}Defer Loan Payments until Graduation?
</label>
<label>School</label>
<input
type="text"
value={formData.selected_school}
onChange={handleSchoolChange}
style={{ width: '100%' }}
/>
{schoolSuggestions.length > 0 && (
<ul
style={{
border: '1px solid #ccc',
padding: '4px',
maxHeight: '150px',
overflowY: 'auto',
background: '#fff',
position: 'absolute'
}}
>
{schoolSuggestions.map((sch, idx) => (
<li
key={idx}
style={{ cursor: 'pointer' }}
onClick={() => handleSchoolSelect(sch)}
>
{sch}
</li>
))}
</ul>
)}
<label>Program</label>
<input
type="text"
value={formData.selected_program}
onChange={handleProgramChange}
style={{ width: '100%' }}
/>
{programSuggestions.length > 0 && (
<ul
style={{
border: '1px solid #ccc',
padding: '4px',
maxHeight: '150px',
overflowY: 'auto',
background: '#fff',
position: 'absolute'
}}
>
{programSuggestions.map((prog, i) => (
<li
key={i}
style={{ cursor: 'pointer' }}
onClick={() => handleProgramSelect(prog)}
>
{prog}
</li>
))}
</ul>
)}
<label>Program Type</label>
<select
name="program_type"
value={formData.program_type}
onChange={handleProgramTypeSelect}
style={{ width: '100%' }}
>
<option value="">(none)</option>
{availableProgramTypes.map((pt, i) => (
<option key={i} value={pt}>
{pt}
</option>
))}
</select>
{['Graduate/Professional Certificate','Doctoral Degree','First Professional Degree']
.includes(formData.program_type) && (
<>
<label>Credit Hours Required</label>
<input
type="number"
name="credit_hours_required"
value={formData.credit_hours_required}
onChange={handleFormChange}
style={{ width: '100%' }}
/>
</>
)}
<label>Credit Hours per Year</label>
<input
type="number"
name="credit_hours_per_year"
value={formData.credit_hours_per_year}
onChange={handleFormChange}
style={{ width: '100%' }}
/>
<label>Yearly Tuition (auto/override)</label>
<input
type="number"
value={displayedTuition}
onChange={handleManualTuitionChange}
placeholder="blank => auto"
style={{ width: '100%' }}
/>
<label>Annual Financial Aid</label>
<input
type="number"
name="annual_financial_aid"
value={formData.annual_financial_aid}
onChange={handleFormChange}
style={{ width: '100%' }}
/>
<label>Existing College Debt</label>
<input
type="number"
name="existing_college_debt"
value={formData.existing_college_debt}
onChange={handleFormChange}
style={{ width: '100%' }}
/>
{formData.college_enrollment_status === 'currently_enrolled' && (
<>
<label>Tuition Paid</label>
<input
type="number"
name="tuition_paid"
value={formData.tuition_paid}
onChange={handleFormChange}
style={{ width: '100%' }}
/>
<label>Hours Completed</label>
<input
type="number"
name="hours_completed"
value={formData.hours_completed}
onChange={handleFormChange}
style={{ width: '100%' }}
/>
<label>Program Length (auto/override)</label>
<input
type="number"
value={manualProgLength.trim() === '' ? autoProgLength : manualProgLength}
onChange={handleManualProgLengthChange}
placeholder="blank => auto"
style={{ width: '100%' }}
/>
</>
)}
<label>Expected Graduation</label>
<input
type="date"
name="expected_graduation"
value={formData.expected_graduation}
onChange={handleFormChange}
style={{ width: '100%' }}
/>
<label>Interest Rate (%)</label>
<input
type="number"
name="interest_rate"
value={formData.interest_rate}
onChange={handleFormChange}
style={{ width: '100%' }}
/>
<label>Loan Term (years)</label>
<input
type="number"
name="loan_term"
value={formData.loan_term}
onChange={handleFormChange}
style={{ width: '100%' }}
/>
<label>Extra Payment (monthly)</label>
<input
type="number"
name="extra_payment"
value={formData.extra_payment}
onChange={handleFormChange}
style={{ width: '100%' }}
/>
<label>Expected Salary After Graduation</label>
<input
type="number"
name="expected_salary"
value={formData.expected_salary}
onChange={handleFormChange}
style={{ width: '100%' }}
/>
</div>
) : (
<p style={{ marginTop: '0.5rem' }}>
Not currently enrolled or prospective. Minimal college fields only.
</p>
)}
<div style={{ marginTop: '1rem', textAlign: 'right' }}>
<button onClick={() => onClose(null, null)} style={{ marginRight: '0.5rem' }}>
Cancel
</button>
<button onClick={handleSave}>Save</button>
</div>
</div>
</div>
);
}