61 lines
2.0 KiB
TypeScript
61 lines
2.0 KiB
TypeScript
"use client";
|
|
|
|
/**
|
|
* Shared layout for the auth pages (login, signup, forgot-password,
|
|
* reset-password). Provides the centered card frame so each page only
|
|
* has to render its own form.
|
|
*
|
|
* Also gates the routes: a signed-in user landing on any /login, /signup,
|
|
* etc. URL is bounced to /u/me (or the `next` query param if present).
|
|
* The check happens client-side because auth state lives in
|
|
* AuthProvider — there's no server-side cookie inspection here yet, and
|
|
* the redirect is purely cosmetic (logged-in users don't gain anything
|
|
* from re-seeing these forms, and signing in over an existing session
|
|
* would just no-op).
|
|
*/
|
|
|
|
import { Suspense, useEffect } from "react";
|
|
import { useRouter, useSearchParams } from "next/navigation";
|
|
import { useAuth } from "@/components/auth/auth-provider";
|
|
|
|
export default function AuthLayout({
|
|
children,
|
|
}: {
|
|
children: React.ReactNode;
|
|
}) {
|
|
// useSearchParams forces a CSR bailout during prerender, so Next.js
|
|
// requires a Suspense boundary anywhere it's read. The gate is the
|
|
// only consumer here — isolate it so the rest of the layout can still
|
|
// SSR normally.
|
|
return (
|
|
<main className="flex items-center justify-center px-4 py-12 min-h-[90vh]">
|
|
<Suspense fallback={<AuthShell>{children}</AuthShell>}>
|
|
<AuthGate>{children}</AuthGate>
|
|
</Suspense>
|
|
</main>
|
|
);
|
|
}
|
|
|
|
function AuthShell({ children }: { children: React.ReactNode }) {
|
|
return <div className="w-full max-w-md">{children}</div>;
|
|
}
|
|
|
|
function AuthGate({ children }: { children: React.ReactNode }) {
|
|
const { user, loading } = useAuth();
|
|
const router = useRouter();
|
|
const params = useSearchParams();
|
|
|
|
useEffect(() => {
|
|
if (loading || !user) return;
|
|
const next = params.get("next");
|
|
router.replace(next && next.startsWith("/") ? next : "/u/me");
|
|
}, [loading, user, router, params]);
|
|
|
|
// Don't flash the form for an instant before the redirect fires.
|
|
if (!loading && user) {
|
|
return <p className="text-sm text-muted-foreground">Redirecting…</p>;
|
|
}
|
|
|
|
return <AuthShell>{children}</AuthShell>;
|
|
}
|