Yaltopia-FIFA/components/auth/forgot-password-form.tsx
Kirubel-Kibru-Yaltopia 89440985f1
Some checks failed
Deploy to Cloudflare Workers / deploy (push) Has been cancelled
x
2026-05-24 21:46:10 +03:00

147 lines
4.9 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import Link from "next/link";
import { createClient } from "@/lib/supabase/client";
import { formatAuthNetworkError } from "@/lib/supabase/env";
import { formatAuthError, isAuthRateLimitError } from "@/lib/supabase/auth-errors";
import {
formatCooldownSeconds,
getResetEmailCooldownRemainingMs,
RESET_EMAIL_COOLDOWN_MS,
startResetEmailCooldown,
} from "@/lib/auth/reset-email-cooldown";
import type { PortalRole } from "@/lib/auth/roles";
import { loginPathForRole } from "@/lib/auth/roles";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
function resetRedirectUrl(portal: PortalRole) {
const origin =
typeof window !== "undefined" ? window.location.origin : "";
const next = `/reset-password?portal=${portal}`;
return `${origin}/auth/callback?next=${encodeURIComponent(next)}`;
}
export function ForgotPasswordForm({ portal }: { portal: PortalRole }) {
const [email, setEmail] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [sent, setSent] = useState(false);
const [cooldownMs, setCooldownMs] = useState(0);
const loginHref = loginPathForRole(portal);
const portalLabel = portal === "league_master" ? "League Master" : "Team Manager";
useEffect(() => {
setCooldownMs(getResetEmailCooldownRemainingMs());
const id = window.setInterval(() => {
const remaining = getResetEmailCooldownRemainingMs();
setCooldownMs(remaining);
if (remaining <= 0) window.clearInterval(id);
}, 1000);
return () => window.clearInterval(id);
}, [sent, error]);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
const remaining = getResetEmailCooldownRemainingMs();
if (remaining > 0) {
setError(
`Please wait ${formatCooldownSeconds(remaining)} seconds before requesting another email.`
);
return;
}
setLoading(true);
setError(null);
try {
const supabase = createClient();
const { error: resetError } = await supabase.auth.resetPasswordForEmail(
email.trim(),
{ redirectTo: resetRedirectUrl(portal) }
);
if (resetError) {
setError(formatAuthError(resetError));
if (isAuthRateLimitError(resetError)) {
startResetEmailCooldown();
setCooldownMs(RESET_EMAIL_COOLDOWN_MS);
}
return;
}
startResetEmailCooldown();
setCooldownMs(RESET_EMAIL_COOLDOWN_MS);
setSent(true);
} catch (err) {
setError(formatAuthNetworkError(err));
} finally {
setLoading(false);
}
}
const submitDisabled = loading || cooldownMs > 0;
if (sent) {
return (
<div className="space-y-4 text-center">
<p className="text-sm text-muted-foreground">
If an account exists for{" "}
<strong className="text-foreground">{email}</strong>, we sent a reset
link. Check your inbox and spam folder.
</p>
<p className="text-xs text-muted-foreground">
The link opens a page to set a new password, then signs you into the{" "}
{portalLabel} portal.
</p>
<p className="text-xs text-muted-foreground">
Did not get it? Wait at least an hour (Supabase email limits) before
trying again, or use Authentication Users in the Supabase dashboard.
</p>
<Button variant="outline" className="w-full" asChild>
<Link href={loginHref}>Back to sign in</Link>
</Button>
</div>
);
}
return (
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
autoComplete="email"
placeholder="you@example.com"
/>
</div>
{error && (
<p className="whitespace-pre-line text-sm text-red-400">{error}</p>
)}
{cooldownMs > 0 && !error && (
<p className="text-xs text-muted-foreground">
You can request another email in{" "}
{formatCooldownSeconds(cooldownMs)}s.
</p>
)}
<Button type="submit" className="w-full" disabled={submitDisabled}>
{loading
? "Sending…"
: cooldownMs > 0
? `Wait ${formatCooldownSeconds(cooldownMs)}s`
: "Send reset link"}
</Button>
<p className="text-center text-sm text-muted-foreground">
<Link href={loginHref} className="hover:underline">
Back to sign in
</Link>
</p>
</form>
);
}