mirror of
https://github.com/arthur-pbty/LazyBot.git
synced 2026-06-03 15:07:29 +02:00
refonte css of site
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
const { Events } = require("discord.js");
|
||||
const { Events, EmbedBuilder } = require("discord.js");
|
||||
const db = require("../db");
|
||||
|
||||
module.exports = {
|
||||
@@ -10,7 +10,7 @@ module.exports = {
|
||||
(err, row) => {
|
||||
if (err || !row || !row.enabled) return;
|
||||
|
||||
let msg = row.message;
|
||||
let msg = row.message || "Bienvenue {mention} sur {server} !";
|
||||
|
||||
msg = msg
|
||||
.replace("{user}", member.user.username)
|
||||
@@ -19,7 +19,15 @@ module.exports = {
|
||||
|
||||
const channel = member.guild.channels.cache.get(row.channel_id);
|
||||
if (channel) {
|
||||
channel.send(msg);
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0x57F287)
|
||||
.setTitle("👋 Bienvenue !")
|
||||
.setDescription(msg)
|
||||
.setThumbnail(member.user.displayAvatarURL({ dynamic: true, size: 256 }))
|
||||
.setFooter({ text: member.guild.name, iconURL: member.guild.iconURL({ dynamic: true }) })
|
||||
.setTimestamp();
|
||||
|
||||
channel.send({ embeds: [embed] });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { Events } = require("discord.js");
|
||||
const { Events, EmbedBuilder } = require("discord.js");
|
||||
const db = require("../db");
|
||||
|
||||
module.exports = {
|
||||
@@ -10,7 +10,7 @@ module.exports = {
|
||||
(err, row) => {
|
||||
if (err || !row || !row.enabled) return;
|
||||
|
||||
let msg = row.message;
|
||||
let msg = row.message || "Au revoir {user}, tu vas nous manquer !";
|
||||
|
||||
msg = msg
|
||||
.replace("{user}", member.user.username)
|
||||
@@ -18,7 +18,15 @@ module.exports = {
|
||||
|
||||
const channel = member.guild.channels.cache.get(row.channel_id);
|
||||
if (channel) {
|
||||
channel.send(msg);
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xED4245)
|
||||
.setTitle("👋 Au revoir...")
|
||||
.setDescription(msg)
|
||||
.setThumbnail(member.user.displayAvatarURL({ dynamic: true, size: 256 }))
|
||||
.setFooter({ text: member.guild.name, iconURL: member.guild.iconURL({ dynamic: true }) })
|
||||
.setTimestamp();
|
||||
|
||||
channel.send({ embeds: [embed] });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
+40
-15
@@ -1,4 +1,4 @@
|
||||
const { Events } = require("discord.js");
|
||||
const { Events, EmbedBuilder } = require("discord.js");
|
||||
const db = require("../db");
|
||||
|
||||
module.exports = {
|
||||
@@ -31,25 +31,31 @@ module.exports = {
|
||||
[guildId],
|
||||
(err, row) => {
|
||||
if (err || !row || !row.enabled || !row.gain_xp_on_message) return;
|
||||
if (row.role_with_without_type === "with") {
|
||||
|
||||
const roleWithWithoutType = row.role_with_without_type || "without";
|
||||
const salonWithWithoutType = row.salon_with_without_type || "without";
|
||||
|
||||
if (roleWithWithoutType === "with") {
|
||||
const userRoles = message.member.roles.cache;
|
||||
const requiredRoles = JSON.parse(row.role_with_without_xp || "[]");
|
||||
if (!requiredRoles.some(roleId => userRoles.has(roleId))) {
|
||||
return;
|
||||
}
|
||||
} else if (row.role_with_without_type === "without") {
|
||||
} else if (roleWithWithoutType === "without") {
|
||||
const userRoles = message.member.roles.cache;
|
||||
const excludedRoles = JSON.parse(row.role_with_without_xp || "[]");
|
||||
if (excludedRoles.some(roleId => userRoles.has(roleId))) {
|
||||
return;
|
||||
}
|
||||
} else if (row.salon_with_without_type === "with") {
|
||||
}
|
||||
|
||||
if (salonWithWithoutType === "with") {
|
||||
const channelId = message.channel.id;
|
||||
const requiredChannels = JSON.parse(row.salon_with_without_xp || "[]");
|
||||
if (!requiredChannels.includes(channelId)) {
|
||||
return;
|
||||
}
|
||||
} else if (row.salon_with_without_type === "without") {
|
||||
} else if (salonWithWithoutType === "without") {
|
||||
const channelId = message.channel.id;
|
||||
const excludedChannels = JSON.parse(row.salon_with_without_xp || "[]");
|
||||
if (excludedChannels.includes(channelId)) {
|
||||
@@ -64,13 +70,14 @@ module.exports = {
|
||||
(err, userRow) => {
|
||||
if (err) return;
|
||||
|
||||
const cooldownSeconds = row.cooldown_xp_message_seconds ?? 60;
|
||||
const lastTimestamp = userRow ? userRow.last_xp_message_timestamp || 0 : 0;
|
||||
if (now - lastTimestamp < row.cooldown_xp_message_seconds * 1000) {
|
||||
if (now - lastTimestamp < cooldownSeconds * 1000) {
|
||||
return;
|
||||
}
|
||||
|
||||
const minXp = row.gain_xp_message_lower_bound;
|
||||
const maxXp = row.gain_xp_message_upper_bound;
|
||||
const minXp = row.gain_xp_message_lower_bound ?? 15;
|
||||
const maxXp = row.gain_xp_message_upper_bound ?? 25;
|
||||
const xpToAdd = Math.floor(Math.random() * (maxXp - minXp + 1)) + minXp;
|
||||
|
||||
let newXp;
|
||||
@@ -84,17 +91,21 @@ module.exports = {
|
||||
newLevel = 1;
|
||||
}
|
||||
|
||||
const multiplier = row.multiplier_courbe_for_level;
|
||||
const multiplier = row.multiplier_courbe_for_level ?? 100;
|
||||
const courbeType = row.xp_courbe_type || "linear";
|
||||
let fonction_courbe;
|
||||
|
||||
if (row.xp_courbe_type === "constante") {
|
||||
if (courbeType === "constante") {
|
||||
fonction_courbe = (level) => multiplier;
|
||||
} else if (row.xp_courbe_type === "linear") {
|
||||
} else if (courbeType === "linear") {
|
||||
fonction_courbe = (level) => (level) * multiplier;
|
||||
} else if (row.xp_courbe_type === "quadratic") {
|
||||
} else if (courbeType === "quadratic") {
|
||||
fonction_courbe = (level) => (level) * (level) * multiplier;
|
||||
} else if (row.xp_courbe_type === "exponential") {
|
||||
} else if (courbeType === "exponential") {
|
||||
fonction_courbe = (level) => Math.pow(2, (level - 1)) * multiplier;
|
||||
} else {
|
||||
// Fallback au cas où
|
||||
fonction_courbe = (level) => (level) * multiplier;
|
||||
}
|
||||
|
||||
let xpForNextLevel = fonction_courbe(newLevel);
|
||||
@@ -106,13 +117,27 @@ module.exports = {
|
||||
if (row.level_announcements_enabled && (newLevel % row.level_annoncement_every_level === 0)) {
|
||||
const channel = message.guild.channels.cache.get(row.level_announcements_channel_id);
|
||||
if (channel) {
|
||||
let announcementMsg = row.level_announcements_message;
|
||||
let announcementMsg = row.level_announcements_message || "🎉 {mention} a atteint le niveau {level} !";
|
||||
announcementMsg = announcementMsg
|
||||
.replace("{user}", message.author.username)
|
||||
.replace("{mention}", `<@${message.author.id}>`)
|
||||
.replace("{level}", newLevel)
|
||||
.replace("{level-xp}", xpForNextLevel);
|
||||
channel.send(announcementMsg);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xFEE75C)
|
||||
.setTitle("🎉 Level Up !")
|
||||
.setDescription(announcementMsg)
|
||||
.setThumbnail(message.author.displayAvatarURL({ dynamic: true, size: 256 }))
|
||||
.addFields(
|
||||
{ name: "Nouveau niveau", value: `${newLevel}`, inline: true },
|
||||
{ name: "XP pour le prochain", value: `${xpForNextLevel}`, inline: true }
|
||||
)
|
||||
.setFooter({ text: message.guild.name, iconURL: message.guild.iconURL({ dynamic: true }) })
|
||||
.setTimestamp();
|
||||
|
||||
// @ mention car c'est une notification importante
|
||||
channel.send({ content: `<@${message.author.id}>`, embeds: [embed] });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+261
-118
@@ -1,137 +1,280 @@
|
||||
/* ===== Reset minimal ===== */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
||||
@import url('global.css');
|
||||
|
||||
/* ===== Navigation ===== */
|
||||
.navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
background-color: rgba(22, 27, 34, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: var(--spacing-md) 0;
|
||||
}
|
||||
|
||||
.navbar-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 var(--spacing-md);
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.navbar-brand img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.navbar-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.navbar-link {
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
transition: color var(--transition-fast);
|
||||
}
|
||||
|
||||
.navbar-link:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.navbar-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.navbar-user img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid var(--primary);
|
||||
}
|
||||
|
||||
.navbar-user span {
|
||||
color: var(--text-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.navbar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
/* ===== Dashboard Layout ===== */
|
||||
body {
|
||||
background-color: #1e1f29; /* fond sombre type Discord */
|
||||
color: #ffffff;
|
||||
padding-top: 80px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.dashboard-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: var(--spacing-xl) var(--spacing-md);
|
||||
}
|
||||
|
||||
/* ===== Dashboard Header ===== */
|
||||
.dashboard-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: var(--spacing-xl);
|
||||
flex-wrap: wrap;
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid var(--primary);
|
||||
box-shadow: 0 0 20px rgba(88, 101, 242, 0.3);
|
||||
}
|
||||
|
||||
.user-details h1 {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: var(--spacing-xs);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.user-details p {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
/* ===== Section Title ===== */
|
||||
.section-title {
|
||||
font-size: 1.25rem;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: var(--spacing-lg);
|
||||
padding-bottom: var(--spacing-sm);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
/* ===== Guilds Grid ===== */
|
||||
.guilds-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.guild-card {
|
||||
background-color: var(--bg-card);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--border-radius-lg);
|
||||
overflow: hidden;
|
||||
transition: transform var(--transition-normal), border-color var(--transition-normal), box-shadow var(--transition-normal);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.guild-card:hover {
|
||||
transform: translateY(-4px);
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 8px 30px rgba(88, 101, 242, 0.15);
|
||||
}
|
||||
|
||||
.guild-card-header {
|
||||
height: 80px;
|
||||
background: linear-gradient(135deg, var(--primary), #7289da);
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.guild-card-avatar {
|
||||
position: absolute;
|
||||
bottom: -30px;
|
||||
left: var(--spacing-md);
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
border: 4px solid var(--bg-card);
|
||||
background-color: var(--bg-dark);
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.guild-card-body {
|
||||
padding: var(--spacing-lg);
|
||||
padding-top: calc(var(--spacing-lg) + 20px);
|
||||
}
|
||||
|
||||
.guild-card-name {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.guild-card-info {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.guild-card-footer {
|
||||
padding: var(--spacing-md);
|
||||
border-top: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* ===== Empty State ===== */
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: var(--spacing-2xl);
|
||||
background-color: var(--bg-card);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--border-radius-lg);
|
||||
}
|
||||
|
||||
.empty-state-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
.empty-state h3 {
|
||||
font-size: 1.25rem;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.empty-state p {
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
/* ===== Loading State ===== */
|
||||
.loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
justify-content: center;
|
||||
padding: var(--spacing-2xl);
|
||||
}
|
||||
|
||||
/* ===== Navigation ===== */
|
||||
nav {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
background-color: #2f3136;
|
||||
padding: 10px 20px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
nav a {
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
nav a:hover {
|
||||
color: #5865f2;
|
||||
}
|
||||
|
||||
/* ===== Titres ===== */
|
||||
h1, h2 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.8rem;
|
||||
margin: 25px 0 10px 0;
|
||||
border-bottom: 2px solid #5865f2;
|
||||
display: inline-block;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
/* ===== Avatar ===== */
|
||||
#avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
margin: 10px 0 20px 0;
|
||||
border: 2px solid #5865f2;
|
||||
}
|
||||
|
||||
/* ===== Bouton d'invitation ===== */
|
||||
#invite-link {
|
||||
display: inline-block;
|
||||
background-color: #5865f2;
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border-radius: 8px;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
margin-bottom: 30px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
#invite-link:hover {
|
||||
background-color: #4752c4;
|
||||
}
|
||||
|
||||
/* ===== Liste des guilds ===== */
|
||||
ul#guilds-list {
|
||||
list-style: none;
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul#guilds-list li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 15px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 10px;
|
||||
background-color: #161a22;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s, color 0.2s;
|
||||
}
|
||||
|
||||
ul#guilds-list li:hover {
|
||||
background-color: #5865f2;
|
||||
color: white;
|
||||
}
|
||||
|
||||
ul#guilds-list li img {
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 3px solid var(--border-color);
|
||||
border-top-color: var(--primary);
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading p {
|
||||
margin-top: var(--spacing-md);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* ===== Responsive ===== */
|
||||
@media (max-width: 600px) {
|
||||
body {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
nav {
|
||||
@media (max-width: 768px) {
|
||||
.dashboard-header {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
ul#guilds-list li {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
|
||||
.user-avatar {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
ul#guilds-list li img {
|
||||
margin-bottom: 5px;
|
||||
|
||||
.user-details h1 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.guilds-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.navbar-nav .navbar-link {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
+118
-71
@@ -1,108 +1,155 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<title>Tableau de bord</title>
|
||||
<title>Dashboard - LazyBot</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="/dashboard.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<a href="/">Accueil</a>
|
||||
<!-- Navigation -->
|
||||
<nav class="navbar">
|
||||
<div class="navbar-container">
|
||||
<a href="/" class="navbar-brand">
|
||||
<img id="bot-avatar" src="https://cdn.discordapp.com/embed/avatars/0.png" alt="LazyBot">
|
||||
<span>LazyBot</span>
|
||||
</a>
|
||||
<div class="navbar-nav">
|
||||
<a href="/" class="navbar-link">Accueil</a>
|
||||
<a href="/dashboard" class="navbar-link">Dashboard</a>
|
||||
<div class="navbar-actions">
|
||||
<div class="navbar-user">
|
||||
<img id="nav-avatar" src="" alt="Avatar">
|
||||
<span id="nav-username"></span>
|
||||
</div>
|
||||
<a href="/auth/logout" class="btn btn-secondary btn-sm">Déconnexion</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<h1 id="greeting">Chargement...</h1>
|
||||
<img id="avatar" src="" alt="Avatar">
|
||||
<!-- Dashboard Content -->
|
||||
<div class="dashboard-container">
|
||||
<!-- Dashboard Header -->
|
||||
<div class="dashboard-header">
|
||||
<div class="user-info">
|
||||
<img id="user-avatar" class="user-avatar" src="" alt="Avatar">
|
||||
<div class="user-details">
|
||||
<h1 id="greeting">Chargement...</h1>
|
||||
<p>Sélectionnez un serveur pour le configurer</p>
|
||||
</div>
|
||||
</div>
|
||||
<a id="invite-link" href="#" class="btn btn-primary">
|
||||
➕ Ajouter le bot à un serveur
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<a id="invite-link" href="#">Ajouter le bot à votre serveur</a>
|
||||
|
||||
<h2>Mes serveurs qui ont le bot :</h2>
|
||||
<ul id="guilds-list"></ul> <!-- Ici on va lister les guilds -->
|
||||
<!-- Guilds Section -->
|
||||
<h2 class="section-title">🏠 Mes serveurs</h2>
|
||||
|
||||
<div id="guilds-container">
|
||||
<div class="loading">
|
||||
<div class="spinner"></div>
|
||||
<p>Chargement des serveurs...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// --- Affichage des infos utilisateur ---
|
||||
fetch("/api/user")
|
||||
// Charger les infos du bot
|
||||
fetch("/api/bot-info")
|
||||
.then(res => res.json())
|
||||
.then(bot => {
|
||||
document.getElementById("bot-avatar").src = `https://cdn.discordapp.com/avatars/${bot.id}/${bot.avatar}.png`;
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
// Charger les infos utilisateur
|
||||
fetch("/api/user")
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error("Non connecté");
|
||||
return res.json();
|
||||
})
|
||||
.then(user => {
|
||||
document.getElementById("greeting").textContent = `Salut ${user.username}#${user.discriminator} !`;
|
||||
document.getElementById("avatar").src = `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png`;
|
||||
const avatarUrl = `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png`;
|
||||
document.getElementById("greeting").textContent = `Bienvenue, ${user.username} !`;
|
||||
document.getElementById("user-avatar").src = avatarUrl;
|
||||
document.getElementById("nav-avatar").src = avatarUrl;
|
||||
document.getElementById("nav-username").textContent = user.username;
|
||||
})
|
||||
.catch(() => {
|
||||
document.getElementById("greeting").textContent = "Utilisateur non connecté.";
|
||||
window.location.href = "/auth/login";
|
||||
});
|
||||
|
||||
|
||||
// --- Affichage des guilds de l'utilisateur ---
|
||||
// Charger les guilds
|
||||
fetch("/api/guilds")
|
||||
.then(res => res.json())
|
||||
.then(guilds => {
|
||||
const list = document.getElementById("guilds-list");
|
||||
const container = document.getElementById("guilds-container");
|
||||
|
||||
if (guilds.length === 0) {
|
||||
list.innerHTML = "<li>Aucun serveur disponible</li>";
|
||||
container.innerHTML = `
|
||||
<div class="empty-state">
|
||||
<div class="empty-state-icon">🤖</div>
|
||||
<h3>Aucun serveur trouvé</h3>
|
||||
<p>Ajoutez LazyBot à un serveur pour commencer à le configurer.</p>
|
||||
<a id="invite-empty" href="#" class="btn btn-primary">Ajouter à un serveur</a>
|
||||
</div>
|
||||
`;
|
||||
// Récupérer le lien pour le bouton empty state
|
||||
fetch("/invite-bot")
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
const btn = document.getElementById("invite-empty");
|
||||
if (btn) btn.href = data.url;
|
||||
});
|
||||
} else {
|
||||
container.innerHTML = '<div class="guilds-grid"></div>';
|
||||
const grid = container.querySelector('.guilds-grid');
|
||||
|
||||
guilds.forEach(g => {
|
||||
const li = document.createElement("li");
|
||||
li.style.cursor = "pointer";
|
||||
li.style.padding = "8px";
|
||||
li.style.border = "1px solid #ccc";
|
||||
li.style.margin = "5px 0";
|
||||
li.style.display = "flex";
|
||||
li.style.alignItems = "center";
|
||||
li.style.gap = "10px";
|
||||
li.style.borderRadius = "8px";
|
||||
li.style.backgroundColor = "#161a22"; // fond sombre pour chaque item
|
||||
li.style.transition = "background 0.2s";
|
||||
|
||||
li.addEventListener("click", () => {
|
||||
window.location.href = `/guild/${g.id}`;
|
||||
});
|
||||
|
||||
li.addEventListener("mouseover", () => {
|
||||
li.style.backgroundColor = "#5865F2"; // couleur Discord au hover
|
||||
li.style.color = "white";
|
||||
});
|
||||
|
||||
li.addEventListener("mouseout", () => {
|
||||
li.style.backgroundColor = "#161a22";
|
||||
li.style.color = "";
|
||||
});
|
||||
|
||||
// Logo de la guild
|
||||
if (g.icon) {
|
||||
const img = document.createElement("img");
|
||||
img.src = `https://cdn.discordapp.com/icons/${g.id}/${g.icon}.png`;
|
||||
img.alt = `${g.name} logo`;
|
||||
img.style.width = "40px";
|
||||
img.style.height = "40px";
|
||||
img.style.borderRadius = "50%";
|
||||
li.appendChild(img);
|
||||
}
|
||||
|
||||
// Nom de la guild
|
||||
const span = document.createElement("span");
|
||||
span.textContent = g.name;
|
||||
li.appendChild(span);
|
||||
|
||||
list.appendChild(li);
|
||||
const card = document.createElement("div");
|
||||
card.className = "guild-card";
|
||||
card.onclick = () => window.location.href = `/guild/${g.id}`;
|
||||
|
||||
const iconUrl = g.icon
|
||||
? `https://cdn.discordapp.com/icons/${g.id}/${g.icon}.png`
|
||||
: `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-body">
|
||||
<h3 class="guild-card-name">${g.name}</h3>
|
||||
<p class="guild-card-info">Cliquez pour configurer</p>
|
||||
</div>
|
||||
<div class="guild-card-footer">
|
||||
<span class="btn btn-sm btn-primary">Configurer →</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
grid.appendChild(card);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
document.getElementById("guilds-list").innerHTML = "<li>Impossible de récupérer les guilds.</li>";
|
||||
document.getElementById("guilds-container").innerHTML = `
|
||||
<div class="empty-state">
|
||||
<div class="empty-state-icon">❌</div>
|
||||
<h3>Erreur de chargement</h3>
|
||||
<p>Impossible de récupérer la liste des serveurs.</p>
|
||||
<button class="btn btn-primary" onclick="location.reload()">Réessayer</button>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
|
||||
// Lien d'invitation
|
||||
fetch("/invite-bot")
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
const link = document.getElementById("invite-link");
|
||||
link.href = data.url; // met le lien dynamique
|
||||
document.getElementById("invite-link").href = data.url;
|
||||
})
|
||||
.catch(() => {
|
||||
console.log("Impossible de récupérer le lien du bot.");
|
||||
});
|
||||
|
||||
|
||||
.catch(() => console.log("Impossible de récupérer le lien du bot."));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,406 @@
|
||||
/* ===== Variables CSS ===== */
|
||||
:root {
|
||||
/* Couleurs principales */
|
||||
--primary: #5865f2;
|
||||
--primary-hover: #4752c4;
|
||||
--primary-light: rgba(88, 101, 242, 0.1);
|
||||
|
||||
/* Couleurs de fond */
|
||||
--bg-dark: #0d1117;
|
||||
--bg-card: #161b22;
|
||||
--bg-card-hover: #1c2128;
|
||||
--bg-input: #0d1117;
|
||||
--bg-nav: #161b22;
|
||||
|
||||
/* Couleurs de texte */
|
||||
--text-primary: #e6edf3;
|
||||
--text-secondary: #8b949e;
|
||||
--text-muted: #6e7681;
|
||||
|
||||
/* Couleurs d'état */
|
||||
--success: #238636;
|
||||
--success-light: rgba(35, 134, 54, 0.15);
|
||||
--danger: #da3633;
|
||||
--danger-light: rgba(218, 54, 51, 0.15);
|
||||
--warning: #d29922;
|
||||
--warning-light: rgba(210, 153, 34, 0.15);
|
||||
|
||||
/* Bordures */
|
||||
--border-color: #30363d;
|
||||
--border-radius: 6px;
|
||||
--border-radius-lg: 12px;
|
||||
|
||||
/* Ombres */
|
||||
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||
--shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.5);
|
||||
|
||||
/* Transitions */
|
||||
--transition-fast: 150ms ease;
|
||||
--transition-normal: 250ms ease;
|
||||
|
||||
/* Espacements */
|
||||
--spacing-xs: 4px;
|
||||
--spacing-sm: 8px;
|
||||
--spacing-md: 16px;
|
||||
--spacing-lg: 24px;
|
||||
--spacing-xl: 32px;
|
||||
--spacing-2xl: 48px;
|
||||
}
|
||||
|
||||
/* ===== Reset ===== */
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: var(--text-primary);
|
||||
background-color: var(--bg-dark);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
transition: color var(--transition-fast);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--primary-hover);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* ===== Typography ===== */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-weight: 600;
|
||||
line-height: 1.25;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
h1 { font-size: 2rem; }
|
||||
h2 { font-size: 1.5rem; }
|
||||
h3 { font-size: 1.25rem; }
|
||||
h4 { font-size: 1rem; }
|
||||
|
||||
p {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* ===== Buttons ===== */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--spacing-sm);
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
border-radius: var(--border-radius);
|
||||
border: 1px solid transparent;
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-fast);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: var(--primary);
|
||||
color: white;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: var(--primary-hover);
|
||||
border-color: var(--primary-hover);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: var(--bg-card);
|
||||
color: var(--text-primary);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: var(--bg-card-hover);
|
||||
border-color: var(--text-muted);
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background-color: var(--success);
|
||||
color: white;
|
||||
border-color: var(--success);
|
||||
}
|
||||
|
||||
.btn-success:hover {
|
||||
background-color: #2ea043;
|
||||
border-color: #2ea043;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background-color: var(--danger);
|
||||
color: white;
|
||||
border-color: var(--danger);
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background-color: #f85149;
|
||||
border-color: #f85149;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-lg {
|
||||
padding: var(--spacing-md) var(--spacing-lg);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: var(--spacing-xs) var(--spacing-sm);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* ===== Form Elements ===== */
|
||||
.form-group {
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
margin-bottom: var(--spacing-sm);
|
||||
font-weight: 500;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.form-input,
|
||||
.form-select,
|
||||
.form-textarea {
|
||||
width: 100%;
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: var(--text-primary);
|
||||
background-color: var(--bg-input);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--border-radius);
|
||||
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
|
||||
}
|
||||
|
||||
.form-input:focus,
|
||||
.form-select:focus,
|
||||
.form-textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 3px var(--primary-light);
|
||||
}
|
||||
|
||||
.form-input::placeholder {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
resize: vertical;
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.form-select {
|
||||
cursor: pointer;
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%238b949e' viewBox='0 0 16 16'%3E%3Cpath d='M8 11L3 6h10l-5 5z'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 12px center;
|
||||
padding-right: 36px;
|
||||
}
|
||||
|
||||
.form-select[multiple] {
|
||||
background-image: none;
|
||||
padding-right: var(--spacing-md);
|
||||
height: auto;
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
.form-select option {
|
||||
padding: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.form-checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form-checkbox input[type="checkbox"] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
accent-color: var(--primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form-hint {
|
||||
margin-top: var(--spacing-xs);
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* ===== Cards ===== */
|
||||
.card {
|
||||
background-color: var(--bg-card);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--border-radius-lg);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: var(--spacing-md) var(--spacing-lg);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
background-color: var(--bg-card-hover);
|
||||
}
|
||||
|
||||
.card-header h2,
|
||||
.card-header h3 {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
padding: var(--spacing-md) var(--spacing-lg);
|
||||
border-top: 1px solid var(--border-color);
|
||||
background-color: var(--bg-card-hover);
|
||||
}
|
||||
|
||||
/* ===== Alerts ===== */
|
||||
.alert {
|
||||
padding: var(--spacing-md);
|
||||
border-radius: var(--border-radius);
|
||||
margin-bottom: var(--spacing-md);
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background-color: var(--success-light);
|
||||
border: 1px solid var(--success);
|
||||
color: #3fb950;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
background-color: var(--danger-light);
|
||||
border: 1px solid var(--danger);
|
||||
color: #f85149;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background-color: var(--warning-light);
|
||||
border: 1px solid var(--warning);
|
||||
color: #d29922;
|
||||
}
|
||||
|
||||
/* ===== Badge ===== */
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 2px 8px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.badge-primary {
|
||||
background-color: var(--primary-light);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.badge-success {
|
||||
background-color: var(--success-light);
|
||||
color: #3fb950;
|
||||
}
|
||||
|
||||
.badge-danger {
|
||||
background-color: var(--danger-light);
|
||||
color: #f85149;
|
||||
}
|
||||
|
||||
/* ===== Container ===== */
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 var(--spacing-md);
|
||||
}
|
||||
|
||||
.container-sm {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.container-lg {
|
||||
max-width: 1400px;
|
||||
}
|
||||
|
||||
/* ===== Grid ===== */
|
||||
.grid {
|
||||
display: grid;
|
||||
gap: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.grid-2 {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.grid-3 {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.grid-2,
|
||||
.grid-3 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== Utilities ===== */
|
||||
.text-center { text-align: center; }
|
||||
.text-right { text-align: right; }
|
||||
.text-muted { color: var(--text-muted); }
|
||||
.text-success { color: #3fb950; }
|
||||
.text-danger { color: #f85149; }
|
||||
|
||||
.mt-sm { margin-top: var(--spacing-sm); }
|
||||
.mt-md { margin-top: var(--spacing-md); }
|
||||
.mt-lg { margin-top: var(--spacing-lg); }
|
||||
.mb-sm { margin-bottom: var(--spacing-sm); }
|
||||
.mb-md { margin-bottom: var(--spacing-md); }
|
||||
.mb-lg { margin-bottom: var(--spacing-lg); }
|
||||
|
||||
.flex { display: flex; }
|
||||
.flex-center { align-items: center; justify-content: center; }
|
||||
.flex-between { justify-content: space-between; }
|
||||
.gap-sm { gap: var(--spacing-sm); }
|
||||
.gap-md { gap: var(--spacing-md); }
|
||||
+534
-104
@@ -1,143 +1,573 @@
|
||||
/* ===== Reset minimal ===== */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
||||
@import url('global.css');
|
||||
|
||||
/* ===== Layout ===== */
|
||||
body {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #1e1f29; /* fond sombre type Discord */
|
||||
color: #ffffff;
|
||||
/* ===== Sidebar ===== */
|
||||
.sidebar {
|
||||
width: 260px;
|
||||
background-color: var(--bg-card);
|
||||
border-right: 1px solid var(--border-color);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
/* ===== Navigation ===== */
|
||||
nav {
|
||||
width: 100%;
|
||||
max-width: 900px;
|
||||
.sidebar-header {
|
||||
padding: var(--spacing-lg);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.sidebar-brand {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
background-color: #2f3136;
|
||||
padding: 10px 20px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
nav a {
|
||||
color: #ffffff;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
margin-right: 15px;
|
||||
transition: color 0.2s;
|
||||
color: var(--text-primary);
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
nav a:hover {
|
||||
color: #5865f2;
|
||||
.sidebar-brand img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* ===== Titres ===== */
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 20px;
|
||||
.guild-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
padding: var(--spacing-sm);
|
||||
background-color: var(--bg-dark);
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.8rem;
|
||||
margin: 25px 0 10px 0;
|
||||
border-bottom: 2px solid #5865f2;
|
||||
display: inline-block;
|
||||
padding-bottom: 5px;
|
||||
text-align: center;
|
||||
.guild-info img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* ===== Formulaires ===== */
|
||||
form {
|
||||
background-color: #2f3136;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 30px;
|
||||
width: 100%;
|
||||
max-width: 700px;
|
||||
.guild-info-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
form label {
|
||||
display: block;
|
||||
margin-bottom: 15px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
form input[type="checkbox"] {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
form select,
|
||||
form textarea {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #5865f2;
|
||||
background-color: #161a22;
|
||||
color: #ffffff;
|
||||
margin-top: 5px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
form textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
form button {
|
||||
background-color: #5865f2;
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
.guild-info-name {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-size: 0.9rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.guild-info-id {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
/* ===== Sidebar Navigation ===== */
|
||||
.sidebar-nav {
|
||||
flex: 1;
|
||||
padding: var(--spacing-md);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.nav-section {
|
||||
margin-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.nav-section-title {
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
padding: var(--spacing-xs) var(--spacing-sm);
|
||||
margin-bottom: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
border-radius: var(--border-radius);
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
transition: all var(--transition-fast);
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
form button:hover {
|
||||
background-color: #4752c4;
|
||||
.nav-item:hover {
|
||||
background-color: var(--bg-dark);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
form small {
|
||||
.nav-item.active {
|
||||
background-color: var(--primary-light);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.nav-item-icon {
|
||||
font-size: 1.1rem;
|
||||
width: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ===== Sidebar Footer ===== */
|
||||
.sidebar-footer {
|
||||
padding: var(--spacing-md);
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.sidebar-footer a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
border-radius: var(--border-radius);
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
font-size: 0.9rem;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.sidebar-footer a:hover {
|
||||
background-color: var(--bg-dark);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* ===== Main Content ===== */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
margin-left: 260px;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* ===== Top Header ===== */
|
||||
.top-header {
|
||||
background-color: var(--bg-card);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: var(--spacing-md) var(--spacing-xl);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.breadcrumb a {
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.breadcrumb a:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.breadcrumb-separator {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.breadcrumb-current {
|
||||
color: var(--text-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.header-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.header-user img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.header-user span {
|
||||
color: var(--text-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* ===== Content Area ===== */
|
||||
.content-area {
|
||||
flex: 1;
|
||||
padding: var(--spacing-xl);
|
||||
max-width: 900px;
|
||||
}
|
||||
|
||||
/* ===== Config Sections ===== */
|
||||
.config-section {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.config-section.active {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
font-size: 0.85rem;
|
||||
color: #b9bbbe;
|
||||
}
|
||||
|
||||
form small ul {
|
||||
list-style: disc inside;
|
||||
margin-top: 5px;
|
||||
/* ===== Config Card ===== */
|
||||
.config-card {
|
||||
background-color: var(--bg-card);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--border-radius-lg);
|
||||
margin-bottom: var(--spacing-lg);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#status-welcome-form,
|
||||
#status-goodbye-form,
|
||||
#status-autorole-form,
|
||||
#status-autorole-vocal-form {
|
||||
margin-top: 10px;
|
||||
.config-card-header {
|
||||
padding: var(--spacing-lg);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.config-card-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.config-card-title h3 {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* ===== Select multiple pour vocal ===== */
|
||||
select[multiple] {
|
||||
.config-card-title .icon {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.config-card-body {
|
||||
padding: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.config-card-footer {
|
||||
padding: var(--spacing-md) var(--spacing-lg);
|
||||
border-top: 1px solid var(--border-color);
|
||||
background-color: var(--bg-dark);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* ===== Toggle Switch ===== */
|
||||
.toggle-switch {
|
||||
position: relative;
|
||||
width: 48px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.toggle-switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.toggle-slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: var(--bg-dark);
|
||||
border: 1px solid var(--border-color);
|
||||
transition: var(--transition-fast);
|
||||
border-radius: 24px;
|
||||
}
|
||||
|
||||
.toggle-slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
left: 2px;
|
||||
bottom: 2px;
|
||||
background-color: var(--text-muted);
|
||||
transition: var(--transition-fast);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.toggle-switch input:checked + .toggle-slider {
|
||||
background-color: var(--primary);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.toggle-switch input:checked + .toggle-slider:before {
|
||||
background-color: white;
|
||||
transform: translateX(24px);
|
||||
}
|
||||
|
||||
/* ===== Form Styles ===== */
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
.form-group:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
font-weight: 500;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: var(--spacing-xs);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.form-sublabel {
|
||||
display: block;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.8rem;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.form-input,
|
||||
.form-select,
|
||||
.form-textarea {
|
||||
width: 100%;
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
background-color: var(--bg-dark);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--border-radius);
|
||||
color: var(--text-primary);
|
||||
font-size: 0.9rem;
|
||||
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
|
||||
}
|
||||
|
||||
.form-input:focus,
|
||||
.form-select:focus,
|
||||
.form-textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 3px var(--primary-light);
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
resize: vertical;
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.form-select[multiple] {
|
||||
height: auto;
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
.form-hint {
|
||||
margin-top: var(--spacing-xs);
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.form-hint code {
|
||||
background-color: var(--bg-dark);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-family: monospace;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
/* ===== Inline Toggle ===== */
|
||||
.inline-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: var(--spacing-md);
|
||||
background-color: var(--bg-dark);
|
||||
border-radius: var(--border-radius);
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
.inline-toggle:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.inline-toggle-label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.inline-toggle-title {
|
||||
font-weight: 500;
|
||||
color: var(--text-primary);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.inline-toggle-desc {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.8rem;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
/* ===== Status Messages ===== */
|
||||
.status-message {
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
border-radius: var(--border-radius);
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.status-message.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.status-message.success {
|
||||
background-color: rgba(35, 134, 54, 0.15);
|
||||
color: var(--success);
|
||||
border: 1px solid var(--success);
|
||||
}
|
||||
|
||||
.status-message.error {
|
||||
background-color: rgba(248, 81, 73, 0.15);
|
||||
color: var(--danger);
|
||||
border: 1px solid var(--danger);
|
||||
}
|
||||
|
||||
/* ===== Sub-sections ===== */
|
||||
.sub-section {
|
||||
margin-top: var(--spacing-lg);
|
||||
padding-top: var(--spacing-lg);
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.sub-section-title {
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: var(--spacing-md);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
/* ===== Variables Info Box ===== */
|
||||
.variables-box {
|
||||
background-color: var(--bg-dark);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--border-radius);
|
||||
padding: var(--spacing-md);
|
||||
margin-top: var(--spacing-md);
|
||||
}
|
||||
|
||||
.variables-box-title {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.variables-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.variable-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px 8px;
|
||||
background-color: var(--bg-card);
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.variable-tag code {
|
||||
color: var(--primary);
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.variable-tag span {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* ===== Mobile Sidebar Toggle ===== */
|
||||
.mobile-toggle {
|
||||
display: none;
|
||||
position: fixed;
|
||||
bottom: var(--spacing-lg);
|
||||
right: var(--spacing-lg);
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
background-color: var(--primary);
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
box-shadow: var(--shadow-lg);
|
||||
z-index: 200;
|
||||
}
|
||||
|
||||
/* ===== Responsive ===== */
|
||||
@media (max-width: 600px) {
|
||||
body {
|
||||
padding: 10px;
|
||||
@media (max-width: 900px) {
|
||||
.sidebar {
|
||||
transform: translateX(-100%);
|
||||
transition: transform var(--transition-normal);
|
||||
}
|
||||
|
||||
form {
|
||||
padding: 15px;
|
||||
|
||||
.sidebar.open {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.mobile-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.content-area {
|
||||
padding: var(--spacing-md);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.form-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.config-card-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
}
|
||||
|
||||
+574
-417
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
const autoroleNewUserForm = document.getElementById("autorole-newuser-form");
|
||||
const autoroleEnabled = document.getElementById("autorole-enabled");
|
||||
const autoroleRole = document.getElementById("autorole-role");
|
||||
const statusAutoroleForm = document.getElementById("status-autorole-form");
|
||||
const saveAutorole = document.getElementById("save-autorole");
|
||||
|
||||
// Charger la config
|
||||
fetch(`/api/bot/get-autorole-newuser-config/${guildId}`)
|
||||
.then(res => res.json())
|
||||
.then(cfg => {
|
||||
@@ -10,20 +10,32 @@ fetch(`/api/bot/get-autorole-newuser-config/${guildId}`)
|
||||
autoroleRole.value = cfg.roleId;
|
||||
});
|
||||
|
||||
autoroleNewUserForm.addEventListener("submit", async e => {
|
||||
e.preventDefault();
|
||||
// Sauvegarder
|
||||
saveAutorole.addEventListener("click", async () => {
|
||||
saveAutorole.disabled = true;
|
||||
saveAutorole.textContent = "Sauvegarde...";
|
||||
|
||||
const res = await fetch("/api/bot/save-autorole-newuser-config", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
guildId,
|
||||
enabled: autoroleEnabled.checked,
|
||||
roleId: autoroleRole.value
|
||||
})
|
||||
});
|
||||
try {
|
||||
const res = await fetch("/api/bot/save-autorole-newuser-config", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
guildId,
|
||||
enabled: autoroleEnabled.checked,
|
||||
roleId: autoroleRole.value
|
||||
})
|
||||
});
|
||||
|
||||
statusAutoroleForm.textContent = (await res.json()).success
|
||||
? "Auto-rôle sauvegardé ✅"
|
||||
: "Erreur ❌";
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
showStatus("status-autorole-form", "Configuration sauvegardée ✅", "success");
|
||||
} else {
|
||||
showStatus("status-autorole-form", "Erreur lors de la sauvegarde ❌", "error");
|
||||
}
|
||||
} catch (error) {
|
||||
showStatus("status-autorole-form", "Erreur de connexion ❌", "error");
|
||||
}
|
||||
|
||||
saveAutorole.disabled = false;
|
||||
saveAutorole.textContent = "Sauvegarder";
|
||||
});
|
||||
|
||||
@@ -1,43 +1,50 @@
|
||||
const autoroleVocalForm = document.getElementById("autorole-vocal-form");
|
||||
const autoroleVocalEnabled = document.getElementById("autorole-vocal-enabled");
|
||||
const autoroleVocalRole = document.getElementById("autorole-vocal-role");
|
||||
const excludeSelect = document.getElementById("autorole-vocal-exclude-channel");
|
||||
const statusAutoroleVocalForm = document.getElementById("status-autorole-vocal-form");
|
||||
const saveAutoroleVocal = document.getElementById("save-autorole-vocal");
|
||||
|
||||
fetch(`/api/bot/get-voice-channels/${guildId}`)
|
||||
.then(res => res.json())
|
||||
.then(channels => {
|
||||
channels.forEach(c => {
|
||||
excludeSelect.appendChild(new Option(`#${c.name}`, c.id));
|
||||
// Charger la config après que les channels soient chargés
|
||||
setTimeout(() => {
|
||||
fetch(`/api/bot/get-autorole-vocal-config/${guildId}`)
|
||||
.then(res => res.json())
|
||||
.then(cfg => {
|
||||
autoroleVocalEnabled.checked = cfg.enabled;
|
||||
autoroleVocalRole.value = cfg.roleId;
|
||||
Array.from(excludeSelect.options).forEach(opt => {
|
||||
opt.selected = cfg.excludeChannelIds?.includes(opt.value);
|
||||
});
|
||||
});
|
||||
return fetch(`/api/bot/get-autorole-vocal-config/${guildId}`);
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(cfg => {
|
||||
autoroleVocalEnabled.checked = cfg.enabled;
|
||||
autoroleVocalRole.value = cfg.roleId;
|
||||
Array.from(excludeSelect.options).forEach(opt => {
|
||||
opt.selected = cfg.excludeChannelIds?.includes(opt.value);
|
||||
}, 500);
|
||||
|
||||
// Sauvegarder
|
||||
saveAutoroleVocal.addEventListener("click", async () => {
|
||||
saveAutoroleVocal.disabled = true;
|
||||
saveAutoroleVocal.textContent = "Sauvegarde...";
|
||||
|
||||
try {
|
||||
const exclude = Array.from(excludeSelect.selectedOptions).map(o => o.value);
|
||||
|
||||
const res = await fetch("/api/bot/save-autorole-vocal-config", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
guildId,
|
||||
enabled: autoroleVocalEnabled.checked,
|
||||
roleId: autoroleVocalRole.value,
|
||||
excludeChannelId: exclude
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
autoroleVocalForm.addEventListener("submit", async e => {
|
||||
e.preventDefault();
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
showStatus("status-autorole-vocal-form", "Configuration sauvegardée ✅", "success");
|
||||
} else {
|
||||
showStatus("status-autorole-vocal-form", "Erreur lors de la sauvegarde ❌", "error");
|
||||
}
|
||||
} catch (error) {
|
||||
showStatus("status-autorole-vocal-form", "Erreur de connexion ❌", "error");
|
||||
}
|
||||
|
||||
const exclude = Array.from(excludeSelect.selectedOptions).map(o => o.value);
|
||||
|
||||
const res = await fetch("/api/bot/save-autorole-vocal-config", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
guildId,
|
||||
enabled: autoroleVocalEnabled.checked,
|
||||
roleId: autoroleVocalRole.value,
|
||||
excludeChannelId: exclude
|
||||
})
|
||||
});
|
||||
|
||||
statusAutoroleVocalForm.textContent = (await res.json()).success
|
||||
? "Auto-rôle vocal sauvegardé ✅"
|
||||
: "Erreur ❌";
|
||||
saveAutoroleVocal.disabled = false;
|
||||
saveAutoroleVocal.textContent = "Sauvegarder";
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
const economyForm = document.getElementById("economy-form");
|
||||
const economyEnabled = document.getElementById("economy-enabled");
|
||||
const currencyName = document.getElementById("economy-currency-name");
|
||||
const currencySymbol = document.getElementById("economy-currency-symbol");
|
||||
@@ -42,7 +41,7 @@ const voiceMoneyMin = document.getElementById("economy-voice-money-min");
|
||||
const voiceMoneyMax = document.getElementById("economy-voice-money-max");
|
||||
const voiceMoneyInterval = document.getElementById("economy-voice-money-interval");
|
||||
|
||||
const statusEconomyForm = document.getElementById("status-economy-form");
|
||||
const saveEconomy = document.getElementById("save-economy");
|
||||
|
||||
// Charger la config existante
|
||||
fetch(`/api/bot/get-economy-config/${guildId}`)
|
||||
@@ -94,60 +93,71 @@ fetch(`/api/bot/get-economy-config/${guildId}`)
|
||||
.catch(console.error);
|
||||
|
||||
// Sauvegarder la config
|
||||
economyForm.addEventListener("submit", async e => {
|
||||
e.preventDefault();
|
||||
saveEconomy.addEventListener("click", async () => {
|
||||
saveEconomy.disabled = true;
|
||||
saveEconomy.textContent = "Sauvegarde...";
|
||||
|
||||
const res = await fetch("/api/bot/save-economy-config", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
guildId,
|
||||
economyEnabled: economyEnabled.checked,
|
||||
currencyName: currencyName.value,
|
||||
currencySymbol: currencySymbol.value,
|
||||
startingBalance: parseInt(startingBalance.value, 10),
|
||||
try {
|
||||
const res = await fetch("/api/bot/save-economy-config", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
guildId,
|
||||
economyEnabled: economyEnabled.checked,
|
||||
currencyName: currencyName.value,
|
||||
currencySymbol: currencySymbol.value,
|
||||
startingBalance: parseInt(startingBalance.value, 10),
|
||||
|
||||
// Daily
|
||||
dailyEnabled: dailyEnabled.checked,
|
||||
dailyAmount: parseInt(dailyAmount.value, 10),
|
||||
dailyCooldownHours: parseInt(dailyCooldown.value, 10),
|
||||
// Daily
|
||||
dailyEnabled: dailyEnabled.checked,
|
||||
dailyAmount: parseInt(dailyAmount.value, 10),
|
||||
dailyCooldownHours: parseInt(dailyCooldown.value, 10),
|
||||
|
||||
// Work
|
||||
workEnabled: workEnabled.checked,
|
||||
workMinAmount: parseInt(workMin.value, 10),
|
||||
workMaxAmount: parseInt(workMax.value, 10),
|
||||
workCooldownMinutes: parseInt(workCooldown.value, 10),
|
||||
// Work
|
||||
workEnabled: workEnabled.checked,
|
||||
workMinAmount: parseInt(workMin.value, 10),
|
||||
workMaxAmount: parseInt(workMax.value, 10),
|
||||
workCooldownMinutes: parseInt(workCooldown.value, 10),
|
||||
|
||||
// Crime
|
||||
crimeEnabled: crimeEnabled.checked,
|
||||
crimeMinAmount: parseInt(crimeMin.value, 10),
|
||||
crimeMaxAmount: parseInt(crimeMax.value, 10),
|
||||
crimeSuccessRate: parseInt(crimeSuccess.value, 10),
|
||||
crimeFinePercent: parseInt(crimeFine.value, 10),
|
||||
crimeCooldownMinutes: parseInt(crimeCooldown.value, 10),
|
||||
// Crime
|
||||
crimeEnabled: crimeEnabled.checked,
|
||||
crimeMinAmount: parseInt(crimeMin.value, 10),
|
||||
crimeMaxAmount: parseInt(crimeMax.value, 10),
|
||||
crimeSuccessRate: parseInt(crimeSuccess.value, 10),
|
||||
crimeFinePercent: parseInt(crimeFine.value, 10),
|
||||
crimeCooldownMinutes: parseInt(crimeCooldown.value, 10),
|
||||
|
||||
// Steal
|
||||
stealEnabled: stealEnabled.checked,
|
||||
stealSuccessRate: parseInt(stealSuccess.value, 10),
|
||||
stealMaxPercent: parseInt(stealMaxPercent.value, 10),
|
||||
stealFinePercent: parseInt(stealFine.value, 10),
|
||||
stealCooldownMinutes: parseInt(stealCooldown.value, 10),
|
||||
// Steal
|
||||
stealEnabled: stealEnabled.checked,
|
||||
stealSuccessRate: parseInt(stealSuccess.value, 10),
|
||||
stealMaxPercent: parseInt(stealMaxPercent.value, 10),
|
||||
stealFinePercent: parseInt(stealFine.value, 10),
|
||||
stealCooldownMinutes: parseInt(stealCooldown.value, 10),
|
||||
|
||||
// Message Money
|
||||
messageMoneyEnabled: messageMoneyEnabled.checked,
|
||||
messageMoneyMin: parseInt(messageMoneyMin.value, 10),
|
||||
messageMoneyMax: parseInt(messageMoneyMax.value, 10),
|
||||
messageMoneyCooldownSeconds: parseInt(messageMoneyCooldown.value, 10),
|
||||
// Message Money
|
||||
messageMoneyEnabled: messageMoneyEnabled.checked,
|
||||
messageMoneyMin: parseInt(messageMoneyMin.value, 10),
|
||||
messageMoneyMax: parseInt(messageMoneyMax.value, 10),
|
||||
messageMoneyCooldownSeconds: parseInt(messageMoneyCooldown.value, 10),
|
||||
|
||||
// Voice Money
|
||||
voiceMoneyEnabled: voiceMoneyEnabled.checked,
|
||||
voiceMoneyMin: parseInt(voiceMoneyMin.value, 10),
|
||||
voiceMoneyMax: parseInt(voiceMoneyMax.value, 10),
|
||||
voiceMoneyIntervalMinutes: parseInt(voiceMoneyInterval.value, 10)
|
||||
})
|
||||
});
|
||||
// Voice Money
|
||||
voiceMoneyEnabled: voiceMoneyEnabled.checked,
|
||||
voiceMoneyMin: parseInt(voiceMoneyMin.value, 10),
|
||||
voiceMoneyMax: parseInt(voiceMoneyMax.value, 10),
|
||||
voiceMoneyIntervalMinutes: parseInt(voiceMoneyInterval.value, 10)
|
||||
})
|
||||
});
|
||||
|
||||
statusEconomyForm.textContent = (await res.json()).success
|
||||
? "Config économie sauvegardée ✅"
|
||||
: "Erreur ❌";
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
showStatus("status-economy-form", "Configuration sauvegardée ✅", "success");
|
||||
} else {
|
||||
showStatus("status-economy-form", "Erreur lors de la sauvegarde ❌", "error");
|
||||
}
|
||||
} catch (error) {
|
||||
showStatus("status-economy-form", "Erreur de connexion ❌", "error");
|
||||
}
|
||||
|
||||
saveEconomy.disabled = false;
|
||||
saveEconomy.textContent = "Sauvegarder";
|
||||
});
|
||||
|
||||
@@ -1,32 +1,47 @@
|
||||
const goodbyeForm = document.getElementById("goodbye-form");
|
||||
const goodbyeEnabled = document.getElementById("goodbye-enabled");
|
||||
const goodbyeChannel = document.getElementById("goodbye-channel");
|
||||
const goodbyeMessage = document.getElementById("goodbye-message");
|
||||
const statusGoodbyeForm = document.getElementById("status-goodbye-form");
|
||||
const saveGoodbye = document.getElementById("save-goodbye");
|
||||
|
||||
// Message par défaut
|
||||
const defaultGoodbyeMessage = "Au revoir **{user}**, on espère te revoir sur **{server}** ! 👋";
|
||||
|
||||
// Charger la config
|
||||
fetch(`/api/bot/get-goodbye-config/${guildId}`)
|
||||
.then(res => res.json())
|
||||
.then(cfg => {
|
||||
goodbyeEnabled.checked = cfg.enabled;
|
||||
goodbyeChannel.value = cfg.channelId;
|
||||
goodbyeMessage.value = cfg.message;
|
||||
goodbyeMessage.value = cfg.message || defaultGoodbyeMessage;
|
||||
});
|
||||
|
||||
goodbyeForm.addEventListener("submit", async e => {
|
||||
e.preventDefault();
|
||||
// Sauvegarder
|
||||
saveGoodbye.addEventListener("click", async () => {
|
||||
saveGoodbye.disabled = true;
|
||||
saveGoodbye.textContent = "Sauvegarde...";
|
||||
|
||||
const res = await fetch("/api/bot/save-goodbye-config", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
guildId,
|
||||
goodbyeEnabled: goodbyeEnabled.checked,
|
||||
channelId: goodbyeChannel.value,
|
||||
goodbyeMessage: goodbyeMessage.value
|
||||
})
|
||||
});
|
||||
try {
|
||||
const res = await fetch("/api/bot/save-goodbye-config", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
guildId,
|
||||
goodbyeEnabled: goodbyeEnabled.checked,
|
||||
channelId: goodbyeChannel.value,
|
||||
goodbyeMessage: goodbyeMessage.value
|
||||
})
|
||||
});
|
||||
|
||||
statusGoodbyeForm.textContent = (await res.json()).success
|
||||
? "Config au revoir sauvegardée ✅"
|
||||
: "Erreur ❌";
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
showStatus("status-goodbye-form", "Configuration sauvegardée ✅", "success");
|
||||
} else {
|
||||
showStatus("status-goodbye-form", "Erreur lors de la sauvegarde ❌", "error");
|
||||
}
|
||||
} catch (error) {
|
||||
showStatus("status-goodbye-form", "Erreur de connexion ❌", "error");
|
||||
}
|
||||
|
||||
saveGoodbye.disabled = false;
|
||||
saveGoodbye.textContent = "Sauvegarder";
|
||||
});
|
||||
|
||||
@@ -1,12 +1,55 @@
|
||||
window.guildId = window.location.pathname.split("/")[2];
|
||||
|
||||
// Nom du serveur
|
||||
// Charger les infos du bot
|
||||
fetch("/api/bot-info")
|
||||
.then(res => res.json())
|
||||
.then(bot => {
|
||||
const botAvatar = document.getElementById("bot-avatar");
|
||||
if (botAvatar) {
|
||||
botAvatar.src = `https://cdn.discordapp.com/avatars/${bot.id}/${bot.avatar}.png`;
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
// Charger les infos utilisateur
|
||||
fetch("/api/user")
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error("Non connecté");
|
||||
return res.json();
|
||||
})
|
||||
.then(user => {
|
||||
const avatarUrl = `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png`;
|
||||
const userAvatar = document.getElementById("user-avatar");
|
||||
const userName = document.getElementById("user-name");
|
||||
|
||||
if (userAvatar) userAvatar.src = avatarUrl;
|
||||
if (userName) userName.textContent = user.username;
|
||||
})
|
||||
.catch(() => {
|
||||
window.location.href = "/auth/login";
|
||||
});
|
||||
|
||||
// Nom et icône du serveur
|
||||
fetch("/api/guilds")
|
||||
.then(res => res.json())
|
||||
.then(guilds => {
|
||||
const guild = guilds.find(g => g.id === guildId);
|
||||
document.getElementById("guild-name").textContent =
|
||||
guild ? `Dashboard : ${guild.name}` : "Serveur introuvable";
|
||||
const guildName = document.getElementById("guild-name");
|
||||
const guildIcon = document.getElementById("guild-icon");
|
||||
const guildIdEl = document.getElementById("guild-id");
|
||||
const breadcrumbGuild = document.getElementById("breadcrumb-guild");
|
||||
|
||||
if (guild) {
|
||||
if (guildName) guildName.textContent = guild.name;
|
||||
if (breadcrumbGuild) breadcrumbGuild.textContent = guild.name;
|
||||
if (guildIdEl) guildIdEl.textContent = `ID: ${guild.id}`;
|
||||
if (guildIcon && guild.icon) {
|
||||
guildIcon.src = `https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}.png`;
|
||||
}
|
||||
document.title = `${guild.name} - LazyBot`;
|
||||
} else {
|
||||
if (guildName) guildName.textContent = "Serveur introuvable";
|
||||
}
|
||||
});
|
||||
|
||||
// Channels texte
|
||||
@@ -16,12 +59,26 @@ fetch(`/api/bot/get-text-channels/${guildId}`)
|
||||
const welcome = document.getElementById("welcome-channel");
|
||||
const goodbye = document.getElementById("goodbye-channel");
|
||||
const levelAnnouncements = document.getElementById("level-announcements-channel");
|
||||
const levelChannelRestrict = document.getElementById("level-channel-with-or-without-xp");
|
||||
|
||||
channels.forEach(c => {
|
||||
const opt = new Option(`#${c.name}`, c.id);
|
||||
welcome?.appendChild(opt);
|
||||
goodbye?.appendChild(opt.cloneNode(true));
|
||||
levelAnnouncements?.appendChild(opt.cloneNode(true));
|
||||
levelChannelRestrict?.appendChild(opt.cloneNode(true));
|
||||
});
|
||||
});
|
||||
|
||||
// Channels vocaux
|
||||
fetch(`/api/bot/get-voice-channels/${guildId}`)
|
||||
.then(res => res.json())
|
||||
.then(channels => {
|
||||
const vocalExclude = document.getElementById("autorole-vocal-exclude-channel");
|
||||
|
||||
channels.forEach(c => {
|
||||
const opt = new Option(`🔊 ${c.name}`, c.id);
|
||||
vocalExclude?.appendChild(opt);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -31,10 +88,12 @@ fetch(`/api/bot/get-roles/${guildId}`)
|
||||
.then(roles => {
|
||||
const newUser = document.getElementById("autorole-role");
|
||||
const vocal = document.getElementById("autorole-vocal-role");
|
||||
const levelRoleRestrict = document.getElementById("level-role-with-or-without-xp");
|
||||
|
||||
roles.forEach(r => {
|
||||
const opt = new Option(r.name, r.id);
|
||||
newUser?.appendChild(opt);
|
||||
vocal?.appendChild(opt.cloneNode(true));
|
||||
levelRoleRestrict?.appendChild(opt.cloneNode(true));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
const levelForm = document.getElementById("level-form");
|
||||
const levelEnabled = document.getElementById("level-enabled");
|
||||
const levelAnnouncementsEnabled = document.getElementById("level-announcement-enabled");
|
||||
const levelAnnouncementsChannel = document.getElementById("level-announcements-channel");
|
||||
@@ -18,102 +17,91 @@ const cooldownXpMessageSeconds = document.getElementById("level-xp-cooldown");
|
||||
const gainXpOnVoice = document.getElementById("voice-xp-enabled");
|
||||
const gainVoiceXpLowerBound = document.getElementById("level-xp-per-voice-min");
|
||||
const gainVoiceXpUpperBound = document.getElementById("level-xp-per-voice-max");
|
||||
const statusLevelForm = document.getElementById("status-level-form");
|
||||
const saveLevel = document.getElementById("save-level");
|
||||
|
||||
// 1️⃣ RÔLES
|
||||
fetch(`/api/bot/get-roles/${guildId}`)
|
||||
.then(res => res.json())
|
||||
.then(roles => {
|
||||
roles.forEach(r => {
|
||||
roleWithWithoutXp?.appendChild(new Option(r.name, r.id));
|
||||
});
|
||||
// Message par défaut
|
||||
const defaultLevelMessage = "Félicitations {mention}, tu es maintenant niveau **{level}** ! 🎉";
|
||||
|
||||
// 2️⃣ SALONS TEXTE
|
||||
return fetch(`/api/bot/get-text-channels/${guildId}`);
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(textSalons => {
|
||||
textSalons.forEach(c => {
|
||||
salonWithWithoutXp?.appendChild(new Option(`#${c.name}`, c.id));
|
||||
});
|
||||
// Charger la config après que les channels/roles soient chargés
|
||||
setTimeout(() => {
|
||||
fetch(`/api/bot/get-level-config/${guildId}`)
|
||||
.then(res => res.json())
|
||||
.then(cfg => {
|
||||
levelEnabled.checked = cfg.enabled;
|
||||
levelAnnouncementsEnabled.checked = cfg.levelAnnouncementsEnabled;
|
||||
levelAnnouncementsChannel.value = cfg.levelAnnouncementsChannelId;
|
||||
levelAnnouncementsMessage.value = cfg.levelAnnouncementsMessage || defaultLevelMessage;
|
||||
|
||||
// 3️⃣ SALONS VOCAUX
|
||||
return fetch(`/api/bot/get-voice-channels/${guildId}`);
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(voiceSalons => {
|
||||
voiceSalons.forEach(c => {
|
||||
salonWithWithoutXp?.appendChild(new Option(`🔊 ${c.name}`, c.id));
|
||||
});
|
||||
xpCourbeType.value = cfg.xpCourbeType;
|
||||
multiplierCourbeForLevel.value = cfg.multiplierCourbeForLevel;
|
||||
levelAnnouncementEveryLevel.value = cfg.levelAnnouncementEveryLevel;
|
||||
levelMax.value = cfg.levelMax;
|
||||
|
||||
// 4️⃣ CONFIG (APRÈS QUE TOUT EST CHARGÉ)
|
||||
return fetch(`/api/bot/get-level-config/${guildId}`);
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(cfg => {
|
||||
levelEnabled.checked = cfg.enabled;
|
||||
levelAnnouncementsEnabled.checked = cfg.levelAnnouncementsEnabled;
|
||||
levelAnnouncementsChannel.value = cfg.levelAnnouncementsChannelId;
|
||||
levelAnnouncementsMessage.value = cfg.levelAnnouncementsMessage;
|
||||
roleWithWithoutType.value = cfg.roleWithWithoutType;
|
||||
Array.from(roleWithWithoutXp.options).forEach(opt => {
|
||||
opt.selected = cfg.roleWithWithoutXp?.includes(opt.value);
|
||||
});
|
||||
|
||||
xpCourbeType.value = cfg.xpCourbeType;
|
||||
multiplierCourbeForLevel.value = cfg.multiplierCourbeForLevel;
|
||||
levelAnnouncementEveryLevel.value = cfg.levelAnnouncementEveryLevel;
|
||||
levelMax.value = cfg.levelMax;
|
||||
salonWithWithoutType.value = cfg.salonWithWithoutType;
|
||||
Array.from(salonWithWithoutXp.options).forEach(opt => {
|
||||
opt.selected = cfg.salonWithWithoutXp?.includes(opt.value);
|
||||
});
|
||||
|
||||
roleWithWithoutType.value = cfg.roleWithWithoutType;
|
||||
Array.from(roleWithWithoutXp.options).forEach(opt => {
|
||||
opt.selected = cfg.roleWithWithoutXp?.includes(opt.value);
|
||||
});
|
||||
gainXpOnMessage.checked = cfg.gainXpOnMessage;
|
||||
gainXpMessageLowerBound.value = cfg.gainXpMessageLowerBound;
|
||||
gainXpMessageUpperBound.value = cfg.gainXpMessageUpperBound;
|
||||
cooldownXpMessageSeconds.value = cfg.cooldownXpMessageSeconds;
|
||||
|
||||
salonWithWithoutType.value = cfg.salonWithWithoutType;
|
||||
Array.from(salonWithWithoutXp.options).forEach(opt => {
|
||||
opt.selected = cfg.salonWithWithoutXp?.includes(opt.value);
|
||||
});
|
||||
|
||||
gainXpOnMessage.checked = cfg.gainXpOnMessage;
|
||||
gainXpMessageLowerBound.value = cfg.gainXpMessageLowerBound;
|
||||
gainXpMessageUpperBound.value = cfg.gainXpMessageUpperBound;
|
||||
cooldownXpMessageSeconds.value = cfg.cooldownXpMessageSeconds;
|
||||
|
||||
gainXpOnVoice.checked = cfg.gainXpOnVoice;
|
||||
gainVoiceXpLowerBound.value = cfg.gainVoiceXpLowerBound;
|
||||
gainVoiceXpUpperBound.value = cfg.gainVoiceXpUpperBound;
|
||||
})
|
||||
.catch(console.error);
|
||||
|
||||
|
||||
levelForm.addEventListener("submit", async e => {
|
||||
e.preventDefault();
|
||||
|
||||
const res = await fetch("/api/bot/save-level-config", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
guildId,
|
||||
levelEnabled: levelEnabled.checked,
|
||||
levelAnnouncementsEnabled: levelAnnouncementsEnabled.checked,
|
||||
levelAnnouncementsChannelId: levelAnnouncementsChannel.value,
|
||||
levelAnnouncementsMessage: levelAnnouncementsMessage.value,
|
||||
xpCourbeType: xpCourbeType.value,
|
||||
multiplierCourbeForLevel: parseInt(multiplierCourbeForLevel.value, 10),
|
||||
levelAnnouncementEveryLevel: parseInt(levelAnnouncementEveryLevel.value, 10),
|
||||
levelMax: parseInt(levelMax.value, 10),
|
||||
roleWithWithoutType: roleWithWithoutType.value,
|
||||
roleWithWithoutXp: Array.from(roleWithWithoutXp.selectedOptions).map(opt => opt.value),
|
||||
salonWithWithoutType: salonWithWithoutType.value,
|
||||
salonWithWithoutXp: Array.from(salonWithWithoutXp.selectedOptions).map(opt => opt.value),
|
||||
gainXpOnMessage: gainXpOnMessage.checked,
|
||||
gainXpMessageLowerBound: parseInt(gainXpMessageLowerBound.value, 10),
|
||||
gainXpMessageUpperBound: parseInt(gainXpMessageUpperBound.value, 10),
|
||||
cooldownXpMessageSeconds: parseInt(cooldownXpMessageSeconds.value, 10),
|
||||
gainXpOnVoice: gainXpOnVoice.checked,
|
||||
gainVoiceXpLowerBound: parseInt(gainVoiceXpLowerBound.value, 10),
|
||||
gainVoiceXpUpperBound: parseInt(gainVoiceXpUpperBound.value, 10)
|
||||
gainXpOnVoice.checked = cfg.gainXpOnVoice;
|
||||
gainVoiceXpLowerBound.value = cfg.gainVoiceXpLowerBound;
|
||||
gainVoiceXpUpperBound.value = cfg.gainVoiceXpUpperBound;
|
||||
})
|
||||
});
|
||||
.catch(console.error);
|
||||
}, 500);
|
||||
|
||||
statusLevelForm.textContent = (await res.json()).success
|
||||
? "Config niveaux sauvegardée ✅"
|
||||
: "Erreur ❌";
|
||||
// Sauvegarder
|
||||
saveLevel.addEventListener("click", async () => {
|
||||
saveLevel.disabled = true;
|
||||
saveLevel.textContent = "Sauvegarde...";
|
||||
|
||||
try {
|
||||
const res = await fetch("/api/bot/save-level-config", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
guildId,
|
||||
levelEnabled: levelEnabled.checked,
|
||||
levelAnnouncementsEnabled: levelAnnouncementsEnabled.checked,
|
||||
levelAnnouncementsChannelId: levelAnnouncementsChannel.value,
|
||||
levelAnnouncementsMessage: levelAnnouncementsMessage.value,
|
||||
xpCourbeType: xpCourbeType.value,
|
||||
multiplierCourbeForLevel: parseInt(multiplierCourbeForLevel.value, 10),
|
||||
levelAnnouncementEveryLevel: parseInt(levelAnnouncementEveryLevel.value, 10),
|
||||
levelMax: parseInt(levelMax.value, 10),
|
||||
roleWithWithoutType: roleWithWithoutType.value,
|
||||
roleWithWithoutXp: Array.from(roleWithWithoutXp.selectedOptions).map(opt => opt.value),
|
||||
salonWithWithoutType: salonWithWithoutType.value,
|
||||
salonWithWithoutXp: Array.from(salonWithWithoutXp.selectedOptions).map(opt => opt.value),
|
||||
gainXpOnMessage: gainXpOnMessage.checked,
|
||||
gainXpMessageLowerBound: parseInt(gainXpMessageLowerBound.value, 10),
|
||||
gainXpMessageUpperBound: parseInt(gainXpMessageUpperBound.value, 10),
|
||||
cooldownXpMessageSeconds: parseInt(cooldownXpMessageSeconds.value, 10),
|
||||
gainXpOnVoice: gainXpOnVoice.checked,
|
||||
gainVoiceXpLowerBound: parseInt(gainVoiceXpLowerBound.value, 10),
|
||||
gainVoiceXpUpperBound: parseInt(gainVoiceXpUpperBound.value, 10)
|
||||
})
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
showStatus("status-level-form", "Configuration sauvegardée ✅", "success");
|
||||
} else {
|
||||
showStatus("status-level-form", "Erreur lors de la sauvegarde ❌", "error");
|
||||
}
|
||||
} catch (error) {
|
||||
showStatus("status-level-form", "Erreur de connexion ❌", "error");
|
||||
}
|
||||
|
||||
saveLevel.disabled = false;
|
||||
saveLevel.textContent = "Sauvegarder";
|
||||
});
|
||||
@@ -0,0 +1,100 @@
|
||||
// Navigation sidebar avec hash routing
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const navItems = document.querySelectorAll('.nav-item[data-section]');
|
||||
const sections = document.querySelectorAll('.config-section');
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
const mobileToggle = document.getElementById('mobile-toggle');
|
||||
|
||||
// Fonction pour changer de section
|
||||
function showSection(sectionId) {
|
||||
// Masquer toutes les sections
|
||||
sections.forEach(section => {
|
||||
section.classList.remove('active');
|
||||
});
|
||||
|
||||
// Retirer la classe active de tous les nav items
|
||||
navItems.forEach(item => {
|
||||
item.classList.remove('active');
|
||||
});
|
||||
|
||||
// Afficher la section cible
|
||||
const targetSection = document.getElementById(`section-${sectionId}`);
|
||||
if (targetSection) {
|
||||
targetSection.classList.add('active');
|
||||
}
|
||||
|
||||
// Activer le nav item correspondant
|
||||
const targetNav = document.querySelector(`.nav-item[data-section="${sectionId}"]`);
|
||||
if (targetNav) {
|
||||
targetNav.classList.add('active');
|
||||
}
|
||||
|
||||
// Fermer la sidebar sur mobile
|
||||
if (window.innerWidth <= 900) {
|
||||
sidebar.classList.remove('open');
|
||||
}
|
||||
|
||||
// Mettre à jour l'URL
|
||||
window.location.hash = sectionId;
|
||||
}
|
||||
|
||||
// Ajouter les listeners sur les nav items
|
||||
navItems.forEach(item => {
|
||||
item.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const sectionId = item.dataset.section;
|
||||
showSection(sectionId);
|
||||
});
|
||||
});
|
||||
|
||||
// Gérer le hash au chargement de la page
|
||||
function handleHash() {
|
||||
const hash = window.location.hash.slice(1); // Enlever le #
|
||||
if (hash && document.getElementById(`section-${hash}`)) {
|
||||
showSection(hash);
|
||||
} else {
|
||||
// Par défaut, afficher la première section
|
||||
const firstNavItem = navItems[0];
|
||||
if (firstNavItem) {
|
||||
showSection(firstNavItem.dataset.section);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Écouter les changements de hash
|
||||
window.addEventListener('hashchange', handleHash);
|
||||
|
||||
// Charger la section initiale
|
||||
handleHash();
|
||||
|
||||
// Toggle sidebar sur mobile
|
||||
if (mobileToggle) {
|
||||
mobileToggle.addEventListener('click', () => {
|
||||
sidebar.classList.toggle('open');
|
||||
});
|
||||
}
|
||||
|
||||
// Fermer la sidebar en cliquant à l'extérieur sur mobile
|
||||
document.addEventListener('click', (e) => {
|
||||
if (window.innerWidth <= 900) {
|
||||
if (!sidebar.contains(e.target) && !mobileToggle.contains(e.target)) {
|
||||
sidebar.classList.remove('open');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Helper function pour afficher les messages de statut
|
||||
window.showStatus = function(elementId, message, type = 'success') {
|
||||
const element = document.getElementById(elementId);
|
||||
if (element) {
|
||||
element.textContent = message;
|
||||
element.className = `status-message show ${type}`;
|
||||
|
||||
// Masquer après 5 secondes
|
||||
setTimeout(() => {
|
||||
element.classList.remove('show');
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
@@ -1,32 +1,47 @@
|
||||
const welcomeForm = document.getElementById("welcome-form");
|
||||
const welcomeEnabled = document.getElementById("welcome-enabled");
|
||||
const welcomeChannel = document.getElementById("welcome-channel");
|
||||
const welcomeMessage = document.getElementById("welcome-message");
|
||||
const statusWelcomeForm = document.getElementById("status-welcome-form");
|
||||
const saveWelcome = document.getElementById("save-welcome");
|
||||
|
||||
// Message par défaut
|
||||
const defaultWelcomeMessage = "Bienvenue {mention} sur **{server}** ! 🎉";
|
||||
|
||||
// Charger la config
|
||||
fetch(`/api/bot/get-welcome-config/${guildId}`)
|
||||
.then(res => res.json())
|
||||
.then(cfg => {
|
||||
welcomeEnabled.checked = cfg.enabled;
|
||||
welcomeChannel.value = cfg.channelId;
|
||||
welcomeMessage.value = cfg.message;
|
||||
welcomeMessage.value = cfg.message || defaultWelcomeMessage;
|
||||
});
|
||||
|
||||
welcomeForm.addEventListener("submit", async e => {
|
||||
e.preventDefault();
|
||||
// Sauvegarder
|
||||
saveWelcome.addEventListener("click", async () => {
|
||||
saveWelcome.disabled = true;
|
||||
saveWelcome.textContent = "Sauvegarde...";
|
||||
|
||||
const res = await fetch("/api/bot/save-welcome-config", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
guildId,
|
||||
welcomeEnabled: welcomeEnabled.checked,
|
||||
channelId: welcomeChannel.value,
|
||||
welcomeMessage: welcomeMessage.value
|
||||
})
|
||||
});
|
||||
try {
|
||||
const res = await fetch("/api/bot/save-welcome-config", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
guildId,
|
||||
welcomeEnabled: welcomeEnabled.checked,
|
||||
channelId: welcomeChannel.value,
|
||||
welcomeMessage: welcomeMessage.value
|
||||
})
|
||||
});
|
||||
|
||||
statusWelcomeForm.textContent = (await res.json()).success
|
||||
? "Config bienvenue sauvegardée ✅"
|
||||
: "Erreur ❌";
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
showStatus("status-welcome-form", "Configuration sauvegardée ✅", "success");
|
||||
} else {
|
||||
showStatus("status-welcome-form", "Erreur lors de la sauvegarde ❌", "error");
|
||||
}
|
||||
} catch (error) {
|
||||
showStatus("status-welcome-form", "Erreur de connexion ❌", "error");
|
||||
}
|
||||
|
||||
saveWelcome.disabled = false;
|
||||
saveWelcome.textContent = "Sauvegarder";
|
||||
});
|
||||
|
||||
+362
-99
@@ -1,130 +1,393 @@
|
||||
/* ===== Reset minimal ===== */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
@import url('global.css');
|
||||
|
||||
body {
|
||||
background-color: #1e1f29; /* fond sombre type Discord */
|
||||
color: #ffffff;
|
||||
line-height: 1.6;
|
||||
/* ===== Hero Section ===== */
|
||||
.hero {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center; /* centre horizontalement */
|
||||
justify-content: flex-start; /* commencer depuis le haut */
|
||||
padding: 20px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: var(--spacing-2xl) var(--spacing-md);
|
||||
min-height: 70vh;
|
||||
background: linear-gradient(135deg, var(--bg-dark) 0%, #0a1628 50%, var(--bg-dark) 100%);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hero::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: radial-gradient(circle at 50% 50%, rgba(88, 101, 242, 0.1) 0%, transparent 50%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.hero-logo {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
margin-bottom: var(--spacing-lg);
|
||||
border: 4px solid var(--primary);
|
||||
box-shadow: var(--shadow-lg), 0 0 40px rgba(88, 101, 242, 0.3);
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { box-shadow: var(--shadow-lg), 0 0 40px rgba(88, 101, 242, 0.3); }
|
||||
50% { box-shadow: var(--shadow-lg), 0 0 60px rgba(88, 101, 242, 0.5); }
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: 3.5rem;
|
||||
font-weight: 800;
|
||||
margin-bottom: var(--spacing-md);
|
||||
background: linear-gradient(135deg, var(--text-primary) 0%, var(--primary) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.hero-subtitle {
|
||||
font-size: 1.25rem;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: var(--spacing-xl);
|
||||
max-width: 600px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.hero-buttons {
|
||||
display: flex;
|
||||
gap: var(--spacing-md);
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* ===== Navigation ===== */
|
||||
nav {
|
||||
.navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
background-color: rgba(22, 27, 34, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: var(--spacing-md) 0;
|
||||
}
|
||||
|
||||
.navbar-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #2f3136;
|
||||
padding: 10px 20px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 30px;
|
||||
width: 100%;
|
||||
max-width: 800px; /* limite largeur nav */
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 var(--spacing-md);
|
||||
}
|
||||
|
||||
nav a {
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
margin-right: 15px;
|
||||
font-weight: 500;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
nav a:hover {
|
||||
color: #5865f2;
|
||||
}
|
||||
|
||||
#profil {
|
||||
.navbar-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
gap: var(--spacing-sm);
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#profil img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
.navbar-brand:hover {
|
||||
color: var(--text-primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.navbar-brand img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
#logout {
|
||||
background-color: #5865f2;
|
||||
color: white;
|
||||
padding: 5px 12px;
|
||||
border-radius: 6px;
|
||||
.navbar-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.navbar-link {
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
transition: color var(--transition-fast);
|
||||
}
|
||||
|
||||
.navbar-link:hover {
|
||||
color: var(--text-primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.navbar-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
padding: var(--spacing-xs) var(--spacing-sm);
|
||||
background-color: var(--bg-card);
|
||||
border-radius: var(--border-radius);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.navbar-user img {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.navbar-user span {
|
||||
color: var(--text-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.navbar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
/* ===== Features Section ===== */
|
||||
.features {
|
||||
padding: var(--spacing-2xl) var(--spacing-md);
|
||||
background-color: var(--bg-card);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
text-align: center;
|
||||
margin-bottom: var(--spacing-2xl);
|
||||
}
|
||||
|
||||
.section-header h2 {
|
||||
font-size: 2.25rem;
|
||||
margin-bottom: var(--spacing-sm);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.section-header p {
|
||||
font-size: 1.1rem;
|
||||
color: var(--text-secondary);
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.features-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: var(--spacing-lg);
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
background-color: var(--bg-dark);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--border-radius-lg);
|
||||
padding: var(--spacing-xl);
|
||||
transition: transform var(--transition-normal), border-color var(--transition-normal), box-shadow var(--transition-normal);
|
||||
}
|
||||
|
||||
.feature-card:hover {
|
||||
transform: translateY(-4px);
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 8px 30px rgba(88, 101, 242, 0.15);
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, var(--primary), #7289da);
|
||||
border-radius: var(--border-radius);
|
||||
font-size: 28px;
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
.feature-card h3 {
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: var(--spacing-sm);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.feature-card p {
|
||||
font-size: 0.95rem;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* ===== Stats Section ===== */
|
||||
.stats {
|
||||
padding: var(--spacing-2xl) var(--spacing-md);
|
||||
text-align: center;
|
||||
background-color: var(--bg-dark);
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: var(--spacing-2xl);
|
||||
flex-wrap: wrap;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 3.5rem;
|
||||
font-weight: 800;
|
||||
background: linear-gradient(135deg, var(--primary), #7289da);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.1rem;
|
||||
margin-top: var(--spacing-sm);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* ===== CTA Section ===== */
|
||||
.cta {
|
||||
padding: var(--spacing-2xl) var(--spacing-md);
|
||||
text-align: center;
|
||||
background: linear-gradient(135deg, rgba(88, 101, 242, 0.1) 0%, transparent 100%);
|
||||
}
|
||||
|
||||
.cta h2 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: var(--spacing-md);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.cta p {
|
||||
font-size: 1.1rem;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: var(--spacing-xl);
|
||||
}
|
||||
|
||||
/* ===== Footer ===== */
|
||||
.footer {
|
||||
padding: var(--spacing-xl) var(--spacing-md);
|
||||
border-top: 1px solid var(--border-color);
|
||||
text-align: center;
|
||||
background-color: var(--bg-card);
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.footer-links a {
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
transition: color var(--transition-fast);
|
||||
}
|
||||
|
||||
.footer-links a:hover {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.footer-copyright {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
#logout:hover {
|
||||
background-color: #4752c4;
|
||||
}
|
||||
|
||||
/* ===== Titres ===== */
|
||||
h1, h2 {
|
||||
text-align: center; /* centre le texte */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 20px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.8rem;
|
||||
margin: 25px 0 10px 0;
|
||||
color: #ffffff;
|
||||
border-bottom: 2px solid #5865f2;
|
||||
display: inline-block;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
/* ===== Bouton d'invitation ===== */
|
||||
#invite-link {
|
||||
display: inline-block;
|
||||
background-color: #5865f2;
|
||||
/* ===== Login Button ===== */
|
||||
.btn-login {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
background-color: var(--primary);
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border-radius: 8px;
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
border-radius: var(--border-radius);
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
transition: background 0.2s;
|
||||
margin-bottom: 20px;
|
||||
transition: background-color var(--transition-fast);
|
||||
}
|
||||
|
||||
#invite-link:hover {
|
||||
background-color: #4752c4;
|
||||
}
|
||||
|
||||
/* ===== Listes ===== */
|
||||
ul {
|
||||
list-style: disc inside;
|
||||
margin: 10px 0 20px 0;
|
||||
max-width: 700px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 10px;
|
||||
.btn-login:hover {
|
||||
background-color: var(--primary-hover);
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* ===== Responsive ===== */
|
||||
@media (max-width: 600px) {
|
||||
nav {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
@media (max-width: 768px) {
|
||||
.hero-title {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
#profil {
|
||||
gap: 5px;
|
||||
|
||||
.hero-subtitle {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.navbar-nav {
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
gap: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.section-header h2 {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.hero-buttons {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.hero-buttons .btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.navbar-nav .navbar-link {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Body padding for fixed navbar */
|
||||
body {
|
||||
padding-top: 65px;
|
||||
}
|
||||
|
||||
+174
-50
@@ -1,62 +1,186 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<title>LazyBot - Bot Discord</title>
|
||||
<title>LazyBot - Bot Discord Multifonction</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="LazyBot est un bot Discord puissant avec système de niveaux, économie, messages personnalisés et bien plus encore.">
|
||||
<link rel="stylesheet" href="/index.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<a href="/">Accueil</a>
|
||||
<a href="/dashboard">Tableau de bord</a>
|
||||
<div id="profil">
|
||||
<img id="avatar" src="" alt="Avatar">
|
||||
<span id="username"></span>
|
||||
<a id="logout" href="/auth/logout">Se déconnecter</a>
|
||||
<!-- Navigation -->
|
||||
<nav class="navbar">
|
||||
<div class="navbar-container">
|
||||
<a href="/" class="navbar-brand">
|
||||
<img id="bot-avatar" src="" alt="LazyBot">
|
||||
<span>LazyBot</span>
|
||||
</a>
|
||||
<div class="navbar-nav">
|
||||
<a href="/" class="navbar-link">Accueil</a>
|
||||
<a href="/dashboard" class="navbar-link">Dashboard</a>
|
||||
<div id="auth-section">
|
||||
<a href="/auth/login" class="btn btn-primary btn-sm">Connexion</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<h1>LazyBot</h1>
|
||||
<a id="invite-link" href="#">Ajouter à Discord</a>
|
||||
<br><br>
|
||||
<h2>Ajout du bot</h2>
|
||||
<ul>
|
||||
<li>Ajoutez le bot à votre serveur Discord en cliquant sur le lien ci-dessus.</li>
|
||||
<li>Utilisez le tableau de bord pour configurer les paramètres du bot sur vos serveurs.</li>
|
||||
<li>Profitez des fonctionnalités offertes par LazyBot pour améliorer votre expérience Discord !</li>
|
||||
</ul>
|
||||
|
||||
<h2>Fonctionnalités</h2>
|
||||
<ul>
|
||||
<li>Messages de bienvenue et d'au revoir personnalisables</li>
|
||||
<li>Rôles automatiques pour les nouveaux membres et les utilisateurs en vocal</li>
|
||||
<li>Et bien plus encore...</li>
|
||||
<!-- Hero Section -->
|
||||
<section class="hero">
|
||||
<div class="hero-content">
|
||||
<img id="hero-logo" class="hero-logo" src="" alt="LazyBot Logo">
|
||||
<h1 class="hero-title">LazyBot</h1>
|
||||
<p class="hero-subtitle">
|
||||
Un bot Discord complet avec système de niveaux, économie, messages de bienvenue personnalisés,
|
||||
rôles automatiques et un dashboard intuitif pour gérer votre serveur.
|
||||
</p>
|
||||
<div class="hero-buttons">
|
||||
<a id="invite-link" href="#" class="btn btn-primary btn-lg">
|
||||
➕ Ajouter à Discord
|
||||
</a>
|
||||
<a href="/dashboard" class="btn btn-secondary btn-lg">
|
||||
⚙️ Dashboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Features Section -->
|
||||
<section class="features">
|
||||
<div class="section-header">
|
||||
<h2>Fonctionnalités</h2>
|
||||
<p>Tout ce dont vous avez besoin pour votre serveur Discord</p>
|
||||
</div>
|
||||
<div class="features-grid">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">📈</div>
|
||||
<h3>Système de Niveaux</h3>
|
||||
<p>Récompensez l'activité de vos membres avec un système d'XP complet. Messages, vocal, courbes personnalisables et annonces automatiques.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">💰</div>
|
||||
<h3>Économie</h3>
|
||||
<p>Un système économique complet avec monnaie personnalisable, commandes daily, work, crime, steal, et transactions entre membres.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">👋</div>
|
||||
<h3>Messages Personnalisés</h3>
|
||||
<p>Accueillez les nouveaux membres et dites au revoir avec des messages entièrement personnalisables avec variables.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">🎭</div>
|
||||
<h3>Rôles Automatiques</h3>
|
||||
<p>Attribuez automatiquement des rôles aux nouveaux membres ou aux utilisateurs en vocal. Configurez les salons à exclure.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">⚙️</div>
|
||||
<h3>Dashboard Intuitif</h3>
|
||||
<p>Configurez tout depuis un dashboard web moderne et facile à utiliser. Pas besoin de commandes compliquées.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">🛡️</div>
|
||||
<h3>Commandes Admin</h3>
|
||||
<p>Gérez les niveaux et l'économie de vos membres avec des commandes d'administration dédiées.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Stats Section -->
|
||||
<section class="stats">
|
||||
<div class="section-header">
|
||||
<h2>Statistiques</h2>
|
||||
</div>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-item">
|
||||
<span id="stat-servers" class="stat-number">-</span>
|
||||
<span class="stat-label">Serveurs</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span id="stat-users" class="stat-number">-</span>
|
||||
<span class="stat-label">Utilisateurs</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span id="stat-commands" class="stat-number">20+</span>
|
||||
<span class="stat-label">Commandes</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CTA Section -->
|
||||
<section class="cta">
|
||||
<h2>Prêt à améliorer votre serveur ?</h2>
|
||||
<p>Ajoutez LazyBot dès maintenant et découvrez toutes ses fonctionnalités.</p>
|
||||
<a id="invite-link-2" href="#" class="btn btn-primary btn-lg">
|
||||
Ajouter à Discord
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="footer">
|
||||
<div class="footer-content">
|
||||
<div class="footer-links">
|
||||
<a href="/dashboard">Dashboard</a>
|
||||
<a href="https://discord.com" target="_blank">Support</a>
|
||||
</div>
|
||||
<p class="footer-copyright">
|
||||
© 2024 LazyBot. Fait avec ❤️ pour Discord.
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// Charger le lien d'invitation
|
||||
fetch("/invite-bot")
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
document.getElementById("invite-link").href = data.url;
|
||||
document.getElementById("invite-link-2").href = data.url;
|
||||
})
|
||||
.catch(() => console.log("Impossible de récupérer le lien du bot."));
|
||||
|
||||
// Charger les infos du bot (avatar)
|
||||
fetch("/api/bot-info")
|
||||
.then(res => res.json())
|
||||
.then(bot => {
|
||||
const avatarUrl = `https://cdn.discordapp.com/avatars/${bot.id}/${bot.avatar}.png`;
|
||||
document.getElementById("bot-avatar").src = avatarUrl;
|
||||
document.getElementById("hero-logo").src = avatarUrl;
|
||||
|
||||
// Stats
|
||||
if (bot.guildCount) {
|
||||
document.getElementById("stat-servers").textContent = bot.guildCount;
|
||||
}
|
||||
if (bot.userCount) {
|
||||
document.getElementById("stat-users").textContent = bot.userCount;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// Utiliser un placeholder si pas d'info bot
|
||||
document.getElementById("bot-avatar").src = "https://cdn.discordapp.com/embed/avatars/0.png";
|
||||
document.getElementById("hero-logo").src = "https://cdn.discordapp.com/embed/avatars/0.png";
|
||||
});
|
||||
|
||||
// Vérifier si l'utilisateur est connecté
|
||||
fetch("/api/user")
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error("Non connecté");
|
||||
return res.json();
|
||||
})
|
||||
.then(user => {
|
||||
const authSection = document.getElementById("auth-section");
|
||||
authSection.innerHTML = `
|
||||
<div class="navbar-actions">
|
||||
<div class="navbar-user">
|
||||
<img src="https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png" alt="Avatar">
|
||||
<span>${user.username}</span>
|
||||
</div>
|
||||
<a href="/auth/logout" class="btn btn-secondary btn-sm">Déconnexion</a>
|
||||
</div>
|
||||
`;
|
||||
})
|
||||
.catch(() => {
|
||||
// Garder le bouton de connexion par défaut
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
<script>
|
||||
fetch("/invite-bot")
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
const link = document.getElementById("invite-link");
|
||||
link.href = data.url; // met le lien dynamique
|
||||
})
|
||||
.catch(() => {
|
||||
console.log("Impossible de récupérer le lien du bot.");
|
||||
});
|
||||
|
||||
|
||||
fetch("/api/user")
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error("Utilisateur non connecté"); // gère les erreurs HTTP
|
||||
return res.json();
|
||||
})
|
||||
.then(user => {
|
||||
document.getElementById("username").textContent = `${user.username}`;
|
||||
document.getElementById("avatar").src = `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png`;
|
||||
})
|
||||
.catch(() => {
|
||||
const profilDiv = document.getElementById("profil");
|
||||
profilDiv.style.display = "none";
|
||||
});
|
||||
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -4,6 +4,26 @@ const loadSlashCommands = require("../slash_commands");
|
||||
|
||||
module.exports = (app, db, client) => {
|
||||
|
||||
// --- Bot info ---
|
||||
router.get("/bot-info", (req, res) => {
|
||||
const bot = client.user;
|
||||
if (!bot) return res.status(500).json({ error: "Bot non connecté" });
|
||||
|
||||
let userCount = 0;
|
||||
client.guilds.cache.forEach(g => {
|
||||
userCount += g.memberCount;
|
||||
});
|
||||
|
||||
res.json({
|
||||
id: bot.id,
|
||||
username: bot.username,
|
||||
discriminator: bot.discriminator,
|
||||
avatar: bot.avatar,
|
||||
guildCount: client.guilds.cache.size,
|
||||
userCount: userCount
|
||||
});
|
||||
});
|
||||
|
||||
// --- User info ---
|
||||
router.get("/user", (req, res) => {
|
||||
if (req.session.user) res.json(req.session.user);
|
||||
|
||||
@@ -8,6 +8,11 @@ const CLIENT_SECRET = process.env.CLIENT_SECRET;
|
||||
const REDIRECT_URI = process.env.REDIRECT_URI;
|
||||
|
||||
module.exports = (app, db, client) => {
|
||||
// --- Redirection login ---
|
||||
router.get("/login", (req, res) => {
|
||||
res.redirect("/auth/discord");
|
||||
});
|
||||
|
||||
// --- Connexion Discord ---
|
||||
router.get("/discord", (req, res) => {
|
||||
const url = `https://discord.com/api/oauth2/authorize?client_id=${CLIENT_ID}&redirect_uri=${encodeURIComponent(REDIRECT_URI)}&response_type=code&scope=identify%20guilds`;
|
||||
|
||||
Reference in New Issue
Block a user