/
opt
/
canhelp
/
apps
/
web
/
src
/
app
/
(auth)
/
forgot-password
/
/opt/canhelp/apps/web/src/app/(auth)/forgot-password
mkdir
upload
Name
Size
Mode
Actions
page.tsx
4218
0644
edit
dl
rm
Edit:
/opt/canhelp/apps/web/src/app/(auth)/forgot-password/page.tsx
(4218B)
'use client' import { useState } from 'react' import Link from 'next/link' import { authClient } from '@/lib/auth' import { useLocale } from '@/context/locale' export default function ForgotPasswordPage() { const { t } = useLocale() const [loading, setLoading] = useState(false) const [sent, setSent] = useState(false) const [error, setError] = useState('') async function handleSubmit(e: React.FormEvent<HTMLFormElement>) { e.preventDefault() setError('') setLoading(true) const form = new FormData(e.currentTarget) const email = form.get('email') as string let result: any = null try { result = await authClient.requestPasswordReset({ email, redirectTo: `${window.location.origin}/reset-password`, }) } catch { setSent(true) setLoading(false) return } if (result?.error) { const status = result.error.status // Only surface a real error if it's clearly an SMTP/server failure (5xx). // For 404 / USER_NOT_FOUND we still show the success screen — standard // security practice to prevent account enumeration. if (status && status >= 500) { setError(t('forgot.error.send_failed')) setLoading(false) return } console.warn('[forgot-password] API error (silenced):', result.error) } // Always show the "check your inbox" screen regardless of whether the // email was found, so we don't reveal which addresses are registered. setSent(true) setLoading(false) } return ( <div className="min-h-screen flex items-center justify-center px-4"> <div className="w-full max-w-md"> <div className="text-center mb-8"> <h1 className="text-3xl font-bold text-white">{t('forgot.title')}</h1> <p className="text-white/70 mt-2">{t('forgot.subtitle')}</p> </div> {sent ? ( <div className="bg-white rounded-xl border border-gray-200 p-6 space-y-4 text-center"> <div className="flex justify-center"> <div className="w-14 h-14 rounded-full bg-green-100 flex items-center justify-center"> <svg className="w-7 h-7 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" /> </svg> </div> </div> <h2 className="text-xl font-semibold text-gray-900">{t('forgot.sent.title')}</h2> <p className="text-gray-500 text-sm">{t('forgot.sent.desc')}</p> <Link href="/login" className="inline-block mt-2 text-green-600 hover:underline text-sm font-medium" > {t('forgot.back_to_login')} </Link> </div> ) : ( <form onSubmit={handleSubmit} className="bg-white rounded-xl border border-gray-200 p-6 space-y-4"> {error && ( <div className="bg-red-50 text-red-700 p-3 rounded-lg text-sm">{error}</div> )} <div> <label className="block text-sm font-medium text-gray-700 mb-1">{t('forgot.email')}</label> <input name="email" type="email" required autoComplete="email" className="w-full border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-400" /> </div> <button type="submit" disabled={loading} className="w-full bg-green-600 text-white py-3 rounded-xl font-semibold hover:bg-green-700 disabled:opacity-50" > {loading ? t('forgot.submitting') : t('forgot.submit')} </button> <p className="text-center text-sm text-gray-500"> <Link href="/login" className="text-green-600 hover:underline font-medium"> {t('forgot.back_to_login')} </Link> </p> </form> )} </div> </div> ) }
Save
cmd:
run