"use client"; import { FormEvent, useEffect, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { AlertCircle, ArrowRight, CheckCircle2, Eye, EyeOff, Lock } from "lucide-react"; import { getSupabaseBrowserClient, hasSupabasePublicEnv, } from "@/lib/supabase/client"; import { useI18n } from "@/hooks/useI18n"; type ResetPasswordClientProps = { nextPath: string; }; export function ResetPasswordClient({ nextPath }: ResetPasswordClientProps) { const router = useRouter(); const { locale } = useI18n(); const isEn = locale === "en-US"; const supabaseReady = hasSupabasePublicEnv(); const [password, setPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState(""); const [showPassword, setShowPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); const [checkingSession, setCheckingSession] = useState(true); const [hasSession, setHasSession] = useState(false); const [loading, setLoading] = useState(false); const [errorText, setErrorText] = useState(""); const [infoText, setInfoText] = useState(""); const copy = { title: isEn ? "Set a new password" : "设置新密码", subtitle: isEn ? "Create a password for your PolyWeather account." : "为您的 PolyWeather 账户创建一个新密码。", password: isEn ? "New password" : "新密码", confirmPassword: isEn ? "Confirm password" : "确认密码", passwordPlaceholder: isEn ? "At least 6 characters" : "至少 6 位字符", confirmPlaceholder: isEn ? "Enter it again" : "再次输入新密码", submit: isEn ? "Update password" : "更新密码", checking: isEn ? "Checking reset link..." : "正在检查重置链接...", expired: isEn ? "This reset link is expired or invalid. Please request a new password reset email." : "重置链接已过期或无效,请重新发送密码重置邮件。", supabaseMissing: isEn ? "Supabase is not configured. Password reset is unavailable." : "Supabase 未配置,无法重置密码。", passwordTooShort: isEn ? "Password must be at least 6 characters." : "密码至少需要 6 位字符。", passwordMismatch: isEn ? "The two passwords do not match." : "两次输入的密码不一致。", success: isEn ? "Password updated. Redirecting..." : "密码已更新,正在跳转...", backLogin: isEn ? "Back to sign in" : "返回登录", } as const; useEffect(() => { if (!supabaseReady) { setErrorText(copy.supabaseMissing); setCheckingSession(false); return; } const run = async () => { const supabase = getSupabaseBrowserClient(); const { data: { session }, } = await supabase.auth.getSession(); const hasRecoverySession = Boolean(session?.user); setHasSession(hasRecoverySession); if (!hasRecoverySession) { setErrorText(copy.expired); } setCheckingSession(false); }; void run(); }, [copy.expired, copy.supabaseMissing, supabaseReady]); const onSubmit = async (event: FormEvent) => { event.preventDefault(); setErrorText(""); setInfoText(""); if (!hasSession) { setErrorText(copy.expired); return; } if (password.length < 6) { setErrorText(copy.passwordTooShort); return; } if (password !== confirmPassword) { setErrorText(copy.passwordMismatch); return; } setLoading(true); try { const supabase = getSupabaseBrowserClient(); const { error } = await supabase.auth.updateUser({ password }); if (error) { setErrorText(error.message); return; } setInfoText(copy.success); window.setTimeout(() => router.replace(nextPath), 700); } finally { setLoading(false); } }; return (
PolyWeather

{copy.title}

{copy.subtitle}

{checkingSession ? (
{copy.checking}
) : (
void onSubmit(event)} className="space-y-5">
setPassword(event.target.value)} placeholder={copy.passwordPlaceholder} className="w-full rounded-xl border border-slate-200 bg-slate-50/70 py-3 pl-11 pr-11 text-sm text-slate-900 placeholder:text-slate-400 transition-all focus:border-blue-500 focus:bg-white focus:outline-none focus:ring-4 focus:ring-blue-500/10 disabled:cursor-not-allowed disabled:opacity-60" />
setConfirmPassword(event.target.value)} placeholder={copy.confirmPlaceholder} className="w-full rounded-xl border border-slate-200 bg-slate-50/70 py-3 pl-11 pr-11 text-sm text-slate-900 placeholder:text-slate-400 transition-all focus:border-blue-500 focus:bg-white focus:outline-none focus:ring-4 focus:ring-blue-500/10 disabled:cursor-not-allowed disabled:opacity-60" />
)} {errorText ? (
{errorText}
) : null} {infoText ? (
{infoText}
) : null} {copy.backLogin}
); }