From dedba5d1be589bfe8331f6b7ac0973bad0cb0d17 Mon Sep 17 00:00:00 2001 From: Zallom Date: Fri, 22 May 2026 12:15:20 +0200 Subject: [PATCH 1/6] feat(thank-you): port Webflow thank-you page to TSX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Page invitée OAuth Discord : - Logo + texte de remerciement, card centrée style Webflow - Widget serveur dynamique : fetch Discord widget + invite à partir de ?guild_id, affiche carte avec icône + nom + membres + badge PARTNERED/VERIFIED - Warning permissions BigInt à partir de ?permissions : compare avec REQUIRED_PERMISSIONS et liste les permissions manquantes (ou avertit pour Administrateur seul) - 2 CTAs : Discord externe + interne Erreurs réseau silencieuses, aucun fetch côté SSR. --- src/pages/thank-you.module.css | 255 +++++++++++++++++++++++++++++ src/pages/thank-you.tsx | 287 +++++++++++++++++++++++++++++++++ 2 files changed, 542 insertions(+) create mode 100644 src/pages/thank-you.module.css create mode 100644 src/pages/thank-you.tsx diff --git a/src/pages/thank-you.module.css b/src/pages/thank-you.module.css new file mode 100644 index 0000000..69c4418 --- /dev/null +++ b/src/pages/thank-you.module.css @@ -0,0 +1,255 @@ +/* + * Thank-you page styles + * Mirrors the Webflow `.link-in-bio` layout. + * Colors reuse the same tokens already defined in src/components/landing/styles/shared.module.css + * so the look matches the rest of the site. + */ + +.page { + --color-black: #040114; + --color-gray-1: #070417; + --color-gray-2: #1b1a25; + --color-gray-3: #242328; + --color-gray-4: #e1e0e9; + --color-white: #ffffff; + --color-primary-rp: #d35f5f; + --color-darker-primary: #bd5454; + --color-border-doc: #444950; + + background-color: var(--color-black); + color: var(--color-gray-4); + font-size: 18px; + line-height: 1.5em; + min-height: 70vh; + padding: 50px 20px; + display: flex; + justify-content: center; + align-items: flex-start; +} + +.card { + z-index: 22; + border: 1px solid var(--color-gray-3); + border-radius: 20px; + background-color: var(--color-gray-1); + text-align: center; + flex-direction: column; + width: 100%; + max-width: 650px; + padding: 60px; + display: flex; + position: relative; +} + +.logoWrap { + justify-content: center; + align-items: center; + margin-bottom: 30px; + margin-left: auto; + margin-right: auto; + text-decoration: none; + display: flex; +} + +.logo { + height: 70px; + width: auto; +} + +.title { + color: var(--color-white); + font-family: var(--ifm-heading-font-family, inherit); + font-size: 28px; + line-height: 1.25em; + margin: 0 0 20px; +} + +.description { + color: var(--color-gray-4); + margin: 0 0 10px; +} + +.buttonRow { + grid-column-gap: 15px; + grid-row-gap: 15px; + flex-direction: column; + padding-top: 20px; + padding-bottom: 0; + display: flex; +} + +.btnPrimary { + display: inline-block; + border: 1px solid var(--color-darker-primary); + border-radius: 50px; + background-color: var(--color-darker-primary); + color: #fff; + text-align: center; + padding: 20px 28px 18px; + font-size: 18px; + font-weight: 500; + line-height: 1.1em; + text-decoration: none; + transition: border-color 0.3s, background-color 0.3s, transform 0.3s, color 0.3s; +} + +.btnPrimary:hover { + text-decoration: none; + color: #fff; + border-color: var(--color-darker-primary); + background-color: var(--color-darker-primary); + transform: translate3d(0, -3px, 0.01px); +} + +.btnSecondary { + display: inline-block; + border: 1px solid var(--color-darker-primary); + border-radius: 50px; + background-color: transparent; + color: var(--color-white); + text-align: center; + padding: 20px 28px 18px; + font-size: 18px; + line-height: 1.1em; + text-decoration: none; + transition: border-color 0.3s, transform 0.3s, background-color 0.3s, color 0.3s; +} + +.btnSecondary:hover { + text-decoration: none; + color: #fff; + border-color: var(--color-darker-primary); + background-color: var(--color-darker-primary); + transform: translate3d(0, -3px, 0.01px); +} + +/* Server card */ + +.centerServer { + margin: 0 auto 20px; +} + +.clientServer { + grid-column-gap: 15px; + grid-row-gap: 15px; + border: 1px solid var(--color-border-doc); + border-radius: 12px; + background-color: var(--color-gray-3); + width: auto; + min-width: 275px; + padding: 20px 24px; + text-decoration: none; + display: inline-flex; + align-items: center; + justify-content: flex-start; + transition: border-color 0.3s; +} + +.clientServer:hover { + border-color: var(--color-primary-rp); + text-decoration: none; +} + +.avatarInfo { + grid-column-gap: 15px; + grid-row-gap: 15px; + display: flex; + align-items: center; + font-size: 14px; + line-height: 1.5em; + text-align: left; +} + +.avatar { + object-fit: cover; + border-radius: 50%; + flex: none; + width: 50px; + height: 50px; +} + +.serverInfos { + grid-column-gap: 6px; + grid-row-gap: 6px; + flex-flow: column; + justify-content: center; + align-items: flex-start; + display: flex; +} + +.serverNameRow { + justify-content: flex-start; + align-items: center; + display: flex; +} + +.serverName { + color: var(--color-white); + margin: 0; + font-size: 18px; + font-weight: 600; +} + +.serverBadge { + margin-left: 10px; +} + +.serverMembers { + color: var(--color-gray-4); + font-size: 14px; +} + +/* Permission warning */ + +.permissionWarning { + padding: 15px; + border-radius: 8px; + text-align: center; + max-width: 600px; + margin: 20px auto; + padding-bottom: 1px; + color: var(--color-white); +} + +.success { + background: #003100; +} + +.adminWarning { + background: #193c47; +} + +.missingPermissions { + background: #4d3800; +} + +.permissionList { + list-style-type: none; + padding: 0; + margin-top: 10px; +} + +.permissionList li { + margin: 5px 0; +} + +.permissionList code { + background-color: rgba(255, 255, 255, 0.08); + padding: 2px 8px; + border-radius: 4px; + font-size: 0.95em; +} + +@media screen and (max-width: 767px) { + .card { + padding: 40px 24px; + } + .title { + font-size: 22px; + } + .btnPrimary, + .btnSecondary { + padding: 20px 26px 16px; + font-size: 16px; + } +} diff --git a/src/pages/thank-you.tsx b/src/pages/thank-you.tsx new file mode 100644 index 0000000..4f97722 --- /dev/null +++ b/src/pages/thank-you.tsx @@ -0,0 +1,287 @@ +import React, {type ReactNode, useEffect, useState} from 'react'; +import Layout from '@theme/Layout'; +import Link from '@docusaurus/Link'; +import styles from './thank-you.module.css'; + +type ServerBadge = 'partner' | 'verified' | null; + +type ServerInfo = { + name: string; + members: string; + inviteUrl: string; + iconUrl: string; + badge: ServerBadge; +}; + +type PermissionWarning = { + tone: 'admin' | 'missing'; + message: string; + missing: string[]; +}; + +const DEFAULT_ICON_URL = + 'https://cdn.prod.website-files.com/677fbd67c3c9318f7fb56659/67c33922eb3265808c183c50_411d8a698dd15ddf.webp'; + +const BADGE_SRC: Record, string> = { + partner: '/img/landing/serverBadgePartner.svg', + verified: '/img/landing/serverBadgeVerified.svg', +}; + +const REQUIRED_PERMISSIONS = 1117660769534n; +const ADMIN_PERMISSION = 8n; + +// Order matches the Webflow source so the missing-permissions list reads the same. +const PERMISSION_MESSAGES: Array<[string, bigint]> = [ + ['Administrateur', ADMIN_PERMISSION], + ['Gérer le serveur', 32n], + ['Gérer les rôles', 268435456n], + ['Gérer les salons', 16n], + ['Expulser des membres', 2n], + ['Bannir des membres', 4n], + ['Gérer les pseudos', 134217728n], + ['Gérer les webhooks', 536870912n], + ['Voir les logs du serveur', 524288n], + ['Voir les salons', 1024n], + ['Modérer les membres', 1099511627776n], + ['Envoyer des messages', 2048n], + ['Gérer les messages', 8192n], + ['Gérer les fils', 17179869184n], + ['Intégrer des liens', 16384n], + ['Joindre des fichiers', 32768n], + ['Voir les anciens messages', 65536n], + ['Ajouter des réactions', 64n], + ['Utiliser des émojis externes', 262144n], + ['Rendre les membres muets', 4194304n], + ['Mettre en sourdine des membres', 8388608n], + ['Déplacer des membres', 16777216n], +]; + +async function fetchServerInfo(guildId: string): Promise { + try { + const widgetResponse = await fetch( + `https://discord.com/api/guilds/${guildId}/widget.json`, + ); + if (!widgetResponse.ok) { + throw new Error('Widget désactivé.'); + } + const widgetData = await widgetResponse.json(); + + const name: string = widgetData.name || 'Serveur inconnu'; + const members: string = + typeof widgetData.presence_count === 'number' + ? `${widgetData.presence_count} membres en ligne` + : 'Nombre de membres inconnu'; + const inviteUrl: string = widgetData.instant_invite || '#'; + + let iconUrl = DEFAULT_ICON_URL; + let badge: ServerBadge = null; + + const inviteCode: string | null = widgetData.instant_invite + ? widgetData.instant_invite.split('/').pop() || null + : null; + + if (inviteCode) { + const inviteResponse = await fetch( + `https://discord.com/api/invites/${inviteCode}?with_counts=true&with_expiration=true`, + ); + if (inviteResponse.ok) { + const inviteData = await inviteResponse.json(); + const server = inviteData.guild; + if (server?.icon) { + iconUrl = `https://cdn.discordapp.com/icons/${server.id}/${server.icon}.png`; + } + if (Array.isArray(server?.features)) { + if (server.features.includes('PARTNERED')) { + badge = 'partner'; + } else if (server.features.includes('VERIFIED')) { + badge = 'verified'; + } + } + } + } + + return {name, members, inviteUrl, iconUrl, badge}; + } catch (error) { + // Discord widget may be disabled or network may be unreachable. + // eslint-disable-next-line no-console + console.error(error); + return null; + } +} + +function computePermissionWarning( + permissionsParam: string, +): PermissionWarning | null { + let currentPermissions: bigint; + try { + currentPermissions = BigInt(permissionsParam); + } catch { + return null; + } + + const hasAdminPermission = + (currentPermissions & ADMIN_PERMISSION) === ADMIN_PERMISSION; + const missingPermissions = PERMISSION_MESSAGES.filter( + ([, value]) => + (REQUIRED_PERMISSIONS & value) === value && + (currentPermissions & value) !== value, + ).map(([name]) => name); + + if (hasAdminPermission || missingPermissions.length === 0) { + return null; + } + + if ( + missingPermissions.length === 1 && + missingPermissions[0] === 'Administrateur' + ) { + return { + tone: 'admin', + message: + '⚠️ Toutes les permissions spécifiques sont accordées, mais sans la permission Administrateur, le bot pourrait ne pas accéder à tous les salons.', + missing: [], + }; + } + + return { + tone: 'missing', + message: + "⚠️ Afin d'assurer le bon fonctionnement du bot, nous vous recommandons d'ajouter les permissions suivantes :", + missing: missingPermissions, + }; +} + +export default function ThankYou(): ReactNode { + const [serverInfo, setServerInfo] = useState(null); + const [permissionWarning, setPermissionWarning] = + useState(null); + + useEffect(() => { + if (typeof window === 'undefined') return; + + const urlParams = new URLSearchParams(window.location.search); + + const guildId = urlParams.get('guild_id'); + if (guildId) { + let cancelled = false; + fetchServerInfo(guildId).then((info) => { + if (!cancelled && info) { + setServerInfo(info); + } + }); + // Permission check is synchronous, so we can compute it without guarding cancellation. + const permissionsParam = urlParams.get('permissions'); + if (permissionsParam) { + setPermissionWarning(computePermissionWarning(permissionsParam)); + } + return () => { + cancelled = true; + }; + } + + const permissionsParam = urlParams.get('permissions'); + if (permissionsParam) { + setPermissionWarning(computePermissionWarning(permissionsParam)); + } + return undefined; + }, []); + + return ( + +
+
+ + RaidProtect title logo + + +

Merci d'avoir invité RaidProtect !

+ + {serverInfo && ( + + )} + + {permissionWarning && ( +
+ {permissionWarning.message} + {permissionWarning.missing.length > 0 && ( +
    + {permissionWarning.missing.map((perm) => ( +
  • + {perm} +
  • + ))} +
+ )} +
+ )} + +

+ Pour bien débuter, nous vous recommandons de consulter notre + documentation et de rejoindre notre serveur. +

+ +
+ + Rejoindre notre serveur Discord + + + Consulter la documentation + +
+
+
+
+ ); +} From d17e0875a9a652256d468fe32563ec21846d12ed Mon Sep 17 00:00:00 2001 From: Zallom Date: Fri, 22 May 2026 12:30:35 +0200 Subject: [PATCH 2/6] fix(thank-you): aligner les styles sur les classes Webflow source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Écarts identifiés vs raidprotectbot.webflow.css : - .page : min-height 70vh→100vh, align-items center (link-in-bio-wrap) - .title : font-size 28px→18px, retirer font heading (un simple

Webflow) - .clientServer : gap 15→30, padding 20-24→30, border-radius 12px→0.8rem - .centerServer : margin-bottom 20→30 - .buttonRow : padding-bottom 0→20 Le 'Merci d'avoir invité RaidProtect' est en fait un

