sveta пре 5 година
родитељ
комит
38435bf311
35 измењених фајлова са 473 додато и 178 уклоњено
  1. 5 0
      package-lock.json
  2. 1 0
      package.json
  3. 17 3
      src/actions/auth/logOut.js
  4. 0 0
      src/actions/auth/saveToken.js
  5. 6 1
      src/actions/auth/signIn.js
  6. 3 1
      src/actions/auth/signUp.js
  7. 10 0
      src/actions/auth/tokenCheckout.js
  8. 35 0
      src/components/admin/CardsCreator/index.js
  9. 33 30
      src/components/common/protectedRoute.js
  10. 90 40
      src/components/public/Header.js
  11. 0 42
      src/components/public/SignIn/index.js
  12. 32 0
      src/components/public/landing.js
  13. 0 0
      src/components/public/signIn/Form/index.js
  14. 0 0
      src/components/public/signIn/Form/validate.js
  15. 42 0
      src/components/public/signIn/index.js
  16. 0 0
      src/components/public/signUp/Form/index.js
  17. 0 0
      src/components/public/signUp/Form/validate.js
  18. 1 1
      src/components/public/SignUp/index.js
  19. 1 1
      src/components/user/ProfilePage/ChangeLoginForm/index.js
  20. 9 3
      src/components/user/ProfilePage/index.js
  21. 12 0
      src/configs/requestsConfigs/index.js
  22. 19 4
      src/configs/routerConfig.js
  23. 10 1
      src/constants/auth.js
  24. 31 0
      src/reducers/auth/logOut.js
  25. 4 3
      src/reducers/auth/signIn.js
  26. 22 0
      src/reducers/auth/tokenCheckout.js
  27. 4 0
      src/reducers/index.js
  28. 9 0
      src/reducers/initialState.js
  29. 21 36
      src/router.js
  30. 5 3
      src/sagas/auth/index.js
  31. 25 0
      src/sagas/auth/logOut.js
  32. 7 9
      src/sagas/auth/signIn.js
  33. 2 0
      src/state/index.js
  34. 2 0
      src/styles/_index.scss
  35. 15 0
      src/styles/components/_landing.scss

+ 5 - 0
package-lock.json

@@ -8474,6 +8474,11 @@
         "array-includes": "^3.0.3"
       }
     },
+    "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",

+ 1 - 0
package.json

@@ -6,6 +6,7 @@
     "axios": "^0.18.0",
     "bootstrap": "^4.2.1",
     "jquery": "^3.3.1",
+    "jwt-decode": "^2.2.0",
     "node-sass": "^4.11.0",
     "popper.js": "^1.14.6",
     "react": "^16.6.3",

+ 17 - 3
src/actions/auth/logOut.js

@@ -1,3 +1,17 @@
-export const logOut = () => ({
-    
-})
+import * as actionTypes from './../../constants/auth';
+
+const logOut = () => ({
+    type: actionTypes.LOG_OUT_REQUEST
+})
+
+export const logOutSuccess = payload => ({
+    type: actionTypes.LOG_OUT_REQUEST_SUCCESS,
+    payload
+})
+
+export const logOutFailure = ({ message: error }) => ({
+    type: actionTypes.LOG_OUT_REQUEST_FAILURE,
+    error
+})
+
+export default logOut;

+ 0 - 0
src/actions/auth/saveToken.js


+ 6 - 1
src/actions/auth/signIn.js

@@ -1,6 +1,6 @@
 import * as actionTypes from './../../constants/auth';
 
