Implémentation de la feature "suivi des objectifs"
This commit is contained in:
1 parent
6e03195d3a
commit
1060bfe2be
14 files changed
+1844
-192
No files matched your search
@@ -6,6 +6,7 @@ import EmptyState from '../components/EmptyState.jsx';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { api } from '../api.js';
|
||||
import { useInvestisseur } from '../context/InvestisseurContext.jsx';
|
||||
import { useUi } from '../context/UiContext.jsx';
|
||||
import Modal from '../components/Modal.jsx';
|
||||
import ConfirmModal from '../components/ConfirmModal.jsx';
|
||||
import SoldeChart from '../components/SoldeChart.jsx';
|
||||
@@ -94,6 +95,7 @@ function platToJSON(rows) {
|
||||
/* ── Composant principal ─────────────────────────────────────── */
|
||||
export default function DepotsRetraits() {
|
||||
const { activeId, activeView, investisseurs } = useInvestisseur();
|
||||
const { objectifsActifs } = useUi();
|
||||
const { search } = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const [activeTab, setActiveTab] = useState('plateformes');
|
||||
@@ -453,6 +455,12 @@ export default function DepotsRetraits() {
|
||||
totalPages: drTotalPages, totalItems: drTotalItems, PAGE_SIZES,
|
||||
} = usePagination(tableRows, 'cl_pagesize_dr', [filter]);
|
||||
|
||||
// Repli sur l'onglet Plateformes si la fixation des objectifs est désactivée
|
||||
// pendant qu'on est sur l'onglet Objectifs (ex. dans un autre onglet du navigateur).
|
||||
useEffect(() => {
|
||||
if (!objectifsActifs && activeTab === 'vision-annuelle') setActiveTab('plateformes');
|
||||
}, [objectifsActifs, activeTab]);
|
||||
|
||||
// Auto-sélectionne le premier mouvement quand la liste change dans l'onglet mouvements
|
||||
// (déclaré après `tableRows` pour éviter la temporal dead zone)
|
||||
useEffect(() => {
|
||||
@@ -575,6 +583,7 @@ export default function DepotsRetraits() {
|
||||
Toujours calculé sur le portefeuille entier (ignore le filtre plateforme),
|
||||
pour l'année sélectionnée (drPlatYear, ou l'année en cours si "Toutes les années"). */
|
||||
const scopeInvestisseurIdsKpi = activeView === 'all' ? investisseurs.map(i => i.id) : [activeId];
|
||||
|
||||
const kpiObjectifYear = Number(drPlatYear || new Date().getFullYear());
|
||||
const kpiObjectifTotal = (() => {
|
||||
const list = objectifsKpi.filter(o => o.annee === kpiObjectifYear && scopeInvestisseurIdsKpi.includes(o.investisseur_id));
|
||||
@@ -904,14 +913,16 @@ export default function DepotsRetraits() {
|
||||
<TrendBadge current={totals.depots - totals.retraits} prev={prevTotals.depots - prevTotals.retraits} />
|
||||
</div>
|
||||
<div style={{ fontSize: '0.8em', color: 'var(--text-muted)', marginTop: 5 }}>{fmtEUR(prevTotals.depots - prevTotals.retraits)} en {prevYear}</div>
|
||||
{kpiEcartObjectif != null && (
|
||||
{objectifsActifs && kpiEcartObjectif != null && (
|
||||
<div style={{
|
||||
fontSize: '0.8em', marginTop: 4, fontWeight: 600,
|
||||
color: kpiEcartObjectif >= 0 ? 'var(--success)' : 'var(--warning)',
|
||||
color: Math.abs(kpiEcartObjectif) < 0.01 ? 'var(--success)' : kpiEcartObjectif >= 0 ? 'var(--danger)' : 'var(--warning)',
|
||||
}}>
|
||||
{kpiEcartObjectif >= 0
|
||||
? `+${fmtEUR(kpiEcartObjectif)} au-delà de l'objectif ${kpiObjectifYear}`
|
||||
: `Reste ${fmtEUR(-kpiEcartObjectif)} pour l'objectif ${kpiObjectifYear}`}
|
||||
{Math.abs(kpiEcartObjectif) < 0.01
|
||||
? `Conforme à l'objectif ${kpiObjectifYear}`
|
||||
: kpiEcartObjectif >= 0
|
||||
? `+${fmtEUR(kpiEcartObjectif)} au-delà de l'objectif ${kpiObjectifYear}`
|
||||
: `${fmtEUR(-kpiEcartObjectif)} en dessous de l'objectif ${kpiObjectifYear}`}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -929,16 +940,18 @@ export default function DepotsRetraits() {
|
||||
{!listFocused && <div className="dr-tabs">
|
||||
<button className={`dr-tab${activeTab === 'plateformes' ? ' active' : ''}`}
|
||||
onClick={() => setActiveTab('plateformes')}>Plateformes</button>
|
||||
<button className={`dr-tab${activeTab === 'vision-annuelle' ? ' active' : ''}`}
|
||||
onClick={() => setActiveTab('vision-annuelle')}>Vision annuelle</button>
|
||||
<button className={`dr-tab${activeTab === 'vision-mensuelle' ? ' active' : ''}`}
|
||||
onClick={() => setActiveTab('vision-mensuelle')}>Vision mensuelle</button>
|
||||
<button className={`dr-tab${activeTab === 'mouvements' ? ' active' : ''}`}
|
||||
onClick={() => { setActiveTab('mouvements'); setSelectedRow(r => r ?? (rows[0] || null)); }}>Mouvements</button>
|
||||
{objectifsActifs && (
|
||||
<button className={`dr-tab${activeTab === 'vision-annuelle' ? ' active' : ''}`}
|
||||
onClick={() => setActiveTab('vision-annuelle')}>Objectifs</button>
|
||||
)}
|
||||
</div>}
|
||||
|
||||
{/* ====== ONGLET VISION ANNUELLE ====== */}
|
||||
{activeTab === 'vision-annuelle' && (
|
||||
{objectifsActifs && activeTab === 'vision-annuelle' && (
|
||||
<div style={{ padding: '0 24px' }}>
|
||||
<SuiviObjectifs
|
||||
rows={allRows}
|
||||
|
||||
Reference in new issue
Block a user