gmao/app_new/templates/admin/template_debug.html

105 lines
6.6 KiB
HTML
Raw Normal View History

{% 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 nest supprimé et le fonctionnement du site nest pas modifié.
« Utilisé probablement » reflète lordre des chargeurs Jinja ; vérifiez les routes avant toute suppression manuelle.
</div>
<div class="card border-0 shadow-sm mb-3">
<div class="card-header d-flex justify-content-between align-items-center">
<strong><i class="bi bi-diagram-3"></i> Architecture cible des templates</strong>
<span class="badge bg-primary">Convention recommandée</span>
</div>
<div class="card-body">
<p class="small text-muted">Le domaine métier est propriétaire de ses écrans. Le dossier global reste réservé au socle partagé ; une seule copie canonique doit être conservée par écran.</p>
<div class="row g-2">
{% for node in template_architecture %}
<div class="col-md-6 col-xl-3">
<div class="border rounded h-100 p-2 {% if 'transition' in node.label|lower %}bg-warning-subtle{% else %}bg-light{% endif %}">
<code class="d-block text-break">{{ node.path }}</code>
<strong class="small">{{ node.label }}</strong>
<p class="small text-muted mb-1">{{ node.description }}</p>
<div class="small">{% for child in node.children %}<span class="badge text-bg-secondary me-1 mb-1">{{ child }}</span>{% endfor %}</div>
</div>
</div>
{% endfor %}
</div>
</div>
</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">
<div>
<code>{{ logical_key }}</code>
<span class="text-muted small ms-2">Cible : <code>{{ templates[0].target_path }}</code></span>
</div>
{% 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>Architecture cible</th><th>Source</th><th>État estimé</th><th>Empreinte</th><th>Actions</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><code class="text-break small">{{ template.target_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><a class="btn btn-outline-primary btn-sm" href="{{ url_for('admin.template_debug_preview', path=template.path) }}" target="_blank" rel="noopener"><i class="bi bi-eye"></i> Prévisualiser</a></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 %}