// ECO-PANNEAU.FR - _react/admin/_admin_regles.jsx window.pano_AdminReglesTab = ({ data, refreshData, openLocalDialog, closeCurrentLayer, activeDialog }) => { const { useState } = React; // ZÉRO-DETTE : Utilisation de notre Hook abstrait ! const { isMounted, safeFetch } = window.pano_useSafeFetch(); // Routage et modales const { activeDialog: hookActiveDialog, openDialog: hookOpenDialog, closeCurrentLayer: hookCloseLayer } = window.pano_useUrlModal ? window.pano_useUrlModal() : {}; const routerActiveDialog = activeDialog || hookActiveDialog; const routerCloseLayer = closeCurrentLayer || hookCloseLayer; const routerOpenDialog = openLocalDialog || hookOpenDialog; // États const [isSaving, setIsSaving] = useState(false); const [settings, setSettings] = useState(data.settings || {}); const [pwdRequestData, setPwdRequestData] = useState(null); // Composants et Icônes const { SettingsIcon, EyeIcon, SaveIcon, LoaderIcon, DatabaseIcon, ServerIcon, ClockIcon, HardDriveIcon, UploadCloudIcon, Trash2Icon, KeyRoundIcon, RefreshCwIcon, ZapIcon, EditIcon, MessageSquareIcon, ShieldAlertIcon } = window.pano_getIcons(); const { FormInput, PasswordPromptModal, TextLogo, Button, DataCard } = window.pano_getComponents(); const DBIconToUse = DatabaseIcon || ServerIcon || SettingsIcon; const ClockIconToUse = ClockIcon || RefreshCwIcon || ZapIcon; const HardDriveIconToUse = HardDriveIcon || SaveIcon || UploadCloudIcon; const MessageIconToUse = MessageSquareIcon || EditIcon; const updateSetting = (key, val) => setSettings({ ...settings, [key]: val }); const handleSave = async (e) => { e.preventDefault(); const payload = {}; let isProtectedChanged = false; const protectedFields = [ 'limit_rgpd_delete', 'limit_draft_delete', 'limit_keepalive_delete', 'limit_shipping_force', 'limit_history_max', 'quota_vault_mb', 'quota_upload_private_mb', 'quota_upload_public_mb', 'limit_pdf_pages', 'purge_zip_transit_hours', 'purge_zip_doe_hours', 'token_register_hours', 'token_reset_hours', 'token_invite_days', 'token_support_days', 'support_access_hours', 'cron_batch_size', 'editor_lock_timeout', 'limit_strikes_suspend', 'magic_link_expire_days', 'token_recovery_hours', 'timeline_max_chars', 'timeline_anti_spam_sec', 'timeline_auto_close_days', // NOUVEAUX CHAMPS (Spam intelligent et Quota global) 'timeline_spam_max_msg', 'timeline_spam_window_sec', 'quota_global_mb' ]; for (const key in settings) { const oldVal = String(data.settings?.[key] ?? '').replace(/\r\n/g, '\n'); const newVal = String(settings[key] ?? '').replace(/\r\n/g, '\n'); if (oldVal !== newVal) { payload[key] = settings[key]; if (protectedFields.includes(key)) { isProtectedChanged = true; } } } if (Object.keys(payload).length === 0) { if (window.pano_showToast) window.pano_showToast("Aucune modification à sauvegarder.", "info"); return; } if (isProtectedChanged) { setPwdRequestData({ title: "Sécurité Zéro-Trust", desc: "La modification des règles et seuils du système nécessite votre clé de sécurité globale (AES_KEY_CONFIRM) :", onConfirm: async (pwd, submitEvt) => { const d = await executeSave({ ...payload, pwd }); if (!isMounted.current) return; if (d) routerCloseLayer(submitEvt); } }); routerOpenDialog('pwd_request', null, e); return; } await executeSave(payload); }; const executeSave = async (finalPayload) => { const d = await safeFetch('settings/update', { body: finalPayload, setLoading: setIsSaving, successMessage: "Règles et options sauvegardées avec succès !" }); if (!isMounted.current) return; if (d && refreshData) refreshData(); return d; }; const renderSelectRow = (key, label) => { const val = settings[key] || 'active'; return (
{label}
); }; let hasChanges = false; for (const key in settings) { const oldVal = String(data.settings?.[key] ?? '').replace(/\r\n/g, '\n'); const newVal = String(settings[key] ?? '').replace(/\r\n/g, '\n'); if (oldVal !== newVal) { hasChanges = true; break; } } const clientOptionsGlobal = [ { id: 'opt_collab', label: 'Travail en collaboration' }, { id: 'opt_messaging', label: 'Messagerie des riverains' }, { id: 'opt_newsletter', label: 'Info riverains' } ]; const clientOptionsChamps = [ { id: 'simp_opt_description', label: 'Description des travaux' }, { id: 'simp_opt_image', label: 'Vue du projet (Image)' }, { id: 'simp_opt_theme', label: 'Couleur du thème' }, { id: 'simp_opt_link', label: 'Lien vers le promoteur' }, { id: 'simp_opt_emergency', label: 'Téléphone d\'urgence' }, { id: 'simp_opt_schedule', label: 'Horaires de nuisances' } ]; const clientOptionsOnglets = [ { id: 'simp_opt_hide_intervenants', label: 'Masquer l\'onglet Intervenants' }, { id: 'simp_opt_hide_lots', label: 'Masquer l\'onglet Lots de travaux' }, { id: 'simp_opt_hide_legal', label: 'Masquer l\'affichage public de l\'Arrêté' } ]; return ( <>

Règles et Options

Personnalisation des règles métier de .

{/* TITRE DE SECTION: OPTIONS CLIENTS */}

Options côté clients

Gérez la disponibilité globale des fonctionnalités pour l'ensemble des clients. L'état "En option" leur permet d'activer ou masquer eux-mêmes ces champs par projet.

{/* GRILLE ÉCLATÉE DES SOUS-CARTES OPTIONS CLIENTS */}

Fonctionnalités globales

{clientOptionsGlobal.map(opt => renderSelectRow(opt.id, opt.label))}

Champs de l'éditeur

{clientOptionsChamps.map(opt => renderSelectRow(opt.id, opt.label))}

Masquage d'onglets

{clientOptionsOnglets.map(opt => renderSelectRow(opt.id, opt.label))}
{/* TITRE DE SECTION: SEUILS SYSTÈME */}

Règles métier et seuils du système

Configuration des limites techniques, des purges automatiques et des expirations de l'application.

{/* GRILLE ÉCLATÉE DES SOUS-CARTES SEUILS */}
{/* MESSAGERIE ET TIMELINE */}

Messagerie et Timeline

{FormInput && updateSetting('timeline_max_chars', e.target.value)} className="min-w-0 w-full" hint="En caractères" />} {FormInput && updateSetting('timeline_auto_close_days', e.target.value)} className="min-w-0 w-full" hint="Clôture la discussion au bout de ce délai" />}
{ShieldAlertIcon && } Anti-Flood Riverain
{FormInput && updateSetting('timeline_spam_max_msg', e.target.value)} className="min-w-0 w-full" hint="S'active après ce nombre de messages..." />} {FormInput && updateSetting('timeline_spam_window_sec', e.target.value)} className="min-w-0 w-full" hint="... envoyés en moins de (secondes)." />} {FormInput && updateSetting('timeline_anti_spam_sec', e.target.value)} className="min-w-0 w-full" hint="Remis à 0 si le client ou support répond." />}
{/* DÉLAIS D'INACTIVITÉ */}

