SPB Git forge

spb/fetcha

Public
11commits 1branches 0releases
1.5 MBsize
maindefault branch
16 days agolast push
TypeScript 97.5% SQL 1.4% Python 0.8%
2.9 KB · 68 lines tsx
Raw Blame History
1"use client";2import * as React from "react";3import Link from "next/link";4import { useRouter, useSearchParams } from "next/navigation";5import { authClient } from "@/lib/auth-client";6import { AuthCard } from "@/components/auth/auth-card";7import { PasswordInput, PasswordRequirements, passwordStrong } from "@/components/auth/password-input";8import { Button } from "@/components/ui/button";9import { Field, FieldError, Label } from "@/components/ui/label";10import { Alert } from "@/components/ui/alert";1112function ResetForm() {13  const router = useRouter();14  const params = useSearchParams();15  const token = params.get("token");16  const invalid = params.get("error") === "INVALID_TOKEN" || !token;17  const [password, setPassword] = React.useState("");18  const [confirm, setConfirm] = React.useState("");19  const [error, setError] = React.useState<string | null>(null);20  const [loading, setLoading] = React.useState(false);2122  async function onSubmit(e: React.FormEvent) {23    e.preventDefault();24    if (!passwordStrong(password)) return setError("Choose a stronger password.");25    if (password !== confirm) return setError("Passwords do not match.");26    setLoading(true);27    const { error } = await authClient.resetPassword({ newPassword: password, token: token! });28    setLoading(false);29    if (error) return setError(error.message ?? "This link is invalid or expired.");30    router.push("/login?reset=1");31  }3233  if (invalid) {34    return (35      <AuthCard title="Link expired" description="This password reset link is invalid or has expired." footer={<Link href="/forgot-password" className="font-medium text-fg underline-offset-4 hover:underline">Request a new link</Link>}>36        <Alert variant="warning">Reset links are valid for one hour and can only be used once.</Alert>37      </AuthCard>38    );39  }4041  return (42    <AuthCard title="Choose a new password" description="All existing sessions will be signed out.">43      <form onSubmit={onSubmit} className="grid gap-4" noValidate>44        {error ? <Alert variant="danger">{error}</Alert> : null}45        <Field>46          <Label htmlFor="password">New password</Label>47          <PasswordInput id="password" autoComplete="new-password" required value={password} onChange={(e) => setPassword(e.target.value)} autoFocus />48          <PasswordRequirements password={password} />49        </Field>50        <Field>51          <Label htmlFor="confirm">Confirm password</Label>52          <PasswordInput id="confirm" autoComplete="new-password" required value={confirm} onChange={(e) => setConfirm(e.target.value)} />53          <FieldError>{confirm && confirm !== password ? "Passwords do not match." : null}</FieldError>54        </Field>55        <Button type="submit" size="lg" loading={loading} className="w-full">Update password</Button>56      </form>57    </AuthCard>58  );59}6061export default function ResetPasswordPage() {62  return (63    <React.Suspense fallback={null}>64      <ResetForm />65    </React.Suspense>66  );67}68