dev1/src/components/FinancialProfileForm.js

220 lines
9.9 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

// FinancialProfileForm.js
import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import authFetch from '../utils/authFetch.js';
import Modal from './ui/modal.js';
import ExpensesWizard from './ExpensesWizard.js'; // same wizard you use in onboarding
import { Button } from './ui/button.js'; // Tailwindbased button (optional)
/* helper clamp 0100 */
const pct = v => Math.min(Math.max(parseFloat(v) || 0, 0), 100);
export default function FinancialProfileForm() {
const nav = useNavigate();
/* ─────────────── local state ─────────────── */
const [currentSalary, setCurrentSalary] = useState('');
const [additionalIncome, setAdditionalIncome] = useState('');
const [monthlyExpenses, setMonthlyExpenses] = useState('');
const [monthlyDebtPayments, setMonthlyDebtPayments] = useState('');
const [retirementSavings, setRetirementSavings] = useState('');
const [emergencyFund, setEmergencyFund] = useState('');
const [retirementContribution, setRetirementContribution] = useState('');
const [emergencyContribution, setEmergencyContribution] = useState('');
const [extraCashEmergencyPct, setExtraCashEmergencyPct] = useState('50');
const [extraCashRetirementPct, setExtraCashRetirementPct] = useState('50');
/* wizard modal */
const [showExpensesWizard, setShowExpensesWizard] = useState(false);
const openWizard = () => setShowExpensesWizard(true);
const closeWizard = () => setShowExpensesWizard(false);
/* ───────────── preload existing row ───────── */
useEffect(() => {
(async () => {
try {
const res = await authFetch('/api/premium/financial-profile');
if (!res.ok) return;
const d = await res.json();
setCurrentSalary (d.current_salary ?? '');
setAdditionalIncome (d.additional_income ?? '');
setMonthlyExpenses (d.monthly_expenses ?? '');
setMonthlyDebtPayments (d.monthly_debt_payments ?? '');
setRetirementSavings (d.retirement_savings ?? '');
setEmergencyFund (d.emergency_fund ?? '');
setRetirementContribution (d.retirement_contribution ?? '');
setEmergencyContribution (d.emergency_contribution ?? '');
setExtraCashEmergencyPct (d.extra_cash_emergency_pct ?? '');
setExtraCashRetirementPct (d.extra_cash_retirement_pct ?? '');
} catch (err) { console.error(err); }
})();
}, []);
/* -----------------------------------------------------------
* keep the two % inputs complementary (must add to 100)
* --------------------------------------------------------- */
function handleChange(e) {
const { name, value } = e.target;
const pct = Math.max(0, Math.min(100, Number(value) || 0)); // clamp 0100
if (name === 'extraCashEmergencyPct') {
setExtraCashEmergencyPct(String(pct));
setExtraCashRetirementPct(String(100 - pct));
} else if (name === 'extraCashRetirementPct') {
setExtraCashRetirementPct(String(pct));
setExtraCashEmergencyPct(String(100 - pct));
} else {
// all other numeric fields:
// allow empty string so users can clear then retype
const update = valSetter => valSetter(value === '' ? '' : Number(value));
switch (name) {
case 'currentSalary': update(setCurrentSalary); break;
case 'additionalIncome': update(setAdditionalIncome); break;
case 'monthlyExpenses': update(setMonthlyExpenses); break;
case 'monthlyDebtPayments': update(setMonthlyDebtPayments); break;
case 'retirementSavings': update(setRetirementSavings); break;
case 'emergencyFund': update(setEmergencyFund); break;
case 'retirementContribution': update(setRetirementContribution); break;
case 'emergencyContribution': update(setEmergencyContribution); break;
default: break;
}
}
}
/* ───────────── submit ─────────────────────── */
async function handleSubmit(e) {
e.preventDefault();
const body = {
current_salary: parseFloat(currentSalary) || 0,
additional_income: parseFloat(additionalIncome) || 0,
monthly_expenses: parseFloat(monthlyExpenses) || 0,
monthly_debt_payments: parseFloat(monthlyDebtPayments) || 0,
retirement_savings: parseFloat(retirementSavings) || 0,
emergency_fund: parseFloat(emergencyFund) || 0,
retirement_contribution: parseFloat(retirementContribution) || 0,
emergency_contribution: parseFloat(emergencyContribution) || 0,
extra_cash_emergency_pct: pct(extraCashEmergencyPct),
extra_cash_retirement_pct: pct(extraCashRetirementPct)
};
try {
const res = await authFetch('/api/premium/financial-profile', {
method : 'POST',
headers: { 'Content-Type':'application/json' },
body : JSON.stringify(body)
});
if (!res.ok) throw new Error(await res.text());
alert('Financial profile saved.');
nav(-1);
} catch (err) {
console.error(err);
alert('Failed to save financial profile.');
}
}
/* ───────────── view ───────────────────────── */
return (
<>
<form
onSubmit={handleSubmit}
className="max-w-2xl mx-auto p-6 space-y-4 bg-white shadow rounded"
>
<h2 className="text-xl font-semibold">EditYourFinancialProfile</h2>
{/* salary / income */}
<label className="block font-medium">CurrentAnnualSalary</label>
<input type="number" className="w-full border rounded p-2"
name="currentSalary" value={currentSalary} onChange={handleChange} />
<label className="block font-medium">AdditionalAnnualIncome</label>
<input type="number" className="w-full border rounded p-2"
name="additionalIncome" value={additionalIncome} onChange={handleChange} />
{/* expenses with wizard */}
<label className="block font-medium">MonthlyLivingExpenses</label>
<div className="flex space-x-2 items-center">
<input type="number" className="w-full border rounded p-2"
value={monthlyExpenses}
onChange={e=>setMonthlyExpenses(e.target.value)} />
<Button className="bg-blue-600 text-white px-3 py-2 rounded"
type="button" onClick={openWizard}>
Need Help?
</Button>
</div>
{/* rest of the numeric fields */}
<label className="block font-medium">MonthlyDebtPayments</label>
<input type="number" className="w-full border rounded p-2"
name="monthlyDebtPayments" value={monthlyDebtPayments} onChange={handleChange} />
<label className="block font-medium">RetirementSavings</label>
<input type="number" className="w-full border rounded p-2"
name="retirementSavings" value={retirementSavings} onChange={handleChange} />
<label className="block font-medium">EmergencyFund</label>
<input type="number" className="w-full border rounded p-2"
name="emergencyFund" value={emergencyFund} onChange={handleChange} />
<label className="block font-medium">MonthlyRetirementContribution</label>
<input type="number" className="w-full border rounded p-2"
name="retirementContribution" value={retirementContribution} onChange={handleChange} />
<label className="block font-medium">MonthlyEmergencyContribution</label>
<input type="number" className="w-full border rounded p-2"
name="emergencyContribution"
value={emergencyContribution}
onChange={handleChange} />
{/* allocation kept in sync */}
<h3 className="text-lg font-medium pt-2">ExtraMonthlyCashAllocation (must total100%)</h3>
<label className="block font-medium">ToEmergencyFund(%)</label>
<input type="number" className="w-full border rounded p-2"
name="extraCashEmergencyPct"
value={extraCashEmergencyPct}
onChange={handleChange} />
<label className="block font-medium">ToRetirement(%)</label>
<input type="number" className="w-full border rounded p-2"
name="extraCashRetirementPct"
value={extraCashRetirementPct}
onChange={handleChange} />
{/* action buttons */}
<div className="pt-4 flex justify-between">
<button
type="button"
onClick={()=>nav(-1)}
className="bg-gray-200 hover:bg-gray-300 text-gray-700 font-semibold py-2 px-4 rounded"
>
Back
</button>
<button
type="submit"
className="bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded"
>
Save
</button>
</div>
</form>
{/* wizard modal */}
{showExpensesWizard && (
<Modal onClose={closeWizard}>
<ExpensesWizard
onClose={closeWizard}
onExpensesCalculated={total => {
setMonthlyExpenses(total);
closeWizard();
}}
/>
</Modal>
)}
</>
);
}