Délais d'inactivité

{FormInput && updateSetting('limit_rgpd_delete', e.target.value)} className="min-w-0 w-full" />} {FormInput && updateSetting('limit_draft_delete', e.target.value)} className="min-w-0 w-full" />} {FormInput && updateSetting('limit_keepalive_delete', e.target.value)} className="min-w-0 w-full" />} {FormInput && updateSetting('editor_lock_timeout', e.target.value)} className="min-w-0 w-full" />}
{/* EXPIRATION DES JETONS ET ACCÈS (Désormais sur 2 colonnes) */}

Expiration des jetons

{FormInput && updateSetting('token_recovery_hours', e.target.value)} className="min-w-0 w-full" hint="Lien de survie" />} {FormInput && updateSetting('magic_link_expire_days', e.target.value)} className="min-w-0 w-full" />} {FormInput && updateSetting('token_register_hours', e.target.value)} className="min-w-0 w-full" />} {FormInput && updateSetting('token_reset_hours', e.target.value)} className="min-w-0 w-full" />} {FormInput && updateSetting('token_invite_days', e.target.value)} className="min-w-0 w-full" />} {FormInput && updateSetting('token_support_days', e.target.value)} className="min-w-0 w-full" />} {FormInput && updateSetting('support_access_hours', e.target.value)} className="min-w-0 w-full sm:col-span-2" hint="Temps accordé à l'Admin" />}
{/* LIMITES ET LOGISTIQUE */}

Limites et Logistique

{FormInput && updateSetting('limit_strikes_suspend', e.target.value)} className="min-w-0 w-full" hint="Signalements riverains modérés" />} {FormInput && updateSetting('limit_shipping_force', e.target.value)} className="min-w-0 w-full" />} {FormInput && updateSetting('limit_history_max', e.target.value)} className="min-w-0 w-full" />} {FormInput && updateSetting('limit_pdf_pages', e.target.value)} className="min-w-0 w-full" />}
{/* QUOTAS ET FICHIERS */}

Quotas et Fichiers

{FormInput && updateSetting('quota_global_mb', e.target.value)} className="min-w-0 w-full" hint="Espace de stockage total par client" />} {FormInput && updateSetting('quota_vault_mb', e.target.value)} className="min-w-0 w-full" />} {FormInput && updateSetting('quota_upload_private_mb', e.target.value)} className="min-w-0 w-full" />} {FormInput && updateSetting('quota_upload_public_mb', e.target.value)} className="min-w-0 w-full" />}
{/* PURGE TEMPORAIRE ET CRON */}

Purge auto (Heures)

{FormInput && updateSetting('purge_zip_transit_hours', e.target.value)} className="min-w-0 w-full" />} {FormInput && updateSetting('purge_zip_doe_hours', e.target.value)} className="min-w-0 w-full" />}

Moteur asynchrone (CRON)

{FormInput && updateSetting('cron_batch_size', e.target.value)} hint="Nombre maximal d'éléments audités par cycle" className="min-w-0 w-full" />}
{routerActiveDialog === 'pwd_request' && pwdRequestData && PasswordPromptModal && ( )} ); }; /* EOF ========== [_react/admin/_admin_regles.jsx] */