From a1de45f52d24467a3c2cc8a6f8ca12db85adcfde Mon Sep 17 00:00:00 2001 From: Johovanis N <95220604+Jovz19200@users.noreply.github.com> Date: Tue, 18 Jun 2024 12:13:17 +0200 Subject: [PATCH] Added vercel.json --- .eslintrc.cjs | 1 + package-lock.json | 460 ++++++++++++++++++++- package.json | 4 + src/__test__/updatePassword.test.tsx | 55 +++ src/components/common/auth/Button.tsx | 13 +- src/components/common/auth/InputField.tsx | 4 +- src/components/common/auth/Password.tsx | 40 ++ src/components/password/UpdateModal.tsx | 102 +++++ src/components/password/passwordUpdate.tsx | 74 ++++ src/pages/passwordUpdatePage.tsx | 25 ++ src/redux/api/updatePasswordApiSlice.ts | 67 +++ src/redux/store.ts | 2 + src/routes/AppRoutes.tsx | 2 + src/schemas/updatepasswordSchema.ts | 24 ++ vercel.json | 5 + 15 files changed, 866 insertions(+), 12 deletions(-) create mode 100644 src/__test__/updatePassword.test.tsx create mode 100644 src/components/common/auth/Password.tsx create mode 100644 src/components/password/UpdateModal.tsx create mode 100644 src/components/password/passwordUpdate.tsx create mode 100644 src/pages/passwordUpdatePage.tsx create mode 100644 src/redux/api/updatePasswordApiSlice.ts create mode 100644 src/schemas/updatepasswordSchema.ts create mode 100644 vercel.json diff --git a/.eslintrc.cjs b/.eslintrc.cjs index c34fca4..7edd915 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -35,6 +35,7 @@ module.exports = { "no-undef": "off", "@typescript-eslint/ban-ts-comment": "off", "react/no-unescaped-entities": "off", + "react/prop-types": "off", "react-refresh/only-export-components": [ "warn", { allowConstantExport: true }, diff --git a/package-lock.json b/package-lock.json index 367687a..841c473 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,9 @@ "name": "eagle-ec-fr", "version": "0.0.0", "dependencies": { + "@heroicons/react": "^2.1.4", "@hookform/resolvers": "^3.6.0", + "@mui/icons-material": "^5.15.20", "@reduxjs/toolkit": "^2.2.5", "@testing-library/jest-dom": "^6.4.5", "@testing-library/react": "^16.0.0", @@ -20,6 +22,7 @@ "react-dom": "^18.2.0", "react-hook-form": "^7.51.5", "react-icons": "^5.2.1", + "react-modal": "^3.16.1", "react-redux": "^9.1.2", "react-router-dom": "^6.23.1", "react-toastify": "^10.0.5", @@ -35,6 +38,7 @@ "@types/jest": "^29.5.12", "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22", + "@types/react-modal": "^3.16.3", "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", "@types/redux": "^3.6.0", @@ -930,6 +934,43 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "peer": true, + "dependencies": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", + "peer": true + }, + "node_modules/@emotion/sheet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==", + "peer": true + }, + "node_modules/@emotion/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==", + "peer": true + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==", + "peer": true + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", @@ -1367,6 +1408,52 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.2.tgz", + "integrity": "sha512-+2XpQV9LLZeanU4ZevzRnGFg2neDeKHgFLjP6YLW+tly0IvrhqT4u8enLGjLH3qeh85g19xY5rsAusfwTdn5lg==", + "peer": true, + "dependencies": { + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.5.tgz", + "integrity": "sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==", + "peer": true, + "dependencies": { + "@floating-ui/core": "^1.0.0", + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.0.tgz", + "integrity": "sha512-lNzj5EQmEKn5FFKc04+zasr09h/uX8RtJRNj5gUXsSQIXHVWTVh+hVAg1vOMCexkX8EgvemMvIFpQfkosnVNyA==", + "peer": true, + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz", + "integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==", + "peer": true + }, + "node_modules/@heroicons/react": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.1.4.tgz", + "integrity": "sha512-ju0wj0wwrUTMQ2Yceyrma7TKuI3BpSjp+qKqV81K9KGcUHdvTMdiwfRc2cwXBp3uXtKuDZkh0v03nWOQnJFv2Q==", + "peerDependencies": { + "react": ">= 16" + } + }, "node_modules/@hookform/resolvers": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.6.0.tgz", @@ -2188,6 +2275,271 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mui/base": { + "version": "5.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", + "integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "@floating-ui/react-dom": "^2.0.8", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", + "@popperjs/core": "^2.11.8", + "clsx": "^2.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "5.15.20", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.20.tgz", + "integrity": "sha512-DoL2ppgldL16utL8nNyj/P12f8mCNdx/Hb/AJnX9rLY4b52hCMIx1kH83pbXQ6uMy6n54M3StmEbvSGoj2OFuA==", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/icons-material": { + "version": "5.15.20", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.20.tgz", + "integrity": "sha512-oGcKmCuHaYbAAoLN67WKSXtHmEgyWcJToT1uRtmPyxMj9N5uqwc/mRtEnst4Wj/eGr+zYH2FiZQ79v9k7kSk1Q==", + "dependencies": { + "@babel/runtime": "^7.23.9" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^5.0.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material": { + "version": "5.15.20", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.20.tgz", + "integrity": "sha512-tVq3l4qoXx/NxUgIx/x3lZiPn/5xDbdTE8VrLczNpfblLYZzlrbxA7kb9mI8NoBF6+w9WE9IrxWnKK5KlPI2bg==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/base": "5.0.0-beta.40", + "@mui/core-downloads-tracker": "^5.15.20", + "@mui/system": "^5.15.20", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.20", + "@types/react-transition-group": "^4.4.10", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^18.2.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "peer": true + }, + "node_modules/@mui/private-theming": { + "version": "5.15.20", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.20.tgz", + "integrity": "sha512-BK8F94AIqSrnaPYXf2KAOjGZJgWfvqAVQ2gVR3EryvQFtuBnG6RwodxrCvd3B48VuMy6Wsk897+lQMUxJyk+6g==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/utils": "^5.15.20", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "5.15.14", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz", + "integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "@emotion/cache": "^11.11.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "5.15.20", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.20.tgz", + "integrity": "sha512-LoMq4IlAAhxzL2VNUDBTQxAb4chnBe8JvRINVNDiMtHE2PiPOoHlhOPutSxEbaL5mkECPVWSv6p8JEV+uykwIA==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/private-theming": "^5.15.20", + "@mui/styled-engine": "^5.15.14", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.20", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.2.14", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.14.tgz", + "integrity": "sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==", + "peer": true, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "5.15.20", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.20.tgz", + "integrity": "sha512-mAbYx0sovrnpAu1zHc3MDIhPqL8RPVC5W5xcO1b7PiSCJPtckIZmBkp8hefamAvUiAV8gpfMOM6Zb+eSisbI2A==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "@types/prop-types": "^15.7.11", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "peer": true + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2242,6 +2594,16 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@reduxjs/toolkit": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.5.tgz", @@ -3091,14 +3453,12 @@ "node_modules/@types/prop-types": { "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", - "devOptional": true + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "node_modules/@types/react": { "version": "18.3.3", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", - "devOptional": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -3113,6 +3473,15 @@ "@types/react": "*" } }, + "node_modules/@types/react-modal": { + "version": "3.16.3", + "resolved": "https://registry.npmjs.org/@types/react-modal/-/react-modal-3.16.3.tgz", + "integrity": "sha512-xXuGavyEGaFQDgBv4UVm8/ZsG+qxeQ7f77yNrW3n+1J6XAstUy5rYHeIHPh1KzsGc6IkCIdu6lQ2xWzu1jBTLg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/react-redux": { "version": "7.1.33", "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.33.tgz", @@ -3155,6 +3524,15 @@ "@types/react-router": "*" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", + "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", + "peer": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/redux": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/@types/redux/-/redux-3.6.0.tgz", @@ -4657,8 +5035,7 @@ "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -4903,6 +5280,16 @@ "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "peer": true }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/domexception": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", @@ -5911,6 +6298,11 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/exenv": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", + "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==" + }, "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -9311,7 +9703,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -9974,7 +10365,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -10093,8 +10483,30 @@ "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "node_modules/react-modal": { + "version": "3.16.1", + "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.1.tgz", + "integrity": "sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==", + "dependencies": { + "exenv": "^1.2.0", + "prop-types": "^15.7.2", + "react-lifecycles-compat": "^3.0.0", + "warning": "^4.0.3" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18", + "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18" + } }, "node_modules/react-redux": { "version": "9.1.2", @@ -10160,6 +10572,22 @@ "react-dom": ">=18" } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -10953,6 +11381,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "peer": true + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -11698,6 +12132,14 @@ "makeerror": "1.0.12" } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", diff --git a/package.json b/package.json index be6537a..543c97f 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,9 @@ "prettier": "prettier . --write" }, "dependencies": { + "@heroicons/react": "^2.1.4", "@hookform/resolvers": "^3.6.0", + "@mui/icons-material": "^5.15.20", "@reduxjs/toolkit": "^2.2.5", "@testing-library/jest-dom": "^6.4.5", "@testing-library/react": "^16.0.0", @@ -28,6 +30,7 @@ "react-dom": "^18.2.0", "react-hook-form": "^7.51.5", "react-icons": "^5.2.1", + "react-modal": "^3.16.1", "react-redux": "^9.1.2", "react-router-dom": "^6.23.1", "react-toastify": "^10.0.5", @@ -43,6 +46,7 @@ "@types/jest": "^29.5.12", "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22", + "@types/react-modal": "^3.16.3", "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", "@types/redux": "^3.6.0", diff --git a/src/__test__/updatePassword.test.tsx b/src/__test__/updatePassword.test.tsx new file mode 100644 index 0000000..0d292a6 --- /dev/null +++ b/src/__test__/updatePassword.test.tsx @@ -0,0 +1,55 @@ +import "@testing-library/jest-dom"; +import { render, screen } from "@testing-library/react"; +import { Provider } from "react-redux"; +import { BrowserRouter as Router } from "react-router-dom"; +import userEvent from "@testing-library/user-event"; +import { ToastContainer } from "react-toastify"; + +import UpdatePasswordmod from "../components/password/UpdateModal"; +import store from "../redux/store"; + +test("test update password", () => { + const setPasswordModal = jest.fn(); + render( + + + + + + , + ); + + const currentPasswordInput = screen.getByPlaceholderText("Old Password"); + const newPasswordInput = screen.getByPlaceholderText("New Password"); + const confirmNewPasswordInput = screen.getByPlaceholderText("Confirm Password"); + const updateButton = screen.getByRole("button", { name: /Save Changes/i }); + + userEvent.type(currentPasswordInput, "Test@123"); + userEvent.type(newPasswordInput, "NewTest@123"); + userEvent.type(confirmNewPasswordInput, "NewTest@123"); + + userEvent.click(updateButton); +}); + +test("test update password with mismatching new password and confirm password", () => { + const setPasswordModal = jest.fn(); + render( + + + + + + , + ); + + const currentPasswordInput = screen.getByPlaceholderText("Old Password"); + const newPasswordInput = screen.getByPlaceholderText("New Password"); + const confirmNewPasswordInput = screen.getByPlaceholderText("Confirm Password"); + const updateButton = screen.getByRole("button", { name: /Save Changes/i }); + + userEvent.type(currentPasswordInput, "Test@123"); + userEvent.type(newPasswordInput, "NewTest@123"); + userEvent.type(confirmNewPasswordInput, "MismatchTest@123"); + + userEvent.click(updateButton); +}); diff --git a/src/components/common/auth/Button.tsx b/src/components/common/auth/Button.tsx index 13db681..6e42219 100644 --- a/src/components/common/auth/Button.tsx +++ b/src/components/common/auth/Button.tsx @@ -4,14 +4,23 @@ interface ButtonProps { text: string; disabled?: boolean; dataTestId?: string; + className?: string; + onClick?: () => void; } -const Button: React.FC = ({ text, disabled, dataTestId }) => ( +const Button: React.FC = ({ + text, + disabled, + dataTestId, + className, + onClick, +}) => ( diff --git a/src/components/common/auth/InputField.tsx b/src/components/common/auth/InputField.tsx index 1d00197..7a9ebc0 100644 --- a/src/components/common/auth/InputField.tsx +++ b/src/components/common/auth/InputField.tsx @@ -8,6 +8,7 @@ interface InputFieldProps { // eslint-disable-next-line @typescript-eslint/no-explicit-any register: UseFormRegister; error: string | undefined; + className?: string; } const InputField: React.FC = ({ @@ -16,10 +17,11 @@ const InputField: React.FC = ({ placeholder, register, error, + className, }) => (
= ({ + id, + placeholder, + register, + error, +}) => { + const [showPassword, setShowPassword] = useState(false); + + return ( +
+ + + {error &&

{error.message}

} +
+ ); +}; + +export default PasswordInput; diff --git a/src/components/password/UpdateModal.tsx b/src/components/password/UpdateModal.tsx new file mode 100644 index 0000000..57f585c --- /dev/null +++ b/src/components/password/UpdateModal.tsx @@ -0,0 +1,102 @@ +import React, { useState } from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { toast, ToastContainer } from "react-toastify"; +import { AxiosError } from "axios"; +import { useDispatch } from "react-redux"; + +import updatePasswordSchema from "../../schemas/updatepasswordSchema"; +import { updatePassword } from "../../redux/api/updatePasswordApiSlice"; +import PasswordInput from "../common/auth/Password"; + +interface UpdatePasswordProps { + setPasswordModal: (isOpen: boolean) => void; +} + +const UpdatePasswordmod: React.FC = ({ + setPasswordModal, +}) => { + const dispatch = useDispatch(); + const [loading, setLoading] = useState(false); + const { + register, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: yupResolver(updatePasswordSchema), + }); + + const onSubmit = async (data: { + oldPassword: string; + newPassword: string; + confirmPassword: string; + }) => { + try { + setLoading(true); + // @ts-ignore + const response = await dispatch(updatePassword(data)).unwrap(); + setLoading(false); + toast.success(response.message); + setTimeout(() => { + setPasswordModal(false); + }, 3000); + } catch (err) { + setLoading(false); + const error = err as AxiosError; + toast.error(error.message); + } + }; + + return ( +
+
+ +

+ Update Password +

+
+ + + + +
+ +
+
+ + +
+ +
+
+ ); +}; + +export default UpdatePasswordmod; diff --git a/src/components/password/passwordUpdate.tsx b/src/components/password/passwordUpdate.tsx new file mode 100644 index 0000000..cbd2c95 --- /dev/null +++ b/src/components/password/passwordUpdate.tsx @@ -0,0 +1,74 @@ +import { SubmitHandler, useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; + +import updatePasswordSchema from "../../schemas/updatepasswordSchema"; +import Button from "../common/auth/Button"; +import InputField from "../common/auth/InputField"; + +interface UpdatePasswordFormInputs { + oldPassword: string; + newPassword: string; + confirmPassword: string; +} + +interface PasswordUpdateFormProps { + onSubmit: SubmitHandler; + onCancel: () => void; + loading: boolean; +} + +const PasswordUpdateForm: React.FC = ({ + onSubmit, + onCancel, + loading, +}) => { + const { + register, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: yupResolver(updatePasswordSchema), + }); + + return ( +
+ + + +
+
+ + ); +}; + +export default PasswordUpdateForm; diff --git a/src/pages/passwordUpdatePage.tsx b/src/pages/passwordUpdatePage.tsx new file mode 100644 index 0000000..a3ccb2a --- /dev/null +++ b/src/pages/passwordUpdatePage.tsx @@ -0,0 +1,25 @@ +import { useState } from "react"; + +import UpdatePasswordmod from "../components/password/UpdateModal"; + +const UpdatePasswordPage = () => { + const [PasswordModal, setPasswordModal] = useState(false); + return ( +
+ + {PasswordModal && ( + + )} +
+ ); +}; + +export default UpdatePasswordPage; diff --git a/src/redux/api/updatePasswordApiSlice.ts b/src/redux/api/updatePasswordApiSlice.ts new file mode 100644 index 0000000..bea1167 --- /dev/null +++ b/src/redux/api/updatePasswordApiSlice.ts @@ -0,0 +1,67 @@ +import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; +import axios, { AxiosError } from "axios"; + +interface UpdatePasswordState { + loading: boolean; + error: string | null; +} + +const initialState: UpdatePasswordState = { + loading: false, + error: null, +}; + +interface UpdatePasswordPayload { + oldPassword: string; + newPassword: string; + confirmPassword: string; +} + +interface UpdatePasswordResponse { + message: string; +} + +interface UpdatePasswordError { + message: string; +} +const token = localStorage.getItem("accessToken"); +export const updatePassword = createAsyncThunk< +UpdatePasswordResponse, +UpdatePasswordPayload, +{ rejectValue: UpdatePasswordError } +>("updatePassword", async (payload, { rejectWithValue }) => { + try { + const response = await axios.put("/users/passwordupdate", payload, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return response.data; + } catch (error) { + const axiosError = error as AxiosError; + return rejectWithValue(axiosError.response?.data as UpdatePasswordError); + } +}); + +const updatePasswordApiSlice = createSlice({ + name: "updatePassword", + initialState, + reducers: {}, + extraReducers: (builder) => { + builder + .addCase(updatePassword.pending, (state) => { + state.loading = true; + state.error = null; + }) + .addCase(updatePassword.fulfilled, (state) => { + state.loading = false; + state.error = null; + }) + .addCase(updatePassword.rejected, (state, { payload }) => { + state.loading = false; + state.error = payload?.message || null; + }); + }, +}); + +export default updatePasswordApiSlice.reducer; diff --git a/src/redux/store.ts b/src/redux/store.ts index 8509031..5142584 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -2,11 +2,13 @@ import { configureStore } from "@reduxjs/toolkit"; import loginReducer from "./api/loginApiSlice"; import registerReducer from "./reducers/registerSlice"; +import updatePasswordApiSlice from "./api/updatePasswordApiSlice"; const store = configureStore({ reducer: { login: loginReducer, register: registerReducer, + password: updatePasswordApiSlice, }, }); diff --git a/src/routes/AppRoutes.tsx b/src/routes/AppRoutes.tsx index 41f21d1..4f81760 100644 --- a/src/routes/AppRoutes.tsx +++ b/src/routes/AppRoutes.tsx @@ -4,6 +4,7 @@ import RootLayout from "../components/layouts/RootLayout"; import Homepage from "../pages/Homepage"; import Login from "../pages/Login"; import RegisterUser from "../pages/RegisterUser"; +import UpdatePasswordPage from "../pages/passwordUpdatePage"; const AppRoutes = () => ( @@ -12,6 +13,7 @@ const AppRoutes = () => ( } /> } /> + } /> ); diff --git a/src/schemas/updatepasswordSchema.ts b/src/schemas/updatepasswordSchema.ts new file mode 100644 index 0000000..6bfd0b1 --- /dev/null +++ b/src/schemas/updatepasswordSchema.ts @@ -0,0 +1,24 @@ +import { object, string, ref } from "yup"; + +const updatePasswordSchema = object({ + oldPassword: string().required("Old password is required"), + newPassword: string() + .required("New password is required") + .min(8, "Password must be at least 8 characters long") + .matches(/[a-z]/, "Password must contain at least one lowercase letter") + .matches(/[A-Z]/, "Password must contain at least one uppercase letter") + .matches(/[0-9]/, "Password must contain at least one number") + .matches( + /[!@#$%^&*(),.?":{}|<>]/, + "Password must contain at least one special character", + ) + .notOneOf( + [ref("oldPassword")], + "New password must be different from old password", + ), + confirmPassword: string() + .required("Confirm password is required") + .oneOf([ref("newPassword")], "Confirm password must match new password"), +}); + +export default updatePasswordSchema; diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..6a92648 --- /dev/null +++ b/vercel.json @@ -0,0 +1,5 @@ +{ + "rewrites": [ + { "source": "/(.*)", "destination": "/" } + ] +}