- QuickWin: campagnes patching rapide avec exclusions générales (OS/reboot) et spécifiques (applicatifs) - Config serveurs: pagination, filtres (search, env, domain, zone, per_page), dry run, bulk edit - Détail campagne: pagination hprod/prod séparée, filtres (search, status, domain), section prod masquée si hprod non terminé - Auth: redirection qw_only vers /quickwin, profil lecture seule quickwin - Serveurs: filtres OS (Linux/Windows) et Owner (secops/ipop/na), exclusion EOL - Sidebar: lien QuickWin conditionné sur permission campaigns ou quickwin Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
137 lines
7.4 KiB
HTML
137 lines
7.4 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}QuickWin{% endblock %}
|
|
{% block content %}
|
|
<div class="flex items-center justify-between mb-6">
|
|
<div>
|
|
<h1 class="text-2xl font-bold" style="color:#00d4ff">QuickWin</h1>
|
|
<p class="text-sm text-gray-500">Campagnes patching rapide — exclusions par serveur — hors-prod d'abord — pas de reboot nécessaire</p>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
{% if can_create %}
|
|
<a href="/quickwin/config" class="btn-sm" style="background:#1e3a5f;color:#00d4ff;padding:6px 16px">Config exclusions</a>
|
|
<button onclick="document.getElementById('createModal').style.display='flex'" class="btn-primary" style="padding:6px 16px;font-size:0.85rem">+ Nouveau QuickWin</button>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
{% if msg %}
|
|
<div style="background:#1a5a2e;color:#8f8;padding:8px 16px;border-radius:6px;margin-bottom:12px;font-size:0.85rem">
|
|
{% if msg == 'deleted' %}Campagne supprimée{% elif msg == 'error' %}Erreur création{% elif msg == 'no_servers' %}Aucun serveur configuré{% else %}{{ msg }}{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- KPIs -->
|
|
<div class="grid grid-cols-4 gap-4 mb-6">
|
|
<div class="card p-4 text-center">
|
|
<div class="text-3xl font-bold" style="color:#00d4ff">{{ runs|length }}</div>
|
|
<div class="text-xs text-gray-500">Campagnes</div>
|
|
</div>
|
|
<div class="card p-4 text-center">
|
|
<div class="text-3xl font-bold" style="color:#00ff88">{{ config_count }}</div>
|
|
<div class="text-xs text-gray-500">Serveurs configurés</div>
|
|
</div>
|
|
<div class="card p-4 text-center">
|
|
<div class="text-3xl font-bold" style="color:#ffcc00">S{{ current_week }}</div>
|
|
<div class="text-xs text-gray-500">Semaine courante</div>
|
|
</div>
|
|
<div class="card p-4 text-center">
|
|
<div class="text-3xl font-bold" style="color:#fff">{{ current_year }}</div>
|
|
<div class="text-xs text-gray-500">Année</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Runs list -->
|
|
<div class="card">
|
|
<div class="p-4 flex items-center justify-between" style="border-bottom:1px solid #1e3a5f">
|
|
<h2 class="text-sm font-bold" style="color:#00d4ff">CAMPAGNES QUICKWIN</h2>
|
|
<span class="text-xs text-gray-500">{{ runs|length }} campagne(s)</span>
|
|
</div>
|
|
<div class="table-wrap">
|
|
<table class="table-cyber w-full">
|
|
<thead>
|
|
<tr>
|
|
<th class="px-3 py-2">ID</th>
|
|
<th class="px-3 py-2">Semaine</th>
|
|
<th class="px-3 py-2">Label</th>
|
|
<th class="px-3 py-2">Statut</th>
|
|
<th class="px-3 py-2">Créé par</th>
|
|
<th class="px-3 py-2">Serveurs</th>
|
|
<th class="px-3 py-2">H-Prod</th>
|
|
<th class="px-3 py-2">Prod</th>
|
|
<th class="px-3 py-2">Patchés</th>
|
|
<th class="px-3 py-2">KO</th>
|
|
<th class="px-3 py-2">Date</th>
|
|
<th class="px-3 py-2">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for r in runs %}
|
|
<tr onclick="window.location='/quickwin/{{ r.id }}'" style="cursor:pointer">
|
|
<td class="px-3 py-2" style="color:#00d4ff;font-weight:bold">#{{ r.id }}</td>
|
|
<td class="px-3 py-2">S{{ '%02d'|format(r.week_number) }} {{ r.year }}</td>
|
|
<td class="px-3 py-2">{{ r.label }}</td>
|
|
<td class="px-3 py-2">
|
|
{% if r.status == 'draft' %}<span class="badge badge-gray">Brouillon</span>
|
|
{% elif r.status == 'hprod_in_progress' %}<span class="badge badge-yellow">H-Prod en cours</span>
|
|
{% elif r.status == 'hprod_done' %}<span class="badge badge-blue">H-Prod terminé</span>
|
|
{% elif r.status == 'prod_in_progress' %}<span class="badge badge-yellow">Prod en cours</span>
|
|
{% elif r.status == 'completed' %}<span class="badge badge-green">Terminé</span>
|
|
{% elif r.status == 'cancelled' %}<span class="badge badge-red">Annulé</span>
|
|
{% endif %}
|
|
</td>
|
|
<td class="px-3 py-2 text-gray-400">{{ r.created_by_name or '?' }}</td>
|
|
<td class="px-3 py-2 text-center">{{ r.total_entries }}</td>
|
|
<td class="px-3 py-2 text-center">{{ r.hprod_count }}</td>
|
|
<td class="px-3 py-2 text-center">{{ r.prod_count }}</td>
|
|
<td class="px-3 py-2 text-center" style="color:#00ff88">{{ r.patched_count }}</td>
|
|
<td class="px-3 py-2 text-center" style="color:#ff3366">{{ r.failed_count }}</td>
|
|
<td class="px-3 py-2 text-gray-500 text-xs">{{ r.created_at.strftime('%d/%m %H:%M') if r.created_at else '' }}</td>
|
|
<td class="px-3 py-2">
|
|
<a href="/quickwin/{{ r.id }}" class="btn-sm" style="background:#1e3a5f;color:#00d4ff">Voir</a>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
{% if not runs %}
|
|
<tr><td colspan="12" class="px-3 py-8 text-center text-gray-500">Aucune campagne QuickWin</td></tr>
|
|
{% endif %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Create modal -->
|
|
<div id="createModal" style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.7);z-index:100;align-items:center;justify-content:center" onclick="if(event.target===this)this.style.display='none'">
|
|
<div class="card" style="width:500px;max-width:90vw;padding:24px">
|
|
<h3 style="color:#00d4ff;font-size:1.1rem;font-weight:bold;margin-bottom:16px">Nouveau QuickWin</h3>
|
|
<form method="post" action="/quickwin/create">
|
|
<div class="mb-3">
|
|
<label class="text-xs text-gray-400 block mb-1">Label</label>
|
|
<input type="text" name="label" placeholder="Quick Win S{{ '%02d'|format(current_week) }} {{ current_year }}" style="width:100%">
|
|
</div>
|
|
<div class="flex gap-3 mb-3">
|
|
<div class="flex-1">
|
|
<label class="text-xs text-gray-400 block mb-1">Semaine</label>
|
|
<input type="number" name="week_number" value="{{ current_week }}" min="1" max="53" style="width:100%">
|
|
</div>
|
|
<div class="flex-1">
|
|
<label class="text-xs text-gray-400 block mb-1">Année</label>
|
|
<input type="number" name="year" value="{{ current_year }}" style="width:100%">
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="text-xs text-gray-400 block mb-1">Serveurs (IDs, vide = tous les configurés)</label>
|
|
<input type="text" name="server_ids" placeholder="Laisser vide pour tous les serveurs configurés" style="width:100%">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="text-xs text-gray-400 block mb-1">Notes</label>
|
|
<textarea name="notes" rows="2" style="width:100%" placeholder="Commentaires..."></textarea>
|
|
</div>
|
|
<div class="flex gap-2 justify-end mt-4">
|
|
<button type="button" class="btn-sm" style="background:#333;color:#ccc;padding:6px 16px" onclick="document.getElementById('createModal').style.display='none'">Annuler</button>
|
|
<button type="submit" class="btn-primary" style="padding:6px 20px">Créer</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|