Initial commit
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { api } from '../api.js';
|
||||
import { useInvestisseur } from '../context/InvestisseurContext.jsx';
|
||||
import { fmtEUR, fmtDate } from '../utils/format.js';
|
||||
|
||||
export default function SimulRemboursements() {
|
||||
const { activeId } = useInvestisseur();
|
||||
const [investissements, setInvestissements] = useState([]);
|
||||
const [selected, setSelected] = useState('');
|
||||
const [echeances, setEcheances] = useState([]);
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [msg, setMsg] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeId) return;
|
||||
api.get('/investissements').then(setInvestissements);
|
||||
}, [activeId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selected) { setEcheances([]); return; }
|
||||
api.get('/simul', { investissement_id: selected }).then(setEcheances);
|
||||
}, [selected]);
|
||||
|
||||
const generate = async () => {
|
||||
if (!selected) return;
|
||||
setBusy(true); setMsg(null);
|
||||
try {
|
||||
const r = await api.post('/simul/generate', {
|
||||
investissement_id: Number(selected),
|
||||
replace: true,
|
||||
});
|
||||
setMsg(`✔ ${r.inserted} échéances générées.`);
|
||||
const e = await api.get('/simul', { investissement_id: selected });
|
||||
setEcheances(e);
|
||||
} catch (e) {
|
||||
setMsg(`✗ ${e.message}`);
|
||||
} finally { setBusy(false); }
|
||||
};
|
||||
|
||||
const inv = investissements.find(i => i.id === Number(selected));
|
||||
const totals = echeances.reduce((acc, e) => {
|
||||
acc.capital += e.capital_prevu;
|
||||
acc.interets += e.interets_prevus;
|
||||
acc.total += e.total_prevu;
|
||||
return acc;
|
||||
}, { capital: 0, interets: 0, total: 0 });
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="topbar"><h2>Projections Remboursements</h2></div>
|
||||
|
||||
<div className="card">
|
||||
<div className="row">
|
||||
<div style={{ flex: 2 }}>
|
||||
<label>Investissement</label>
|
||||
<select value={selected} onChange={e => setSelected(e.target.value)}>
|
||||
<option value="">— Choisir —</option>
|
||||
{investissements.map(i =>
|
||||
<option key={i.id} value={i.id}>
|
||||
{i.nom_projet} ({fmtEUR(i.montant_investi)} – {i.taux_interet ?? '?'}% – {i.duree_mois ?? '?'}m – {i.type_remb || '?'})
|
||||
</option>
|
||||
)}
|
||||
</select>
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<button className="primary" onClick={generate} disabled={!selected || busy} style={{ width: '100%' }}>
|
||||
{busy ? '…' : 'Générer / Régénérer'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{msg && <div className={msg.startsWith('✔') ? 'success-msg' : 'error'} style={{ marginTop: 12 }}>{msg}</div>}
|
||||
{inv && (!inv.taux_interet || !inv.duree_mois) && (
|
||||
<div className="error" style={{ marginTop: 12 }}>
|
||||
Cet investissement n'a pas de <strong>taux</strong> et/ou de <strong>durée</strong>. Renseignez-les dans la fiche.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{echeances.length > 0 && (
|
||||
<div className="card">
|
||||
<div className="kpi-grid" style={{ marginBottom: 12 }}>
|
||||
<div className="kpi"><div className="label">Capital prévu</div><div className="value">{fmtEUR(totals.capital)}</div></div>
|
||||
<div className="kpi"><div className="label">Intérêts prévus</div><div className="value success">{fmtEUR(totals.interets)}</div></div>
|
||||
<div className="kpi"><div className="label">Total prévu</div><div className="value">{fmtEUR(totals.total)}</div></div>
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th><th>Date prévue</th>
|
||||
<th className="num">Capital</th><th className="num">Intérêts</th>
|
||||
<th className="num">Total échéance</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{echeances.map(e => (
|
||||
<tr key={e.id}>
|
||||
<td>{e.numero_echeance}</td>
|
||||
<td>{fmtDate(e.date_prevue)}</td>
|
||||
<td className="num">{fmtEUR(e.capital_prevu)}</td>
|
||||
<td className="num">{fmtEUR(e.interets_prevus)}</td>
|
||||
<td className="num">{fmtEUR(e.total_prevu)}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user