juntekim.com/stripe_to_invoice/app/auth/callback/AuthCallbackClient.tsx
2026-02-07 19:38:11 +00:00

67 lines
2.2 KiB
TypeScript

"use client";
import { useEffect, useRef } from "react";
import { useSearchParams, useRouter } from "next/navigation";
export default function AuthCallbackClient() {
const params = useSearchParams();
const router = useRouter();
const ran = useRef(false);
useEffect(() => {
if (ran.current) return;
ran.current = true;
const token = params.get("token");
if (!token) {
router.replace("/login");
return;
}
fetch("/api/auth/callback", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token }),
})
.then(async (res) => {
if (!res.ok) throw new Error(await res.text());
router.replace("/app");
})
.catch(() => {
router.replace("/login");
});
}, [params, router]);
return (
<div className="min-h-screen bg-gradient-to-b from-slate-50 to-white flex items-center justify-center">
<div className="text-center">
{/* Animated loader */}
<div className="mb-6">
<div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-blue-100">
<div className="relative w-12 h-12">
<div className="absolute inset-0 rounded-full border-2 border-slate-200"></div>
<div className="absolute inset-0 rounded-full border-2 border-transparent border-t-blue-600 border-r-blue-600 animate-spin"></div>
</div>
</div>
</div>
{/* Text */}
<h1 className="text-2xl font-bold text-slate-900 mb-2">Signing you in</h1>
<p className="text-slate-600">
Please wait while we authenticate your account
</p>
{/* Progress indication */}
<div className="mt-8">
<div className="inline-flex items-center gap-2">
<div className="flex gap-1">
<span className="w-2 h-2 bg-blue-600 rounded-full animate-pulse"></span>
<span className="w-2 h-2 bg-blue-600 rounded-full animate-pulse" style={{ animationDelay: "0.2s" }}></span>
<span className="w-2 h-2 bg-blue-600 rounded-full animate-pulse" style={{ animationDelay: "0.4s" }}></span>
</div>
</div>
</div>
</div>
</div>
);
}