feat: complete UI redesign with DaisyUI + Tailwind CSS v4

Design system overhaul using DaisyUI v5 on Tailwind CSS v4:

- Custom 'ohmstream' dark theme with orange primary (#FF9F1C),
  magenta secondary, gold accent matching existing palette
- Tailwind CSS-first config (input.css source, style.css built output)
- DaisyUI components: navbar, drawer, cards, badges, alerts, tables,
  progress bars, tabs, toggles, stats, form controls, tooltips
- Mobile-first responsive layout with drawer navigation
- Eliminated ~500+ lines of embedded CSS across 15+ template files
- Removed all inline style spam from admin_panel and settings_section
- Preserved all HTMX triggers, Alpine.js state, and Jinja2 logic
- Updated auth-ui.js for DaisyUI tab-active class compatibility

Build: npm run build:css (minified) / npm run watch:css (dev)
This commit is contained in:
root
2026-04-11 19:46:52 +00:00
parent 87f245d3fc
commit 4101d98a41
28 changed files with 2534 additions and 2808 deletions
+51 -47
View File
@@ -1,85 +1,89 @@
<div class="settings-container section-container">
<div class="section-header">
<h2>Administration</h2>
<div class="mb-10">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold">Administration</h2>
</div>
<!-- Stats Cards -->
<div id="admin-stats" class="admin-stats-grid" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 15px; margin-bottom: 30px;">
<div class="admin-stat-card" style="padding: 20px; background: var(--bg-card); border-radius: var(--card-radius); border: 1px solid #2a2d32;text-align: center;">
<div style="font-size: 2rem; font-weight: 800; color: var(--primary);" id="stat-total-users">{{ users|length }}</div>
<div style="color: var(--text-dim); font-size: 0.85rem;">Utilisateurs</div>
<div class="stats stats-vertical md:stats-horizontal shadow w-full mb-6">
<div class="stat bg-base-200 border border-base-300 rounded-box">
<div class="stat-title">Utilisateurs</div>
<div class="stat-value text-primary">{{ users|length }}</div>
</div>
<div class="admin-stat-card" style="padding: 20px; background: var(--bg-card); border-radius: var(--card-radius); border: 1px solid #2a2d32;text-align: center;">
<div style="font-size: 2rem; font-weight: 800; color: var(--accent);" id="stat-active-users">{{ users|selectattr('is_active')|list|length }}</div>
<div style="color: var(--text-dim); font-size: 0.85rem;">Actifs</div>
<div class="stat bg-base-200 border border-base-300 rounded-box">
<div class="stat-title">Actifs</div>
<div class="stat-value text-secondary">{{ users|selectattr('is_active')|list|length }}</div>
</div>
<div class="admin-stat-card" style="padding: 20px; background: var(--bg-card); border-radius: var(--card-radius); border: 1px solid #2a2d32;text-align: center;">
<div style="font-size: 2rem; font-weight: 800; color: #f0a500;" id="stat-admin-users">{{ users|selectattr('is_admin')|list|length }}</div>
<div style="color: var(--text-dim); font-size: 0.85rem;">Admins</div>
<div class="stat bg-base-200 border border-base-300 rounded-box">
<div class="stat-title">Admins</div>
<div class="stat-value text-warning">{{ users|selectattr('is_admin')|list|length }}</div>
</div>
</div>
<!-- Users Table -->
<div style="background: var(--bg-card); border-radius: var(--card-radius); border: 1px solid #2a2d32;overflow: hidden;">
<div style="padding: 20px 25px; border-bottom: 1px solid #2a2d32;">
<h3 style="margin: 0; color: var(--primary);">Gestion des utilisateurs</h3>
<div class="bg-base-200 border border-base-300 rounded-box overflow-hidden">
<div class="px-6 py-5 border-b border-base-300">
<h3 class="font-bold text-primary m-0">Gestion des utilisateurs</h3>
</div>
{% if users %}
<div style="overflow-x: auto;">
<table style="width: 100%; border-collapse: collapse;">
<div class="overflow-x-auto">
<table class="table table-sm">
<thead>
<tr style="border-bottom: 1px solid #2a2d32;">
<th style="padding: 12px 20px; text-align: left; color: var(--text-dim); font-size: 0.8rem; text-transform: uppercase;">Utilisateur</th>
<th style="padding: 12px 15px; text-align: left; color: var(--text-dim); font-size: 0.8rem; text-transform: uppercase;">Email</th>
<th style="padding: 12px 15px; text-align: center; color: var(--text-dim); font-size: 0.8rem; text-transform: uppercase;">Statut</th>
<th style="padding: 12px 15px; text-align: center; color: var(--text-dim); font-size: 0.8rem; text-transform: uppercase;">Role</th>
<th style="padding: 12px 15px; text-align: left; color: var(--text-dim); font-size: 0.8rem; text-transform: uppercase;">Derniere connexion</th>
<th style="padding: 12px 15px; text-align: left; color: var(--text-dim); font-size: 0.8rem; text-transform: uppercase;">Inscription</th>
<th style="padding: 12px 20px; text-align: center; color: var(--text-dim); font-size: 0.8rem; text-transform: uppercase;">Actions</th>
<tr>
<th>Utilisateur</th>
<th>Email</th>
<th class="text-center">Statut</th>
<th class="text-center">Role</th>
<th>Derniere connexion</th>
<th>Inscription</th>
<th class="text-center">Actions</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr style="border-bottom: 1px solid #2a2d32; {% if not user.is_active %}opacity: 0.5;{% endif %}">
<td style="padding: 12px 20px;">
<div style="font-weight: 600;">{{ user.username }}</div>
<tr class="{% if not user.is_active %}opacity-50{% endif %}">
<td>
<div class="font-semibold">{{ user.username }}</div>
{% if user.full_name %}
<div style="font-size: 0.8rem; color: var(--text-dim);">{{ user.full_name }}</div>
<div class="text-xs text-base-content/50">{{ user.full_name }}</div>
{% endif %}
</td>
<td style="padding: 12px 15px; color: var(--text-dim); font-size: 0.9rem;">{{ user.email or '-' }}</td>
<td style="padding: 12px 15px; text-align: center;">
<span style="display: inline-block; padding: 3px 10px; border-radius: 4px; font-size: 0.75rem; font-weight: 600; background: {% if user.is_active %}rgba(45,147,108,0.1); color: #2d936c{% else %}rgba(230,57,70,0.1); color: #e63946{% endif %};">
{% if user.is_active %}Actif{% else %}Inactif{% endif %}
</span>
<td class="text-base-content/60 text-sm">{{ user.email or '-' }}</td>
<td class="text-center">
{% if user.is_active %}
<span class="badge badge-success badge-sm">Actif</span>
{% else %}
<span class="badge badge-error badge-sm">Inactif</span>
{% endif %}
</td>
<td style="padding: 12px 15px; text-align: center;">
<span style="display: inline-block; padding: 3px 10px; border-radius: 4px; font-size: 0.75rem; font-weight: 600; background: {% if user.is_admin %}rgba(244,162,97,0.15); color: #f4a261{% else %}var(--bg-elevated); color: var(--text-dim){% endif %};">
{% if user.is_admin %}Admin{% else %}User{% endif %}
</span>
<td class="text-center">
{% if user.is_admin %}
<span class="badge badge-primary badge-sm">Admin</span>
{% else %}
<span class="badge badge-ghost badge-sm">User</span>
{% endif %}
</td>
<td style="padding: 12px 15px; color: var(--text-dim); font-size: 0.85rem;">
<td class="text-base-content/50 text-sm">
{{ user.last_login.strftime('%d/%m/%Y %H:%M') if user.last_login else '-' }}
</td>
<td style="padding: 12px 15px; color: var(--text-dim); font-size: 0.85rem;">
<td class="text-base-content/50 text-sm">
{{ user.created_at.strftime('%d/%m/%Y') if user.created_at else '-' }}
</td>
<td style="padding: 12px 20px; text-align: center; white-space: nowrap;">
<td class="text-center whitespace-nowrap">
{% if user.id != current_user.id %}
<button class="btn btn-sm {% if user.is_active %}btn-secondary{% else %}btn-accent{% endif %}"
<button class="btn btn-xs {% if user.is_active %}btn-ghost{% else %}btn-success{% endif %}"
hx-put="/api/admin/users/{{ user.id }}/toggle-active" hx-swap="none"
hx-on::after-request="htmx.ajax('GET', '/api/admin/ui', {target: '#admin-panel-content'})"
title="{% if user.is_active %}Desactiver{% else %}Activer{% endif %}">
{% if user.is_active %}Desactiver{% else %}Activer{% endif %}
</button>
<button class="btn btn-sm {% if user.is_admin %}btn-secondary{% else %}btn-accent{% endif %}"
<button class="btn btn-xs {% if user.is_admin %}btn-ghost{% else %}btn-success{% endif %}"
hx-put="/api/admin/users/{{ user.id }}/toggle-admin" hx-swap="none"
hx-on::after-request="htmx.ajax('GET', '/api/admin/ui', {target: '#admin-panel-content'})"
title="{% if user.is_admin %}Retrograder{% else %}Promouvoir{% endif %}">
{% if user.is_admin %}Retrograder{% else %}Admin{% endif %}
</button>
<button class="btn btn-sm btn-danger"
<button class="btn btn-xs btn-error"
hx-delete="/api/admin/users/{{ user.id }}" hx-swap="none"
hx-confirm="Supprimer {{ user.username }} ?"
hx-on::after-request="htmx.ajax('GET', '/api/admin/ui', {target: '#admin-panel-content'})"
@@ -87,7 +91,7 @@
<i class="fas fa-trash"></i>
</button>
{% else %}
<span style="color: var(--text-dim); font-size: 0.8rem;">Vous</span>
<span class="text-base-content/40 text-xs">Vous</span>
{% endif %}
</td>
</tr>
@@ -96,7 +100,7 @@
</table>
</div>
{% else %}
<div style="padding: 40px; text-align: center; color: var(--text-dim);">Aucun utilisateur</div>
<div class="p-10 text-center text-base-content/40">Aucun utilisateur</div>
{% endif %}
</div>
</div>