<repo_name>AndreyDrapatiy/react-redux-shop<file_sep>/src/store/actions/auth/auth.js
import axios from "../../../axios";
import * as actionTypes from '../actionTypes';
import {cart_check_state} from '../index'


export const auth_start = () => {
    return {
        type: actionTypes.AUTH_START,
    }
};

export const auth_success = (token, userId) => {
    return {
        type: actionTypes.AUTH_SUCCESS,
        idToken: token,
        userId: userId
    }
};

export const auth_fail = (error) => {
    return {
        type: actionTypes.AUTH_FAIL,
        error: error
    }
};

export const logout = () => {

    localStorage.removeItem('token');
    localStorage.removeItem('userId');

    return {
        type: actionTypes.AUTH_LOGOUT
    }
}

export const check_auth_timeout = (expirationTime) => {
    return dispatch => {
        setTimeout(() => {
            dispatch(logout())
        }, expirationTime * 1000)
    }
}

export const auth = (email, password, method) => {
    return dispatch => {
        dispatch(auth_start());

        const authData = {
            email: email,
            password: <PASSWORD>,
            returnSecureToken: true
        }

        let signInUrl = 'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=<KEY>'
        let signUpUrl = 'https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=<KEY>'

        axios.post(method ? signInUrl : signUpUrl, authData)
            .then(response => {
                localStorage.setItem('token', response.data.idToken);
                localStorage.setItem('userId', response.data.localId);
                dispatch(auth_success(response.data.idToken, response.data.localId))
                dispatch(check_auth_timeout(response.data.expiresIn))
                dispatch(cart_check_state())
            })

            .catch(err => {
                dispatch(auth_fail(err.response.data.error.message))
            })
    }
};

export const auth_check_state = () => {
    return dispatch => {
        const token = localStorage.getItem('token');
        if (!token) {
            dispatch(logout());
        }  else {
                const userId = localStorage.getItem('userId');
                dispatch(auth_success(token, userId));
        }
    };
};<file_sep>/src/store/actions/actionTypes.js

export const LOAD_ITEMS = 'LOAD_ITEMS';
export const LOAD_SINGLE = 'LOAD_SINGLE';

export const UPDATE_CART = 'UPDATE_CART';
export const UPDATE_CART_IDS = 'UPDATE_CART_IDS';
export const GET_CART_ITEMS = 'GET_CART_ITEMS';

export const IS_LOADED = 'IS_LOADED';
export const FAIL_LOAD = 'FAIL_LOAD';

export const AUTH_START = 'AUTH_START'
export const AUTH_SUCCESS = 'AUTH_SUCCESS'
export const AUTH_FAIL = 'AUTH_FAIL'
export const AUTH_LOGOUT = 'AUTH_LOGOUT'
<file_sep>/src/containers/Modal/ToCartModal.js
import React, {useState} from 'react';
import {makeStyles} from '@material-ui/core/styles';
import Modal from '@material-ui/core/Modal';
import ShoppingCartIcon from "@material-ui/icons/ShoppingCart";
import Button from "@material-ui/core/Button";
import CardMedia from "@material-ui/core/CardMedia";
import AddIcon from '@material-ui/icons/AddCircleRounded';
import RemoveIcon from '@material-ui/icons/Remove';
import IconButton from "@material-ui/core/IconButton";
import Typography from "@material-ui/core/Typography";
import Grid from '@material-ui/core/Grid';
import * as actions from "../../store/actions/";
import {connect} from "react-redux";

function rand() {
    return Math.round(Math.random() * 20) - 10;
}

function getModalStyle() {
    const top = 50 + rand();
    const left = 50 + rand();

    return {
        top: `${top}%`,
        left: `${left}%`,
        transform: `translate(-${top}%, -${left}%)`,
    };
}

const useStyles = makeStyles((theme) => ({
    paper: {
        position: 'absolute',
        width: 400,
        backgroundColor: theme.palette.background.paper,
        border: '2px solid #000',
        boxShadow: theme.shadows[5],
        padding: theme.spacing(2, 4, 3),
    },
    media: {
        height: 0,
        paddingTop: '56.25%', // 16:9
    },
}));

