diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx index 13397d4..6d40929 100644 --- a/frontend/src/pages/Dashboard.jsx +++ b/frontend/src/pages/Dashboard.jsx @@ -156,10 +156,11 @@ function TrendBadge({ current, prev, invert = false }) { } /* ── Carte KPI individuelle ── */ -function KpiCard({ title, value, badge, refValue, onClick }) { +function KpiCard({ title, value, badge, refValue, onClick, hint }) { return (
{ e.currentTarget.style.boxShadow = '0 0 0 2px var(--primary)'; e.currentTarget.style.opacity = '0.88'; } : undefined} @@ -178,7 +179,7 @@ function KpiCard({ title, value, badge, refValue, onClick }) { } /* ── KPI filtrés par année ── */ -function DashboardKpis({ portfolio, netMode, pfuRates, capitalMensuelData }) { +function DashboardKpis({ portfolio, netMode, pfuRates, capitalMensuelData, activeId, activeView, soldePortefeuilleLive }) { const { annee, modeGlobal, rawDataGlobal, rawData, currentYear, currentMonth, setInclureInterets, setInclureCapital, setInclureCashback, @@ -187,6 +188,31 @@ function DashboardKpis({ portfolio, netMode, pfuRates, capitalMensuelData }) { const isCurrentYear = !modeGlobal && Number(annee) === currentYear; const isFutureYear = !modeGlobal && Number(annee) > currentYear; + // ── Porte-monnaie — même snapshot backend que DepotsRetraits/Plateformes + // (GET /dashboard[?asOf=]), formule unique cf. mémoire + // project_taxreport_interets_plateforme. "Depuis le début" → valeur live + // déjà chargée par le parent ; année précise → snapshot fin d'année. + const [walletAsOf, setWalletAsOf] = useState({}); + useEffect(() => { + if (modeGlobal) return; + if (!activeId && activeView !== 'all') return; + const scopeParams = activeView === 'all' ? { scope: 'all' } : undefined; + const prevYear = Number(annee) - 1; + const keys = [`${annee}-12-31`, `${prevYear}-12-31`]; + Promise.all(keys.map(asOf => + api.get('/dashboard', { ...scopeParams, asOf }).then(d => [asOf, d]) + )).then(entries => { + setWalletAsOf(prev => ({ ...prev, ...Object.fromEntries(entries) })); + }).catch(e => console.error('walletAsOf load error:', e)); + }, [annee, modeGlobal, activeId, activeView]); // eslint-disable-line + + const soldePortefeuilleValue = modeGlobal + ? soldePortefeuilleLive + : (walletAsOf[`${annee}-12-31`]?.solde_portefeuille_total ?? null); + const soldePortefeuillePrev = modeGlobal + ? null + : (walletAsOf[`${Number(annee) - 1}-12-31`]?.solde_portefeuille_total ?? null); + // Capital de référence pour l'année sélectionnée // Pour les années passées : dernier mois de capitalMensuel (= capital déployé fin décembre) // Pour l'année courante / mode global : capital actuellement déployé (portfolio) @@ -380,6 +406,17 @@ function DashboardKpis({ portfolio, netMode, pfuRates, capitalMensuelData }) { ? fmtEUR(enDefautPrev) + ' en ' + prevMonthLabel : null} /> + + : null} + refValue={!modeGlobal && soldePortefeuillePrev != null + ? fmtEUR(soldePortefeuillePrev) + ' en ' + (Number(annee) - 1) + : null} + /> - +
diff --git a/frontend/src/pages/DepotsRetraits.jsx b/frontend/src/pages/DepotsRetraits.jsx index 3ab9980..c5494af 100644 --- a/frontend/src/pages/DepotsRetraits.jsx +++ b/frontend/src/pages/DepotsRetraits.jsx @@ -467,8 +467,14 @@ export default function DepotsRetraits() { const years = [...new Set(allRows.map(r => r.date_operation.slice(0,4)))].sort().reverse(); /* Données tableau Plateformes — filtrées par drPlatYear si actif */ + // Tri unique (alphabétique, détenteur en second critère) appliqué aux deux + // branches de drPlatData — évite que l'ordre des plateformes change selon le + // filtre année sélectionné. + const sortPlatAlpha = (a, b) => + a.nom.localeCompare(b.nom, 'fr') || (a.detenteur_nom ?? '').localeCompare(b.detenteur_nom ?? '', 'fr'); + const drPlatData = useMemo(() => { - if (!drPlatYear) return dashData?.cashByPlatform ?? []; + if (!drPlatYear) return [...(dashData?.cashByPlatform ?? [])].sort(sortPlatAlpha); // Mouvements de l'année sélectionnée (dépôts/retraits de l'année) const src = allRows.filter(r => r.date_operation.slice(0, 4) === drPlatYear); @@ -480,6 +486,22 @@ export default function DepotsRetraits() { else map[key].retraits += r.montant; } + // Une plateforme sans dépôt/retrait cette année-là (ex. uniquement des + // remboursements ou un réinvestissement) doit quand même apparaître dans le + // tableau — sinon elle disparaît alors qu'elle a eu de vrais mouvements. + for (const r of allRemb) { + if (!r.date_remb || r.date_remb.slice(0, 4) !== drPlatYear || !r.plateforme_id) continue; + const key = String(r.plateforme_id); + if (!map[key]) map[key] = { plateforme_id: r.plateforme_id, nom: r.plateforme_nom, detenteur_nom: r.plateforme_detenteur_nom || null, depots: 0, retraits: 0, solde_net: 0, solde_portefeuille: 0 }; + } + for (const rv of allReinvests) { + if (!rv.date_reinvestissement || rv.date_reinvestissement.slice(0, 4) !== drPlatYear) continue; + const inv = allInv.find(i => i.id === rv.investissement_id); + if (!inv?.plateforme_id) continue; + const key = String(inv.plateforme_id); + if (!map[key]) map[key] = { plateforme_id: inv.plateforme_id, nom: inv.plateforme_nom, detenteur_nom: inv.plateforme_detenteur_nom || null, depots: 0, retraits: 0, solde_net: 0, solde_portefeuille: 0 }; + } + // Solde porte-monnaie = cumul jusqu'au 31/12 de l'année sélectionnée, calculé // côté backend (GET /dashboard?asOf=) pour rester la même formule que le solde // "live" — bonus, intérêts plateforme et corrections de solde inclus. @@ -492,8 +514,8 @@ export default function DepotsRetraits() { p.solde_net = p.depots - p.retraits; p.solde_portefeuille = soldeMap[String(p.plateforme_id)] ?? 0; return p; - }).sort((a, b) => b.solde_net - a.solde_net); - }, [allRows, dashDataAsOf, drPlatYear]); + }).sort(sortPlatAlpha); + }, [allRows, allRemb, allInv, allReinvests, dashDataAsOf, drPlatYear]); const drPlatTotals = useMemo(() => drPlatData.reduce((acc, p) => {