// ECO-PANNEAU.FR - _react/admin/_admin.jsx window.pano_AdminView = ({ data, refreshData, initialTab }) => { const { useState, useEffect, useMemo } = React; // ZÉRO-DETTE : Utilisation du Hook global ! const { isMounted, safeFetch } = window.pano_useSafeFetch(); const urlModal = window.pano_useUrlModal ? window.pano_useUrlModal() : {}; const activeTab = urlModal.currentTab || initialTab || 'dashboard'; const setActiveTab = urlModal.changeTab; const { activeModal, activeDialog, activeChat, targetId, dialogId, openModal, openDialog, closeCurrentLayer, replaceCurrentLayer, openChat } = urlModal; const adminData = data.me || data.myAdminData || data.myClientData || data.adminData || {}; const [adminOpts, setAdminOpts] = useState(() => { try { const rawMode = adminData.ui_mode || adminData.uiMode; return rawMode ? JSON.parse(rawMode) : {}; } catch (e) { if (window.pano_logFallback) window.pano_logFallback("Erreur de parsing des options Admin (ui_mode)."); return {}; } }); useEffect(() => { try { const rawMode = adminData.ui_mode || adminData.uiMode; if (rawMode) { setAdminOpts(JSON.parse(rawMode)); } } catch(e) {} }, [adminData.ui_mode, adminData.uiMode]); const updateAdminOpts = async (newOpts) => { setAdminOpts(newOpts); const payload = { ...adminData, name: adminData.name || 'Système', full_name: adminData.full_name || adminData.fullName || 'Système', uiMode: JSON.stringify(newOpts) }; await safeFetch('clients/profile/update', { body: payload, silent: true }); if (!isMounted.current) return; if (refreshData) refreshData(); }; const toggleDashboardPin = (listKey, id, e) => { if (e) { e.preventDefault(); e.stopPropagation(); } const currentArr = adminOpts[listKey] || []; const newArr = currentArr.includes(id) ? currentArr.filter(x => x !== id) : [...currentArr, id]; const newOpts = { ...adminOpts, [listKey]: newArr }; updateAdminOpts(newOpts); }; const [managingPanneau, setManagingPanneau] = useState(null); const [editingEntity, setEditingEntity] = useState(null); const [draggedItem, setDraggedItem] = useState(null); const [isSaving, setIsSaving] = useState(false); const [validationErrors, setValidationErrors] = useState([]); const [previewPanneau, setPreviewPanneau] = useState(null); const [confirmConfig, setConfirmConfig] = useState(null); // ÉTAT CENTRALISÉ DE LA TÉLÉMÉTRIE const [sysSummary, setSysSummary] = useState({ loaded: false, diagStatus: 'emerald', backupStatus: 'emerald', logStatus: 'emerald', queueStatus: 'success', php: '', ram: '', db: '', disk: '', backupsCount: 0, logDesc: '', backupDesc: '' }); const icons = useMemo(() => window.pano_getIcons(), []); const { ActivityIcon, BuildingIcon, PackageIcon, UsersIcon, CreditCardIcon, MessageSquareIcon, SettingsIcon, ShieldAlertIcon, ServerIcon, RefreshCwIcon, SlidersHorizontalIcon } = icons; const comps = useMemo(() => window.pano_getComponents(), []); const { AdminDashboardTab, AdminPanelsTab, AdminLogisticsTab, AdminClientsTab, AdminInvoicesTab, AdminMessagesTab, AdminSettingsTab, AdminReglesTab, AdminCyberdefenseTab, DashboardLayout, PanneauEditorForm, EntityEditorModal, PreviewModal, ConfirmModal, GlobalChatModal, Button, Modal, AdminModalsSystem, AdminModalsBackups } = comps; const panneaux = data.panneaux || []; const interactions = data.interactions || []; const unseenPanels = panneaux.filter(p => p.id !== 'demo-panneau' && p.status !== 'Brouillon' && ((!p.admin_seen && p.status === 'Actif') || p.status === 'Attente validation') ).length; const pendingShipping = panneaux.filter(p => p.physicalPanels > 0 && p.status !== 'Brouillon' && p.id !== 'demo-panneau' && ['En attente de validation', 'Maquette refusée', 'En attente de commande au fournisseur', "En attente d'impression"].includes(p.shipping_status) ).length; const unreadMessages = interactions.filter(m => m.isAlert === 2 || (!m.resolved && m.authorType !== 'Admin' && m.authorType !== 'Systeme' && m.authorType !== 'System') ).length; const navItems = [ { id: 'dashboard', label: 'Supervision', icon: }, { id: 'panneaux', label: 'Parc de panneaux', icon: , badge: unseenPanels }, { id: 'logistique', label: 'Logistique (A1)', icon: , badge: pendingShipping }, { id: 'clients', label: 'Base clients', icon: }, { id: 'factures', label: 'Facturation globale', icon: }, { id: 'messages', label: 'Messagerie globale', icon: , badge: unreadMessages }, { id: 'systeme', label: 'Paramètres système', icon: }, { id: 'regles', label: 'Règles et Options', icon: }, { id: 'cyberdefense', label: 'Cyberdéfense', icon: } ]; const handleLogout = async () => { await safeFetch('auth/logout', { method: 'GET', silent: true }); window.location.href = '?'; }; // MOTEUR CENTRAL DE TÉLÉMÉTRIE const fetchTelemetry = async (silent = false) => { try { const [diagRes, backRes, logRes] = await Promise.all([ safeFetch('system/diagnostics', { method: 'GET', silent: true }), safeFetch('system/backups', { method: 'GET', silent: true }), safeFetch('system/logs', { method: 'GET', silent: true }) ]); if (!isMounted.current) return; if (diagRes && diagRes.status === 'success') { const dbDetail = diagRes.data.find(d => d.id === 'db')?.detail || '0 ms'; const diskDetail = diagRes.data.find(d => d.id === 'disk')?.detail || '0 GB'; const queueStatus = diagRes.data.find(d => d.id === 'queue')?.status || 'success'; const dbVal = parseFloat(dbDetail); const diskVal = parseFloat(diskDetail); let diagStatus = 'emerald'; if (dbVal > 500 || diskVal < 1 || queueStatus === 'error') diagStatus = 'red'; else if (dbVal > 200 || diskVal < 5 || queueStatus === 'warning') diagStatus = 'amber'; else if (queueStatus === 'info') diagStatus = 'blue'; const backupsData = (backRes && backRes.data) ? backRes.data : []; const backupsCount = backupsData.length; let backupStatus = 'emerald'; let backupDesc = window.pano_formatPlural(backupsCount, "archive", "archives"); if (backupsCount === 0) { backupStatus = 'red'; backupDesc = "Alerte : Aucune sauvegarde"; } else { const latestBackup = backupsData.reduce((max, b) => b.date > max.date ? b : max, backupsData[0]); const hoursSinceLast = (Date.now() - latestBackup.date) / (1000 * 60 * 60); if (hoursSinceLast > 48) { const delayDays = Math.floor(hoursSinceLast/24); backupStatus = 'red'; backupDesc = `Critique : Retard de ${window.pano_formatPlural(delayDays, "jour", "jours")}`; } else if (hoursSinceLast > 24) { backupStatus = 'amber'; backupDesc = `Attention : Retard de ${Math.floor(hoursSinceLast)}h`; } else { backupStatus = 'emerald'; backupDesc = `À jour (${window.pano_formatPlural(backupsCount, "archive", "archives")})`; } } const logsContent = (logRes && logRes.data?.logs) ? logRes.data.logs : ''; let logStatus = 'emerald'; let logDesc = 'Journal vierge (sain)'; const lowerLogs = logsContent.toLowerCase(); if (lowerLogs.includes('fatal error') || lowerLogs.includes('exception')) { logStatus = 'red'; logDesc = 'Erreurs critiques détectées !'; } else if (logsContent.length > 5) { logStatus = 'amber'; logDesc = 'Avertissements enregistrés'; } setSysSummary({ loaded: true, diagStatus, backupStatus, logStatus, queueStatus, php: diagRes.data.find(d => d.id === 'php')?.detail || 'OK', ram: diagRes.data.find(d => d.id === 'ram')?.detail || 'OK', db: dbDetail, disk: diskDetail, backupsCount, logDesc, backupDesc }); } } catch(e) {} }; // BOUCLE DE POLLING CENTRALISÉE useEffect(() => { fetchTelemetry(); let telemetryInterval; let currentDelay = 10000; let lastActivityTime = Date.now(); let isTabVisible = !document.hidden; const performTelemetrySync = async () => { if (!isTabVisible) return; await fetchTelemetry(true); if (!isMounted.current) return; const idleTime = Date.now() - lastActivityTime; if (idleTime > 120000) currentDelay = 60000; else if (idleTime > 30000) currentDelay = 30000; else currentDelay = 10000; scheduleNextPoll(currentDelay); }; const scheduleNextPoll = (delay) => { clearTimeout(telemetryInterval); if (isTabVisible) telemetryInterval = setTimeout(performTelemetrySync, delay); }; const handleVisibilityChange = () => { isTabVisible = !document.hidden; if (isTabVisible) { lastActivityTime = Date.now(); currentDelay = 10000; performTelemetrySync(); } else { clearTimeout(telemetryInterval); } }; const handleUserInteraction = () => { if (currentDelay > 10000) { lastActivityTime = Date.now(); currentDelay = 10000; scheduleNextPoll(10000); } else { lastActivityTime = Date.now(); } }; document.addEventListener('visibilitychange', handleVisibilityChange); document.addEventListener('click', handleUserInteraction); document.addEventListener('keydown', handleUserInteraction); scheduleNextPoll(currentDelay); return () => { clearTimeout(telemetryInterval); document.removeEventListener('visibilitychange', handleVisibilityChange); document.removeEventListener('click', handleUserInteraction); document.removeEventListener('keydown', handleUserInteraction); }; }, []); const adminSettings = data.settings || {}; // CORRECTION: Validation souple des valeurs numériques ou string "0"/"1" const isBillingComplete = !!( adminSettings.billing_company?.trim() && adminSettings.billing_address?.trim() && adminSettings.billing_siret?.trim() && (adminSettings.billing_has_tva == 0 || adminSettings.billing_tva?.trim()) ); const isMaintenance = adminSettings.maintenance == 1; const purchasesAllowed = adminSettings.allow_new_purchases != 0; let sysStatusColor = 'bg-emerald-500 shadow-[0_0_8px_rgba(16,185,129,0.8)]'; if (isMaintenance || !isBillingComplete || sysSummary.diagStatus === 'red' || sysSummary.backupStatus === 'red' || sysSummary.logStatus === 'red') { sysStatusColor = 'bg-red-500 shadow-[0_0_8px_rgba(239,68,68,0.8)]'; } else if (!purchasesAllowed || sysSummary.diagStatus === 'amber' || sysSummary.backupStatus === 'amber' || sysSummary.logStatus === 'amber') { sysStatusColor = 'bg-amber-500 shadow-[0_0_8px_rgba(245,158,11,0.8)]'; } else if (sysSummary.diagStatus === 'blue') { sysStatusColor = 'bg-blue-500 shadow-[0_0_8px_rgba(59,130,246,0.8)]'; } const sidebarFooter = (
openModal('diagnostics', null, false)} className="flex justify-between items-center bg-slate-800 hover:bg-slate-700 text-white rounded-xl p-3 cursor-pointer transition w-full group min-w-0" title="Diagnostics système et Maintenance">
État système
); const handleCreateNewPanel = async () => { setIsSaving(true); let idReservé = null; let maxTries = 5; let serverData = {}; while (!idReservé && maxTries > 0) { if (!isMounted.current) break; const tempId = 'pan_' + window.pano_generateID().replace(/-/g, '').substring(0, 12); try { const d = await safeFetch('panneaux/reserve_id', { body: { id: tempId }, silent: true }); if (d && d.status === 'success') { idReservé = tempId; serverData = d.data || {}; } } catch(e) { if (window.pano_logFallback) window.pano_logFallback(`Erreur lors de la réservation ID: ${e.message}`); } maxTries--; } if (!isMounted.current) return; setIsSaving(false); if (idReservé) { setManagingPanneau({ ...serverData, id: idReservé, name: '', location: '', permitNumber: '', status: 'Réservé', offerType: 'rental', currentRate: data.prices?.rentalMo ?? 180, physicalPanels: 0, hasNoAds: false, privateDocs: [], shippingAddress: '', client_uid: '' // Côté admin, le client_uid doit être attribué via l'interface }); setValidationErrors([]); openModal('editor', idReservé, false); } else { if (window.pano_showToast) window.pano_showToast("Erreur lors de la réservation de l'identifiant. Veuillez réessayer.", "error"); } }; const demoPanneau = panneaux.find(p => p.id === 'demo-panneau') || { id: 'demo-panneau', status: 'Actif', offerType: 'demo', name: 'Panneau de démonstration', location: 'Paris', themeColor: '#059669', hasNoAds: false }; const panneauCible = managingPanneau || (targetId ? (panneaux.find(p => p.id === targetId) || (targetId === 'demo-panneau' ? demoPanneau : null)) : null); const currentTargetForPreview = activeModal === 'preview' || activeModal === 'preview_admin' ? targetId : (activeDialog === 'preview_draft' ? dialogId : targetId); const previewCible = previewPanneau || (currentTargetForPreview ? (panneaux.find(p => p.id === currentTargetForPreview) || (currentTargetForPreview === 'demo-panneau' ? demoPanneau : null)) : null); const saveEditedEntity = () => { const p = panneauCible || {}; const currentData = p.draft_data && Object.keys(p.draft_data).length > 0 ? { ...p, ...p.draft_data } : p; let newInter = [...(currentData.intervenants || [])]; let newLots = JSON.parse(JSON.stringify(currentData.lots || [])); const loc = editingEntity.location; if (loc.type === 'intervenant') { if (loc.index !== undefined) newInter[loc.index] = editingEntity.data; else newInter.push({...editingEntity.data, id: crypto.randomUUID()}); } else if (loc.type === 'lot') { if (loc.index !== undefined) newLots[loc.index] = {...newLots[loc.index], ...editingEntity.data}; else newLots.push({...editingEntity.data, id: crypto.randomUUID(), entreprises: []}); } else if (loc.type === 'entreprise') { if (loc.index !== undefined) newLots[loc.lotIndex].entreprises[loc.index] = editingEntity.data; else newLots[loc.lotIndex].entreprises.push({...editingEntity.data, id: crypto.randomUUID()}); } const isDraftPanel = p.status === 'Brouillon' || p.status === 'Réservé'; if (!isDraftPanel) { setManagingPanneau({ ...p, draft_data: { ...(p.draft_data || {}), intervenants: newInter, lots: newLots } }); } else { setManagingPanneau({ ...p, intervenants: newInter, lots: newLots }); } closeCurrentLayer(); }; const deleteEntity = (loc) => { setConfirmConfig({ title: "Supprimer l'élément", message: "Êtes-vous sûr de vouloir supprimer cet élément de l'équipe du chantier ?", confirmText: "Supprimer", type: 'error', isDestructive: true, onConfirm: () => { const p = panneauCible || {}; const currentData = p.draft_data && Object.keys(p.draft_data).length > 0 ? { ...p, ...p.draft_data } : p; let newInter = [...(currentData.intervenants || [])]; let newLots = JSON.parse(JSON.stringify(currentData.lots || [])); if (loc.type === 'intervenant') newInter.splice(loc.index, 1); else if (loc.type === 'lot') newLots.splice(loc.index, 1); else newLots[loc.lotIndex].entreprises.splice(loc.index, 1); const isDraftPanel = p.status === 'Brouillon' || p.status === 'Réservé'; if (!isDraftPanel) { setManagingPanneau({ ...p, draft_data: { ...(p.draft_data || {}), intervenants: newInter, lots: newLots } }); } else { setManagingPanneau({ ...p, intervenants: newInter, lots: newLots }); } closeCurrentLayer(); } }); openDialog('confirm', null, true); }; const handleDragStart = (e, location) => { setDraggedItem(location); e.dataTransfer.effectAllowed = 'move'; }; const handleDragOver = (e) => { e.preventDefault(); e.dataTransfer.dropEffect = 'move'; }; const handleDrop = (e, dropLocation) => { e.preventDefault(); if (!draggedItem || draggedItem.type !== dropLocation.type) return; if (draggedItem.type === 'entreprise' && draggedItem.lotIndex !== dropLocation.lotIndex) return; const p = panneauCible || {}; const currentData = p.draft_data && Object.keys(p.draft_data).length > 0 ? { ...p, ...p.draft_data } : p; let newInter = [...(currentData.intervenants || [])]; let newLots = JSON.parse(JSON.stringify(currentData.lots || [])); if (draggedItem.type === 'intervenant') { const item = newInter.splice(draggedItem.index, 1)[0]; newInter.splice(dropLocation.index, 0, item); } else if (draggedItem.type === 'lot') { const item = newLots.splice(draggedItem.index, 1)[0]; newLots.splice(dropLocation.index, 0, item); } else if (draggedItem.type === 'entreprise') { const arr = newLots[dropLocation.lotIndex].entreprises; const item = arr.splice(draggedItem.index, 1)[0]; arr.splice(dropLocation.index, 0, item); } const isDraftPanel = p.status === 'Brouillon' || p.status === 'Réservé'; if (!isDraftPanel) { setManagingPanneau({ ...p, draft_data: { ...(p.draft_data || {}), intervenants: newInter, lots: newLots } }); } else { setManagingPanneau({ ...p, intervenants: newInter, lots: newLots }); } setDraggedItem(null); }; const validatePanel = (uiPrefs = {}) => { const p = { ...(panneauCible || {}), ...(panneauCible?.draft_data || {}) }; if (p.id === 'demo-panneau' || p.offerType === 'demo') return []; const errors = []; if (!p.name?.trim()) errors.push("Le nom du chantier est manquant."); if (!p.location?.trim()) errors.push("L'adresse du panneau est manquante."); if (!uiPrefs.simp_opt_hide_legal && !p.pdfId) errors.push("L'arrêté légal (PDF) n'a pas été uploadé."); return errors; }; const handleSavePanneau = async (forceDraft = false, forceReactivate = false, uiPrefs = {}) => { const pCible = panneauCible || {}; if (!pCible.id) return; if (!forceDraft && (pCible.status === 'Actif' || forceReactivate)) { const errs = validatePanel(uiPrefs); if (errs.length > 0) { setValidationErrors(errs); setTimeout(() => { const scrollEls = document.querySelectorAll('.custom-scrollbar'); scrollEls.forEach(el => el.scrollTo({ top: 0, behavior: 'smooth' })); }, 50); return; } } let finalStatus = pCible.status; if (finalStatus === 'Réservé') finalStatus = 'Brouillon'; let detailsToSave; const original = data.panneaux.find(x => x.id === pCible.id) || (pCible.id === 'demo-panneau' ? demoPanneau : pCible); if (forceReactivate || !forceDraft) { finalStatus = 'Actif'; detailsToSave = { ...pCible, ...(pCible.draft_data || {}), draft_data: {} }; detailsToSave.name = detailsToSave.name?.trim() || 'Projet sans nom'; detailsToSave.location = detailsToSave.location?.trim() || ''; } else { if (finalStatus === 'Brouillon') { detailsToSave = { ...pCible, draft_data: {} }; detailsToSave.name = detailsToSave.name?.trim() || 'Brouillon sans nom'; detailsToSave.location = detailsToSave.location?.trim() || ''; } else { const newDraftData = pCible.draft_data ? { ...pCible.draft_data } : {}; if (Object.keys(newDraftData).length > 0) { newDraftData.name = newDraftData.name?.trim() || 'Projet sans nom'; newDraftData.location = newDraftData.location?.trim() || ''; } detailsToSave = { ...original, draft_data: newDraftData }; } } setIsSaving(true); // --- JUST-IN-TIME FETCH (JIT) POUR ÉRADIQUER LE CONFLIT D'ÉDITION --- let freshOriginal = null; if (pCible.id !== 'demo-panneau') { try { const dSync = await safeFetch('sync', { method: 'GET', silent: true }); if (dSync && dSync.data && dSync.data.panneaux) { freshOriginal = dSync.data.panneaux.find(x => x.id === pCible.id); } } catch(e) {} } const originalToUse = freshOriginal || original; // --- ANTI-CONFLIT D'ÉDITION : Fusion des métadonnées serveurs ultra-fraîches --- if (originalToUse) { Object.assign(detailsToSave, { updated_at: originalToUse.updated_at, shipping_status: originalToUse.shipping_status, tracking_number: originalToUse.tracking_number, tracking_link: originalToUse.tracking_link, admin_seen: originalToUse.admin_seen }); } const payload = { id: pCible.id, status: finalStatus, offerType: pCible.offerType, currentRate: pCible.currentRate, physicalPanels: pCible.physicalPanels, details: detailsToSave }; const d = await safeFetch('panneaux', { body: payload, setLoading: setIsSaving, successMessage: forceDraft ? "Brouillon sauvegardé !" : "Panneau publié avec succès !" }); if (!isMounted.current) return; if (d) { setManagingPanneau(null); closeCurrentLayer(); refreshData(); } }; const handleDiscardDraft = async () => { const p = panneauCible || {}; if (!p.id) return; const original = data.panneaux.find(x => x.id === p.id) || (p.id === 'demo-panneau' ? demoPanneau : undefined); if (!original || !original.draft_data || Object.keys(original.draft_data).length === 0) { setManagingPanneau(null); closeCurrentLayer(); return; } const payload = { id: original.id, status: original.status, offerType: original.offerType, currentRate: original.currentRate, physicalPanels: original.physicalPanels, details: { ...original, draft_data: {} } }; const d = await safeFetch('panneaux', { body: payload, setLoading: setIsSaving, successMessage: "Modifications annulées. Version en ligne restaurée." }); if (!isMounted.current) return; if (d) { setManagingPanneau(null); closeCurrentLayer(); if (refreshData) refreshData(); } }; const renderContent = () => { const baseProps = { data, refreshData, activeModal, activeDialog, targetId, dialogId, adminOpts, toggleDashboardPin, updateAdminOpts, openLocalModal: openModal, openLocalDialog: openDialog, closeCurrentLayer, handleCreateNewPanel }; switch(activeTab) { case 'dashboard': return AdminDashboardTab && ; case 'panneaux': return AdminPanelsTab && ; case 'logistique': return AdminLogisticsTab && ; case 'clients': return AdminClientsTab && ; case 'factures': return AdminInvoicesTab && ; case 'messages': return AdminMessagesTab && ; case 'systeme': return AdminSettingsTab && ; case 'regles': return AdminReglesTab && ; case 'cyberdefense': return AdminCyberdefenseTab && ; default: return AdminDashboardTab && ; } }; if (!DashboardLayout) return null; return ( {renderContent()} {GlobalChatModal && ( )} {AdminModalsSystem && ( )} {AdminModalsBackups && ( )} {activeModal === 'editor' && panneauCible && PanneauEditorForm && ( p.id === panneauCible.id) || (panneauCible.id === 'demo-panneau' ? demoPanneau : undefined)} onCancel={() => { setManagingPanneau(null); closeCurrentLayer(); }} onSaveDraft={() => handleSavePanneau(true)} onPublish={(uiPrefs) => { const errs = validatePanel(uiPrefs); if (errs.length > 0) { setValidationErrors(errs); setTimeout(() => { const scrollEls = document.querySelectorAll('.custom-scrollbar'); scrollEls.forEach(el => el.scrollTo({ top: 0, behavior: 'smooth' })); }, 50); } else { setValidationErrors([]); handleSavePanneau(false, false, uiPrefs); } }} onDiscardDraft={handleDiscardDraft} onPreview={(draftPanel) => { setPreviewPanneau(draftPanel); openDialog('preview_draft', null, true); }} onEditEntity={(loc, eData) => { setEditingEntity({location: loc, data: eData}); openDialog('entity_editor', null, true); }} draggedItem={draggedItem} handleDragStart={handleDragStart} handleDragOver={handleDragOver} handleDrop={handleDrop} deleteEntity={deleteEntity} isSaving={isSaving} validationErrors={validationErrors} currentUserRole="admin" /> )} {activeDialog === 'entity_editor' && editingEntity && EntityEditorModal && ( )} {(activeModal === 'preview' || activeModal === 'preview_admin' || activeDialog === 'preview_draft') && previewCible && PreviewModal && ( { setPreviewPanneau(null); closeCurrentLayer(); }} refreshData={refreshData} isAdmin={true} onValidate={async () => { const d = await safeFetch('panneaux/mark_seen', { body: { id: previewCible.id }, successMessage: "Panneau validé." }); if (!isMounted.current) return; if(d) { closeCurrentLayer(); refreshData(); } }} onContactClient={() => { if (openChat) openChat('SUPPORT_' + (previewCible.client_uid || previewCible.clientName), false); }} onSuspend={() => { openDialog('suspend', previewCible.id, true); }} onDelete={() => { openDialog('delete_panel', previewCible.id, true); }} /> )} {activeDialog === 'confirm' && confirmConfig && ConfirmModal && } ); }; /* EOF ========== [_react/admin/_admin.jsx] */