gmao/app_new/ent/templates/ent/message_detail.html

326 lines
No EOL
17 KiB
HTML

{% extends "base.html" %}
{% block title %}{{ message.subject or '(sans sujet)' }} - ENT77{% endblock %}
{% block content %}
<div class="container-fluid">
<div class="mb-3">
<a href="{{ url_for('ent.inbox') }}" class="btn btn-outline-secondary btn-sm">
<i class="bi bi-arrow-left"></i> Retour à la boîte
</a>
</div>
<div class="row">
<!-- Message à gauche -->
<div class="col-lg-8">
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between align-items-start">
<div>
<h4 class="mb-1">{{ message.subject or '(sans sujet)' }}</h4>
<div class="text-muted">
<strong>De:</strong> {{ message.sender_name or 'Inconnu' }}
{% if message.sender_email %}
&lt;{{ message.sender_email }}&gt;
{% endif %}
</div>
{% if message.recipients %}
<div class="text-muted small">
<strong>À:</strong> {{ message.recipients }}
</div>
{% endif %}
</div>
<div class="text-end">
<div class="text-muted small">
{{ message.date|datetime_fmt if message.date else '-' }}
</div>
{% if message.has_attachment %}
<span class="badge bg-info"><i class="bi bi-paperclip"></i> Pièce jointe</span>
{% endif %}
{% if message.is_forwarded %}
<span class="badge bg-secondary">Transféré</span>
{% endif %}
</div>
</div>
</div>
<div class="card-body">
<div class="email-content">
{{ message.body|safe if message.body else '<p class="text-muted">(pas de contenu)</p>' }}
</div>
</div>
<!-- Pièces jointes -->
{% if attachments %}
<div class="card-footer">
<h6><i class="bi bi-paperclip"></i> Pièces jointes</h6>
<div>
{% for att in attachments %}
<div class="d-inline-block me-2 mb-2">
<a href="{{ url_for('ent.download_attachment', att_id=att.id) }}"
class="btn btn-outline-secondary btn-sm">
<i class="bi bi-download"></i> {{ att.name }}
<small class="text-muted">({{ (att.size / 1024)|round(1) }} KB)</small>
</a>
</div>
{% endfor %}
</div>
</div>
{% endif %}
</div>
</div>
<!-- Panneau Hermes à droite -->
<div class="col-lg-4">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<span><i class="bi bi-robot"></i> Interprétation GMAO</span>
<div class="btn-group btn-group-sm">
<a href="{{ url_for('outlook.context_config') }}" class="btn btn-outline-secondary" title="Configurer le contexte GMAO">
<i class="bi bi-gear"></i>
</a>
<button class="btn btn-outline-primary" onclick="showInterpretForm()">
<i class="bi bi-plus"></i> Nouvelle
</button>
</div>
</div>
<div class="card-body">
<!-- Alerte contexte GMAO -->
<div class="alert alert-info small mb-3" id="context-alert">
<i class="bi bi-info-circle"></i>
<span id="context-status">Chargement du contexte...</span>
<a href="{{ url_for('outlook.context_config') }}" class="alert-link">Modifier</a>
</div>
<!-- Formulaire nouvelle interprétation -->
<div id="interpret-form" style="display: none;">
<div class="mb-3">
<label class="form-label">Contexte additionnel (optionnel)</label>
<textarea id="user-context" class="form-control" rows="3"
placeholder="Ajoutez un contexte spécifique pour ce message..."></textarea>
<div class="form-text">Le contexte GMAO global sera automatiquement inclus.</div>
</div>
<button id="interpret-btn" class="btn btn-primary w-100" onclick="interpretMessage()">
<span class="spinner-border spinner-border-sm d-none" id="interpret-spinner"></span>
<i class="bi bi-magic"></i> Interpréter avec Hermes
</button>
</div>
<!-- Liste des interprétations -->
<div id="interpretations-list">
{% if interpretations %}
{% for interp in interpretations %}
<div class="interpretation-card mb-3 p-3 border rounded" id="interp-{{ interp.id }}">
<div class="d-flex justify-content-between align-items-start mb-2">
<small class="text-muted">{{ interp.created_at|datetime_fmt if interp.created_at else '-' }}</small>
<span class="badge
{% if interp.status == 'pending' %}bg-warning text-dark
{% elif interp.status == 'accepted' %}bg-success
{% else %}bg-secondary{% endif %}">
{{ 'En attente' if interp.status == 'pending' else ('Acceptée' if interp.status == 'accepted' else 'Rejetée') }}
</span>
</div>
{% if interp.analysis_type %}
<!-- Analyse structurée -->
<div class="analysis-structured mb-2">
{% if interp.analysis_type == 'curative' %}
<span class="badge bg-danger mb-2"><i class="bi bi-exclamation-triangle"></i> Curative</span>
{% elif interp.analysis_type == 'preventive' %}
<span class="badge bg-primary mb-2"><i class="bi bi-calendar-check"></i> Préventive</span>
{% elif interp.analysis_type == 'administrative' %}
<span class="badge bg-warning text-dark mb-2"><i class="bi bi-clipboard-check"></i> Administrative</span>
{% elif interp.analysis_type == 'formation' %}
<span class="badge bg-info text-dark mb-2"><i class="bi bi-mortarboard"></i> Formation</span>
{% else %}
<span class="badge bg-secondary mb-2"><i class="bi bi-info-circle"></i> Information</span>
{% endif %}
{% if interp.analysis_action == 'create' %}
<span class="badge bg-success mb-2"><i class="bi bi-plus-circle"></i> Nouvelle intervention</span>
{% elif interp.analysis_action == 'update' %}
<span class="badge bg-warning text-dark mb-2"><i class="bi bi-pencil"></i> Mise à jour</span>
{% else %}
<span class="badge bg-secondary mb-2"><i class="bi bi-chat"></i> Information</span>
{% endif %}
{% if interp.analysis_priority %}
<span class="badge
{% if interp.analysis_priority == 'critical' %}bg-danger
{% elif interp.analysis_priority == 'high' %}bg-warning text-dark
{% elif interp.analysis_priority == 'medium' %}bg-info text-dark
{% else %}bg-secondary{% endif %} mb-2">
{{ 'Critique' if interp.analysis_priority == 'critical' else ('Haute' if interp.analysis_priority == 'high' else ('Normale' if interp.analysis_priority == 'medium' else 'Basse')) }}
</span>
{% endif %}
{% if interp.analysis_summary %}
<div class="mt-2">
<strong><i class="bi bi-chat-left-text"></i> {{ interp.analysis_summary[:100] }}{% if interp.analysis_summary|length > 100 %}...{% endif %}</strong>
</div>
{% endif %}
<div class="row mt-2 small">
{% if interp.suggested_equipment %}
<div class="col-12">
<i class="bi bi-tools"></i> <strong>Équipement:</strong> {{ interp.suggested_equipment }}
</div>
{% endif %}
</div>
{% if interp.suggested_description %}
<div class="mt-2 small text-muted">
<strong>Description:</strong> {{ interp.suggested_description[:200] }}{% if interp.suggested_description|length > 200 %}...{% endif %}
</div>
{% endif %}
<!-- Boutons d'action -->
{% if interp.analysis_action == 'create' and interp.status == 'pending' %}
<div class="mt-3 d-flex gap-2">
<a href="{{ url_for('interventions.create', from_interpretation=interp.id, source='ent') }}"
class="btn btn-success btn-sm">
<i class="bi bi-plus-circle"></i> Créer l'intervention
</a>
<button class="btn btn-outline-danger btn-sm" onclick="rejectInterpretation({{ interp.id }})">
<i class="bi bi-x-circle"></i> Rejeter
</button>
</div>
{% elif interp.status == 'accepted' %}
<div class="mt-3">
<span class="text-success small"><i class="bi bi-check-circle"></i> Intervention créée</span>
</div>
{% elif interp.status == 'rejected' %}
<div class="mt-3">
<span class="text-muted small"><i class="bi bi-x-circle"></i> Interprétation rejetée</span>
</div>
{% endif %}
</div>
{% else %}
<!-- Ancienne interprétation non structurée -->
<div class="interpretation-text">
{{ interp.interpretation|replace('\n', '<br>')|safe if interp.interpretation else 'Pas d\'interprétation' }}
</div>
{% endif %}
</div>
{% endfor %}
{% else %}
<div class="text-center text-muted py-4">
<i class="bi bi-lightbulb display-4"></i>
<p class="mt-2">Aucune interprétation</p>
<button class="btn btn-primary" onclick="showInterpretForm()">
<i class="bi bi-magic"></i> Interpréter ce message
</button>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
<style>
.email-content {
max-height: 60vh;
overflow-y: auto;
}
.email-content img {
max-width: 100%;
height: auto;
}
.email-content table {
max-width: 100%;
overflow-x: auto;
display: block;
}
.email-content a {
word-break: break-all;
}
.interpretation-card {
background: #f8f9fa;
}
.interpretation-text {
font-size: 0.9rem;
line-height: 1.5;
}
</style>
<script>
function showInterpretForm() {
document.getElementById('interpret-form').style.display = 'block';
document.getElementById('user-context').focus();
}
function interpretMessage() {
const btn = document.getElementById('interpret-btn');
const spinner = document.getElementById('interpret-spinner');
const context = document.getElementById('user-context').value;
btn.disabled = true;
spinner.classList.remove('d-none');
fetch('/ent/api/interpret/{{ message.id }}', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({context: context})
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert('Erreur: ' + (data.error || 'Erreur inconnue'));
btn.disabled = false;
spinner.classList.add('d-none');
}
})
.catch(error => {
alert('Erreur: ' + error.message);
btn.disabled = false;
spinner.classList.add('d-none');
});
}
function rejectInterpretation(id) {
if (!confirm('Rejeter cette interprétation ?')) return;
fetch('/ent/api/interpretation/' + id + '/reject', {method: 'POST'})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert('Erreur: ' + (data.error || 'Erreur inconnue'));
}
});
}
// Charger le statut du contexte GMAO au chargement de la page
document.addEventListener('DOMContentLoaded', function() {
loadContextStatus();
});
function loadContextStatus() {
fetch('/outlook/api/context')
.then(response => response.json())
.then(data => {
const statusEl = document.getElementById('context-status');
if (data.full_context && data.full_context.trim()) {
const sections = [
data.establishment_name,
data.technical_context,
data.equipment_context,
data.procedure_context,
data.staff_info,
data.contact_info,
data.hermes_instructions
].filter(s => s && s.trim()).length;
statusEl.innerHTML = `<strong>Contexte GMAO configuré</strong> (${sections} section${sections > 1 ? 's' : ''} remplie${sections > 1 ? 's' : ''})`;
} else {
statusEl.innerHTML = `<strong class="text-warning">Aucun contexte GMAO configuré</strong> - `;
}
})
.catch(error => {
document.getElementById('context-status').innerHTML = '<strong class="text-danger">Erreur de chargement</strong>';
});
}
</script>
{% endblock %}