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

@@ -25,6 +25,8 @@ import {
AlertCircle,
} 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';
@@ -35,7 +37,7 @@ import type { BudgetData } from '../types/budget';
const bp2027Data = bp2027Raw as BP2027Data;
const budgetData = budgetRaw as BudgetData;
const STORAGE_KEY = 'itinova_budget2027_saisies';
// La clé de stockage est désormais dynamique par année (voir getCapexStorageKey)
// Icônes par clé de champ
const ICON_MAP: Record<string, React.ElementType> = {
@@ -87,15 +89,15 @@ const CHAMPS: { key: keyof BudgetFormValues; label: string; description: string
{ key: 'autres', label: 'Autres investissements SI', description: 'Tout autre investissement informatique' },
];
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 {}; }
}
function saveSaisies(data: Record<string, Partial<BudgetFormValues>>) {
try { localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); } catch {}
function saveSaisiesForAnnee(annee: number, data: Record<string, Partial<BudgetFormValues>>) {
try { localStorage.setItem(getCapexStorageKey(annee), JSON.stringify(data)); } catch {}
}
function getDefaultValues(etabCode: string, renouvellement2027: number): BudgetFormValues {
@@ -167,8 +169,9 @@ function CurrencyInput({
export default function Budget2027() {
const [, navigate] = useLocation();
const { parametres } = useParametres();
const { annee } = useAnnee();
const [selectedCode, setSelectedCode] = useState<string>('');
const [saisies, setSaisies] = useState<Record<string, Partial<BudgetFormValues>>>(loadSaisies);
const [saisies, setSaisies] = useState<Record<string, Partial<BudgetFormValues>>>(() => loadSaisiesForAnnee(annee));
const [saved, setSaved] = useState(false);
const [searchEtab, setSearchEtab] = useState('');
const [dropdownOpen, setDropdownOpen] = useState(false);
@@ -213,6 +216,12 @@ export default function Budget2027() {
);
}, [formValues]);
// Recharger les saisies quand l'année change
useEffect(() => {
setSaisies(loadSaisiesForAnnee(annee));
setSelectedCode('');
}, [annee]);
const updateField = (key: keyof BudgetFormValues, value: number | string) => {
setSaisies(prev => {
const next = {
@@ -222,14 +231,14 @@ export default function Budget2027() {
[key]: value,
},
};
saveSaisies(next);
saveSaisiesForAnnee(annee, next);
return next;
});
setSaved(false);
};
const handleSave = () => {
saveSaisies(saisies);
saveSaisiesForAnnee(annee, saisies);
setSaved(true);
setTimeout(() => setSaved(false), 2500);
};
@@ -238,7 +247,7 @@ export default function Budget2027() {
setSaisies(prev => {
const next = { ...prev };
delete next[selectedCode];
saveSaisies(next);
saveSaisiesForAnnee(annee, next);
return next;
});
};
@@ -265,12 +274,15 @@ export default function Budget2027() {
<div className="flex items-center justify-between gap-4">
<div>
<h1 className="text-xl font-bold text-foreground" style={{ fontFamily: 'Sora, sans-serif' }}>
Construction Budget 2027
CAPEX - Construction {annee}
</h1>
<p className="text-sm text-muted-foreground mt-0.5">
Campagne budgétaire CAPEX DSI Prévisionnel 2027
Campagne budgétaire CAPEX DSI Prévisionnel {annee}
</p>
</div>
<div className="flex items-center gap-3">
<AnneeSelectorBar />
</div>
{selectedCode && (
<div className="flex items-center gap-2">
<button