diff --git a/frontend/package-lock.json b/frontend/package-lock.json index b2321ba..8dae5b0 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -14,6 +14,7 @@ "axios": "^1.4.0", "gsap": "^3.12.5", "js-cookie": "^3.0.5", + "jsonwebtoken": "^8.5.1", "moment": "^2.30.1", "next": "14.1.0", "next-themes": "^0.2.1", @@ -32,6 +33,7 @@ "tailwind-scrollbar": "^3.0.5" }, "devDependencies": { + "@types/jsonwebtoken": "^8.5.1", "@types/node": "^20", "@types/react": "^18", "@types/react-beautiful-dnd": "^13.1.8", @@ -1683,6 +1685,16 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-rNAPdomlIUX0i0cg2+I+Q1wOUr531zHBQ+cV/28PJ39bSPKjahatZZ2LMuhiguETkCgLVzfruw/ZvNMNkKoSzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "20.11.20", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.20.tgz", @@ -2417,6 +2429,12 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -2795,6 +2813,15 @@ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.682", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.682.tgz", @@ -4646,6 +4673,37 @@ "node": ">=6" } }, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -4661,6 +4719,27 @@ "node": ">=4.0" } }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -4729,12 +4808,54 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -5953,6 +6074,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/safe-regex-test": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index 42934c8..511000f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,6 +15,7 @@ "axios": "^1.4.0", "gsap": "^3.12.5", "js-cookie": "^3.0.5", + "jsonwebtoken": "^8.5.1", "moment": "^2.30.1", "next": "14.1.0", "next-themes": "^0.2.1", @@ -33,6 +34,7 @@ "tailwind-scrollbar": "^3.0.5" }, "devDependencies": { + "@types/jsonwebtoken": "^8.5.1", "@types/node": "^20", "@types/react": "^18", "@types/react-beautiful-dnd": "^13.1.8", diff --git a/frontend/src/components/pages/forgetPassword/index.tsx b/frontend/src/components/pages/forgetPassword/index.tsx index 14692a1..9593149 100644 --- a/frontend/src/components/pages/forgetPassword/index.tsx +++ b/frontend/src/components/pages/forgetPassword/index.tsx @@ -1,27 +1,47 @@ +import { useForgetPasswordMutation } from "@/features/user"; import { useRouter } from "next/router"; import { SubmitHandler, useForm } from "react-hook-form"; +import Swal from "sweetalert2"; type FormData = { email: string; }; const ForgetPasswordPage = () => { - const router = useRouter(); const { register, handleSubmit, formState: { errors }, } = useForm({ mode: "onChange" }); - const handleSubmitEmail: SubmitHandler = (data) => { - router.push("/reset-password"); + const [forgetPassword] = useForgetPasswordMutation(); + const router = useRouter(); + const handleSubmitEmail: SubmitHandler = async (data) => { + const res: any = await forgetPassword(data); + if (res?.data?.statusCode === 200) { + Swal.fire({ + position: "center", + icon: "success", + title: "Reset email was sent!", + text: res?.data?.message, + showConfirmButton: true, + }); + router.push("/"); + } else { + Swal.fire({ + position: "center", + icon: "error", + title: "Something went wrong! Try again", + text: res?.error?.data?.message, + showConfirmButton: false, + timer: 2500, + }); + } }; return ( -
-
+
+

Forgot Password

-

- Enter your email to reset your password. -

+

Enter your email to reset your password.

{ const router = useRouter(); + const [resetPassword] = useResetPasswordMutation(); + const [userId, setUserId] = useState(); + const [tokenError, setTokenError] = useState(null); const [togglePassword, setTogglePassword] = useState<{ password: boolean; confirmPassword: boolean; @@ -18,22 +24,38 @@ const ResetPasswordPage = () => { password: false, confirmPassword: false, }); + const { register, handleSubmit, watch, formState: { errors }, } = useForm({ mode: "onChange" }); - const handleResetPassword: SubmitHandler = (data) => { - Swal.fire({ - position: "center", - icon: "success", - title: "Password reset successful", - text: "Now, try to login with new password", - showConfirmButton: false, - timer: 2000, - }); - router.push("/login"); + + const handleResetPassword: SubmitHandler = async (data) => { + const payload = { + userId, + password: data.password, + }; + const res: any = await resetPassword(payload); + if (res?.data?.statusCode === 200) { + Swal.fire({ + position: "center", + icon: "success", + title: res?.data?.message, + showConfirmButton: false, + }); + router.push("/login"); + } else { + Swal.fire({ + position: "center", + icon: "error", + title: "Something went wrong! Try again", + text: res?.error?.data?.message, + showConfirmButton: false, + timer: 2500, + }); + } }; const handleTogglePassword = (type: string) => { @@ -47,17 +69,56 @@ const ResetPasswordPage = () => { } }; + useEffect(() => { + const paths = router?.asPath; + const params = new URLSearchParams(paths.split("?")[1]); + const userId = params.get("userId"); + const token = params.get("token") as string; + if (token) { + try { + jwt.verify( + token, + process.env.NEXT_PUBLIC_JWT_ACCESS_TOKEN_SECRET as string + ); + + setUserId(userId); + } catch (error) { + setTokenError( + "The token is expired or invalid. Please request a new password reset." + ); + } + } + }, [router]); + const password = watch("password"); + if (tokenError) { + return ( +
+
+

Token Error

+

{tokenError}

+ + Resend + +
+
+ ); + } + return ( -
-
+
+

Reset Password

-

Change your password carefully

+

Change your password carefully

{
diff --git a/frontend/src/features/user/index.ts b/frontend/src/features/user/index.ts index 80e9fb4..10e6305 100644 --- a/frontend/src/features/user/index.ts +++ b/frontend/src/features/user/index.ts @@ -47,6 +47,24 @@ const userApi = apiSlice.injectEndpoints({ }), invalidatesTags: ["user"] as any, }), + forgetPassword: builder.mutation({ + query: (data) => ({ + method: "POST", + url: `/user/forget-password`, + body: data, + credentials: "include", + }), + invalidatesTags: ["user"] as any, + }), + resetPassword: builder.mutation({ + query: (data) => ({ + method: "POST", + url: `/user/reset-password`, + body: data, + credentials: "include", + }), + invalidatesTags: ["user"] as any, + }), }), }); @@ -57,4 +75,6 @@ export const { useLoggedInUserQuery, useUpdateUserMutation, useLogoutUserMutation, + useForgetPasswordMutation, + useResetPasswordMutation, } = userApi;