Chaque carte (Total, Actifs, Succès, Échecs) est maintenant un lien vers /?filter=<type>. Le tableau affiche uniquement les jobs correspondants, avec un badge de filtre actif et un bouton "Effacer" dans l'en-tête. Cliquer à nouveau sur la même carte désactive le filtre.
203 lines
11 KiB
HTML
203 lines
11 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Dashboard — CronHub{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="mb-8 flex items-center justify-between">
|
|
<div>
|
|
<h2 class="text-2xl font-bold text-slate-800">Dashboard</h2>
|
|
<p class="text-slate-500 text-sm mt-1">Vue d'ensemble des cron jobs</p>
|
|
</div>
|
|
<a href="/jobs/new"
|
|
class="flex items-center gap-2 bg-orange-500 hover:bg-orange-600 text-white px-4 py-2 rounded-lg font-medium transition">
|
|
<i class="fas fa-plus"></i> Nouveau job
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Stats -->
|
|
<div class="grid grid-cols-4 gap-4 mb-8">
|
|
<a href="{{ '/' if active_filter == 'total' else '/?filter=total' }}"
|
|
class="bg-white rounded-xl p-5 shadow-sm border transition hover:shadow-md
|
|
{{ 'border-blue-400 ring-2 ring-blue-200' if active_filter == 'total' else 'border-slate-100 hover:border-blue-200' }}">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
|
|
<i class="fas fa-list text-blue-600"></i>
|
|
</div>
|
|
<div>
|
|
<p class="text-2xl font-bold text-slate-800">{{ stats.total }}</p>
|
|
<p class="text-xs text-slate-500">Total</p>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
<a href="{{ '/' if active_filter == 'active' else '/?filter=active' }}"
|
|
class="bg-white rounded-xl p-5 shadow-sm border transition hover:shadow-md
|
|
{{ 'border-green-400 ring-2 ring-green-200' if active_filter == 'active' else 'border-slate-100 hover:border-green-200' }}">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-10 h-10 bg-green-100 rounded-lg flex items-center justify-center">
|
|
<i class="fas fa-circle-check text-green-600"></i>
|
|
</div>
|
|
<div>
|
|
<p class="text-2xl font-bold text-slate-800">{{ stats.active }}</p>
|
|
<p class="text-xs text-slate-500">Actifs</p>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
<a href="{{ '/' if active_filter == 'success' else '/?filter=success' }}"
|
|
class="bg-white rounded-xl p-5 shadow-sm border transition hover:shadow-md
|
|
{{ 'border-emerald-400 ring-2 ring-emerald-200' if active_filter == 'success' else 'border-slate-100 hover:border-emerald-200' }}">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-10 h-10 bg-emerald-100 rounded-lg flex items-center justify-center">
|
|
<i class="fas fa-check text-emerald-600"></i>
|
|
</div>
|
|
<div>
|
|
<p class="text-2xl font-bold text-slate-800">{{ stats.success }}</p>
|
|
<p class="text-xs text-slate-500">Succès</p>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
<a href="{{ '/' if active_filter == 'failed' else '/?filter=failed' }}"
|
|
class="bg-white rounded-xl p-5 shadow-sm border transition hover:shadow-md
|
|
{{ 'border-red-400 ring-2 ring-red-200' if active_filter == 'failed' else 'border-slate-100 hover:border-red-200' }}">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-10 h-10 bg-red-100 rounded-lg flex items-center justify-center">
|
|
<i class="fas fa-triangle-exclamation text-red-600"></i>
|
|
</div>
|
|
<div>
|
|
<p class="text-2xl font-bold text-slate-800">{{ stats.failed }}</p>
|
|
<p class="text-xs text-slate-500">Échecs</p>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Jobs table -->
|
|
<div class="bg-white rounded-xl shadow-sm border border-slate-100 overflow-hidden">
|
|
<div class="px-6 py-4 border-b border-slate-100 flex items-center justify-between">
|
|
<div class="flex items-center gap-3">
|
|
<h3 class="font-semibold text-slate-700">Jobs planifiés</h3>
|
|
{% if active_filter %}
|
|
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-semibold
|
|
{% if active_filter == 'failed' %}bg-red-100 text-red-700
|
|
{% elif active_filter == 'success' %}bg-emerald-100 text-emerald-700
|
|
{% elif active_filter == 'active' %}bg-green-100 text-green-700
|
|
{% else %}bg-blue-100 text-blue-700{% endif %}">
|
|
<i class="fas fa-filter text-xs"></i>
|
|
{{ {'total': 'Tous', 'active': 'Actifs', 'success': 'Succès', 'failed': 'Échecs'}[active_filter] }}
|
|
</span>
|
|
<a href="/" class="text-xs text-slate-400 hover:text-slate-600 transition" title="Effacer le filtre">
|
|
<i class="fas fa-xmark"></i> Effacer
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
<span class="text-xs text-slate-400">Rafraîchissement auto toutes les 30s</span>
|
|
</div>
|
|
|
|
{% if jobs %}
|
|
<div class="overflow-x-auto">
|
|
<table class="w-full">
|
|
<thead>
|
|
<tr class="bg-slate-50 text-left text-xs font-semibold text-slate-500 uppercase tracking-wide">
|
|
<th class="px-6 py-3">Nom</th>
|
|
<th class="px-6 py-3">Schedule</th>
|
|
<th class="px-6 py-3">Commande</th>
|
|
<th class="px-6 py-3">Statut</th>
|
|
<th class="px-6 py-3">Dernière exéc.</th>
|
|
<th class="px-6 py-3">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-slate-100">
|
|
{% for job in jobs %}
|
|
<tr class="hover:bg-slate-50 transition">
|
|
<td class="px-6 py-4">
|
|
<a href="/jobs/{{ job.id }}" class="font-semibold text-slate-800 hover:text-orange-600 transition">
|
|
{{ job.name }}
|
|
</a>
|
|
{% if job.description %}
|
|
<p class="text-xs text-slate-400 truncate max-w-xs">{{ job.description }}</p>
|
|
{% endif %}
|
|
</td>
|
|
<td class="px-6 py-4">
|
|
<code class="text-xs bg-slate-100 px-2 py-1 rounded font-mono text-slate-600">{{ job.schedule }}</code>
|
|
</td>
|
|
<td class="px-6 py-4 max-w-xs">
|
|
<code class="text-xs text-slate-500 truncate block">{{ job.command }}</code>
|
|
</td>
|
|
<td class="px-6 py-4">
|
|
{% if not job.enabled %}
|
|
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-semibold bg-slate-100 text-slate-500">
|
|
<i class="fas fa-pause text-xs"></i> Désactivé
|
|
</span>
|
|
{% elif job.last_status == 'running' %}
|
|
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-semibold bg-blue-100 text-blue-700">
|
|
<span class="pulse-dot w-1.5 h-1.5 bg-blue-500 rounded-full"></span> En cours
|
|
</span>
|
|
{% elif job.last_status == 'success' %}
|
|
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-semibold bg-green-100 text-green-700">
|
|
<i class="fas fa-check text-xs"></i> Succès
|
|
</span>
|
|
{% elif job.last_status == 'failed' %}
|
|
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-semibold bg-red-100 text-red-700">
|
|
<i class="fas fa-xmark text-xs"></i> Échec
|
|
</span>
|
|
{% else %}
|
|
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-semibold bg-orange-100 text-orange-700">
|
|
<i class="fas fa-clock text-xs"></i> Actif
|
|
</span>
|
|
{% endif %}
|
|
</td>
|
|
<td class="px-6 py-4 text-xs text-slate-400">
|
|
{% if job.last_run %}
|
|
{{ job.last_run | replace("T", " ") | truncate(16, True, '') }}
|
|
{% else %}
|
|
<span class="text-slate-300">Jamais</span>
|
|
{% endif %}
|
|
</td>
|
|
<td class="px-6 py-4">
|
|
<div class="flex items-center gap-2">
|
|
<!-- Toggle -->
|
|
<form method="POST" action="/jobs/{{ job.id }}/toggle">
|
|
<button type="submit"
|
|
title="{% if job.enabled %}Désactiver{% else %}Activer{% endif %}"
|
|
class="w-8 h-8 flex items-center justify-center rounded-lg transition
|
|
{% if job.enabled %}bg-green-100 text-green-600 hover:bg-green-200{% else %}bg-slate-100 text-slate-400 hover:bg-slate-200{% endif %}">
|
|
<i class="fas {% if job.enabled %}fa-toggle-on{% else %}fa-toggle-off{% endif %} text-sm"></i>
|
|
</button>
|
|
</form>
|
|
<!-- Run now -->
|
|
<form method="POST" action="/jobs/{{ job.id }}/run-now">
|
|
<button type="submit" title="Exécuter maintenant"
|
|
class="w-8 h-8 flex items-center justify-center rounded-lg bg-orange-100 text-orange-600 hover:bg-orange-200 transition">
|
|
<i class="fas fa-play text-xs"></i>
|
|
</button>
|
|
</form>
|
|
<!-- Edit -->
|
|
<a href="/jobs/{{ job.id }}/edit" title="Modifier"
|
|
class="w-8 h-8 flex items-center justify-center rounded-lg bg-blue-100 text-blue-600 hover:bg-blue-200 transition">
|
|
<i class="fas fa-pen text-xs"></i>
|
|
</a>
|
|
<!-- Delete -->
|
|
<form id="del-{{ job.id }}" method="POST" action="/jobs/{{ job.id }}/delete">
|
|
<button type="button" title="Supprimer"
|
|
onclick="confirmDelete('del-{{ job.id }}')"
|
|
class="w-8 h-8 flex items-center justify-center rounded-lg bg-red-100 text-red-600 hover:bg-red-200 transition">
|
|
<i class="fas fa-trash text-xs"></i>
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="py-16 text-center">
|
|
<i class="fas fa-clock text-4xl text-slate-200 mb-4"></i>
|
|
<p class="text-slate-400 font-medium">Aucun job planifié</p>
|
|
<a href="/jobs/new" class="mt-4 inline-flex items-center gap-2 text-orange-500 hover:text-orange-600 text-sm font-medium">
|
|
<i class="fas fa-plus"></i> Créer votre premier job
|
|
</a>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endblock %}
|