standard côté Webflow (pas un titre), donc fonte body et couleur grise héritée. --- src/pages/thank-you.module.css | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/pages/thank-you.module.css b/src/pages/thank-you.module.css index 69c4418..f73a68e 100644 --- a/src/pages/thank-you.module.css +++ b/src/pages/thank-you.module.css @@ -20,11 +20,11 @@ color: var(--color-gray-4); font-size: 18px; line-height: 1.5em; - min-height: 70vh; + min-height: 100vh; padding: 50px 20px; display: flex; justify-content: center; - align-items: flex-start; + align-items: center; } .card { @@ -57,10 +57,9 @@ } .title { - color: var(--color-white); - font-family: var(--ifm-heading-font-family, inherit); - font-size: 28px; - line-height: 1.25em; + color: var(--color-gray-4); + font-size: 18px; + line-height: 1.5em; margin: 0 0 20px; } @@ -74,7 +73,7 @@ grid-row-gap: 15px; flex-direction: column; padding-top: 20px; - padding-bottom: 0; + padding-bottom: 20px; display: flex; } @@ -126,18 +125,18 @@ /* Server card */ .centerServer { - margin: 0 auto 20px; + margin: 0 auto 30px; } .clientServer { - grid-column-gap: 15px; - grid-row-gap: 15px; + grid-column-gap: 30px; + grid-row-gap: 30px; border: 1px solid var(--color-border-doc); - border-radius: 12px; + border-radius: 0.8rem; background-color: var(--color-gray-3); width: auto; min-width: 275px; - padding: 20px 24px; + padding: 30px; text-decoration: none; display: inline-flex; align-items: center; From 59c2854e8b59c85742aac0565f7680e10576b86d Mon Sep 17 00:00:00 2001 From: Zallom Date: Fri, 22 May 2026 12:48:02 +0200 Subject: [PATCH 3/6] fix(thank-you): retirer le Layout Docusaurus pour matcher l'utility page Webflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plus de navbar/footer Docusaurus : la page est désormais plein écran (min-height 100vh sur .page) comme le Webflow .utility-page-wrap. On garde uniquement pour le titre et la description ; le data-theme dark est appliqué côté client par le script inline Docusaurus. --- src/pages/thank-you.tsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/pages/thank-you.tsx b/src/pages/thank-you.tsx index 4f97722..77a6dd5 100644 --- a/src/pages/thank-you.tsx +++ b/src/pages/thank-you.tsx @@ -1,5 +1,5 @@ import React, {type ReactNode, useEffect, useState} from 'react'; -import Layout from '@theme/Layout'; +import Head from '@docusaurus/Head'; import Link from '@docusaurus/Link'; import styles from './thank-you.module.css'; @@ -187,9 +187,14 @@ export default function ThankYou(): ReactNode { }, []); return ( - + <> + + Merci | RaidProtect + +

@@ -282,6 +287,6 @@ export default function ThankYou(): ReactNode {
- + ); } From 771af71edc6ff0981e7881d19b5537879fe5475b Mon Sep 17 00:00:00 2001 From: Zallom Date: Fri, 22 May 2026 12:54:28 +0200 Subject: [PATCH 4/6] =?UTF-8?q?fix(thank-you):=20ajouter=20halo=20radial,?= =?UTF-8?q?=20dropdown=20langue,=20r=C3=A9seaux=20sociaux?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reproduits du Webflow source : - .decoration : halo central (linear-gradient + blur 150px) derrière la card - .footerThank : grid 2 cols, dropdown langue à gauche + 4 socials à droite - .langDropdown : ouvert au hover/focus-within, items style .dropdown-link-3 - .socialWrap + .socialLink + .socialIcon : 4 icones (Discord/X/YouTube/GitHub) - .title margin-bottom 20→50px pour aérer entre 'Merci' et 'Pour bien débuter' --- src/pages/thank-you.module.css | 136 ++++++++++++++++++++++++++++++++- src/pages/thank-you.tsx | 105 +++++++++++++++++++++++++ 2 files changed, 239 insertions(+), 2 deletions(-) diff --git a/src/pages/thank-you.module.css b/src/pages/thank-you.module.css index f73a68e..f9abc1a 100644 --- a/src/pages/thank-you.module.css +++ b/src/pages/thank-you.module.css @@ -25,6 +25,23 @@ display: flex; justify-content: center; align-items: center; + position: relative; + overflow: hidden; +} + +/* Halo radial dégradé derrière la card (linear 180° + blur, + * comme Webflow .utility-page-content-decoration). */ +.decoration { + position: absolute; + inset: 0; + margin: auto; + width: 500px; + height: 50%; + background-image: linear-gradient(180deg, var(--color-primary-rp) 40%, #a561a3); + filter: blur(150px); + opacity: 0.5; + pointer-events: none; + z-index: 0; } .card { @@ -60,7 +77,7 @@ color: var(--color-gray-4); font-size: 18px; line-height: 1.5em; - margin: 0 0 20px; + margin: 0 0 50px; } .description { @@ -239,16 +256,131 @@ font-size: 0.95em; } +/* Footer (langue + réseaux sociaux) */ + +.footerThank { + display: grid; + grid-template-columns: 1fr 1fr; + grid-column-gap: 16px; + grid-row-gap: 16px; + justify-content: center; + align-items: center; + margin-top: 20px; +} + +.langDropdown { + position: relative; + display: inline-flex; + justify-content: center; + align-items: center; +} + +.langTrigger { + color: #fff; + display: inline-flex; + align-items: center; + padding: 1.15px 12px; + cursor: pointer; + user-select: none; +} + +.langTrigger:hover { + color: var(--color-primary-rp); +} + +.iconLanguage { + display: inline-block; + margin-right: 5px; + transform: translateY(4px); +} + +.currentLanguage { + font-family: var(--ifm-font-family-base, inherit); +} + +.langDropdownList { + position: absolute; + inset: auto auto 30px auto; + display: none; + flex-direction: column; + min-width: 10rem; + padding: 0.5rem; + border-radius: 10px; + background-color: var(--color-gray-2); + text-align: left; + z-index: 30; +} + +.langDropdown:hover .langDropdownList, +.langDropdown:focus-within .langDropdownList { + display: flex; +} + +.langDropdownItem { + color: var(--color-gray-4); + margin-top: 0.2rem; + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + text-decoration: none; + border-radius: 5px; +} + +.langDropdownItem:hover { + background-color: var(--color-gray-3); + color: var(--color-gray-4); + text-decoration: none; +} + +.langDropdownItemCurrent { + background-color: var(--color-gray-3); + color: var(--color-primary-rp); +} + +.socialWrap { + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; + grid-column-gap: 20px; + grid-row-gap: 20px; + margin: 0; +} + +.socialLink { + display: block; + color: var(--color-white); + transition: transform 0.3s ease-in-out; +} + +.socialLink:hover { + transform: translateY(-3px); +} + +.socialIcon { + width: 18px; + height: 18px; + display: block; +} + @media screen and (max-width: 767px) { .card { padding: 40px 24px; } .title { - font-size: 22px; + font-size: 18px; + margin-bottom: 40px; } .btnPrimary, .btnSecondary { padding: 20px 26px 16px; font-size: 16px; } + .footerThank { + grid-template-columns: 1fr; + grid-row-gap: 20px; + } + .decoration { + width: 100%; + max-width: 400px; + } } diff --git a/src/pages/thank-you.tsx b/src/pages/thank-you.tsx index 77a6dd5..517f90c 100644 --- a/src/pages/thank-you.tsx +++ b/src/pages/thank-you.tsx @@ -285,7 +285,112 @@ export default function ThankYou(): ReactNode { Consulter la documentation + +
+
+
+ + Français +
+ +
+ + +
+ +