diff --git a/components/auth/ResetPasswordPage.tsx b/components/auth/ResetPasswordPage.tsx new file mode 100644 index 0000000..79b4aeb --- /dev/null +++ b/components/auth/ResetPasswordPage.tsx @@ -0,0 +1,188 @@ +import React, { useMemo, useState } from 'react'; +import { authService } from '../../services/auth/auth.service'; +import { PillIcon } from '../icons/Icons'; + +const MIN_PASSWORD_LENGTH = 6; + +const ResetPasswordPage: React.FC = () => { + const token = useMemo(() => { + if (typeof window === 'undefined') return null; + const params = new URLSearchParams(window.location.search); + return params.get('token'); + }, []); + + const [password, setPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + const [error, setError] = useState(null); + const [status, setStatus] = useState<'idle' | 'submitting' | 'success'>( + 'idle' + ); + + if (!token) { + return ( +
+
+

+ Password Reset Link Invalid +

+

+ We could not find a valid reset token. Please return to the sign in + page and request a new password reset email. +

+ +
+
+ ); + } + + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + setError(null); + + if (password.length < MIN_PASSWORD_LENGTH) { + setError('Password must be at least 6 characters long.'); + return; + } + + if (password !== confirmPassword) { + setError('Passwords do not match.'); + return; + } + + try { + setStatus('submitting'); + await authService.resetPassword(token, password); + setStatus('success'); + } catch (err) { + setError( + err instanceof Error + ? err.message + : 'Unable to reset password. Please try again.' + ); + setStatus('idle'); + } + }; + + if (status === 'success') { + return ( +
+
+
+ +
+

+ Password Updated +

+

+ Your password has been reset successfully. You can now sign in with + your new credentials. +

+ +
+
+ ); + } + + return ( +
+
+
+
+ +
+

+ Reset Password +

+

+ Choose a new password for your account. +

+
+ +
+
+
+ + setPassword(event.target.value)} + className='mt-1 block w-full px-3 py-2 border border-slate-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm bg-white dark:bg-slate-700 dark:border-slate-600 dark:placeholder-slate-400 dark:text-white' + placeholder='Enter a new password' + /> +
+ +
+ + setConfirmPassword(event.target.value)} + className='mt-1 block w-full px-3 py-2 border border-slate-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm bg-white dark:bg-slate-700 dark:border-slate-600 dark:placeholder-slate-400 dark:text-white' + placeholder='Re-enter your password' + /> +
+ + {error && ( +
+ {error} +
+ )} + + +
+ +
+
+
+ ); +}; + +export default ResetPasswordPage; diff --git a/components/auth/index.ts b/components/auth/index.ts index 081e12c..7155113 100644 --- a/components/auth/index.ts +++ b/components/auth/index.ts @@ -2,3 +2,4 @@ export { default as AuthPage } from './AuthPage'; export { default as AvatarDropdown } from './AvatarDropdown'; export { default as ChangePasswordModal } from './ChangePasswordModal'; +export { default as ResetPasswordPage } from './ResetPasswordPage';