Skip to main content

18.15 Redirecting the User To the Checkout Page

Auth.js

src\containers\Auth\Auth.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { Redirect } from "react-router-dom";
import Button from "../../components/UI/Button/Button";
import Input from "../../components/UI/Input/Input";
import Spinner from "../../components/UI/Spinner/Spinner";
import * as actions from "../../store/actions/index";
import classes from "./Auth.module.css";

class Auth extends Component {
state = {
controls: {
email: {
elementType: "input",
elementConfig: {
type: "email",
placeholder: "Mail Address",
},
value: "",
validation: {
required: true,
isEmail: true,
},
valid: false,
touched: false,
},
password: {
elementType: "input",
elementConfig: {
type: "password",
placeholder: "Password",
},
value: "",
validation: {
required: true,
minLength: 6,
},
valid: false,
touched: false,
},
},
isSignup: true,
};

componentDidMount() {
if (!this.props.buildingBurger && this.props.authRedirectPath !== "/") {
this.props.onSetAuthRedirectPath();
}
}

checkValidity(value, rules) {
let isValid = true;
if (!rules) {
return true;
}

if (rules.required) {
isValid = value.trim() !== "" && isValid;
}

if (rules.minLength) {
isValid = value.length >= rules.minLength && isValid;
}

if (rules.maxLength) {
isValid = value.length <= rules.maxLength && isValid;
}

if (rules.isEmail) {
const pattern = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;
isValid = pattern.test(value) && isValid;
}

if (rules.isNumeric) {
const pattern = /^\d+$/;
isValid = pattern.test(value) && isValid;
}

return isValid;
}

inputChangedHandler = (event, controlName) => {
const updatedControls = {
...this.state.controls,
[controlName]: {
...this.state.controls[controlName],
value: event.target.value,
valid: this.checkValidity(
event.target.value,
this.state.controls[controlName].validation
),
touched: true,
},
};
this.setState({ controls: updatedControls });
};

submitHandler = (event) => {
event.preventDefault();
this.props.onAuth(
this.state.controls.email.value,
this.state.controls.password.value,
this.state.isSignup
);
};

switchAuthModeHandler = () => {
this.setState((prevState) => {
return { isSignup: !prevState.isSignup };
});
};
render() {
const formElementsArray = [];
for (let key in this.state.controls) {
formElementsArray.push({
id: key,
config: this.state.controls[key],
});
}

let form = formElementsArray.map((formElement) => (
<Input
key={formElement.id}
elementType={formElement.config.elementType}
elementConfig={formElement.config.elementConfig}
value={formElement.config.value}
invalid={!formElement.config.valid}
shouldValidate={formElement.config.validation}
touched={formElement.config.touched}
changed={(event) => this.inputChangedHandler(event, formElement.id)}
/>
));

if (this.props.loading) {
form = <Spinner />;
}

let errorMessage = null;

if (this.props.error) {
errorMessage = <p>{this.props.error.message}</p>;
}

let authRedirect = null;
if (this.props.isAuthenticated) {
authRedirect = <Redirect to={this.props.authRedirectPath} />;
}

return (
<div className={classes.Auth}>
{authRedirect}
{errorMessage}
<form onSubmit={this.submitHandler}>
{form}
<Button btnType="Success">Submit</Button>
<Button clicked={this.switchAuthModeHandler} btnType="Danger">
SWITCH TO {this.state.isSignup ? "SIGNIN" : "SIGNUP"}
</Button>
</form>
</div>
);
}
}

const mapStateToProps = (state) => {
return {
loading: state.auth.loading,
error: state.auth.error,
isAuthenticated: state.auth.token !== null,
buildingBurger: state.burgerBuilder.building,
authRedirectPath: state.auth.authRedirectPath,
};
};

