Files
ludikevent_crm/templates/dashboard/administrateur/view.twig
Serreau Jovann e644dc4b85 ```
 feat(templates): Améliore la lisibilité et l'esthétique de l'interface

Ce commit met à jour les couleurs et les styles de texte dans plusieurs
templates pour améliorer la lisibilité et l'esthétique globale de
l'interface utilisateur.  Les couleurs de texte secondaires sont
ajustées pour un meilleur contraste.
```
2026-01-29 18:20:22 +01:00

391 lines
32 KiB
Twig

{% extends 'dashboard/base.twig' %}
{% block title %}Administrateur : {{ admin.firstName }} {{ admin.name }}{% endblock %}
{% block actions %}
<a data-turbo="false" href="{{ path('app_crm_administrateur') }}" class="flex items-center space-x-2 px-4 py-2 text-slate-300 hover:text-slate-800 dark:hover:text-white transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/></svg>
<span class="text-sm font-bold tracking-tight">Retour à la liste</span>
</a>
{% endblock %}
{% block body %}
<div class="max-w-full pb-12 px-6">
{# --- HEADER GÉNÉRAL --- #}
<div class="mb-10 flex flex-col md:flex-row md:items-center justify-between gap-6 pb-8 border-b border-slate-200 dark:border-slate-800">
<div class="flex items-center space-x-6">
<div class="h-20 w-20 rounded-3xl bg-gradient-to-br from-indigo-600 to-indigo-800 flex items-center justify-center text-white text-3xl font-black shadow-2xl shadow-indigo-500/20 border border-white/10">
{{ admin.firstName|first|upper }}{{ admin.name|first|upper }}
</div>
<div>
<div class="flex flex-col mb-1">
{% if 'ROLE_CLIENT_MAIN' in admin.roles %}
<div class="flex items-center space-x-2 mb-2">
<span class="px-3 py-1 bg-indigo-500 text-white text-[9px] font-black uppercase rounded-lg tracking-[0.2em] shadow-lg shadow-indigo-500/40">
Administrateur Principal
</span>
<div class="h-1.5 w-1.5 rounded-full bg-indigo-500 animate-pulse"></div>
</div>
{% endif %}
<h1 class="text-4xl font-black text-slate-800 dark:text-white tracking-tight">
{{ admin.firstName }} {{ admin.name }}
</h1>
</div>
<div class="flex items-center space-x-4">
<div class="flex items-center space-x-2 text-slate-400">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 12a4 4 0 10-8 0 4 4 0 008 0zm0 0v1.5a2.5 2.5 0 005 0V12a9 9 0 10-9 9m4.5-1.206a8.959 8.959 0 01-4.5 1.207" /></svg>
<span class="text-sm font-bold text-slate-600 dark:text-slate-300">{{ admin.email }}</span>
</div>
<div class="h-1 w-1 bg-slate-300 dark:bg-slate-700 rounded-full"></div>
<span class="text-[10px] font-black text-slate-300 uppercase tracking-widest">System ID: #{{ admin.id }}</span>
</div>
</div>
</div>
<div class="flex items-center">
{% if not admin.actif %}
<div class="flex items-center bg-white dark:bg-slate-900 border border-amber-500/30 rounded-2xl overflow-hidden shadow-xl">
<div class="flex items-center px-5 py-3 space-x-3 bg-amber-500/5">
<div class="w-2 h-2 rounded-full bg-amber-500 shadow-[0_0_8px_rgba(245,158,11,0.8)]"></div>
<span class="text-[10px] font-black text-amber-500 uppercase tracking-widest">Compte non activé</span>
</div>
<a data-turbo="false" href="{{ path('app_crm_administrateur_view', {id: admin.id, act: 'updateStatut', status: 'true'}) }}"
class="px-6 py-3 bg-emerald-600 hover:bg-emerald-500 text-white text-[10px] font-black uppercase tracking-widest transition-all active:scale-95 border-l border-white/5">
Activer
</a>
</div>
{% else %}
<div class="flex items-center bg-white dark:bg-slate-900 border border-emerald-500/30 rounded-2xl overflow-hidden shadow-xl">
<div class="flex items-center px-5 py-3 space-x-3 bg-emerald-500/5">
<div class="w-2 h-2 rounded-full bg-emerald-500 shadow-[0_0_8px_rgba(16,185,129,0.8)]"></div>
<span class="text-[10px] font-black text-emerald-500 uppercase tracking-widest">Accès en ligne</span>
</div>
<a data-turbo="false" href="{{ path('app_crm_administrateur_view', {id: admin.id, act: 'updateStatut', status: 'false'}) }}"
onclick="return confirm('Voulez-vous vraiment désactiver cet accès ?')"
class="px-6 py-3 bg-slate-100 dark:bg-slate-800 hover:bg-red-600text-slate-300 dark:text-slate-400 hover:text-white text-[10px] font-black uppercase tracking-widest transition-all active:scale-95 border-l border-slate-200 dark:border-white/5">
Désactiver
</a>
</div>
{% endif %}
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8">
{# --- COLONNE GAUCHE : IDENTITÉ --- #}
<div class="lg:col-span-4">
{{ form_start(form) }}
<div class="bg-white dark:bg-[#1e293b] rounded-3xl p-8 border border-slate-200 dark:border-slate-800 shadow-sm sticky top-6">
<div class="flex items-center space-x-4 mb-8">
<div class="h-16 w-16 rounded-2xl bg-indigo-600 flex items-center justify-center text-white text-2xl font-black shadow-lg shadow-indigo-500/20">
{{ admin.firstName|first|upper }}{{ admin.name|first|upper }}
</div>
<div>
<h2 class="text-xl font-black text-slate-800 dark:text-white uppercase tracking-tight">Identité</h2>
<p class="text-[10px] text-slate-400 font-black uppercase tracking-widest italic">Données personnelles</p>
</div>
</div>
<div class="space-y-5">
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1 text-indigo-500">Prénom</label>
{{ form_widget(form.firstName, { 'attr': {'class': 'w-full px-4 py-3 bg-slate-50 dark:bg-slate-900 border-none rounded-xl focus:ring-2 focus:ring-indigo-500 transition-all text-sm font-medium dark:text-white'} }) }}
</div>
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1 text-indigo-500">Nom</label>
{{ form_widget(form.name, { 'attr': {'class': 'w-full px-4 py-3 bg-slate-50 dark:bg-slate-900 border-none rounded-xl focus:ring-2 focus:ring-indigo-500 transition-all text-sm font-medium dark:text-white'} }) }}
</div>
<hr class="border-slate-100 dark:border-slate-800 my-4">
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1 text-amber-500">Username</label>
{{ form_widget(form.username, { 'attr': {'class': 'w-full px-4 py-3 bg-slate-50 dark:bg-slate-900 border-none rounded-xl focus:ring-2 focus:ring-amber-500 transition-all text-sm font-mono dark:text-white'} }) }}
</div>
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1 text-amber-500">Email professionnel</label>
{{ form_widget(form.email, { 'attr': {'class': 'w-full px-4 py-3 bg-slate-50 dark:bg-slate-900 border-none rounded-xl focus:ring-2 focus:ring-amber-500 transition-all text-sm dark:text-white'} }) }}
</div>
</div>
<button type="submit" class="w-full mt-10 flex items-center justify-center space-x-2 px-6 py-4 bg-slate-900 dark:bg-indigo-600 hover:bg-indigo-700 text-white rounded-2xl font-bold shadow-xl transition-all active:scale-95">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>
<span>Enregistrer l'identité</span>
</button>
</div>
{{ form_end(form) }}
</div>
{# --- COLONNE DROITE : SÉCURITÉ & DROITS --- #}
<div class="lg:col-span-8 space-y-8">
{# CARTE SÉCURITÉ #}
<div class="bg-white dark:bg-[#1e293b] rounded-3xl p-8 border border-slate-200 dark:border-slate-800 shadow-sm">
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-8">
<h3 class="text-[10px] font-black text-indigo-500 uppercase tracking-[0.2em] flex items-center">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" /></svg>
Sécurité et Mot de passe
</h3>
<a data-turbo="false" href="{{ path('app_crm_administrateur_view', {id: admin.id, act: 'sendResetLink'}) }}"
onclick="return confirm('Envoyer l\'email de réinitialisation ?')"
class="px-4 py-2 bg-slate-100 dark:bg-slate-800 hover:bg-indigo-500 hover:text-white text-slate-600 dark:text-slate-300 text-[10px] font-black uppercase rounded-xl transition-all border border-slate-200 dark:border-slate-700">
Envoyer un lien de réinitialisation
</a>
</div>
{{ form_start(passwordForm) }}
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Nouveau mot de passe</label>
{{ form_widget(passwordForm.password.first, { 'attr': {'class': 'w-full px-4 py-3 bg-slate-50 dark:bg-slate-900 border-none rounded-xl focus:ring-2 focus:ring-indigo-500 transition-all text-sm dark:text-white'} }) }}
</div>
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Confirmer</label>
{{ form_widget(passwordForm.password.second, { 'attr': {'class': 'w-full px-4 py-3 bg-slate-50 dark:bg-slate-900 border-none rounded-xl focus:ring-2 focus:ring-indigo-500 transition-all text-sm dark:text-white'} }) }}
</div>
</div>
<div class="mt-6 flex justify-end">
<button type="submit" class="px-6 py-3 bg-slate-800 dark:bg-slate-700 text-white text-[10px] font-black uppercase rounded-xl hover:bg-indigo-600 transition-all">
Mettre à jour
</button>
</div>
{{ form_end(passwordForm) }}
</div>
{# BLOC 2FA #}
<div class="flex items-center justify-between p-6 bg-slate-50 dark:bg-slate-900/40 rounded-3xl border border-slate-200 dark:border-slate-800">
<div class="flex items-center space-x-4">
<div class="h-12 w-12 rounded-xl bg-indigo-500/10 flex items-center justify-center text-indigo-500 border border-indigo-500/20">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/></svg>
</div>
<div>
<h4 class="text-sm font-bold text-slate-800 dark:text-white">Authentification 2FA</h4>
<p class="text-xs text-slate-300 dark:text-slate-400">
{% if admin.googleAuthenticatorSecret %}
<span class="text-emerald-500 font-bold">● Activée</span> — Accès hautement sécurisé
{% else %}
<span class="text-rose-500 font-bold">○ Désactivée</span> — Risque de sécurité
{% endif %}
</p>
</div>
</div>
<a data-turbo="false" href="{{ path('app_crm_administrateur_view', {id: admin.id, act: admin.googleAuthenticatorSecret ? 'disable2fa' : 'sendLink2faenable'}) }}"
class="px-4 py-2 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 text-slate-700 dark:text-slate-200 text-xs font-bold rounded-xl hover:bg-indigo-500 hover:text-white transition-all shadow-sm">
<span>{{ admin.googleAuthenticatorSecret ? 'Désactiver' : 'Envoyer le lien' }}</span>
</a>
</div>
{# HABILITATIONS OPÉRATIONNELLES #}
{% if 'ROLE_CLIENT_MAIN' not in admin.roles %}
<form action="{{ path('app_crm_administrateur_view', {id: admin.id, act: 'updateRoles'}) }}" method="POST">
<div class="bg-white dark:bg-[#1e293b] rounded-3xl p-8 border border-slate-200 dark:border-slate-800 shadow-sm">
<div class="flex items-center justify-between mb-8">
<h3 class="text-[10px] font-black text-emerald-500 uppercase tracking-[0.2em] flex items-center">
<span class="w-8 h-8 bg-emerald-100 dark:bg-emerald-900/30 text-emerald-600 dark:text-emerald-400 rounded-lg flex items-center justify-center mr-3">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" /></svg>
</span>
Habilitations Opérationnelles
</h3>
<button type="submit" class="px-5 py-2 bg-emerald-500 hover:bg-emerald-600 text-white text-[10px] font-black uppercase rounded-xl transition-all shadow-lg shadow-emerald-500/20">
Mettre à jour les droits
</button>
</div>
<div class="grid grid-cols-1 gap-4">
{% set availableRoles = [
{'role': 'ROLE_ADMIN_EDIT', 'label': 'Gestion des Administrateurs', 'icon': 'M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z'},
{'role': 'ROLE_ADMIN_PRODUCT', 'label': 'Catalogue Produits', 'icon': 'M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4'},
{'role': 'ROLE_ADMIN_CONTRAT', 'label': 'Gestion Contrats', 'icon': 'M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z'}
] %}
{% for item in availableRoles %}
<div class="p-4 rounded-2xl bg-slate-50 dark:bg-slate-900/50 border border-slate-100 dark:border-slate-800 flex items-center justify-between group">
<div class="flex items-center space-x-4">
<div class="w-10 h-10 rounded-xl bg-white dark:bg-slate-900 flex items-center justify-center text-slate-400 group-hover:text-emerald-500 transition-colors shadow-sm">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="{{ item.icon }}" /></svg>
</div>
<div>
<span class="block text-sm font-bold text-slate-700 dark:text-slate-200">{{ item.label }}</span>
<span class="block text-[10px] font-medium text-slate-400 uppercase tracking-widest mt-0.5">{{ item.role }}</span>
</div>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" name="roles[]" value="{{ item.role }}" class="sr-only peer" {% if item.role in admin.roles %}checked{% endif %}>
<div class="w-11 h-6 bg-slate-300 dark:bg-slate-700 rounded-full peer peer-checked:bg-emerald-500 transition-all after:content-[''] after:absolute after:top-[4px] after:left-[4px] after:bg-white after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:after:translate-x-5"></div>
</label>
</div>
{% endfor %}
</div>
</div>
</form>
{% else %}
<div class="bg-emerald-500/5 dark:bg-emerald-500/10 rounded-3xl p-8 border border-emerald-500/20 shadow-sm flex items-center space-x-6 border-l-4 border-l-emerald-500">
<div class="w-14 h-14 bg-emerald-500 text-white rounded-2xl flex items-center justify-center shadow-lg shadow-emerald-500/20">
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" /></svg>
</div>
<div>
<h4 class="text-emerald-600 dark:text-emerald-400 font-black text-sm uppercase tracking-wider">Habilitation Totale Active</h4>
<p class="text-emerald-600/70 dark:text-emerald-400/70 text-xs mt-1">Le rang Client Principal accorde nativement toutes les permissions système.</p>
</div>
</div>
{% endif %}
{# PRIVILÈGE ROOT #}
{% if is_granted('ROLE_ROOT') %}
<form action="{{ path('app_crm_administrateur_view', {id: admin.id, act: 'updateRoles'}) }}" method="POST">
<div class="bg-white dark:bg-[#1e293b] rounded-3xl p-8 border border-slate-200 dark:border-slate-800 shadow-sm border-l-4 border-l-indigo-500">
<div class="flex items-center justify-between mb-8">
<h3 class="text-[10px] font-black text-indigo-500 uppercase tracking-[0.2em] flex items-center">
<span class="w-8 h-8 bg-indigo-100 dark:bg-indigo-900/30 text-indigo-600 dark:text-indigo-400 rounded-lg flex items-center justify-center mr-3">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" /></svg>
</span>
Privilège de Structure
</h3>
<button type="submit" class="px-5 py-2 bg-indigo-600 hover:bg-indigo-700 text-white text-[10px] font-black uppercase rounded-xl transition-all shadow-lg shadow-indigo-500/20">
Confirmer le rang
</button>
</div>
<div class="p-6 rounded-2xl bg-slate-900 border border-slate-800 shadow-inner group transition-all hover:border-indigo-500/30">
<label class="flex items-center justify-between cursor-pointer">
<div class="flex items-center space-x-5">
<div class="relative">
<input type="checkbox" name="roles[]" value="ROLE_CLIENT_MAIN" class="sr-only peer" {% if 'ROLE_CLIENT_MAIN' in admin.roles %}checked{% endif %}>
<div class="w-14 h-7 bg-slate-700 rounded-full peer peer-checked:bg-indigo-600 transition-all duration-300"></div>
<div class="absolute top-1 left-1 w-5 h-5 bg-white rounded-full transition-all duration-300 peer-checked:translate-x-7 shadow-lg"></div>
</div>
<div>
<span class="block text-sm font-bold text-white tracking-wide group-hover:text-indigo-400 transition-colors uppercase">Administrateur Client Principal</span>
<p class="text-[10px] text-slate-300 font-medium mt-1 uppercase italic">Contrôle total de l'organisation</p>
</div>
</div>
</label>
</div>
</div>
</form>
{% endif %}
</div>
</div>
{# --- SECTION JOURNAUX (AUDIT) --- #}
<div class="mt-12 w-full bg-white dark:bg-[#1e293b] rounded-3xl border border-slate-200 dark:border-slate-800 shadow-sm overflow-hidden">
<div class="px-8 py-6 border-b border-slate-100 dark:border-slate-800 flex items-center justify-between bg-slate-50/30 dark:bg-slate-800/30">
<div>
<h3 class="text-[10px] font-black text-indigo-500 uppercase tracking-[0.3em]">Journal d'Audit Personnel</h3>
<p class="text-xs text-slate-300 dark:text-slate-400 mt-1">Historique des actions et vérification d'intégrité numérique</p>
</div>
<span class="px-4 py-1.5 bg-indigo-50 dark:bg-indigo-900/20 text-indigo-600 dark:text-indigo-400 text-[10px] font-black uppercase rounded-lg border border-indigo-100 dark:border-indigo-800/50">
{{ auditLogs.getTotalItemCount }} ÉVÈNEMENTS
</span>
</div>
<div class="overflow-x-auto">
<table class="w-full text-left border-collapse">
<thead>
<tr class="bg-slate-50/50 dark:bg-slate-900/40 border-b border-slate-100 dark:border-slate-800 uppercase tracking-widest text-[10px] font-black text-slate-400">
<th class="px-8 py-4">Horodatage</th>
<th class="px-8 py-4 text-center">Action</th>
<th class="px-8 py-4 text-center">Intégrité</th>
<th class="px-8 py-4">Détails</th>
{% if is_granted('ROLE_ROOT') %}<th class="px-8 py-4 text-right">Actions</th>{% endif %}
</tr>
</thead>
<tbody class="divide-y divide-slate-100 dark:divide-slate-800 text-sm">
{% for log in auditLogs %}
<tr class="hover:bg-slate-50/50 dark:hover:bg-slate-800/30 transition-colors">
<td class="px-8 py-4 whitespace-nowrap align-top">
<div class="text-sm font-bold text-slate-700 dark:text-slate-200">{{ log.actionAt|date('d/m/Y') }}</div>
<div class="text-[10px] text-slate-400 font-mono mt-0.5 tracking-wider">{{ log.actionAt|date('H:i:s') }}</div>
</td>
<td class="px-8 py-4 whitespace-nowrap text-center align-top">
{% set typeMapping = {
'CREATE': { 'label': 'CRÉATION', 'style': 'bg-emerald-500/10 text-emerald-600 border-emerald-500/20' },
'DELETE': { 'label': 'SUPPRESSION', 'style': 'bg-rose-500/10 text-rose-600 border-rose-500/20' },
'UPDATE': { 'label': 'MODIFICATION', 'style': 'bg-blue-500/10 text-blue-600 border-blue-500/20' },
'AUTH': { 'label': 'CONNEXION', 'style': 'bg-amber-500/10 text-amber-600 border-amber-500/20' },
'VIEW': { 'label': 'CONSULTATION', 'style': 'bg-sky-500/10 text-sky-600 border-sky-500/20' },
'SECURITY_ALERT': { 'label': 'ALERTE SÉCURITÉ', 'style': 'bg-orange-500/10 text-orange-600 border-orange-500/20' },
'SECURITY_CRITICAL': { 'label': 'CRITIQUE', 'style': 'bg-red-600 text-white border-red-700 animate-pulse font-black' },
'2FA_INVITE': { 'label': 'INVITATION 2FA', 'style': 'bg-indigo-500/10 text-indigo-500 border-indigo-500/20' },
'2FA_DISABLED': { 'label': '2FA DÉSACTIVÉE', 'style': 'bg-rose-500/10 text-rose-600 border-rose-500/20 font-bold' },
'UPDATE_STATUS': { 'label': 'STATUT COMPTE', 'style': 'bg-slate-500/10 text-slate-300 border-slate-500/20' },
'UPDATE_ROLES': { 'label': 'DROITS ACCÈS', 'style': 'bg-purple-500/10 text-purple-600 border-purple-500/20' }
} %}
{# On récupère la configuration ou on utilise une valeur par défaut #}
{% set config = typeMapping[log.type] ?? { 'label': log.type|replace({'_': ' '}), 'style': 'bg-slate-500/10 text-slate-300 border-slate-500/20' } %}
<span class="px-3 py-1.5 rounded-lg text-[10px] font-black border uppercase tracking-widest {{ config.style }}">
{{ config.label }}
</span>
</td>
<td class="px-8 py-4 text-center align-top">
{% if log.hashCode == log.generateSignature %}
<div class="inline-flex flex-col items-center text-emerald-500" title="Signature HMAC valide">
<svg class="w-5 h-5 mb-1" fill="none" stroke="currentColor" stroke-width="3" viewBox="0 0 24 24"><path d="M5 13l4 4L19 7"/></svg>
<span class="text-[8px] font-black uppercase">Sécurisé</span>
</div>
{% else %}
<div class="inline-flex flex-col items-center text-rose-500 animate-pulse" title="ALTÉRATION DÉTECTÉE">
<svg class="w-5 h-5 mb-1" fill="none" stroke="currentColor" stroke-width="3" viewBox="0 0 24 24"><path d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>
<span class="text-[8px] font-black uppercase tracking-tighter">Corrompu</span>
</div>
{% endif %}
</td>
<td class="px-8 py-4 align-top">
<div class="text-xs text-slate-600 dark:text-slate-300 font-medium leading-relaxed max-w-xl">{{ log.message }}</div>
<div class="mt-2 text-[9px] font-mono text-slate-400 bg-slate-100 dark:bg-slate-900 px-2 py-0.5 rounded inline-block">ID: {{ log.hashCode|slice(0, 16) }}...</div>
</td>
{% if is_granted('ROLE_ROOT') %}
<td class="px-8 py-4 text-right align-top">
<form action="{{ path('app_crm_audit_logs_delete', {id: log.id}) }}" method="POST" onsubmit="return confirm('Supprimer définitivement ce log ?');">
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ log.id) }}">
<button type="submit" class="p-2 text-slate-400 hover:text-rose-500 hover:bg-rose-50 dark:hover:bg-rose-900/10 rounded-xl transition-all">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg>
</button>
</form>
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if auditLogs.getTotalItemCount > 0 %}
<div class="px-8 py-6 bg-slate-50/50 dark:bg-slate-900/40 border-t border-slate-100 dark:border-slate-800">
{{ knp_pagination_render(auditLogs) }}
</div>
{% endif %}
</div>
{# CONNEXIONS #}
<div class="mt-2 bg-white dark:bg-[#1e293b] rounded-3xl border border-slate-200 dark:border-slate-800 shadow-sm overflow-hidden">
<div class="p-8 border-b border-slate-100 dark:border-slate-800 bg-slate-50/50 dark:bg-slate-900/10">
<h3 class="text-[10px] font-black text-emerald-500 uppercase tracking-[0.3em]">Dernières Connexions</h3>
</div>
<div class="overflow-x-auto">
<table class="w-full text-left font-medium">
<thead>
<tr class="bg-slate-50/50 dark:bg-slate-900/50 border-b border-slate-100 dark:border-slate-800">
<th class="px-8 py-4 text-[10px] font-black uppercase text-slate-400 tracking-widest">Date & Heure</th>
<th class="px-8 py-4 text-[10px] font-black uppercase text-slate-400 tracking-widest">IP</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100 dark:divide-slate-800 text-sm">
{% for login in loginRegisters %}
<tr class="hover:bg-slate-50/50 dark:hover:bg-slate-800/30 transition-colors">
<td class="px-8 py-5 text-slate-700 dark:text-slate-200">{{ login.loginAt|date('d/m/Y H:i') }}</td>
<td class="px-8 py-5">
<span class="px-3 py-1 bg-slate-100 dark:bg-slate-800 rounded-lg text-xs font-mono text-slate-300 border border-slate-200 dark:border-slate-700">
{{ login.ip }}
</span>
</td>
</tr>
{% else %}
<tr><td colspan="2" class="px-8 py-12 text-center text-slate-400 text-sm italic">Aucune connexion enregistrée.</td></tr>
{% endfor %}
</tbody>
</table>
</div>
{% if loginRegisters.getTotalItemCount > 0 %}<div class="p-6 border-t border-slate-100 dark:border-slate-800">{{ knp_pagination_render(loginRegisters) }}</div>{% endif %}
</div>
</div>
{% endblock %}