'use client'; import Link from 'next/link'; import { useSearchParams } from 'next/navigation'; import { useState } from 'react'; import { CheckCircle2, Loader2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; interface PasswordSetFormProps { /** API endpoint that accepts `{ token, password }` and sets / resets the password. */ endpoint: string; title: string; description: string; successTitle: string; successDescription: string; submitLabel: string; } const MIN_LENGTH = 12; /** * Shared form used by both the activation and password-reset flows. The * activation token is read from the `?token=` query string. Empty / missing * tokens land the user in an explicit error state instead of submitting a * doomed request. */ export function PasswordSetForm({ endpoint, title, description, successTitle, successDescription, submitLabel, }: PasswordSetFormProps) { const search = useSearchParams(); const token = search.get('token') ?? ''; const [password, setPassword] = useState(''); const [confirm, setConfirm] = useState(''); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const [done, setDone] = useState(false); const tooShort = password.length > 0 && password.length < MIN_LENGTH; const mismatch = confirm.length > 0 && password !== confirm; const canSubmit = !!token && password.length >= MIN_LENGTH && password === confirm && !loading; async function handleSubmit(e: React.FormEvent) { e.preventDefault(); if (!canSubmit) return; setLoading(true); setError(''); try { const res = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token, password }), }); if (!res.ok) { const data = await res.json().catch(() => ({})); setError((data as { error?: string }).error ?? 'Something went wrong. Please try again.'); return; } setDone(true); } catch { setError('Unable to connect. Please try again.'); } finally { setLoading(false); } } if (!token) { return (

Link is missing or invalid

Please use the link from the email we sent you. If the link is broken, request a new one.

Request a new link
); } if (done) { return (

{successTitle}

{successDescription}

Sign in
); } return (

{title}

{description}

setPassword(e.target.value)} required autoFocus autoComplete="new-password" minLength={MIN_LENGTH} disabled={loading} />

At least {MIN_LENGTH} characters.

{tooShort && (

Password must be at least {MIN_LENGTH} characters.

)}
setConfirm(e.target.value)} required autoComplete="new-password" disabled={loading} /> {mismatch &&

Passwords don't match.

}
{error &&

{error}

}
); }