Files
dumpsterChat/web/src/components/ForgotPasswordPage.tsx
T
hobokenchicken 82ddf914e4 feat: add forgot password UI flow
- ForgotPasswordPage: email input, sends reset request, shows success
- ResetPasswordPage: reads token from ?token= query param, password
  + confirm inputs, validates min length, shows success with login link
- LoginForm: added [FORGOT PASSWORD?] link below the form
- App.tsx: /forgot-password and /reset-password routes (public, no auth)
- auth store: requestPasswordReset + resetPassword actions
- Backend routes already existed at POST /auth/request-password-reset
  and POST /auth/reset-password
2026-06-30 15:49:23 -04:00

89 lines
2.8 KiB
TypeScript

import { useState } from "react";
import { Link } from "react-router-dom";
import { useAuthStore } from "../stores/auth.ts";
export function ForgotPasswordPage() {
const [email, setEmail] = useState("");
const [sent, setSent] = useState(false);
const { requestPasswordReset, isLoading, error, clearError } =
useAuthStore();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
clearError();
try {
await requestPasswordReset(email);
setSent(true);
} catch {
// error displayed from store
}
};
return (
<div className="h-full w-full flex items-center justify-center bg-gb-bg p-4">
<div className="terminal-border bg-gb-bg-h p-6 w-full max-w-md">
<pre className="text-gb-orange font-mono text-lg mb-6">
{"┌─ DUMPSTER ─┐"}
</pre>
<h2 className="text-gb-fg-s mb-4">[FORGOT PASSWORD]</h2>
{sent ? (
<div className="space-y-4">
<p className="text-gb-fg text-sm">
If an account exists for{" "}
<span className="text-gb-aqua">{email}</span>, we've sent a
password reset link. Check your inbox.
</p>
<p className="text-gb-fg-f text-xs">
The link expires in 1 hour.
</p>
<div className="pt-2">
<Link
to="/login"
className="text-gb-blue hover:text-gb-aqua text-sm"
>
[BACK TO LOGIN]
</Link>
</div>
</div>
) : (
<form onSubmit={handleSubmit} className="space-y-4">
<p className="text-gb-fg-t text-sm">
Enter the email address associated with your account and we'll
send a link to reset your password.
</p>
<div>
<label className="block text-gb-fg-t mb-1">EMAIL:</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="terminal-input"
required
autoComplete="email"
autoFocus
/>
</div>
{error && <p className="text-gb-red text-sm">ERR: {error}</p>}
<button
type="submit"
className="terminal-button w-full"
disabled={isLoading}
>
{isLoading ? "[SENDING...]" : "[SEND RESET LINK]"}
</button>
<div className="text-center">
<Link
to="/login"
className="text-gb-blue hover:text-gb-aqua text-sm"
>
[BACK TO LOGIN]
</Link>
</div>
</form>
)}
</div>
</div>
);
}