feat : graphique visiteurs par pays + réseaux détail + suppression groupes AS
This commit is contained in:
@@ -1,20 +1,87 @@
|
||||
/* Admin stats : groupes AS + chargement pages via flux RSS XML /trending?period=14d */
|
||||
|
||||
// ── Groupes de réseaux ────────────────────────────────────────────────────────
|
||||
|
||||
// ── Visiteurs par pays ────────────────────────────────────────────────────────
|
||||
(function () {
|
||||
var addBtn = document.getElementById('as-group-add');
|
||||
if (!addBtn) { return; }
|
||||
var el = document.getElementById('stats-country-container');
|
||||
var asList = (typeof FOLIO_AS_LIST !== 'undefined') ? FOLIO_AS_LIST : [];
|
||||
if (!el || !asList.length) { return; }
|
||||
|
||||
addBtn.addEventListener('click', function () {
|
||||
var tpl = document.getElementById('as-group-tpl').content.cloneNode(true);
|
||||
document.getElementById('as-groups-list').appendChild(tpl);
|
||||
// Noms de pays en français via l'API Intl
|
||||
var dispNames = null;
|
||||
try { dispNames = new Intl.DisplayNames(['fr'], { type: 'region' }); } catch (e) {}
|
||||
function countryName(code) {
|
||||
if (!code || code === '??') { return 'Inconnu'; }
|
||||
try { return dispNames ? dispNames.of(code) : code; } catch (e) { return code; }
|
||||
}
|
||||
|
||||
// Drapeau emoji depuis le code ISO-2
|
||||
function flag(code) {
|
||||
if (!code || code.length !== 2) { return ''; }
|
||||
var cp = Array.from(code.toUpperCase()).map(function (c) {
|
||||
return 0x1F1E6 + c.charCodeAt(0) - 65;
|
||||
});
|
||||
return String.fromCodePoint(cp[0], cp[1]) + ' ';
|
||||
}
|
||||
|
||||
// Agréger par pays
|
||||
var byCountry = {};
|
||||
var asByCountry = {}; // country → [{name, asn, hits}]
|
||||
asList.forEach(function (as) {
|
||||
var c = as.country || '??';
|
||||
byCountry[c] = (byCountry[c] || 0) + as.hits;
|
||||
if (!asByCountry[c]) { asByCountry[c] = []; }
|
||||
asByCountry[c].push(as);
|
||||
});
|
||||
|
||||
document.getElementById('as-groups-list').addEventListener('click', function (e) {
|
||||
if (e.target.classList.contains('as-group-delete')) {
|
||||
e.target.closest('.as-group-row').remove();
|
||||
}
|
||||
var countries = Object.keys(byCountry).map(function (c) {
|
||||
return { code: c, hits: byCountry[c], networks: asByCountry[c] };
|
||||
}).sort(function (a, b) { return b.hits - a.hits; }).slice(0, 20);
|
||||
|
||||
if (!countries.length) { el.innerHTML = '<p class="text-muted mb-0">Aucune donnée.</p>'; return; }
|
||||
|
||||
var maxH = countries[0].hits || 1;
|
||||
|
||||
var html = '<div class="accordion accordion-flush" id="acc-countries">';
|
||||
countries.forEach(function (c, i) {
|
||||
var pct = Math.round(c.hits / maxH * 100);
|
||||
var cname = flag(c.code) + countryName(c.code);
|
||||
var vis = c.hits.toLocaleString('fr-FR');
|
||||
var accId = 'acc-country-' + i;
|
||||
var nets = c.networks.slice().sort(function (a, b) { return b.hits - a.hits; });
|
||||
var maxN = nets[0] ? nets[0].hits : 1;
|
||||
|
||||
var netRows = nets.map(function (n) {
|
||||
var npct = Math.round(n.hits / maxN * 100);
|
||||
return '<div class="d-flex align-items-center gap-2 py-1">'
|
||||
+ '<div class="text-muted small" style="width:9rem;flex-shrink:0">'
|
||||
+ (n.name || '?') + (n.asn ? ' <span class="opacity-50">AS' + n.asn + '</span>' : '') + '</div>'
|
||||
+ '<div class="flex-grow-1"><div class="progress" style="height:4px">'
|
||||
+ '<div class="progress-bar bg-info" style="width:' + npct + '%"></div>'
|
||||
+ '</div></div>'
|
||||
+ '<div class="text-end text-muted small" style="width:4rem;flex-shrink:0">'
|
||||
+ n.hits.toLocaleString('fr-FR') + '</div>'
|
||||
+ '</div>';
|
||||
}).join('');
|
||||
|
||||
html += '<div class="accordion-item border-0">'
|
||||
+ '<div class="d-flex align-items-center gap-2 py-2 px-0" data-bs-toggle="collapse"'
|
||||
+ ' data-bs-target="#' + accId + '" role="button" aria-expanded="false">'
|
||||
+ '<div class="fw-medium" style="width:10rem;flex-shrink:0">' + cname + '</div>'
|
||||
+ '<div class="flex-grow-1"><div class="progress" style="height:6px">'
|
||||
+ '<div class="progress-bar" style="width:' + pct + '%"></div>'
|
||||
+ '</div></div>'
|
||||
+ '<div class="text-end fw-semibold" style="width:5rem;flex-shrink:0">'
|
||||
+ vis + ' <span class="text-muted fw-normal small">vis.</span></div>'
|
||||
+ '<span class="text-muted" style="width:1rem;flex-shrink:0;font-size:.7rem">▾</span>'
|
||||
+ '</div>'
|
||||
+ '<div id="' + accId + '" class="collapse">'
|
||||
+ '<div class="ps-2 pb-2 border-start ms-3">' + netRows + '</div>'
|
||||
+ '</div>'
|
||||
+ '</div>';
|
||||
});
|
||||
html += '</div>';
|
||||
el.innerHTML = html;
|
||||
}());
|
||||
|
||||
// ── Pages les plus visitées (RSS XML + sparklines) ───────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user