Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion client/src/app/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
import { Suspense } from 'react';
import { Suspense, lazy } from 'react';
import { Route, Routes } from 'react-router-dom';
import Loader from '../../shared/components/molecules/loader';
import { LOADING } from './constants';
import { LoginLazyComponent } from '../components';

// Add lazy loading for ResetPassword component
const ResetPasswordLazyComponent = lazy(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put it in components/index.tsx and import from there similar to login page

() => import('../../modules/auth/v1/reset-password')
);

export function AppUnProtectedRoutes() {
return (
<Routes>
<Route
path="/reset-password"
element={
<Suspense fallback={<Loader size={32} secondary={LOADING} />}>
<ResetPasswordLazyComponent />
</Suspense>
}
/>
<Route
path="*"
element={
Expand Down
24 changes: 24 additions & 0 deletions client/src/modules/auth/v1/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import A2ZButton from '../../../shared/components/atoms/button';
import { useUserAuthForm } from './hooks';
import { useState } from 'react';
import A2ZTypography from '../../../shared/components/atoms/typography';
import { useNavigate } from 'react-router-dom';

const StyledSection = styled('section')(({ theme }) => ({
paddingTop: theme.spacing(4),
Expand Down Expand Up @@ -44,6 +45,7 @@ const StyledFooter = styled('p')(({ theme }) => ({
const UserAuthForm = () => {
const [formType, setFormType] = useState<string>('login');
const { loading, handleSubmit } = useUserAuthForm({ type: formType });
const navigate = useNavigate();

return (
<StyledSection>
Expand Down Expand Up @@ -90,6 +92,28 @@ const UserAuthForm = () => {
icon={<PasswordIcon />}
/>

{/* Forgot Password Link - Only shown on login */}
{formType === 'login' && (
<Box sx={{ width: '100%', textAlign: 'right', mt: 0.5 }}>
<A2ZTypography
text="Forgot Password?"
component="span"
props={{
onClick: () => navigate('/reset-password'),
sx: {
fontSize: '0.9rem',
textDecoration: 'underline',
color: 'text.secondary',
cursor: 'pointer',
'&:hover': {
opacity: 0.8,
},
},
}}
/>
</Box>
)}

<A2ZButton
type="submit"
sx={{
Expand Down
134 changes: 134 additions & 0 deletions client/src/modules/auth/v1/reset-password/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// client/src/modules/auth/v1/reset-password/index.tsx

import { Box, styled, Typography } from '@mui/material';
import InputBox from '../../../../shared/components/atoms/input-box';
import EmailIcon from '@mui/icons-material/Email';
import A2ZButton from '../../../../shared/components/atoms/button';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import A2ZTypography from '../../../../shared/components/atoms/typography';

const StyledSection = styled('section')(({ theme }) => ({
paddingTop: theme.spacing(4),
paddingBottom: theme.spacing(4),
paddingLeft: '5vw',
paddingRight: '5vw',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
minHeight: '100vh',
}));

const StyledForm = styled('form')(() => ({
width: '80%',
maxWidth: 400,
}));

const StyledTitle = styled(Typography)(({ theme }) => ({
fontSize: '2.5rem',
fontFamily: 'Gelasio, serif',
textTransform: 'capitalize',
textAlign: 'center',
marginBottom: theme.spacing(6),
}));

const StyledFooter = styled('p')(({ theme }) => ({
marginTop: theme.spacing(6),
color: theme.palette.text.secondary,
fontSize: '1.125rem',
textAlign: 'center',
}));

const ResetPassword = () => {
const [loading, setLoading] = useState(false);
const navigate = useNavigate();

const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setLoading(true);

const formData = new FormData(e.currentTarget);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid form data, try to use state management of email

const email = formData.get('email');

// TODO: Implement password reset API call here
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implement api call

console.log('Reset password for:', email);

// Simulate API call
setTimeout(() => {
setLoading(false);
alert('Password reset link sent to your email!');
}, 2000);
};

return (
<StyledSection>
<StyledForm onSubmit={handleSubmit}>
<StyledTitle>Reset Password</StyledTitle>

<Box
sx={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: 1,
}}
>
<Typography
sx={{
mb: 2,
textAlign: 'center',
color: 'text.secondary',
}}
>
Enter your email address and we'll send you a link to reset your
password.
Comment on lines +84 to +85
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update the text by Enter your email address to receive a link to reset your password.

</Typography>

<InputBox
id="reset-email"
name="email"
type="email"
placeholder="Email"
fullWidth
icon={<EmailIcon />}
/>

<A2ZButton
type="submit"
sx={{
mt: 2,
width: '100%',
}}
loading={loading}
>
Send Reset Link
</A2ZButton>
</Box>

<StyledFooter>
Remember your password?{' '}
<A2ZTypography
text="Sign in here"
component="span"
props={{
onClick: () => navigate('/'),
sx: {
fontSize: '1.125rem',
marginLeft: 1,
textDecoration: 'underline',
color: 'inherit',
cursor: 'pointer',
'&:hover': {
opacity: 0.8,
},
},
}}
/>
</StyledFooter>
</StyledForm>
</StyledSection>
);
};

export default ResetPassword;
Loading