Skip to content

Commit 14ba52a

Browse files
committed
added reset password
1 parent 4b9536c commit 14ba52a

File tree

5 files changed

+253
-55
lines changed

5 files changed

+253
-55
lines changed

src/components/common/validation/validation.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ export const isValidEmail = (email: string) => {
1717
};
1818

1919
export const validatePassword = (pwd :string) => {
20-
if (!isvalidpassword(pwd)) return 'At least 6 characters including a number, uppercase, and lowercase letter';
20+
if (!isvalidpassword(pwd)) return 'At least 6 characters including a number, and lowercase letter';
2121
return '';
2222
};
2323

2424
const isvalidpassword = (newPassword: string) => {
25-
const passwordregex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{6,}$/;
25+
const passwordregex = /^(?=.*[a-z])(?=.*\d)[a-zA-Z\d]{6,}$/;
2626
return passwordregex.test(newPassword);
2727
};

src/pages/resetPassword.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import SEO from '@/components/common/SEO'
2+
import React, { ReactElement, useEffect } from 'react';
3+
import Layout from '@/components/global/Layout';
4+
import Recovery from "../views/recover/recover"
5+
import ReactGA from 'react-ga4';
6+
7+
8+
function Recover () {
9+
useEffect(() => {
10+
ReactGA.send({ hitType: 'Recover', page: window.location.pathname });
11+
}, []);
12+
return (
13+
<div>
14+
<SEO/>
15+
<Recovery />
16+
</div>
17+
)
18+
}
19+
20+
export default Recover;
21+
22+
Recover.getLayout = (page: ReactElement) => <Layout>{page}</Layout>;

src/views/login/login.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {signIn, useSession} from 'next-auth/react';
77
import {useRouter} from 'next/router';
88
import Spinner from '@/components/common/Spinner';
99
import {mockProviders} from 'next-auth/client/__tests__/helpers/mocks';
10-
import callbackUrl = mockProviders.github.callbackUrl;
1110
import {TextField} from '@mui/material';
1211

