// ECO-PANNEAU.FR - _react/_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 } = window.pano_getIcons(); const { FormInput, PasswordPromptModal, TextLogo, Button, DataCard } = window.pano_getComponents(); // Fallback pour les icônes si manquantes const DBIconToUse = DatabaseIcon || ServerIcon || SettingsIcon; const ClockIconToUse = ClockIcon || RefreshCwIcon || ZapIcon; const HardDriveIconToUse = HardDriveIcon || SaveIcon || UploadCloudIcon; // Méthodes métier const updateSetting = (key, val) => setSettings({ ...settings, [key]: val }); const handleSave = async (e) => { e.preventDefault(); const payload = {}; let isProtectedChanged = false; // Champs nécessitant une protection par mot de passe dans cet onglet 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' ]; 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: "Confirmation de sécurité", desc: "La modification des seuils du système nécessite le mot de passe administrateur :", 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; }; // Helper pour le rendu des menus déroulants à 4 choix stricts const renderSelectRow = (key, label) => { const val = settings[key] || 'active'; // Par défaut return (
{label}
); }; // Calcul de "hasChanges" pour afficher/griser le bouton "Sauvegarder" 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; } } // Configuration des rendus répétitifs 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é' } ]; // Rendu UI return ( <>

Règles et Options

Personnalisation des règles métier de .

{/* Options clients Unifiées */}

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.

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))}
{/* Règles métier et seuils */}

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

Délais d'inactivité (Jours)
{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" />}
Limites d'interface et Logistique
{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 (Mo)
{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 des fichiers temporaires (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" />}
Expiration des jetons et accès
{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" />}
{/* Barre de sauvegarde */}
{/* Modales */} {routerActiveDialog === 'pwd_request' && pwdRequestData && PasswordPromptModal && ( )} ); }; /* EOF ========== [_react/_admin_regles.jsx] */