// ECO-PANNEAU.FR - _react/admin/_admin_panneaux.jsx // 1. - COMPOSANT CARTE ISOLÉ ET RÉUTILISABLE ADMINISTRATEUR window.pano_AdminPanelCard = ({ panel: p, clients, isPinned, toggleDashboardPin, unreadSupport, needsControl, hasBeenSuspended, isViolation, detected, setPreviewPanneau, refreshData, setManagingPanneau, setValidationErrors, openModal, openLocalModal, openDialog, openLocalDialog, closeCurrentLayer, openChat, activeDialog, dialogId, data }) => { const { useState } = React; const { isMounted, safeFetch } = window.pano_useSafeFetch(); const [isSaving, setIsSaving] = useState(false); const [suspendReason, setSuspendReason] = useState(""); const [adminPassword, setAdminPassword] = useState(""); const [deletePassword, setDeletePassword] = useState(""); const [deleteAesKey, setDeleteAesKey] = useState(""); const [deleteReason, setDeleteReason] = useState(""); const { DataCard, IconBadge, StatusBadge, Button, NotificationBadge, Modal, FormInput } = window.pano_getComponents(); const { BuildingIcon, PinIcon, QrCodeIcon, EyeIcon, AlertTriangleIcon, CheckCircleIcon, MessageSquareIcon, UserIcon, LockIcon, Trash2Icon, PowerIcon } = window.pano_getIcons(); const handleOpenModal = (n, id, s) => { if (openModal) openModal(n, id, s); else if (openLocalModal) openLocalModal(n, id, s); }; const handleOpenDialog = (n, id, s) => { if (openDialog) openDialog(n, id, s); else if (openLocalDialog) openLocalDialog(n, id, s); }; const handleCloseLayer = () => { if (closeCurrentLayer) closeCurrentLayer(); }; const handleOpenChat = (id, s) => { if (openChat) openChat(id, s); else { const prev = window.history.state || { panoStack: [], level: 0, currentTab: 'dashboard' }; const newStack = (prev.panoStack || []).filter(l => l.type !== 'chat'); newStack.push({ type: 'chat', name: 'chat', targetId: id }); window.history.pushState({ ...prev, panoStack: newStack, level: newStack.length }, '', window.location.href); window.dispatchEvent(new Event('pano_stack_sync')); } }; const owner = clients.find(c => c.id === (p.client_uid || p.clientName)); const supportThreadId = 'SUPPORT_' + (p.client_uid || p.clientName); const hasDraft = p.draft_data && Object.keys(p.draft_data).length > 0; const adminSettings = data?.settings || {}; const limitStrikes = parseInt(adminSettings.limit_strikes_suspend || 5, 10); const suspendCount = p.suspend_count || 0; const canHardDelete = suspendCount >= limitStrikes; let cardVariant = 'default'; if (isViolation) cardVariant = 'danger'; else if (p.status === 'Attente validation') cardVariant = 'warning'; else if (p.status === 'Suspendu') cardVariant = 'warning'; const handleValidatePanel = async () => { const d = await safeFetch('panneaux/mark_seen', { body: { id: p.id }, setLoading: setIsSaving, successMessage: "Panneau validé et activé." }); if (!isMounted.current) return; if (d && refreshData) refreshData(); }; const handleSuspendSubmit = async (e) => { e.preventDefault(); if (!suspendReason.trim() || !adminPassword) return; const d1 = await safeFetch('panneaux/status', { body: { id: p.id, status: 'Suspendu', password: adminPassword, reason: suspendReason }, setLoading: setIsSaving }); if (!isMounted.current) return; if (d1) { const message = `Le panneau "${p.name}" a été suspendu ou rejeté car il nécessite une correction.\n\nMotif :\n${suspendReason}\n\nVeuillez apporter les corrections nécessaires pour pouvoir le réactiver (statut "Actif").\n\nVous trouverez en pièce jointe de l'e-mail qui vient de vous être envoyé, un rappel de nos Conditions Générales de Vente (CGV).`; await safeFetch('interactions', { body: { panneauId: supportThreadId, detail: message, author: 'Admin', targetEmail: 'Client', type: 'message' } }); if (!isMounted.current) return; if (refreshData) refreshData(); handleCloseLayer(); setTimeout(() => { if (isMounted.current) { setSuspendReason(""); setAdminPassword(""); } }, 300); } }; const handleDeleteSubmit = async (e) => { e.preventDefault(); if (!deletePassword || !deleteAesKey || !deleteReason.trim()) return; const needsDOE = p.status === 'Actif' || p.status === 'Suppression programmée' || p.status === 'Suspendu'; if (needsDOE) { const d = await safeFetch('archives/doe', { body: { id: p.id, admin_delete_reason: deleteReason, password: deletePassword, aes_key_confirm: deleteAesKey }, setLoading: setIsSaving, successMessage: "Panneau clôturé (D.O.E généré) et client notifié." }); if (!isMounted.current) return; if (d) { if (refreshData) refreshData(); handleCloseLayer(); setTimeout(() => { if (isMounted.current) { setDeletePassword(""); setDeleteAesKey(""); setDeleteReason(""); } }, 300); } } else { const d = await safeFetch('panneaux/delete', { body: { id: p.id, force_immediate: true, password: deletePassword, aes_key_confirm: deleteAesKey, admin_delete_reason: deleteReason }, setLoading: setIsSaving, successMessage: "Panneau supprimé définitivement et client notifié." }); if (!isMounted.current) return; if (d) { if (refreshData) refreshData(); handleCloseLayer(); setTimeout(() => { if (isMounted.current) { setDeletePassword(""); setDeleteAesKey(""); setDeleteReason(""); } }, 300); } } }; return ( <>
{ e.stopPropagation(); toggleDashboardPin && toggleDashboardPin('panels', p.id, e); }} className={`absolute top-2 right-2 z-20 p-1 cursor-pointer transition-all duration-200 ${isPinned ? 'text-purple-500 opacity-100 hover:scale-110' : 'text-slate-300 opacity-0 group-hover:opacity-100 hover:text-slate-500 hover:scale-110'}`} style={{ transform: 'rotate(30deg)' }} title={isPinned ? "Désépingler" : "Épingler au tableau de bord"} >

{p.name || 'Projet sans nom'}

{p.location &&

{p.location}

}

{owner ? owner.name : p.clientName}

{p.status === 'Actif' && hasDraft && ( Modifié )} {p.status !== 'Actif' && }
{needsControl ? ( ) : ( )} {isViolation && ( {canHardDelete && (
{/* Modales Encapsulées */} {activeDialog === 'suspend' && dialogId === p.id && Modal && ( ( <> )} >

Avertissement N°{suspendCount + 1} / {limitStrikes}

Le client recevra un e-mail formel contenant votre motif ainsi que les Conditions Générales de Vente (CGV) en pièce jointe PDF.