95 lines
3.3 KiB
TypeScript
95 lines
3.3 KiB
TypeScript
|
|
"use client";
|
||
|
|
|
||
|
|
import { Suspense, useEffect, useState } from "react";
|
||
|
|
import { useSearchParams, useRouter } from "next/navigation";
|
||
|
|
import { Loader2, CheckCircle, XCircle } from "lucide-react";
|
||
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||
|
|
import { Button } from "@/components/ui/button";
|
||
|
|
|
||
|
|
function VerifyContent() {
|
||
|
|
const searchParams = useSearchParams();
|
||
|
|
const router = useRouter();
|
||
|
|
const [status, setStatus] = useState<"loading" | "success" | "error">("loading");
|
||
|
|
const [errorMessage, setErrorMessage] = useState("");
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const token = searchParams.get("token");
|
||
|
|
const error = searchParams.get("error");
|
||
|
|
|
||
|
|
if (error) {
|
||
|
|
setStatus("error");
|
||
|
|
setErrorMessage(decodeURIComponent(error));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!token) {
|
||
|
|
setStatus("error");
|
||
|
|
setErrorMessage("No verification token provided");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// The verification is handled by Better Auth automatically
|
||
|
|
// If we reach this page with a token, it means the verification was successful
|
||
|
|
// and the user should be redirected
|
||
|
|
const callbackUrl = searchParams.get("callbackURL") || "/";
|
||
|
|
|
||
|
|
// Short delay to show success state
|
||
|
|
setTimeout(() => {
|
||
|
|
setStatus("success");
|
||
|
|
setTimeout(() => {
|
||
|
|
router.push(callbackUrl);
|
||
|
|
}, 1500);
|
||
|
|
}, 500);
|
||
|
|
}, [searchParams, router]);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<Card className="border-0 shadow-2xl bg-white/95 dark:bg-slate-900/95 backdrop-blur">
|
||
|
|
<CardHeader className="space-y-1 text-center">
|
||
|
|
<div className="flex justify-center mb-4">
|
||
|
|
<div className="h-12 w-12 rounded-xl bg-gradient-to-br from-purple-600 to-blue-600 flex items-center justify-center">
|
||
|
|
<span className="text-2xl font-bold text-white">P</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<CardTitle className="text-2xl font-bold">
|
||
|
|
{status === "loading" && "Verifying..."}
|
||
|
|
{status === "success" && "Verified!"}
|
||
|
|
{status === "error" && "Verification Failed"}
|
||
|
|
</CardTitle>
|
||
|
|
<CardDescription>
|
||
|
|
{status === "loading" && "Please wait while we verify your magic link"}
|
||
|
|
{status === "success" && "You're being redirected..."}
|
||
|
|
{status === "error" && errorMessage}
|
||
|
|
</CardDescription>
|
||
|
|
</CardHeader>
|
||
|
|
<CardContent className="flex justify-center py-8">
|
||
|
|
{status === "loading" && (
|
||
|
|
<Loader2 className="h-12 w-12 animate-spin text-purple-600" />
|
||
|
|
)}
|
||
|
|
{status === "success" && (
|
||
|
|
<div className="h-16 w-16 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center">
|
||
|
|
<CheckCircle className="h-8 w-8 text-green-600 dark:text-green-400" />
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
{status === "error" && (
|
||
|
|
<div className="space-y-4 text-center">
|
||
|
|
<div className="h-16 w-16 rounded-full bg-red-100 dark:bg-red-900/30 flex items-center justify-center mx-auto">
|
||
|
|
<XCircle className="h-8 w-8 text-red-600 dark:text-red-400" />
|
||
|
|
</div>
|
||
|
|
<Button onClick={() => router.push("/auth/sign-in")}>
|
||
|
|
Try again
|
||
|
|
</Button>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</CardContent>
|
||
|
|
</Card>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
export default function VerifyPage() {
|
||
|
|
return (
|
||
|
|
<Suspense fallback={<div>Loading...</div>}>
|
||
|
|
<VerifyContent />
|
||
|
|
</Suspense>
|
||
|
|
);
|
||
|
|
}
|