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:
@@ -387,34 +387,58 @@ export default function Budget2027() {
|
||||
|
||||
{selectedCode && selectedBP && (
|
||||
<>
|
||||
{/* Données informatives 2026 */}
|
||||
<section className="bg-amber-50 border border-amber-200 rounded-xl p-5">
|
||||
<div className="flex items-start gap-3 mb-4">
|
||||
<Info className="w-4 h-4 text-amber-600 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<h3 className="font-semibold text-amber-900" style={{ fontFamily: 'Sora, sans-serif' }}>
|
||||
Données historiques — à titre informatif
|
||||
</h3>
|
||||
<p className="text-sm text-amber-700 mt-0.5">
|
||||
Enveloppes de renouvellement allouées par le Siège Itinova (non modifiables)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<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-xl font-bold text-amber-900 mt-1 tabular-nums">
|
||||
{selectedBP.renouvellement_2026 > 0 ? formatEuros(selectedBP.renouvellement_2026) : '—'}
|
||||
</p>
|
||||
<p className="text-xs text-amber-600 mt-0.5">Enveloppe Siège</p>
|
||||
</div>
|
||||
</section>
|
||||
{/* 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">
|
||||
<div className="flex items-start gap-3 mb-4">
|
||||
<Info className="w-4 h-4 text-amber-600 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<h3 className="font-semibold text-amber-900" style={{ fontFamily: 'Sora, sans-serif' }}>
|
||||
Données historiques — à titre informatif
|
||||
</h3>
|
||||
<p className="text-sm text-amber-700 mt-0.5">
|
||||
Renouvellement de l'année précédente ({anneeN1}) — non modifiable
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<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 {anneeN1}</p>
|
||||
<p className="text-xl font-bold text-amber-900 mt-1 tabular-nums">
|
||||
{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>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* Formulaire de saisie */}
|
||||
<section className="space-y-3">
|
||||
<div className="flex items-center gap-2 pb-2 border-b border-border">
|
||||
<TrendingUp className="w-4 h-4 text-muted-foreground" />
|
||||
<h2 className="font-semibold text-foreground" style={{ fontFamily: 'Sora, sans-serif' }}>
|
||||
Budget prévisionnel 2027 — Saisie directeur
|
||||
Budget prévisionnel {annee} — Saisie directeur
|
||||
</h2>
|
||||
<span className="text-xs text-muted-foreground ml-1">(tous les champs sont optionnels)</span>
|
||||
</div>
|
||||
|
||||
@@ -45,6 +45,9 @@ export default function Home() {
|
||||
} = useBudgetData();
|
||||
|
||||
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 [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
||||
const [viewMode, setViewMode] = useState<ViewMode>('grid');
|
||||
@@ -232,7 +235,20 @@ export default function Home() {
|
||||
|
||||
{/* Contenu — vue vignettes ou liste */}
|
||||
<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">
|
||||
<CheckCircle className="w-12 h-12 text-muted-foreground/30 mb-3" />
|
||||
<p className="text-muted-foreground font-medium">Aucun établissement trouvé</p>
|
||||
|
||||
Reference in New Issue
Block a user