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

309 lines
No EOL
12 KiB
HTML

{% extends "base.html" %}
{% block title %}Status Système - GMAO{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1><i class="bi bi-speedometer2"></i> Status du Système</h1>
<div>
<span id="refresh-timer" class="text-muted me-2" style="font-size:0.85rem;"></span>
<button id="btn-refresh" onclick="refreshAll()" class="btn btn-outline-light">
<i class="bi bi-arrow-clockwise"></i> Actualiser
</button>
</div>
</div>
<!-- Loader -->
<div id="loader" class="text-center py-5">
<div class="spinner-border text-light" role="status" style="width:3rem;height:3rem;">
<span class="visually-hidden">Chargement...</span>
</div>
<p class="mt-3 text-muted">Vérification des connexions en temps réel...</p>
</div>
<!-- Contenu (caché pendant le chargement) -->
<div id="content" style="display:none;">
<!-- Résumé -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card bg-dark text-white h-100">
<div class="card-body text-center">
<h5 class="card-title"><i class="bi bi-hdd-stack"></i> Services</h5>
<p class="display-4 mb-0" id="total-services">-</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-info text-white h-100">
<div class="card-body text-center">
<h5 class="card-title"><i class="bi bi-check-circle"></i> Configurés</h5>
<p class="display-4 mb-0" id="configured-count">-</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-white h-100" id="connected-card">
<div class="card-body text-center">
<h5 class="card-title"><i class="bi bi-plug"></i> Connectés</h5>
<p class="display-4 mb-0" id="connected-count">-</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-white h-100" id="watchdog-card">
<div class="card-body text-center">
<h5 class="card-title"><i class="bi bi-shield-exclamation"></i> Watchdogs</h5>
<p class="display-4 mb-0" id="watchdog-count">-</p>
</div>
</div>
</div>
</div>
<!-- Services -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0"><i class="bi bi-hdd-stack"></i> État des services</h5>
<small id="last-check" class="text-muted"></small>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-dark table-hover mb-0">
<thead>
<tr>
<th>Service</th>
<th>Configuration</th>
<th>Connexion</th>
<th>Détails</th>
</tr>
</thead>
<tbody id="services-body">
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Watchdogs -->
<div class="row mt-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-shield-exclamation"></i> Watchdogs</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-dark table-hover mb-0">
<thead>
<tr>
<th>Watchdog</th>
<th>Statut</th>
<th>PID</th>
<th>Détails</th>
</tr>
</thead>
<tbody id="watchdogs-body">
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Liens rapides -->
<div class="row mt-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-link-45deg"></i> Configuration rapide</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-2 mb-2">
<a href="{{ url_for('outlook_pages.index') }}" class="btn btn-outline-info w-100">
<i class="bi bi-envelope"></i> Outlook
</a>
</div>
<div class="col-md-2 mb-2">
<a href="{{ url_for('ent.index') }}" class="btn btn-outline-info w-100">
<i class="bi bi-person-badge"></i> ENT
</a>
</div>
<div class="col-md-2 mb-2">
<a href="{{ url_for('pronote.connect') }}" class="btn btn-outline-info w-100">
<i class="bi bi-calendar-week"></i> PRONOTE
</a>
</div>
<div class="col-md-2 mb-2">
<a href="{{ url_for('yeastar.config_page') }}" class="btn btn-outline-info w-100">
<i class="bi bi-telephone"></i> Yeastar
</a>
</div>
<div class="col-md-2 mb-2">
<a href="{{ url_for('gmao_config.index') }}" class="btn btn-outline-info w-100">
<i class="bi bi-gear"></i> GMAO
</a>
</div>
<div class="col-md-2 mb-2">
<a href="{{ url_for('logs.index') }}" class="btn btn-outline-info w-100">
<i class="bi bi-journal-text"></i> Logs
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div><!-- fin #content -->
</div>
<script>
const ICONS = {
'bi-database': 'base de données',
'bi-envelope': 'email',
'bi-person-badge': 'ENT',
'bi-calendar-week': 'PRONOTE',
'bi-telephone': 'téléphonie',
'bi-gear': 'configuration'
};
let refreshInterval = null;
let countdown = 30;
function renderServices(systems) {
const tbody = document.getElementById('services-body');
tbody.innerHTML = '';
systems.forEach(s => {
let configBadge, connBadge;
if (s.configured) {
configBadge = '<span class="badge bg-success"><i class="bi bi-check"></i> Configuré</span>';
} else {
configBadge = '<span class="badge bg-secondary"><i class="bi bi-x"></i> Non configuré</span>';
}
if (s.connected) {
connBadge = '<span class="badge bg-success"><i class="bi bi-plug-fill"></i> Connecté</span>';
} else if (s.configured) {
connBadge = '<span class="badge bg-danger"><i class="bi bi-plug"></i> Déconnecté</span>';
} else {
connBadge = '<span class="badge bg-secondary"><i class="bi bi-dash"></i> N/A</span>';
}
tbody.innerHTML += `<tr>
<td><i class="bi ${s.icon} me-2"></i><strong>${s.name}</strong></td>
<td>${configBadge}</td>
<td>${connBadge}</td>
<td><small>${s.message}</small></td>
</tr>`;
});
}
function renderWatchdogs(watchdogs) {
const tbody = document.getElementById('watchdogs-body');
tbody.innerHTML = '';
watchdogs.forEach(w => {
let statusBadge;
if (w.running) {
statusBadge = '<span class="badge bg-success"><i class="bi bi-circle-fill"></i> Actif</span>';
} else {
statusBadge = '<span class="badge bg-secondary"><i class="bi bi-circle"></i> Inactif</span>';
}
const pid = w.pid ? `<code>${w.pid}</code>` : '<span class="text-muted">—</span>';
tbody.innerHTML += `<tr>
<td><i class="bi bi-gear me-2"></i>${w.name}</td>
<td>${statusBadge}</td>
<td>${pid}</td>
<td><small>${w.message}</small></td>
</tr>`;
});
}
function updateSummary(data) {
const total = data.systems.length;
const configured = data.configured_count;
const connected = data.connected_count;
const wdRunning = data.watchdogs.filter(w => w.running).length;
const wdTotal = data.watchdogs.length;
document.getElementById('total-services').textContent = total;
document.getElementById('configured-count').textContent = `${configured}/${total}`;
document.getElementById('connected-count').textContent = `${connected}/${total}`;
document.getElementById('watchdog-count').textContent = `${wdRunning}/${wdTotal}`;
const connCard = document.getElementById('connected-card');
connCard.className = 'card text-white h-100';
if (connected === total) connCard.classList.add('bg-success');
else if (connected > 0) connCard.classList.add('bg-warning');
else connCard.classList.add('bg-danger');
const wdCard = document.getElementById('watchdog-card');
wdCard.className = 'card text-white h-100';
if (wdRunning === wdTotal) wdCard.classList.add('bg-success');
else if (wdRunning > 0) wdCard.classList.add('bg-warning');
else wdCard.classList.add('bg-danger');
if (data.timestamp) {
const dt = new Date(data.timestamp);
document.getElementById('last-check').textContent =
'Dernière vérification : ' + dt.toLocaleTimeString('fr-FR');
}
}
async function refreshAll() {
const btn = document.getElementById('btn-refresh');
const icon = btn.querySelector('i');
icon.classList.add('spin');
icon.classList.replace('bi-arrow-clockwise', 'bi-hourglass-split');
btn.disabled = true;
try {
const resp = await fetch('/status/api/check');
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const data = await resp.json();
renderServices(data.systems);
renderWatchdogs(data.watchdogs);
updateSummary(data);
document.getElementById('loader').style.display = 'none';
document.getElementById('content').style.display = 'block';
} catch (e) {
console.error('Erreur refresh status:', e);
} finally {
icon.classList.replace('bi-hourglass-split', 'bi-arrow-clockwise');
icon.classList.remove('spin');
btn.disabled = false;
countdown = 30;
}
}
function startAutoRefresh() {
countdown = 30;
refreshInterval = setInterval(() => {
countdown--;
document.getElementById('refresh-timer').textContent =
`Prochaine vérification dans ${countdown}s`;
if (countdown <= 0) {
refreshAll();
}
}, 1000);
}
// Premier chargement + auto-refresh toutes les 30s
refreshAll().then(() => startAutoRefresh());
</script>
<style>
.spin {
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
</style>
{% endblock %}