From e124a970a712f39a5a2b2d100be07553c0df2515 Mon Sep 17 00:00:00 2001 From: Manus Date: Thu, 4 Jun 2026 14:38:48 +0000 Subject: [PATCH] =?UTF-8?q?Checkpoint:=20Ajout=20d'un=20=C3=A9diteur=20de?= =?UTF-8?q?=20prompt=20de=20processus=20d'import=20dans=20l'onglet=20"Inve?= =?UTF-8?q?ntaire=20postes"=20de=20la=20modale=20Imports=20&=20Donn=C3=A9e?= =?UTF-8?q?s.=20Le=20prompt=20est=20affich=C3=A9=20dans=20un=20bloc=20plia?= =?UTF-8?q?ble/d=C3=A9pliable,=20modifiable=20via=20un=20textarea=20monosp?= =?UTF-8?q?ace=20(16=20lignes),=20sauvegardable=20en=20localStorage=20(cl?= =?UTF-8?q?=C3=A9=20budgetsi=5Fimport=5Fprompt),=20r=C3=A9initialisable=20?= =?UTF-8?q?aux=20valeurs=20par=20d=C3=A9faut.=20Le=20prompt=20par=20d?= =?UTF-8?q?=C3=A9faut=20documente=20le=20format=20attendu,=20les=20colonne?= =?UTF-8?q?s=20obligatoires/optionnelles,=20les=20r=C3=A8gles=20de=20trait?= =?UTF-8?q?ement=20et=20le=20comportement=20attendu.=20Boutons=20Modifier?= =?UTF-8?q?=20/=20Enregistrer=20/=20Annuler=20/=20R=C3=A9initialiser=20par?= =?UTF-8?q?=20d=C3=A9faut.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/ImportModal.tsx | 187 +++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/client/src/components/ImportModal.tsx b/client/src/components/ImportModal.tsx index 68e6814..5b1686e 100644 --- a/client/src/components/ImportModal.tsx +++ b/client/src/components/ImportModal.tsx @@ -1,7 +1,7 @@ // ImportModal.tsx — Fenêtre d'import de données (Inventaire, Établissements, Utilisateurs) // Design: Corporate Modernism — Itinova Budget SI -import { useState, useRef } from 'react'; +import { useState, useRef, useEffect } from 'react'; import { Upload, X, @@ -12,6 +12,12 @@ import { AlertCircle, FileSpreadsheet, Info, + Pencil, + Save, + RotateCcw, + ChevronDown, + ChevronUp, + BrainCircuit, } from 'lucide-react'; import { toast } from 'sonner'; @@ -39,6 +45,50 @@ export interface Utilisateur { const ETAB_KEY = 'budgetsi_etablissements'; const USER_KEY = 'budgetsi_utilisateurs'; +const IMPORT_PROMPT_KEY = 'budgetsi_import_prompt'; + +const DEFAULT_IMPORT_PROMPT = `# Processus d'import de l'inventaire ISI-APP + +## Format du fichier source +- Export CSV depuis ISI-APP (séparateur : point-virgule ou virgule) +- Encodage : UTF-8 ou Windows-1252 +- Première ligne : en-têtes de colonnes + +## Colonnes obligatoires +- **Code établissement** : identifiant unique (ex : 1063CRD, 2013ANG…) + Noms acceptés : code, code_etab, code_etablissement, code etablissement +- **Nom établissement** : libellé complet + Noms acceptés : nom, nom_etab, nom_etablissement, etablissement, libelle_etab + +## Colonnes optionnelles +- type : fixe / portable / bureau / laptop +- date_achat : date d'achat du poste (JJ/MM/AAAA) +- modele : modèle du matériel +- fabricant : marque (Lenovo, Dell, HP…) +- groupe : groupement ou association de rattachement +- ville : commune de l'établissement + +## Règles de traitement +1. Les établissements uniques sont extraits et enregistrés dans la liste des établissements +2. Les établissements déjà présents ne sont pas écrasés (fusion intelligente) +3. Les lignes sans code ou sans nom sont ignorées +4. Les doublons de postes sont conservés (un poste = une ligne) + +## Comportement attendu +- Créer ou mettre à jour la liste des établissements (code + nom) +- Enregistrer les métadonnées du fichier importé (nom, date, nb lignes) +- Afficher un résumé : nb lignes traitées, nb établissements détectés, nb nouveaux`; + +export function loadImportPrompt(): string { + try { + const raw = localStorage.getItem(IMPORT_PROMPT_KEY); + return raw ? JSON.parse(raw) : DEFAULT_IMPORT_PROMPT; + } catch { return DEFAULT_IMPORT_PROMPT; } +} + +export function saveImportPrompt(prompt: string) { + localStorage.setItem(IMPORT_PROMPT_KEY, JSON.stringify(prompt)); +} export function loadEtablissements(): Etablissement[] { try { @@ -152,6 +202,52 @@ export function ImportModal({ open, onClose }: ImportModalProps) { const [userStatus, setUserStatus] = useState<'idle' | 'success' | 'error'>('idle'); const [userMsg, setUserMsg] = useState(''); + // ── Prompt de processus d'import ──────────────────────────────────────────── + const [importPrompt, setImportPrompt] = useState(() => loadImportPrompt()); + const [promptDraft, setPromptDraft] = useState(''); + const [promptEditing, setPromptEditing] = useState(false); + const [promptExpanded, setPromptExpanded] = useState(false); + const [promptSaved, setPromptSaved] = useState(false); + + // Sync prompt depuis localStorage à l'ouverture + useEffect(() => { + if (open) { + const p = loadImportPrompt(); + setImportPrompt(p); + setPromptDraft(p); + setPromptEditing(false); + } + }, [open]); + + const handlePromptEdit = () => { + setPromptDraft(importPrompt); + setPromptEditing(true); + setPromptExpanded(true); + }; + + const handlePromptSave = () => { + saveImportPrompt(promptDraft); + setImportPrompt(promptDraft); + setPromptEditing(false); + setPromptSaved(true); + toast.success('Prompt enregistré', { description: 'La configuration du processus d\'import a été sauvegardée.' }); + setTimeout(() => setPromptSaved(false), 2500); + }; + + const handlePromptCancel = () => { + setPromptDraft(importPrompt); + setPromptEditing(false); + }; + + const handlePromptReset = () => { + if (!confirm('Réinitialiser le prompt aux valeurs par défaut ?')) return; + setPromptDraft(DEFAULT_IMPORT_PROMPT); + saveImportPrompt(DEFAULT_IMPORT_PROMPT); + setImportPrompt(DEFAULT_IMPORT_PROMPT); + setPromptEditing(false); + toast.info('Prompt réinitialisé'); + }; + if (!open) return null; // ── Inventaire ISI-APP ────────────────────────────────────────────────────── @@ -457,6 +553,93 @@ export function ImportModal({ open, onClose }: ImportModalProps) {
{tab === 'inventaire' && ( <> + {/* ── Prompt de processus d'import ── */} +
+ {/* En-tête du prompt */} +
!promptEditing && setPromptExpanded(e => !e)} + > +
+
+ +
+
+

Prompt de processus d'import

+

Instructions de traitement du fichier ISI-APP

+
+
+
+ {!promptEditing && ( + + )} + {!promptEditing && ( + + {promptExpanded + ? + : } + + )} +
+
+ + {/* Corps du prompt */} + {(promptExpanded || promptEditing) && ( +
+ {promptEditing ? ( +
+