---
name: project-interets-mensuels-chart
description: InteretsMensuelsChart + InteretsDonutChart + InteretsChartContext — barres empilées + donut + mode TOUT (vue annuelle)
metadata: 
  node_type: memory
  type: project
  originSessionId: 9d8a4dc8-0caa-4a53-bf00-040dfe2afe4a
---

## Architecture (3 fichiers)

- `frontend/src/context/InteretsChartContext.jsx` — état partagé entre les deux graphiques
- `frontend/src/components/InteretsMensuelsChart.jsx` — graphique barres empilées
- `frontend/src/components/InteretsDonutChart.jsx` — donut de répartition

Utilisés dans `Dashboard.jsx` via `<InteretsChartProvider>` wrapper, layout flex 2/3 + 1/3 avec `alignItems: 'stretch'`.

## InteretsChartContext

Centralise : `annee`, `inclureInterets/Capital/Cashback`, `selectedMonth`, `rawData`, `months` (useMemo), `annualTotal`, `availableYears`, couleurs UiContext (`chartInterets/Capital/Cashback`).

Reçoit en props depuis Dashboard : `netMode`, `pfuRates`, `activeView`, `activeId`.

**Deux fetches :**
- `GET /api/dashboard/interets-mensuels?annee=&scope=` → `rawData` — retourne `{ rembourses, projections, annees }`. LEFT JOINs pour bonus (`bonus_investisseur_id`).
- `GET /api/dashboard/interets-annuels?scope=` → `rawDataGlobal` — retourne `{ rembourses, projections, annees }` groupés par année. Projections filtrées `>= date('now', 'start of month')`.

**Mode global (TOUT) :**
- `modeGlobal` (bool, false par défaut) + `toggleModeGlobal`
- `years` useMemo avec fenêtrage : max 8 années passées, année courante ≤ position 9, toujours ≥ 3 années projetées, max 12 barres
- `selectedYear` (number | null) + `setSelectedYear`
- `globalTotal` = somme de tous les `years[].total`

Algorithme fenêtrage `years` :
```js
pastYears   = allSorted.filter(y < currentYear)
futureYears = allSorted.filter(y > currentYear)
pastCount   = min(pastYears.length, 8)
pastSlice   = pastYears.slice(-pastCount)
usedSlots   = pastCount + 1
projCount   = max(3, 12 - usedSlots)
futureSlice = futureYears.slice(0, projCount)
visibleYears = [...pastSlice, currentYear, ...futureSlice]
```

Chaque objet `year` dans `years[]` a les mêmes champs qu'un `month` (capitalAmt, cashbackAmt, interetsAmt, capitalProjAmt, interetsProjAmt, actual, projected, total, isCurrent, isFuture, label).

## InteretsMensuelsChart — barres empilées

- 3 toggles avec icônes de la bibliothèque (`GET /api/icons`, `<img src={ICONS_BASE+filename}>`, opacity active/inactive)
- `ICONS_BASE = '/api/icons-files/'` ; noms : `interets`, `capital`, `cashback`
- Segments empilés : Capital → Cashback → Intérêts (bottom to top)
- Couche `actual` (opaque) + `projected` (`hexToRgba(color, 0.28)`)
- Sélecteur année : fenêtre glissante ±1 an, `windowStart` state local
- **Bouton TOUT** : après les chevrons années, style `.solde-range-btn / .active` ; en mode TOUT les chevrons et le sélecteur d'années sont masqués
- En mode TOUT : `items = years`, `barCount = years.length`, gap=10, labels = années, clic → `setSelectedYear(toggle)`
- En mode mensuel : `items = months`, `barCount = 12`, gap=8, labels = mois, clic → `setSelectedMonth(toggle)`
- `filteredTotal(item)` unifié pour les deux modes → `rawMax`, `totalH`, label barre, grand total en-tête
- Légende bas : swatches reçu/projeté + sélecteur Reçu/Projeté

