ajout du suivi utilisateur
This commit is contained in:
275
templates/users.html
Normal file
275
templates/users.html
Normal file
@@ -0,0 +1,275 @@
|
||||
{% 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 %}
|
||||
Reference in New Issue
Block a user