// Parametres.tsx — Page de paramétrage du calcul de renouvellement // Design: Corporate Modernism — Itinova Budget SI 2027 import { useState, useEffect } from 'react'; import { useLocation } from 'wouter'; import { Settings, Monitor, Laptop, Euro, RotateCcw, Save, CheckCircle, ChevronLeft, Info, BarChart3, Calendar, } from 'lucide-react'; import { ANNEES_DISPONIBLES } from '../contexts/AnneeContext'; import { AppSidebar } from '../components/AppSidebar'; import { useParametres, PARAMETRES_DEFAULTS } from '../contexts/ParametresContext'; import { formatEuros } from '../lib/format'; import { toast } from 'sonner'; interface FieldState { seuilFixesAns: number; seuilPortablesAns: number; coutFixe: number; coutPortable: number; anneeDefaut: number; } function SliderInput({ label, icon: Icon, value, min, max, step, unit, description, onChange, accentClass, }: { label: string; icon: React.ElementType; value: number; min: number; max: number; step: number; unit: string; description: string; onChange: (v: number) => void; accentClass: string; }) { return (

{label}

{description}

{value} {unit}
{/* Slider */}
onChange(Number(e.target.value))} className="w-full h-2 rounded-full appearance-none cursor-pointer accent-primary bg-muted" />
{min} {unit} {max} {unit}
{/* Input numérique direct */}
{ const v = Number(e.target.value); if (!isNaN(v) && v >= min && v <= max) onChange(v); }} className="w-24 px-3 py-1.5 text-sm border border-border rounded-lg bg-muted/50 focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary tabular-nums" /> {unit}
); } export default function Parametres() { const [, navigate] = useLocation(); const { parametres, setParametres, resetParametres } = useParametres(); const [draft, setDraft] = useState({ ...parametres }); const [saved, setSaved] = useState(false); // Sync si les paramètres changent depuis l'extérieur useEffect(() => { setDraft({ ...parametres }); }, [parametres]); const hasChanges = draft.seuilFixesAns !== parametres.seuilFixesAns || draft.seuilPortablesAns !== parametres.seuilPortablesAns || draft.coutFixe !== parametres.coutFixe || draft.coutPortable !== parametres.coutPortable || draft.anneeDefaut !== parametres.anneeDefaut; const handleSave = () => { setParametres(draft); setSaved(true); toast.success('Paramètres enregistrés', { description: 'Les budgets ont été recalculés avec les nouveaux seuils.', }); setTimeout(() => setSaved(false), 2500); }; const handleReset = () => { setDraft({ ...PARAMETRES_DEFAULTS }); resetParametres(); toast.info('Paramètres réinitialisés', { description: 'Les valeurs par défaut ont été restaurées.', }); }; const update = (key: keyof FieldState) => (v: number) => { setDraft((d) => ({ ...d, [key]: v })); }; return (
{/* Contenu principal */}
{/* En-tête */}

Paramètres de calcul

Seuils de vétusté et coûts unitaires — Budget 2027

{/* Contenu */}
{/* Bandeau info */}

Les modifications sont appliquées immédiatement après enregistrement. Tous les budgets des 60 établissements sont recalculés automatiquement. Les valeurs sont sauvegardées dans votre navigateur.

{/* Section vétusté */}

Seuils de vétusté

Un équipement est considéré à renouveler si son âge (calculé au 01/01/2027) est supérieur ou égal au seuil défini ci-dessous.

{/* Section coûts */}

Coûts unitaires TTC

Prix d'achat unitaire estimé utilisé pour le calcul du budget de renouvellement.

{/* Section année par défaut */}

Année d'ouverture par défaut

L'année sélectionnée au démarrage de l'application lorsqu'aucune session précédente n'est mémorisée.

Année par défaut

Exercice ouvert au premier lancement

{ANNEES_DISPONIBLES.map(a => ( ))}
{/* Aperçu de l'impact */}

Paramètres en cours d'édition

{[ { label: 'Seuil PC Fixes', value: `${draft.seuilFixesAns} ans`, changed: draft.seuilFixesAns !== parametres.seuilFixesAns }, { label: 'Seuil PC Portables', value: `${draft.seuilPortablesAns} ans`, changed: draft.seuilPortablesAns !== parametres.seuilPortablesAns }, { label: 'Coût fixe unitaire', value: formatEuros(draft.coutFixe), changed: draft.coutFixe !== parametres.coutFixe }, { label: 'Coût portable unitaire', value: formatEuros(draft.coutPortable), changed: draft.coutPortable !== parametres.coutPortable }, { label: 'Année par défaut', value: `${draft.anneeDefaut}`, changed: draft.anneeDefaut !== parametres.anneeDefaut }, ].map((item) => (

{item.label}

{item.value} {item.changed && ( modifié )}

))}
{hasChanges && (

Des modifications non enregistrées sont en attente. Cliquez sur Enregistrer pour les appliquer.

)}
); }