122 lines
4.7 KiB
TypeScript
122 lines
4.7 KiB
TypeScript
"use client";
|
|
|
|
import { useSearchParams, useRouter } from "next/navigation";
|
|
import { useEffect, useState } from "react";
|
|
import { X, AlertCircle, CheckCircle } from "lucide-react";
|
|
|
|
export default function Toast() {
|
|
const searchParams = useSearchParams();
|
|
const router = useRouter();
|
|
const [isVisible, setIsVisible] = useState(false);
|
|
const [message, setMessage] = useState<{ type: "success" | "error"; text: string; title: string } | null>(null);
|
|
|
|
useEffect(() => {
|
|
const error = searchParams.get("error");
|
|
const success = searchParams.get("success");
|
|
|
|
if (error) {
|
|
let text = "An unexpected error occurred.";
|
|
let title = "Error";
|
|
|
|
switch (error) {
|
|
case "access_denied":
|
|
text = "You cancelled the authorization request.";
|
|
title = "Access Denied";
|
|
break;
|
|
case "invalid_request":
|
|
text = "The request parameters were invalid.";
|
|
title = "Invalid Request";
|
|
break;
|
|
case "state_mismatch":
|
|
text = "Security check failed (State Mismatch). Please try again.";
|
|
title = "Security Error";
|
|
break;
|
|
case "guild_mismatch":
|
|
text = "The server you authorized on Discord does not match the one you selected on the dashboard.";
|
|
title = "Server Mismatch";
|
|
break;
|
|
case "not_beta_server":
|
|
text = "This server is not part of the Closed Beta program. You cannot add the bot to it yet.";
|
|
title = "Beta Access Restricted";
|
|
break;
|
|
case "token_exchange_failed":
|
|
text = "Failed to communicate with Discord. Please try again.";
|
|
title = "Connection Failed";
|
|
break;
|
|
case "internal_server_error":
|
|
text = "Something went wrong on our end.";
|
|
title = "Server Error";
|
|
break;
|
|
case "config_error":
|
|
text = "System configuration error. Please contact support.";
|
|
title = "Configuration Error";
|
|
break;
|
|
default:
|
|
text = error;
|
|
}
|
|
|
|
setMessage({ type: "error", text, title });
|
|
setIsVisible(true);
|
|
} else if (success) {
|
|
let text = "Operation completed successfully.";
|
|
let title = "Success";
|
|
|
|
if (success === "bot_added") {
|
|
text = "Void Sentinel has been successfully added to your server!";
|
|
title = "Bot Added";
|
|
}
|
|
|
|
setMessage({ type: "success", text, title });
|
|
setIsVisible(true);
|
|
}
|
|
}, [searchParams]);
|
|
|
|
const closeToast = () => {
|
|
setIsVisible(false);
|
|
// Optional: clear params from URL without refresh
|
|
const params = new URLSearchParams(window.location.search);
|
|
params.delete("error");
|
|
params.delete("success");
|
|
params.delete("guild_id"); // Clean up other params if we want
|
|
router.replace(`?${params.toString()}`);
|
|
};
|
|
|
|
if (!isVisible || !message) return null;
|
|
|
|
return (
|
|
<div className="fixed bottom-6 right-6 z-50 animate-in slide-in-from-bottom-5 fade-in duration-300">
|
|
<div className={`
|
|
flex items-start gap-3 p-4 rounded-xl border shadow-2xl backdrop-blur-md max-w-md
|
|
${message.type === 'error'
|
|
? "bg-red-500/10 border-red-500/20 text-red-200"
|
|
: "bg-green-500/10 border-green-500/20 text-green-200"
|
|
}
|
|
`}>
|
|
<div className="mt-0.5">
|
|
{message.type === 'error' ? (
|
|
<AlertCircle className="w-5 h-5 text-red-400" />
|
|
) : (
|
|
<CheckCircle className="w-5 h-5 text-green-400" />
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex-1 mr-4">
|
|
<h4 className={`font-semibold text-sm mb-1 ${message.type === 'error' ? 'text-red-100' : 'text-green-100'}`}>
|
|
{message.title}
|
|
</h4>
|
|
<p className="text-sm opacity-90 leading-relaxed">
|
|
{message.text}
|
|
</p>
|
|
</div>
|
|
|
|
<button
|
|
onClick={closeToast}
|
|
className="p-1 hover:bg-white/10 rounded-lg transition-colors -mr-1 -mt-1"
|
|
>
|
|
<X className="w-4 h-4 opacity-70" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|