diff --git a/frontend-manager-student/package-lock.json b/frontend-manager-student/package-lock.json index 5d92827..28f3ef7 100644 --- a/frontend-manager-student/package-lock.json +++ b/frontend-manager-student/package-lock.json @@ -23,6 +23,7 @@ "jwt-decode": "^3.1.2", "moment": "^2.29.1", "nanoid": "^4.0.1", + "nprogress": "^0.2.0", "react": "^17.0.2", "react-bootstrap": "^2.7.2", "react-countup": "^6.1.1", @@ -32,6 +33,7 @@ "react-facebook-login": "^4.1.1", "react-google-login": "^5.2.2", "react-google-recaptcha": "^2.1.0", + "react-helmet-async": "^1.3.0", "react-hot-toast": "^2.2.0", "react-icons": "^4.3.1", "react-lazy-load-image-component": "^1.5.5", @@ -47,8 +49,7 @@ "sass": "^1.57.1", "sass-loader": "^13.2.0", "slick-carousel": "^1.8.1", - "sweetalert": "^2.1.2", - "sweetalert2": "^11.4.8", + "sweetalert2": "^11.7.3", "toast": "^0.3.47", "uuid": "^8.3.2", "web-vitals": "^1.0.1" @@ -7707,11 +7708,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==" - }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -13599,6 +13595,11 @@ "node": ">=8" } }, + "node_modules/nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" + }, "node_modules/nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", @@ -15508,11 +15509,6 @@ "asap": "~2.0.6" } }, - "node_modules/promise-polyfill": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.1.0.tgz", - "integrity": "sha512-g0LWaH0gFsxovsU7R5LrrhHhWAWiHRnh1GPrhXnPgYsDkIqjRYUYSZEsej/wtleDrz5xVSIDbeKfidztp2XHFQ==" - }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -15893,6 +15889,11 @@ "react": "^16.0.0" } }, + "node_modules/react-fast-compare": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", + "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" + }, "node_modules/react-google-login": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/react-google-login/-/react-google-login-5.2.2.tgz", @@ -15919,6 +15920,22 @@ "react": ">=16.4.1" } }, + "node_modules/react-helmet-async": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz", + "integrity": "sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "invariant": "^2.2.4", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.2.0", + "shallowequal": "^1.1.0" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.6.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-hot-toast": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.0.tgz", @@ -17047,6 +17064,11 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -17625,19 +17647,10 @@ "node": ">=4" } }, - "node_modules/sweetalert": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/sweetalert/-/sweetalert-2.1.2.tgz", - "integrity": "sha512-iWx7X4anRBNDa/a+AdTmvAzQtkN1+s4j/JJRWlHpYE8Qimkohs8/XnFcWeYHH2lMA8LRCa5tj2d244If3S/hzA==", - "dependencies": { - "es6-object-assign": "^1.1.0", - "promise-polyfill": "^6.0.2" - } - }, "node_modules/sweetalert2": { - "version": "11.6.15", - "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.6.15.tgz", - "integrity": "sha512-FqMy1gRGHEI5G145NE5XSP059TziCJu9Xf9/mkki/aKu5pLNcYzjggOzKO5Ex10EBgAGDXQ99jyGfYYzGCYXRQ==", + "version": "11.7.3", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.7.3.tgz", + "integrity": "sha512-fUN/fyVSBZNtY4Rr/Qtxn7tNNnlRAbUhQxTQ9uOo0xVMIHBmqq4/9pau5N9dB2pvkB353XL/ywRAycscLoYU3w==", "funding": { "type": "individual", "url": "https://github.com/sponsors/limonte" @@ -24705,11 +24718,6 @@ "is-symbol": "^1.0.2" } }, - "es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==" - }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -29078,6 +29086,11 @@ "path-key": "^3.0.0" } }, + "nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" + }, "nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", @@ -30244,11 +30257,6 @@ "asap": "~2.0.6" } }, - "promise-polyfill": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.1.0.tgz", - "integrity": "sha512-g0LWaH0gFsxovsU7R5LrrhHhWAWiHRnh1GPrhXnPgYsDkIqjRYUYSZEsej/wtleDrz5xVSIDbeKfidztp2XHFQ==" - }, "prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -30535,6 +30543,11 @@ "resolved": "https://registry.npmjs.org/react-facebook-login/-/react-facebook-login-4.1.1.tgz", "integrity": "sha512-COnHEHlYGTKipz4963safFAK9PaNTcCiXfPXMS/yxo8El+/AJL5ye8kMJf23lKSSGGPgqFQuInskIHVqGqTvSw==" }, + "react-fast-compare": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", + "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" + }, "react-google-login": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/react-google-login/-/react-google-login-5.2.2.tgz", @@ -30553,6 +30566,18 @@ "react-async-script": "^1.1.1" } }, + "react-helmet-async": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz", + "integrity": "sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==", + "requires": { + "@babel/runtime": "^7.12.5", + "invariant": "^2.2.4", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.2.0", + "shallowequal": "^1.1.0" + } + }, "react-hot-toast": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.0.tgz", @@ -31353,6 +31378,11 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -31797,19 +31827,10 @@ } } }, - "sweetalert": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/sweetalert/-/sweetalert-2.1.2.tgz", - "integrity": "sha512-iWx7X4anRBNDa/a+AdTmvAzQtkN1+s4j/JJRWlHpYE8Qimkohs8/XnFcWeYHH2lMA8LRCa5tj2d244If3S/hzA==", - "requires": { - "es6-object-assign": "^1.1.0", - "promise-polyfill": "^6.0.2" - } - }, "sweetalert2": { - "version": "11.6.15", - "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.6.15.tgz", - "integrity": "sha512-FqMy1gRGHEI5G145NE5XSP059TziCJu9Xf9/mkki/aKu5pLNcYzjggOzKO5Ex10EBgAGDXQ99jyGfYYzGCYXRQ==" + "version": "11.7.3", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.7.3.tgz", + "integrity": "sha512-fUN/fyVSBZNtY4Rr/Qtxn7tNNnlRAbUhQxTQ9uOo0xVMIHBmqq4/9pau5N9dB2pvkB353XL/ywRAycscLoYU3w==" }, "symbol-tree": { "version": "3.2.4", diff --git a/frontend-manager-student/package.json b/frontend-manager-student/package.json index 34d3792..b6e13bc 100644 --- a/frontend-manager-student/package.json +++ b/frontend-manager-student/package.json @@ -20,6 +20,7 @@ "jwt-decode": "^3.1.2", "moment": "^2.29.1", "nanoid": "^4.0.1", + "nprogress": "^0.2.0", "react": "^17.0.2", "react-bootstrap": "^2.7.2", "react-countup": "^6.1.1", @@ -29,6 +30,7 @@ "react-facebook-login": "^4.1.1", "react-google-login": "^5.2.2", "react-google-recaptcha": "^2.1.0", + "react-helmet-async": "^1.3.0", "react-hot-toast": "^2.2.0", "react-icons": "^4.3.1", "react-lazy-load-image-component": "^1.5.5", @@ -44,8 +46,7 @@ "sass": "^1.57.1", "sass-loader": "^13.2.0", "slick-carousel": "^1.8.1", - "sweetalert": "^2.1.2", - "sweetalert2": "^11.4.8", + "sweetalert2": "^11.7.3", "toast": "^0.3.47", "uuid": "^8.3.2", "web-vitals": "^1.0.1" diff --git a/frontend-manager-student/src/api/api_media.js b/frontend-manager-student/src/api/api_media.js new file mode 100644 index 0000000..4877ffe --- /dev/null +++ b/frontend-manager-student/src/api/api_media.js @@ -0,0 +1,15 @@ +const API_MEDIA = { + /** + * @author Nguyễn Tiến Tài + * @created_at 15/03/2023 + * @descriptionKey API Upload Media + */ + UPLOAD_MEDIA: '/media/v1/media/private/upload', + /** + * @author Nguyễn Tiến Tài + * @created_at 15/03/2023 + * @descriptionKey API Remove Media + */ + REMOVE_MEDIA: '/media/v1/media/private/remove', +}; +export default API_MEDIA; diff --git a/frontend-manager-student/src/api/api_user.js b/frontend-manager-student/src/api/api_user.js index d0367fe..140c774 100644 --- a/frontend-manager-student/src/api/api_user.js +++ b/frontend-manager-student/src/api/api_user.js @@ -5,6 +5,18 @@ const API_STUDENT = { * @descriptionKey API login student */ LOGIN_STUDENT: '/student/v1/user/login', + /** + * @author Nguyễn Tiến Tài + * @created_at 15/03/2023 + * @descriptionKey API change password student + */ + CHANGE_PASSWORD_STUDENT: '/student/v1/user/private/change-password', + /** + * @author Nguyễn Tiến Tài + * @created_at 16/03/2023 + * @descriptionKey API forget password student + */ + FORGET_PASSWORD_STUDENT: '/student/v1/user/forget-password', /** * @author Nguyễn Tiến Tài * @created_at 03/03/2023 @@ -33,14 +45,14 @@ const API_STUDENT = { /** * @author Châu Gia Bảo * @created_at 10/03/2023 - * @descriptionKey API GET ALL BOOK + * @descriptionKey API GET DETAIL BOOK */ GET_DETAIL_BOOK_STUDENT: '/student/v1/user/private/book/detail', /** * @author Châu Gia Bảo * @created_at 10/03/2023 - * @descriptionKey API GET ALL BOOK + * @descriptionKey API GET BORROW BOOK */ BORROW_BOOK_STUDENT: '/student/v1/user/private/borrow_book/borrow', }; diff --git a/frontend-manager-student/src/components/Nprogress/RouterNprogress.jsx b/frontend-manager-student/src/components/Nprogress/RouterNprogress.jsx new file mode 100644 index 0000000..d3b8d57 --- /dev/null +++ b/frontend-manager-student/src/components/Nprogress/RouterNprogress.jsx @@ -0,0 +1,24 @@ +//!LIBRARY +import NProgress from 'nprogress'; +import { useEffect } from 'react'; +import { useLocation } from 'react-router-dom'; + +const RouterNprogress = () => { + const location = useLocation(); + + useEffect(() => { + const unlisten = () => { + NProgress.start(); + }; + + return unlisten; + }, [location]); + + useEffect(() => { + NProgress.done(); + }); + + return null; +}; + +export default RouterNprogress; diff --git a/frontend-manager-student/src/configs/constants.js b/frontend-manager-student/src/configs/constants.js index c090e6b..98f0ae4 100644 --- a/frontend-manager-student/src/configs/constants.js +++ b/frontend-manager-student/src/configs/constants.js @@ -13,6 +13,7 @@ const CONSTANTS = { * @description millisecond/ second * @return {Number} */ + _2_SECOND: 2 * 1000, _1_MINUTES: 60 * 1000, _5_MINUTES: 5 * 60 * 1000, _4_MINUTES: 4 * 60 * 1000, @@ -67,17 +68,28 @@ const CONSTANTS = { * @author Nguyễn Tiến Tài * @created_at 02/03/2023 * @descriptionKey Key Auth token localStorage - * @return {Number} + * @return {string} */ AUTH_TOKEN: 'auth-token', /** * @author Nguyễn Tiến Tài * @created_at 04/03/2023 * @descriptionKey Header - * @return {Number} + * @return {string} */ OS_TYPE_HEADER: 'web', OS_VERSION_HEADER: '1.0', APP_VERSION_HEADER: '1.0', + /** + * @author Nguyễn Tiến Tài + * @created_at 15/03/2023 + * @descriptionKey TYPE MEDIA + * @return {string} + */ + MEDIA_TYPE: { + JPEG: 'image/jpeg', + PNG: 'image/png', + FILE: 'file' + } }; export default CONSTANTS; diff --git a/frontend-manager-student/src/configs/text_notification.js b/frontend-manager-student/src/configs/text_notification.js index 4ec32a6..c4ed766 100644 --- a/frontend-manager-student/src/configs/text_notification.js +++ b/frontend-manager-student/src/configs/text_notification.js @@ -1,10 +1,14 @@ const TEXT_NOTIFICATION = { /** - * @author Nguyễn Tiến Tài + * @author Nguyễn Tiến Tài,Châu Gia Bảo * @created_at 04/03/2023 + * @updated_at 15/03/2023,16/03/2023 * @description LOgin success */ NOTIFICATION_LOGIN_SUCCESS: 'Đăng nhập thành công', NOTIFICATION_LOGOUT_SUCCESS: 'Đăng xuất thành công', + NOTIFICATION_CHANGE_PASSWORD_SUCCESS: 'Đổi mật khẩu thành công', + NOTIFICATION_FORGET_PASSWORD_SUCCESS: 'Đã gửi link reset password đến ${email}', + NOTIFICATION_LOGIN_SESSION_EXPIRE: 'Phiên đăng nhập của bạn đã hết hạn', }; export default TEXT_NOTIFICATION; diff --git a/frontend-manager-student/src/contexts/auth_student/auth_student.js b/frontend-manager-student/src/contexts/auth_student/auth_student.js index c8cf2c2..4e55bd0 100644 --- a/frontend-manager-student/src/contexts/auth_student/auth_student.js +++ b/frontend-manager-student/src/contexts/auth_student/auth_student.js @@ -28,7 +28,7 @@ const AuthStudent = () => { if (decodedToken) { dispatch(Profile_Student_Initial()); } - }, [decodedToken, token_student, dispatch, token_localStorage]); + }, [decodedToken, token_student]); return {}; }; diff --git a/frontend-manager-student/src/contexts/global_context.js b/frontend-manager-student/src/contexts/global_context.js index 747e4bf..3c9a22e 100644 --- a/frontend-manager-student/src/contexts/global_context.js +++ b/frontend-manager-student/src/contexts/global_context.js @@ -55,7 +55,7 @@ export const DataProviderStudent = ({ children }) => { //Start newToken(); } - }, [token_access_localStorage, dispatch]); + }, [dispatch]); //! Data const data = { diff --git a/frontend-manager-student/src/custom_hook/uploadMediaCloud.jsx b/frontend-manager-student/src/custom_hook/uploadMediaCloud.jsx new file mode 100644 index 0000000..6d657f9 --- /dev/null +++ b/frontend-manager-student/src/custom_hook/uploadMediaCloud.jsx @@ -0,0 +1,45 @@ +//!LIBRARY +import { useDispatch } from 'react-redux'; + +//!REDUX THUNK +import { Upload_Media_Initial } from 'redux/media/upload_remove_media/media_thunk'; + +//!SHARE +import CONSTANTS from 'configs/constants'; +import NOTIFICATION from 'utils/notification'; + +const useUploadCloud = () => { + const dispatch = useDispatch(); + + const handleUpload = (e) => { + e.preventDefault(); + try { + // Check input file + const file = e.target.files[0]; + if (!file) return NOTIFICATION.notifyError('File not Exists'); + + if (file.size > 1024 * 1024) + // 1mb + return NOTIFICATION.notifyError('Size too large !'); + + //Check type file + if (file.type !== CONSTANTS.MEDIA_TYPE.JPEG && file.type !== CONSTANTS.MEDIA_TYPE.PNG) { + // 1mb + return NOTIFICATION.notifyError('File format is incorrect.'); + } + + // Create Form data save image computer + let formData = new FormData(); + formData.append(CONSTANTS.MEDIA_TYPE.FILE, file); + + //Action upload + dispatch(Upload_Media_Initial({ formData })); + } catch (error) { + console.log(error); + NOTIFICATION.notifyError(error.response.data.msg); + } + }; + return { handleUpload }; +}; + +export default useUploadCloud; diff --git a/frontend-manager-student/src/imports/auth_import/index.js b/frontend-manager-student/src/imports/auth_import/index.js new file mode 100644 index 0000000..e9dad78 --- /dev/null +++ b/frontend-manager-student/src/imports/auth_import/index.js @@ -0,0 +1,9 @@ +//Change Password +export { default as TabChangePassword } from 'pages/Auth/ChangePassword/component/TabChangePassword'; + +//Login +export { default as TabLogin } from 'pages/Auth/Login/components/TabLogin'; + +//Forget +export { default as TabForgetPassword } from 'pages/Auth/Login/components/TabForgetPassword'; + diff --git a/frontend-manager-student/src/imports/loading_import/index.js b/frontend-manager-student/src/imports/loading_import/index.js new file mode 100644 index 0000000..918544c --- /dev/null +++ b/frontend-manager-student/src/imports/loading_import/index.js @@ -0,0 +1,2 @@ +//Loading All Button +export { default as Loading } from 'components/Loading'; diff --git a/frontend-manager-student/src/index.js b/frontend-manager-student/src/index.js index c6908ae..ca4858e 100644 --- a/frontend-manager-student/src/index.js +++ b/frontend-manager-student/src/index.js @@ -7,6 +7,7 @@ import { BrowserRouter as Router } from 'react-router-dom'; import 'react-toastify/dist/ReactToastify.css'; import 'slick-carousel/slick/slick.css'; import 'slick-carousel/slick/slick-theme.css'; +import 'nprogress/nprogress.css'; import './assets/boxicons-2.0.7/css/boxicons.min.css'; import './index.css'; import './styles/style.scss'; @@ -20,10 +21,14 @@ import { DataProviderStudent } from 'contexts/global_context'; import App from './App'; import reportWebVitals from './reportWebVitals'; +//! COMPONENT +import RouterNprogress from 'components/Nprogress/RouterNprogress'; + ReactDOM.render( + diff --git a/frontend-manager-student/src/pages/Auth/ChangePassword/component/TabChangePassword.jsx b/frontend-manager-student/src/pages/Auth/ChangePassword/component/TabChangePassword.jsx new file mode 100644 index 0000000..907cbe8 --- /dev/null +++ b/frontend-manager-student/src/pages/Auth/ChangePassword/component/TabChangePassword.jsx @@ -0,0 +1,110 @@ +//! LIBRARY +import React from 'react'; +import { useDispatch, useSelector } from 'react-redux'; + +//! COMPONENTS +import Section, { SectionBody, SectionTitle } from 'components/Section'; + +//! SHARE +import HELPERS from 'utils/helper'; +import NOTIFICATION from 'utils/notification'; + +//! REDUX THUNK +import { Change_Password_Initial } from 'redux/student/authentication_slice/auth_thunk'; + +//!IMPORT +import { Loading } from 'imports/loading_import'; + +const TabChangePassword = () => { + const dispatch = useDispatch(); + + // Take profile account in store + const { loading_change_password } = useSelector((state) => ({ + ...state.auth_student, + })); + + const handleChangePasswordStudent = (e) => { + e.preventDefault(); + + const values = HELPERS.formDataGeneral(e.target); + + //Check input + if (!values.password || !values.oldPassword || !values.confirmPassword) { + return NOTIFICATION.notifyError('Các trường không được để trống !!!'); + } + + // Action Change Password + dispatch(Change_Password_Initial(values)); + }; + return ( + +
+
+ Thay đổi mật khẩu + +
+
+ +
+
+ + + + +
+
+ + +
+
+ + + + +
+
+ + +
+
+ + + + +
+
+ {loading_change_password ? ( + + ) : ( + + )} +
+
+
+
+
+
+ ); +}; + +export default TabChangePassword; diff --git a/frontend-manager-student/src/pages/Auth/ChangePassword/index.jsx b/frontend-manager-student/src/pages/Auth/ChangePassword/index.jsx index 9bbb4e0..0694abe 100644 --- a/frontend-manager-student/src/pages/Auth/ChangePassword/index.jsx +++ b/frontend-manager-student/src/pages/Auth/ChangePassword/index.jsx @@ -1,54 +1,16 @@ -import Helmet from 'components/Helmet'; -import Section, { SectionBody, SectionTitle } from 'components/Section'; +//! LIBRARY import React from 'react'; +//! COMPONENT +import Helmet from 'components/Helmet'; + +//! IMPORT +import { TabChangePassword } from 'imports/auth_import'; + const ChangePassword = () => { return ( -
-
- Thay đổi mật khẩu - -
-
- -
-
- - - - -
-
- - -
-
- - - - -
-
- - -
-
- - - - -
-
- - -
-
-
-
-
+
); }; diff --git a/frontend-manager-student/src/pages/Auth/Login/components/TabForgetPassword.jsx b/frontend-manager-student/src/pages/Auth/Login/components/TabForgetPassword.jsx new file mode 100644 index 0000000..94e8624 --- /dev/null +++ b/frontend-manager-student/src/pages/Auth/Login/components/TabForgetPassword.jsx @@ -0,0 +1,66 @@ +//!LIBRARY +import React from 'react'; +import { useDispatch, useSelector } from 'react-redux'; + +//! SHARE +import HELPERS from 'utils/helper'; +import NOTIFICATION from 'utils/notification'; + +//!REDUX THUNK +import { Forget_Password_Initial } from 'redux/student/authentication_slice/auth_thunk'; + +//!IMPORT +import {Loading} from 'imports/loading_import' + +const TabForgetPassword = ({ setShowLogin, setForgetPage }) => { + const dispatch = useDispatch(); + + //Store Student + const { loading_forget_password } = useSelector((state) => ({ + ...state.auth_student, + })); + + const handleForgetStudent = (e) => { + e.preventDefault(); + + const values = HELPERS.formDataGeneral(e.target); + + //Check input + if (!values.email) { + return NOTIFICATION.notifyError('Email Không được bỏ trống !!!'); + } + + // Action Login + dispatch(Forget_Password_Initial(values)); + }; + return ( + +
setShowLogin(false)} style={{ cursor: 'pointer' }}> + +
+
+
+ Quên mật khẩu +
+ +
+ + +
+ +
+ {loading_forget_password ? : } +
+ setForgetPage(false)}> + Quay lại trang đăng nhập + +
+
+
+
+ ); +}; + +export default TabForgetPassword; diff --git a/frontend-manager-student/src/pages/Auth/Login/components/TabLogin.jsx b/frontend-manager-student/src/pages/Auth/Login/components/TabLogin.jsx new file mode 100644 index 0000000..f99718c --- /dev/null +++ b/frontend-manager-student/src/pages/Auth/Login/components/TabLogin.jsx @@ -0,0 +1,84 @@ +//!LIBRARY +import React, { useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; + +//! REDUX THUNK +import { Login_Mssv_Initial } from 'redux/student/authentication_slice/auth_thunk'; + +//! SHARE +import NOTIFICATION from 'utils/notification'; +import HELPERS from 'utils/helper'; + +//! IMPORT +import { TabForgetPassword } from 'imports/auth_import'; +const TabLogin = ({ showLogin, setShowLogin }) => { + const dispatch = useDispatch(); + + //Store Student + const { loading_login } = useSelector((state) => ({ + ...state.auth_student, + })); + + const [forgetPage, setForgetPage] = useState(false); + + const handleLoginStudent = (e) => { + e.preventDefault(); + + const values = HELPERS.formDataGeneral(e.target); + + //Check input + if (!values.mssv || !values.password) { + return NOTIFICATION.notifyError('Mã số sinh viên hoặc mật khẩu không chính xác !!!'); + } + + // Action Login + dispatch(Login_Mssv_Initial(values)); + }; + return ( + +
+ {!forgetPage ? ( + <> +
setShowLogin(false)} style={{ cursor: 'pointer' }}> + +
+
+
+ Đăng nhập +
+ +
+ + +
+ + +
+ + +
+
+ {loading_login ?

Loading...

: } +
+ setForgetPage(true)}> + Quên mật khẩu? + +
+
+
+ + ) : ( + <> + + + )} +
+
+ ); +}; + +export default TabLogin; diff --git a/frontend-manager-student/src/pages/Auth/Login/index.jsx b/frontend-manager-student/src/pages/Auth/Login/index.jsx index 9a19eb1..de01c8e 100644 --- a/frontend-manager-student/src/pages/Auth/Login/index.jsx +++ b/frontend-manager-student/src/pages/Auth/Login/index.jsx @@ -1,98 +1,14 @@ -//!LIBRARY -import { useDispatch } from 'react-redux'; +//! LIBRARY +import React from 'react'; -//! REDUX THUNK -import { Login_Mssv_Initial } from 'redux/student/authentication_slice/auth_thunk'; -import NOTIFICATION from 'utils/notification'; - -//! SHARE -import { useState } from 'react'; -import HELPERS from 'utils/helper'; +//! IMPORT +import { TabLogin } from 'imports/auth_import'; const Login = ({ showLogin, setShowLogin }) => { - const dispatch = useDispatch(); - const [forgetPage, setForgetPage] = useState(false); - // console.log('re-render'); - - const handleSubmit = (e) => { - e.preventDefault(); - - const values = HELPERS.formDataGeneral(e.target); - - //Check input - if (!values.mssv || !values.password) { - return NOTIFICATION.notifyError('Mã số sinh viên hoặc mật khẩu không chính xác !!!'); - } - - // Action Login - dispatch(Login_Mssv_Initial(values)); - }; - return ( -
- {!forgetPage ? ( - <> -
setShowLogin(false)} style={{ cursor: 'pointer' }}> - -
-
-
- Đăng nhập -
- -
- - -
- - -
- - -
-
- -
- setForgetPage(true)}> - Quên mật khẩu? - -
-
-
- - ) : ( - <> -
setShowLogin(false)} style={{ cursor: 'pointer' }}> - -
-
-
- Quên mật khẩu -
- -
- - -
- -
- -
- setForgetPage(false)}> - Quay lại trang đăng nhập - -
-
-
- - )} -
+ + + ); }; diff --git a/frontend-manager-student/src/pages/UserProfile/ProfileLayout.jsx b/frontend-manager-student/src/pages/UserProfile/ProfileLayout.jsx index 1f6a6f4..8198cf1 100644 --- a/frontend-manager-student/src/pages/UserProfile/ProfileLayout.jsx +++ b/frontend-manager-student/src/pages/UserProfile/ProfileLayout.jsx @@ -1,8 +1,13 @@ -import Helmet from 'components/Helmet'; -import Section, { SectionBody, SectionTitle } from 'components/Section'; +//! LIBRARY import { Col, Row } from 'react-bootstrap'; import { useSelector } from 'react-redux'; import { Link, useLocation } from 'react-router-dom'; + +//! COMPONENT +import Helmet from 'components/Helmet'; +import Section, { SectionBody, SectionTitle } from 'components/Section'; + +//! SHARE import { profileSidebar } from 'utils/dummy'; const UserProfile = (props) => { diff --git a/frontend-manager-student/src/pages/UserProfile/UserInfo.jsx b/frontend-manager-student/src/pages/UserProfile/UserInfo.jsx index 3926b94..aa08159 100644 --- a/frontend-manager-student/src/pages/UserProfile/UserInfo.jsx +++ b/frontend-manager-student/src/pages/UserProfile/UserInfo.jsx @@ -1,19 +1,35 @@ +//!LIBRARY import React, { useEffect, useState } from 'react'; import { Col, Row } from 'react-bootstrap'; -import { YearPicker, MonthPicker, DayPicker } from 'react-dropdown-date'; +import { DayPicker, MonthPicker, YearPicker } from 'react-dropdown-date'; import { useSelector } from 'react-redux'; +//! CUSTOMER HOOK +import useUploadCloud from 'custom_hook/uploadMediaCloud'; + +//!IMPORT +import { Loading } from 'imports/loading_import'; + const UserInfo = () => { + //Store Profile const { profile_student } = useSelector((state) => ({ ...state.auth_student, })); + //Store Media + const { result_upload, loading_media } = useSelector((state) => ({ + ...state.media, + })); + // date picking setting const [date, setDate] = useState({ year: '', month: '', day: '' }); const [gender, setGender] = useState(null); const [selectedFile, setSelectedFile] = useState(); const [preview, setPreview] = useState(); + //File custom hook media + const { handleUpload } = useUploadCloud(); + const onSelectFile = (e) => { if (!e.target.files || e.target.files.length === 0) { setSelectedFile(profile_student?.data?.avatar_uri); @@ -43,7 +59,7 @@ const UserInfo = () => { }, [selectedFile, profile_student?.data?.avatar_uri]); return ( - <> +
Thông tin tài khoản
Quản lý thông tin hồ sơ để bảo mật tài khoản
@@ -141,9 +157,9 @@ const UserInfo = () => {
- + {loading_media ? : }
- +
Dung lượng file tối đa 1 MB @@ -154,7 +170,7 @@ const UserInfo = () => {
- + ); }; diff --git a/frontend-manager-student/src/redux/media/upload_remove_media/media_slice.js b/frontend-manager-student/src/redux/media/upload_remove_media/media_slice.js new file mode 100644 index 0000000..1521895 --- /dev/null +++ b/frontend-manager-student/src/redux/media/upload_remove_media/media_slice.js @@ -0,0 +1,34 @@ +import { createSlice } from '@reduxjs/toolkit'; +import { Upload_Media_Initial } from './media_thunk'; +const initialState = { + loading_media: false, + error: null, + result_upload: null, + result_destroy: null, +}; +const Media_Cloud = createSlice({ + name: 'Media_Cloud', + initialState, + reducers: { + reset_upload: (state) => { + state.result_upload = null; + }, + }, + extraReducers: { + //* Upload Cloud + [Upload_Media_Initial.pending]: (state, action) => { + state.loading_media = true; + }, + [Upload_Media_Initial.fulfilled]: (state, action) => { + state.loading_media = false; + state.result_upload = action.payload.element; + }, + [Upload_Media_Initial.rejected]: (state, action) => { + state.loading_media = false; + state.error = action.payload; + }, + }, +}); +const Media_Cloud_Slice = Media_Cloud.reducer; +export const { reset_upload } = Media_Cloud.actions; +export default Media_Cloud_Slice; diff --git a/frontend-manager-student/src/redux/media/upload_remove_media/media_thunk.js b/frontend-manager-student/src/redux/media/upload_remove_media/media_thunk.js new file mode 100644 index 0000000..f41708b --- /dev/null +++ b/frontend-manager-student/src/redux/media/upload_remove_media/media_thunk.js @@ -0,0 +1,57 @@ +//! LIBRARY +import { createAsyncThunk } from '@reduxjs/toolkit'; +import axios from 'axios'; + +//! NOTIFICATION +import NOTIFICATION from 'utils/notification'; + +//! API STUDENT +import API_MEDIA from 'api/api_media'; + +//! SHARE +import CONSTANTS from 'configs/constants'; +import TEXT_NOTIFICATION from 'configs/text_notification'; +import { setToken } from 'utils/auth'; +import HELPERS from 'utils/helper'; + +/** + * @author Nguyễn Tiến Tài + * @created_at 15/03/2023 + * @descriptionKey Upload Media + * @function Upload_Media_Initial + * @return {Object} + */ +export const Upload_Media_Initial = createAsyncThunk('media/upload', async ({ formData }, { rejectWithValue }) => { + try { + //Call Api axios + const response = await axios.post( + `${API_MEDIA.UPLOAD_MEDIA}`, + formData, + { + headers: HELPERS.headerBrowserMedia(), + }, + ); + + //Take response Success + const successData = response?.data; + + // return result data + return successData; + + } catch (error) { + if (error) { + //Take response Error + const errorData = error.response.data; + + // return result data + const result_data = HELPERS.takeDataResponse(errorData); + + if (errorData) { + // Notification Error + NOTIFICATION.notifyError(result_data.data || result_data.message); + } + // return error + return rejectWithValue(result_data); + } + } +}); diff --git a/frontend-manager-student/src/redux/store.js b/frontend-manager-student/src/redux/store.js index 1fe95af..67af379 100644 --- a/frontend-manager-student/src/redux/store.js +++ b/frontend-manager-student/src/redux/store.js @@ -9,6 +9,7 @@ import CONSTANTS from 'configs/constants'; import AuthenticationSlice from './student/authentication_slice/auth_slice'; import CONFIGS from 'configs/configs'; import BookSlice from './student/book_slice/book_slice'; +import MediaSlice from './media/upload_remove_media/media_slice'; const rootReducer = (state, action) => { return AuthenticationSlice(state, action); }; @@ -17,6 +18,7 @@ const store = configureStore({ reducer: { auth_student: AuthenticationSlice, book: BookSlice, + media: MediaSlice, reducer: rootReducer, }, middleware: diff --git a/frontend-manager-student/src/redux/student/authentication_slice/auth_slice.js b/frontend-manager-student/src/redux/student/authentication_slice/auth_slice.js index 9de416a..f5e6521 100644 --- a/frontend-manager-student/src/redux/student/authentication_slice/auth_slice.js +++ b/frontend-manager-student/src/redux/student/authentication_slice/auth_slice.js @@ -7,9 +7,14 @@ import { Logout_Student_Initial, Profile_Student_Initial, Renew_Token_Student_Initial, + Change_Password_Initial, + Forget_Password_Initial } from './auth_thunk'; const initialState = { + loading_login: false, + loading_change_password: false, + loading_forget_password: false, loading: false, error: null, token_student: null, @@ -30,14 +35,14 @@ const Authentication = createSlice({ extraReducers: { //* POST LOGIN MSSV STUDENT [Login_Mssv_Initial.pending]: (state, action) => { - state.loading = true; + state.loading_login = true; }, [Login_Mssv_Initial.fulfilled]: (state, action) => { - state.loading = false; + state.loading_login = false; state.token_student = action.payload; }, [Login_Mssv_Initial.rejected]: (state, action) => { - state.loading = false; + state.loading_login = false; state.error = action.payload; }, @@ -62,7 +67,7 @@ const Authentication = createSlice({ }, [Logout_Student_Initial.fulfilled]: (state, action) => { - state.loading = true; + state.loading = false; state.token_student = null; state.profile_student = null; }, @@ -70,6 +75,20 @@ const Authentication = createSlice({ [Logout_Student_Initial.rejected]: (state, action) => { state.loading = true; }, + //* CHANGE PASSWORD STUDENT + [Change_Password_Initial.pending]: (state, action) => { + state.loading_change_password = true; + }, + + [Change_Password_Initial.fulfilled]: (state, action) => { + state.loading_change_password = true; + state.token_student = null; + state.profile_student = null; + }, + + [Change_Password_Initial.rejected]: (state, action) => { + state.loading_change_password = false; + }, //* GET RE_NEW_TOKEN [Renew_Token_Student_Initial.pending]: (state, action) => { @@ -83,6 +102,17 @@ const Authentication = createSlice({ state.loading = false; state.error = action.payload; }, + //* FORGET PASSWORD + [Forget_Password_Initial.pending]: (state, action) => { + state.loading_forget_password = true; + }, + [Forget_Password_Initial.fulfilled]: (state, action) => { + state.loading_forget_password = false; + }, + [Forget_Password_Initial.rejected]: (state, action) => { + state.loading_forget_password = false; + state.error = action.payload; + }, }, }); const AuthenticationSlice = Authentication.reducer; diff --git a/frontend-manager-student/src/redux/student/authentication_slice/auth_thunk.js b/frontend-manager-student/src/redux/student/authentication_slice/auth_thunk.js index df4fc99..95956b9 100644 --- a/frontend-manager-student/src/redux/student/authentication_slice/auth_thunk.js +++ b/frontend-manager-student/src/redux/student/authentication_slice/auth_thunk.js @@ -11,7 +11,7 @@ import API_STUDENT from 'api/api_user'; //! SHARE import CONSTANTS from 'configs/constants'; import TEXT_NOTIFICATION from 'configs/text_notification'; -import { setToken } from 'utils/auth'; +import { setToken, clearToken } from 'utils/auth'; import HELPERS from 'utils/helper'; /** @@ -41,7 +41,6 @@ export const Login_Mssv_Initial = createAsyncThunk('student/mssv', async ({ mssv //Take response Success const successData = response.data; - console.log(successData, 'login'); //Check data if (successData) { @@ -124,14 +123,14 @@ export const Profile_Student_Initial = createAsyncThunk('student/profile', async /** * @author Châu Gia Bảo * @created_at 06/03/2023 - * @descriptionKey Call api Profile Student + * @descriptionKey Call api Logout Student * @function Logout_Student_Initial * @return {Object} */ export const Logout_Student_Initial = createAsyncThunk('student/logout', async (_, { rejectWithValue }) => { try { //Call Api axios - const response = await axios.post(`${API_STUDENT.LOGOUT_STUDENT}`, { + const response = await axios.get(`${API_STUDENT.LOGOUT_STUDENT}`, { headers: HELPERS.headerBrowser(), withCredentials: true, }); @@ -144,6 +143,9 @@ export const Logout_Student_Initial = createAsyncThunk('student/logout', async ( // return result data const result_data = HELPERS.takeDataResponse(successData); + // Clear LocalStorage + clearToken(CONSTANTS.AUTH_TOKEN); + // notify success NOTIFICATION.notifySuccess(TEXT_NOTIFICATION.NOTIFICATION_LOGOUT_SUCCESS); @@ -218,3 +220,143 @@ export const Renew_Token_Student_Initial = createAsyncThunk('student/new/token', } } }); + +/** + * @author Nguyễn Tiến Tài + * @created_at 15/03/2023 + * @descriptionKey Call api Change Password Student + * @function Change_Password_Initial + * @return {Object} + */ +export const Change_Password_Initial = createAsyncThunk('student/changePassword', async ( + { oldPassword, password, confirmPassword }, + { rejectWithValue } +) => { + try { + //Call Api axios + const response = await axios.post( + `${API_STUDENT.CHANGE_PASSWORD_STUDENT}`, + { + input: { + user_change_password_input: { + oldPassword, + password, + confirmPassword + }, + }, + }, + { + headers: HELPERS.headerBrowser(), + }, + ); + + //Take response Success + const successData = response.data; + + //Check data + if (successData) { + // return result data + const result_data = HELPERS.takeDataResponse(successData); + + // Clear LocalStorage + clearToken(CONSTANTS.AUTH_TOKEN); + + // Notification Success + NOTIFICATION.notifySuccess(TEXT_NOTIFICATION.NOTIFICATION_CHANGE_PASSWORD_SUCCESS || result_data.message); + + // Notification login session expire + setTimeout(() => { + NOTIFICATION.swalLoginSessionExpired(TEXT_NOTIFICATION.NOTIFICATION_LOGIN_SESSION_EXPIRE || result_data.message); + }, CONSTANTS._2_SECOND) + + // return result data + return result_data; + } + } catch (error) { + if (error) { + //Take response Error + const errorData = error.response.data; + + // return result data + const result_data = HELPERS.takeDataResponse(errorData); + + if (errorData) { + // Notification Error + NOTIFICATION.notifyError(result_data.data || result_data.message); + } + + // return error + return rejectWithValue(result_data); + } + } +}); + +/** + * @author Nguyễn Tiến Tài + * @created_at 16/03/2023 + * @descriptionKey Call api Forget Password Student + * @function Change_Password_Initial + * @return {Object} + */ +/** + * @author Nguyễn Tiến Tài + * @created_at 15/03/2023 + * @descriptionKey Call api Change Password Student + * @function Change_Password_Initial + * @return {Object} + */ +export const Forget_Password_Initial = createAsyncThunk('student/forgetPassword', async ( + { email }, + { rejectWithValue } +) => { + try { + //Call Api axios + const response = await axios.post( + `${API_STUDENT.FORGET_PASSWORD_STUDENT}`, + { + input: { + user_forget_password_input: { + email + }, + }, + }, + { + headers: HELPERS.headerBrowser(), + }, + ); + + //Take response Success + const successData = response.data; + + //Check data + if (successData) { + // return result data + const result_data = HELPERS.takeDataResponse(successData); + + const message = HELPERS.getURIFromTemplate(TEXT_NOTIFICATION.NOTIFICATION_FORGET_PASSWORD_SUCCESS, { + email + }) + // Notification Success + NOTIFICATION.notifySuccess(message || result_data.message); + + // return result data + return result_data; + } + } catch (error) { + if (error) { + //Take response Error + const errorData = error.response.data; + + // return result data + const result_data = HELPERS.takeDataResponse(errorData); + + if (errorData) { + // Notification Error + NOTIFICATION.notifyError(result_data.data || result_data.message); + } + + // return error + return rejectWithValue(result_data); + } + } +}); diff --git a/frontend-manager-student/src/utils/auth.js b/frontend-manager-student/src/utils/auth.js index 748137e..f560800 100644 --- a/frontend-manager-student/src/utils/auth.js +++ b/frontend-manager-student/src/utils/auth.js @@ -44,7 +44,7 @@ export function setToken(key, value) { return localStorage.setItem(key, value); } -export function clearToken() { - localStorage.removeItem('access_token'); - return; +export function clearToken(key) { + return localStorage.removeItem(key); + } diff --git a/frontend-manager-student/src/utils/helper.js b/frontend-manager-student/src/utils/helper.js index 275386d..7fd97ec 100644 --- a/frontend-manager-student/src/utils/helper.js +++ b/frontend-manager-student/src/utils/helper.js @@ -4,6 +4,7 @@ import jwt_decode from 'jwt-decode'; //! SHARE import { getDeviceId, getToken } from './auth'; import CONSTANTS from 'configs/constants'; +import REGEX from './regex'; const HELPERS = { /** @@ -30,6 +31,30 @@ const HELPERS = { return headers; }, + /** + * @author Nguyễn Tiến Tài + * @created_at 15/03/2023 + * @descriptionKey return header media + * @function getToken + * @return {String} + */ + headerBrowserMedia: () => { + // add the authorization to the headers + const headers = { + 'Content-Type': 'multipart/form-data', + 'X-DEVICE-ID': getDeviceId(), + 'X-OS-TYPE': CONSTANTS.OS_TYPE_HEADER, + 'X-OS-VERSION': CONSTANTS.OS_VERSION_HEADER, + 'X-APP-VERSION': CONSTANTS.APP_VERSION_HEADER, + 'X-DEVICE-NAME': window.navigator.userAgent, + }; + const token = getToken(CONSTANTS.AUTH_TOKEN); + if (token) { + headers.Authorization = token ? `Bearer ${token}` : null; + } + + return headers; + }, /** * @author Nguyễn Tiến Tài * @created_at 02/03/2023 @@ -85,6 +110,16 @@ const HELPERS = { return false; } }, + /** + * @author Nguyễn Tiến Tài + * @created_at 16/03/2023 + * @description from String template to URI + * @param {template,data} + * @returns {string} + */ + getURIFromTemplate(template, data) { + return template.replace(REGEX.REGEX_IS_STRING_PARAM, (_, key) => data[key]); + }, }; export default HELPERS; diff --git a/frontend-manager-student/src/utils/notification.js b/frontend-manager-student/src/utils/notification.js index d02aa8f..4fe8480 100644 --- a/frontend-manager-student/src/utils/notification.js +++ b/frontend-manager-student/src/utils/notification.js @@ -1,6 +1,6 @@ //! LIBRARY import { toast } from 'react-toastify'; - +import Swal from 'sweetalert2'; //! SHARE import CONSTANTS from 'configs/constants'; @@ -36,5 +36,23 @@ const NOTIFICATION = { draggable: CONSTANTS.DELETED_ENABLE, }); }, + /** + * @author Nguyễn Tiến Tài + * @created_at 15/03/2023 + * @descriptionKey login session expired + */ + swalLoginSessionExpired(message) { + Swal.fire({ + title: message, + icon: 'warning', + showCancelButton: false, + confirmButtonColor: '#3085d6', + confirmButtonText: 'Đăng nhập lại', + }).then((result) => { + if (result.isConfirmed) { + window.location.href = '/' + } + }); + } }; export default NOTIFICATION; diff --git a/frontend-manager-student/src/utils/regex.js b/frontend-manager-student/src/utils/regex.js new file mode 100644 index 0000000..9df6362 --- /dev/null +++ b/frontend-manager-student/src/utils/regex.js @@ -0,0 +1,9 @@ +const REGEX = { + /** + * @author Nguyễn Tiến Tài + * @created_at 23/02/2022 + * @description REGEX is Replace string param + */ + REGEX_IS_STRING_PARAM: /\${(\w+)}/g, +} +export default REGEX;