const ToCartModal = (props) => {
    const classes = useStyles();
    const [modalStyle] = useState(getModalStyle);
    const [open, setOpen] = useState(false);
    const [quantity, setQuantity] = useState(1);

    const inc = () => {
        setQuantity(quantity + 1)
    };

    const dec = () => {
        if (quantity !== 1) {
            setQuantity(quantity - 1)
        } else return null
    };

    const computedData = () => {
        let data = [quantity, props.item.id]
        return data;
    };

    const handleOpen = () => {
        setOpen(true);
    };

    const handleClose = () => {
        setOpen(false);
    };

    const addBtnHandler = () => {
        props.addToCart(props.userToken, props.userId, props.userCart, computedData());
        handleClose();
    };

    const body = (
        <div style={modalStyle} className={classes.paper}>
            <Grid container spacing={3}>
                <Grid item xs={12}>
                <Typography variant="h5" color="textPrimary" component="h3">
                    {props.item.title}
                </Typography>
                </Grid>
                <Grid item xs={12}>
                <CardMedia
                    className={classes.media}
                    image={props.item.img}
                />
                </Grid>
                <Grid item xs={12}>
                <Typography variant="h5" color="textPrimary" component="h3">
                    $ {props.item.price * quantity} (x{quantity})
                </Typography>
                </Grid>

                <Grid container direction="row" justify="space-between" alignItems="center">
                    <Grid item container xs={4} direction="row" justify="flex-start">
                        <IconButton aria-label="add to favorites" onClick={dec}><RemoveIcon/></IconButton>
                        <IconButton aria-label="add to favorites" onClick={inc}><AddIcon/></IconButton>
                    </Grid>
                    <Grid item container xs={4} direction="row" justify="flex-end">
                        <Button size="large" color="secondary" onClick={() => addBtnHandler()}>add <ShoppingCartIcon/></Button>
                    </Grid>
                </Grid>

            </Grid>
        </div>
    );

    return (
        <div>
            <Button size="medium" color="secondary" onClick={handleOpen}>Add To Cart</Button>
            <Modal
                open={open}
                onClose={handleClose}
                aria-labelledby="simple-modal-title"
                aria-describedby="simple-modal-description"
            >
                {body}
            </Modal>
        </div>
    );
};

const mapStateToProps = state => {
    return {
        userId: state.user.userId,
        userToken: state.user.token,
        userCart: state.cart
    }
}
const mapDispatchToProps = dispatch => {
    return {
        addToCart: (token, userId, cart, currentItem) => dispatch(actions.add_to_cart(token, userId, cart, currentItem))
    }
};

export default connect(mapStateToProps, mapDispatchToProps)(ToCartModal)<file_sep>/src/containers/Auth/Auth.js
import React, { useState } from 'react';
import TextField from '@material-ui/core/TextField';
import { Redirect } from 'react-router-dom'
import Button from '@material-ui/core/Button';
import Grid from "@material-ui/core/Grid";
import { connect } from 'react-redux';
import * as actions from "../../store/actions/";
import Typography from "@material-ui/core/Typography";


const Auth = (props) => {

    const [formValid, setFormValid] = useState({
        isValidEmail: false,
        isValidPass: false
    });

    const [isSignIn, setIsSignIn] = useState(true)
    const [email, setEmail] = useState('')
    const [password, setPassword] = useState('')

    const emailHandler = (event) => {
        setEmail(event.target.value);
    }

    const passwordHandler = (event) => {
        setPassword(event.target.value);
    }

    const submitHandler = (event) => {
        event.preventDefault();
        props.onAuth(email, password, isSignIn)
    }

    const switchAuthMode = () => {
        setIsSignIn(!isSignIn)
    }

    return (
        <form onSubmit={submitHandler}>
            <Grid container direction="column"
                  justify="center"
                  alignItems="center"
                  spacing={4}>
                <Grid container justify="flex-start" item xs={4}>
                    <Typography variant="h5" color="textPrimary" component="h3">
                        {isSignIn ? 'Sign In' : 'Sign Up'}
                    </Typography>
                </Grid>
                <Grid container item xs={4}>
                    <TextField fullWidth id="email" onChange={emailHandler} label="Email"/>
                </Grid>
                <Grid container item xs={4}>
                    <TextField fullWidth id="password" type="<PASSWORD>" onChange={passwordHandler} label="Password"/>
                </Grid>
                <Grid container justify="flex-start" item xs={4}>
                    <Typography  color="textSecondary" component="p">
                        {props.authError}
                    </Typography>
                </Grid>
                <Grid container justify="space-between" item xs={4}>
                    <Button variant="outlined" color="primary" onClick={switchAuthMode}>Switch to {isSignIn ? 'sign up' : 'sign in'}</Button>
                    <Button variant="contained" type="submit" disabled={false} color="primary">
                        Submit
                    </Button>
                </Grid>

                {props.isAuthSuccess !== null && !props.isLoading  ? <Redirect to='/' /> : null}

            </Grid>
        </form>
    );
}

const mapStateToProps = state => {
    return {
        authError: state.user.error,
        isAuthSuccess: state.user.userId,
        isLoading: state.user.loading
    }
}

