Checkpoint: Renouvellement PC affiche un état vide pour les années sans inventaire importé (hors 2026-2027). Construction Budget affiche la valeur de renouvellement de l'année N-1 dans les données historiques (depuis les saisies CAPEX N-1 ou l'enveloppe 2026 du fichier BP2027 en fallback). Titre du formulaire dynamique selon l'année.

This commit is contained in:
Manus
2026-06-03 10:04:55 +00:00
parent 787928c722
commit 0adfcba5d2
2 changed files with 63 additions and 23 deletions

View File

@@ -387,7 +387,27 @@ export default function Budget2027() {
{selectedCode && selectedBP && ( {selectedCode && selectedBP && (
<> <>
{/* Données informatives 2026 */} {/* Données historiques N-1 */}
{(() => {
const anneeN1 = annee - 1;
// Valeur N-1 : d'abord chercher dans les saisies CAPEX de l'année N-1,
// sinon utiliser l'enveloppe 2026 issue du fichier BP2027 (si N-1 === 2026)
let valeurN1: number = 0;
try {
const storageN1 = localStorage.getItem(`itinova_budget_capex_${anneeN1}`);
if (storageN1) {
const saisiesN1 = JSON.parse(storageN1) as Record<string, { renouvellement_2027?: number }>;
const saisieEtab = saisiesN1[selectedCode];
if (saisieEtab?.renouvellement_2027 !== undefined) {
valeurN1 = saisieEtab.renouvellement_2027;
}
}
} catch {}
// Fallback : enveloppe 2026 du fichier BP2027 si N-1 = 2026
if (valeurN1 === 0 && anneeN1 === 2026 && selectedBP.renouvellement_2026 > 0) {
valeurN1 = selectedBP.renouvellement_2026;
}
return (
<section className="bg-amber-50 border border-amber-200 rounded-xl p-5"> <section className="bg-amber-50 border border-amber-200 rounded-xl p-5">
<div className="flex items-start gap-3 mb-4"> <div className="flex items-start gap-3 mb-4">
<Info className="w-4 h-4 text-amber-600 mt-0.5 flex-shrink-0" /> <Info className="w-4 h-4 text-amber-600 mt-0.5 flex-shrink-0" />
@@ -396,25 +416,29 @@ export default function Budget2027() {
Données historiques à titre informatif Données historiques à titre informatif
</h3> </h3>
<p className="text-sm text-amber-700 mt-0.5"> <p className="text-sm text-amber-700 mt-0.5">
Enveloppes de renouvellement allouées par le Siège Itinova (non modifiables) Renouvellement de l'année précédente ({anneeN1}) — non modifiable
</p> </p>
</div> </div>
</div> </div>
<div className="bg-white/70 rounded-lg px-4 py-3 border border-amber-200"> <div className="bg-white/70 rounded-lg px-4 py-3 border border-amber-200">
<p className="text-xs text-amber-600 font-medium uppercase tracking-wide">Renouvellement 2026</p> <p className="text-xs text-amber-600 font-medium uppercase tracking-wide">Renouvellement {anneeN1}</p>
<p className="text-xl font-bold text-amber-900 mt-1 tabular-nums"> <p className="text-xl font-bold text-amber-900 mt-1 tabular-nums">
{selectedBP.renouvellement_2026 > 0 ? formatEuros(selectedBP.renouvellement_2026) : ''} {valeurN1 > 0 ? formatEuros(valeurN1) : '0 '}
</p>
<p className="text-xs text-amber-600 mt-0.5">
{valeurN1 > 0 ? 'Valeur saisie ou enveloppe allouée' : 'Aucune saisie pour cet exercice'}
</p> </p>
<p className="text-xs text-amber-600 mt-0.5">Enveloppe Siège</p>
</div> </div>
</section> </section>
);
})()}
{/* Formulaire de saisie */} {/* Formulaire de saisie */}
<section className="space-y-3"> <section className="space-y-3">
<div className="flex items-center gap-2 pb-2 border-b border-border"> <div className="flex items-center gap-2 pb-2 border-b border-border">
<TrendingUp className="w-4 h-4 text-muted-foreground" /> <TrendingUp className="w-4 h-4 text-muted-foreground" />
<h2 className="font-semibold text-foreground" style={{ fontFamily: 'Sora, sans-serif' }}> <h2 className="font-semibold text-foreground" style={{ fontFamily: 'Sora, sans-serif' }}>
Budget prévisionnel 2027 — Saisie directeur Budget prévisionnel {annee} — Saisie directeur
</h2> </h2>
<span className="text-xs text-muted-foreground ml-1">(tous les champs sont optionnels)</span> <span className="text-xs text-muted-foreground ml-1">(tous les champs sont optionnels)</span>
</div> </div>

View File

@@ -45,6 +45,9 @@ export default function Home() {
} = useBudgetData(); } = useBudgetData();
const { annee } = useAnnee(); const { annee } = useAnnee();
// Les données de parc ne sont disponibles que pour 2026 et 2027
const ANNEES_AVEC_DONNEES = [2026, 2027];
const hasDonnees = ANNEES_AVEC_DONNEES.includes(annee);
const [selectedEtab, setSelectedEtab] = useState<Etablissement | null>(null); const [selectedEtab, setSelectedEtab] = useState<Etablissement | null>(null);
const [sidebarCollapsed, setSidebarCollapsed] = useState(false); const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const [viewMode, setViewMode] = useState<ViewMode>('grid'); const [viewMode, setViewMode] = useState<ViewMode>('grid');
@@ -232,7 +235,20 @@ export default function Home() {
{/* Contenu — vue vignettes ou liste */} {/* Contenu — vue vignettes ou liste */}
<div className="flex-1 overflow-y-auto px-6 py-5"> <div className="flex-1 overflow-y-auto px-6 py-5">
{etablissements.length === 0 ? ( {!hasDonnees ? (
<div className="flex flex-col items-center justify-center py-20 text-center">
<div className="w-16 h-16 rounded-2xl bg-muted/40 flex items-center justify-center mb-4">
<Monitor className="w-8 h-8 text-muted-foreground/40" />
</div>
<p className="text-lg font-semibold text-foreground" style={{ fontFamily: 'Sora, sans-serif' }}>
Aucune donnée pour {annee}
</p>
<p className="text-sm text-muted-foreground mt-2 max-w-sm">
Les données d'inventaire parc PC pour l'exercice {annee} n'ont pas encore été importées.
Les années disponibles sont 2026 et 2027.
</p>
</div>
) : etablissements.length === 0 ? (
<div className="flex flex-col items-center justify-center py-16 text-center"> <div className="flex flex-col items-center justify-center py-16 text-center">
<CheckCircle className="w-12 h-12 text-muted-foreground/30 mb-3" /> <CheckCircle className="w-12 h-12 text-muted-foreground/30 mb-3" />
<p className="text-muted-foreground font-medium">Aucun établissement trouvé</p> <p className="text-muted-foreground font-medium">Aucun établissement trouvé</p>