const mapDispatchToProps = (dispatch) => {
return {
onAuth: (email, password, isSignup) =>
dispatch(actions.auth(email, password, isSignup)),
onSetAuthRedirectPath: () => dispatch(actions.setAuthRedirectPath("/")),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Auth);

BurgerBuilder.js

src\containers\BurgerBuilder\BurgerBuilder.js
import React, { Component, Fragment } from "react";
import { connect } from "react-redux";
import axios from "../../axios-orders";
import BuildControls from "../../components/Burger/BuildControls/BuildControls";
import Burger from "../../components/Burger/Burger";
import OrderSummary from "../../components/Burger/OrderSummary/OrderSummary";
import Modal from "../../components/UI/Modal/Modal";
import Spinner from "../../components/UI/Spinner/Spinner";
import withErrorHandler from "../../hoc/withErrorHandler/withErrorHandler";
import * as actions from "../../store/actions/index";

class BurgerBuilder extends Component {
// constructor(props) {
// super(props);
// this.state = {...}
// }
state = {
purchasing: false,
};

componentDidMount() {
console.log(this.props);
this.props.onInitIngredients();
}

updatePurchaseState(ingredients) {
const sum = Object.keys(ingredients)
.map((igKey) => {
return ingredients[igKey];
})
.reduce((sum, el) => {
return sum + el;
}, 0);
return sum > 0;
}

purchaseHandler = () => {
if (this.props.isAuthenticated) {
this.setState({ purchasing: true });
} else {
this.props.onSetAuthRedirectPath("/checkout");
this.props.history.push("/auth");
}
};

purchaseCancelHandler = () => {
this.setState({ purchasing: false });
};

purchaseContinueHandler = () => {
this.props.onInitPurchase();
this.props.history.push("/checkout");
};
render() {
const disabledInfo = {
...this.props.ings,
};

for (let key in disabledInfo) {
disabledInfo[key] = disabledInfo[key] <= 0;
}
let orderSummary = null;

let burger = this.props.error ? (
<p>Ingredients can't be loaded!</p>
) : (
<Spinner />
);

if (this.props.ings) {
burger = (
<Fragment>
<Burger ingredients={this.props.ings} />
<BuildControls
ingredientAdded={this.props.onIgredientAdded}
ingredientRemoved={this.props.onIgredientRemoved}
disabled={disabledInfo}
purchasable={this.updatePurchaseState(this.props.ings)}
ordered={this.purchaseHandler}
isAuth={this.props.isAuthenticated}
price={this.props.price}
/>
</Fragment>
);

orderSummary = (
<OrderSummary
ingredients={this.props.ings}
purchaseCancelled={this.purchaseCancelHandler}
purchaseContinued={this.purchaseContinueHandler}
price={this.props.price}
/>
);
}

return (
<Fragment>
<Modal
show={this.state.purchasing}
modalClosed={this.purchaseCancelHandler}
>
{orderSummary}
</Modal>
{burger}
</Fragment>
);
}
}

const mapStateToProps = (state) => {
return {
ings: state.burgerBuilder.ingredients,
price: state.burgerBuilder.totalPrice,
error: state.burgerBuilder.error,
isAuthenticated: state.auth.token !== null,
};
};

const mapDispatchToProps = (dispatch) => {
return {
onIgredientAdded: (ingName) => dispatch(actions.addIngredient(ingName)),
onIgredientRemoved: (ingName) =>
dispatch(actions.removeIngredient(ingName)),
onInitIngredients: () => dispatch(actions.initIngredients()),
onInitPurchase: () => dispatch(actions.purchaseInit()),
onSetAuthRedirectPath: (path) =>
dispatch(actions.setAuthRedirectPath(path)),
};
};

export default connect(
mapStateToProps,
mapDispatchToProps
)(withErrorHandler(BurgerBuilder, axios));

actionTypes.js

src\store\actions\actionTypes.js
export const ADD_INGREDIENT = "ADD_INGREDIENT";
export const REMOVE_INGREDIENT = "REMOVE_INGREDIENT";
export const SET_INGREDIENTS = "SET_INGREDIENTS";
export const FETCH_INGREDIENTS_FAILED = "FETCH_INGREDIENTS_FAILED";

export const PURCHASE_BURGER_START = "PURCHASE_BURGER_START";
export const PURCHASE_BURGER_SUCCESS = "PURCHASE_BURGER_SUCCESS";
export const PURCHASE_BURGER_FAIL = "PURCHASE_BURGER_FAIL";
export const PURCHASE_INIT = "PURCHASE_INIT";

export const FETCH_ORDERS_START = "FETCH_ORDERS_START";
export const FETCH_ORDERS_SUCCESS = "FETCH_ORDERS_SUCCESS";
export const FETCH_ORDERS_FAIL = "FETCH_ORDERS_FAIL";

export const AUTH_START = "AUTH_START";
export const AUTH_SUCCESS = "AUTH_SUCCESS";
export const AUTH_FAIL = "AUTH_FAIL";
export const AUTH_LOGOUT = "AUTH_LOGOUT";

export const SET_AUTH_REDIRECT_PATH = "SET_AUTH_REDIRECT_PATH";

actions/auth.js

src\store\actions\auth.js
import axios from "axios";
import * as actionTypes from "./actionTypes";

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

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

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

export const logout = () => {
return {
type: actionTypes.AUTH_LOGOUT,
};
};

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

export const auth = (email, password, isSignup) => {
return (dispatch) => {
dispatch(authStart());
const authData = {
email: email,
password: password,
returnSecureToken: true,
};
let url =
"https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=AIzaSyDsmwPeH2yE7yqvoeYolCLgB9ju50rYivo";
if (!isSignup) {
url =
"https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=AIzaSyDsmwPeH2yE7yqvoeYolCLgB9ju50rYivo";
}
axios
.post(url, authData)
.then((response) => {
console.log(response);
dispatch(authSuccess(response.data.idToken, response.data.localId));
dispatch(checkAuthTimeout(response.data.expiresIn));
})
.catch((err) => {
dispatch(authFail(err.response.data.error));
});
};
};

export const setAuthRedirectPath = (path) => {
return {
type: actionTypes.SET_AUTH_REDIRECT_PATH,
path: path,
};
};

index.js

src\store\actions\index.js
export { auth, logout, setAuthRedirectPath } from "./auth";
export {
addIngredient,
initIngredients,
removeIngredient,
} from "./burgerBuilder";
export { fetchOrders, purchaseBurger, purchaseInit } from "./order";

reducers/auth.js

src\store\reducers\auth.js
import * as actionTypes from "../actions/actionTypes";
import { updateObject } from "./../utility";

const initialState = {
token: null,
userId: null,
error: null,
loading: false,
authRedirectPath: "/",
};

const authStart = (state, action) => {
return updateObject(state, { error: null, loading: true });
};

const authSuccess = (state, action) => {
return updateObject(state, {
token: action.idToken,
userId: action.userId,
error: null,
loading: false,
});
};

const authFail = (state, action) => {
return updateObject(state, {
error: action.error,
loading: false,
});
};

const authLogout = (state, action) => {
return updateObject(state, { token: null, userId: null });
};

const setAuthRedirectPath = (state, action) => {
return updateObject(state, {
authRedirectPath: action.path,
});
};

const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.AUTH_START:
return authStart(state, action);
case actionTypes.AUTH_SUCCESS:
return authSuccess(state, action);
case actionTypes.AUTH_FAIL:
return authFail(state, action);
case actionTypes.AUTH_LOGOUT:
return authLogout(state, action);
case actionTypes.SET_AUTH_REDIRECT_PATH:
return setAuthRedirectPath(state, action);

default:
return state;
}
};
export default reducer;