## InteretsDonutChart — donut de répartition

- Asservi à `InteretsChartContext` : mêmes toggles + `selectedMonth` (mode mensuel) ou `selectedYear` (mode TOUT)
- Mode mensuel : vue annuelle → totaux 12 mois ; vue mensuelle → données du mois cliqué
- Mode TOUT : vue globale → totaux toutes années ; vue annuelle → données de l'année cliquée
- Clic sur le sous-titre en légende → reset sélection (`setSelectedMonth(null)` ou `setSelectedYear(null)`)
- **Caps arrondis radiaux** : `roundedArcPath()` — path SVG `M outerS A outerArc A capR capR 0 0 1 innerE A innerArc A capR capR 0 0 0 outerS Z`
  - Le cap est un semicercle reliant outer→inner à l'angle exact du segment, sans débordement angulaire
  - Cercle complet (1 segment, GAP=0) : deux demi-cercles + `fillRule="evenodd"`
- GAP = 4° entre segments ; GAP = 0 si segment unique
- Constantes : `CX=CY=120`, `OUTER_R=108`, `INNER_R=72`, `capR=18`
- Anneau de fond : `<circle r={(OUTER_R+INNER_R)/2} strokeWidth={OUTER_R-INNER_R} opacity={0.18}/>`
- Texte central : label (mois/année/Total) + montant
- Titre "Répartition" : une ligne EN BAS dans la zone légende (`justifyContent: space-between`, séparé par `borderBottom`)
- SVG : `viewBox="0 0 240 240"`, `maxWidth: 260`, `height: auto`

## Layout Dashboard

```jsx
<InteretsChartProvider netMode={netMode} pfuRates={pfuRates} activeView={activeView} activeId={activeId}>
  <div style={{ display:'flex', gap:16, alignItems:'stretch', marginTop:8, marginBottom:24 }}>
    <div style={{ flex:2, minWidth:0 }}><InteretsMensuelsChart /></div>
    <div style={{ flex:1,  minWidth:0 }}><InteretsDonutChart /></div>
  </div>
</InteretsChartProvider>
```

`alignItems:'stretch'` + `height:'100%'` sur la carte donut → même hauteur. `maxWidth:260` sur le SVG évite que le donut explose en hauteur.

## Sélecteur Reçu / Projeté

`showActual` + `showProjected` dans le contexte (+ `toggleActual` / `toggleProjected`).

Règle "force reçu" : si `showProjected` est déjà false, `toggleActual` ne peut pas désactiver `showActual`.

Sélecteur UI dans la ligne légende du bar chart :
- Fond `#f0f0f0`, borderRadius 8, padding 3
- Pill actif : fond blanc, boxShadow, fontWeight 600, couleur `#1a1a2e`
- Pill inactif : transparent, couleur `#9ca3af`

Impacts :
- `buildSegments` filtre `actual[]` si `!showActual`, `projected[]` si `!showProjected`
- `filteredTotal(item)` = somme conditionnelle → utilisé pour `rawMax`, `totalH`, label valeur, grand total en-tête
- `totalH` doit utiliser `filteredTotal` (pas `item.total`) sinon le label sort du viewBox quand un filtre est actif
- Donut : `sumA(key)` / `sumP(key)` retournent 0 si le filtre correspondant est off

## Fichiers backend associés

`backend/src/routes/dashboard.js` — deux endpoints :
- `/interets-mensuels` : groupé par mois pour une année donnée
- `/interets-annuels` : groupé par année, toutes années, projections >= début mois courant

**Why:** Visualiser répartition mensuelle/annuelle/multi-années par type (intérêts/capital/cashback), reçu vs projeté.
**How to apply:** Pour ajouter un 3e graphique lié, consommer `useInteretsChart()`. Toujours passer par python3/bash pour éditer ces fichiers (jamais Edit/Write direct sur les grands composants SVG).
