refact: unify js and css

This commit is contained in:
2025-08-17 05:48:27 +02:00
parent dcd88bd383
commit 3fd49d1351
16 changed files with 2924 additions and 247 deletions

View File

@@ -212,6 +212,125 @@
</div>
{% endmacro %}
{# Macro pour page layout standardisé avec hero section #}
{% macro page_layout(title, subtitle=None, breadcrumbs=[], actions=[]) %}
{% if breadcrumbs %}
<nav class="flex mb-6" aria-label="Breadcrumb">
<ol class="flex items-center space-x-2 text-sm">
{% for crumb in breadcrumbs %}
<li class="flex items-center">
{% if not loop.last %}
<a href="{{ crumb.url }}" class="text-gray-500 hover:text-gray-700 transition-colors">
{{ crumb.text }}
</a>
<svg class="w-4 h-4 mx-2 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
</svg>
{% else %}
<span class="text-gray-900 font-medium">{{ crumb.text }}</span>
{% endif %}
</li>
{% endfor %}
</ol>
</nav>
{% endif %}
<!-- Hero Section avec gradient -->
<div class="bg-gradient-to-r from-blue-600 to-purple-600 text-white rounded-xl p-8 shadow-lg mb-8">
<div class="flex items-center justify-between">
<div class="flex-1">
<h1 class="text-4xl font-bold mb-2">{{ title }}</h1>
{% if subtitle %}
<p class="text-xl opacity-90 mb-1">{{ subtitle }}</p>
{% endif %}
<p class="text-sm opacity-75">{{ moment().format('dddd D MMMM YYYY') if moment else 'Lundi 16 août 2025' }}</p>
</div>
<div class="hidden md:block">
{% if actions %}
<div class="flex items-center space-x-3">
{% for action in actions %}
<a href="{{ action.url }}"
class="bg-white/20 hover:bg-white/30 text-white px-6 py-3 rounded-xl transition-all duration-300 font-semibold shadow-lg hover:shadow-xl transform hover:scale-105 flex items-center">
{% if action.icon %}{{ action.icon|safe }}{% endif %}
{{ action.text }}
</a>
{% endfor %}
</div>
{% else %}
<div class="w-24 h-24 bg-white/20 rounded-full flex items-center justify-center">
<svg class="w-12 h-12" fill="currentColor" viewBox="0 0 20 20">
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</div>
{% endif %}
</div>
</div>
</div>
{{ caller() }}
{% endmacro %}
{# Macro pour section de contenu standardisée #}
{% macro content_section(title=None, description=None, actions=[], classes="") %}
<div class="bg-white rounded-xl shadow-lg overflow-hidden {{ classes }}">
{% if title %}
<div class="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
<div>
<h2 class="text-lg font-semibold text-gray-900">{{ title }}</h2>
{% if description %}
<p class="text-sm text-gray-600 mt-1">{{ description }}</p>
{% endif %}
</div>
{% if actions %}
<div class="flex items-center space-x-2">
{% for action in actions %}
<a href="{{ action.url }}"
class="text-blue-600 hover:text-blue-800 text-sm font-medium">
{% if action.icon %}{{ action.icon|safe }}{% endif %}
{{ action.text }}
</a>
{% endfor %}
</div>
{% endif %}
</div>
{% endif %}
<div class="p-6">
{{ caller() }}
</div>
</div>
{% endmacro %}
{# Macro pour état vide unifié #}
{% macro empty_state(title, description, icon, action=None, secondary_action=None) %}
<div class="bg-white rounded-xl shadow-lg overflow-hidden">
<div class="p-6 text-center py-12">
<div class="w-24 h-24 bg-gradient-to-br from-gray-100 to-gray-200 rounded-full flex items-center justify-center mx-auto mb-6">
{{ icon|safe }}
</div>
<h3 class="text-xl font-semibold text-gray-900 mb-2">{{ title }}</h3>
<p class="text-base text-gray-600 mb-6 max-w-md mx-auto">{{ description }}</p>
<div class="space-y-4">
{% if action %}
<a href="{{ action.url }}"
class="inline-flex items-center px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-semibold">
{% if action.icon %}{{ action.icon|safe }}{% endif %}
{{ action.text }}
</a>
{% endif %}
{% if secondary_action %}
<div class="text-sm text-gray-600">
<p><strong>💡 Astuce :</strong> {{ secondary_action.text }}</p>
</div>
{% endif %}
</div>
</div>
</div>
{% endmacro %}
{# Macro pour filtres simplifiés et discrets #}
{% macro simple_filter_section(filters_config, current_values={}, total_items=0, filtered_items=0) %}
<div class="bg-gray-50 rounded-lg p-4 mb-6 border border-gray-200">
@@ -292,4 +411,130 @@
</div>
</div>
</div>
{% endmacro %}
{# Macro pour tableau standardisé #}
{% macro data_table(headers, rows, actions_column=False, zebra=True) %}
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
{% for header in headers %}
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{{ header }}
</th>
{% endfor %}
{% if actions_column %}
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
Actions
</th>
{% endif %}
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
{% for row in rows %}
<tr class="{% if zebra and loop.index % 2 == 0 %}bg-gray-50{% endif %} hover:bg-blue-50 transition-colors">
{% for cell in row.cells %}
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{{ cell|safe }}
</td>
{% endfor %}
{% if actions_column and row.actions %}
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<div class="flex justify-end space-x-2">
{% for action in row.actions %}
<a href="{{ action.url }}"
class="text-{{ action.color|default('blue') }}-600 hover:text-{{ action.color|default('blue') }}-900 transition-colors">
{{ action.text }}
</a>
{% endfor %}
</div>
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endmacro %}
{# Macro pour liste d'éléments uniformisée #}
{% macro item_list(items, show_meta=True, show_actions=True) %}
<div class="space-tight">
{% for item in items %}
<div class="flex items-center justify-between p-4 bg-gray-50 hover:bg-blue-50 rounded-lg transition-colors group">
<a href="{{ item.url }}" class="flex items-center space-x-4 flex-1 min-w-0">
{% if item.avatar %}
<div class="w-10 h-10 {{ item.avatar.classes|default('bg-gradient-to-br from-blue-500 to-purple-600') }} rounded-full flex items-center justify-center text-white font-bold text-sm">
{{ item.avatar.content|safe }}
</div>
{% endif %}
<div class="min-w-0 flex-1">
<h3 class="text-sm font-semibold text-gray-900 group-hover:text-blue-700 transition-colors truncate">
{{ item.title }}
</h3>
{% if show_meta and item.meta %}
<div class="flex items-center text-xs text-gray-500 space-x-2 mt-1">
{% for meta in item.meta %}
<span class="flex items-center">
{% if meta.icon %}{{ meta.icon|safe }}{% endif %}
{{ meta.text }}
</span>
{% if not loop.last %}<span></span>{% endif %}
{% endfor %}
</div>
{% endif %}
</div>
</a>
{% if show_actions and item.indicators %}
<div class="flex items-center space-x-3 flex-shrink-0">
{% for indicator in item.indicators %}
{{ indicator|safe }}
{% endfor %}
</div>
{% endif %}
</div>
{% endfor %}
</div>
{% endmacro %}
{# Macro pour notifications/alertes uniformisées #}
{% macro alert(type, title, message, dismissible=True, icon=True) %}
{% set alert_classes = {
'info': 'bg-blue-50 border-blue-200 text-blue-800',
'success': 'bg-green-50 border-green-200 text-green-800',
'warning': 'bg-orange-50 border-orange-200 text-orange-800',
'error': 'bg-red-50 border-red-200 text-red-800'
} %}
{% set icons = {
'info': '<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"/></svg>',
'success': '<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/></svg>',
'warning': '<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/></svg>',
'error': '<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/></svg>'
} %}
<div class="border rounded-lg p-4 {{ alert_classes[type] }} {% if dismissible %}relative{% endif %}" role="alert">
<div class="flex items-start">
{% if icon %}
<div class="flex-shrink-0 mr-3 mt-0.5">
{{ icons[type]|safe }}
</div>
{% endif %}
<div class="flex-1">
<h3 class="text-sm font-medium">{{ title }}</h3>
<p class="text-sm mt-1">{{ message }}</p>
</div>
{% if dismissible %}
<button type="button" class="flex-shrink-0 ml-3 hover:opacity-75 transition-opacity"
onclick="this.parentElement.remove()">
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
</svg>
</button>
{% endif %}
</div>
</div>
{% endmacro %}