/**
 * =========================================================================
 * PLATEFORME ECO-PANNEAU.FR - VERSION 1.0.0
 * Socle UI - Composants partagés, Icônes et Utilitaires
 * =========================================================================
 */

const { useState, useEffect, useRef } = React;

// =========================================================================
// 1. SYSTÈME DE NOTIFICATIONS GLOBAL (TOASTS)
// =========================================================================

window.showToast = (message, type = 'success', duration = 4000) => {
    let container = document.getElementById('toast-container');
    if (!container) {
        container = document.createElement('div');
        container.id = 'toast-container';
        container.className = 'fixed top-4 right-4 z-[9999] flex flex-col gap-2 pointer-events-none';
        document.body.appendChild(container);
    }

    const toast = document.createElement('div');
    const bgColor = type === 'success' ? 'bg-emerald-50 border-emerald-200 text-emerald-800' : 
                   (type === 'error' ? 'bg-red-50 border-red-200 text-red-800' : 'bg-blue-50 border-blue-200 text-blue-800');
    
    toast.className = `flex items-center gap-3 px-4 py-3 rounded-xl border shadow-lg ${bgColor} transform transition-all duration-300 translate-x-full opacity-0 pointer-events-auto`;
    toast.innerHTML = `
        <div class="font-bold text-sm">${message}</div>
    `;

    container.appendChild(toast);

    // Animation d'entrée
    requestAnimationFrame(() => {
        toast.classList.remove('translate-x-full', 'opacity-0');
        toast.classList.add('translate-x-0', 'opacity-100');
    });

    // Suppression automatique
    setTimeout(() => {
        toast.classList.remove('translate-x-0', 'opacity-100');
        toast.classList.add('translate-x-full', 'opacity-0');
        setTimeout(() => toast.remove(), 300);
    }, duration);
};

// =========================================================================
// 2. UTILITAIRES ET UPLOADS (COMPRESSION IMAGE & APERÇU PDF CLIENT)
// =========================================================================

