Files
ludikevent_crm/templates/dashboard/base.twig
Serreau Jovann b1b2687320 feat(ansible): Ajoute des headers de sécurité et limite la taille des requêtes.
 feat(Security): Active l'authentification à deux facteurs (2FA).
 feat(Account): Ajoute une entité et un formulaire pour les administrateurs.
🐛 fix(Security): Corrige la redirection après la connexion.
 feat(CRM): Ajoute une page d'administration des comptes administrateurs.
2026-01-15 18:51:17 +01:00

113 lines
7.2 KiB
Twig
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="fr" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Administration{% endblock %} - LudikEvent</title>
{{ vite_asset('admin.js', {}) }}
</head>
<body class="bg-gray-50 dark:bg-gray-900 text-gray-800 dark:text-gray-100 antialiased">
<div class="flex h-screen overflow-hidden">
{# Overlay pour mobile #}
<div id="sidebar-overlay" class="fixed inset-0 z-20 bg-black/50 lg:hidden hidden"></div>
{# SIDEBAR #}
<aside id="sidebar" class="fixed inset-y-0 left-0 z-30 w-64 bg-white dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 transform -translate-x-full lg:translate-x-0 transition-transform duration-300 ease-in-out">
<div class="flex items-center justify-between px-6 h-16 border-b border-gray-200 dark:border-gray-700">
<span class="text-xl font-bold bg-gradient-to-r from-blue-600 to-indigo-500 bg-clip-text text-transparent">LudikEvent CRM</span>
</div>
<nav class="flex flex-col p-4 space-y-1 h-[calc(100vh-64px)] overflow-y-auto">
{% macro nav_link(path, label, icon_svg, current_route) %}
{% set isActive = app.request.get('_route') == current_route %}
<a href="{{ path }}" class="flex items-center space-x-3 p-3 rounded-lg transition-all duration-200 {{ isActive ? 'bg-blue-50 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400' : 'text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700' }}">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">{{ icon_svg|raw }}</svg>
<span class="font-medium text-sm">{{ label }}</span>
</a>
{% endmacro %}
{% import _self as menu %}
{{ menu.nav_link(path('app_crm'), 'Tableau de bord', '<path d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"></path>', 'app_crm') }}
{{ menu.nav_link('#', 'Clients', '<path d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"></path>', 'app_clients') }}
{{ menu.nav_link('#', 'Articles', '<path d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"></path>', 'app_articles') }}
{# Groupe Paramètres #}
<div class="pt-2">
{% set isAdminRoute = app.request.get('_route') matches '/^app_crm_administrateur/' %}
<button id="settings-toggle" class="w-full flex items-center justify-between p-3 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition duration-150">
<div class="flex items-center space-x-3">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37a1.724 1.724 0 002.572-1.065z"></path></svg>
<span class="font-medium text-sm">Paramètres</span>
</div>
<svg id="settings-chevron" class="w-4 h-4 transition-transform duration-200 {{ isAdminRoute ? 'rotate-180' }}" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M19 9l-7 7-7-7"></path></svg>
</button>
<ul id="settings-submenu" class="ml-9 mt-1 space-y-1 {{ isAdminRoute ? '' : 'hidden' }}">
<li>
<a href="{{ path('app_crm_administrateur') }}" class="block p-2 rounded-lg text-sm {{ isAdminRoute ? 'text-blue-600 font-bold dark:text-blue-400' : 'text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white' }}">
Administrateurs
</a>
</li>
<li>
<a href="#" class="block p-2 rounded-lg text-sm text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white">Services</a>
</li>
</ul>
</div>
</nav>
</aside>
{# CONTENU PRINCIPAL #}
<main class="flex-1 flex flex-col min-w-0 lg:ml-64 bg-gray-50 dark:bg-gray-900">
{# Header #}
<header class="h-16 flex items-center justify-between px-6 bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 sticky top-0 z-20">
<button id="sidebar-toggle" class="lg:hidden text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-700 p-2 rounded-md transition-colors">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M4 6h16M4 12h16M4 18h16"></path></svg>
</button>
<div class="flex items-center space-x-4 ml-auto">
<div class="text-right hidden sm:block">
<p class="text-xs font-medium text-gray-900 dark:text-white">{{ app.user.username|default('Admin') }}</p>
<a href="{{ path('app_logout') }}" class="text-[10px] text-red-500 hover:underline">Déconnexion</a>
</div>
</div>
</header>
{# Zone Flash Messages #}
<div id="flash-container" class="fixed top-20 right-6 z-50 flex flex-col gap-3 w-80">
{% for label, messages in app.flashes %}
{% for message in messages %}
<div class="flash-message transform transition-all duration-500 p-4 rounded-lg shadow-lg border flex items-center justify-between {{ label == 'success' ? 'bg-green-100 border-green-200 text-green-800 dark:bg-green-900/80 dark:text-green-300 dark:border-green-800' : 'bg-red-100 border-red-200 text-red-800 dark:bg-red-900/80 dark:text-red-300 dark:border-red-800' }}">
<p class="text-sm font-medium">{{ message }}</p>
<button type="button" onclick="this.parentElement.remove()" class="ml-4 opacity-50 hover:opacity-100">×</button>
</div>
{% endfor %}
{% endfor %}
</div>
{# Zone Contenu #}
<div class="p-6 md:p-8">
<div class="flex flex-col md:flex-row md:items-center justify-between mb-8 gap-4">
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">
{% block title_header %}{{ block('title') }}{% endblock %}
</h1>
<div class="flex items-center space-x-3">
{% block actions %}{% endblock %}
</div>
</div>
<div class="animate-fadeIn w-full">
{% block body %}{% endblock %}
</div>
</div>
</main>
</div>
</body>
</html>