18.20 Displaying User Specific Orders
Realtime Database (Firebase)
Source: console.firebase.google.com: Rules
{
"rules": {
"ingredients": {
".read": "true",
".write": "true"
},
"orders": {
".read": "auth != null",
".write": "auth != null",
".indexOn": ["userId"]
}
}
}
ContactData.js
src\containers\Checkout\ContactData\ContactData.js
import React, { Component } from "react";
import { connect } from "react-redux";
import axios from "../../../axios-orders";
import Button from "../../../components/UI/Button/Button";
import Input from "../../../components/UI/Input/Input";
import Spinner from "../../../components/UI/Spinner/Spinner";
import withErrorHandler from "../../../hoc/withErrorHandler/withErrorHandler";
import * as actions from "../../../store/actions/index";
import classes from "./ContactData.module.css";
class ContactData extends Component {
state = {
orderForm: {
name: {
elementType: "input",
elementConfig: {
type: "text",
placeholder: "Your Name",
},
value: "",
validation: {
required: true,
},
valid: false,
touched: false,
},
street: {
elementType: "input",
elementConfig: {
type: "text",
placeholder: "Street",
},
value: "",
validation: {
required: true,
},
valid: false,
touched: false,
},
zipCode: {
elementType: "input",
elementConfig: {
type: "text",
placeholder: "ZIP Code",
},
value: "",
validation: {
required: true,
minLength: 5,
maxLength: 5,
},
valid: false,
touched: false,
},
country: {
elementType: "input",
elementConfig: {
type: "text",
placeholder: "Country",
},
value: "",
validation: {
required: true,
},
valid: false,
touched: false,
},
email: {
elementType: "input",
elementConfig: {
type: "email",
placeholder: "Your e-mail",
},
value: "",
validation: {
required: true,
},
valid: false,
touched: false,
},
deliveryMethod: {
elementType: "select",
elementConfig: {
options: [
{ value: "fastest", displayValue: "Fastest" },
{ value: "cheapest", displayValue: "Cheapest" },
],
},
value: "fastest",
valid: true,
validation: {
required: false,
},
},
},
formIsValid: false,
};
orderHandler = (event) => {
event.preventDefault();
const formData = {};
for (let formElementIdentifier in this.state.orderForm) {
formData[formElementIdentifier] = this.state.orderForm[
formElementIdentifier
].value;
}
const order = {
ingredients: this.props.ings,
price: this.props.price,
orderData: formData,
userId: this.props.userId,
};
this.props.onOrderBurger(order, this.props.token);
};
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, inputIdentifier) => {
const updatedOrderForm = {
...this.state.orderForm,
};
const updatedFormElement = {
...updatedOrderForm[inputIdentifier],
};
updatedFormElement.value = event.target.value;
updatedFormElement.valid = this.checkValidity(
updatedFormElement.value,
updatedFormElement.validation
);
updatedFormElement.touched = true;
updatedOrderForm[inputIdentifier] = updatedFormElement;
let formIsValid = true;
for (let inputIdentifier in updatedOrderForm) {
formIsValid = updatedOrderForm[inputIdentifier].valid && formIsValid;
}
this.setState({ orderForm: updatedOrderForm, formIsValid: formIsValid });
};
render() {
const formElementsArray = [];
for (let key in this.state.orderForm) {
formElementsArray.push({
id: key,
config: this.state.orderForm[key],
});
}
let form = (
<form onSubmit={this.orderHandler}>
{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)}
/>
))}
<Button btnType="Success" disabled={!this.state.formIsValid}>
ORDER
</Button>
</form>
);
if (this.props.loading) {
form = <Spinner />;
}
return (
<div className={classes.ContactData}>
<h4>Enter your Contact Data</h4>
{form}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
ings: state.burgerBuilder.ingredients,
price: state.burgerBuilder.totalPrice,
loading: state.order.loading,
token: state.auth.token,
userId: state.auth.userId,
};
};
const mapDispatchToProps = (dispatch) => {
return {
onOrderBurger: (orderData, token) =>
dispatch(actions.purchaseBurger(orderData, token)),
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(withErrorHandler(ContactData, axios));
Orders.js
src\containers\Orders\Orders.js
import React, { Component } from "react";
import { connect } from "react-redux";
import axios from "../../axios-orders";
import Order from "../../components/Order/Order";
import Spinner from "../../components/UI/Spinner/Spinner";
import withErrorHandler from "../../hoc/withErrorHandler/withErrorHandler";
import * as actions from "../../store/actions/index";
class Orders extends Component {
componentDidMount() {
this.props.onFetchOrders(this.props.token, this.props.userId);
}
render() {
let orders = <Spinner />;
if (!this.props.loading) {
orders = this.props.orders.map((order) => (
<Order
key={order.id}
ingredients={order.ingredients}
price={order.price}
/>
));
}
return orders;
}
}
const mapStateToProps = (state) => {
return {
orders: state.order.orders,
loading: state.order.loading,
token: state.auth.token,
userId: state.auth.userId,
};
};
const mapDispatchToProps = (dispatch) => {
return {
onFetchOrders: (token, userId) =>
dispatch(actions.fetchOrders(token, userId)),
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(withErrorHandler(Orders, axios));
order.js
src\store\actions\order.js
import axios from "../../axios-orders";
import * as actionTypes from "./actionTypes";
export const purchaseBurgerSuccess = (id, orderData) => {
return {
type: actionTypes.PURCHASE_BURGER_SUCCESS,
orderId: id,
orderData: orderData,
};
};
export const purchaseBurgerFail = (error) => {
return {
type: actionTypes.PURCHASE_BURGER_FAIL,
error: error,
};
};
export const purchaseBurgerStart = () => {
return {
type: actionTypes.PURCHASE_BURGER_START,
};
};
export const purchaseBurger = (orderData, token) => {
return (dispatch) => {
dispatch(purchaseBurgerStart());
axios
.post("/orders.json?auth=" + token, orderData)
.then((response) => {
console.log(response.data);
dispatch(purchaseBurgerSuccess(response.data.name, orderData));
})
.catch((error) => {
dispatch(purchaseBurgerFail(error));
});
};
};
export const purchaseInit = () => {
return {
type: actionTypes.PURCHASE_INIT,
};
};
export const fetchOrdersSuccess = (orders) => {
return {
type: actionTypes.FETCH_ORDERS_SUCCESS,
orders: orders,
};
};
export const fetchOrdersFail = (error) => {
return {
type: actionTypes.FETCH_ORDERS_FAIL,
error: error,
};
};
export const fetchOrdersStart = () => {
return {
type: actionTypes.FETCH_ORDERS_START,
};
};
export const fetchOrders = (token, userId) => {
return (dispatch, getState) => {
dispatch(fetchOrdersStart());
const queryParams =
"?auth=" + token + '&orderBy="userId"&equalTo="' + userId + '"';
axios
.get("/orders.json" + queryParams)
.then((res) => {
const fetchedOrders = [];
for (let key in res.data) {
fetchedOrders.push({
...res.data[key],
id: key,
});
}
dispatch(fetchOrdersSuccess(fetchedOrders));
})
.catch((err) => {
dispatch(fetchOrdersFail(err));
});
};
};