276 lines
9.2 KiB
HTML
276 lines
9.2 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Supervision - Utilisateurs{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h4 class="mb-0"><i class="bi bi-people"></i> Utilisateurs Amadea</h4>
|
|
<span id="last-update" class="text-muted small"></span>
|
|
</div>
|
|
|
|
<div id="alert-zone"></div>
|
|
|
|
<!-- Graphique d'activite horaire -->
|
|
<div class="card mb-3">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h6 class="mb-0"><i class="bi bi-bar-chart"></i> Utilisateurs actifs par heure</h6>
|
|
<select id="period-select" class="form-select form-select-sm" style="width: auto;">
|
|
<option value="today">Aujourd'hui</option>
|
|
<option value="7days">7 derniers jours</option>
|
|
</select>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="chart-container"
|
|
style="height: 100px; display: flex; align-items: flex-end; gap: 3px;">
|
|
</div>
|
|
<div id="chart-labels" style="display: flex; gap: 3px; margin-top: 4px;"></div>
|
|
<div id="chart-unavailable" class="d-none">
|
|
<div class="alert alert-info mb-0">
|
|
<i class="bi bi-info-circle"></i>
|
|
Les donnees historiques des jours precedents ne sont pas disponibles (fichiers archives).
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tableau utilisateurs -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h6 class="mb-0"><i class="bi bi-person-lines-fill"></i> Utilisateurs connectes aujourd'hui</h6>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<table class="table table-hover mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th>Utilisateur</th>
|
|
<th>Statut</th>
|
|
<th>Derniere action</th>
|
|
<th>Actions (24h)</th>
|
|
<th>Depuis</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="users-tbody"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script>
|
|
var STATUS_CONFIG = {
|
|
actif: { badge: 'bg-success', label: 'ACTIF' },
|
|
inactif: { badge: 'bg-warning text-dark', label: 'INACTIF' },
|
|
deconnecte: { badge: 'bg-secondary', label: 'DECONNECTE' },
|
|
};
|
|
|
|
var currentHourly = [];
|
|
var currentPeriod = 'today';
|
|
|
|
/* --- Graphique CSS pur --- */
|
|
|
|
function renderChart(data) {
|
|
var container = document.getElementById('chart-container');
|
|
var labelsEl = document.getElementById('chart-labels');
|
|
var unavail = document.getElementById('chart-unavailable');
|
|
|
|
container.style.display = 'flex';
|
|
labelsEl.style.display = 'flex';
|
|
unavail.classList.add('d-none');
|
|
container.textContent = '';
|
|
labelsEl.textContent = '';
|
|
|
|
if (!data || data.length === 0) {
|
|
var msg = document.createElement('span');
|
|
msg.className = 'text-muted small';
|
|
msg.textContent = 'Aucune donnee disponible.';
|
|
container.appendChild(msg);
|
|
return;
|
|
}
|
|
|
|
var max = 1;
|
|
data.forEach(function(d) { if ((d.count || 0) > max) max = d.count; });
|
|
|
|
data.forEach(function(item) {
|
|
var count = item.count || 0;
|
|
var heightPct = count > 0 ? Math.max((count / max) * 100, 6) : 0;
|
|
|
|
var bar = document.createElement('div');
|
|
bar.style.flex = '1';
|
|
bar.style.minWidth = '14px';
|
|
bar.style.height = heightPct + '%';
|
|
bar.style.background = '#0d6efd';
|
|
bar.style.borderRadius = '3px 3px 0 0';
|
|
bar.style.opacity = count > 0 ? '1' : '0.15';
|
|
bar.style.transition = 'height 0.3s';
|
|
bar.title = item.hour !== undefined
|
|
? item.hour + 'h : ' + count + ' utilisateur(s)'
|
|
: item.date + ' : ' + count + ' utilisateur(s)';
|
|
container.appendChild(bar);
|
|
|
|
var lbl = document.createElement('div');
|
|
lbl.style.flex = '1';
|
|
lbl.style.minWidth = '14px';
|
|
lbl.style.textAlign = 'center';
|
|
lbl.style.fontSize = '0.6rem';
|
|
lbl.style.color = '#6c757d';
|
|
lbl.style.overflow = 'hidden';
|
|
if (item.hour !== undefined) {
|
|
lbl.textContent = item.hour % 3 === 0 ? item.hour + 'h' : '';
|
|
} else {
|
|
var d = new Date(item.date);
|
|
lbl.textContent = d.getUTCDate() + '/' + (d.getUTCMonth() + 1);
|
|
}
|
|
labelsEl.appendChild(lbl);
|
|
});
|
|
}
|
|
|
|
function renderWeekly(weekly) {
|
|
var unavail = document.getElementById('chart-unavailable');
|
|
var container = document.getElementById('chart-container');
|
|
var labelsEl = document.getElementById('chart-labels');
|
|
|
|
var allNull = weekly.every(function(d) { return d.count === null; });
|
|
if (allNull) {
|
|
container.style.display = 'none';
|
|
labelsEl.style.display = 'none';
|
|
unavail.classList.remove('d-none');
|
|
return;
|
|
}
|
|
container.style.display = 'flex';
|
|
labelsEl.style.display = 'flex';
|
|
unavail.classList.add('d-none');
|
|
renderChart(weekly.map(function(d) {
|
|
return { date: d.date, count: d.count === null ? 0 : d.count };
|
|
}));
|
|
}
|
|
|
|
/* --- Tableau --- */
|
|
|
|
function renderTable(users) {
|
|
var tbody = document.getElementById('users-tbody');
|
|
tbody.textContent = '';
|
|
|
|
if (!users || users.length === 0) {
|
|
var tr = document.createElement('tr');
|
|
var td = document.createElement('td');
|
|
td.colSpan = 5;
|
|
td.className = 'text-center text-muted py-3';
|
|
td.textContent = "Aucun utilisateur detecte aujourd'hui.";
|
|
tr.appendChild(td);
|
|
tbody.appendChild(tr);
|
|
return;
|
|
}
|
|
|
|
users.forEach(function(u) {
|
|
var sc = STATUS_CONFIG[u.status] || { badge: 'bg-secondary', label: (u.status || '').toUpperCase() };
|
|
var tr = document.createElement('tr');
|
|
|
|
/* Utilisateur */
|
|
var tdUser = document.createElement('td');
|
|
var strong = document.createElement('strong');
|
|
strong.textContent = u.login || '';
|
|
tdUser.appendChild(strong);
|
|
tr.appendChild(tdUser);
|
|
|
|
/* Statut */
|
|
var tdStatus = document.createElement('td');
|
|
var badge = document.createElement('span');
|
|
badge.className = 'badge ' + sc.badge;
|
|
badge.textContent = sc.label;
|
|
tdStatus.appendChild(badge);
|
|
tr.appendChild(tdStatus);
|
|
|
|
/* Derniere action */
|
|
var tdAction = document.createElement('td');
|
|
tdAction.textContent = u.last_action_time || '\u2014';
|
|
if (u.last_action_label) {
|
|
var br = document.createElement('br');
|
|
var small = document.createElement('small');
|
|
small.className = 'text-muted';
|
|
small.textContent = u.last_action_label;
|
|
tdAction.appendChild(br);
|
|
tdAction.appendChild(small);
|
|
}
|
|
tr.appendChild(tdAction);
|
|
|
|
/* Actions 24h */
|
|
var tdCount = document.createElement('td');
|
|
tdCount.textContent = String(u.action_count_24h || 0);
|
|
tr.appendChild(tdCount);
|
|
|
|
/* Depuis */
|
|
var tdSince = document.createElement('td');
|
|
tdSince.textContent = u.connected_since || '\u2014';
|
|
tr.appendChild(tdSince);
|
|
|
|
tbody.appendChild(tr);
|
|
});
|
|
}
|
|
|
|
/* --- Alertes --- */
|
|
|
|
function showAlert(type, message) {
|
|
var zone = document.getElementById('alert-zone');
|
|
zone.textContent = '';
|
|
var div = document.createElement('div');
|
|
div.className = 'alert alert-' + type;
|
|
var icon = document.createElement('i');
|
|
icon.className = 'bi bi-exclamation-triangle me-1';
|
|
div.appendChild(icon);
|
|
div.appendChild(document.createTextNode(message));
|
|
zone.appendChild(div);
|
|
}
|
|
|
|
function clearAlert() {
|
|
document.getElementById('alert-zone').textContent = '';
|
|
}
|
|
|
|
/* --- Refresh --- */
|
|
|
|
function refreshUsers() {
|
|
fetch('/api/users')
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
if (data.error) {
|
|
showAlert('warning', data.error);
|
|
renderTable([]);
|
|
renderChart([]);
|
|
return;
|
|
}
|
|
if (data.no_files) {
|
|
showAlert('info', "Aucun log disponible pour aujourd'hui.");
|
|
renderTable([]);
|
|
renderChart([]);
|
|
return;
|
|
}
|
|
clearAlert();
|
|
renderTable(data.users || []);
|
|
currentHourly = data.hourly || [];
|
|
if (currentPeriod === 'today') {
|
|
renderChart(currentHourly);
|
|
}
|
|
document.getElementById('last-update').textContent =
|
|
'Mis a jour : ' + new Date().toLocaleTimeString('fr-FR');
|
|
})
|
|
.catch(function() {});
|
|
}
|
|
|
|
document.getElementById('period-select').addEventListener('change', function() {
|
|
currentPeriod = this.value;
|
|
if (currentPeriod === 'today') {
|
|
document.getElementById('chart-container').style.display = 'flex';
|
|
document.getElementById('chart-labels').style.display = 'flex';
|
|
document.getElementById('chart-unavailable').classList.add('d-none');
|
|
renderChart(currentHourly);
|
|
} else {
|
|
fetch('/api/users/activity/weekly')
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) { renderWeekly(data.weekly || []); })
|
|
.catch(function() {});
|
|
}
|
|
});
|
|
|
|
refreshUsers();
|
|
setInterval(refreshUsers, 30000);
|
|
</script>
|
|
{% endblock %}
|