From 15ae4f8c6411cc677d151afd61c5b252320de8bb Mon Sep 17 00:00:00 2001 From: "Builder.io" Date: Sun, 9 Nov 2025 07:40:20 +0000 Subject: [PATCH] Create Discord verification code linking page cgen-c3bd05681895490792cb7f6b7559f85a --- client/pages/DiscordVerify.tsx | 288 ++++++++++++++++++++++++--------- 1 file changed, 211 insertions(+), 77 deletions(-) diff --git a/client/pages/DiscordVerify.tsx b/client/pages/DiscordVerify.tsx index 4e762bda..b4f81a9d 100644 --- a/client/pages/DiscordVerify.tsx +++ b/client/pages/DiscordVerify.tsx @@ -1,90 +1,224 @@ -import { useEffect, useState } from "react"; +import { useState, useEffect } from "react"; import { useNavigate, useSearchParams } from "react-router-dom"; import { useAuth } from "@/contexts/AuthContext"; -import LoadingScreen from "@/components/LoadingScreen"; -import { useAethexToast } from "@/hooks/use-aethex-toast"; +import Layout from "@/components/Layout"; +import SEO from "@/components/SEO"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { AlertTriangle, CheckCircle, Loader } from "lucide-react"; export default function DiscordVerify() { const navigate = useNavigate(); const [searchParams] = useSearchParams(); - const { user, loading: authLoading } = useAuth(); - const { success: toastSuccess, error: toastError } = useAethexToast(); - const [isProcessing, setIsProcessing] = useState(false); + const { user } = useAuth(); + + const [verificationCode, setVerificationCode] = useState(""); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + const [success, setSuccess] = useState(false); + const [discordUser, setDiscordUser] = useState(null); + + const code = searchParams.get("code"); useEffect(() => { - const handleVerification = async () => { - if (authLoading) return; - - if (!user) { - toastError({ - title: "Not Authenticated", - description: "Please sign in to link your Discord account", - }); - navigate("/login"); - return; - } - - const code = searchParams.get("code"); - if (!code) { - toastError({ - title: "Invalid Link", - description: "No verification code provided", - }); - navigate("/dashboard?tab=connections"); - return; - } - - setIsProcessing(true); - - try { - const response = await fetch("/api/discord/link", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - verification_code: code, - user_id: user.id, - }), - }); - - if (!response.ok) { - const errorData = await response.json(); - throw new Error(errorData.error || "Verification failed"); - } - - const data = await response.json(); - - toastSuccess({ - title: "Discord Linked!", - description: - "Your Discord account has been successfully linked to AeThex", - }); - - // Redirect to profile settings - setTimeout(() => { - navigate("/dashboard?tab=connections"); - }, 2000); - } catch (error: any) { - console.error("Discord verification error:", error); - toastError({ - title: "Linking Failed", - description: error?.message || "Could not link Discord account", - }); - navigate("/dashboard?tab=connections"); - } finally { - setIsProcessing(false); - } - }; - - if (!authLoading) { - handleVerification(); + // If code in URL, auto-fill it + if (code) { + setVerificationCode(code); + handleVerify(code); } - }, [searchParams, user, authLoading, navigate, toastSuccess, toastError]); + }, [code]); + + useEffect(() => { + // Redirect if not authenticated + if (!user) { + navigate("/login?next=/profile/link-discord"); + } + }, [user, navigate]); + + const handleVerify = async (codeToVerify: string) => { + if (!codeToVerify.trim()) { + setError("Please enter a verification code"); + return; + } + + setIsLoading(true); + setError(null); + setDiscordUser(null); + + try { + const response = await fetch("/api/discord/verify-code", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + verification_code: codeToVerify.trim(), + user_id: user?.id, + }), + }); + + const data = await response.json(); + + if (!response.ok) { + setError(data.message || "Verification failed. Please try again."); + setIsLoading(false); + return; + } + + // Success + setDiscordUser(data.discord_user); + setSuccess(true); + setVerificationCode(""); + + // Redirect to dashboard after 3 seconds + setTimeout(() => { + navigate("/profile/settings"); + }, 3000); + } catch (err) { + setError( + err instanceof Error ? err.message : "An error occurred. Please try again." + ); + setIsLoading(false); + } + }; + + if (!user) { + return null; + } return ( - + + + +
+
+ + + + + + + Link Discord Account + + + + + {success && discordUser ? ( + // Success State +
+ + + Successfully Linked! + + Your Discord account ({discordUser.username}) has been + linked to your AeThex profile. + + + +
+

+ Discord User +

+

+ {discordUser.username}#{discordUser.discriminator || "0000"} +

+
+ +

+ Redirecting to your profile settings... +

+ + +
+ ) : ( + // Input State +
+
+

+ How to get your code: +

+
    +
  1. Open Discord
  2. +
  3. Go to any server where the AeThex bot is installed
  4. +
  5. Type /verify
  6. +
  7. Copy the 6-digit code from the bot's response
  8. +
+
+ + {error && ( + + + Verification Failed + {error} + + )} + +
+ + setVerificationCode(e.target.value)} + maxLength={6} + disabled={isLoading} + className="text-center text-lg tracking-widest" + /> +
+ + + + +
+ )} +
+
+ + {/* Info Box */} +
+

+ 💡 Tip: You can also sign in directly with Discord + on the login page if you're creating a new account. +

+
+
+
+
); }