gmao/app_new/templates/admin/data_management.html
2026-08-14 16:02:25 +00:00

451 lines
No EOL
25 KiB
HTML

{% extends "base.html" %}
{% block title %}Gestion des données — Admin GMAO{% endblock %}
{% block content %}
<div class="container-fluid">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1><i class="bi bi-database-exclamation text-danger"></i> Gestion des données</h1>
<a href="{{ url_for('admin.index') }}" class="btn btn-outline-secondary"><i class="bi bi-arrow-left"></i> Retour admin</a>
</div>
<div class="alert alert-danger">
<i class="bi bi-exclamation-triangle-fill"></i>
<strong>Attention :</strong> Ces actions sont irréversibles. Les équipements et interventions sont supprimés logiquement (masqués). Les mails sont supprimés définitivement.
</div>
<!-- Statistiques -->
<div class="row g-3 mb-4">
<div class="col-md-2">
<div class="card text-center border-warning">
<div class="card-body py-2">
<div class="display-6 text-warning">{{ stats.equipments }}</div>
<small class="text-muted">Équipements</small>
</div>
</div>
</div>
<div class="col-md-2">
<div class="card text-center border-primary">
<div class="card-body py-2">
<div class="display-6 text-primary">{{ stats.interventions }}</div>
<small class="text-muted">Interventions</small>
</div>
</div>
</div>
<div class="col-md-2">
<div class="card text-center border-info">
<div class="card-body py-2">
<div class="display-6 text-info">{{ stats.interventions_planifiees }}</div>
<small class="text-muted">Planifiées</small>
</div>
</div>
</div>
<div class="col-md-2">
<div class="card text-center border-success">
<div class="card-body py-2">
<div class="display-6 text-success">{{ stats.interventions_terminees }}</div>
<small class="text-muted">Terminées</small>
</div>
</div>
</div>
<div class="col-md-2">
<div class="card text-center border-secondary">
<div class="card-body py-2">
<div class="display-6 text-secondary">{{ stats.outlook_mails }}</div>
<small class="text-muted">Mails Outlook</small>
</div>
</div>
</div>
<div class="col-md-2">
<div class="card text-center border-dark">
<div class="card-body py-2">
<div class="display-6 text-dark">{{ stats.outlook_interpretations }}</div>
<small class="text-muted">Interp. Outlook</small>
</div>
</div>
</div>
<div class="col-md-2">
<div class="card text-center border-warning">
<div class="card-body py-2">
<div class="display-6 text-warning">{{ stats.ent_messages }}</div>
<small class="text-muted">Messages ENT</small>
</div>
</div>
</div>
<div class="col-md-2">
<div class="card text-center border-info">
<div class="card-body py-2">
<div class="display-6 text-info">{{ stats.ent_interpretations }}</div>
<small class="text-muted">Interp. ENT</small>
</div>
</div>
</div>
</div>
<!-- Suppressions en masse -->
<div class="row g-3 mb-4">
<div class="col-md-3">
<div class="card border-danger h-100">
<div class="card-header bg-danger text-white">
<i class="bi bi-trash"></i> Supprimer tous les équipements
</div>
<div class="card-body">
<p class="text-muted small">{{ stats.equipments }} équipement(s). Suppression logique.</p>
<form method="POST" action="{{ url_for('admin.delete_all_equipments') }}" onsubmit="return confirm('CONFIRMATION : Supprimer tous les équipements ?')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="mb-2">
<label class="form-label small">Tapez <strong>SUPPRIMER</strong> :</label>
<input type="text" name="confirm_text" class="form-control form-control-sm" placeholder="SUPPRIMER" required>
</div>
<button type="submit" class="btn btn-danger btn-sm w-100"><i class="bi bi-trash"></i> Supprimer équipements</button>
</form>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-danger h-100">
<div class="card-header bg-danger text-white">
<i class="bi bi-trash"></i> Supprimer toutes les interventions
</div>
<div class="card-body">
<p class="text-muted small">{{ stats.interventions }} intervention(s). Suppression logique.</p>
<form method="POST" action="{{ url_for('admin.delete_all_interventions') }}" onsubmit="return confirm('CONFIRMATION : Supprimer toutes les interventions ?')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="mb-2">
<label class="form-label small">Tapez <strong>SUPPRIMER</strong> :</label>
<input type="text" name="confirm_text" class="form-control form-control-sm" placeholder="SUPPRIMER" required>
</div>
<button type="submit" class="btn btn-danger btn-sm w-100"><i class="bi bi-trash"></i> Supprimer interventions</button>
</form>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-danger h-100">
<div class="card-header bg-danger text-white">
<i class="bi bi-trash"></i> Supprimer tous les mails Outlook
</div>
<div class="card-body">
<p class="text-muted small">{{ stats.outlook_mails }} mail(s) + {{ stats.outlook_interpretations }} interp. Suppression définitive.</p>
<form method="POST" action="{{ url_for('admin.delete_all_mails') }}" onsubmit="return confirm('CONFIRMATION : Supprimer tous les mails ET interprétations ?')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="mb-2">
<label class="form-label small">Tapez <strong>SUPPRIMER</strong> :</label>
<input type="text" name="confirm_text" class="form-control form-control-sm" placeholder="SUPPRIMER" required>
</div>
<button type="submit" class="btn btn-danger btn-sm w-100"><i class="bi bi-trash"></i> Supprimer mails Outlook</button>
</form>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-danger h-100">
<div class="card-header bg-danger text-white">
<i class="bi bi-trash"></i> Supprimer tous les messages ENT
</div>
<div class="card-body">
<p class="text-muted small">{{ stats.ent_messages }} message(s) + {{ stats.ent_interpretations }} interp. Suppression définitive.</p>
<form method="POST" action="{{ url_for('admin.delete_all_ent_messages') }}" onsubmit="return confirm('CONFIRMATION : Supprimer tous les messages ENT ET interprétations ?')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="mb-2">
<label class="form-label small">Tapez <strong>SUPPRIMER</strong> :</label>
<input type="text" name="confirm_text" class="form-control form-control-sm" placeholder="SUPPRIMER" required>
</div>
<button type="submit" class="btn btn-danger btn-sm w-100"><i class="bi bi-trash"></i> Supprimer messages ENT</button>
</form>
</div>
</div>
</div>
</div>
<!-- Liste des interventions -->
<div class="card mb-3">
<div class="card-header d-flex justify-content-between align-items-center">
<span><i class="bi bi-wrench"></i> Interventions en base ({{ stats.interventions }})</span>
</div>
{% if interventions.items %}
<div class="table-responsive">
<table class="table table-sm table-hover mb-0">
<thead>
<tr>
<th>#</th>
<th>Titre</th>
<th>Type</th>
<th>Statut</th>
<th>Équipement</th>
<th>Date</th>
<th class="text-end">Actions</th>
</tr>
</thead>
<tbody>
{% for interv in interventions.items %}
<tr>
<td>#{{ interv.id }}</td>
<td>{{ interv.title[:40] }}{% if interv.title|length > 40 %}…{% endif %}</td>
<td>
{% if interv.type == 'curatif' %}<span class="badge bg-danger">Curatif</span>
{% else %}<span class="badge bg-info">Préventif</span>{% endif %}
</td>
<td><span class="badge {{ interv.status|status_badge }}">{{ interv.status|format_status }}</span></td>
<td>{{ interv.equipment.name[:25] if interv.equipment else '—' }}</td>
<td>{{ interv.scheduled_date.strftime('%d/%m/%Y') if interv.scheduled_date else '—' }}</td>
<td class="text-end">
<form method="POST" action="{{ url_for('admin.delete_single_intervention', interv_id=interv.id) }}" style="display:inline;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="btn btn-sm btn-outline-danger" onclick="return confirm('Supprimer cette intervention ?')" title="Supprimer"><i class="bi bi-trash"></i></button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="card-footer">
{% if interventions.has_prev %}
<a href="{{ url_for('admin.data_management', interventions_page=interventions.prev_num) }}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-chevron-left"></i> Précédent</a>
{% endif %}
<span class="mx-2">Page {{ interventions.page }} / {{ interventions.pages }}</span>
{% if interventions.has_next %}
<a href="{{ url_for('admin.data_management', interventions_page=interventions.next_num) }}" class="btn btn-sm btn-outline-secondary">Suivant <i class="bi bi-chevron-right"></i></a>
{% endif %}
</div>
{% else %}
<div class="card-body text-center text-muted py-4">
<i class="bi bi-clock display-4"></i>
<p class="mt-2">Aucune intervention.</p>
</div>
{% endif %}
</div>
<!-- Liste des équipements -->
<div class="card mb-3">
<div class="card-header d-flex justify-content-between align-items-center">
<span><i class="bi bi-pc-display"></i> Équipements en base ({{ stats.equipments }})</span>
</div>
{% if equipments.items %}
<div class="table-responsive">
<table class="table table-sm table-hover mb-0">
<thead>
<tr>
<th>Équipement</th>
<th>Salle</th>
<th>Statut</th>
<th class="text-end">Actions</th>
</tr>
</thead>
<tbody>
{% for eq in equipments.items %}
<tr>
<td><a href="{{ url_for('equipments.detail', id=eq.id) }}">{{ eq.name[:30] }}{% if eq.name|length > 30 %}…{% endif %}</a></td>
<td>{{ eq.room.name if eq.room else '—' }}</td>
<td>{{ eq.status or '—' }}</td>
<td class="text-end">
<form method="POST" action="{{ url_for('admin.delete_single_equipment', equip_id=eq.id) }}" style="display:inline;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="btn btn-sm btn-outline-danger" onclick="return confirm('Supprimer cet équipement ?')" title="Supprimer"><i class="bi bi-trash"></i></button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="card-footer">
{% if equipments.has_prev %}
<a href="{{ url_for('admin.data_management', equipments_page=equipments.prev_num) }}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-chevron-left"></i> Précédent</a>
{% endif %}
<span class="mx-2">Page {{ equipments.page }} / {{ equipments.pages }}</span>
{% if equipments.has_next %}
<a href="{{ url_for('admin.data_management', equipments_page=equipments.next_num) }}" class="btn btn-sm btn-outline-secondary">Suivant <i class="bi bi-chevron-right"></i></a>
{% endif %}
</div>
{% else %}
<div class="card-body text-center text-muted py-4">
<i class="bi bi-gear-fill display-4"></i>
<p class="mt-2">Aucun équipement.</p>
</div>
{% endif %}
</div>
<!-- Liste des mails Outlook -->
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<span><i class="bi bi-envelope"></i> Mails Outlook en base ({{ stats.outlook_mails }})</span>
</div>
{% if mails.items %}
<div class="table-responsive">
<table class="table table-sm table-hover mb-0">
<thead>
<tr>
<th>Expéditeur</th>
<th>Sujet</th>
<th>Date</th>
<th>Interprété</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for mail in mails.items %}
<tr>
<td>
{{ mail.from_name or '—' }}
{% if mail.from_email %}<br><small class="text-muted">{{ mail.from_email }}</small>{% endif %}
</td>
<td>{{ mail.subject[:60] if mail.subject else '—' }}</td>
<td>{{ mail.received_at.strftime('%d/%m/%Y %H:%M') if mail.received_at else '—' }}</td>
<td>
{% if outlook_interp_map and mail.id in outlook_interp_map %}
{% set ointerp = outlook_interp_map[mail.id] %}
{% if ointerp.status == 'pending' %}
<span class="badge bg-warning text-dark">En attente</span>
{% elif ointerp.status == 'processed' %}
<span class="badge bg-success">Traitée</span>
{% elif ointerp.status == 'rejected' %}
<span class="badge bg-danger">Rejetée</span>
{% else %}
<span class="badge bg-info">{{ ointerp.status }}</span>
{% endif %}
{% if ointerp.analysis_type %}
<br><small class="text-muted">{{ ointerp.analysis_type }}</small>
{% endif %}
{% else %}
<span class="badge bg-secondary">Non interprété</span>
{% endif %}
</td>
<td>
<div class="btn-group btn-group-sm">
{% if not (outlook_interp_map and mail.id in outlook_interp_map) %}
<form method="POST" action="{{ url_for('admin.interpret_outlook_mail', mail_id=mail.id) }}" style="display:inline;" onsubmit="return confirm('Interpréter ce mail maintenant ?')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="page" value="{{ mails.page }}">
<button type="submit" class="btn btn-sm btn-outline-primary" title="Interpréter maintenant"><i class="bi bi-robot"></i></button>
</form>
{% endif %}
{% if mail.from_email %}
<form method="POST" action="{{ url_for('admin.ignore_sender') }}" style="display:inline;" onsubmit="return confirm('Ignorer cet expéditeur ? Il ne sera plus interprété par le watchdog.')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="sender" value="{{ mail.from_email }}">
<input type="hidden" name="page" value="{{ mails.page }}">
<button type="submit" class="btn btn-sm btn-outline-warning" title="Ignorer cet expéditeur"><i class="bi bi-person-x"></i></button>
</form>
{% endif %}
<form method="POST" action="{{ url_for('admin.delete_single_mail', mail_id=mail.id) }}" style="display:inline;" onsubmit="return confirm('Supprimer ce mail ?')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="page" value="{{ mails.page }}">
<button type="submit" class="btn btn-sm btn-outline-danger" title="Supprimer"><i class="bi bi-trash"></i></button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="card-footer">
{% if mails.has_prev %}
<a href="{{ url_for('admin.data_management', page=mails.prev_num) }}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-chevron-left"></i> Précédent</a>
{% endif %}
<span class="mx-2">Page {{ mails.page }} / {{ mails.pages }}</span>
{% if mails.has_next %}
<a href="{{ url_for('admin.data_management', page=mails.next_num) }}" class="btn btn-sm btn-outline-secondary">Suivant <i class="bi bi-chevron-right"></i></a>
{% endif %}
</div>
{% else %}
<div class="card-body text-center text-muted py-4">
<i class="bi bi-inbox display-4"></i>
<p class="mt-2">Aucun mail en base.</p>
</div>
{% endif %}
</div>
<!-- Liste des messages ENT -->
<div class="card mt-3">
<div class="card-header d-flex justify-content-between align-items-center">
<span><i class="bi bi-broadcast"></i> Messages ENT en base ({{ stats.ent_messages }})</span>
</div>
{% if ent_messages.items %}
<div class="table-responsive">
<table class="table table-sm table-hover mb-0">
<thead>
<tr>
<th>Expéditeur</th>
<th>Sujet</th>
<th>Date</th>
<th>Interprété</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for msg in ent_messages.items %}
<tr>
<td>
{{ msg.sender_name or '—' }}
{% if msg.original_sender %}<br><small class="text-muted">Transféré par: {{ msg.original_sender }}</small>{% endif %}
</td>
<td>{{ msg.subject[:60] if msg.subject else '—' }}</td>
<td>{{ msg.date.strftime('%d/%m/%Y %H:%M') if msg.date else '—' }}</td>
<td>
{% if ent_interp_map and msg.id in ent_interp_map %}
{% set interp = ent_interp_map[msg.id] %}
{% if interp.status == 'pending' %}
<span class="badge bg-warning text-dark">En attente</span>
{% elif interp.status == 'processed' %}
<span class="badge bg-success">Traitée</span>
{% elif interp.status == 'rejected' %}
<span class="badge bg-danger">Rejetée</span>
{% else %}
<span class="badge bg-info">{{ interp.status }}</span>
{% endif %}
{% if interp.analysis_type %}
<br><small class="text-muted">{{ interp.analysis_type }}</small>
{% endif %}
{% else %}
<span class="badge bg-secondary">Non interprété</span>
{% endif %}
</td>
<td>
<div class="btn-group btn-group-sm">
{% if not (ent_interp_map and msg.id in ent_interp_map) %}
<form method="POST" action="{{ url_for('admin.interpret_ent_message', msg_id=msg.id) }}" style="display:inline;" onsubmit="return confirm('Interpréter ce message maintenant ?')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="page" value="{{ ent_messages.page }}">
<button type="submit" class="btn btn-sm btn-outline-primary" title="Interpréter maintenant"><i class="bi bi-robot"></i></button>
</form>
{% endif %}
{% if msg.sender_name %}
<form method="POST" action="{{ url_for('admin.ignore_sender') }}" style="display:inline;" onsubmit="return confirm('Ignorer cet expéditeur ? Il ne sera plus interprété par le watchdog.')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="sender" value="{{ msg.sender_name }}">
<input type="hidden" name="page" value="{{ ent_messages.page }}">
<button type="submit" class="btn btn-sm btn-outline-warning" title="Ignorer cet expéditeur"><i class="bi bi-person-x"></i></button>
</form>
{% endif %}
<form method="POST" action="{{ url_for('admin.delete_single_ent_message', msg_id=msg.id) }}" style="display:inline;" onsubmit="return confirm('Supprimer ce message ENT ?')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="page" value="{{ ent_messages.page }}">
<button type="submit" class="btn btn-sm btn-outline-danger" title="Supprimer"><i class="bi bi-trash"></i></button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="card-footer">
{% if ent_messages.has_prev %}
<a href="{{ url_for('admin.data_management', page=ent_messages.prev_num) }}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-chevron-left"></i> Précédent</a>
{% endif %}
<span class="mx-2">Page {{ ent_messages.page }} / {{ ent_messages.pages }}</span>
{% if ent_messages.has_next %}
<a href="{{ url_for('admin.data_management', page=ent_messages.next_num) }}" class="btn btn-sm btn-outline-secondary">Suivant <i class="bi bi-chevron-right"></i></a>
{% endif %}
</div>
{% else %}
<div class="card-body text-center text-muted py-4">
<i class="bi bi-broadcast display-4"></i>
<p class="mt-2">Aucun message ENT en base.</p>
</div>
{% endif %}
</div>
</div>
{% endblock %}