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 ? ( +
+