Checkpoint: Migration complète localStorage → BDD : useBudgetData, ParametresContext, Budget2027, DsiCapex, DsiOpex, ImportModal (indicateur inventaires + suppression double-écriture). Ajout procédures tRPC : inventaire.listMeta, inventaire.getStats, opex.initFromSource, opex.deleteAll, opex.cloneFromN1. 14 tests Vitest passants. 0 erreur TypeScript.

This commit is contained in:
Manus
2026-06-10 12:49:29 +00:00
parent caf95da1c1
commit 020156d62c
10 changed files with 1027 additions and 1088 deletions

View File

@@ -255,9 +255,12 @@ interface ImportModalProps {
export function ImportModal({ open, onClose }: ImportModalProps) {
const { annee } = useAnnee();
const utils = trpc.useUtils();
const { data: inventaireMetaList } = trpc.inventaire.listMeta.useQuery();
const importInventaireMutation = trpc.inventaire.import.useMutation({
onSuccess: () => {
utils.inventaire.get.invalidate();
utils.inventaire.listMeta.invalidate();
utils.inventaire.getStats.invalidate();
window.dispatchEvent(new CustomEvent('budgetsi_inventaire_updated', { detail: { annee } }));
},
});
@@ -537,31 +540,6 @@ export function ImportModal({ open, onClose }: ImportModalProps) {
// ── Envoyer à la base de données via tRPC ──────────────────────────────────
const result = await importInventaireMutation.mutateAsync({ annee, filename: file.name, postes: postesPayload });
// ── Aussi sauvegarder dans localStorage pour compatibilité avec les pages existantes ──
const etablissementsArray = Array.from(etabData.entries()).map(([code, d]) => ({
code,
nom: d.nom,
fixes: d.fixes,
portables: d.portables,
stats: {
nb_fixes_total: d.fixes.length,
nb_portables_total: d.portables.length,
nb_fixes_renouveler: 0,
nb_portables_renouveler: 0,
nb_fixes_sans_date: d.fixes.filter(f => f.age_ans === null).length,
nb_portables_sans_date: d.portables.filter(p => p.age_ans === null).length,
budget_fixes: 0,
budget_portables: 0,
budget_total: 0,
},
}));
const inventaireData = {
meta: { date_calcul: new Date().toISOString(), date_reference: `${annee}-01-01`, nb_etablissements: etabData.size, total_fixes: nbFixes, total_portables: nbPortables, filename: file.name },
etablissements: etablissementsArray,
};
localStorage.setItem(getInventaireStorageKey(annee), JSON.stringify(inventaireData));
localStorage.setItem(`budgetsi_inventaire_import_${annee}`, JSON.stringify({ filename: file.name, date: new Date().toISOString(), size: file.size, nbLignes: rows.length - 1, nbEtablissements: etabData.size, nbFixes, nbPortables, format: useIsiFormat ? 'isi-app' : 'classique' }));
// ── Upsert établissements dans la BDD ──────────────────────────────────────
for (const [code, info] of Array.from(etabData.entries())) {
try {
@@ -871,8 +849,9 @@ export function ImportModal({ open, onClose }: ImportModalProps) {
</div>
<div className="flex flex-wrap gap-2 p-3">
{[2025, 2026, 2027, 2028, 2029, 2030].map(yr => {
const hasData = !!localStorage.getItem(`budgetsi_inventaire_data_${yr}`);
const meta = (() => { try { const d = localStorage.getItem(`budgetsi_inventaire_import_${yr}`); return d ? JSON.parse(d) : null; } catch { return null; } })();
const metaRow = inventaireMetaList?.find(m => m.annee === yr);
const hasData = !!metaRow;
const meta = metaRow ? { filename: metaRow.filename, nbLignes: (metaRow.nbFixes ?? 0) + (metaRow.nbPortables ?? 0), date: metaRow.dateImport?.toISOString() ?? '' } : null;
return (
<div
key={yr}