mirror of
https://github.com/arthur-pbty/LazyBot.git
synced 2026-06-03 23:36:37 +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");
|
const db = require("../db");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@@ -10,7 +10,7 @@ module.exports = {
|
|||||||
(err, row) => {
|
(err, row) => {
|
||||||
if (err || !row || !row.enabled) return;
|
if (err || !row || !row.enabled) return;
|
||||||
|
|
||||||
let msg = row.message;
|
let msg = row.message || "Bienvenue {mention} sur {server} !";
|
||||||
|
|
||||||
msg = msg
|
msg = msg
|
||||||
.replace("{user}", member.user.username)
|
.replace("{user}", member.user.username)
|
||||||
@@ -19,7 +19,15 @@ module.exports = {
|
|||||||
|
|
||||||
const channel = member.guild.channels.cache.get(row.channel_id);
|
const channel = member.guild.channels.cache.get(row.channel_id);
|
||||||
if (channel) {
|
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");
|
const db = require("../db");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@@ -10,7 +10,7 @@ module.exports = {
|
|||||||
(err, row) => {
|
(err, row) => {
|
||||||
if (err || !row || !row.enabled) return;
|
if (err || !row || !row.enabled) return;
|
||||||
|
|
||||||
let msg = row.message;
|
let msg = row.message || "Au revoir {user}, tu vas nous manquer !";
|
||||||
|
|
||||||
msg = msg
|
msg = msg
|
||||||
.replace("{user}", member.user.username)
|
.replace("{user}", member.user.username)
|
||||||
@@ -18,7 +18,15 @@ module.exports = {
|
|||||||
|
|
||||||
const channel = member.guild.channels.cache.get(row.channel_id);
|
const channel = member.guild.channels.cache.get(row.channel_id);
|
||||||
if (channel) {
|
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");
|
const db = require("../db");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@@ -31,25 +31,31 @@ module.exports = {
|
|||||||
[guildId],
|
[guildId],
|
||||||
(err, row) => {
|
(err, row) => {
|
||||||
if (err || !row || !row.enabled || !row.gain_xp_on_message) return;
|
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 userRoles = message.member.roles.cache;
|
||||||
const requiredRoles = JSON.parse(row.role_with_without_xp || "[]");
|
const requiredRoles = JSON.parse(row.role_with_without_xp || "[]");
|
||||||
if (!requiredRoles.some(roleId => userRoles.has(roleId))) {
|
if (!requiredRoles.some(roleId => userRoles.has(roleId))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (row.role_with_without_type === "without") {
|
} else if (roleWithWithoutType === "without") {
|
||||||
const userRoles = message.member.roles.cache;
|
const userRoles = message.member.roles.cache;
|
||||||
const excludedRoles = JSON.parse(row.role_with_without_xp || "[]");
|
const excludedRoles = JSON.parse(row.role_with_without_xp || "[]");
|
||||||
if (excludedRoles.some(roleId => userRoles.has(roleId))) {
|
if (excludedRoles.some(roleId => userRoles.has(roleId))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (row.salon_with_without_type === "with") {
|
}
|
||||||
|
|
||||||
|
if (salonWithWithoutType === "with") {
|
||||||
const channelId = message.channel.id;
|
const channelId = message.channel.id;
|
||||||
const requiredChannels = JSON.parse(row.salon_with_without_xp || "[]");
|
const requiredChannels = JSON.parse(row.salon_with_without_xp || "[]");
|
||||||
if (!requiredChannels.includes(channelId)) {
|
if (!requiredChannels.includes(channelId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (row.salon_with_without_type === "without") {
|
} else if (salonWithWithoutType === "without") {
|
||||||
const channelId = message.channel.id;
|
const channelId = message.channel.id;
|
||||||
const excludedChannels = JSON.parse(row.salon_with_without_xp || "[]");
|
const excludedChannels = JSON.parse(row.salon_with_without_xp || "[]");
|
||||||
if (excludedChannels.includes(channelId)) {
|
if (excludedChannels.includes(channelId)) {
|
||||||
@@ -64,13 +70,14 @@ module.exports = {
|
|||||||
(err, userRow) => {
|
(err, userRow) => {
|
||||||
if (err) return;
|
if (err) return;
|
||||||
|
|
||||||
|
const cooldownSeconds = row.cooldown_xp_message_seconds ?? 60;
|
||||||
const lastTimestamp = userRow ? userRow.last_xp_message_timestamp || 0 : 0;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const minXp = row.gain_xp_message_lower_bound;
|
const minXp = row.gain_xp_message_lower_bound ?? 15;
|
||||||
const maxXp = row.gain_xp_message_upper_bound;
|
const maxXp = row.gain_xp_message_upper_bound ?? 25;
|
||||||
const xpToAdd = Math.floor(Math.random() * (maxXp - minXp + 1)) + minXp;
|
const xpToAdd = Math.floor(Math.random() * (maxXp - minXp + 1)) + minXp;
|
||||||
|
|
||||||
let newXp;
|
let newXp;
|
||||||
@@ -84,17 +91,21 @@ module.exports = {
|
|||||||
newLevel = 1;
|
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;
|
let fonction_courbe;
|
||||||
|
|
||||||
if (row.xp_courbe_type === "constante") {
|
if (courbeType === "constante") {
|
||||||
fonction_courbe = (level) => multiplier;
|
fonction_courbe = (level) => multiplier;
|
||||||
} else if (row.xp_courbe_type === "linear") {
|
} else if (courbeType === "linear") {
|
||||||
fonction_courbe = (level) => (level) * multiplier;
|
fonction_courbe = (level) => (level) * multiplier;
|
||||||
} else if (row.xp_courbe_type === "quadratic") {
|
} else if (courbeType === "quadratic") {
|
||||||
fonction_courbe = (level) => (level) * (level) * multiplier;
|
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;
|
fonction_courbe = (level) => Math.pow(2, (level - 1)) * multiplier;
|
||||||
|
} else {
|
||||||
|
// Fallback au cas où
|
||||||
|
fonction_courbe = (level) => (level) * multiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
let xpForNextLevel = fonction_courbe(newLevel);
|
let xpForNextLevel = fonction_courbe(newLevel);
|
||||||
@@ -106,13 +117,27 @@ module.exports = {
|
|||||||
if (row.level_announcements_enabled && (newLevel % row.level_annoncement_every_level === 0)) {
|
if (row.level_announcements_enabled && (newLevel % row.level_annoncement_every_level === 0)) {
|
||||||
const channel = message.guild.channels.cache.get(row.level_announcements_channel_id);
|
const channel = message.guild.channels.cache.get(row.level_announcements_channel_id);
|
||||||
if (channel) {
|
if (channel) {
|
||||||
let announcementMsg = row.level_announcements_message;
|
let announcementMsg = row.level_announcements_message || "🎉 {mention} a atteint le niveau {level} !";
|
||||||
announcementMsg = announcementMsg
|
announcementMsg = announcementMsg
|
||||||
.replace("{user}", message.author.username)
|
.replace("{user}", message.author.username)
|
||||||
.replace("{mention}", `<@${message.author.id}>`)
|
.replace("{mention}", `<@${message.author.id}>`)
|
||||||
.replace("{level}", newLevel)
|
.replace("{level}", newLevel)
|
||||||
.replace("{level-xp}", xpForNextLevel);
|
.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] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+259
-116
@@ -1,137 +1,280 @@
|
|||||||
/* ===== Reset minimal ===== */
|
@import url('global.css');
|
||||||
* {
|
|
||||||
margin: 0;
|
/* ===== Navigation ===== */
|
||||||
padding: 0;
|
.navbar {
|
||||||
box-sizing: border-box;
|
position: fixed;
|
||||||
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
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 {
|
body {
|
||||||
background-color: #1e1f29; /* fond sombre type Discord */
|
padding-top: 80px;
|
||||||
color: #ffffff;
|
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;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 20px;
|
justify-content: center;
|
||||||
|
padding: var(--spacing-2xl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== Navigation ===== */
|
.spinner {
|
||||||
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 {
|
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
|
border: 3px solid var(--border-color);
|
||||||
|
border-top-color: var(--primary);
|
||||||
border-radius: 50%;
|
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 ===== */
|
/* ===== Responsive ===== */
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 768px) {
|
||||||
body {
|
.dashboard-header {
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav {
|
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
gap: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ul#guilds-list li {
|
.user-avatar {
|
||||||
flex-direction: column;
|
width: 48px;
|
||||||
align-items: center;
|
height: 48px;
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ul#guilds-list li img {
|
.user-details h1 {
|
||||||
margin-bottom: 5px;
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guilds-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.navbar-nav .navbar-link {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+121
-74
@@ -1,108 +1,155 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html lang="fr">
|
||||||
<head>
|
<head>
|
||||||
<title>Tableau de bord</title>
|
<title>Dashboard - LazyBot</title>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="stylesheet" href="/dashboard.css">
|
<link rel="stylesheet" href="/dashboard.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav>
|
<!-- Navigation -->
|
||||||
<a href="/">Accueil</a>
|
<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>
|
</nav>
|
||||||
|
|
||||||
|
<!-- 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>
|
<h1 id="greeting">Chargement...</h1>
|
||||||
<img id="avatar" src="" alt="Avatar">
|
<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>
|
<!-- Guilds Section -->
|
||||||
|
<h2 class="section-title">🏠 Mes serveurs</h2>
|
||||||
|
|
||||||
<h2>Mes serveurs qui ont le bot :</h2>
|
<div id="guilds-container">
|
||||||
<ul id="guilds-list"></ul> <!-- Ici on va lister les guilds -->
|
<div class="loading">
|
||||||
|
<div class="spinner"></div>
|
||||||
|
<p>Chargement des serveurs...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// --- Affichage des infos utilisateur ---
|
// Charger les infos du bot
|
||||||
fetch("/api/user")
|
fetch("/api/bot-info")
|
||||||
.then(res => res.json())
|
.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 => {
|
.then(user => {
|
||||||
document.getElementById("greeting").textContent = `Salut ${user.username}#${user.discriminator} !`;
|
const avatarUrl = `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png`;
|
||||||
document.getElementById("avatar").src = `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(() => {
|
.catch(() => {
|
||||||
document.getElementById("greeting").textContent = "Utilisateur non connecté.";
|
window.location.href = "/auth/login";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Charger les guilds
|
||||||
// --- Affichage des guilds de l'utilisateur ---
|
|
||||||
fetch("/api/guilds")
|
fetch("/api/guilds")
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(guilds => {
|
.then(guilds => {
|
||||||
const list = document.getElementById("guilds-list");
|
const container = document.getElementById("guilds-container");
|
||||||
|
|
||||||
if (guilds.length === 0) {
|
if (guilds.length === 0) {
|
||||||
list.innerHTML = "<li>Aucun serveur disponible</li>";
|
container.innerHTML = `
|
||||||
} else {
|
<div class="empty-state">
|
||||||
guilds.forEach(g => {
|
<div class="empty-state-icon">🤖</div>
|
||||||
const li = document.createElement("li");
|
<h3>Aucun serveur trouvé</h3>
|
||||||
li.style.cursor = "pointer";
|
<p>Ajoutez LazyBot à un serveur pour commencer à le configurer.</p>
|
||||||
li.style.padding = "8px";
|
<a id="invite-empty" href="#" class="btn btn-primary">Ajouter à un serveur</a>
|
||||||
li.style.border = "1px solid #ccc";
|
</div>
|
||||||
li.style.margin = "5px 0";
|
`;
|
||||||
li.style.display = "flex";
|
// Récupérer le lien pour le bouton empty state
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
document.getElementById("guilds-list").innerHTML = "<li>Impossible de récupérer les guilds.</li>";
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
fetch("/invite-bot")
|
fetch("/invite-bot")
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
const link = document.getElementById("invite-link");
|
const btn = document.getElementById("invite-empty");
|
||||||
link.href = data.url; // met le lien dynamique
|
if (btn) btn.href = data.url;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
container.innerHTML = '<div class="guilds-grid"></div>';
|
||||||
|
const grid = container.querySelector('.guilds-grid');
|
||||||
|
|
||||||
|
guilds.forEach(g => {
|
||||||
|
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(() => {
|
.catch(() => {
|
||||||
console.log("Impossible de récupérer le lien du bot.");
|
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 => {
|
||||||
|
document.getElementById("invite-link").href = data.url;
|
||||||
|
})
|
||||||
|
.catch(() => console.log("Impossible de récupérer le lien du bot."));
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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); }
|
||||||
+533
-103
@@ -1,143 +1,573 @@
|
|||||||
/* ===== Reset minimal ===== */
|
@import url('global.css');
|
||||||
* {
|
|
||||||
margin: 0;
|
/* ===== Layout ===== */
|
||||||
padding: 0;
|
body {
|
||||||
box-sizing: border-box;
|
display: flex;
|
||||||
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
min-height: 100vh;
|
||||||
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
/* ===== Sidebar ===== */
|
||||||
background-color: #1e1f29; /* fond sombre type Discord */
|
.sidebar {
|
||||||
color: #ffffff;
|
width: 260px;
|
||||||
|
background-color: var(--bg-card);
|
||||||
|
border-right: 1px solid var(--border-color);
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
z-index: 100;
|
||||||
padding: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== Navigation ===== */
|
.sidebar-header {
|
||||||
nav {
|
padding: var(--spacing-lg);
|
||||||
width: 100%;
|
border-bottom: 1px solid var(--border-color);
|
||||||
max-width: 900px;
|
}
|
||||||
|
|
||||||
|
.sidebar-brand {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-start;
|
align-items: center;
|
||||||
background-color: #2f3136;
|
gap: var(--spacing-sm);
|
||||||
padding: 10px 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav a {
|
|
||||||
color: #ffffff;
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-weight: 500;
|
color: var(--text-primary);
|
||||||
margin-right: 15px;
|
font-weight: 700;
|
||||||
transition: color 0.2s;
|
font-size: 1.1rem;
|
||||||
|
margin-bottom: var(--spacing-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
nav a:hover {
|
.sidebar-brand img {
|
||||||
color: #5865f2;
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== Titres ===== */
|
.guild-info {
|
||||||
h1 {
|
display: flex;
|
||||||
text-align: center;
|
align-items: center;
|
||||||
font-size: 2.5rem;
|
gap: var(--spacing-sm);
|
||||||
margin-bottom: 20px;
|
padding: var(--spacing-sm);
|
||||||
|
background-color: var(--bg-dark);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
.guild-info img {
|
||||||
font-size: 1.8rem;
|
width: 40px;
|
||||||
margin: 25px 0 10px 0;
|
height: 40px;
|
||||||
border-bottom: 2px solid #5865f2;
|
border-radius: 50%;
|
||||||
display: inline-block;
|
|
||||||
padding-bottom: 5px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== Formulaires ===== */
|
.guild-info-text {
|
||||||
form {
|
flex: 1;
|
||||||
background-color: #2f3136;
|
min-width: 0;
|
||||||
padding: 20px;
|
|
||||||
border-radius: 12px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 700px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
form label {
|
.guild-info-name {
|
||||||
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;
|
|
||||||
font-weight: 600;
|
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;
|
cursor: pointer;
|
||||||
transition: background 0.2s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
form button:hover {
|
.nav-item:hover {
|
||||||
background-color: #4752c4;
|
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;
|
display: block;
|
||||||
margin-top: 10px;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
color: #b9bbbe;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
form small ul {
|
/* ===== Config Card ===== */
|
||||||
list-style: disc inside;
|
.config-card {
|
||||||
margin-top: 5px;
|
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,
|
.config-card-header {
|
||||||
#status-goodbye-form,
|
padding: var(--spacing-lg);
|
||||||
#status-autorole-form,
|
border-bottom: 1px solid var(--border-color);
|
||||||
#status-autorole-vocal-form {
|
display: flex;
|
||||||
margin-top: 10px;
|
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;
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== Select multiple pour vocal ===== */
|
.config-card-title .icon {
|
||||||
select[multiple] {
|
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;
|
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 ===== */
|
/* ===== Responsive ===== */
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 900px) {
|
||||||
body {
|
.sidebar {
|
||||||
padding: 10px;
|
transform: translateX(-100%);
|
||||||
|
transition: transform var(--transition-normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
form {
|
.sidebar.open {
|
||||||
padding: 15px;
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+554
-397
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 autoroleEnabled = document.getElementById("autorole-enabled");
|
||||||
const autoroleRole = document.getElementById("autorole-role");
|
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}`)
|
fetch(`/api/bot/get-autorole-newuser-config/${guildId}`)
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(cfg => {
|
.then(cfg => {
|
||||||
@@ -10,9 +10,12 @@ fetch(`/api/bot/get-autorole-newuser-config/${guildId}`)
|
|||||||
autoroleRole.value = cfg.roleId;
|
autoroleRole.value = cfg.roleId;
|
||||||
});
|
});
|
||||||
|
|
||||||
autoroleNewUserForm.addEventListener("submit", async e => {
|
// Sauvegarder
|
||||||
e.preventDefault();
|
saveAutorole.addEventListener("click", async () => {
|
||||||
|
saveAutorole.disabled = true;
|
||||||
|
saveAutorole.textContent = "Sauvegarde...";
|
||||||
|
|
||||||
|
try {
|
||||||
const res = await fetch("/api/bot/save-autorole-newuser-config", {
|
const res = await fetch("/api/bot/save-autorole-newuser-config", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
@@ -23,7 +26,16 @@ autoroleNewUserForm.addEventListener("submit", async e => {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
statusAutoroleForm.textContent = (await res.json()).success
|
const data = await res.json();
|
||||||
? "Auto-rôle sauvegardé ✅"
|
if (data.success) {
|
||||||
: "Erreur ❌";
|
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,17 +1,11 @@
|
|||||||
const autoroleVocalForm = document.getElementById("autorole-vocal-form");
|
|
||||||
const autoroleVocalEnabled = document.getElementById("autorole-vocal-enabled");
|
const autoroleVocalEnabled = document.getElementById("autorole-vocal-enabled");
|
||||||
const autoroleVocalRole = document.getElementById("autorole-vocal-role");
|
const autoroleVocalRole = document.getElementById("autorole-vocal-role");
|
||||||
const excludeSelect = document.getElementById("autorole-vocal-exclude-channel");
|
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}`)
|
// Charger la config après que les channels soient chargés
|
||||||
.then(res => res.json())
|
setTimeout(() => {
|
||||||
.then(channels => {
|
fetch(`/api/bot/get-autorole-vocal-config/${guildId}`)
|
||||||
channels.forEach(c => {
|
|
||||||
excludeSelect.appendChild(new Option(`#${c.name}`, c.id));
|
|
||||||
});
|
|
||||||
return fetch(`/api/bot/get-autorole-vocal-config/${guildId}`);
|
|
||||||
})
|
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(cfg => {
|
.then(cfg => {
|
||||||
autoroleVocalEnabled.checked = cfg.enabled;
|
autoroleVocalEnabled.checked = cfg.enabled;
|
||||||
@@ -20,10 +14,14 @@ fetch(`/api/bot/get-voice-channels/${guildId}`)
|
|||||||
opt.selected = cfg.excludeChannelIds?.includes(opt.value);
|
opt.selected = cfg.excludeChannelIds?.includes(opt.value);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}, 500);
|
||||||
|
|
||||||
autoroleVocalForm.addEventListener("submit", async e => {
|
// Sauvegarder
|
||||||
e.preventDefault();
|
saveAutoroleVocal.addEventListener("click", async () => {
|
||||||
|
saveAutoroleVocal.disabled = true;
|
||||||
|
saveAutoroleVocal.textContent = "Sauvegarde...";
|
||||||
|
|
||||||
|
try {
|
||||||
const exclude = Array.from(excludeSelect.selectedOptions).map(o => o.value);
|
const exclude = Array.from(excludeSelect.selectedOptions).map(o => o.value);
|
||||||
|
|
||||||
const res = await fetch("/api/bot/save-autorole-vocal-config", {
|
const res = await fetch("/api/bot/save-autorole-vocal-config", {
|
||||||
@@ -37,7 +35,16 @@ autoroleVocalForm.addEventListener("submit", async e => {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
statusAutoroleVocalForm.textContent = (await res.json()).success
|
const data = await res.json();
|
||||||
? "Auto-rôle vocal sauvegardé ✅"
|
if (data.success) {
|
||||||
: "Erreur ❌";
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
saveAutoroleVocal.disabled = false;
|
||||||
|
saveAutoroleVocal.textContent = "Sauvegarder";
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
const economyForm = document.getElementById("economy-form");
|
|
||||||
const economyEnabled = document.getElementById("economy-enabled");
|
const economyEnabled = document.getElementById("economy-enabled");
|
||||||
const currencyName = document.getElementById("economy-currency-name");
|
const currencyName = document.getElementById("economy-currency-name");
|
||||||
const currencySymbol = document.getElementById("economy-currency-symbol");
|
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 voiceMoneyMax = document.getElementById("economy-voice-money-max");
|
||||||
const voiceMoneyInterval = document.getElementById("economy-voice-money-interval");
|
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
|
// Charger la config existante
|
||||||
fetch(`/api/bot/get-economy-config/${guildId}`)
|
fetch(`/api/bot/get-economy-config/${guildId}`)
|
||||||
@@ -94,9 +93,11 @@ fetch(`/api/bot/get-economy-config/${guildId}`)
|
|||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
|
|
||||||
// Sauvegarder la config
|
// Sauvegarder la config
|
||||||
economyForm.addEventListener("submit", async e => {
|
saveEconomy.addEventListener("click", async () => {
|
||||||
e.preventDefault();
|
saveEconomy.disabled = true;
|
||||||
|
saveEconomy.textContent = "Sauvegarde...";
|
||||||
|
|
||||||
|
try {
|
||||||
const res = await fetch("/api/bot/save-economy-config", {
|
const res = await fetch("/api/bot/save-economy-config", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
@@ -147,7 +148,16 @@ economyForm.addEventListener("submit", async e => {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
statusEconomyForm.textContent = (await res.json()).success
|
const data = await res.json();
|
||||||
? "Config économie sauvegardée ✅"
|
if (data.success) {
|
||||||
: "Erreur ❌";
|
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,20 +1,26 @@
|
|||||||
const goodbyeForm = document.getElementById("goodbye-form");
|
|
||||||
const goodbyeEnabled = document.getElementById("goodbye-enabled");
|
const goodbyeEnabled = document.getElementById("goodbye-enabled");
|
||||||
const goodbyeChannel = document.getElementById("goodbye-channel");
|
const goodbyeChannel = document.getElementById("goodbye-channel");
|
||||||
const goodbyeMessage = document.getElementById("goodbye-message");
|
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}`)
|
fetch(`/api/bot/get-goodbye-config/${guildId}`)
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(cfg => {
|
.then(cfg => {
|
||||||
goodbyeEnabled.checked = cfg.enabled;
|
goodbyeEnabled.checked = cfg.enabled;
|
||||||
goodbyeChannel.value = cfg.channelId;
|
goodbyeChannel.value = cfg.channelId;
|
||||||
goodbyeMessage.value = cfg.message;
|
goodbyeMessage.value = cfg.message || defaultGoodbyeMessage;
|
||||||
});
|
});
|
||||||
|
|
||||||
goodbyeForm.addEventListener("submit", async e => {
|
// Sauvegarder
|
||||||
e.preventDefault();
|
saveGoodbye.addEventListener("click", async () => {
|
||||||
|
saveGoodbye.disabled = true;
|
||||||
|
saveGoodbye.textContent = "Sauvegarde...";
|
||||||
|
|
||||||
|
try {
|
||||||
const res = await fetch("/api/bot/save-goodbye-config", {
|
const res = await fetch("/api/bot/save-goodbye-config", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
@@ -26,7 +32,16 @@ goodbyeForm.addEventListener("submit", async e => {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
statusGoodbyeForm.textContent = (await res.json()).success
|
const data = await res.json();
|
||||||
? "Config au revoir sauvegardée ✅"
|
if (data.success) {
|
||||||
: "Erreur ❌";
|
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];
|
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")
|
fetch("/api/guilds")
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(guilds => {
|
.then(guilds => {
|
||||||
const guild = guilds.find(g => g.id === guildId);
|
const guild = guilds.find(g => g.id === guildId);
|
||||||
document.getElementById("guild-name").textContent =
|
const guildName = document.getElementById("guild-name");
|
||||||
guild ? `Dashboard : ${guild.name}` : "Serveur introuvable";
|
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
|
// Channels texte
|
||||||
@@ -16,12 +59,26 @@ fetch(`/api/bot/get-text-channels/${guildId}`)
|
|||||||
const welcome = document.getElementById("welcome-channel");
|
const welcome = document.getElementById("welcome-channel");
|
||||||
const goodbye = document.getElementById("goodbye-channel");
|
const goodbye = document.getElementById("goodbye-channel");
|
||||||
const levelAnnouncements = document.getElementById("level-announcements-channel");
|
const levelAnnouncements = document.getElementById("level-announcements-channel");
|
||||||
|
const levelChannelRestrict = document.getElementById("level-channel-with-or-without-xp");
|
||||||
|
|
||||||
channels.forEach(c => {
|
channels.forEach(c => {
|
||||||
const opt = new Option(`#${c.name}`, c.id);
|
const opt = new Option(`#${c.name}`, c.id);
|
||||||
welcome?.appendChild(opt);
|
welcome?.appendChild(opt);
|
||||||
goodbye?.appendChild(opt.cloneNode(true));
|
goodbye?.appendChild(opt.cloneNode(true));
|
||||||
levelAnnouncements?.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 => {
|
.then(roles => {
|
||||||
const newUser = document.getElementById("autorole-role");
|
const newUser = document.getElementById("autorole-role");
|
||||||
const vocal = document.getElementById("autorole-vocal-role");
|
const vocal = document.getElementById("autorole-vocal-role");
|
||||||
|
const levelRoleRestrict = document.getElementById("level-role-with-or-without-xp");
|
||||||
|
|
||||||
roles.forEach(r => {
|
roles.forEach(r => {
|
||||||
const opt = new Option(r.name, r.id);
|
const opt = new Option(r.name, r.id);
|
||||||
newUser?.appendChild(opt);
|
newUser?.appendChild(opt);
|
||||||
vocal?.appendChild(opt.cloneNode(true));
|
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 levelEnabled = document.getElementById("level-enabled");
|
||||||
const levelAnnouncementsEnabled = document.getElementById("level-announcement-enabled");
|
const levelAnnouncementsEnabled = document.getElementById("level-announcement-enabled");
|
||||||
const levelAnnouncementsChannel = document.getElementById("level-announcements-channel");
|
const levelAnnouncementsChannel = document.getElementById("level-announcements-channel");
|
||||||
@@ -18,43 +17,20 @@ const cooldownXpMessageSeconds = document.getElementById("level-xp-cooldown");
|
|||||||
const gainXpOnVoice = document.getElementById("voice-xp-enabled");
|
const gainXpOnVoice = document.getElementById("voice-xp-enabled");
|
||||||
const gainVoiceXpLowerBound = document.getElementById("level-xp-per-voice-min");
|
const gainVoiceXpLowerBound = document.getElementById("level-xp-per-voice-min");
|
||||||
const gainVoiceXpUpperBound = document.getElementById("level-xp-per-voice-max");
|
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
|
// Message par défaut
|
||||||
fetch(`/api/bot/get-roles/${guildId}`)
|
const defaultLevelMessage = "Félicitations {mention}, tu es maintenant niveau **{level}** ! 🎉";
|
||||||
.then(res => res.json())
|
|
||||||
.then(roles => {
|
|
||||||
roles.forEach(r => {
|
|
||||||
roleWithWithoutXp?.appendChild(new Option(r.name, r.id));
|
|
||||||
});
|
|
||||||
|
|
||||||
// 2️⃣ SALONS TEXTE
|
// Charger la config après que les channels/roles soient chargés
|
||||||
return fetch(`/api/bot/get-text-channels/${guildId}`);
|
setTimeout(() => {
|
||||||
})
|
fetch(`/api/bot/get-level-config/${guildId}`)
|
||||||
.then(res => res.json())
|
|
||||||
.then(textSalons => {
|
|
||||||
textSalons.forEach(c => {
|
|
||||||
salonWithWithoutXp?.appendChild(new Option(`#${c.name}`, c.id));
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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));
|
|
||||||
});
|
|
||||||
|
|
||||||
// 4️⃣ CONFIG (APRÈS QUE TOUT EST CHARGÉ)
|
|
||||||
return fetch(`/api/bot/get-level-config/${guildId}`);
|
|
||||||
})
|
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(cfg => {
|
.then(cfg => {
|
||||||
levelEnabled.checked = cfg.enabled;
|
levelEnabled.checked = cfg.enabled;
|
||||||
levelAnnouncementsEnabled.checked = cfg.levelAnnouncementsEnabled;
|
levelAnnouncementsEnabled.checked = cfg.levelAnnouncementsEnabled;
|
||||||
levelAnnouncementsChannel.value = cfg.levelAnnouncementsChannelId;
|
levelAnnouncementsChannel.value = cfg.levelAnnouncementsChannelId;
|
||||||
levelAnnouncementsMessage.value = cfg.levelAnnouncementsMessage;
|
levelAnnouncementsMessage.value = cfg.levelAnnouncementsMessage || defaultLevelMessage;
|
||||||
|
|
||||||
xpCourbeType.value = cfg.xpCourbeType;
|
xpCourbeType.value = cfg.xpCourbeType;
|
||||||
multiplierCourbeForLevel.value = cfg.multiplierCourbeForLevel;
|
multiplierCourbeForLevel.value = cfg.multiplierCourbeForLevel;
|
||||||
@@ -81,11 +57,14 @@ fetch(`/api/bot/get-roles/${guildId}`)
|
|||||||
gainVoiceXpUpperBound.value = cfg.gainVoiceXpUpperBound;
|
gainVoiceXpUpperBound.value = cfg.gainVoiceXpUpperBound;
|
||||||
})
|
})
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
// Sauvegarder
|
||||||
|
saveLevel.addEventListener("click", async () => {
|
||||||
|
saveLevel.disabled = true;
|
||||||
|
saveLevel.textContent = "Sauvegarde...";
|
||||||
|
|
||||||
levelForm.addEventListener("submit", async e => {
|
try {
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
const res = await fetch("/api/bot/save-level-config", {
|
const res = await fetch("/api/bot/save-level-config", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
@@ -113,7 +92,16 @@ levelForm.addEventListener("submit", async e => {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
statusLevelForm.textContent = (await res.json()).success
|
const data = await res.json();
|
||||||
? "Config niveaux sauvegardée ✅"
|
if (data.success) {
|
||||||
: "Erreur ❌";
|
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,20 +1,26 @@
|
|||||||
const welcomeForm = document.getElementById("welcome-form");
|
|
||||||
const welcomeEnabled = document.getElementById("welcome-enabled");
|
const welcomeEnabled = document.getElementById("welcome-enabled");
|
||||||
const welcomeChannel = document.getElementById("welcome-channel");
|
const welcomeChannel = document.getElementById("welcome-channel");
|
||||||
const welcomeMessage = document.getElementById("welcome-message");
|
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}`)
|
fetch(`/api/bot/get-welcome-config/${guildId}`)
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(cfg => {
|
.then(cfg => {
|
||||||
welcomeEnabled.checked = cfg.enabled;
|
welcomeEnabled.checked = cfg.enabled;
|
||||||
welcomeChannel.value = cfg.channelId;
|
welcomeChannel.value = cfg.channelId;
|
||||||
welcomeMessage.value = cfg.message;
|
welcomeMessage.value = cfg.message || defaultWelcomeMessage;
|
||||||
});
|
});
|
||||||
|
|
||||||
welcomeForm.addEventListener("submit", async e => {
|
// Sauvegarder
|
||||||
e.preventDefault();
|
saveWelcome.addEventListener("click", async () => {
|
||||||
|
saveWelcome.disabled = true;
|
||||||
|
saveWelcome.textContent = "Sauvegarde...";
|
||||||
|
|
||||||
|
try {
|
||||||
const res = await fetch("/api/bot/save-welcome-config", {
|
const res = await fetch("/api/bot/save-welcome-config", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
@@ -26,7 +32,16 @@ welcomeForm.addEventListener("submit", async e => {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
statusWelcomeForm.textContent = (await res.json()).success
|
const data = await res.json();
|
||||||
? "Config bienvenue sauvegardée ✅"
|
if (data.success) {
|
||||||
: "Erreur ❌";
|
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";
|
||||||
});
|
});
|
||||||
|
|||||||
+361
-98
@@ -1,130 +1,393 @@
|
|||||||
/* ===== Reset minimal ===== */
|
@import url('global.css');
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
/* ===== Hero Section ===== */
|
||||||
background-color: #1e1f29; /* fond sombre type Discord */
|
.hero {
|
||||||
color: #ffffff;
|
|
||||||
line-height: 1.6;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center; /* centre horizontalement */
|
align-items: center;
|
||||||
justify-content: flex-start; /* commencer depuis le haut */
|
justify-content: center;
|
||||||
padding: 20px;
|
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 ===== */
|
/* ===== 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;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
max-width: 1200px;
|
||||||
background-color: #2f3136;
|
margin: 0 auto;
|
||||||
padding: 10px 20px;
|
padding: 0 var(--spacing-md);
|
||||||
border-radius: 10px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 800px; /* limite largeur nav */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nav a {
|
.navbar-brand {
|
||||||
color: #ffffff;
|
|
||||||
text-decoration: none;
|
|
||||||
margin-right: 15px;
|
|
||||||
font-weight: 500;
|
|
||||||
transition: color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav a:hover {
|
|
||||||
color: #5865f2;
|
|
||||||
}
|
|
||||||
|
|
||||||
#profil {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
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 {
|
.navbar-brand:hover {
|
||||||
width: 40px;
|
color: var(--text-primary);
|
||||||
height: 40px;
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-brand img {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#logout {
|
.navbar-nav {
|
||||||
background-color: #5865f2;
|
display: flex;
|
||||||
color: white;
|
align-items: center;
|
||||||
padding: 5px 12px;
|
gap: var(--spacing-lg);
|
||||||
border-radius: 6px;
|
}
|
||||||
|
|
||||||
|
.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;
|
font-size: 0.9rem;
|
||||||
transition: background 0.2s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#logout:hover {
|
/* ===== Login Button ===== */
|
||||||
background-color: #4752c4;
|
.btn-login {
|
||||||
}
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
/* ===== Titres ===== */
|
gap: var(--spacing-sm);
|
||||||
h1, h2 {
|
background-color: var(--primary);
|
||||||
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;
|
|
||||||
color: white;
|
color: white;
|
||||||
padding: 10px 20px;
|
padding: var(--spacing-sm) var(--spacing-md);
|
||||||
border-radius: 8px;
|
border-radius: var(--border-radius);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
transition: background 0.2s;
|
transition: background-color var(--transition-fast);
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#invite-link:hover {
|
.btn-login:hover {
|
||||||
background-color: #4752c4;
|
background-color: var(--primary-hover);
|
||||||
}
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
/* ===== Listes ===== */
|
|
||||||
ul {
|
|
||||||
list-style: disc inside;
|
|
||||||
margin: 10px 0 20px 0;
|
|
||||||
max-width: 700px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== Responsive ===== */
|
/* ===== Responsive ===== */
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 768px) {
|
||||||
nav {
|
.hero-title {
|
||||||
flex-direction: column;
|
font-size: 2.5rem;
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#profil {
|
.hero-subtitle {
|
||||||
gap: 5px;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
+159
-35
@@ -1,62 +1,186 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html lang="fr">
|
||||||
<head>
|
<head>
|
||||||
<title>LazyBot - Bot Discord</title>
|
<title>LazyBot - Bot Discord Multifonction</title>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<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">
|
<link rel="stylesheet" href="/index.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav>
|
<!-- Navigation -->
|
||||||
<a href="/">Accueil</a>
|
<nav class="navbar">
|
||||||
<a href="/dashboard">Tableau de bord</a>
|
<div class="navbar-container">
|
||||||
<div id="profil">
|
<a href="/" class="navbar-brand">
|
||||||
<img id="avatar" src="" alt="Avatar">
|
<img id="bot-avatar" src="" alt="LazyBot">
|
||||||
<span id="username"></span>
|
<span>LazyBot</span>
|
||||||
<a id="logout" href="/auth/logout">Se déconnecter</a>
|
</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>
|
</div>
|
||||||
</nav>
|
</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>
|
|
||||||
|
|
||||||
|
<!-- 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>
|
<h2>Fonctionnalités</h2>
|
||||||
<ul>
|
<p>Tout ce dont vous avez besoin pour votre serveur Discord</p>
|
||||||
<li>Messages de bienvenue et d'au revoir personnalisables</li>
|
</div>
|
||||||
<li>Rôles automatiques pour les nouveaux membres et les utilisateurs en vocal</li>
|
<div class="features-grid">
|
||||||
<li>Et bien plus encore...</li>
|
<div class="feature-card">
|
||||||
</body>
|
<div class="feature-icon">📈</div>
|
||||||
<script>
|
<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")
|
fetch("/invite-bot")
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
const link = document.getElementById("invite-link");
|
document.getElementById("invite-link").href = data.url;
|
||||||
link.href = data.url; // met le lien dynamique
|
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(() => {
|
.catch(() => {
|
||||||
console.log("Impossible de récupérer le lien du bot.");
|
// 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")
|
fetch("/api/user")
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (!res.ok) throw new Error("Utilisateur non connecté"); // gère les erreurs HTTP
|
if (!res.ok) throw new Error("Non connecté");
|
||||||
return res.json();
|
return res.json();
|
||||||
})
|
})
|
||||||
.then(user => {
|
.then(user => {
|
||||||
document.getElementById("username").textContent = `${user.username}`;
|
const authSection = document.getElementById("auth-section");
|
||||||
document.getElementById("avatar").src = `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png`;
|
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(() => {
|
.catch(() => {
|
||||||
const profilDiv = document.getElementById("profil");
|
// Garder le bouton de connexion par défaut
|
||||||
profilDiv.style.display = "none";
|
|
||||||
});
|
});
|
||||||
|
</script>
|
||||||
</script>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -4,6 +4,26 @@ const loadSlashCommands = require("../slash_commands");
|
|||||||
|
|
||||||
module.exports = (app, db, client) => {
|
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 ---
|
// --- User info ---
|
||||||
router.get("/user", (req, res) => {
|
router.get("/user", (req, res) => {
|
||||||
if (req.session.user) res.json(req.session.user);
|
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;
|
const REDIRECT_URI = process.env.REDIRECT_URI;
|
||||||
|
|
||||||
module.exports = (app, db, client) => {
|
module.exports = (app, db, client) => {
|
||||||
|
// --- Redirection login ---
|
||||||
|
router.get("/login", (req, res) => {
|
||||||
|
res.redirect("/auth/discord");
|
||||||
|
});
|
||||||
|
|
||||||
// --- Connexion Discord ---
|
// --- Connexion Discord ---
|
||||||
router.get("/discord", (req, res) => {
|
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`;
|
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