From 6453218e2b255165c7e9bf1dd0dece461f6e34cb Mon Sep 17 00:00:00 2001 From: Olivier Date: Sat, 15 Aug 2026 11:32:36 +0200 Subject: [PATCH] selection des remboursements en masse --- frontend/src/pages/InvestissementDetail.jsx | 101 +++++++++++++------- 1 file changed, 65 insertions(+), 36 deletions(-) diff --git a/frontend/src/pages/InvestissementDetail.jsx b/frontend/src/pages/InvestissementDetail.jsx index 92f1654..5622fff 100644 --- a/frontend/src/pages/InvestissementDetail.jsx +++ b/frontend/src/pages/InvestissementDetail.jsx @@ -432,6 +432,7 @@ export default function InvestissementDetail() { const baseRates = brutsApresLocal; return { _simul: s, + selected: true, payload: { investissement_id: Number(id), date_remb: s.date_prevue, @@ -453,12 +454,21 @@ export default function InvestissementDetail() { setBulkRembModal(true); }; + const toggleBulkRembItem = (idx) => { + setBulkRembItems(items => items.map((it, i) => i === idx ? { ...it, selected: !it.selected } : it)); + }; + + const toggleBulkRembAll = (checked) => { + setBulkRembItems(items => items.map(it => ({ ...it, selected: checked }))); + }; + const runBulkRemb = async () => { + const toProcess = bulkRembItems.filter(it => it.selected); setBulkRembProcessing(true); setBulkRembProgress(0); try { - for (let i = 0; i < bulkRembItems.length; i++) { - await api.post('/remboursements', bulkRembItems[i].payload); + for (let i = 0; i < toProcess.length; i++) { + await api.post('/remboursements', toProcess[i].payload); setBulkRembProgress(i + 1); } setBulkRembDone(true); @@ -2368,61 +2378,80 @@ export default function InvestissementDetail() { title="Traitement en masse des remboursements" onClose={() => { if (!bulkRembProcessing) setBulkRembModal(false); }} width={560} - footer={ - bulkRembDone ? ( + footer={(() => { + const selectedCount = bulkRembItems.filter(it => it.selected).length; + return bulkRembDone ? (
) : (
-
- ) - } + ); + })()} > {bulkRembDone ? (
- {bulkRembItems.length} remboursement{bulkRembItems.length > 1 ? 's' : ''} enregistré{bulkRembItems.length > 1 ? 's' : ''} avec succès. + {bulkRembItems.filter(it => it.selected).length} remboursement{bulkRembItems.filter(it => it.selected).length > 1 ? 's' : ''} enregistré{bulkRembItems.filter(it => it.selected).length > 1 ? 's' : ''} avec succès.
) : bulkRembItems.length === 0 ? (

Aucune échéance passée non enregistrée trouvée.

- ) : ( -
-

- {bulkRembItems.length} échéance{bulkRembItems.length > 1 ? 's' : ''} passée{bulkRembItems.length > 1 ? 's' : ''} sans remboursement enregistré. Chaque entrée sera créée avec les montants projetés. -

-
- - - - - - - - - - - {bulkRembItems.map((item, idx) => ( - - - - - + ) : (() => { + const selectedCount = bulkRembItems.filter(it => it.selected).length; + const allSelected = selectedCount === bulkRembItems.length; + return ( +
+

+ {bulkRembItems.length} échéance{bulkRembItems.length > 1 ? 's' : ''} passée{bulkRembItems.length > 1 ? 's' : ''} sans remboursement enregistré. Décochez celles à exclure — chaque entrée cochée sera créée avec les montants projetés. +

+
+
DateCapitalIntérêts brutsTotal
{fmtDate(item.payload.date_remb)}{fmtEUR(item.payload.capital)}{fmtEUR(item.payload.interets_bruts)}{fmtEUR(item.payload.capital + item.payload.interets_bruts)}
+ + + + + + + - ))} - -
+ toggleBulkRembAll(e.target.checked)} /> + DateCapitalIntérêts brutsTotal
+ + + {bulkRembItems.map((item, idx) => ( + toggleBulkRembItem(idx)} + style={{ + borderTop: '1px solid var(--border)', cursor: 'pointer', + opacity: item.selected ? 1 : 0.45, + }}> + + e.stopPropagation()} + onChange={() => toggleBulkRembItem(idx)} /> + + {fmtDate(item.payload.date_remb)} + {fmtEUR(item.payload.capital)} + {fmtEUR(item.payload.interets_bruts)} + {fmtEUR(item.payload.capital + item.payload.interets_bruts)} + + ))} + + +
- - )} + ); + })()} {simulMenu && (