SPB Git forge

spb/polyllm

Public
15commits 1branches 0releases
2.2 MBsize
maindefault branch
13 days agolast push
TypeScript 97.4% SQL 1% JavaScript 0.9% CSS 0.6%
4.4 KB · 106 lines tsx
Raw Blame History
1"use client";2import * as React from "react";3import Link from "next/link";4import { useRouter } from "next/navigation";5import { AlertTriangle, LockKeyhole } from "lucide-react";6import { authClient } from "@/lib/auth-client";7import { Button } from "@/components/ui/button";8import { Field } from "@/components/ui/input";9import { toast } from "@/components/ui/toast";10import { AuthCard, FormError } from "./auth-card";11import { PasswordInput } from "./password-input";12import { PasswordStrength, scorePassword } from "./password-strength";13import { humanAuthError, passwordLengthError, type AuthClientError } from "./auth-errors";1415export function ResetForm({ token, errorCode }: { token: string | null; errorCode: string | null }) {16  const router = useRouter();17  const [password, setPassword] = React.useState("");18  const [confirm, setConfirm] = React.useState("");19  const [loading, setLoading] = React.useState(false);20  const [error, setError] = React.useState<string | null>(null);21  const [fieldErrors, setFieldErrors] = React.useState<{ password?: string; confirm?: string }>({});22  const [invalidLink, setInvalidLink] = React.useState<string | null>(errorCode ? humanAuthError({ code: errorCode }, "reset") : token ? null : "This reset link is missing its token. Request a new one.");2324  async function onSubmit(e: React.FormEvent<HTMLFormElement>) {25    e.preventDefault();26    setError(null);27    const fe: typeof fieldErrors = {};28    const pwErr = passwordLengthError(password);29    if (pwErr) fe.password = pwErr;30    else if (scorePassword(password).score < 2) fe.password = "That password is too easy to guess. Try a longer or less common one.";31    if (confirm !== password) fe.confirm = "The passwords don't match.";32    setFieldErrors(fe);33    if (Object.keys(fe).length || !token) return;3435    setLoading(true);36    try {37      const { error: err } = await authClient.resetPassword({ newPassword: password, token });38      if (err) {39        const ae = err as AuthClientError;40        if (ae.code === "INVALID_TOKEN" || ae.code === "TOKEN_EXPIRED") {41          setInvalidLink(humanAuthError(ae, "reset"));42          return;43        }44        setError(humanAuthError(ae, "reset"));45        return;46      }47      toast.success("Password updated", "Sign in with your new password.");48      router.push("/login?notice=reset");49    } catch {50      setError("We couldn't reach the server. Check your connection and try again.");51    } finally {52      setLoading(false);53    }54  }5556  if (invalidLink) {57    return (58      <AuthCard59        icon={<AlertTriangle />}60        title="This link doesn't work"61        description={invalidLink}62        footer={63          <Link href="/login" className="font-medium text-accent underline-offset-4 hover:underline">64            Back to sign in65          </Link>66        }67      >68        <Button asChild size="lg" className="w-full">69          <Link href="/forgot-password">Request a new link</Link>70        </Button>71      </AuthCard>72    );73  }7475  return (76    <AuthCard icon={<LockKeyhole />} title="Choose a new password" description="For your security, all other sessions will be signed out once the password changes.">77      <form onSubmit={onSubmit} noValidate className="space-y-4">78        <FormError id="reset-error">{error}</FormError>79        <Field label="New password" htmlFor="reset-password" error={fieldErrors.password}>80          <PasswordInput81            id="reset-password"82            name="new-password"83            autoComplete="new-password"84            required85            minLength={10}86            maxLength={128}87            value={password}88            onChange={(e) => setPassword(e.target.value)}89            placeholder="10 to 128 characters"90            invalid={!!fieldErrors.password}91            aria-describedby="reset-password-strength"92            autoFocus93          />94          <PasswordStrength password={password} id="reset-password-strength" />95        </Field>96        <Field label="Confirm new password" htmlFor="reset-confirm" error={fieldErrors.confirm}>97          <PasswordInput id="reset-confirm" name="confirm-password" autoComplete="new-password" required value={confirm} onChange={(e) => setConfirm(e.target.value)} placeholder="Type it once more" invalid={!!fieldErrors.confirm} />98        </Field>99        <Button type="submit" size="lg" className="mt-2 w-full" loading={loading}>100          Update password101        </Button>102      </form>103    </AuthCard>104  );105}106