const mapDispatchToProps = dispatch => {
    return {
        onAuth: (email, password, isSignIn) => dispatch(actions.auth(email, password, isSignIn)),
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Auth);<file_sep>/src/App.js
import React, {useEffect} from 'react';

import * as ROUTES from './router/routes';


import Bar from './components/Bar/Bar'
import ItemsList from "./containers/ItemsList/ItemsList";
import Auth from "./containers/Auth/Auth";

import { connect, useDispatch } from 'react-redux'
import * as actions from "../src/store/actions/";

import {BrowserRouter as Router, Switch, Route} from "react-router-dom";
import Container from '@material-ui/core/Container';
import {makeStyles} from "@material-ui/core/styles";
import Cart from "./containers/Cart/Cart";

const useStyles = makeStyles({
    root: {
        marginTop: '120px',
    },
});

function App() {
    const classes = useStyles();


    const dispatch = useDispatch();
    useEffect(() => {
        dispatch(actions.auth_check_state());
            dispatch(actions.cart_check_state());
    }, [])

    return (

            <Router>

                <Bar/>

                <Container maxWidth={'lg'}  className={classes.root}>

                    <Route exact path={ROUTES.HOME} component={ItemsList}/>

                    <Switch>
                        <Route path={ROUTES.AUTH} component={Auth}/>
                        {/*<Route path={ROUTES.SINGLE} component={SingleItem}/>*/}
                        <Route path={ROUTES.CART} component={Cart}/>
                    </Switch>

                </Container>
            </Router>
    );
}



export default connect(null, null)(App);
<file_sep>/src/store/actions/product/product.js
import * as actionTypes from '../actionTypes';
import axios from "../../../axios";

export const is_loaded = (res) => {
    return {
        type: actionTypes.IS_LOADED,
        result: res
    }
};

export const fail_load = (res) => {
    return {
        type: actionTypes.FAIL_LOAD,
        result: res
    }
};

export const save_loaded_items = (res) => {
    return {
        type: actionTypes.LOAD_ITEMS,
        result: res,
    }
};

export const load_items = () => {
    return dispatch => {
        axios.get('/data.json')
            .then(response => {
                dispatch(save_loaded_items(response.data));
                dispatch(is_loaded(true))
            })
            .catch(err => console.log(err));
        dispatch(fail_load(true))
    }
};<file_sep>/src/store/reducers/reducer.js
import * as actionTypes from '../actions/actionTypes';

const initialState = {
    items: null,
    single: null,
    isLoaded: false,
    failLoad: false,
    cart: [],
    cartIds:[],
    user: {
        token: null,
        userId: null,
        error: null,
        loading: false
    }
};

const reducer = (state = initialState, action) => {
    switch (action.type) {

        case actionTypes.AUTH_START: {
            return {
                ...state,
                user: {...{ error: null, loading: true}}
            }
        }

        case actionTypes.AUTH_SUCCESS: {
            return {
                ...state,
                user: {...{token: action.idToken, userId: action.userId, error: null, loading: false}}
            }
        }

        case actionTypes.AUTH_FAIL: {
            return {
                ...state,
                user: {...{error: action.error, loading: false, userId: null}
                }
            }
        }

        case actionTypes.AUTH_LOGOUT: {
            return {
                ...state,
                user: {...{token: null, userId: null}},
                cart: [],
                cartIds: []
            }
        }

        case actionTypes.LOAD_ITEMS: {
            return {
                ...state,
               items: action.result
            };
        }

        case actionTypes.IS_LOADED: {
            return {
                ...state,
                isLoaded: action.result
            }
        }
        case actionTypes.FAIL_LOAD: {
            return {
                ...state,
                failedLoad: action.result
            }
        }

        case actionTypes.UPDATE_CART: {
            return {
                ...state,
                cart: action.result
            }
        }

        case actionTypes.UPDATE_CART_IDS: {
            return {
                ...state,
                cartIds: action.result
            }
        }

        case actionTypes.GET_CART_ITEMS: {
            return {
                cartItems: state.cart
            }
        }
        default:
            break;

    }
    return state;
};

export default reducer;<file_sep>/src/router/routes.js
export const HOME = '/';
export const AUTH = '/auth';
export const CART = '/cart';
export const SINGLE = '/single/:id';
<file_sep>/src/store/actions/index.js
export {load_items, is_loaded, fail_load, save_loaded_items} from './product/product'
export {get_cart_items, update_cart, add_to_cart, cart_check_state} from './cart/cart'
export {auth, logout, auth_check_state} from './auth/auth';

<file_sep>/src/containers/ItemsList/ItemsList.js
import React, {Component} from 'react'
import CircularProgress from '@material-ui/core/CircularProgress';
import Item from '../../components/Item/Item'
import {connect} from 'react-redux'
import * as actions from "../../store/actions/";
import Grid from '@material-ui/core/Grid';

class ItemsList extends Component {

    componentWillMount() {
        this.props.loadItems();
    }

    render() {

        return (
            <Grid
                container
                spacing={3}
                direction="row"
                justify="flex-start"
                alignItems="center"
            >
                {
                    this.props.isLoaded ?
                    this.props.loadedItems.map((item, index) =>
                        <Grid key={item.id} item xs={4}>
                        <Item
                            title={item.title}
                            date={item.date}
                            available={item.available}
                            price={item.price}
                            key={index}
                            id={item.id}
                            description={item.description}
                            img={item.img}

                        /></Grid>) :
                    <CircularProgress/>
                }

            </Grid>
        );
    }
}

const mapStateToProps = state => {
    return {
        loadedItems: state.items,
        isLoaded: state.isLoaded,
    }
};

const mapDispatchToProps = dispatch => {
    return {
        loadItems: () => dispatch(actions.load_items()),
    }
};

export default connect(mapStateToProps, mapDispatchToProps)(ItemsList)

