g1t/apps/web/app/routes/forgot.tsx

57 lines2,067 bytesCodeBlame
1import { Form, Link } from "react-router";
2
3import type { Route } from "./+types/forgot";
4import { page } from "../lib/meta";
5import { AuthCard } from "../components/auth-card";
6import { ErrorText, Field, Input, SubmitButton } from "../components/ui";
7import { identity } from "../lib/services.server";
8import { assertSameOrigin, clientOf } from "../lib/session.server";
9
10export function meta(args: Route.MetaArgs) {
11 return page(args, { title: "Reset your password · g1t" });
12}
13
14export async function action({ request }: Route.ActionArgs) {
15 assertSameOrigin(request);
16 const form = await request.formData();
17 try {
18 await identity.requestPasswordReset(String(form.get("email") ?? ""), clientOf(request));
19 } catch (error) {
20 // Logged, never shown: what went wrong could say whether the address
21 // has an account. The person hears only that it did not go.
22 console.warn("forgot:", error);
23 return { sent: false, error: "We could not send a reset link just now. Try again in a few minutes." };
24 }
25 return { sent: true, error: null };
26}
27
28export default function Forgot({ actionData }: Route.ComponentProps) {
29 return (
30 <AuthCard
31 title="Forgot your password?"
32 subtitle="We will email you a link"
33 footer={
34 <Link to="/login" className="text-fg underline underline-offset-4">
35 Back to sign in
36 </Link>
37 }
38 >
39 {actionData?.sent ? (
40 <p className="rounded-lg border border-line bg-surface p-4 text-sm leading-6">
41 If that address belongs to an account, a reset link is on its way to
42 it. It works for one hour.
43 </p>
44 ) : (
45 <Form method="post" className="space-y-4">
46 <Field label="Email">
47 <Input name="email" type="email" autoComplete="email" required autoFocus />
48 </Field>
49 <ErrorText>{actionData?.error}</ErrorText>
50 <div className="pt-2 *:w-full">
51 <SubmitButton pending="Sending…">Send reset link</SubmitButton>
52 </div>
53 </Form>
54 )}
55 </AuthCard>
56 );
57}