diff --git a/netlify.toml b/netlify.toml new file mode 100644 index 0000000..ee8f601 --- /dev/null +++ b/netlify.toml @@ -0,0 +1,13 @@ +[[redirects]] + from = "/*" + to = "/index.html" + status = 200 + +[context.production.environment] + REACT_APP_SVR_API = "https://stonehaven-academy.herokuapp.com/api" + +[context.branch-deploy.environment] + REACT_APP_SVR_API = "https://stonehaven-server-staging.herokuapp.com/api" + +[context.deploy-preview.environment] + REACT_APP_SVR_API = "https://stonehaven-server-staging.herokuapp.com/api" \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 2bc6c44..99c4a16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4640,6 +4640,28 @@ } } }, + "eslint-config-airbnb": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-18.0.1.tgz", + "integrity": "sha512-hLb/ccvW4grVhvd6CT83bECacc+s4Z3/AEyWQdIT2KeTsG9dR7nx1gs7Iw4tDmGKozCNHFn4yZmRm3Tgy+XxyQ==", + "dev": true, + "requires": { + "eslint-config-airbnb-base": "^14.0.0", + "object.assign": "^4.1.0", + "object.entries": "^1.1.0" + } + }, + "eslint-config-airbnb-base": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.0.0.tgz", + "integrity": "sha512-2IDHobw97upExLmsebhtfoD3NAKhV4H0CJWP3Uprd/uk+cHuWYOczPVxQ8PxLFUAw7o3Th1RAU8u1DoUpr+cMA==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.7", + "object.assign": "^4.1.0", + "object.entries": "^1.1.0" + } + }, "eslint-config-prettier": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.4.0.tgz", @@ -7949,6 +7971,11 @@ "object.assign": "^4.1.0" } }, + "jwt-decode": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz", + "integrity": "sha1-fYa9VmefWM5qhHBKZX3TkruoGnk=" + }, "killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", @@ -9040,6 +9067,18 @@ "object-keys": "^1.0.11" } }, + "object.entries": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", + "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, "object.fromentries": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.0.tgz", diff --git a/package.json b/package.json index 325b50b..cacd984 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "axios": "^0.19.0", "dotenv": "^8.1.0", "isotope-layout": "^3.0.6", + "jwt-decode": "^2.2.0", "node-sass": "^4.12.0", "prop-types": "^15.7.2", "react": "^16.8.6", @@ -39,6 +40,7 @@ ] }, "devDependencies": { + "eslint-config-airbnb": "^18.0.1", "eslint-config-prettier": "^6.4.0" } } diff --git a/src/App.js b/src/App.js index 495fdd2..a4da35a 100644 --- a/src/App.js +++ b/src/App.js @@ -2,14 +2,15 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Switch, Route, Redirect } from 'react-router-dom'; import DUMMY_DATA from './DUMMY_DATA'; +import { tokenServices, ProtectedRoute } from './utils'; import { NavBar, HomePage, PathPage, LessonPage, CatalogPage, - // RegistrationPage, - // DashboardPage, + RegistrationPage, + DashboardPage, SupportPage, AboutPage, ErrorPage, @@ -27,54 +28,115 @@ const links = { dashboard: '/dashboard', }; -function App() { - const { courses } = DUMMY_DATA; +class App extends React.Component { + constructor(props) { + super(props); + this.state = { + user: null, + isAuthenticated: false, + }; + } - return ( -
- - - - } - /> - - {/* */} - {/* */} - - + // componentWillMount = () => { + // const { history, location } = this.props; + // console.log(history); + // console.log(location); + // } + + /** + * get user data from database + */ + componentDidMount = () => { + const user = tokenServices.getToken(); + if (user) { + this.setState({ isAuthenticated: true, user }); + } + }; + + handleLogin = () => { + const user = tokenServices.getToken(); + if (user) { + this.setState({ isAuthenticated: true, user }); + } else { + this.setState({ isAuthenticated: null, user: null }); + } + }; - - { - const courseObj = courses.find( - course => course.slug === props.match.params.course, - ); - const prevCourse = courses.find(course => course.order === courseObj.order - 1); - const nextCourse = courses.find(course => course.order === courseObj.order + 1); - const order = parseInt(props.match.params.order, 10); - if (!courseObj || order >= courseObj.lessons.length) return ; - return ( - { + tokenServices.removeToken(); + this.setState({ isAuthenticated: false, user: null }); + }; - /> - ); - }} + render() { + const { courses } = DUMMY_DATA; + const { user, isAuthenticated } = this.state; + + return ( +
+ - - -
- ); + + + } + /> + + {/* Login Protected Route */} + + {/* Dashboard Protected Route */} + + + + + + { + const courseObj = courses.find( + course => course.slug === props.match.params.course, + ); + const prevCourse = courses.find( + course => course.order === courseObj.order - 1, + ); + const nextCourse = courses.find( + course => course.order === courseObj.order + 1, + ); + const order = parseInt(props.match.params.order, 10); + if (!courseObj || order >= courseObj.lessons.length) return ; + return ( + + ); + }} + /> + + +
+ ); + } } App.propTypes = { diff --git a/src/components/NavBar.js b/src/components/NavBar.js index 8d839db..9c96142 100644 --- a/src/components/NavBar.js +++ b/src/components/NavBar.js @@ -11,9 +11,17 @@ class NavBar extends React.Component { this.state = { menuOpen: false, + isAuthenticated: false, }; } + componentDidUpdate(prevProps, prevState) { + const { isAuthenticated } = this.props; + if (isAuthenticated !== prevState.isAuthenticated) { + this.setState({ isAuthenticated }); + } + } + // This keeps your state in sync with the opening/closing of the menu // via the default means, e.g. clicking the X, pressing the ESC key etc. handleStateChange(state) { @@ -61,14 +69,14 @@ class NavBar extends React.Component {
  • Catalog
  • - {/* Commented out until implemented */}
  • About
  • Support
  • this.closeMenu()} target="_blank" rel="noopener noreferrer" href="https://forms.gle/2YMiTeQ4iuZByx4ZA">Feedback
  • - {/*
  • Dashboard
  • */} - {/*
  • Login
  • */} + {this.state.isAuthenticated ? (
  • Dashboard
  • ) : null} + {this.state.isAuthenticated ? (
  • {this.props.handleLogoff();}} to="/">Log Off
  • ) : ( +
  • Login
  • )}
    this.closeMenu()} to={links.about}> About - this.closeMenu()} to={links.login}> - LogIn - - this.closeMenu()} to={links.dashboard} /> this.closeMenu()} to={links.support}> Support this.closeMenu()} target="_blank" rel="noopener noreferrer" href="https://forms.gle/2YMiTeQ4iuZByx4ZA">Feedback + + {this.state.isAuthenticated ? ( + this.closeMenu()} to={links.dashboard}> + Dashboard + + ) : (null + )} + {this.state.isAuthenticated ? ({ this.props.handleLogoff();}} to="/">Log Off) : ( + Login)} + +
    @@ -107,6 +122,7 @@ NavBar.propTypes = { links: PropTypes.shape({ home: PropTypes.string, }).isRequired, + isAuthenticated: PropTypes.bool, }; export default NavBar; diff --git a/src/components/dashboard/DashboardMenu.js b/src/components/dashboard/DashboardMenu.js new file mode 100644 index 0000000..5368a3f --- /dev/null +++ b/src/components/dashboard/DashboardMenu.js @@ -0,0 +1,50 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +/** + * Dashboard navigation menu + * @param {Object} props Passed props + * @param {string} activeView Active navigation text + * @param {Function} showOverview Display function handler + * @param {Function} showSettings Display function handler + * @param {Function} logout Logout function handler + */ +const DashboardMenu = ({ activeView, showOverview, showSettings, logout }) => ( +
    +

    My Dashboard

    + +
    +); + +DashboardMenu.propTypes = { + activeView: PropTypes.string.isRequired, + showOverview: PropTypes.func.isRequired, + showSettings: PropTypes.func.isRequired, + logout: PropTypes.func, +}; + +export default DashboardMenu; diff --git a/src/components/pages/DashboardPage.js b/src/components/pages/DashboardPage.js index bdc0c71..d3a38a2 100644 --- a/src/components/pages/DashboardPage.js +++ b/src/components/pages/DashboardPage.js @@ -1,7 +1,71 @@ import React from 'react'; +import tokenService from '../../utils/tokenServices'; +import DashboardMenu from '../dashboard/DashboardMenu'; +import '../../css/pages/DashboardPage.scss'; -const DashboardPage = () => ( -
    I am a dashboard page that will route to the correct dashboard type!
    -); + +class DashboardPage extends React.Component{ + state = { + userName: '', + userID: '', + userPersona: '', + active: 'overview', + } + + componentDidMount(){ + const token = tokenService.getToken(); + if(!token){ + // Redirect to HTTP ErrorCode 500 page, this is a placeholder, replace with real + this.props.history.push('/login'); + } else { + this.setState({userID: token.id}); + this.setState({userPersona: token.persona}); + this.setState({userName: token.fullName}); + } + } + + goToOverview = () => { + this.setState({ active: 'overview' }); + }; + + goToSettings = () => { + this.setState({ active: 'settings' }); + }; + + render(){ + return ( +
    + + {this.state.active === 'overview' ? ( +
    +

    + {`Welcome, ${this.state.userName || 'Anon'}`} +

    +
    + ) : ( + '' + )} + {this.state.active === 'settings' ? ( +
    +

    + {this.state.userName} +'s Account +

    +

    + {`User ID: ${this.state.userID}`} +

    +
    + ) : ( + '' + )} +
    + ); + } +} export default DashboardPage; diff --git a/src/components/pages/HomePage.js b/src/components/pages/HomePage.js index eec8f96..c7133fc 100644 --- a/src/components/pages/HomePage.js +++ b/src/components/pages/HomePage.js @@ -1,5 +1,4 @@ import React from 'react'; -import { NavLink } from 'react-router-dom'; import '../../css/pages/HomePage.scss'; // import Glide from '@glidejs/glide/'; diff --git a/src/components/pages/RegistrationPage.js b/src/components/pages/RegistrationPage.js index ff45702..fdf16eb 100644 --- a/src/components/pages/RegistrationPage.js +++ b/src/components/pages/RegistrationPage.js @@ -1,7 +1,8 @@ import React, { Component } from 'react'; +import PropTypes from 'prop-types'; import LogIn from '../registration/LogIn'; import SignUp from '../registration/SignUp'; - +import TokenServices from '../../utils/tokenServices' import '../../css/pages/RegistrationPage.scss'; class RegistrationPage extends Component { @@ -13,6 +14,25 @@ class RegistrationPage extends Component { } } + /** + * Checks wether or not the user is already logged in + */ + componentDidMount() { + if(TokenServices.getToken()){ + // console.log("You are logged in redirecting") + // this.props.history.push("/dashboard"); + this.handleLogin() + }else{ + // console.log("You are not logged in doing nothing") + } + } + + handleLogin = event => { + // event.preventDefault(); + // this.props.history.push("/dashboard"); + this.props.handleLogin(); + } + changeToSignup = event => { event.preventDefault(); this.setState({ hideSignUp: false }); @@ -28,11 +48,15 @@ class RegistrationPage extends Component { return (
    {hideSignUp - ? this.changeToSignup} /> - : this.backToLogin} /> } + ? this.changeToSignup} handleLogin={this.handleLogin}/> + : this.backToLogin} handleSignup={this.handleLogin}/> }
    ) } } +RegistrationPage.propTypes = { + handleLogin: PropTypes.func.isRequired, +}; + export default RegistrationPage; diff --git a/src/components/registration/LogIn.js b/src/components/registration/LogIn.js index 486e508..069e254 100644 --- a/src/components/registration/LogIn.js +++ b/src/components/registration/LogIn.js @@ -1,65 +1,90 @@ -import React,{Component} from 'react'; -import Axios from 'axios'; +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import axios from 'axios'; import '../../css/registration/LogIn.scss'; +const { REACT_APP_SVR_API } = process.env; -class LogIn extends Component{ +class LogIn extends Component { constructor(props) { super(props); this.state = { email: '', password: '', - } + }; } - LogInHandler = () =>{ + LogInHandler = () => { const { email, password } = this.state; const data = { email, password, - } - - Axios.post('http://localhost:5001/api/user/login',data) - .then(response=>{ + }; + + axios + .post(`${REACT_APP_SVR_API}/user/login`, data) + .then(response => { localStorage.setItem('app-token', response.data.token); + // this.props.history.push("/dashboard"); + this.props.handleLogin(); }) - .catch(error=>{ - this.showSuccess = false; - this.showError = true; - try{ - if(error.response.status === 400){ - console.log('Bad Request'); - }else if(error.response.status === 500){ - console.log('Something bad happended on the server.') - }else{ - console.log('Uh oh...') + .catch(error => { + try { + // Handles errors that are not HTTP specific + console.error(error); + this.setState({ showRegistrationFailure: true }); + if (!error.status) { + console.error('A network error has occured.'); + } else if (error.response.status === 400) { + console.error('Bad Request'); + } else if (error.response.status === 500) { + console.error('Something bad happended on the server.'); + } else { + console.error('An unknown error has occurred'); } - }catch(ex){ - this.errors = error.response.data; - Promise.reject(error); + } catch (ex) { + Promise.reject(ex); } - }) - } + }); + }; - render(){ + render() { const { email, password } = this.state; const { changeToSignup } = this.props; return (
    -

    Login

    +

    Login

    Email

    - this.setState({email:event.target.value})}/> + this.setState({ email: event.target.value })} + />

    Password

    - this.setState({password:event.target.value})}/> - + this.setState({ password: event.target.value })} + /> +
    -

    Don't have an account,

    +

    + {`Don't have an account, `} + +

    ); } } +LogIn.propTypes = { + handleLogin: PropTypes.func.isRequired, +}; -export default LogIn; \ No newline at end of file +export default LogIn; diff --git a/src/components/registration/SignUp.js b/src/components/registration/SignUp.js index 9cce2f8..fea739b 100644 --- a/src/components/registration/SignUp.js +++ b/src/components/registration/SignUp.js @@ -1,9 +1,26 @@ import React, { Component } from 'react'; -import Axios from 'axios'; +import PropTypes from 'prop-types'; +import axios from 'axios'; import '../../css/registration/SignUp.scss'; +const { REACT_APP_SVR_API } = process.env; + +/** + * @description Validates all inputs to theses constraints + */ +const validate = (email, password, confirmedPassword, fullName, location) => ({ + email: email.length === 0, + password: password.length < 8 || password !== confirmedPassword, + fullName: fullName.length === 0, + location: location.length === 0, +}); + +const WarningBanner = warn_data => + // eslint-disable-next-line implicit-arrow-linebreak + warn_data.warn ?
    {warn_data.message}
    : null; + class SignUp extends Component { - constructor(props){ + constructor(props) { super(props); this.state = { @@ -13,73 +30,51 @@ class SignUp extends Component { confirmPassword: '', location: '', fullName: '', - // Might have to refactored this... + // Might have to refactor this... touched: { email: false, password: false, }, - // Controls when there are failed validations when registering - disableButton: true, - // Modal - showModal: false, - // Shows error message if server fails - showRegistrationFailure: false, - // Shows when waiting for server response - showSpinner: false, - successfulRegister: false, - } + }; } - - - /** - * Controls modal and spinner controlls - */ - showModal = () => { - this.setState({ showModal: true }); - }; - - hideModal = () => { - this.setState({ showModal: false }); - }; - - showSpinner = () => { - this.setState({ loading: true }); - }; - - hideSpinner = () => { - this.setState({ loading: false }); - }; /** * Creates a user with the server */ createUserHandler = () => { const { email, password, location, fullName } = this.state; + const { handleSignup } = this.props; const data = { email, password, location, fullName, }; - Axios.post('http://localhost:5001/api/user/', data) + axios + .post(`${REACT_APP_SVR_API}/user/`, data) .then(response => { - console.log(response); + // console.log(response); // Stores token in local storeage for the time being localStorage.setItem('app-token', response.data.token); // Sweet Alert for successful registration - // Redirect to dashboard + handleSignup(); }) .catch(error => { - this.setState({ showRegistrationFailure: true }); try { - if (error.response.status === 400) { - console.log('Bad Request'); + // Handles errors that are not HTTP specific + console.error(error); + this.setState({ showRegistrationFailure: true }); + if (!error.status) { + console.error('A network error has occured.'); + } else if (error.response.status === 400) { + console.error('Bad Request'); } else if (error.response.status === 500) { - console.log('Something bad happended on the server.'); + console.error('Something bad happended on the server.'); } else { - console.log('Uh oh...'); + console.error('An unknown error has occurred'); } } catch (ex) { + alert('Something went wrong...'); Promise.reject(ex); } }); @@ -108,11 +103,11 @@ class SignUp extends Component { }; handleBlur = field => event => { - this.setState({ - touched: { ...this.state.touched, [field]: true }, - }); + this.setState(prevState => ({ + touched: { ...prevState.touched, [field]: true }, + })); }; - + /** * @description Controls the submit button */ @@ -126,38 +121,30 @@ class SignUp extends Component { }; canBeSubmitted() { - const { email, password, confirmPassword, location, fullName } = this.state; + const { email, password, confirmPassword, location, fullName } = this.state; const errors = validate( email, password, - confirmPassword, + confirmPassword, // Moda fullName, - location + location, ); const isDisabled = Object.keys(errors).some(x => errors[x]); return !isDisabled; } - render() { - const { email, password, confirmPassword, location, fullName } = this.state; + const { email, password, confirmPassword, location, fullName, touched } = this.state; const { backToLogin } = this.props; - const errors = validate( - email, - password, - confirmPassword, - fullName, - location - ); + const errors = validate(email, password, confirmPassword, fullName, location); const isDisabled = Object.keys(errors).some(x => errors[x]); const shouldMarkError = field => { const hasError = errors[field]; - const shouldShow = this.state.touched[field]; + const shouldShow = touched[field]; return hasError ? shouldShow : false; }; - return (

    Register

    @@ -166,25 +153,25 @@ class SignUp extends Component {

    Password

    Confirm Password

    @@ -192,33 +179,38 @@ class SignUp extends Component {

    Location

    -

    - By clicking "Sign Up" you are agreeing - to our Terms and Agreement -

    + {/*

    + {`By clicking "Sign Up" you are agreeing to our `} + Terms and Agreement +

    */} -

    Go back to

    +

    + {`Go back to `} + +

    ({ - email: email.length === 0, - password: password.length < 8 || password !== confirmedPassword, - fullName: fullName.length === 0, - location: location.length === 0, -}); - -function WarningBanner(props) { - if (!props.warn) { - return null; - } - return
    {props.message}
    ; -} - +SignUp.propTypes = { + handleSignup: PropTypes.func.isRequired, +}; export default SignUp; diff --git a/src/css/pages/DashboardPage.scss b/src/css/pages/DashboardPage.scss index 07f22e3..18e1df8 100644 --- a/src/css/pages/DashboardPage.scss +++ b/src/css/pages/DashboardPage.scss @@ -1,3 +1,149 @@ -#an-id { +//TODO: Resolve class names to be camelCase or kabob-case + +.dashboard { + display: flex; + min-height: calc(100vh - 80px); +} + +@media screen and (max-width: 860px) { + .dashboard { + flex-direction: column; + align-items: stretch; + text-align: center; + } + + .accountForms form { + padding: 0; + display: flex; + flex-direction: column; + align-items: center; + } + + .accountForms form label input { + margin: 10px 0; + } + + .accountForms form button { + margin: 20px auto; + width: 100%; + max-width: 2000px; + } +} + +.dashboardHeader, .menuHeader{ + display: block; + background-color: transparent; + border: none; + font-size: 1.5rem; + font-family: var(--fontfamily-alt); + padding: 0; + float: none; + margin-left: 0; +} + +.menuHeader{ + color: var(--white); + margin-bottom: 10px; + font-family: var(--fontfamily); +} + +.dashboardHeader, .dashboardText{ + color: var(--black); +} + +.dashboardBody { + padding: 50px; + flex: 1; +} + +.dashboardBody p { + padding: 15px 0; + margin: 0; + font-family: var(--fontfamily-roboto); +} + +.dashboard .activeView { + font-weight: bolder; + font-family: var(--fontfamily-roboto); + color: var(--greyblue); +} + +.accountMenu { + padding: 30px; + background-color: var(--light-blue); +} + +.accountMenu ul { + list-style: none; + padding: 0; +} + +.accountMenu ul li { + margin-bottom: 15px; +} + +.accountMenu .activeView { + color: var(--orange); +} + +.linkButton { + background-color: transparent; + border: none; + cursor: pointer; + display: inline; + margin: 0; + padding: 0; + font-size: 18px; + font-family: var(--fontfamily-roboto); + color: var(--white); +} + +.linkButton:hover, +.linkButton:focus { + text-decoration: underline; +} + +.accountSettings { + display: flex; + flex-direction: column; +} + +.accountForms { + display: flex; + justify-content: space-around; + flex-wrap: wrap; +} + +.accountForms form { + display: flex; + flex-direction: column; + flex: 1; + margin: 0; + padding-right: 30px; +} + +.accountForms form input { + display: block; + margin: 10px; + padding-left: 20px; + min-width: 200px; + max-width: 400px; + background-color: var(--greyblue); outline: none; + font-size: 1rem; + font-family: var(--fontfamily-roboto); +} + +.accountForms form button { + font-size: 18px; + margin: 20px 0; + max-width: 200px; + font-family: var(--fontfamily); + background-color: var(--light-blue); + outline: none; +} + +.accountForms form button:active { + outline: 1px solid var(--grey); + outline-offset: -4px; } diff --git a/src/utils/ProtectedRoute.js b/src/utils/ProtectedRoute.js new file mode 100644 index 0000000..593f99e --- /dev/null +++ b/src/utils/ProtectedRoute.js @@ -0,0 +1,32 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Route, Redirect } from 'react-router-dom'; + +/** + * Higher Order Component that wraps the Router component to obfuscate authentication in routes. + * @param {Object} props Props Variables + * @param {Symbol} props.component Rendered component + * @param {String} props.redirectLink Link for default route + * @param {bool} props.isAuthenticated A boolean flag + */ +const ProtectedDashboardRoute = ({ component: Component, isAuthenticated, redirectLink, ...rest }) => ( + + isAuthenticated ? ( + + ) : ( + + ) + } + /> +); + +ProtectedDashboardRoute.propTypes = { + component: PropTypes.func.isRequired, + isAuthenticated: PropTypes.bool.isRequired, + redirectLink: PropTypes.string.isRequired, + location: PropTypes.object, // FIXME: Check out how to get this to not yell with .isRequired +}; + +export default ProtectedDashboardRoute; diff --git a/src/utils/index.js b/src/utils/index.js index 8ab2346..63becc3 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1,2 +1,4 @@ export { FontAwesomeIcon } from './fontAwesome'; export { default as userAPI } from './userAPI'; +export { default as tokenServices } from './tokenServices'; +export { default as ProtectedRoute } from './ProtectedRoute'; diff --git a/src/utils/tokenServices.js b/src/utils/tokenServices.js new file mode 100644 index 0000000..bb708d1 --- /dev/null +++ b/src/utils/tokenServices.js @@ -0,0 +1,23 @@ +const jwtdecode = require('jwt-decode'); +/** + * Gets the token from local storage and decodes the information needed + */ +function getToken() { + const token = localStorage.getItem('app-token'); + if(!token){ + return null; + } + const results = jwtdecode(token); + return {id: results.id, persona: results.persona, fullName: results.fullName} +} +/** + * Removes token from local storage ie: logout + */ +function removeToken() { + localStorage.removeItem('app-token'); +} + +module.exports = { + getToken, + removeToken, +} \ No newline at end of file