window.uploadFile = async (file, type, onProgress) => {
    let fileToSend = file;
    let previewBlob = null;
    let hasLocalProcessing = false;
    
    try {
        // A. TRAITEMENT CLIENT DES IMAGES (Compression WEBP + Redimensionnement)
        if (type === 'image' && file.type.startsWith('image/')) {
            if (onProgress) onProgress("Optimisation de l'image en cours...", 10);
            
            const img = new Image();
            const imgUrl = URL.createObjectURL(file);
            
            await new Promise((resolve, reject) => {
                img.onload = resolve;
                img.onerror = reject;
                img.src = imgUrl;
            });
            
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');
            let width = img.width;
            let height = img.height;
            const MAX_SIZE = 1920; 
            
            if (width > MAX_SIZE || height > MAX_SIZE) {
                if (width > height) {
                    height = Math.round((height * MAX_SIZE) / width);
                    width = MAX_SIZE;
                } else {
                    width = Math.round((width * MAX_SIZE) / height);
                    height = MAX_SIZE;
                }
            }
            
            canvas.width = width;
            canvas.height = height;
            ctx.drawImage(img, 0, 0, width, height);
            URL.revokeObjectURL(imgUrl);
            
            // Conversion en WebP (qualité 85%)
            const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/webp', 0.85));
            fileToSend = new File([blob], file.name.replace(/\.[^/.]+$/, "") + ".webp", { type: 'image/webp' });
            hasLocalProcessing = true;
            
            if (onProgress) onProgress("Image optimisée, préparation de l'envoi...", 30);
        }
        // B. TRAITEMENT CLIENT DES PDF (Génération de la miniature)
        else if (type === 'pdf' && file.type === 'application/pdf' && window.pdfjsLib) {
            if (onProgress) onProgress("Génération de l'aperçu PDF en cours...", 10);

            const arrayBuffer = await file.arrayBuffer();
            const pdf = await window.pdfjsLib.getDocument({ data: arrayBuffer }).promise;
            const page = await pdf.getPage(1);
            
            // Échelle 1.5 offre un bon équilibre entre lisibilité de l'aperçu et poids final
            const viewport = page.getViewport({ scale: 1.5 });
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');
            canvas.width = viewport.width;
            canvas.height = viewport.height;
            
            await page.render({ canvasContext: ctx, viewport: viewport }).promise;
            
            // Conversion de l'aperçu en WebP (qualité 80%)
            previewBlob = await new Promise(resolve => canvas.toBlob(resolve, 'image/webp', 0.8));
            hasLocalProcessing = true;
            
            if (onProgress) onProgress("Aperçu généré, préparation de l'envoi...", 30);
        }
    } catch (e) {
        console.error("Erreur lors du traitement local du fichier :", e);
        window.showToast("L'optimisation locale a échoué. Le fichier sera envoyé tel quel.", "error");
        hasLocalProcessing = false;
    }

    return new Promise((resolve, reject) => {
        const formData = new FormData();
        formData.append('file', fileToSend);
        formData.append('type', type);
        
        if (previewBlob) {
            formData.append('preview', previewBlob, 'preview.webp');
        }

        const xhr = new XMLHttpRequest();
        xhr.open('POST', window.ECO_CONFIG.apiBaseUrl + 'file/upload', true);
        
        xhr.upload.onprogress = (e) => {
            if (e.lengthComputable) {
                const base = hasLocalProcessing ? 30 : 0;
                const multiplier = hasLocalProcessing ? 70 : 100;
                const percent = Math.round(base + ((e.loaded / e.total) * multiplier));
                
                if (onProgress) {
                    onProgress(`Envoi au serveur : ${Math.round((e.loaded / e.total) * 100)}%`, percent);
                }
            }
        };

        xhr.onload = () => {
            if (xhr.status >= 200 && xhr.status < 300) {
                try {
                    const res = JSON.parse(xhr.responseText);
                    if (res.status === 'success') {
                        if (onProgress) onProgress("Traitement terminé !", 100);
                        resolve(res.data.id);
                    } else {
                        reject(res.message || 'Erreur upload serveur');
                    }
                } catch (err) { 
                    reject('Réponse invalide du serveur'); 
                }
            } else {
                reject('Erreur HTTP ' + xhr.status);
            }
        };
        
        xhr.onerror = () => reject("Erreur réseau (déconnexion ou blocage)");
        xhr.send(formData);
    });
};

// =========================================================================
// 3. BIBLIOTHÈQUE D'ICÔNES VECTORIELLES (SVG) - EXHAUSTIVE POUR ÉVITER LES CRASHS
// =========================================================================

const createIcon = (svgPaths) => ({ size = 24, className = '', ...props }) => (
    <svg xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className} {...props}>
        {svgPaths}
    </svg>
);

