diff --git a/.eslintcache b/.eslintcache index 51d57fb..8026594 100644 --- a/.eslintcache +++ b/.eslintcache @@ -1 +1 @@ -[{"D:\\WEB\\pixelteh\\src\\index.js":"1","D:\\WEB\\pixelteh\\src\\App.js":"2","D:\\WEB\\pixelteh\\src\\reportWebVitals.js":"3","D:\\WEB\\pixelteh\\src\\components\\LoginContainer.js":"4","D:\\WEB\\pixelteh\\src\\components\\Login.js":"5","D:\\WEB\\pixelteh\\src\\redux\\store.js":"6","D:\\WEB\\pixelteh\\src\\redux\\reducers\\index.js":"7","D:\\WEB\\pixelteh\\src\\redux\\reducers\\authReducer.js":"8","D:\\WEB\\pixelteh\\src\\redux\\actions\\authActions.js":"9","D:\\WEB\\pixelteh\\src\\sagas\\rootSaga.js":"10","D:\\WEB\\pixelteh\\src\\sagas\\userSaga.js":"11"},{"size":750,"mtime":1608653016664,"results":"12","hashOfConfig":"13"},{"size":834,"mtime":1608720258845,"results":"14","hashOfConfig":"13"},{"size":362,"mtime":499162500000,"results":"15","hashOfConfig":"13"},{"size":1489,"mtime":1608720143345,"results":"16","hashOfConfig":"13"},{"size":3060,"mtime":1608712915791,"results":"17","hashOfConfig":"13"},{"size":385,"mtime":1608716090779,"results":"18","hashOfConfig":"13"},{"size":183,"mtime":1608630649201,"results":"19","hashOfConfig":"13"},{"size":1379,"mtime":1608717726728,"results":"20","hashOfConfig":"13"},{"size":830,"mtime":1608717726804,"results":"21","hashOfConfig":"13"},{"size":182,"mtime":1608716131851,"results":"22","hashOfConfig":"13"},{"size":799,"mtime":1608717726877,"results":"23","hashOfConfig":"13"},{"filePath":"24","messages":"25","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"26"},"164dkoh",{"filePath":"27","messages":"28","errorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"29","messages":"30","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"26"},{"filePath":"31","messages":"32","errorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"33","usedDeprecatedRules":"26"},{"filePath":"34","messages":"35","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"26"},{"filePath":"36","messages":"37","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"26"},{"filePath":"38","messages":"39","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"26"},{"filePath":"40","messages":"41","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"26"},{"filePath":"42","messages":"43","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"26"},{"filePath":"44","messages":"45","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"26"},{"filePath":"46","messages":"47","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"26"},"D:\\WEB\\pixelteh\\src\\index.js",[],["48","49"],"D:\\WEB\\pixelteh\\src\\App.js",["50"],"D:\\WEB\\pixelteh\\src\\reportWebVitals.js",[],"D:\\WEB\\pixelteh\\src\\components\\LoginContainer.js",["51","52"],"import React from \"react\";\r\nimport * as Yup from 'yup';\r\nimport Login from \"./Login\";\r\nimport {useDispatch, useSelector} from \"react-redux\";\r\nimport {loginFailed, loginSuccess, setLogin} from \"../redux/actions/authActions\";\r\n\r\nconst LoginContainer = () => {\r\n\r\n const SignupSchema = Yup.object().shape({\r\n email: Yup.string()\r\n .email('Invalid email')\r\n .required('Required'),\r\n password: Yup.string()\r\n .min(8, 'Too Short!')\r\n .matches(\r\n /^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{8,}$/,\r\n 'Must Contain 8 Characters, One Uppercase, One Lowercase and one Number'\r\n )\r\n .required('Required'),\r\n });\r\n\r\n const dispatch = useDispatch();\r\n\r\n const { authData, isAuth, errorMessage } = useSelector(({ auth }) => auth);\r\n\r\n const submitAuth = (authData) => { debugger\r\n dispatch(setLogin(authData));\r\n localStorage.getItem(\"user\");\r\n if (localStorage.user !== 0) {\r\n dispatch(loginSuccess());\r\n } else {\r\n dispatch(loginFailed());\r\n console.log(errorMessage);\r\n }\r\n }\r\n\r\n return (\r\n \r\n )\r\n};\r\n\r\nexport default LoginContainer;","D:\\WEB\\pixelteh\\src\\components\\Login.js",[],"D:\\WEB\\pixelteh\\src\\redux\\store.js",[],"D:\\WEB\\pixelteh\\src\\redux\\reducers\\index.js",[],"D:\\WEB\\pixelteh\\src\\redux\\reducers\\authReducer.js",[],"D:\\WEB\\pixelteh\\src\\redux\\actions\\authActions.js",[],"D:\\WEB\\pixelteh\\src\\sagas\\rootSaga.js",[],"D:\\WEB\\pixelteh\\src\\sagas\\userSaga.js",[],{"ruleId":"53","replacedBy":"54"},{"ruleId":"55","replacedBy":"56"},{"ruleId":"57","severity":1,"message":"58","line":14,"column":11,"nodeType":"59","messageId":"60","endLine":14,"endColumn":23},{"ruleId":"57","severity":1,"message":"61","line":24,"column":13,"nodeType":"59","messageId":"60","endLine":24,"endColumn":21},{"ruleId":"57","severity":1,"message":"62","line":24,"column":23,"nodeType":"59","messageId":"60","endLine":24,"endColumn":29},"no-native-reassign",["63"],"no-negated-in-lhs",["64"],"no-unused-vars","'submitLogout' is assigned a value but never used.","Identifier","unusedVar","'authData' is assigned a value but never used.","'isAuth' is assigned a value but never used.","no-global-assign","no-unsafe-negation"] \ No newline at end of file +[{"D:\\WEB\\pixelteh\\src\\index.js":"1","D:\\WEB\\pixelteh\\src\\App.js":"2","D:\\WEB\\pixelteh\\src\\reportWebVitals.js":"3","D:\\WEB\\pixelteh\\src\\redux\\store.js":"4","D:\\WEB\\pixelteh\\src\\components\\HomePage.js":"5","D:\\WEB\\pixelteh\\src\\components\\Routes\\PrivateRoute.js":"6","D:\\WEB\\pixelteh\\src\\components\\Login\\LoginContainer.js":"7","D:\\WEB\\pixelteh\\src\\components\\Routes\\PublicRoute.js":"8","D:\\WEB\\pixelteh\\src\\sagas\\rootSaga.js":"9","D:\\WEB\\pixelteh\\src\\redux\\reducers\\index.js":"10","D:\\WEB\\pixelteh\\src\\components\\Login\\LogoutButton.js":"11","D:\\WEB\\pixelteh\\src\\components\\Login\\useAuth.js":"12","D:\\WEB\\pixelteh\\src\\components\\Login\\Login.js":"13","D:\\WEB\\pixelteh\\src\\sagas\\userSaga.js":"14","D:\\WEB\\pixelteh\\src\\redux\\reducers\\authReducer.js":"15","D:\\WEB\\pixelteh\\src\\redux\\actions\\authActions.js":"16"},{"size":750,"mtime":1608820263391,"results":"17","hashOfConfig":"18"},{"size":1017,"mtime":1608890440427,"results":"19","hashOfConfig":"18"},{"size":362,"mtime":499162500000,"results":"20","hashOfConfig":"18"},{"size":385,"mtime":1608716090779,"results":"21","hashOfConfig":"18"},{"size":242,"mtime":1608887470844,"results":"22","hashOfConfig":"18"},{"size":545,"mtime":1608832631102,"results":"23","hashOfConfig":"18"},{"size":1167,"mtime":1608896743518,"results":"24","hashOfConfig":"18"},{"size":552,"mtime":1608832631102,"results":"25","hashOfConfig":"18"},{"size":182,"mtime":1608716131851,"results":"26","hashOfConfig":"18"},{"size":183,"mtime":1608887470844,"results":"27","hashOfConfig":"18"},{"size":285,"mtime":1608832631101,"results":"28","hashOfConfig":"18"},{"size":768,"mtime":1608889040932,"results":"29","hashOfConfig":"18"},{"size":3058,"mtime":1608897094117,"results":"30","hashOfConfig":"18"},{"size":822,"mtime":1608888416348,"results":"31","hashOfConfig":"18"},{"size":1449,"mtime":1608832631104,"results":"32","hashOfConfig":"18"},{"size":804,"mtime":1608887607116,"results":"33","hashOfConfig":"18"},{"filePath":"34","messages":"35","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"36"},"164dkoh",{"filePath":"37","messages":"38","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"39","messages":"40","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"36"},{"filePath":"41","messages":"42","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"36"},{"filePath":"43","messages":"44","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"36"},{"filePath":"45","messages":"46","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"36"},{"filePath":"47","messages":"48","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"49","messages":"50","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"36"},{"filePath":"51","messages":"52","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"36"},{"filePath":"53","messages":"54","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"36"},{"filePath":"55","messages":"56","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"36"},{"filePath":"57","messages":"58","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"36"},{"filePath":"59","messages":"60","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"61","messages":"62","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"36"},{"filePath":"63","messages":"64","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"36"},{"filePath":"65","messages":"66","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"36"},"D:\\WEB\\pixelteh\\src\\index.js",[],["67","68"],"D:\\WEB\\pixelteh\\src\\App.js",[],"D:\\WEB\\pixelteh\\src\\reportWebVitals.js",[],"D:\\WEB\\pixelteh\\src\\redux\\store.js",[],"D:\\WEB\\pixelteh\\src\\components\\HomePage.js",[],"D:\\WEB\\pixelteh\\src\\components\\Routes\\PrivateRoute.js",[],"D:\\WEB\\pixelteh\\src\\components\\Login\\LoginContainer.js",[],"D:\\WEB\\pixelteh\\src\\components\\Routes\\PublicRoute.js",[],"D:\\WEB\\pixelteh\\src\\sagas\\rootSaga.js",[],"D:\\WEB\\pixelteh\\src\\redux\\reducers\\index.js",[],"D:\\WEB\\pixelteh\\src\\components\\Login\\LogoutButton.js",[],"D:\\WEB\\pixelteh\\src\\components\\Login\\useAuth.js",[],"D:\\WEB\\pixelteh\\src\\components\\Login\\Login.js",[],"D:\\WEB\\pixelteh\\src\\sagas\\userSaga.js",[],"D:\\WEB\\pixelteh\\src\\redux\\reducers\\authReducer.js",[],"D:\\WEB\\pixelteh\\src\\redux\\actions\\authActions.js",[],{"ruleId":"69","replacedBy":"70"},{"ruleId":"71","replacedBy":"72"},"no-native-reassign",["73"],"no-negated-in-lhs",["74"],"no-global-assign","no-unsafe-negation"] \ No newline at end of file diff --git a/src/App.js b/src/App.js index 1a5308c..a5d7003 100644 --- a/src/App.js +++ b/src/App.js @@ -1,32 +1,42 @@ -import React from "react"; -import LoginContainer from "./components/LoginContainer"; -import {useDispatch, useSelector} from "react-redux"; +import React, {useEffect} from "react"; +import LoginContainer from "./components/Login/LoginContainer"; import "bootstrap/dist/css/bootstrap.min.css"; -import {logoutFailed, logoutSuccess, setLogout} from "./redux/actions/authActions"; +import {Switch, Redirect} from "react-router-dom"; +import HomePage from "./components/HomePage"; +import PrivateRoute from "./components/Routes/PrivateRoute"; +import PublicRoute from "./components/Routes/PublicRoute"; +import {useAuth} from "./components/Login/useAuth"; function App() { - const dispatch = useDispatch(); + const {onLoginSuccess} = useAuth(); - const { errorMessage } = useSelector(({ auth }) => auth); - -//will be passed to the home page - const submitLogout = () => { - dispatch(setLogout()); - localStorage.getItem("user"); - if (localStorage.user === 0) { - dispatch(logoutSuccess()); - } else { - dispatch(logoutFailed()); - console.log(errorMessage); - } - }; + useEffect(() => { + if (localStorage.user){ + onLoginSuccess(); + } // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); return (
- + + + + +
); } export default App; + + + + + + + + + + + diff --git a/src/components/HomePage.js b/src/components/HomePage.js new file mode 100644 index 0000000..ced4a89 --- /dev/null +++ b/src/components/HomePage.js @@ -0,0 +1,13 @@ +import React from "react"; +import LogoutButton from "./Login/LogoutButton"; + +function HomePage() { + return ( + <> +
Home Page
+ + + ) +} + +export default HomePage; \ No newline at end of file diff --git a/src/components/Login.js b/src/components/Login/Login.js similarity index 99% rename from src/components/Login.js rename to src/components/Login/Login.js index 1d7eab5..2efbdbd 100644 --- a/src/components/Login.js +++ b/src/components/Login/Login.js @@ -6,7 +6,6 @@ import Row from "react-bootstrap/Row"; import Col from "react-bootstrap/Col"; const Login = ({initialValues, validationSchema, submitButtonText, onSubmit}) => { - return ( <> { @@ -14,24 +14,19 @@ const LoginContainer = () => { .min(8, 'Too Short!') .matches( /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/, - 'Must Contain 8 Characters, One Uppercase, One Lowercase and one Number' + 'Must contain 8 Characters, 1 Uppercase, 1 Lowercase, 1 Number' ) .required('Required'), }); - const dispatch = useDispatch(); + const {onLogin, isAuth} = useAuth(); - const { authData, isAuth, errorMessage } = useSelector(({ auth }) => auth); + const submitAuth = (authData) => { + onLogin(authData); + } - const submitAuth = (authData) => { debugger - dispatch(setLogin(authData)); - localStorage.getItem("user"); - if (localStorage.user !== 0) { - dispatch(loginSuccess()); - } else { - dispatch(loginFailed()); - console.log(errorMessage); - } + if (isAuth) { + return ; } return ( diff --git a/src/components/Login/LogoutButton.js b/src/components/Login/LogoutButton.js new file mode 100644 index 0000000..660be66 --- /dev/null +++ b/src/components/Login/LogoutButton.js @@ -0,0 +1,13 @@ +import React from "react"; +import {useAuth} from "./useAuth"; + +const LogoutButton = () => { + const {onLogout, isAuth} = useAuth(); + return isAuth ? ( + + ) : null +} + +export default LogoutButton; \ No newline at end of file diff --git a/src/components/Login/useAuth.js b/src/components/Login/useAuth.js new file mode 100644 index 0000000..352e65c --- /dev/null +++ b/src/components/Login/useAuth.js @@ -0,0 +1,22 @@ +import {useCallback} from "react"; +import {useDispatch, useSelector} from "react-redux"; +import {loginRequest, loginSuccess, logoutRequest} from "../../redux/actions/authActions"; + +export function useAuth() { + const dispatch = useDispatch(); + const { authData, isAuth, isLoading, errorMessage } = useSelector(({ auth }) => auth); + + const onLogin = useCallback((authData) => dispatch(loginRequest(authData)), [dispatch]); + const onLogout = useCallback(() => dispatch(logoutRequest()), [dispatch]); + const onLoginSuccess = useCallback(() => dispatch(loginSuccess()), [dispatch]); + + return { + authData, + isAuth, + isLoading, + errorMessage, + onLogout, + onLogin, + onLoginSuccess + } +} \ No newline at end of file diff --git a/src/components/Routes/PrivateRoute.js b/src/components/Routes/PrivateRoute.js new file mode 100644 index 0000000..98bf6a8 --- /dev/null +++ b/src/components/Routes/PrivateRoute.js @@ -0,0 +1,22 @@ +import React from "react"; +import { Route, Redirect } from "react-router-dom"; +import {useAuth} from "../Login/useAuth"; + +function PrivateRoute({ component: Component, ...rest }) { + + const { isAuth } = useAuth(); + return ( + + isAuth + ? ( + + ) : ( + + ) + } + /> + ); +} + +export default PrivateRoute; \ No newline at end of file diff --git a/src/components/Routes/PublicRoute.js b/src/components/Routes/PublicRoute.js new file mode 100644 index 0000000..b63f24d --- /dev/null +++ b/src/components/Routes/PublicRoute.js @@ -0,0 +1,22 @@ +import React from "react"; +import {Route, Redirect} from "react-router-dom"; +import {useAuth} from "../Login/useAuth"; + +function PublicRoute({component: Component, ...rest}) { + + const {isAuth} = useAuth(); + return ( + + !isAuth + ? ( + + ) : ( + + ) + } + /> + ); +} + +export default PublicRoute; \ No newline at end of file diff --git a/src/index.sass b/src/index.sass index 95f34d4..08126c7 100644 --- a/src/index.sass +++ b/src/index.sass @@ -18,6 +18,23 @@ code text-align: center box-shadow: 0px 17px 24px 0px rgba(48, 63, 159, 0.2) +@media only screen and (max-width : 992px) + .App + min-width: 320px + min-height: 350px + margin: 100px + + .row + margin: 0 + + .col + margin: 0 +@media only screen and (max-width : 576px) + .App + margin: 0 + padding: 0 + height: 100% + .form margin-top: 10% @@ -28,15 +45,18 @@ code &__field margin-bottom: 25px - position: relative + &_input width: 80% + position: relative &__error position: absolute top: 30px - left: 25px + left: 15% + font-size: 11px + max-width: 320px white-space: nowrap overflow: hidden text-overflow: ellipsis diff --git a/src/redux/actions/authActions.js b/src/redux/actions/authActions.js index dc66bf7..b4c3e0a 100644 --- a/src/redux/actions/authActions.js +++ b/src/redux/actions/authActions.js @@ -1,36 +1,35 @@ -export const SET_LOGIN = "SET_LOGIN"; -export const SET_LOGOUT = "SET_LOGOUT"; +export const LOGIN_REQUEST = "LOGIN_REQUEST"; export const LOGIN_SUCCESS = "LOGIN_SUCCESS"; export const LOGIN_FAILED = "LOGIN_FAILED"; + +export const LOGOUT_REQUEST = "LOGOUT_REQUEST"; export const LOGOUT_SUCCESS = "LOGOUT_SUCCESS"; export const LOGOUT_FAILED = "LOGOUT_FAILED"; -export const setLogin = (authData) => ({ - type: SET_LOGIN, +export const loginRequest = (authData) => ({ + type: LOGIN_REQUEST, payload: authData, }); -export const setLogout = () => ({ - type: SET_LOGOUT, - payload: null, -}); - export const loginSuccess = () => ({ - type: LOGIN_SUCCESS, - payload: true, + type: LOGIN_SUCCESS }); + export const loginFailed = () => ({ type: LOGIN_FAILED, payload: "login failed" }); +export const logoutRequest = () => ({ + type: LOGOUT_REQUEST +}); + export const logoutSuccess = () => ({ - type: LOGIN_SUCCESS, - payload: false, + type: LOGOUT_SUCCESS }); export const logoutFailed = () => ({ - type: LOGIN_FAILED, + type: LOGOUT_FAILED, payload: "logout failed" }); diff --git a/src/redux/reducers/authReducer.js b/src/redux/reducers/authReducer.js index bb241b6..c5be9a6 100644 --- a/src/redux/reducers/authReducer.js +++ b/src/redux/reducers/authReducer.js @@ -1,55 +1,57 @@ import { - LOGIN_FAILED, + LOGIN_REQUEST, LOGIN_SUCCESS, - LOGOUT_FAILED, + LOGIN_FAILED, + LOGOUT_REQUEST, LOGOUT_SUCCESS, - SET_LOGIN, - SET_LOGOUT + LOGOUT_FAILED, } from "../actions/authActions"; let initialState = { authData: null, isAuth: false, - error: false, - errorMessage: "" + isLoading: false, + errorMessage: null }; const authReducer = (state = initialState, action) => { - console.log("state => ", state) switch (action.type) { - case SET_LOGIN: + case LOGIN_REQUEST: return { ...state, authData: action.payload, + isLoading: true } case LOGIN_SUCCESS: return { ...state, - isAuth: action.payload, + isAuth: true, + isLoading: false } case LOGIN_FAILED: return { ...state, - error: true, - errorMessage: action.payload + errorMessage: action.payload, + isLoading: false } - case SET_LOGOUT: + case LOGOUT_REQUEST: return { ...state, - authData: action.payload, - isAuth: false + authData: null, + isLoading: true } case LOGOUT_SUCCESS: return { ...state, - isAuth: action.payload, + isAuth: false, + isLoading: false } case LOGOUT_FAILED: return { ...state, - error: true, - errorMessage: action.payload + errorMessage: action.payload, + isLoading: false } default: return state; diff --git a/src/sagas/userSaga.js b/src/sagas/userSaga.js index 008ae20..a66cd74 100644 --- a/src/sagas/userSaga.js +++ b/src/sagas/userSaga.js @@ -1,17 +1,17 @@ import { put, all, takeLatest } from "redux-saga/effects"; import { + LOGIN_REQUEST, + LOGOUT_REQUEST, loginFailed, loginSuccess, logoutFailed, logoutSuccess, - SET_LOGIN, - SET_LOGOUT } from "../redux/actions/authActions"; function* login({ payload }) { try { - localStorage.setItem("user", JSON.stringify(payload)); + localStorage.setItem("user", JSON.stringify(payload.email)); yield put(loginSuccess(payload)); } catch (error) { @@ -31,7 +31,7 @@ function* logout() { export default function* userSaga() { yield all([ - yield takeLatest(SET_LOGIN, login), - yield takeLatest(SET_LOGOUT, logout), + yield takeLatest(LOGIN_REQUEST, login), + yield takeLatest(LOGOUT_REQUEST, logout), ]); } \ No newline at end of file