Checkpoint: Sélecteur d'année global (2025, 2026, 2027+) sur Renouvellement PC, CAPEX-Construction, CAPEX-Synthèse et DSI-OPEX. Données CAPEX isolées par année en localStorage. Titres dynamiques selon l'année sélectionnée.

This commit is contained in:
Manus
2026-06-03 09:49:14 +00:00
parent 858f0dc4d9
commit 787928c722
7 changed files with 189 additions and 55 deletions

View File

@@ -18,6 +18,8 @@ import {
Info,
} from 'lucide-react';
import { AppSidebar } from '../components/AppSidebar';
import { AnneeSelectorBar } from '../components/AnneeSelectorBar';
import { useAnnee, getCapexStorageKey } from '../contexts/AnneeContext';
import bp2027Raw from '../data_bp2027.json';
import budgetRaw from '../data_budget.json';
import { useParametres } from '../contexts/ParametresContext';
@@ -27,7 +29,7 @@ import type { BudgetData } from '../types/budget';
const bp2027Data = bp2027Raw as BP2027Data;
const budgetData = budgetRaw as BudgetData;
const STORAGE_KEY = 'itinova_budget2027_saisies';
// Clé de stockage dynamique par année (voir getCapexStorageKey)
// Colonnes du tableau (correspondance exacte avec le fichier Excel BP2027)
const COLONNES = [
@@ -56,9 +58,9 @@ const COL_COLORS: Record<string, { header: string; cell: string; total: string }
slate: { header: 'bg-slate-500 text-white', cell: 'text-slate-700', total: 'bg-slate-50 text-slate-800 font-semibold' },
};
function loadSaisies(): Record<string, Partial<BudgetFormValues>> {
function loadSaisiesForAnnee(annee: number): Record<string, Partial<BudgetFormValues>> {
try {
const raw = localStorage.getItem(STORAGE_KEY);
const raw = localStorage.getItem(getCapexStorageKey(annee));
return raw ? JSON.parse(raw) : {};
} catch { return {}; }
}
@@ -83,6 +85,7 @@ type SortDir = 'asc' | 'desc';
export default function DsiCapex() {
const { parametres } = useParametres();
const { annee } = useAnnee();
const [saisies, setSaisies] = useState<Record<string, Partial<BudgetFormValues>>>({});
const [search, setSearch] = useState('');
const [sortCol, setSortCol] = useState<string>('nom');
@@ -91,8 +94,8 @@ export default function DsiCapex() {
const [lastRefresh, setLastRefresh] = useState(Date.now());
useEffect(() => {
setSaisies(loadSaisies());
}, [lastRefresh]);
setSaisies(loadSaisiesForAnnee(annee));
}, [lastRefresh, annee]);
const handleRefresh = () => setLastRefresh(Date.now());
@@ -191,19 +194,22 @@ export default function DsiCapex() {
<div className="flex items-center justify-between gap-4 flex-wrap">
<div>
<h1 className="text-xl font-bold text-foreground" style={{ fontFamily: 'Sora, sans-serif' }}>
DSI CAPEX Prévisionnel Budget 2027
CAPEX - Synthèse {annee}
</h1>
<p className="text-sm text-muted-foreground mt-0.5">
Tableau consolidé des investissements SI par établissement
</p>
</div>
<button
onClick={handleRefresh}
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg border border-border text-sm text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors"
>
<RefreshCw className="w-4 h-4" />
Actualiser
</button>
<div className="flex items-center gap-3">
<AnneeSelectorBar />
<button
onClick={handleRefresh}
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg border border-border text-sm text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors"
>
<RefreshCw className="w-4 h-4" />
Actualiser
</button>
</div>
</div>
</header>
@@ -213,7 +219,7 @@ export default function DsiCapex() {
<div className="bg-card border border-border rounded-xl p-4">
<div className="flex items-center gap-2 mb-1">
<Euro className="w-4 h-4 text-orange-500" />
<span className="text-xs text-muted-foreground uppercase tracking-wide font-medium">Total CAPEX 2027</span>
<span className="text-xs text-muted-foreground uppercase tracking-wide font-medium">Total CAPEX {annee}</span>
</div>
<p className="text-2xl font-bold text-orange-600 tabular-nums" style={{ fontFamily: 'Sora, sans-serif' }}>
{formatEuros(totalGeneral)}