Correction de l'affichage en tant que mono détenteur

This commit is contained in:
2026-07-04 18:32:51 +02:00
parent 69d006d358
commit 11790c63fe
6 changed files with 109 additions and 37 deletions
+9 -9
View File
@@ -889,7 +889,7 @@ export default function DepotsRetraits() {
<EmptyState /> <EmptyState />
</> </>
); );
if (!loading && !modalOpen && allRows.length === 0) return ( if (!loading && !modalOpen && !corrModal.open && allRows.length === 0) return (
<> <>
<div className="topbar"><h2>Dépôts / Retraits</h2></div> <div className="topbar"><h2>Dépôts / Retraits</h2></div>
<EmptyState <EmptyState
@@ -1029,11 +1029,11 @@ export default function DepotsRetraits() {
) : ( ) : (
<table> <table>
<thead> <thead>
<tr><th>Plateforme</th><th>Détenteur</th><th className="num">Dépôts</th><th className="num">Retraits</th><th className="num">Diff. Dépôts vs Retraits</th><th className="num">Porte-monnaie</th><th className="num">Poids</th><th style={{ width: 28 }}></th></tr> <tr><th>Plateforme</th>{multiDetenteur && <th>Détenteur</th>}<th className="num">Dépôts</th><th className="num">Retraits</th><th className="num">Diff. Dépôts vs Retraits</th><th className="num">Porte-monnaie</th><th className="num">Poids</th><th style={{ width: 28 }}></th></tr>
</thead> </thead>
<tbody> <tbody>
{drPlatData.length === 0 && ( {drPlatData.length === 0 && (
<tr><td colSpan={8} className="text-muted" style={{ textAlign: 'center' }}>Aucune plateforme</td></tr> <tr><td colSpan={multiDetenteur ? 8 : 7} className="text-muted" style={{ textAlign: 'center' }}>Aucune plateforme</td></tr>
)} )}
{(() => { {(() => {
const totalSolde = drPlatTotals.solde_net; const totalSolde = drPlatTotals.solde_net;
@@ -1053,7 +1053,7 @@ export default function DepotsRetraits() {
<td> <td>
<span style={{ fontWeight: isActive ? 600 : undefined }}>{p.nom}</span> <span style={{ fontWeight: isActive ? 600 : undefined }}>{p.nom}</span>
</td> </td>
<td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{p.detenteur_nom || ''}</td> {multiDetenteur && <td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{p.detenteur_nom || ''}</td>}
<td className="num">{fmtEUR(p.depots)}</td> <td className="num">{fmtEUR(p.depots)}</td>
<td className="num">{fmtEUR(p.retraits)}</td> <td className="num">{fmtEUR(p.retraits)}</td>
<td className="num">{fmtEUR(p.solde_net)}</td> <td className="num">{fmtEUR(p.solde_net)}</td>
@@ -1080,7 +1080,7 @@ export default function DepotsRetraits() {
<tfoot> <tfoot>
<tr style={{ borderTop: '2px solid var(--border)', fontWeight: 700, background: 'var(--surface-2)' }}> <tr style={{ borderTop: '2px solid var(--border)', fontWeight: 700, background: 'var(--surface-2)' }}>
<td>Total — {drPlatData.length} plateformes</td> <td>Total — {drPlatData.length} plateformes</td>
<td /> {multiDetenteur && <td />}
<td className="num">{fmtEUR(drPlatTotals.depots)}</td> <td className="num">{fmtEUR(drPlatTotals.depots)}</td>
<td className="num">{fmtEUR(drPlatTotals.retraits)}</td> <td className="num">{fmtEUR(drPlatTotals.retraits)}</td>
<td className="num">{fmtEUR(drPlatTotals.solde_net)}</td> <td className="num">{fmtEUR(drPlatTotals.solde_net)}</td>
@@ -1236,12 +1236,12 @@ export default function DepotsRetraits() {
<table> <table>
<thead> <thead>
<tr> <tr>
<th>Date</th><th>Plateforme</th><th>Détenteur</th><th>Type de mouvement</th><th className="num">Montant</th><th style={{ width: 36 }}></th> <th>Date</th><th>Plateforme</th>{multiDetenteur && <th>Détenteur</th>}<th>Type de mouvement</th><th className="num">Montant</th><th style={{ width: 36 }}></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{tableRows.length === 0 && ( {tableRows.length === 0 && (
<tr><td colSpan={5} className="text-muted" style={{ textAlign: 'center', padding: 24 }}> <tr><td colSpan={multiDetenteur ? 6 : 5} className="text-muted" style={{ textAlign: 'center', padding: 24 }}>
{loading ? 'Chargement' : 'Aucun mouvement'} {loading ? 'Chargement' : 'Aucun mouvement'}
</td></tr> </td></tr>
)} )}
@@ -1255,7 +1255,7 @@ export default function DepotsRetraits() {
style={{ cursor: 'pointer' }}> style={{ cursor: 'pointer' }}>
<td>{fmtDate(r.date_operation)}</td> <td>{fmtDate(r.date_operation)}</td>
<td>{r.plateforme_nom}</td> <td>{r.plateforme_nom}</td>
<td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{r.plateforme_detenteur_nom || ''}</td> {multiDetenteur && <td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{r.plateforme_detenteur_nom || ''}</td>}
<td> <td>
<span style={{ <span style={{
display: 'inline-block', display: 'inline-block',
@@ -1284,7 +1284,7 @@ export default function DepotsRetraits() {
style={{ cursor: 'pointer', opacity: isAuto ? 0.8 : 1 }}> style={{ cursor: 'pointer', opacity: isAuto ? 0.8 : 1 }}>
<td>{fmtDate(r.date_operation)}</td> <td>{fmtDate(r.date_operation)}</td>
<td>{r.plateforme_nom}</td> <td>{r.plateforme_nom}</td>
<td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{r.plateforme_detenteur_nom || ''}</td> {multiDetenteur && <td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{r.plateforme_detenteur_nom || ''}</td>}
<td> <td>
<span className={`badge ${r.type}`}>{r.type}</span> <span className={`badge ${r.type}`}>{r.type}</span>
{isAuto && ( {isAuto && (
+7 -7
View File
@@ -962,7 +962,7 @@ export default function Investissements() {
<thead> <thead>
<tr> <tr>
<th>Plateforme</th> <th>Plateforme</th>
<th>Détenteur</th> {multiDetenteur && <th>Détenteur</th>}
<th className="num">Projets</th> <th className="num">Projets</th>
<th className="num">Montant investi</th> <th className="num">Montant investi</th>
<th className="num">Capital investi</th> <th className="num">Capital investi</th>
@@ -974,7 +974,7 @@ export default function Investissements() {
</thead> </thead>
<tbody> <tbody>
{platData.length === 0 && ( {platData.length === 0 && (
<tr><td colSpan={9} className="text-muted" style={{ textAlign: 'center', padding: 24 }}> <tr><td colSpan={multiDetenteur ? 9 : 8} className="text-muted" style={{ textAlign: 'center', padding: 24 }}>
Aucun investissement Aucun investissement
</td></tr> </td></tr>
)} )}
@@ -999,7 +999,7 @@ export default function Investissements() {
}}>!</span> }}>!</span>
)} )}
</td> </td>
<td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{p.detenteur_nom || '—'}</td> {multiDetenteur && <td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{p.detenteur_nom || '—'}</td>}
<td className="num">{p.count}</td> <td className="num">{p.count}</td>
<td className="num">{fmtEUR(p.investi)}</td> <td className="num">{fmtEUR(p.investi)}</td>
<td className="num">{fmtEUR(p.encours)}</td> <td className="num">{fmtEUR(p.encours)}</td>
@@ -1032,7 +1032,7 @@ export default function Investissements() {
<td style={{ padding: '10px 12px', color: 'var(--text-muted)', fontSize: 'var(--fs-sm)', fontWeight: 600 }}> <td style={{ padding: '10px 12px', color: 'var(--text-muted)', fontSize: 'var(--fs-sm)', fontWeight: 600 }}>
Total {platData.length} plateformes Total {platData.length} plateformes
</td> </td>
<td /> {multiDetenteur && <td />}
<td className="num" style={{ padding: '10px 12px' }}>{platTotals.count}</td> <td className="num" style={{ padding: '10px 12px' }}>{platTotals.count}</td>
<td className="num" style={{ padding: '10px 12px' }}>{fmtEUR(platTotals.investi)}</td> <td className="num" style={{ padding: '10px 12px' }}>{fmtEUR(platTotals.investi)}</td>
<td className="num" style={{ padding: '10px 12px' }}>{fmtEUR(platTotals.encours)}</td> <td className="num" style={{ padding: '10px 12px' }}>{fmtEUR(platTotals.encours)}</td>
@@ -1202,7 +1202,7 @@ export default function Investissements() {
<table style={{ cursor: 'pointer' }}> <table style={{ cursor: 'pointer' }}>
<thead> <thead>
<tr> <tr>
<th>Date</th><th>Projet</th><th>Plateforme</th><th>Détenteur</th> <th>Date</th><th>Projet</th><th>Plateforme</th>{multiDetenteur && <th>Détenteur</th>}
<th className="num">Montant</th><th className="num">Taux</th> <th className="num">Montant</th><th className="num">Taux</th>
<th className="num">Durée</th><th>Type remb.</th> <th className="num">Durée</th><th>Type remb.</th>
<th className="num">Inv. remboursés</th><th className="num">Cap. restant </th> <th className="num">Inv. remboursés</th><th className="num">Cap. restant </th>
@@ -1213,7 +1213,7 @@ export default function Investissements() {
</thead> </thead>
<tbody> <tbody>
{rows.length === 0 && ( {rows.length === 0 && (
<tr><td colSpan={13} className="text-muted" style={{ textAlign: 'center', padding: 24 }}> <tr><td colSpan={multiDetenteur ? 13 : 12} className="text-muted" style={{ textAlign: 'center', padding: 24 }}>
{loading ? 'Chargement…' : 'Aucun investissement'} {loading ? 'Chargement…' : 'Aucun investissement'}
</td></tr> </td></tr>
)} )}
@@ -1233,7 +1233,7 @@ export default function Investissements() {
{r.emetteur && <div className="text-muted" style={{ fontSize: 11 }}>{r.emetteur}</div>} {r.emetteur && <div className="text-muted" style={{ fontSize: 11 }}>{r.emetteur}</div>}
</td> </td>
<td>{r.plateforme_nom}</td> <td>{r.plateforme_nom}</td>
<td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{r.plateforme_detenteur_nom || '—'}</td> {multiDetenteur && <td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{r.plateforme_detenteur_nom || '—'}</td>}
<td className="num">{fmtEUR(capInv)}</td> <td className="num">{fmtEUR(capInv)}</td>
<td className="num">{r.taux_interet != null ? fmtPct(r.taux_interet) : '—'}</td> <td className="num">{r.taux_interet != null ? fmtPct(r.taux_interet) : '—'}</td>
<td className="num">{r.duree_mois ? `${r.duree_mois} m.` : '—'}</td> <td className="num">{r.duree_mois ? `${r.duree_mois} m.` : '—'}</td>
+5 -2
View File
@@ -424,6 +424,9 @@ export default function Plateformes() {
]; ];
}, [platRows, investisseurs]); }, [platRows, investisseurs]);
/* ── Vrai/faux multi-détenteur global (indépendant de la plateforme sélectionnée) ── */
const multiDetenteur = new Set(plats.map(p => p.investisseur_id)).size > 1;
/* ── Reset détenteur quand plateforme change (skip restauration initiale) ── */ /* ── Reset détenteur quand plateforme change (skip restauration initiale) ── */
const isFirstPlatChange = useRef(true); const isFirstPlatChange = useRef(true);
useEffect(() => { useEffect(() => {
@@ -1534,7 +1537,7 @@ export default function Plateformes() {
<thead> <thead>
<tr> <tr>
<th>Projet</th> <th>Projet</th>
<th>Détenteur</th> {multiDetenteur && <th>Détenteur</th>}
<th className="num">Date de souscription</th> <th className="num">Date de souscription</th>
<th className="num">Date cible</th> <th className="num">Date cible</th>
<th className="num">Montant</th> <th className="num">Montant</th>
@@ -1555,7 +1558,7 @@ export default function Plateformes() {
<tr key={r.id} style={{ cursor: 'pointer' }} <tr key={r.id} style={{ cursor: 'pointer' }}
onClick={() => navigate(`/investissements/${r.id}`, { state: { from: { path: location.pathname, search: location.search, label: 'Plateformes' } } })}> onClick={() => navigate(`/investissements/${r.id}`, { state: { from: { path: location.pathname, search: location.search, label: 'Plateformes' } } })}>
<td style={{ fontWeight: 500 }}>{r.nom_projet}</td> <td style={{ fontWeight: 500 }}>{r.nom_projet}</td>
<td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{(() => { const inv = investisseurs.find(i => i.id === r.investisseur_id); return inv ? memberLabel(inv) : '—'; })()}</td> {multiDetenteur && <td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{(() => { const inv = investisseurs.find(i => i.id === r.investisseur_id); return inv ? memberLabel(inv) : '—'; })()}</td>}
<td className="num" style={{ fontSize: 'var(--fs-sm)', color: 'var(--text-muted)' }}>{fmtDate(r.date_souscription)}</td> <td className="num" style={{ fontSize: 'var(--fs-sm)', color: 'var(--text-muted)' }}>{fmtDate(r.date_souscription)}</td>
<td className="num" style={{ fontSize: 'var(--fs-sm)', color: 'var(--text-muted)' }}>{fmtDate(r.date_cible)}</td> <td className="num" style={{ fontSize: 'var(--fs-sm)', color: 'var(--text-muted)' }}>{fmtDate(r.date_cible)}</td>
<td className="num">{fmtEUR(r.montant_investi)}</td> <td className="num">{fmtEUR(r.montant_investi)}</td>
+11 -9
View File
@@ -899,7 +899,7 @@ export default function Remboursements() {
<thead> <thead>
<tr> <tr>
<th>Plateforme</th> <th>Plateforme</th>
<th>Détenteur</th> {multiDetenteur && <th>Détenteur</th>}
<th className="num">Capital remboursé</th> <th className="num">Capital remboursé</th>
<th className="num">Cashback</th> <th className="num">Cashback</th>
<th className="num">Intérêts ({netMode ? 'Net' : 'Brut'})</th> <th className="num">Intérêts ({netMode ? 'Net' : 'Brut'})</th>
@@ -911,7 +911,7 @@ export default function Remboursements() {
</thead> </thead>
<tbody> <tbody>
{byPlatform.length === 0 && ( {byPlatform.length === 0 && (
<tr><td colSpan={9} className="text-muted" style={{ textAlign: 'center', padding: 24 }}> <tr><td colSpan={multiDetenteur ? 9 : 8} className="text-muted" style={{ textAlign: 'center', padding: 24 }}>
{loading ? 'Chargement…' : 'Aucun remboursement'} {loading ? 'Chargement…' : 'Aucun remboursement'}
</td></tr> </td></tr>
)} )}
@@ -927,7 +927,7 @@ export default function Remboursements() {
title={isActive ? 'Cliquer pour retirer le filtre' : 'Cliquer pour filtrer sur cette plateforme'} title={isActive ? 'Cliquer pour retirer le filtre' : 'Cliquer pour filtrer sur cette plateforme'}
> >
<td><span style={{ fontWeight: isActive ? 600 : undefined }}>{p.nom}</span></td> <td><span style={{ fontWeight: isActive ? 600 : undefined }}>{p.nom}</span></td>
<td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{p.detenteur_nom || '—'}</td> {multiDetenteur && <td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{p.detenteur_nom || '—'}</td>}
<td className="num">{fmtEUR(p.capital)}</td> <td className="num">{fmtEUR(p.capital)}</td>
<td className="num">{p.cashback > 0 ? fmtEUR(p.cashback) : '—'}</td> <td className="num">{p.cashback > 0 ? fmtEUR(p.cashback) : '—'}</td>
<td className="num">{fmtEUR(val)}</td> <td className="num">{fmtEUR(val)}</td>
@@ -954,7 +954,7 @@ export default function Remboursements() {
<tfoot> <tfoot>
<tr style={{ borderTop: '2px solid var(--border)', fontWeight: 700, background: 'var(--surface-2)' }}> <tr style={{ borderTop: '2px solid var(--border)', fontWeight: 700, background: 'var(--surface-2)' }}>
<td>Total {byPlatform.length} plateformes</td> <td>Total {byPlatform.length} plateformes</td>
<td /> {multiDetenteur && <td />}
<td className="num">{fmtEUR(byPlatformTotals.capital)}</td> <td className="num">{fmtEUR(byPlatformTotals.capital)}</td>
<td className="num">{byPlatformTotals.cashback > 0 ? fmtEUR(byPlatformTotals.cashback) : '—'}</td> <td className="num">{byPlatformTotals.cashback > 0 ? fmtEUR(byPlatformTotals.cashback) : '—'}</td>
<td className="num">{fmtEUR(netMode ? byPlatformTotals.interets_nets : byPlatformTotals.interets_bruts)}</td> <td className="num">{fmtEUR(netMode ? byPlatformTotals.interets_nets : byPlatformTotals.interets_bruts)}</td>
@@ -1131,7 +1131,7 @@ export default function Remboursements() {
<table> <table>
<thead> <thead>
<tr> <tr>
<th>Date</th><th>Plateforme</th><th>Détenteur</th><th>Nom du projet</th><th>Versement</th> <th>Date</th><th>Plateforme</th>{multiDetenteur && <th>Détenteur</th>}<th>Nom du projet</th><th>Versement</th>
<th className="num">Capital Remboursé</th><th className="num">Capital Restant </th> <th className="num">Capital Remboursé</th><th className="num">Capital Restant </th>
<th className="num">Cashback</th> <th className="num">Cashback</th>
<th className="num">Intérêts Bruts</th> <th className="num">Intérêts Bruts</th>
@@ -1143,7 +1143,7 @@ export default function Remboursements() {
</thead> </thead>
<tbody> <tbody>
{tableRows.length === 0 && ( {tableRows.length === 0 && (
<tr><td colSpan={14} className="text-muted" style={{ textAlign: 'center', padding: 24 }}> <tr><td colSpan={multiDetenteur ? 14 : 13} className="text-muted" style={{ textAlign: 'center', padding: 24 }}>
{loading ? 'Chargement…' : 'Aucun remboursement'} {loading ? 'Chargement…' : 'Aucun remboursement'}
</td></tr> </td></tr>
)} )}
@@ -1169,9 +1169,11 @@ export default function Remboursements() {
}}> }}>
<td>{fmtDate(r.date_remb)}</td> <td>{fmtDate(r.date_remb)}</td>
<td>{r.plateforme_nom}</td> <td>{r.plateforme_nom}</td>
<td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}> {multiDetenteur && (
{isCorrection ? (r.investisseur_nom || '—') : (r.plateforme_detenteur_nom || '—')} <td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>
</td> {isCorrection ? (r.investisseur_nom || '—') : (r.plateforme_detenteur_nom || '—')}
</td>
)}
<td> <td>
{isCorrection {isCorrection
? <span style={{ color: 'var(--text-muted)', fontStyle: 'italic' }}>{r.nom_projet}</span> ? <span style={{ color: 'var(--text-muted)', fontStyle: 'italic' }}>{r.nom_projet}</span>
@@ -132,6 +132,8 @@ export default function ComptesSection() {
}); });
}; };
const multiDetenteur = investisseurs.length > 1;
return ( return (
<> <>
<div className="card"> <div className="card">
@@ -154,7 +156,7 @@ export default function ComptesSection() {
<tr> <tr>
<th>Nom</th> <th>Nom</th>
<th style={{ width: '14%' }}>Type</th> <th style={{ width: '14%' }}>Type</th>
<th style={{ width: '22%' }}>Détenteur</th> {multiDetenteur && <th style={{ width: '22%' }}>Détenteur</th>}
<th style={{ width: '16%' }}>Banque</th> <th style={{ width: '16%' }}>Banque</th>
<th style={{ width: '10%' }}>Exonération</th> <th style={{ width: '10%' }}>Exonération</th>
<th style={{ width: 80 }} /> <th style={{ width: 80 }} />
@@ -162,7 +164,7 @@ export default function ComptesSection() {
</thead> </thead>
<tbody> <tbody>
{comptes.length === 0 && ( {comptes.length === 0 && (
<tr><td colSpan={6} className="text-muted" style={{ textAlign: 'center', fontStyle: 'italic' }}> <tr><td colSpan={multiDetenteur ? 6 : 5} className="text-muted" style={{ textAlign: 'center', fontStyle: 'italic' }}>
Aucun compte défini. Aucun compte défini.
</td></tr> </td></tr>
)} )}
@@ -172,7 +174,7 @@ export default function ComptesSection() {
<tr key={c.id}> <tr key={c.id}>
<td style={{ fontWeight: 500 }}>{c.nom}</td> <td style={{ fontWeight: 500 }}>{c.nom}</td>
<td><span className="badge">{TYPE_COMPTE_LABELS[c.type] ?? c.type}</span></td> <td><span className="badge">{TYPE_COMPTE_LABELS[c.type] ?? c.type}</span></td>
<td>{inv ? memberLabel(inv) : <span className="text-muted"></span>}</td> {multiDetenteur && <td>{inv ? memberLabel(inv) : <span className="text-muted"></span>}</td>}
<td>{c.banque || <span className="text-muted"></span>}</td> <td>{c.banque || <span className="text-muted"></span>}</td>
<td> <td>
{c.exoneration_fiscale && c.exoneration_fiscale !== 'aucune' ? ( {c.exoneration_fiscale && c.exoneration_fiscale !== 'aucune' ? (
@@ -17,6 +17,12 @@ function dlBlob(content, filename, type) {
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
} }
/** Date du jour au format YYYY-MM-DD, en heure locale (ne pas utiliser toISOString ici, décale d'un jour selon le fuseau). */
function localToday() {
const d = new Date();
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
}
const countryLabel = code => COUNTRIES.find(c => c.code === code)?.name ?? code ?? '—'; const countryLabel = code => COUNTRIES.find(c => c.code === code)?.name ?? code ?? '—';
const FISCALITE_LABELS = { const FISCALITE_LABELS = {
flat_tax: 'Flat Tax', flat_tax: 'Flat Tax',
@@ -283,6 +289,13 @@ function PlatDetailPanel({ plat, onEdit }) {
function PlatPickerModal({ open, onClose, referentiel, onSelect, onManual, err }) { function PlatPickerModal({ open, onClose, referentiel, onSelect, onManual, err }) {
const [search, setSearch] = useState(''); const [search, setSearch] = useState('');
const [domFilter, setDomFilter] = useState('all'); // 'all' | 'fr' | 'etr' const [domFilter, setDomFilter] = useState('all'); // 'all' | 'fr' | 'etr'
const [confirming, setConfirming] = useState(null); // ref en cours de confirmation
const [dateOuverture, setDateOuverture] = useState('');
useEffect(() => {
if (open) { setConfirming(null); setDateOuverture(''); }
}, [open]);
if (!open) return null; if (!open) return null;
const q = search.trim().toLowerCase(); const q = search.trim().toLowerCase();
@@ -298,6 +311,53 @@ function PlatPickerModal({ open, onClose, referentiel, onSelect, onManual, err }
return f ? (LOGO_BASE + f) : null; return f ? (LOGO_BASE + f) : null;
}; };
/* ── Étape de confirmation : date d'ouverture du compte ── */
if (confirming) {
const img = imgFor(confirming);
return (
<Modal
open={open}
title="Ajouter une plateforme"
onClose={onClose}
width={480}
footer={
<div style={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}>
<button type="button" className="ghost" onClick={() => setConfirming(null)}> Retour</button>
<button type="button" className="primary" onClick={() => onSelect(confirming, dateOuverture)}>
Ajouter
</button>
</div>
}
>
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
{err && <div className="error">{err}</div>}
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
{img
? <img src={img} alt={confirming.nom} style={{ width: 44, height: 44, objectFit: 'contain', borderRadius: 8, flexShrink: 0 }} />
: <div style={{ width: 44, height: 44, borderRadius: 8, flexShrink: 0, background: 'var(--surface-2)', border: '1px solid var(--border)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 18, color: 'var(--text-muted)' }}>🏦</div>
}
<div style={{ fontWeight: 600, fontSize: 'var(--fs-md)' }}>{confirming.nom}</div>
</div>
<p style={{ margin: 0, color: 'var(--text-muted)', fontSize: 'var(--fs-sm)', lineHeight: 1.5 }}>
Pouvez-vous SVP préciser la date d'ouverture de votre compte sur cette plateforme ?
</p>
<div>
<label style={{ display: 'block', fontWeight: 600, marginBottom: 4, fontSize: 'var(--fs-sm)' }}>
Date d'ouverture du compte
</label>
<input
type="date"
autoFocus
value={dateOuverture}
onChange={e => setDateOuverture(e.target.value)}
style={{ width: '100%', boxSizing: 'border-box' }}
/>
</div>
</div>
</Modal>
);
}
return ( return (
<Modal <Modal
open={open} open={open}
@@ -372,7 +432,7 @@ function PlatPickerModal({ open, onClose, referentiel, onSelect, onManual, err }
<button <button
key={r.id} key={r.id}
type="button" type="button"
onClick={() => onSelect(r)} onClick={() => { setConfirming(r); setDateOuverture(localToday()); }}
style={{ style={{
display: 'flex', flexDirection: 'column', alignItems: 'center', display: 'flex', flexDirection: 'column', alignItems: 'center',
gap: 8, padding: '16px 10px 12px', gap: 8, padding: '16px 10px 12px',
@@ -779,7 +839,7 @@ export default function PlateformesSection() {
}; };
/* ── Ajouter depuis le référentiel ──────────────────────────── */ /* ── Ajouter depuis le référentiel ──────────────────────────── */
const addPlatFromRef = async (ref) => { const addPlatFromRef = async (ref, dateOuverture) => {
try { try {
await api.post('/plateformes', { await api.post('/plateformes', {
nom: ref.nom, nom: ref.nom,
@@ -792,6 +852,7 @@ export default function PlateformesSection() {
type_pret_defaut: ref.type_pret_defaut || null, type_pret_defaut: ref.type_pret_defaut || null,
freq_interets_defaut: ref.freq_interets_defaut || null, freq_interets_defaut: ref.freq_interets_defaut || null,
referentiel_id: ref.id, referentiel_id: ref.id,
date_ouverture: dateOuverture || null,
}); });
setShowAddPicker(false); setShowAddPicker(false);
await load(); await load();
@@ -942,6 +1003,8 @@ export default function PlateformesSection() {
} catch (e) { setErr(e.message); } } catch (e) { setErr(e.message); }
}; };
const multiDetenteur = new Set(plats.map(p => p.investisseur_id)).size > 1;
/* ── PFU CRUD ────────────────────────────────────────────────── */ /* ── PFU CRUD ────────────────────────────────────────────────── */
@@ -1000,14 +1063,14 @@ export default function PlateformesSection() {
<th style={{ padding: '10px 8px', textAlign: 'left' }}>Domiciliation</th> <th style={{ padding: '10px 8px', textAlign: 'left' }}>Domiciliation</th>
<th style={{ padding: '10px 8px', textAlign: 'left' }}>Catégories</th> <th style={{ padding: '10px 8px', textAlign: 'left' }}>Catégories</th>
<th style={{ padding: '10px 8px', textAlign: 'left' }}>Secteurs</th> <th style={{ padding: '10px 8px', textAlign: 'left' }}>Secteurs</th>
<th style={{ padding: '10px 8px', textAlign: 'left' }}>Détenteur</th> {multiDetenteur && <th style={{ padding: '10px 8px', textAlign: 'left' }}>Détenteur</th>}
<th style={{ padding: '10px 16px', textAlign: 'center', width: 60 }}>Invest.</th> <th style={{ padding: '10px 16px', textAlign: 'center', width: 60 }}>Invest.</th>
<th style={{ padding: '10px 8px', width: 40 }}></th> <th style={{ padding: '10px 8px', width: 40 }}></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{plats.length === 0 && ( {plats.length === 0 && (
<tr><td colSpan={7} className="text-muted" style={{ textAlign: 'center', padding: 24 }}>Aucune plateforme</td></tr> <tr><td colSpan={multiDetenteur ? 8 : 7} className="text-muted" style={{ textAlign: 'center', padding: 24 }}>Aucune plateforme</td></tr>
)} )}
{plats.map((p, i) => { {plats.map((p, i) => {
const imgSrc = p.icone_filename ? logoUrl(p.icone_filename) : p.logo_filename ? logoUrl(p.logo_filename) : null; const imgSrc = p.icone_filename ? logoUrl(p.icone_filename) : p.logo_filename ? logoUrl(p.logo_filename) : null;
@@ -1054,9 +1117,11 @@ export default function PlateformesSection() {
{sects.length === 0 && <span style={{ fontSize: 11, color: 'var(--text-muted)' }}></span>} {sects.length === 0 && <span style={{ fontSize: 11, color: 'var(--text-muted)' }}></span>}
</div> </div>
</td> </td>
<td style={{ padding: '10px 8px', fontSize: 12, color: 'var(--text-muted)' }}> {multiDetenteur && (
{inv ? memberLabel(inv) : ''} <td style={{ padding: '10px 8px', fontSize: 12, color: 'var(--text-muted)' }}>
</td> {inv ? memberLabel(inv) : '—'}
</td>
)}
<td style={{ padding: '10px 16px', textAlign: 'center', fontWeight: 600, <td style={{ padding: '10px 16px', textAlign: 'center', fontWeight: 600,
color: (p.nb_investissements ?? 0) > 0 ? 'var(--text)' : 'var(--text-muted)' }}> color: (p.nb_investissements ?? 0) > 0 ? 'var(--text)' : 'var(--text-muted)' }}>
{p.nb_investissements ?? 0} {p.nb_investissements ?? 0}