buildmymcpserver/apps/web/app/login/callback/page.tsx
Marco Sadjadi 2b098c5d33 fix(web): wrap useSearchParams in Suspense so next build can prerender
/servers/new and /login/callback call useSearchParams() directly, which
bails the page out of static rendering and fails `next build` during
prerender. Split each into a thin Suspense wrapper + inner component.
Latent since `next dev` never prerenders — only surfaces in a prod build.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 00:36:56 +02:00

72 lines
2.1 KiB
TypeScript

'use client';
import { Suspense, useEffect, useState } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import { Logo } from '@/components/logo';
import { apiFetch } from '@/lib/api';
function CallbackInner() {
const router = useRouter();
const params = useSearchParams();
const token = params.get('token');
const [state, setState] = useState<'verifying' | 'ok' | 'error'>('verifying');
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (!token) {
setState('error');
setError('Missing token');
return;
}
(async () => {
try {
await apiFetch('/v1/auth/verify', {
method: 'POST',
body: JSON.stringify({ token }),
});
setState('ok');
setTimeout(() => router.replace('/dashboard'), 200);
} catch (err) {
setState('error');
setError((err as Error).message);
}
})();
}, [token, router]);
return (
<div className="flex min-h-screen items-center justify-center px-6">
<div className="w-full max-w-sm text-center">
<Logo className="mx-auto mb-8" />
{state === 'verifying' && (
<p className="text-[13px] text-[--color-fg-muted]">Verifying your magic link</p>
)}
{state === 'ok' && (
<p className="text-[13px] text-emerald-300">Signed in. Redirecting</p>
)}
{state === 'error' && (
<>
<p className="text-[13px] text-[--color-danger]">Could not verify magic link.</p>
{error && <p className="mt-2 mono text-[11px] text-[--color-fg-subtle]">{error}</p>}
</>
)}
</div>
</div>
);
}
// useSearchParams() requires a Suspense boundary or `next build` cannot
// statically render this route.
export default function CallbackPage() {
return (
<Suspense
fallback={
<div className="flex min-h-screen items-center justify-center px-6">
<p className="text-[13px] text-[--color-fg-muted]">Verifying your magic link</p>
</div>
}
>
<CallbackInner />
</Suspense>
);
}