mirror of
https://github.com/arthur-pbty/LazyBot.git
synced 2026-06-06 22:41:40 +02:00
add vocal stats system
This commit is contained in:
@@ -146,7 +146,7 @@ body {
|
||||
background-color: var(--bg-card);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--border-radius-lg);
|
||||
overflow: hidden;
|
||||
overflow: visible;
|
||||
transition: transform var(--transition-normal), border-color var(--transition-normal), box-shadow var(--transition-normal);
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -161,7 +161,7 @@ body {
|
||||
height: 80px;
|
||||
background: linear-gradient(135deg, var(--primary), #7289da);
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
border-radius: var(--border-radius-lg) var(--border-radius-lg) 0 0;
|
||||
}
|
||||
|
||||
.guild-card-avatar {
|
||||
|
||||
@@ -117,8 +117,9 @@
|
||||
: `https://cdn.discordapp.com/embed/avatars/0.png`;
|
||||
|
||||
card.innerHTML = `
|
||||
<div class="guild-card-header"></div>
|
||||
<img class="guild-card-avatar" src="${iconUrl}" alt="${g.name}">
|
||||
<div class="guild-card-header">
|
||||
<img class="guild-card-avatar" src="${iconUrl}" alt="${g.name}">
|
||||
</div>
|
||||
<div class="guild-card-body">
|
||||
<h3 class="guild-card-name">${g.name}</h3>
|
||||
<p class="guild-card-info">Cliquez pour configurer</p>
|
||||
|
||||
@@ -552,6 +552,43 @@ body {
|
||||
z-index: 200;
|
||||
}
|
||||
|
||||
/* ===== Stats Channels List ===== */
|
||||
.stats-channel-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: var(--spacing-md);
|
||||
background: var(--bg-secondary);
|
||||
border-radius: var(--radius-md);
|
||||
margin-bottom: var(--spacing-sm);
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.stats-channel-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-xs);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.stats-channel-info strong {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.stats-channel-type {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.stats-channel-format {
|
||||
background: var(--bg-card);
|
||||
padding: 2px 8px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-secondary);
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
/* ===== Responsive ===== */
|
||||
@media (max-width: 900px) {
|
||||
.sidebar {
|
||||
|
||||
@@ -58,6 +58,10 @@
|
||||
<span class="nav-item-icon">🔢</span>
|
||||
Comptage
|
||||
</a>
|
||||
<a class="nav-item" data-section="statschannels">
|
||||
<span class="nav-item-icon">📊</span>
|
||||
Salons de stats
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -698,6 +702,80 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Section: Salons de statistiques -->
|
||||
<section class="config-section" id="section-statschannels">
|
||||
<div class="config-card">
|
||||
<div class="config-card-header">
|
||||
<div class="config-card-title">
|
||||
<span class="icon">📊</span>
|
||||
<h3>Salons de statistiques</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="config-card-body">
|
||||
<div class="info-box">
|
||||
<strong>💡 Comment ça marche ?</strong><br>
|
||||
Créez des salons vocaux dont le nom affiche des statistiques du serveur en temps réel.
|
||||
Les noms sont mis à jour automatiquement toutes les 5 minutes.
|
||||
</div>
|
||||
|
||||
<!-- Formulaire d'ajout -->
|
||||
<div class="sub-section">
|
||||
<h4 class="sub-section-title">➕ Ajouter un salon de stats</h4>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Salon vocal</label>
|
||||
<select class="form-select" id="stats-channel-select"></select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Type de statistique</label>
|
||||
<select class="form-select" id="stats-type-select">
|
||||
<option value="members">👥 Membres (total)</option>
|
||||
<option value="humans">👤 Membres (sans bots)</option>
|
||||
<option value="bots">🤖 Bots</option>
|
||||
<option value="online">🟢 Membres en ligne</option>
|
||||
<option value="voice">🎤 Membres en vocal</option>
|
||||
<option value="roles">🎭 Nombre de rôles</option>
|
||||
<option value="channels">📺 Nombre de salons</option>
|
||||
<option value="boosts">🚀 Boosts</option>
|
||||
<option value="boost_level">💎 Niveau de boost</option>
|
||||
<option value="role_members">🏷️ Membres avec un rôle</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="stats-role-group" style="display: none;">
|
||||
<label class="form-label">Rôle à compter</label>
|
||||
<select class="form-select" id="stats-role-select"></select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Format du nom</label>
|
||||
<input type="text" class="form-input" id="stats-format-input" value="📊 Membres: {stat}" placeholder="📊 Membres: {stat}">
|
||||
</div>
|
||||
|
||||
<div class="variables-box">
|
||||
<div class="variables-box-title">Variables disponibles</div>
|
||||
<div class="variables-list">
|
||||
<span class="variable-tag"><code>{stat}</code> <span>→ valeur de la statistique</span></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-primary" id="add-stats-channel" style="margin-top: var(--spacing-md);">
|
||||
➕ Ajouter le salon
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Liste des salons configurés -->
|
||||
<div class="sub-section">
|
||||
<h4 class="sub-section-title">📋 Salons configurés</h4>
|
||||
<div id="stats-channels-list">
|
||||
<p class="text-muted">Aucun salon configuré.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -714,5 +792,6 @@
|
||||
<script src="/guild/economyForm.js"></script>
|
||||
<script src="/guild/privateroomForm.js"></script>
|
||||
<script src="/guild/countingForm.js"></script>
|
||||
<script src="/guild/statsChannelsForm.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
// ===== STATS CHANNELS FORM =====
|
||||
(async function () {
|
||||
const channelSelect = document.getElementById("stats-channel-select");
|
||||
const typeSelect = document.getElementById("stats-type-select");
|
||||
const roleGroup = document.getElementById("stats-role-group");
|
||||
const roleSelect = document.getElementById("stats-role-select");
|
||||
const formatInput = document.getElementById("stats-format-input");
|
||||
const addBtn = document.getElementById("add-stats-channel");
|
||||
const listContainer = document.getElementById("stats-channels-list");
|
||||
|
||||
const statTypeNames = {
|
||||
members: "👥 Membres (total)",
|
||||
humans: "👤 Membres (sans bots)",
|
||||
bots: "🤖 Bots",
|
||||
online: "🟢 En ligne",
|
||||
voice: "🎤 En vocal",
|
||||
roles: "🎭 Rôles",
|
||||
channels: "📺 Salons",
|
||||
boosts: "🚀 Boosts",
|
||||
boost_level: "💎 Niveau boost",
|
||||
role_members: "🏷️ Membres avec rôle"
|
||||
};
|
||||
|
||||
// Afficher/masquer le sélecteur de rôle
|
||||
typeSelect.addEventListener("change", () => {
|
||||
if (typeSelect.value === "role_members") {
|
||||
roleGroup.style.display = "block";
|
||||
} else {
|
||||
roleGroup.style.display = "none";
|
||||
}
|
||||
|
||||
// Mettre à jour le format par défaut
|
||||
const formats = {
|
||||
members: "👥 Membres: {stat}",
|
||||
humans: "👤 Humains: {stat}",
|
||||
bots: "🤖 Bots: {stat}",
|
||||
online: "🟢 En ligne: {stat}",
|
||||
voice: "🎤 En vocal: {stat}",
|
||||
roles: "🎭 Rôles: {stat}",
|
||||
channels: "📺 Salons: {stat}",
|
||||
boosts: "🚀 Boosts: {stat}",
|
||||
boost_level: "💎 Niveau: {stat}",
|
||||
role_members: "🏷️ Rôle: {stat}"
|
||||
};
|
||||
formatInput.value = formats[typeSelect.value] || "📊 {stat}";
|
||||
});
|
||||
|
||||
// Charger les salons vocaux
|
||||
async function loadVoiceChannels() {
|
||||
try {
|
||||
const res = await fetch(`/api/bot/get-voice-channels/${guildId}`);
|
||||
const channels = await res.json();
|
||||
channelSelect.innerHTML = '<option value="">-- Sélectionner un salon --</option>';
|
||||
channels.forEach(ch => {
|
||||
const opt = document.createElement("option");
|
||||
opt.value = ch.id;
|
||||
opt.textContent = "🔊 " + ch.name;
|
||||
channelSelect.appendChild(opt);
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Erreur chargement salons vocaux:", err);
|
||||
}
|
||||
}
|
||||
|
||||
// Charger les rôles
|
||||
async function loadRoles() {
|
||||
try {
|
||||
const res = await fetch(`/api/bot/get-roles/${guildId}`);
|
||||
const roles = await res.json();
|
||||
roleSelect.innerHTML = '<option value="">-- Sélectionner un rôle --</option>';
|
||||
roles.forEach(role => {
|
||||
const opt = document.createElement("option");
|
||||
opt.value = role.id;
|
||||
opt.textContent = role.name;
|
||||
roleSelect.appendChild(opt);
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Erreur chargement rôles:", err);
|
||||
}
|
||||
}
|
||||
|
||||
// Charger la liste des salons configurés
|
||||
async function loadStatsChannels() {
|
||||
try {
|
||||
const res = await fetch(`/api/bot/get-stats-channels/${guildId}`);
|
||||
const channels = await res.json();
|
||||
|
||||
if (channels.length === 0) {
|
||||
listContainer.innerHTML = '<p class="text-muted">Aucun salon configuré.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Récupérer les infos des salons vocaux pour afficher les noms
|
||||
const voiceRes = await fetch(`/api/bot/get-voice-channels/${guildId}`);
|
||||
const voiceChannels = await voiceRes.json();
|
||||
const voiceMap = {};
|
||||
voiceChannels.forEach(ch => voiceMap[ch.id] = ch.name);
|
||||
|
||||
// Récupérer les rôles
|
||||
const rolesRes = await fetch(`/api/bot/get-roles/${guildId}`);
|
||||
const roles = await rolesRes.json();
|
||||
const rolesMap = {};
|
||||
roles.forEach(r => rolesMap[r.id] = r.name);
|
||||
|
||||
listContainer.innerHTML = channels.map(ch => {
|
||||
const channelName = voiceMap[ch.channel_id] || "Salon inconnu";
|
||||
const typeName = statTypeNames[ch.stat_type] || ch.stat_type;
|
||||
const roleInfo = ch.stat_type === "role_members" && ch.role_id
|
||||
? ` (${rolesMap[ch.role_id] || "Rôle inconnu"})`
|
||||
: "";
|
||||
|
||||
return `
|
||||
<div class="stats-channel-item" data-id="${ch.id}">
|
||||
<div class="stats-channel-info">
|
||||
<strong>🔊 ${channelName}</strong>
|
||||
<span class="stats-channel-type">${typeName}${roleInfo}</span>
|
||||
<code class="stats-channel-format">${ch.format}</code>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-danger delete-stats-channel" data-id="${ch.id}">
|
||||
🗑️ Supprimer
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
}).join("");
|
||||
|
||||
// Ajouter les événements de suppression
|
||||
document.querySelectorAll(".delete-stats-channel").forEach(btn => {
|
||||
btn.addEventListener("click", async () => {
|
||||
const id = btn.dataset.id;
|
||||
if (!confirm("Supprimer ce salon de statistiques ?")) return;
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/bot/delete-stats-channel/${id}`, {
|
||||
method: "DELETE"
|
||||
});
|
||||
const result = await res.json();
|
||||
if (result.success) {
|
||||
loadStatsChannels();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Erreur suppression:", err);
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Erreur chargement stats channels:", err);
|
||||
}
|
||||
}
|
||||
|
||||
// Ajouter un salon
|
||||
addBtn.addEventListener("click", async () => {
|
||||
const channelId = channelSelect.value;
|
||||
const statType = typeSelect.value;
|
||||
const roleId = typeSelect.value === "role_members" ? roleSelect.value : null;
|
||||
const format = formatInput.value || "📊 {stat}";
|
||||
|
||||
if (!channelId) {
|
||||
alert("Veuillez sélectionner un salon.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (statType === "role_members" && !roleId) {
|
||||
alert("Veuillez sélectionner un rôle.");
|
||||
return;
|
||||
}
|
||||
|
||||
addBtn.disabled = true;
|
||||
addBtn.textContent = "Ajout...";
|
||||
|
||||
try {
|
||||
const res = await fetch("/api/bot/add-stats-channel", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ guildId, channelId, statType, roleId, format })
|
||||
});
|
||||
const result = await res.json();
|
||||
|
||||
if (result.success) {
|
||||
channelSelect.value = "";
|
||||
loadStatsChannels();
|
||||
} else {
|
||||
alert("Erreur lors de l'ajout.");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Erreur ajout:", err);
|
||||
alert("Erreur réseau.");
|
||||
}
|
||||
|
||||
addBtn.disabled = false;
|
||||
addBtn.textContent = "➕ Ajouter le salon";
|
||||
});
|
||||
|
||||
// Init
|
||||
await loadVoiceChannels();
|
||||
await loadRoles();
|
||||
await loadStatsChannels();
|
||||
})();
|
||||
@@ -82,6 +82,11 @@
|
||||
<h3>Jeu de Comptage</h3>
|
||||
<p>Un mini-jeu collaboratif où les membres comptent à l'infini. Ils doivent alterner et ne pas se tromper sinon le compteur repart à 0 !</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">📊</div>
|
||||
<h3>Salons de Statistiques</h3>
|
||||
<p>Affichez les stats de votre serveur en temps réel dans des salons vocaux : membres, bots, en ligne, boosts, rôles et plus encore.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">⚙️</div>
|
||||
<h3>Dashboard Intuitif</h3>
|
||||
@@ -110,7 +115,7 @@
|
||||
<span class="stat-label">Utilisateurs</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span id="stat-commands" class="stat-number">20+</span>
|
||||
<span id="stat-commands" class="stat-number">30+</span>
|
||||
<span class="stat-label">Commandes</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user