V1.0.1
This commit is contained in:
@@ -31,6 +31,9 @@
|
||||
<div id="chart-labels" style="display: flex; gap: 3px; margin-top: 4px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="chart-hint" class="d-none mt-2">
|
||||
<small class="text-muted"><i class="bi bi-hand-index"></i> Cliquez sur une barre pour voir les utilisateurs de ce jour.</small>
|
||||
</div>
|
||||
<div id="chart-unavailable" class="d-none">
|
||||
<div class="alert alert-info mb-0">
|
||||
<i class="bi bi-info-circle"></i>
|
||||
@@ -43,12 +46,12 @@
|
||||
<!-- 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>
|
||||
<h6 class="mb-0" id="table-title"><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>
|
||||
<tr id="table-headers">
|
||||
<th>Utilisateur</th>
|
||||
<th>Statut</th>
|
||||
<th>Derniere action</th>
|
||||
@@ -70,8 +73,10 @@ var STATUS_CONFIG = {
|
||||
deconnecte: { badge: 'bg-secondary', label: 'DECONNECTE' },
|
||||
};
|
||||
|
||||
var currentHourly = [];
|
||||
var currentPeriod = 'today';
|
||||
var currentHourly = [];
|
||||
var currentPeriod = 'today';
|
||||
var selectedBarEl = null;
|
||||
var lastTodayUsers = [];
|
||||
|
||||
/* --- Graphique CSS pur --- */
|
||||
|
||||
@@ -81,15 +86,15 @@ function renderYAxis(max) {
|
||||
var values = [max, Math.round(max / 2), 0];
|
||||
values.forEach(function(v) {
|
||||
var lbl = document.createElement('div');
|
||||
lbl.style.fontSize = '0.6rem';
|
||||
lbl.style.color = '#6c757d';
|
||||
lbl.style.fontSize = '0.6rem';
|
||||
lbl.style.color = '#6c757d';
|
||||
lbl.style.lineHeight = '1';
|
||||
lbl.textContent = v;
|
||||
yaxis.appendChild(lbl);
|
||||
});
|
||||
}
|
||||
|
||||
function renderChart(data) {
|
||||
function renderChart(data, options) {
|
||||
var container = document.getElementById('chart-container');
|
||||
var labelsEl = document.getElementById('chart-labels');
|
||||
var unavail = document.getElementById('chart-unavailable');
|
||||
@@ -99,11 +104,12 @@ function renderChart(data) {
|
||||
unavail.classList.add('d-none');
|
||||
container.textContent = '';
|
||||
labelsEl.textContent = '';
|
||||
selectedBarEl = null;
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
renderYAxis(0);
|
||||
var msg = document.createElement('span');
|
||||
msg.className = 'text-muted small';
|
||||
msg.className = 'text-muted small';
|
||||
msg.textContent = 'Aucune donnee disponible.';
|
||||
container.appendChild(msg);
|
||||
return;
|
||||
@@ -114,7 +120,7 @@ function renderChart(data) {
|
||||
renderYAxis(max);
|
||||
|
||||
data.forEach(function(item) {
|
||||
var count = item.count || 0;
|
||||
var count = item.count || 0;
|
||||
var heightPct = count > 0 ? Math.max((count / max) * 100, 6) : 0;
|
||||
|
||||
var bar = document.createElement('div');
|
||||
@@ -128,6 +134,23 @@ function renderChart(data) {
|
||||
bar.title = item.hour !== undefined
|
||||
? item.hour + 'h : ' + count + ' utilisateur(s)'
|
||||
: item.date + ' : ' + count + ' utilisateur(s)';
|
||||
|
||||
if (options && options.onBarClick && item.date !== undefined) {
|
||||
bar.style.cursor = 'pointer';
|
||||
(function(capturedItem, capturedBar) {
|
||||
capturedBar.addEventListener('click', function() {
|
||||
if (selectedBarEl) {
|
||||
selectedBarEl.style.background = '#0d6efd';
|
||||
selectedBarEl.style.outline = '';
|
||||
}
|
||||
capturedBar.style.background = '#0a58ca';
|
||||
capturedBar.style.outline = '2px solid #0a3fa8';
|
||||
selectedBarEl = capturedBar;
|
||||
options.onBarClick(capturedItem);
|
||||
});
|
||||
})(item, bar);
|
||||
}
|
||||
|
||||
container.appendChild(bar);
|
||||
|
||||
var lbl = document.createElement('div');
|
||||
@@ -151,25 +174,45 @@ function renderWeekly(weekly) {
|
||||
var unavail = document.getElementById('chart-unavailable');
|
||||
var container = document.getElementById('chart-container');
|
||||
var labelsEl = document.getElementById('chart-labels');
|
||||
var hint = document.getElementById('chart-hint');
|
||||
|
||||
var allNull = weekly.every(function(d) { return d.count === null; });
|
||||
if (allNull) {
|
||||
container.style.display = 'none';
|
||||
labelsEl.style.display = 'none';
|
||||
hint.classList.add('d-none');
|
||||
document.getElementById('chart-yaxis').textContent = '';
|
||||
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 };
|
||||
}));
|
||||
|
||||
hint.classList.remove('d-none');
|
||||
renderChart(
|
||||
weekly.map(function(d) {
|
||||
return { date: d.date, count: d.count === null ? 0 : d.count };
|
||||
}),
|
||||
{
|
||||
onBarClick: function(item) {
|
||||
loadDayUsers(item.date);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/* --- Tableau --- */
|
||||
|
||||
function setTableMode(mode, dateLabel) {
|
||||
var title = document.getElementById('table-title');
|
||||
var thead = document.getElementById('table-headers');
|
||||
if (mode === 'today') {
|
||||
title.innerHTML = '<i class="bi bi-person-lines-fill"></i> Utilisateurs connectes aujourd\'hui';
|
||||
thead.innerHTML = '<th>Utilisateur</th><th>Statut</th><th>Derniere action</th><th>Actions (24h)</th><th>Depuis</th>';
|
||||
} else {
|
||||
title.innerHTML = '<i class="bi bi-person-lines-fill"></i> Utilisateurs — ' + (dateLabel || '');
|
||||
thead.innerHTML = '<th>Utilisateur</th><th>Derniere utilisation</th><th>Actions (jour)</th><th>Duree de presence</th>';
|
||||
}
|
||||
}
|
||||
|
||||
function renderTable(users) {
|
||||
var tbody = document.getElementById('users-tbody');
|
||||
tbody.textContent = '';
|
||||
@@ -189,14 +232,12 @@ function renderTable(users) {
|
||||
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;
|
||||
@@ -204,25 +245,20 @@ function renderTable(users) {
|
||||
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.className = 'text-muted d-block';
|
||||
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);
|
||||
@@ -231,6 +267,64 @@ function renderTable(users) {
|
||||
});
|
||||
}
|
||||
|
||||
function renderDayTable(users, dateStr) {
|
||||
setTableMode('day', dateStr);
|
||||
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 = 4;
|
||||
td.className = 'text-center text-muted py-3';
|
||||
td.textContent = 'Aucun utilisateur detecte ce jour.';
|
||||
tr.appendChild(td);
|
||||
tbody.appendChild(tr);
|
||||
return;
|
||||
}
|
||||
|
||||
users.forEach(function(u) {
|
||||
var tr = document.createElement('tr');
|
||||
|
||||
var tdUser = document.createElement('td');
|
||||
var strong = document.createElement('strong');
|
||||
strong.textContent = u.login || '';
|
||||
tdUser.appendChild(strong);
|
||||
tr.appendChild(tdUser);
|
||||
|
||||
var tdAction = document.createElement('td');
|
||||
tdAction.textContent = u.last_action_time || '\u2014';
|
||||
if (u.last_action_label) {
|
||||
var small = document.createElement('small');
|
||||
small.className = 'text-muted d-block';
|
||||
small.textContent = u.last_action_label;
|
||||
tdAction.appendChild(small);
|
||||
}
|
||||
tr.appendChild(tdAction);
|
||||
|
||||
var tdCount = document.createElement('td');
|
||||
tdCount.textContent = String(u.action_count || 0);
|
||||
tr.appendChild(tdCount);
|
||||
|
||||
var tdDuration = document.createElement('td');
|
||||
tdDuration.textContent = u.duration || '\u2014';
|
||||
tr.appendChild(tdDuration);
|
||||
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
}
|
||||
|
||||
function loadDayUsers(date) {
|
||||
fetch('/api/users/day/' + date)
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) {
|
||||
if (data.error) { showAlert('warning', data.error); return; }
|
||||
clearAlert();
|
||||
renderDayTable(data.users || [], date);
|
||||
})
|
||||
.catch(function() {});
|
||||
}
|
||||
|
||||
/* --- Alertes --- */
|
||||
|
||||
function showAlert(type, message) {
|
||||
@@ -257,20 +351,20 @@ function refreshUsers() {
|
||||
.then(function(data) {
|
||||
if (data.error) {
|
||||
showAlert('warning', data.error);
|
||||
renderTable([]);
|
||||
renderChart([]);
|
||||
if (currentPeriod === 'today') { renderTable([]); renderChart([]); }
|
||||
return;
|
||||
}
|
||||
if (data.no_files) {
|
||||
showAlert('info', "Aucun log disponible pour aujourd'hui.");
|
||||
renderTable([]);
|
||||
renderChart([]);
|
||||
if (currentPeriod === 'today') { renderTable([]); renderChart([]); }
|
||||
return;
|
||||
}
|
||||
clearAlert();
|
||||
renderTable(data.users || []);
|
||||
currentHourly = data.hourly || [];
|
||||
lastTodayUsers = data.users || [];
|
||||
currentHourly = data.hourly || [];
|
||||
if (currentPeriod === 'today') {
|
||||
setTableMode('today');
|
||||
renderTable(lastTodayUsers);
|
||||
renderChart(currentHourly);
|
||||
}
|
||||
document.getElementById('last-update').textContent =
|
||||
@@ -281,10 +375,14 @@ function refreshUsers() {
|
||||
|
||||
document.getElementById('period-select').addEventListener('change', function() {
|
||||
currentPeriod = this.value;
|
||||
var hint = document.getElementById('chart-hint');
|
||||
if (currentPeriod === 'today') {
|
||||
hint.classList.add('d-none');
|
||||
document.getElementById('chart-container').style.display = 'flex';
|
||||
document.getElementById('chart-labels').style.display = 'flex';
|
||||
document.getElementById('chart-unavailable').classList.add('d-none');
|
||||
setTableMode('today');
|
||||
renderTable(lastTodayUsers);
|
||||
renderChart(currentHourly);
|
||||
} else {
|
||||
fetch('/api/users/activity/weekly')
|
||||
|
||||
Reference in New Issue
Block a user