feat(adminsite): session guards and the auth screens
Route-group layouts do the guarding. A customer session on /staff/* is redirected to its own home rather than shown a refusal -- there is nothing to tell them about. This is UX only: admin enforces the same boundary with RequireStaff/RequireCustomer and answers 404 rather than 403 for another account's data, which is the layer that actually matters. Signup carries the honeypot the backend expects and reports "check your email" rather than claiming an account exists, matching a backend that creates nothing until the link is opened. Buttons match site/'s .btn--solid and .btn--line, neutral border included. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
"use client";
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import { Suspense } from "react";
|
||||
import { api } from "@/lib/api";
|
||||
|
||||
function Verify() {
|
||||
const token = useSearchParams().get("token") ?? "";
|
||||
const { data, error, isLoading } = useQuery({
|
||||
queryKey: ["verify", token],
|
||||
queryFn: () => api.verify(token),
|
||||
enabled: token !== "",
|
||||
retry: false,
|
||||
});
|
||||
|
||||
if (!token)
|
||||
return (
|
||||
<Message
|
||||
title="That link is incomplete"
|
||||
body="It is missing its token. Use the link in the email exactly as sent."
|
||||
/>
|
||||
);
|
||||
if (isLoading) return <Message title="Verifying…" body="One moment." />;
|
||||
if (error || !data?.verified)
|
||||
return (
|
||||
<Message
|
||||
title="That link is invalid or has expired"
|
||||
body="Links last 24 hours and can only be used once. Sign up again to get a fresh one."
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="grid max-w-xl gap-3">
|
||||
<h1 className="text-3xl">Email verified</h1>
|
||||
<p className="text-ink-2">Your account is ready.</p>
|
||||
<Link href="/login" className="justify-self-start text-accent underline">
|
||||
Sign in
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Message({ title, body }: { title: string; body: string }) {
|
||||
return (
|
||||
<div className="grid max-w-xl gap-3">
|
||||
<h1 className="text-3xl">{title}</h1>
|
||||
<p className="text-ink-2">{body}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function VerifyPage() {
|
||||
return (
|
||||
<main className="mx-auto max-w-rail px-5 py-12">
|
||||
<Suspense fallback={null}>
|
||||
<Verify />
|
||||
</Suspense>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user