TypeScript 97.4%
SQL 1%
JavaScript 0.9%
CSS 0.6%
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