window.Home = createIcon(<><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></>);
window.Users = createIcon(<><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></>);
window.User = createIcon(<><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></>);
window.Building = createIcon(<><rect x="4" y="2" width="16" height="20" rx="2" ry="2"></rect><path d="M9 22v-4h6v4"></path><path d="M8 6h.01"></path><path d="M16 6h.01"></path><path d="M12 6h.01"></path><path d="M12 10h.01"></path><path d="M12 14h.01"></path><path d="M16 10h.01"></path><path d="M16 14h.01"></path><path d="M8 10h.01"></path><path d="M8 14h.01"></path></>);
window.Settings = createIcon(<><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></>);
window.MessageSquare = createIcon(<><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></>);
window.Package = createIcon(<><line x1="16.5" y1="9.4" x2="7.5" y2="4.21"></line><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path><polyline points="3.27 6.96 12 12.01 20.73 6.96"></polyline><line x1="12" y1="22.08" x2="12" y2="12"></line></>);
window.FileText = createIcon(<><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></>);
window.Activity = createIcon(<><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline></>);
window.Eye = createIcon(<><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></>);
window.Terminal = createIcon(<><polyline points="4 17 10 11 4 5"></polyline><line x1="12" y1="19" x2="20" y2="19"></line></>);
window.Archive = createIcon(<><polyline points="21 8 21 21 3 21 3 8"></polyline><rect x="1" y="3" width="22" height="5"></rect><line x1="10" y1="12" x2="14" y2="12"></line></>);
window.Save = createIcon(<><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path><polyline points="17 21 17 13 7 13 7 21"></polyline><polyline points="7 3 7 8 15 8"></polyline></>);
window.Download = createIcon(<><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></>);
window.RefreshCw = createIcon(<><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></>);
window.Trash = createIcon(<><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></>);
window.Trash2 = createIcon(<><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></>);
window.Loader = createIcon(<><line x1="12" y1="2" x2="12" y2="6"></line><line x1="12" y1="18" x2="12" y2="22"></line><line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line><line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line><line x1="2" y1="12" x2="6" y2="12"></line><line x1="18" y1="12" x2="22" y2="12"></line><line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line><line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line></>);
window.ShieldAlert = createIcon(<><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></>);
window.Shield = createIcon(<><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></>);
window.CreditCard = createIcon(<><rect x="1" y="4" width="22" height="16" rx="2" ry="2"></rect><line x1="1" y1="10" x2="23" y2="10"></line></>);
window.AlertTriangle = createIcon(<><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></>);
window.Zap = createIcon(<><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></>);
window.KeyRound = createIcon(<><path d="M2 18v3c0 .6.4 1 1 1h4v-3h3v-3h2l1.4-1.4a6.5 6.5 0 1 0-4-4Z"></path><circle cx="16.5" cy="7.5" r=".5"></circle></>);
window.CheckCircle = createIcon(<><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></>);
window.Check = createIcon(<><polyline points="20 6 9 17 4 12"></polyline></>);
window.Mail = createIcon(<><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline></>);
window.AlertOctagon = createIcon(<><polygon points="7.86 2 16.14 2 22 7.86 22 16.14 16.14 22 7.86 22 2 16.14 2 7.86 7.86 2"></polygon><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></>);
window.Power = createIcon(<><path d="M18.36 6.64a9 9 0 1 1-12.73 0"></path><line x1="12" y1="2" x2="12" y2="12"></line></>);
window.MapPin = createIcon(<><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path><circle cx="12" cy="10" r="3"></circle></>);
window.Edit = createIcon(<><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></>);
window.Plus = createIcon(<><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></>);
window.ArrowLeft = createIcon(<><line x1="19" y1="12" x2="5" y2="12"></line><polyline points="12 19 5 12 12 5"></polyline></>);
window.X = createIcon(<><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></>);
window.UploadCloud = createIcon(<><polyline points="16 16 12 12 8 16"></polyline><line x1="12" y1="12" x2="12" y2="21"></line><path d="M20.39 18.39A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.3"></path><polyline points="16 16 12 12 8 16"></polyline></>);
window.FileIcon = createIcon(<><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path><polyline points="13 2 13 9 20 9"></polyline></>);
window.ImageIcon = createIcon(<><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><polyline points="21 15 16 10 5 21"></polyline></>);
window.Camera = createIcon(<><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"></path><circle cx="12" cy="13" r="4"></circle></>);
window.Menu = createIcon(<><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></>);
window.LogOut = createIcon(<><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></>);
window.Calendar = createIcon(<><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></>);
window.Clock = createIcon(<><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></>);
window.Phone = createIcon(<><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"></path></>);
window.Link = createIcon(<><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></>);
window.AlignLeft = createIcon(<><line x1="17" y1="10" x2="3" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="17" y1="18" x2="3" y2="18"></line></>);
window.Palette = createIcon(<><path d="M12 2a10 10 0 1 0 10 10 1 1 0 0 0-1-1h-2a1 1 0 0 1-1-1V8a1 1 0 0 1 1-1h.5"></path><circle cx="7.5" cy="10.5" r="1.5"></circle><circle cx="10.5" cy="7.5" r="1.5"></circle><circle cx="14.5" cy="7.5" r="1.5"></circle><circle cx="17.5" cy="10.5" r="1.5"></circle></>);
window.Info = createIcon(<><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></>);
window.ChevronRight = createIcon(<><polyline points="9 18 15 12 9 6"></polyline></>);
window.ChevronDown = createIcon(<><polyline points="6 9 12 15 18 9"></polyline></>);
window.Search = createIcon(<><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></>);
window.Bell = createIcon(<><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path><path d="M13.73 21a2 2 0 0 1-3.46 0"></path></>);
window.Lock = createIcon(<><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></>);
window.Send = createIcon(<><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></>);

// =========================================================================
// 4. COMPOSANTS DE PRÉSENTATION
// =========================================================================

window.StatCard = ({ title, value, icon, color, bg, onClick }) => (
    <div onClick={onClick} className={`bg-white rounded-3xl p-6 shadow-sm border border-slate-100 flex items-center gap-4 ${onClick ? 'cursor-pointer hover:shadow-md transition hover:border-slate-300' : ''}`}>
        <div className={`w-14 h-14 rounded-2xl flex items-center justify-center shrink-0 ${bg} ${color}`}>{icon}</div>
        <div className="min-w-0">
            <p className="text-sm font-bold text-slate-500 uppercase tracking-widest truncate">{title}</p>
            <p className="text-2xl font-black text-slate-800 mt-1">{value}</p>
        </div>
    </div>
);

window.PriceInput = ({ label, value, onChange }) => (
    <div className="p-4 bg-slate-50 border border-slate-100 rounded-xl">
        <label className="text-xs font-bold text-slate-700 uppercase block mb-2">{label}</label>
        <div className="relative">
            <input type="number" step="1" min="0" value={value} onChange={e => onChange(parseInt(e.target.value) || 0)} className="w-full border-2 border-slate-200 rounded-xl p-2.5 pr-8 text-sm focus:border-emerald-500 outline-none transition font-bold text-slate-800" />
            <span className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 font-bold">€</span>
        </div>
    </div>
);

// =========================================================================
// 5. COMPOSANTS MODALES
// =========================================================================

window.Modal = ({ title, onClose, children, preventClose = false, zIndex = "z-[100]" }) => {
    useEffect(() => {
        const handleEsc = (e) => { if (e.key === 'Escape' && !preventClose) onClose(); };
        window.addEventListener('keydown', handleEsc);
        return () => window.removeEventListener('keydown', handleEsc);
    }, [onClose, preventClose]);

    return (
        <div className={`fixed inset-0 bg-slate-900/80 backdrop-blur-sm ${zIndex} flex items-center justify-center p-4 animate-in fade-in duration-200`} onMouseDown={(e) => { if (e.target === e.currentTarget && !preventClose) onClose(); }}>
            <div className="bg-white rounded-3xl shadow-2xl w-full max-w-lg overflow-hidden flex flex-col max-h-[90vh] animate-in zoom-in-95 duration-200" onMouseDown={e => e.stopPropagation()}>
                <div className="p-5 border-b border-slate-100 flex justify-between items-center bg-slate-50/50 shrink-0">
                    <h3 className="font-black text-slate-800 text-lg pr-4">{title}</h3>
                    {!preventClose && (
                        <button onClick={onClose} className="p-2 text-slate-400 hover:text-red-500 hover:bg-red-50 rounded-xl transition shrink-0">
                            <window.X size={20} />
                        </button>
                    )}
                </div>
                <div className="p-5 sm:p-6 overflow-y-auto custom-scrollbar flex-1 relative">
                    {children}
                </div>
            </div>
        </div>
    );
};

window.ConfirmModal = ({ title, message, confirmText = "Confirmer", cancelText = "Annuler", isDestructive = false, onConfirm, onCancel, zIndex = "z-[200]" }) => {
    const [isProcessing, setIsProcessing] = useState(false);
    return (
        <window.Modal title={title} onClose={onCancel} preventClose={isProcessing} zIndex={zIndex}>
            <div className="space-y-6">
                <p className="text-slate-600 font-medium leading-relaxed">{message}</p>
                <div className="flex gap-3 pt-2">
                    <button onClick={onCancel} disabled={isProcessing} className="flex-1 py-3 bg-slate-100 text-slate-700 font-bold rounded-xl hover:bg-slate-200 transition disabled:opacity-50">
                        {cancelText}
                    </button>
                    <button 
                        onClick={async () => { setIsProcessing(true); await onConfirm(); }} 
                        disabled={isProcessing} 
                        className={`flex-1 py-3 text-white font-black uppercase tracking-widest rounded-xl transition flex justify-center items-center gap-2 shadow-lg disabled:opacity-50 ${isDestructive ? 'bg-red-600 hover:bg-red-700' : 'bg-emerald-600 hover:bg-emerald-700'}`}
                    >
                        {isProcessing ? <window.Loader className="animate-spin" size={18}/> : confirmText}
                    </button>
                </div>
            </div>
        </window.Modal>
    );
};

window.PromptModal = ({ title, message, placeholder = "", confirmText = "Valider", cancelText = "Annuler", onConfirm, onCancel, zIndex = "z-[200]" }) => {
    const [val, setVal] = useState('');
    const [isProcessing, setIsProcessing] = useState(false);
    
    return (
        <window.Modal title={title} onClose={onCancel} preventClose={isProcessing} zIndex={zIndex}>
            <form onSubmit={async (e) => { e.preventDefault(); setIsProcessing(true); await onConfirm(val); }} className="space-y-6">
                <p className="text-slate-600 font-medium leading-relaxed">{message}</p>
                <input 
                    type="text" 
                    value={val} 
                    onChange={e => setVal(e.target.value)} 
                    placeholder={placeholder} 
                    className="w-full border-2 border-slate-200 rounded-xl p-3 focus:border-emerald-500 outline-none transition font-bold" 
                    autoFocus 
                    required 
                />
                <div className="flex gap-3 pt-2">
                    <button type="button" onClick={onCancel} disabled={isProcessing} className="flex-1 py-3 bg-slate-100 text-slate-700 font-bold rounded-xl hover:bg-slate-200 transition disabled:opacity-50">
                        {cancelText}
                    </button>
                    <button type="submit" disabled={isProcessing || !val.trim()} className="flex-1 py-3 bg-emerald-600 text-white font-black uppercase tracking-widest rounded-xl hover:bg-emerald-700 transition flex justify-center items-center gap-2 shadow-lg disabled:opacity-50">
                        {isProcessing ? <window.Loader className="animate-spin" size={18}/> : confirmText}
                    </button>
                </div>
            </form>
        </window.Modal>
    );
};

// =========================================================================
// 6. LAYOUT PRINCIPAL (SHELL)
// =========================================================================

window.DashboardLayout = ({ role, user, theme = 'emerald', navItems, activeTab, setActiveTab, onLogout, children }) => {
    const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
    
    const themeConfig = {
        emerald: { bg: 'bg-emerald-900', hover: 'hover:bg-emerald-800', active: 'bg-emerald-800 text-white border-emerald-400', text: 'text-emerald-100', border: 'border-emerald-800' },
        purple: { bg: 'bg-slate-900', hover: 'hover:bg-slate-800', active: 'bg-purple-600 text-white border-purple-400', text: 'text-slate-300', border: 'border-slate-800' },
        blue: { bg: 'bg-blue-900', hover: 'hover:bg-blue-800', active: 'bg-blue-800 text-white border-blue-400', text: 'text-blue-100', border: 'border-blue-800' }
    }[theme];

    return (
        <div className="flex h-screen bg-slate-50 overflow-hidden">
            {/* Sidebar Desktop */}
            <aside className={`hidden md:flex w-72 flex-col ${themeConfig.bg} text-white shadow-2xl z-20`}>
                <div className={`p-6 border-b ${themeConfig.border} flex items-center gap-4`}>
                    <div className="w-12 h-12 bg-white rounded-2xl flex items-center justify-center shrink-0 shadow-inner">
                        <img src="favicon.svg" alt="Logo" className="w-8 h-8" />
                    </div>
                    <div className="min-w-0">
                        <h2 className="font-black text-lg truncate uppercase tracking-widest">{role}</h2>
                        <p className={`text-xs ${themeConfig.text} truncate`}>{user}</p>
                    </div>
                </div>
                
                <nav className="flex-1 p-4 space-y-2 overflow-y-auto custom-scrollbar">
                    {navItems.map(item => (
                        <button 
                            key={item.id} 
                            onClick={() => setActiveTab(item.id)} 
                            className={`w-full flex items-center gap-3 px-4 py-3.5 rounded-2xl transition font-bold border ${activeTab === item.id ? themeConfig.active : `border-transparent ${themeConfig.text} ${themeConfig.hover}`}`}
                        >
                            <span className={activeTab === item.id ? 'scale-110 transition-transform' : ''}>{item.icon}</span>
                            <span>{item.label}</span>
                            {item.badge > 0 && (
                                <span className={`ml-auto text-[10px] px-2 py-0.5 rounded-full ${activeTab === item.id ? 'bg-white text-slate-900' : 'bg-red-500 text-white'}`}>
                                    {item.badge}
                                </span>
                            )}
                        </button>
                    ))}
                </nav>
                
                <div className={`p-4 border-t ${themeConfig.border}`}>
                    <button onClick={onLogout} className={`w-full flex items-center gap-3 px-4 py-3 rounded-2xl ${themeConfig.text} ${themeConfig.hover} transition font-bold`}>
                        <window.LogOut size={18} /> Déconnexion
                    </button>
                </div>
            </aside>

            {/* Header & Menu Mobile */}
            <div className="flex-1 flex flex-col h-screen overflow-hidden relative">
                <header className="md:hidden bg-white h-16 border-b border-slate-200 flex items-center justify-between px-4 shrink-0 z-30 shadow-sm">
                    <div className="flex items-center gap-3">
                        <img src="favicon.svg" alt="Logo" className="w-8 h-8" />
                        <div>
                            <p className="font-black text-slate-800 text-sm uppercase tracking-widest">{role}</p>
                            <p className="text-[10px] font-bold text-slate-500 truncate max-w-[150px]">{user}</p>
                        </div>
                    </div>
                    <button onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)} className="p-2 text-slate-600 bg-slate-100 rounded-xl">
                        {isMobileMenuOpen ? <window.X size={24} /> : <window.Menu size={24} />}
                    </button>
                </header>

                {/* Overlay Menu Mobile */}
                {isMobileMenuOpen && (
                    <div className="md:hidden fixed inset-0 top-16 z-30 bg-slate-900/95 backdrop-blur-sm animate-in fade-in flex flex-col">
                        <nav className="flex-1 p-4 space-y-2 overflow-y-auto">
                            {navItems.map(item => (
                                <button 
                                    key={item.id} 
                                    onClick={() => { setActiveTab(item.id); setIsMobileMenuOpen(false); }} 
                                    className={`w-full flex items-center gap-3 px-4 py-4 rounded-2xl transition font-bold text-left ${activeTab === item.id ? 'bg-emerald-500 text-white' : 'text-slate-300 hover:bg-slate-800'}`}
                                >
                                    {item.icon} <span>{item.label}</span>
                                    {item.badge > 0 && <span className="ml-auto bg-red-500 text-white text-[10px] px-2 py-0.5 rounded-full">{item.badge}</span>}
                                </button>
                            ))}
                        </nav>
                        <div className="p-6 border-t border-slate-800">
                            <button onClick={onLogout} className="w-full flex items-center justify-center gap-2 px-4 py-4 rounded-2xl text-red-400 bg-red-500/10 font-bold">
                                <window.LogOut size={20} /> Déconnexion
                            </button>
                        </div>
                    </div>
                )}

                {/* Contenu Principal */}
                <main className="flex-1 overflow-y-auto p-4 sm:p-6 lg:p-8 custom-scrollbar scroll-smooth">
                    {children}
                </main>
            </div>
        </div>
    );
};

/* EOF ========== [_www/_react/_socle_ui.jsx] */