78 lines
5 KiB
HTML
78 lines
5 KiB
HTML
|
|
{% extends "base.html" %}
|
|||
|
|
{% block title %}Audit des templates — Administration{% endblock %}
|
|||
|
|
{% block content %}
|
|||
|
|
<div class="container-fluid py-3">
|
|||
|
|
<div class="d-flex flex-column flex-lg-row justify-content-between align-items-lg-center gap-2 mb-3">
|
|||
|
|
<div>
|
|||
|
|
<h1 class="h3 mb-1"><i class="bi bi-files"></i> Audit des templates</h1>
|
|||
|
|
<p class="text-muted mb-0">Repérez les copies identiques ou divergentes et marquez celles qui ne doivent plus être utilisées.</p>
|
|||
|
|
</div>
|
|||
|
|
<a href="{{ url_for('admin.data_management') }}" class="btn btn-outline-secondary btn-sm"><i class="bi bi-arrow-left"></i> Gestion des données</a>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="alert alert-info py-2">
|
|||
|
|
<i class="bi bi-info-circle"></i> Le marquage <strong>obsolète</strong> est documentaire : aucun fichier n’est supprimé et le fonctionnement du site n’est pas modifié.
|
|||
|
|
« Utilisé probablement » reflète l’ordre des chargeurs Jinja ; vérifiez les routes avant toute suppression manuelle.
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="row g-2 mb-3">
|
|||
|
|
{% for label, value, color in [('Templates', stats.total, 'primary'), ('Groupes doublons', stats.duplicate_groups, 'warning'), ('Identiques', stats.identical_groups, 'success'), ('Divergents', stats.divergent_groups, 'danger'), ('Marqués obsolètes', stats.obsolete, 'secondary')] %}
|
|||
|
|
<div class="col-6 col-md"><div class="card border-0 shadow-sm h-100"><div class="card-body py-2"><div class="small text-muted">{{ label }}</div><div class="h4 text-{{ color }} mb-0">{{ value }}</div></div></div></div>
|
|||
|
|
{% endfor %}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="btn-group btn-group-sm mb-3" role="group">
|
|||
|
|
<a class="btn {{ 'btn-primary' if selected_filter == 'duplicates' else 'btn-outline-primary' }}" href="{{ url_for('admin.template_debug', filter='duplicates') }}">Doublons</a>
|
|||
|
|
<a class="btn {{ 'btn-primary' if selected_filter == 'obsolete' else 'btn-outline-primary' }}" href="{{ url_for('admin.template_debug', filter='obsolete') }}">Obsolètes</a>
|
|||
|
|
<a class="btn {{ 'btn-primary' if selected_filter == 'all' else 'btn-outline-primary' }}" href="{{ url_for('admin.template_debug', filter='all') }}">Tous</a>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{% if groups %}
|
|||
|
|
{% for logical_key, templates in groups|dictsort %}
|
|||
|
|
<div class="card border-0 shadow-sm mb-3">
|
|||
|
|
<div class="card-header d-flex flex-wrap justify-content-between gap-2 align-items-center">
|
|||
|
|
<code>{{ logical_key }}</code>
|
|||
|
|
{% if templates[0].duplicate_count > 1 %}
|
|||
|
|
<span class="badge bg-{{ 'success' if templates[0].comparison == 'identique' else 'danger' }}">{{ templates[0].duplicate_count }} copies — {{ templates[0].comparison }}</span>
|
|||
|
|
{% else %}<span class="badge bg-secondary">unique</span>{% endif %}
|
|||
|
|
</div>
|
|||
|
|
<div class="table-responsive">
|
|||
|
|
<table class="table table-sm align-middle mb-0">
|
|||
|
|
<thead><tr><th>Fichier</th><th>Source</th><th>État estimé</th><th>Empreinte</th><th style="min-width:300px">Décision</th></tr></thead>
|
|||
|
|
<tbody>
|
|||
|
|
{% for template in templates %}
|
|||
|
|
<tr class="{% if template.mark and template.mark.status == 'obsolete' %}table-secondary{% endif %}">
|
|||
|
|
<td><code class="text-break">{{ template.path }}</code></td>
|
|||
|
|
<td><span class="badge bg-{{ 'primary' if template.source == 'global' else 'info' }}">{{ template.source }}</span></td>
|
|||
|
|
<td>
|
|||
|
|
{% if template.mark and template.mark.status == 'obsolete' %}<span class="badge bg-secondary">Obsolète</span>
|
|||
|
|
{% elif template.probable_active %}<span class="badge bg-success">Utilisé probablement</span>
|
|||
|
|
{% else %}<span class="badge bg-warning text-dark">Masqué probablement</span>{% endif %}
|
|||
|
|
</td>
|
|||
|
|
<td><code title="{{ template.hash }}">{{ template.hash[:10] }}</code></td>
|
|||
|
|
<td>
|
|||
|
|
<form method="post" action="{{ url_for('admin.template_debug_mark') }}" class="d-flex gap-1">
|
|||
|
|
<input type="hidden" name="path" value="{{ template.path }}">
|
|||
|
|
<input type="hidden" name="return_filter" value="{{ selected_filter }}">
|
|||
|
|
{% if template.mark and template.mark.status == 'obsolete' %}
|
|||
|
|
<span class="small text-muted flex-grow-1">{{ template.mark.notes or 'Aucune note' }}</span>
|
|||
|
|
<button class="btn btn-outline-success btn-sm" name="action" value="restore"><i class="bi bi-arrow-counterclockwise"></i> Réactiver</button>
|
|||
|
|
{% else %}
|
|||
|
|
<input class="form-control form-control-sm" name="notes" placeholder="Raison / remplacement conseillé">
|
|||
|
|
<button class="btn btn-outline-danger btn-sm text-nowrap" name="action" value="obsolete"><i class="bi bi-archive"></i> Obsolète</button>
|
|||
|
|
{% endif %}
|
|||
|
|
</form>
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
{% endfor %}
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
{% endfor %}
|
|||
|
|
{% else %}
|
|||
|
|
<div class="alert alert-secondary">Aucun template ne correspond à ce filtre.</div>
|
|||
|
|
{% endif %}
|
|||
|
|
</div>
|
|||
|
|
{% endblock %}
|