Files
worklenz/worklenz-frontend/src/pages/auth/forgot-password-page.tsx
chamikaJ 8825b0410a init
2025-04-17 18:28:54 +05:30

159 lines
4.8 KiB
TypeScript

import { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useMediaQuery } from 'react-responsive';
import { Link, useNavigate } from 'react-router-dom';
import { UserOutlined } from '@ant-design/icons';
import { Form, Card, Input, Flex, Button, Typography, Result } from 'antd/es';
import PageHeader from '@components/AuthPageHeader';
import { useDocumentTitle } from '@/hooks/useDoumentTItle';
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
import { evt_forgot_password_page_visit, evt_reset_password_click } from '@/shared/worklenz-analytics-events';
import { resetPassword, verifyAuthentication } from '@features/auth/authSlice';
import { useAppDispatch } from '@/hooks/useAppDispatch';
import { setSession } from '@/utils/session-helper';
import { setUser } from '@features/user/userSlice';
import logger from '@/utils/errorLogger';
const ForgotPasswordPage = () => {
const [form] = Form.useForm();
const [isLoading, setIsLoading] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
const [urlParams, setUrlParams] = useState({
teamId: '',
});
const navigate = useNavigate();
const { trackMixpanelEvent } = useMixpanelTracking();
useDocumentTitle('Forgot Password');
const dispatch = useAppDispatch();
// Localization
const { t } = useTranslation('auth/forgot-password');
// media queries from react-responsive package
const isMobile = useMediaQuery({ query: '(max-width: 576px)' });
useEffect(() => {
trackMixpanelEvent(evt_forgot_password_page_visit);
const searchParams = new URLSearchParams(window.location.search);
setUrlParams({
teamId: searchParams.get('team') || '',
});
const verifyAuthStatus = async () => {
try {
const session = await dispatch(verifyAuthentication()).unwrap();
if (session?.authenticated) {
setSession(session.user);
dispatch(setUser(session.user));
navigate('/worklenz/home');
}
} catch (error) {
logger.error('Failed to verify authentication status', error);
}
};
void verifyAuthStatus();
}, [dispatch, navigate, trackMixpanelEvent]);
const onFinish = useCallback(
async (values: any) => {
if (values.email.trim() === '') return;
try {
setIsLoading(true);
const result = await dispatch(resetPassword(values.email)).unwrap();
if (result.done) {
trackMixpanelEvent(evt_reset_password_click);
setIsSuccess(true);
}
} catch (error) {
logger.error('Failed to reset password', error);
} finally {
setIsLoading(false);
}
},
[dispatch, t]
);
return (
<Card
style={{
width: '100%',
boxShadow: 'none',
}}
styles={{
body: {
paddingInline: isMobile ? 24 : 48,
},
}}
variant="outlined"
>
{isSuccess ? (
<Result status="success" title={t('successTitle')} subTitle={t('successMessage')} />
) : (
<>
<PageHeader description={t('headerDescription')} />
<Form
name="forgot-password"
form={form}
layout="vertical"
autoComplete="off"
requiredMark="optional"
initialValues={{ remember: true }}
onFinish={onFinish}
style={{ width: '100%' }}
>
<Form.Item
name="email"
rules={[
{
required: true,
type: 'email',
message: t('emailRequired'),
},
]}
>
<Input
prefix={<UserOutlined />}
placeholder={t('emailPlaceholder')}
size="large"
style={{ borderRadius: 4 }}
/>
</Form.Item>
<Form.Item>
<Flex vertical gap={8}>
<Button
block
type="primary"
htmlType="submit"
size="large"
loading={isLoading}
style={{ borderRadius: 4 }}
>
{t('resetPasswordButton')}
</Button>
<Typography.Text style={{ textAlign: 'center' }}>{t('orText')}</Typography.Text>
<Link to="/auth/login">
<Button
block
type="default"
size="large"
style={{
borderRadius: 4,
}}
>
{t('returnToLoginButton')}
</Button>
</Link>
</Flex>
</Form.Item>
</Form>
</>
)}
</Card>
);
};
export default ForgotPasswordPage;