-export const signIn = payload => ({
+const signIn = payload => ({
     type: actionTypes.SIGN_IN_REQUEST,
     payload
 });
@@ -12,3 +12,8 @@ export const signInFailure = error => ({
     type: actionTypes.SIGN_IN_REQUEST_FAILURE,
     error
 });
+export const signInStateZeroing = () => ({
+    type: actionTypes.SIGN_IN_STATE_ZEROING
+})
+
+export default signIn;

+ 3 - 1
src/actions/auth/signUp.js

@@ -1,6 +1,6 @@
 import * as actionTypes from './../../constants/auth';
 
-export const signUp = payload => ({
+const signUp = payload => ({
     type: actionTypes.SIGN_UP_REQUEST,
     payload
 });
@@ -12,3 +12,5 @@ export const signUpFailure = error => ({
     type: actionTypes.SIGN_UP_REQUEST_FAILURE,
     error
 });
+
+export default signUp;

+ 10 - 0
src/actions/auth/tokenCheckout.js

@@ -0,0 +1,10 @@
+import * as types from './../../constants/auth';
+
+export const activeToken = payload => ({
+    type: types.ACTIVE_TOKEN,
+    payload
+})
+
+export const inactiveToken = () => ({
+    type: types.INACTIVE_TOKEN
+})

+ 35 - 0
src/components/admin/CardsCreator/index.js

@@ -0,0 +1,35 @@
+import React from 'react';
+import { Field, reduxForm } from 'redux-form';
+import { connect } from 'react-redux';
+
+class CardCreator extends React.Component {
+    state = {
+        type: null
+    }
+
+    render() {
+        const { formProps } = this.props;
+        console.log('------------ PROPS --------------');
+        console.log(formProps);
+
+        return (
+            <div>
+                <h1>Create Test</h1>
+                <Field name='some' component='select'>
+                    <option value="some value 1">Optional</option>
+                    <option value="some value 2">Multioptional</option>
+                    <option value="some value 3">Open Answer</option>
+                    <option value="some value 4">Conformity</option>
+                </Field>
+            </div>
+        )
+    }
+}
+
+const mapStateToProps = state => ({
+    formProps: state.form
+})
+
+export default connect(mapStateToProps, null)(reduxForm({
+    form: "CardCreator"
+})(CardCreator))

+ 33 - 30
src/components/common/protectedRoute.js

@@ -1,47 +1,50 @@
 import React from 'react';
 import { Route, Redirect } from 'react-router-dom';
+import { connect } from 'react-redux';
+import { bindActionCreators } from 'redux';
+import { inactiveToken, activeToken } from './../../actions/auth/tokenCheckout'
+import decode from 'jwt-decode';
+
 import * as routes from './../../constants/routes';
+import storageKey from './../../utils/storageKey';
 
-export default ({ component: Component, user, tokenAuth, access, ...rest }) => (
+const protectedRoute = ({ component: Component, activeToken, inactiveToken, access, ...rest }) => (
     <Route
         {...rest}
         render={props => {
-            {/* let checkedData;
-
-            if (data) {
-                checkedData = data;
-            }
-            else {
-                const storagedUser = JSON.parse(localStorage.getItem(token));
-                if (storagedUser) {
-                    checkedData = storagedUser;
-                    tokenAuth(storagedUser);
-                }
-                else {
-                    checkedData = null;
+            try {
+                const user = decode(localStorage.getItem(storageKey));
+                activeToken(user.role);
+                {/* if (user.exp < Date.now()*1000) {
+                    throw new Error('Date expired');
+                } */}
+
+                if (access === 'admin-only' && !user.role) {
+                    throw new Error('Permission Denied')
                 }
-            } */}
-            const checkedData = user && user.data ? user.data : null;
 
-            console.log('\n\nHere goes the route:', '\nUser', user, '\n\n\n')
-
-            if (access === 'public') {
                 return <Component {...props} />
             }
+            catch ({ message }) {
+                if (message === 'Permission Denied') {
+                    return <div>Permission denied</div>
+                }
 
-            if ((access === 'user-only' || access === 'admin-only') && !checkedData) {
-                return <Redirect to={routes.SIGN_IN} />
-            }
+                inactiveToken();
 
-            if (access === 'user-only' && checkedData) {
-                return <Component {...props} />
-            }
+                if (access === 'public') {
+                    return <Component {...props} />
+                }
 
-            if (access === 'admin-only' && checkedData.role) {
-                return <Component {...props} />
+                return <Redirect to={routes.SIGN_IN} />
             }
-
-            return null // permission denied
         }}
     />
-)
+)
+
+const mapDispatchToProps = dispatch => bindActionCreators({
+    activeToken,
+    inactiveToken
+}, dispatch);
+
+export default connect(null, mapDispatchToProps)(protectedRoute);

+ 90 - 40
src/components/public/Header.js

@@ -1,59 +1,109 @@
-import React from 'react';
-import { Link } from "react-router-dom";
-import * as routes from '../../constants/routes'
+import React, { PureComponent } from 'react';
+import { Link, withRouter, Redirect } from "react-router-dom";
+import { connect } from 'react-redux';
+import { bindActionCreators } from 'redux';
+import axios from 'axios';
+
+import storageKey from './../../utils/storageKey';
+import { signInStateZeroing } from './../../actions/auth/signIn';
+import { LOG_OUT_URL } from './../../constants/auth';
+
+import * as routes from '../../constants/routes';
+
+//pure component + make storage check an action -> connect router
+class Header extends PureComponent {
+    handleLogOut = () => {
+        console.log(this.props)
+        const { history, zeroState } = this.props;
+
+        axios.get(LOG_OUT_URL, {})
+            .then(() => {
+                console.log('request was succesfull')
+                localStorage.removeItem(storageKey);
+                zeroState();
+                history.push('/sign-in');
+            })
+            .catch(e => alert('Sorry, something bad happened, try log out later \n' + e.message));
+    }
 
-export default class Header extends React.Component {
     render() {
+        const { role } = this.props;
+
         return (
-            <nav class="navbar navbar-expand-lg navbar-dark bg-dark p-3">
-                <a class="navbar-brand" href="#">Test.io</a>
-                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
-                    <span class="navbar-toggler-icon"></span>
+            <nav className="navbar navbar-expand-lg navbar-dark bg-dark p-3">
+                <Link to={routes.LANDING} className="navbar-brand" href="#">Test.io</Link>
+                <button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
+                    <span className="navbar-toggler-icon" />
                 </button>
 
-                <div class="collapse navbar-collapse" id="navbarSupportedContent">
-                    <ul class="navbar-nav mr-auto">
-                        <li class="nav-item active">
-                            <Link onClick={this.handleClick} class="nav-link font-ci font-ci-bold" to={routes.HOME}>Home <span class="sr-only">(current)</span></Link>
+                <div className="collapse navbar-collapse" id="navbarSupportedContent">
+                    <ul className="navbar-nav mr-auto">
+                        <li className="nav-item active">
+                            <Link onClick={this.handleClick} className="nav-link" to={routes.HOME}>Home <span className="sr-only">(current)</span></Link>
                         </li>
-                        <li class="nav-item">
-                            <Link onClick={this.handleClick} class="nav-link font-ci font-ci-bold" to={routes.PROFILE}>Profile</Link>
+                        <li className="nav-item">
+                            <Link onClick={this.handleClick} className="nav-link" to={routes.PROFILE}>Profile</Link>
                         </li>
-                        <li class="nav-item">
-                            <Link onClick={this.handleClick} class="nav-link font-ci font-ci-bold " to={routes.TESTS}>Tests</Link>
+                        <li className="nav-item">
+                            <Link onClick={this.handleClick} className="nav-link" to={routes.TESTS}>Tests</Link>
                         </li>
-                        <li class="nav-item">
-                            <Link onClick={this.handleClick} class="nav-link font-ci font-ci-bold " to={routes.CATEGORIES}>Catigories</Link>
+                        <li className="nav-item">
+                            <Link onClick={this.handleClick} className="nav-link" to={routes.CATEGORIES}>Catigories</Link>
                         </li>
                         {/* TODO: admin-only ! */}
-                        <li class="nav-item dropdown">
-                            <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
-                                Menu Admina
-                                    </a>
-                            <div class="dropdown-menu" aria-labelledby="navbarDropdown">
-                                <Link onClick={this.handleClick} class="nav-link font-ci font-ci-bold " to={routes.DELETE_USER}>Delete</Link>
-                                <a class="dropdown-item" href="#">Action</a>
-                                <a class="dropdown-item" href="#">Another action</a>
-                                <div class="dropdown-divider"></div>
-                                <a class="dropdown-item" href="#">Something else here</a>
-                            </div>
-                        </li>
 
+                        {
+                            role
+                                ?
+                                <li className="nav-item dropdown">
+                                    {/* eslint-disable-next-line */}
+                                    <a className="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                                        Dropdown
+                                </a>
+                                    <div className="dropdown-menu" aria-labelledby="navbarDropdown">
+                                        <Link to={routes.CREATE_TEST} className="nav-link text-secondary">Create Test</Link>
+                                        <Link to={routes.CREATE_CATEGORY} className="nav-link text-secondary">Create Category</Link>
+
+                                        <div className="dropdown-divider"></div>
+                                        {/* eslint-disable-next-line */}
+                                        <a className="nav-link text-secondary" href="#">Something else here</a>
+                                    </div>
+                                </li>
+                                :
+                                null
+                        }
                     </ul>
-                    <form class="form-inline my-2 my-lg-0">
-                        <input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search" />
-                        <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
+                    <form className="form-inline my-lg-0">
+                        <input className="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search" />
+                        <button className="btn btn-outline-success my-2 my-sm-0 mr-2" type="submit">Search</button>
                     </form>
-                    <div className="pl-2">
-                        <Link to={routes.SIGN_IN}>
-                            <button className="btn btn-outline-primary">Sign In</button>
-                        </Link>
-                        <Link to={routes.SIGN_UP}>
-                            <button className="btn btn-outline-primary ml-2">Sign Up</button>
-                        </Link>
+                    <div className="controls">
+                        {
+                            role !== null
+                                ?
+                                <button onClick={this.handleLogOut} className="btn btn-outline-primary">Log Out</button>
+                                :
+                                <React.Fragment>
+                                    <Link to={routes.SIGN_IN}>
+                                        <button className="btn btn-outline-primary">Sign In</button>
+                                    </Link>
+                                    <Link to={routes.SIGN_UP}>
+                                        <button className="btn btn-outline-primary mx-2">Sign Up</button>
+                                    </Link>
+                                </React.Fragment>
+                        }
                     </div>
                 </div>
             </nav>
         )
     }
 }
+
+const mapStateToProps = state => ({
+    role: state.tokenCheckout.role,
+});
+const mapDispatchToProps = dispatch => bindActionCreators({
+    zeroState: signInStateZeroing
+}, dispatch)
+
+export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Header));

+ 0 - 42
src/components/public/SignIn/index.js

@@ -1,42 +0,0 @@
-import React from 'react';
-import Form from './Form';
-import { bindActionCreators } from 'redux';
-import { connect } from 'react-redux';
-import { Redirect } from 'react-router-dom'
-
-import Spinner from './../../common/spinner';
-import { signIn } from './../../../actions/auth/signIn';
-
-const SignIn = ({ signIn, user }) => {
-    const someShit = user.isFetching
-        ?
-        <Spinner />
-        : user.data
-            ?
-            <Redirect to='/' />
-            : user.error
-                ?
-                (
-                    <React.Fragment>
-                        <Form signIn={signIn} requestError={user.error} />
-                    </React.Fragment>
-
-                )
-                :
-                <Form signIn={signIn} />
-
-    return someShit
-
-}
-
-
-
-const mapStateToProps = state => ({
-    user: state.signIn
-});
-const mapDispatchToProps = dispatch => bindActionCreators({
-    signIn
-}, dispatch);
-
-
-export default connect(mapStateToProps, mapDispatchToProps)(SignIn);

+ 32 - 0
src/components/public/landing.js

@@ -0,0 +1,32 @@
+import React from 'react';
+
+const landing = () => (
+    <div id="carouselExampleIndicators" className="carousel slide" data-ride="carousel">
+        <ol className="carousel-indicators">
+            <li data-target="#carouselExampleIndicators" data-slide-to="0" className="active"></li>
+            <li data-target="#carouselExampleIndicators" data-slide-to="1"></li>
+            <li data-target="#carouselExampleIndicators" data-slide-to="2"></li>
+        </ol>
+        <div className="carousel-inner">
+            <div className="carousel-item active">
+                <img src="https://images.theconversation.com/files/17962/original/jt558trs-1353642967.jpg?ixlib=rb-1.1.0&rect=23%2C5%2C3831%2C2573&q=45&auto=format&w=926&fit=clip" className="d-block w-100 slider-image" alt="..." />
+            </div>
+            <div className="carousel-item">
+                <img src="http://dyslexiahelp.umich.edu/sites/default/files/upload/testing%20instuments%20dreamstimesmall_5908127.jpg" className="d-block w-100 slider-image" alt="..." />
+            </div>
+            <div className="carousel-item">
+                <img src="https://amp.businessinsider.com/images/55a7e74f2acae716008b7469-750-562.jpg" className="d-block w-100 slider-image" alt="..." />
+            </div>
+        </div>
+        <a className="carousel-control-prev" href="#carouselExampleIndicators" role="button" data-slide="prev">
+            <span className="carousel-control-prev-icon" aria-hidden="true"></span>
+            <span className="sr-only">Previous</span>
+        </a>
+        <a className="carousel-control-next" href="#carouselExampleIndicators" role="button" data-slide="next">
+            <span className="carousel-control-next-icon" aria-hidden="true"></span>
+            <span className="sr-only">Next</span>
+        </a>
+    </div>
+)
+
+export default landing;

src/components/public/SignIn/Form/index.js → src/components/public/signIn/Form/index.js


src/components/public/SignIn/Form/validate.js → src/components/public/signIn/Form/validate.js


+ 42 - 0
src/components/public/signIn/index.js

@@ -0,0 +1,42 @@
+import React from 'react';
+import Form from './Form';
+import { bindActionCreators } from 'redux';
+import { connect } from 'react-redux';
+import { Redirect } from 'react-router-dom'
+
+import Spinner from './../../common/spinner';
+import signIn from './../../../actions/auth/signIn';
+
+
+
+const SignIn = ({ signIn, user }) => {
+    return (
+        user.isFetching
+            ?
+            <Spinner />
+            : user.data
+                ?
+                <Redirect to='/' />
+                : user.error
+                    ?
+                    (
+                        <React.Fragment>
+                            <Form signIn={signIn} requestError={user.error} />
+                        </React.Fragment>
+                    )
+                    :
+                    <Form signIn={signIn} />
+    )
+}
+
+
+
+const mapStateToProps = state => ({
+    user: state.signIn,
+});
+const mapDispatchToProps = dispatch => bindActionCreators({
+    signIn
+}, dispatch);
+
+
+export default connect(mapStateToProps, mapDispatchToProps)(SignIn);

src/components/public/SignUp/Form/index.js → src/components/public/signUp/Form/index.js


src/components/public/SignUp/Form/validate.js → src/components/public/signUp/Form/validate.js


+ 1 - 1
src/components/public/SignUp/index.js

@@ -5,7 +5,7 @@ import { connect } from 'react-redux';
 import { Redirect } from 'react-router-dom';
 
 import Spinner from './../../common/spinner';
-import { signUp } from './../../../actions/auth/signUp';
+import signUp from './../../../actions/auth/signUp';
 
 const SignUp = ({ status, signUp }) => (
     status.isFetching

+ 1 - 1
src/components/user/ProfilePage/ChangeLoginForm/index.js

@@ -1,5 +1,4 @@
 import React from 'react';
-import { connect } from 'react-redux';
 import { Field, reduxForm } from 'redux-form';
 
 import formInput from '../../../common/formInput';
@@ -22,6 +21,7 @@ class ChangeLoginForm extends React.Component {
         const { children, handleSubmit, actions:{changeLoginRequest}, handlers: {  handleClick }, data, token } = this.props;
         const { submit}=this
         console.log( data)
+
         return (
             <form onSubmit={handleSubmit(submit)} >
                 <Field name="name" type="email" placeholder="Enter new login" component={formInput} />

+ 9 - 3
src/components/user/ProfilePage/index.js

@@ -28,11 +28,13 @@ class ProfilePage extends React.Component {
     }
 
     render() {
+
         const { signIn:{data, token}, changeLoginRequest, chageLoginReducer, changeEmailRequest, changeEmailReducer, changePasswordRequest} = this.props;
       console.log(changeEmailReducer)
         const {clicked, klick, click} =this.state;
         const {handleClick, handleClicks, handleClicked} = this;
 
+
         return (
             <div className="page page--bottom-only profile-page">
                 <div className="border bg-autumn-foliage m-5 rounded d-flex">
@@ -46,13 +48,14 @@ class ProfilePage extends React.Component {
                 </div> */}
                 
                     <div>
-                        <img className="m-4 " src=" https://cdn.iconscout.com/icon/free/png-256/avatar-373-456325.png" ></img>
+                        <img className="m-4" src="https://cdn.iconscout.com/icon/free/png-256/avatar-373-456325.png" alt="banner" ></img>
                     </div>
                     </div>
                     <div className="border-left bg-autumn-foliage m-5 rounded w-75 ">
                             {!clicked
                             ?
                         <div  className ="d-flex mb-3" >
+
                             <h3 className = " p-2 flex-fill bd-highlight">Name</h3>
                             <h5 className="p-2 font-po-bold flex-fill bd-highlight align-self-end">{
                                 chageLoginReducer.data == null
@@ -63,10 +66,11 @@ class ProfilePage extends React.Component {
 
                                 }</h5>
                             <button type="button" onClick ={handleClick}  className=" flex-fill bg-shadow btn btn-outline-primary col-3">Change</button>
+
                         </div>
                         :
                         <div>
-                        <h3 className = " p-2 flex-fill bd-highlight">Name</h3>
+                        <h3 className="p-2 flex-fill bd-highlight">Name</h3>
                         <h5 className="p-2 font-po-bold flex-fill bd-highlight align-self-end">{data.name}</h5>
                         <ChangeLoginForm data ={data} token={token} actions ={{changeLoginRequest}} handlers={{ handleClick}}> </ChangeLoginForm>
                         </div>
@@ -88,7 +92,7 @@ class ProfilePage extends React.Component {
                         </div>
                         :
                         <div  className ="d-flex mb-3" >
-                            <h3 className = "p-2 flex-fill bd-highlight">Email</h3>
+                            <h3 className="p-2 flex-fill bd-highlight">Email</h3>
                             <h5 className="p-2 flex-fill font-po-bold bd-highlight align-self-end">{data.email}</h5>
                             <ChangeEmailForm  data ={data} token={token} actions ={{changeEmailRequest}} handlers={{ handleClicks}}></ChangeEmailForm>
                         </div>
@@ -97,6 +101,7 @@ class ProfilePage extends React.Component {
                                 !click
                                 ?
                         <div  className ="d-flex mb-3" >
+
                             <h3 className = "p-2 flex-fill bd-highlight">Password</h3>
                             <button type="button" onClick ={handleClicked} className=" flex-fill bg-shadow btn btn-outline-primary col-3">Change</button>
                         </div>
@@ -104,6 +109,7 @@ class ProfilePage extends React.Component {
                         <div  className ="d-flex mb-3" >
                             <h3 className = "p-2 flex-fill bd-highlight">Password</h3>
                            <ChangePasswordForm data ={data} token={token} actions ={{changePasswordRequest}} handlers={{ handleClicked}}></ChangePasswordForm>
+
                         </div>
                             }
                  

+ 12 - 0
src/configs/requestsConfigs/index.js

@@ -0,0 +1,12 @@
+export const defaultConfig = {
+    headers: {
+        "Content-Type": "application/json"
+    }
+}
+
+export const makeConfigWithJWT = jwt => ({
+    headers: {
+        "Content-Type": "application/json",
+        "Authorization": `Bearer ${jwt}`
+    }
+})

+ 19 - 4
src/configs/routerConfig.js

@@ -1,17 +1,22 @@
 import React, { lazy } from 'react';
+
+import Landing from './../components/public/landing';
 import * as routes from './../constants/routes';
 
+const SignIn = lazy(() => import('../components/public/SignIn'));
+const SignUp = lazy(() => import('../components/public/SignUp'));
+
 const ProfilePage = lazy(() => import('../components/user/ProfilePage'))
 const DeleteUser = lazy(()=>import('../components/admin/deleteUser/index'))
-// import SignIn from '../components/public/SignIn';
-const SignIn = lazy(() => import('./../components/public/SignIn'));
-const SignUp = lazy(() => import('./../components/public/SignUp'));
+
+
+const CardsCreator = lazy(() => import('./../components/admin/CardsCreator'))
 
 export default [
     {
         path: routes.LANDING,
         access: 'public',
-        component: () => <div>landing</div>
+        component: Landing
     },
     {
         path: routes.SIGN_IN,
@@ -43,11 +48,21 @@ export default [
         access: 'user-only',
         component: () => <div>test</div>
     },
+    {
+        path: '/admin',
+        access: 'admin-only',
+        component: () => <div>Some admin-only component</div>
+    },
     {
         path: routes.DELETE_USER,
         access: 'admin-only',
         component: DeleteUser
     },
+    {
+        path: routes.CREATE_TEST,
+        access: 'public',
+        component: CardsCreator
+    },
     {
         access: 'public',
         component: () => <div>404</div>

+ 10 - 1
src/constants/auth.js

@@ -2,8 +2,17 @@ export const SIGN_IN_URL = 'https://test-app-a-level.herokuapp.com/auth/login';
 export const SIGN_IN_REQUEST = 'SIGN_IN_REQUEST';
 export const SIGN_IN_REQUEST_SUCCESS = 'SIGN_IN_REQUEST_SUCCESS';
 export const SIGN_IN_REQUEST_FAILURE = 'SIGN_IN_REQUEST_FAILURE';
+export const SIGN_IN_STATE_ZEROING = 'SIGN_IN_STATE_ZEROING';
 
 export const SIGN_UP_URL = 'https://test-app-a-level.herokuapp.com/auth/register';
 export const SIGN_UP_REQUEST = 'SIGN_UP_REQUEST';
 export const SIGN_UP_REQUEST_SUCCESS = 'SIGN_UP_REQUEST_SUCCESS';
-export const SIGN_UP_REQUEST_FAILURE = 'SIGN_UP_REQUEST_FAILURE';
+export const SIGN_UP_REQUEST_FAILURE = 'SIGN_UP_REQUEST_FAILURE';
+
+export const LOG_OUT_URL = 'https://test-app-a-level.herokuapp.com/auth/logout';
+export const LOG_OUT_REQUEST = 'LOG_OUT_REQUEST';
+export const LOG_OUT_REQUEST_SUCCESS = 'LOG_OUT_REQUEST_SUCCESS';
+export const LOG_OUT_REQUEST_FAILURE = 'LOG_OUT_REQUEST_FAILURE';
+
+export const ACTIVE_TOKEN = 'ACTIVE_TOKEN';
+export const INACTIVE_TOKEN = 'INACTIVE_TOKEN'; 

+ 31 - 0
src/reducers/auth/logOut.js

@@ -0,0 +1,31 @@
+import * as actionTypes from './../../constants/auth';
+import initialState from './../initialState';
+
+export default function signInReducer(state = initialState.logOut, {type, error}) {
+
+    switch (type) {
+        case actionTypes.LOG_OUT_REQUEST: {
+            return {
+                ...state,
+                isFetching: true
+            }
+        }
+        case actionTypes.LOG_OUT_REQUEST_SUCCESS: {
+            return {
+                ...state,
+                isFetching: false,
+                isSuccessful: true
+            }
+        }
+        case actionTypes.LOG_OUT_REQUEST_FAILURE: {
+            return {
+                ...state,
+                isFetching: false,
+                error
+            }
+        }
+        default: {
+            return state;
+        }
+    }
+}

+ 4 - 3
src/reducers/auth/signIn.js

@@ -11,23 +11,24 @@ export default function signInReducer(state = initialState.signIn, {payload, typ
             }
         }
         case actionTypes.SIGN_IN_REQUEST_SUCCESS: {
-            const { user, token } = payload;
+            const { user } = payload;
 
             return {
                 ...state,
                 isFetching: false,
                 data: user,
-                token
             }
         }
         case actionTypes.SIGN_IN_REQUEST_FAILURE: {
-
             return {
                 ...state,
                 isFetching: false,
                 error
             }
         }
+        case actionTypes.SIGN_IN_STATE_ZEROING: {
+            return initialState.signIn
+        }
         default: {
             return state;
         }

+ 22 - 0
src/reducers/auth/tokenCheckout.js

@@ -0,0 +1,22 @@
+import * as actionTypes from './../../constants/auth';
+import initialState from './../initialState';
+
+export default function signInReducer(state = initialState.tokenCheckout, {type, payload: role, error}) {
+    switch(type) {
+        case actionTypes.ACTIVE_TOKEN: {
+            return {
+                ...state,
+                role
+            }
+        }
+        case actionTypes.INACTIVE_TOKEN: {
+            return {
+                ...state,
+                role: null
+            }
+        }
+        default: {  
+            return state;
+        }
+    }
+}

+ 4 - 0
src/reducers/index.js

@@ -3,12 +3,16 @@ import { reducer as form } from 'redux-form';
 
 import signIn from './auth/signIn';
 import signUp from './auth/signUp';
+import logOut from './auth/logOut';
+import tokenCheckout from './auth/tokenCheckout';
 
 import chageLoginReducer from './user/chageLogin';
 import changeEmailReducer from './user/changeEmail';
 
 export default combineReducers({
 
+    tokenCheckout,
+    logOut,
     signIn,
     signUp,
     chageLoginReducer,

+ 9 - 0
src/reducers/initialState.js

@@ -20,4 +20,13 @@ export default {
         data: null,
         error: null
     },
+
+    logOut: {
+        isFetching: false,
+        isSuccessful: null,
+        error: null
+    },
+    tokenCheckout: {
+        role: null
+    }
 }

+ 21 - 36
src/router.js

@@ -1,6 +1,5 @@
-import React, { Suspense, lazy } from "react";
-import { Switch, Route, withRouter } from "react-router-dom";
-import { connect } from 'react-redux';
+import React, { Suspense } from "react";
+import { Switch } from "react-router-dom";
 
 import ProtectedRoute from './components/common/protectedRoute';
 import config from './configs/routerConfig';
@@ -8,37 +7,23 @@ import config from './configs/routerConfig';
 import Header from './components/public/Header';
 import Spinner from './components/common/spinner';
 
-import { bindActionCreators } from "redux";
+const router = props => (
+    <div className="app">
+        <Header />
+        <Suspense fallback={<Spinner />}>
+            <Switch>
+                {config.map(route =>
+                    <ProtectedRoute
+                        path={route.path}
+                        component={route.component}
+                        access={route.access}
+                        key={route.path}
+                        exact
+                    />
+                )}
+            </Switch>
+        </Suspense>
+    </div>
+)
 
-class Router extends React.Component {
-    render() {
-        const { user, tokenAuth } = this.props;
-        // TODO: add footer
-        return (
-            <div className="app">
-                <Header />
-                <Suspense fallback={<Spinner />}>
-                    <Switch>
-                        {config.map(route =>
-                            <ProtectedRoute
-                                path={route.path}
-                                component={route.component}
-                                access={route.access}
-                                user={user}
-                                key={route}
-                                exact
-                            />
-                        )}
-                    </Switch>
-                </Suspense>
-            </div>
-        )
-    }
-}
-
-const mapStateToProps = state => ({
-    user: state.signIn
-})
-// const mapDispatchToProps = dispatch => bindActionCreators({ tokenAuth }, dispatch);
-
-export default withRouter(connect(mapStateToProps, null)(Router));
+export default router;

+ 5 - 3
src/sagas/auth/index.js

@@ -1,10 +1,12 @@
+import { takeEvery } from 'redux-saga/effects';
+
+import * as actionTypes from './../../constants/auth'
 import signIn from './signIn';
 import signUp from './signUp';
-import * as actionTypes from './../../constants/auth'
-
-import { takeEvery } from 'redux-saga/effects';
+import logOut from './logOut';
 
 export default function* () {
     yield takeEvery(actionTypes.SIGN_IN_REQUEST, signIn)
     yield takeEvery(actionTypes.SIGN_UP_REQUEST, signUp)
+    yield takeEvery(actionTypes.LOG_OUT_REQUEST, logOut)
 }

+ 25 - 0
src/sagas/auth/logOut.js

@@ -0,0 +1,25 @@
+import { put, call } from 'redux-saga/effects';
+import axios from 'axios';
+
+import { LOG_OUT_URL } from './../../constants/auth';
+import storageKey from './../../utils/storageKey';
+import { logOutFailure, logOutSuccess } from './../../actions/auth/logOut';
+
+const removeItem = () => {
+    localStorage.removeItem(storageKey);
+}
+
+export default function* ({ payload }) {
+    try {
+        const user = yield call(() =>
+            axios.get(LOG_OUT_URL, payload)
+                .then(({ data }) => data)
+        )
+
+        yield call(removeItem);
+        yield put(logOutSuccess(user));
+    }
+    catch ({ message }) {
+        yield put(logOutFailure(message));
+    }
+}

+ 7 - 9
src/sagas/auth/signIn.js

@@ -3,23 +3,21 @@ import axios from 'axios';
 
 import { SIGN_IN_URL } from './../../constants/auth';
 import storageKey from './../../utils/storageKey';
-import { signInSuccess, signInFailure } from './../../actions/auth/signIn'
+import { signInSuccess, signInFailure } from './../../actions/auth/signIn';
+import { defaultConfig as config } from '../../configs/requestsConfigs';
+
+const setItem = (value) => {
+    localStorage.setItem(storageKey, value);
+}
 
 export default function* ({ payload }) {
     try {
-        const config = {
-            headers: {
-                "Content-Type": "application/json"
-            }
-        }
-
         const user = yield call(() =>
             axios.post(SIGN_IN_URL, payload, config)
                 .then(({ data }) => data)
         )
 
-        //TODO: TOKEN
-        // yield call(localStorage.setItem, storageKey, user.token);
+        yield call(setItem, user.token);
         yield put(signInSuccess(user));
     }
     catch ({ message }) {

+ 2 - 0
src/state/index.js

@@ -17,6 +17,8 @@ const store = createStore(
     applyMiddleware(sagaMiddleware, logger)
 );
 
+window.state = () => store.getState()
+
 sagaMiddleware.run(sagas);
 
 export default store;

+ 2 - 0
src/styles/_index.scss

@@ -3,3 +3,5 @@
 @import 'extended/vh';
 
 @import 'base/typography';
+
+@import 'components/landing';

+ 15 - 0
src/styles/components/_landing.scss

@@ -0,0 +1,15 @@
+img.slider-image {
+    height: calc(100vh - 72px);
+    @media screen and (max-width: 1200px) {
+        // border: 10px solid red;
+        height: 500px;
+    }    
+    @media screen and (max-width: 600px) {
+        // border: 10px solid green;
+        height: 400px;
+    }
+    @media screen and (max-width: 450px) {
+        // border: 10px solid yellow;        
+        height: 300px;
+    }
+}