From 1f9c290010397a8b6a1b5dcb96ecec317640edb4 Mon Sep 17 00:00:00 2001 From: Nathan FONTEYNE Date: Thu, 9 Jul 2026 16:48:09 +0200 Subject: [PATCH] update: dashboard page admin --- public/admin.html | 24 +++++++++ public/css/style.css | 29 +++++++++++ public/js/admin.js | 75 +++++++++++++++++++++++++++++ public/js/nav.js | 1 + src/auth/routes.js | 1 - src/repositories/setlistsRepo.js | 21 ++++++++ src/repositories/songsRepo.js | 11 +++++ src/repositories/suggestionsRepo.js | 13 +++++ src/repositories/usersRepo.js | 17 ++++++- src/routes/admin.js | 56 +++++++++++++++++++++ src/routes/index.js | 2 + 11 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 public/admin.html create mode 100644 public/js/admin.js create mode 100644 src/routes/admin.js diff --git a/public/admin.html b/public/admin.html new file mode 100644 index 0000000..668ee42 --- /dev/null +++ b/public/admin.html @@ -0,0 +1,24 @@ + + + + + +Administration — Octane + + + + +
+ +
+
+
+
Chargement…
+
+ + + + + + + diff --git a/public/css/style.css b/public/css/style.css index 85dcc3e..122cdb8 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -1294,3 +1294,32 @@ a.back-link:hover { color: var(--accent); } } .vote-review-detail { margin-top: 0.75rem; } + +/* ---------- Admin dashboard ---------- */ + +.admin-user-list { display: flex; flex-direction: column; gap: 0.6rem; } + +.admin-user-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + border: 1px solid var(--border); + border-radius: var(--radius-sm); + padding: 0.6rem 0.85rem; + flex-wrap: wrap; +} + +.admin-user-identity { + display: flex; + align-items: center; + gap: 0.75rem; +} + +.admin-user-counts { + display: flex; + gap: 0.9rem; + color: var(--muted); + font-size: 0.88rem; + white-space: nowrap; +} diff --git a/public/js/admin.js b/public/js/admin.js new file mode 100644 index 0000000..06f1881 --- /dev/null +++ b/public/js/admin.js @@ -0,0 +1,75 @@ +function formatDate(dateStr) { + const d = new Date(dateStr); + return d.toLocaleDateString('fr-FR', { year: 'numeric', month: 'long', day: 'numeric' }); +} + +function showError(message) { + document.getElementById('error').innerHTML = `
${escapeHtml(message)}
`; +} + +function statTile(value, label) { + return ` +
+
${value}
+
${escapeHtml(label)}
+
+ `; +} + +function adminUserRowTemplate(u) { + return ` +
+
+ ${avatarHtml(u, 'avatar-sm')} +
+
${escapeHtml(u.name)}${u.isAdmin ? ' admin' : ''}
+
Membre depuis ${formatDate(u.createdAt)} · Dernière connexion ${formatDate(u.lastSeenAt)}
+
+
+
+ 🎵 ${u.songsAdded} + 💡 ${u.suggestionsProposed} + ✔ ${u.votesCast} +
+
+ `; +} + +(async function init() { + const me = await initNav('admin'); + if (!me.isAdmin) { + window.location.href = '/index.html'; + return; + } + + try { + const stats = await api.get('/api/admin/stats'); + const container = document.getElementById('content'); + container.innerHTML = ` +

Administration

+

Vue d'ensemble de l'activité et des données de l'application.

+ +

Vue d'ensemble

+
+ ${statTile(stats.userCount, 'Utilisateurs')} + ${statTile(stats.songs.totalSongs, 'Morceaux au répertoire')} + ${statTile(stats.songs.distinctArtists, 'Artistes différents')} + ${statTile(stats.songs.tutorialsAdded, 'Tutoriels ajoutés')} + ${statTile(stats.suggestions.pending, 'Suggestions en attente')} + ${statTile(stats.suggestions.approved, 'Suggestions approuvées')} + ${statTile(stats.suggestions.totalVotes, 'Votes exprimés')} + ${statTile(stats.concerts.pastConcerts, 'Concerts passés')} + ${statTile(stats.concerts.upcomingConcerts, 'Concerts à venir')} + ${statTile(stats.concerts.avgSongsPerConcert, 'Morceaux / concert (moy.)')} +
+ +

Activité des utilisateurs

+

La date de dernière connexion est approximative (dernière synchronisation du profil, pas un journal de connexion précis).

+
+ ${stats.users.map(adminUserRowTemplate).join('')} +
+ `; + } catch (err) { + showError(err.message); + } +})(); diff --git a/public/js/nav.js b/public/js/nav.js index 7033a2c..c1d224f 100644 --- a/public/js/nav.js +++ b/public/js/nav.js @@ -31,6 +31,7 @@ async function initNav(activePage) { Prochain concert Disponibilités Historique + ${me.isAdmin ? `Administration` : ''}