/* Public set-password page — where invite and reset links land (/reset?token=…). * Outside the auth gate by design: the person arriving here has no session yet. * Success stores the fresh session token and drops the user into the dashboard. */ "use client"; import { Suspense, useState } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import { authApi, type ApiError, type SessionResponse } from "@/lib/api"; function ResetForm() { const params = useSearchParams(); const router = useRouter(); const token = params.get("token") ?? ""; const [password, setPassword] = useState(""); const [confirm, setConfirm] = useState(""); const [error, setError] = useState(""); const [busy, setBusy] = useState(false); const submit = async (e: React.FormEvent) => { e.preventDefault(); setError(""); if (password !== confirm) { setError("Passwords don't match."); return; } setBusy(true); try { const session = await authApi("/auth/reset", { token, password }); window.localStorage.setItem("handler_token", session.token); router.replace("/"); } catch (err) { setError((err as ApiError).message || "Something went wrong."); } finally { setBusy(false); } }; return (
Claude Monitor
{token ? ( <>

Choose a password for your account. The link you followed is one-shot — once set, sign in with your email and this password.

setPassword(e.target.value)} minLength={8} autoFocus required /> setConfirm(e.target.value)} minLength={8} required /> {error && (

{error}

)} ) : (

This page needs a reset link (…/reset?token=…). Ask an admin for one, or use “Forgot password?” on the sign-in page.

)}
); } export default function ResetPage() { // useSearchParams requires a Suspense boundary under the static export. return ( ); }