burgerBuilder.js

src\store\reducers\burgerBuilder.js
import * as actionTypes from "../actions/actionTypes";
import { updateObject } from "../utility";
// import { setIngredients } from "./../actions/burgerBuilder";

const initialState = {
ingredients: null,
totalPrice: 4,
error: false,
building: false,
};

const INGREDIENT_PRICES = {
salad: 0.5,
cheese: 0.4,
meat: 1.3,
bacon: 0.7,
};

const addIngredient = (state, action) => {
const updatedIngredient = {
[action.ingredientName]: state.ingredients[action.ingredientName] + 1,
};
const updatedIngredients = updateObject(state.ingredients, updatedIngredient);
const updatedState = {
ingredients: updatedIngredients,
totalPrice: state.totalPrice + INGREDIENT_PRICES[action.ingredientName],
building: true,
};
return updateObject(state, updatedState);
};

const removeIngredient = (state, action) => {
const updatedIng = {
[action.ingredientName]: state.ingredients[action.ingredientName] - 1,
};
const updatedIngs = updateObject(state.ingredients, updatedIng);
const updatedSt = {
ingredients: updatedIngs,
totalPrice: state.totalPrice - INGREDIENT_PRICES[action.ingredientName],
building: true,
};
return updateObject(state, updatedSt);
};

const setIngredients = (state, action) => {
return updateObject(state, {
ingredients: {
salad: action.ingredients.salad,
bacon: action.ingredients.bacon,
cheese: action.ingredients.cheese,
meat: action.ingredients.meat,
},
totalPrice: 4,
error: false,
building: false,
});
};

const fetchIngredientsFailed = (state, action) => {
return updateObject(state, { error: true });
};

const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.ADD_INGREDIENT:
return addIngredient(state, action);

case actionTypes.REMOVE_INGREDIENT:
return removeIngredient(state, action);

case actionTypes.SET_INGREDIENTS:
return setIngredients(state, action);

case actionTypes.FETCH_INGREDIENTS_FAILED:
return fetchIngredientsFailed(state, action);

default:
return state;
}
};

export default reducer;