'use client' import { useState, useEffect, Suspense } from 'react' // CHANGED: add Suspense import { useSearchParams, useRouter } from 'next/navigation' import PageLayout from '../components/PageLayout' import Waves from '../components/background/waves' import { ToastProvider, useToast } from '../components/toast/toastComponent' function PasswordResetPageInner() { const searchParams = useSearchParams() const router = useRouter() const token = searchParams.get('token') // Email request state const [email, setEmail] = useState('') const [requestLoading, setRequestLoading] = useState(false) const [requestSuccess, setRequestSuccess] = useState(false) const [requestError, setRequestError] = useState('') // Reset-with-token state const [password, setPassword] = useState('') const [confirmPassword, setConfirmPassword] = useState('') const [showPassword, setShowPassword] = useState(false) const [resetLoading, setResetLoading] = useState(false) const [resetSuccess, setResetSuccess] = useState(false) const [resetError, setResetError] = useState('') const { showToast } = useToast() // Basic validators const validEmail = (val: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val) const validPassword = (val: string) => /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{8,}$/.test(val) // Auto-redirect after successful password reset useEffect(() => { if (resetSuccess) { const t = setTimeout(() => router.push('/login'), 1800) return () => clearTimeout(t) } }, [resetSuccess, router]) const handleRequestSubmit = async (e: React.FormEvent) => { e.preventDefault() if (requestLoading) return if (!validEmail(email)) { const msg = 'Please enter a valid email address.' setRequestError(msg) showToast({ variant: 'error', title: 'Invalid email', message: msg, }) return } setRequestError('') setRequestLoading(true) try { // TODO: call API endpoint: POST /auth/password-reset/request await new Promise(r => setTimeout(r, 1100)) setRequestSuccess(true) showToast({ variant: 'success', title: 'Password reset email', message: 'If this email exists, a reset link has been sent.', }) } catch { const msg = 'Request failed. Please try again.' setRequestError(msg) showToast({ variant: 'error', title: 'Request failed', message: msg, }) } finally { setRequestLoading(false) } } const handleResetSubmit = async (e: React.FormEvent) => { e.preventDefault() if (resetLoading) return if (!validPassword(password)) { const msg = 'Password does not meet the requirements.' setResetError(msg) showToast({ variant: 'error', title: 'Invalid password', message: msg, }) return } if (password !== confirmPassword) { const msg = 'Passwords do not match.' setResetError(msg) showToast({ variant: 'error', title: 'Passwords do not match', message: msg, }) return } setResetError('') setResetLoading(true) try { // TODO: call API endpoint: POST /auth/password-reset/confirm { token, password } await new Promise(r => setTimeout(r, 1200)) setResetSuccess(true) showToast({ variant: 'success', title: 'Password updated', message: 'Your password has been changed. Redirecting to login...', }) } catch { const msg = 'Reset failed. Please try again.' setResetError(msg) showToast({ variant: 'error', title: 'Reset failed', message: msg, }) } finally { setResetLoading(false) } } const passwordHints = [ { label: 'At least 8 characters', pass: password.length >= 8 }, { label: 'Uppercase letter (A-Z)', pass: /[A-Z]/.test(password) }, { label: 'Lowercase letter (a-z)', pass: /[a-z]/.test(password) }, { label: 'Number (0-9)', pass: /\d/.test(password) }, { label: 'Special character (!@#$...)', pass: /[\W_]/.test(password) } ] return ( {/* push content a bit further down while still centering */} {/* Widened container to match header */} {/* Translucent form card (matching login glass style) */} Reset password {!token ? 'Request a link to reset your password.' : 'Set a new secure password.'} {!token && ( Email address { setEmail(e.target.value); setRequestError(''); setRequestSuccess(false)}} className="w-full rounded-xl border border-white/40 bg-white/60 px-4 py-3 text-[#0F172A] placeholder-slate-600/80 shadow-sm focus:outline-none focus:ring-2 focus:ring-[#8D6B1D]/80 focus:border-[#8D6B1D] transition" placeholder="your.email@example.com" required /> {requestError && ( {requestError} )} {requestSuccess && ( Email sent (if the address exists). Please check your inbox. )} {requestLoading ? ( <> Senden... > ) : ( 'Request reset link' )} Remember it now?{' '} router.push('/login')} className="text-[#8D6B1D] hover:text-[#7A5E1A] hover:underline font-medium" > Back to login )} {token && ( New password { setPassword(e.target.value); setResetError(''); setResetSuccess(false)}} className="w-full rounded-xl border border-white/40 bg-white/60 px-4 py-3 pr-12 text-[#0F172A] placeholder-slate-600/80 shadow-sm focus:outline-none focus:ring-2 focus:ring-[#8D6B1D]/80 focus:border-[#8D6B1D] transition" placeholder="Your new password" required /> setShowPassword(p => !p)} className="absolute inset-y-0 right-0 px-3 text-xs font-medium text-indigo-700 hover:underline" > {showPassword ? 'Hide' : 'Show'} {passwordHints.map(h => ( {h.label} ))} Confirm password { setConfirmPassword(e.target.value); setResetError(''); setResetSuccess(false)}} className="w-full rounded-xl border border-white/40 bg-white/60 px-4 py-3 text-[#0F172A] placeholder-slate-600/80 shadow-sm focus:outline-none focus:ring-2 focus:ring-[#8D6B1D]/80 focus:border-[#8D6B1D] transition" placeholder="Confirm password" required /> {confirmPassword && password !== confirmPassword && ( Passwords do not match. )} {resetError && ( {resetError} )} {resetSuccess && ( Password saved. Redirecting to login... )} {resetLoading ? ( <> Saving... > ) : ( 'Set new password' )} Link expired?{' '} router.push('/password-reset')} className="text-indigo-600 dark:text-indigo-400 hover:underline font-medium" > Request again )} ) } export default function PasswordResetPage() { return ( Loading... } > ) }
{!token ? 'Request a link to reset your password.' : 'Set a new secure password.'}
Passwords do not match.
Loading...