From 95400472e6a5adfcc461c41b993180a6698c23ba Mon Sep 17 00:00:00 2001
From: saj
Date: Sun, 18 Oct 2026 18:54:10 +0100
Subject: [PATCH 1/4] fix: let users resend the password reset code with a
cooldown (#528)
Closes #528
Co-Authored-By: Claude Haiku 5.5
---
src/components/auth/ForgotPasswordForm.tsx | 28 +++++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/src/components/auth/ForgotPasswordForm.tsx b/src/components/auth/ForgotPasswordForm.tsx
index 1d69a9a..2f2a01d 100644
--- a/src/components/auth/ForgotPasswordForm.tsx
+++ b/src/components/auth/ForgotPasswordForm.tsx
@@ -1,6 +1,6 @@
"use client";
-import { useState } from "react";
+import { useEffect, useState } from "react";
import Link from "next/link";
import { ApiError } from "@/lib/api";
import { requestPasswordReset, resetPassword, MIN_ACCOUNT_PASSWORD_LENGTH } from "@/lib/passwordApi";
@@ -23,6 +23,13 @@ export function ForgotPasswordForm() {
const [confirm, setConfirm] = useState("");
const [error, setError] = useState(null);
const [busy, setBusy] = useState(false);
+ const [cooldown, setCooldown] = useState(0);
+
+ useEffect(() => {
+ if (cooldown <= 0) return;
+ const timer = setTimeout(() => setCooldown((s) => s - 1), 1000);
+ return () => clearTimeout(timer);
+ }, [cooldown]);
async function onEmail(e: React.FormEvent) {
e.preventDefault();
@@ -52,6 +59,17 @@ export function ForgotPasswordForm() {
setStep("password");
}
+ // Sends a fresh code. The 30 s cooldown matches the signup resend button.
+ async function onResend() {
+ setError(null);
+ try {
+ await requestPasswordReset(email.trim());
+ setCooldown(30);
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Could not resend the code.");
+ }
+ }
+
async function onPassword(e: React.FormEvent) {
e.preventDefault();
if (password.length < MIN_ACCOUNT_PASSWORD_LENGTH) {
@@ -135,6 +153,14 @@ export function ForgotPasswordForm() {
If an account exists for that email, we sent a 6-digit code to it.
+
{errorBox}