1312
const LoginComponent = () => {
@@ -23,7 +22,6 @@ const LoginComponent = () => {
2322
const handleLoginWithGoogle = async () => {
2423
try {
2524
setLoading(true);
26-
2725
const googleSignInResponse = await signIn('cognito_google', {
2826
redirect: false,
2927
callbackUrl: `http://localhost:3000${query?.callBack}`,
@@ -130,10 +128,13 @@ const LoginComponent = () => {
130128
>
131129
LOGIN
132130
</button>
133-
<p className="text-xl text-center text-black font-weight-bold mt-9"
134-
style={{color: '#1976D2', fontSize: '14px'}}>
135-
Reset Password
136-
</p>
131+
132+
<Link href={`/resetPassword`}>
133+
<p className="mt-5 text-xl text-center text-black font-weight-bold" style={{fontSize: '14px', color: '#1976D2'}}>
134+
Reset Password
135+
</p>
136+
</Link>
137+
137138
<Link href={`/register?callBack=${router.query.callBack ? router.query.callBack : '/'}`}>
138139
<p className="mt-5 text-xl text-center text-black font-weight-bold" style={{fontSize: '14px'}}>
139140
No account? <span style={{color: '#1976D2'}}>Create one</span>

src/views/recover/recover.tsx

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
import React, { useState } from 'react';
2+
import Image from 'next/image';
3+
import logo from '../../../public/images/logo.png';
4+
import Link from 'next/link';
5+
import MiniFooter from '@/components/global/minifooter';
6+
import {Amplify, Auth} from "aws-amplify";
7+
import Spinner from '@/components/common/Spinner';
8+
import { TextField } from '@mui/material';
9+
import { isValidEmail, validateEmail, validatePassword } from '@/components/common/validation/validation';
10+
11+
const COGNITO_REGION = process.env.NEXT_PUBLIC_COGNITO_REGION;
12+
const LOGIN_REGISTER_COGNITO_CLIENT_ID = process.env.NEXT_PUBLIC_LOGIN_REGISTER_COGNITO_CLIENT_ID;
13+
const COGNITO_USER_POOL_ID = process.env.NEXT_PUBLIC_COGNITO_USER_POOL_ID;
14+
15+
const Recover = () => {
16+
const validationObject = {
17+
email: { error: false, fb: false, msg: '' },
18+
newPassword: { error: false, fb: false, msg: '' },
19+
confirmPassword: { error: false, fb: false, msg: '' },
20+
verificationCode: { error: false, fb: false, msg: '' },
21+
};
22+
const [isValid, setValidate] = useState(validationObject);
23+
const [email, setEmail] = useState('');
24+
const [activeConfirmCode, setActiveConfirmCode] = useState(false);
25+
const [successMessage, setSuccessMessage] = useState('');
26+
const [errorMessage, setErrorMessage] = useState('');
27+
const [loading, setLoading] = useState(false);
28+
const [user, setUser] = useState({
29+
verificationCode: '',
30+
newPassword: '',
31+
confirmPassword: '',
32+
});
33+
34+
const awsAmplifyConfig = {
35+
mandatorySignId: true,
36+
region: COGNITO_REGION,
37+
userPoolId: COGNITO_USER_POOL_ID,
38+
userPoolWebClientId: LOGIN_REGISTER_COGNITO_CLIENT_ID
39+
};
40+
Amplify.configure(awsAmplifyConfig);
41+
42+
const handleInputChange = (e: any) => {
43+
const { name, value } = e.target;
44+
setUser({ ...user, [name]: value });
45+
};
46+
47+
const handleValidation = ({ target }: any) => {
48+
const { value, name, type } = target;
49+
const { newPassword } = user;
50+
let error = !value;
51+
let msg = '';
52+
if (!value) {
53+
msg = `${name} is required`;
54+
} else if (name === 'email') {
55+
msg = validateEmail(value);
56+
error = !isValidEmail(value);
57+
} else if (type === 'password') {
58+
msg = validatePassword(value);
59+
if(msg) error = true
60+
if (name === 'confirmPassword' && newPassword !== value) {
61+
error = true
62+
msg ='Password does not match'
63+
}
64+
}
65+
setValidate({ ...isValid, [name]: { error, msg } });
66+
};
67+
68+
const resetPassword = async (email: string) => {
69+
try {
70+
await Auth.forgotPassword(email);
71+
return { success: true };
72+
} catch (err: any) {
73+
console.error('Error requesting password reset:', err);
74+
return { success: false, error: err?.message };
75+
}
76+
};
77+
78+
const handleResetPassword = async () => {
79+
setLoading(true);
80+
const resetResult = await resetPassword(email);
81+
setLoading(false);
82+
if (resetResult.success) {
83+
setSuccessMessage('Verification code sent to your email');
84+
setActiveConfirmCode(true);
85+
} else {
86+
setErrorMessage(resetResult.error || 'An unexpected error occurred');
87+
}
88+
};
89+
90+
async function handleRest() {
91+
if(activeConfirmCode) {
92+
await handleConfirmPwd()
93+
} else if (!email || validateEmail(email)) {
94+
setErrorMessage(isValid.email.msg);
95+
} else {
96+
setErrorMessage('');
97+
return handleResetPassword()
98+
}
99+
}
100+
101+
async function handleConfirmPwd() {
102+
const {verificationCode, confirmPassword, newPassword} = user;
103+
if(verificationCode && newPassword === confirmPassword ) {
104+
setLoading(true);
105+
try {
106+
await Auth.forgotPasswordSubmit(email, verificationCode, newPassword);
107+
setLoading(false);
108+
setActiveConfirmCode(false);
109+
setSuccessMessage('Password reset successfully');
110+
} catch (error) {
111+
console.error('Error resetting password:', error);
112+
}
113+
} else {
114+
setErrorMessage( 'Please enter valid data ');
115+
}
116+
setEmail('')
117+
setLoading(false);
118+
}
119+
120+
return (
121+
<div className="main-box bg-dark-600" style={{ height: 'auto !important', minHeight: '100vh' }}>
122+
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
123+
<Image src={logo} alt="logo" className="mt-10 text-center" />
124+
<div className="whitebox bg-white rounded px-10 mt-11 py-5" style={{ width: '464px' }}>
125+
<p className="text-black text-xl font-weight-bold mt-4 text-center" style={{ fontSize: '1.4rem' }}>
126+
Reset Password
127+
</p>
128+
{errorMessage && (
129+
<div className="font-regular mt-5 relative block w-full rounded-lg bg-red-500 p-4 text-base leading-5 text-white opacity-100">
130+
{errorMessage}
131+
</div>
132+
)}
133+
{successMessage && (
134+
<div className="font-regular mt-5 relative block w-full rounded-lg bg-green-500 p-4 text-base leading-5 text-white opacity-100">
135+
{successMessage}
136+
</div>
137+
)}
138+
{loading && (
139+
<div className={'align-items-center d-flex flex justify-center mt-3'}>
140+
<Spinner height={'1rem'} width={'1rem'} />
141+
</div>
142+
)}
143+
{!activeConfirmCode ? (
144+
<TextField
145+
label="Email Address"
146+
id="emailAddress"
147+
size="small"
148+
name="email"
149+
variant="standard"
150+
className="w-full mb-6 mt-5"
151+
value={email}
152+
error={isValid['email']?.error}
153+
helperText={isValid['email']?.msg}
154+
onChange={(e: any) => setEmail(e.target.value)}
155+
onBlur={handleValidation}
156+
/>
157+
) : (
158+
<>
159+
<TextField
160+
label="Verification Code"
161+
id="code"
162+
size="small"
163+
name="verificationCode"
164+
variant="standard"
165+
className="w-full mb-6 mt-5"
166+
value={user.verificationCode}
167+
error={isValid['verificationCode']?.error}
168+
helperText={isValid['verificationCode']?.msg}
169+
onChange={handleInputChange}
170+
onBlur={handleValidation}
171+
/>
172+
<TextField
173+
label="New Password"
174+
id="newPassword"
175+
name="newPassword"
176+
size="small"
177+
variant="standard"
178+
className="w-full mr-6 mb-6"
179+
type="password"
180+
value={user.newPassword}
181+
onChange={handleInputChange}
182+
onBlur={handleValidation}
183+
error={isValid['newPassword']?.error}
184+
helperText={isValid['newPassword']?.msg}
185+
/>
186+
<TextField
187+
label="Confirm Password"
188+
id="confirmPassword"
189+
name="confirmPassword"
190+
size="small"
191+
variant="standard"
192+
type="password"
193+
className="w-full mb-6"
194+
onBlur={handleValidation}
195+
value={user.confirmPassword}
196+
onChange={handleInputChange}
197+
error={isValid['confirmPassword']?.error}
198+
helperText={isValid['confirmPassword']?.msg}
199+
/>
200+
</>
201+
)}
202+
<button
203+
onClick={handleRest}
204+
className="text-white font-weight-bold text-center bg-dark-600 w-full rounded py-2 mt-5"
205+
>
206+
{!activeConfirmCode ? 'RESET PASSWORD' : ' Confirm'}
207+
</button>
208+
<Link href="/login">
209+
<p className=" text-black text-xl font-weight-bold mt-5 text-center mb-5" style={{ color: '#1976D2',fontSize: '14px' }}>
210+
Sign in
211+
</p>
212+
</Link>
213+
</div>
214+
</div>
215+
<MiniFooter />
216+
</div>
217+
);
218+
};
219+
220+
export default Recover;

src/views/register/register.tsx

Lines changed: 2 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -240,56 +240,11 @@ const RegisterComponent = () => {
240240
Privacy Policy
241241
</a>
242242
</p>
243-
244-
<Link href={`/login?callBack=${query.callBack ? query.callBack : '/'}`}>
243+
` <Link href={`/login?callBack=${query.callBack ? query.callBack : '/'}`}>
245244
<p className="mt-5 text-center text-black text-md font-weight-bold">
246245
Already have an account? <span style={{color: '#1976D2'}}>Log in</span>
247246
</p>
248-
</Link>
249-
<style>
250-
{`
251-
.bottom-border {
252-
border-left: none;
253-
border-right: none;
254-
border-top: none;
255-
border-radius: 0;
256-
border-bottom: 1px solid #000;
257-
}
258-
.bottom-border::placeholder {
259-
color: #000;
260-
}
261-
@media (max-width: 576px) {
262-
.Maincontainer{
263-
height:auto !important;
264-
}
265-
.container {
266-
flex-direction:column
267-
}
268-
}
269-
@media (min-width: 576px) and (max-width: 768px) {
270-
.Maincontainer{
271-
height:auto !important;
272-
}
273-
.container {
274-
flex-direction:column;
275-
width:450px !important;
276-
}
277-
}
278-
@media (min-width: 768px) and (max-width: 992px) {
279-
.footer{
280-
margin-bottom: 40px !important;
281-
}
282-
}
283-
.box{
284-
border:1px solid #e5e7eb !important;
285-
box-shadow:none !important;
286-
border-radius: 5px !important;
287-
}
288-
.sec-box button{
289-
justify-content:center
290-
}
291-
`}
292-
</style>
247+
</Link>K
293248
</div>
294249
</div>
295250
</div>

0 commit comments

Comments
 (0)