Commit 63ecdad7 authored by Pavel Mishakov's avatar Pavel Mishakov

finished lesson 68

parent d0dadb30
This diff is collapsed.
...@@ -3,12 +3,14 @@ ...@@ -3,12 +3,14 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@reduxjs/toolkit": "^1.9.1",
"@testing-library/jest-dom": "^5.16.4", "@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.2.0", "@testing-library/react": "^13.2.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"axios": "^1.2.1", "axios": "^1.2.1",
"react": "^18.1.0", "react": "^18.1.0",
"react-dom": "^18.1.0", "react-dom": "^18.1.0",
"react-redux": "^8.0.5",
"react-router-dom": "^6.4.4", "react-router-dom": "^6.4.4",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
......
...@@ -4,8 +4,15 @@ import Checkout from "./containers/Checkout/Checkout"; ...@@ -4,8 +4,15 @@ import Checkout from "./containers/Checkout/Checkout";
import ContactData from "./containers/Checkout/ContactData/ContactData"; import ContactData from "./containers/Checkout/ContactData/ContactData";
import Layout from "./components/Layout/Layout"; import Layout from "./components/Layout/Layout";
import Orders from "./containers/Orders/Orders"; import Orders from "./containers/Orders/Orders";
import { getIngredients } from "./store/ingredients.slice";
import { useDispatch } from "react-redux";
import { useEffect } from "react";
function App() { function App() {
const dispatch = useDispatch()
useEffect(() => {
dispatch(getIngredients())
}, [])
return ( return (
<BrowserRouter> <BrowserRouter>
<Routes> <Routes>
......
...@@ -16,6 +16,14 @@ class ApiBurger { ...@@ -16,6 +16,14 @@ class ApiBurger {
console.log(err) console.log(err)
} }
} }
getIngredients = async() => {
try {
const response = await burgerInstance.get('/ingredients.json')
return response?.data
} catch (err) {
console.log(err)
}
}
} }
export const apiBurger = new ApiBurger() export const apiBurger = new ApiBurger()
\ No newline at end of file
import React from 'react'; import React from 'react';
import './BuildControls.css'; import './BuildControls.css';
import BuildControl from './BuildControl/BuildControl' import BuildControl from './BuildControl/BuildControl'
import { useSelector } from 'react-redux';
const BuildControls = props => { const BuildControls = props => {
const ingredients = useSelector(state => state.ingredients.basket)
return ( return (
<div className='BuildControls'> <div className='BuildControls'>
<p>Current Price: <strong>{props.price} KZT</strong></p> <p>Current Price: <strong>{props.price} KZT</strong></p>
{Object.keys(props.ingredients).map(ingType => { {Object.keys(ingredients).map(ingType => {
return <BuildControl return <BuildControl
key={ingType} key={ingType}
type={ingType} type={ingType}
......
import React from 'react'; import React from 'react';
import { useSelector } from 'react-redux';
import './Burger.css'; import './Burger.css';
import Ingredient from './Ingredient/Ingredient' import Ingredient from './Ingredient/Ingredient'
// const Burger = (props) => { // const Burger = (props) => {
//const ingredientKeys = Object.keys(props.ingredients) // ['salad', 'bacon'...] //const ingredientKeys = Object.keys(props.ingredients) // ['salad', 'bacon'...]
// props = {ingredients} {ingredients} // props = {ingredients} {ingredients}
const Burger = ({ingredients}) => { const Burger = () => {
const ingredients = useSelector(state => state.ingredients.basket)
const ingredientKeys = Object.keys(ingredients) // ['salad', 'bacon'...] const ingredientKeys = Object.keys(ingredients) // ['salad', 'bacon'...]
let ingList = [] let ingList = []
ingredientKeys.forEach(igKey => { ingredientKeys.forEach(igKey => {
......
...@@ -110,3 +110,10 @@ ...@@ -110,3 +110,10 @@
background: linear-gradient(#bf3813, #c45e38); background: linear-gradient(#bf3813, #c45e38);
margin: 2% auto; margin: 2% auto;
} }
.Tomato {
width: 70%;
height: 8%;
background: linear-gradient(to right, red 50%, white 20%, orange 30%);
margin: 2% auto;
}
...@@ -12,16 +12,8 @@ const Ingredient = ({type}) => { ...@@ -12,16 +12,8 @@ const Ingredient = ({type}) => {
<div className='Seeds2' /> <div className='Seeds2' />
</div> </div>
) )
case 'meat':
return <div className='Meat' />
case 'bacon':
return <div className='Bacon' />
case 'cheese':
return <div className='Cheese' />
case 'salad':
return <div className='Salad' />
default: default:
return null return <div className={type} />
} }
} }
......
import React from "react"; import React from "react";
import { useSelector } from "react-redux";
import Button from "../../UI/Button/Button"; import Button from "../../UI/Button/Button";
import './OrderSummary.css' import './OrderSummary.css'
const OrderSummary = (props) => { const OrderSummary = (props) => {
const ingredients = useSelector(state => state.ingredients.basket)
const totalPrice = useSelector(state => state.ingredients.totalPrice)
const ingredientSummary = Object.keys(props.ingredients) const ingredientSummary = Object.keys(ingredients)
.map(igKey => { .map(igKey => {
return ( return (
<li key={igKey}> <li key={igKey}>
<span style={{textTransform: 'capitalize'}}> <span style={{textTransform: 'capitalize'}}>
{igKey} {igKey}
</span> : {props.ingredients[igKey]} </span> : {ingredients[igKey]}
</li> </li>
) )
}) })
...@@ -22,7 +25,7 @@ const OrderSummary = (props) => { ...@@ -22,7 +25,7 @@ const OrderSummary = (props) => {
<ul> <ul>
{ingredientSummary} {ingredientSummary}
</ul> </ul>
<p><strong>Total price: </strong>{props.price} KZT</p> <p><strong>Total price: </strong>{totalPrice} KZT</p>
<p>Continue to checkout</p> <p>Continue to checkout</p>
<Button btnType={'Danger'} clicked={props.purchaseCancelled}>CANCEL</Button> <Button btnType={'Danger'} clicked={props.purchaseCancelled}>CANCEL</Button>
<Button btnType={'Success'} clicked={props.purchaseContinued}>CONTINUE</Button> <Button btnType={'Success'} clicked={props.purchaseContinued}>CONTINUE</Button>
......
import React from 'react'; import React, { useEffect } from 'react';
import { useState } from 'react'; import { useState } from 'react';
import { createSearchParams, useNavigate } from 'react-router-dom'; import { useDispatch, useSelector } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import BuildControls from '../../components/BuildControls/BuildControls'; import BuildControls from '../../components/BuildControls/BuildControls';
import Burger from '../../components/Burger/Burger' import Burger from '../../components/Burger/Burger'
import OrderSummary from '../../components/Burger/OrderSummary/OrderSummary'; import OrderSummary from '../../components/Burger/OrderSummary/OrderSummary';
import Modal from '../../components/UI/Modal/Modal'; import Modal from '../../components/UI/Modal/Modal';
import {INGREDIENT_PRICES} from "../../constants/ingredients_prices"; import { addIngredient, removeIngredient } from '../../store/ingredients.slice';
const BurgerBuilder = () => { const BurgerBuilder = () => {
const dispatch = useDispatch()
const navigate = useNavigate() const navigate = useNavigate()
const [ingredients, setIngredients] = useState({ const ingredients = useSelector(state => state.ingredients.basket)
salad: 0, const totalPrice = useSelector(state => state.ingredients.totalPrice)
bacon: 0,
cheese: 0,
meat: 0
})
const [totalPrice, setTotalPrice] = useState(200)
const [purchasable, setPurchasable] = useState(false) const [purchasable, setPurchasable] = useState(false)
const [purchasing, setPurchasing] = useState(false) const [purchasing, setPurchasing] = useState(false)
const addIngredientHandler = (type) => { const addIngredientHandler = (type) => {
const oldCount = ingredients[type] dispatch(addIngredient(type))
const updateCount = oldCount + 1
const updatedIngredients = {...ingredients}
updatedIngredients[type] = updateCount
const priceAddition = INGREDIENT_PRICES[type]
const newPrice = totalPrice + priceAddition
setIngredients(updatedIngredients)
setTotalPrice(newPrice)
updatePurchaseState(updatedIngredients)
} }
const removeIngredientHandler = (type) => { const removeIngredientHandler = (type) => {
const oldCount = ingredients[type] dispatch(removeIngredient(type))
if (oldCount <= 0) return
const updateCount = oldCount - 1
const updatedIngredients = {...ingredients}
updatedIngredients[type] = updateCount
const priceAddition = INGREDIENT_PRICES[type]
const newPrice = totalPrice - priceAddition
setIngredients(updatedIngredients)
setTotalPrice(newPrice)
updatePurchaseState(updatedIngredients)
} }
const disabledInfo = {...ingredients} const disabledInfo = {...ingredients}
...@@ -57,7 +35,7 @@ const BurgerBuilder = () => { ...@@ -57,7 +35,7 @@ const BurgerBuilder = () => {
disabledInfo[key] = disabledInfo[key] <= 0 disabledInfo[key] = disabledInfo[key] <= 0
} }
const updatePurchaseState = (ingredients) => { const updatePurchaseState = () => {
const sum = Object.keys(ingredients) const sum = Object.keys(ingredients)
.map(igKey => ingredients[igKey]) .map(igKey => ingredients[igKey])
.reduce((sum, el) => sum + el, 0) .reduce((sum, el) => sum + el, 0)
...@@ -65,6 +43,10 @@ const BurgerBuilder = () => { ...@@ -65,6 +43,10 @@ const BurgerBuilder = () => {
setPurchasable(sum > 0) setPurchasable(sum > 0)
} }
useEffect(() => {
updatePurchaseState()
}, [ingredients])
const purchaseHandler = () => { const purchaseHandler = () => {
setPurchasing(true) setPurchasing(true)
} }
...@@ -73,9 +55,7 @@ const BurgerBuilder = () => { ...@@ -73,9 +55,7 @@ const BurgerBuilder = () => {
} }
const purchaseContinueHandler = () => { const purchaseContinueHandler = () => {
const params = new createSearchParams(ingredients) navigate({pathname: '/checkout'})
//salad=0&meat=0&cheese=0&bacon=0
navigate({pathname: '/checkout' , search: params.toString()})
} }
return ( return (
...@@ -85,15 +65,13 @@ const BurgerBuilder = () => { ...@@ -85,15 +65,13 @@ const BurgerBuilder = () => {
closed={purchaseCancelHandler} closed={purchaseCancelHandler}
> >
<OrderSummary <OrderSummary
ingredients={ingredients}
price={totalPrice} price={totalPrice}
purchaseCancelled={purchaseCancelHandler} purchaseCancelled={purchaseCancelHandler}
purchaseContinued={purchaseContinueHandler} purchaseContinued={purchaseContinueHandler}
/> />
</Modal> </Modal>
<Burger ingredients={ingredients} /> <Burger />
<BuildControls <BuildControls
ingredients={ingredients}
price={totalPrice} price={totalPrice}
ingredientAdded={addIngredientHandler} ingredientAdded={addIngredientHandler}
ingredientRemoved={removeIngredientHandler} ingredientRemoved={removeIngredientHandler}
......
import React from "react"; import React from "react";
import { useRef } from "react"; import { Outlet, useNavigate } from "react-router-dom";
import { NavLink, Outlet, useNavigate, useSearchParams } from "react-router-dom";
import CheckoutSummary from "../../components/Order/CheckoutSummary/CheckoutSummary"; import CheckoutSummary from "../../components/Order/CheckoutSummary/CheckoutSummary";
import { parseSearch } from "../../helper/parseSearch"; import { useSelector } from "react-redux";
import {INGREDIENT_PRICES} from "../../constants/ingredients_prices";
const Checkout = () => { const Checkout = () => {
const navigate = useNavigate() const navigate = useNavigate()
const [searchParams, setSearchParams] = useSearchParams()
const parsed = parseSearch(searchParams) const ingredients = useSelector(state => state.ingredients.basket)
const ingredients = useRef(parsed)
const checkoutCancelledHandler = () => { const checkoutCancelledHandler = () => {
navigate(-1) navigate(-1)
} }
const getTotalPrice = (ingredients) => {
return Object.keys(ingredients).reduce((total, ingName) => {
total += INGREDIENT_PRICES[ingName] * ingredients[ingName]
return total
}, 0)
}
const checkoutContinuedHandler = () => { const checkoutContinuedHandler = () => {
const price = getTotalPrice(ingredients.current) navigate('contact-data')
navigate('contact-data', {state: {ingredients: ingredients.current, price}})
} }
return ( return (
......
...@@ -4,10 +4,13 @@ import Button from "../../../components/UI/Button/Button"; ...@@ -4,10 +4,13 @@ import Button from "../../../components/UI/Button/Button";
import {useLocation, useNavigate} from "react-router-dom"; import {useLocation, useNavigate} from "react-router-dom";
import {apiBurger} from "../../../api/apiBurger"; import {apiBurger} from "../../../api/apiBurger";
import Spinner from "../../../components/UI/Spinner/Spinner"; import Spinner from "../../../components/UI/Spinner/Spinner";
import { useSelector } from 'react-redux';
const ContactData = () => { const ContactData = () => {
const navigate = useNavigate() const navigate = useNavigate()
const location = useLocation() const ingredients = useSelector(state => state.ingredients.basket)
const totalPrice = useSelector(state => state.ingredients.totalPrice)
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [customer, setCustomer] = useState({ const [customer, setCustomer] = useState({
name: '', name: '',
...@@ -19,10 +22,9 @@ const ContactData = () => { ...@@ -19,10 +22,9 @@ const ContactData = () => {
const orderHandler = async (event) => { const orderHandler = async (event) => {
event.preventDefault() event.preventDefault()
setLoading(true) setLoading(true)
const {ingredients, price} = location.state
const order = { const order = {
ingredients: ingredients, ingredients: ingredients,
price: price, price: totalPrice,
customer: {...customer} customer: {...customer}
} }
try { try {
......
...@@ -3,10 +3,14 @@ import ReactDOM from 'react-dom/client'; ...@@ -3,10 +3,14 @@ import ReactDOM from 'react-dom/client';
import './index.css'; import './index.css';
import App from './App'; import App from './App';
import reportWebVitals from './reportWebVitals'; import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import {store} from './store/store'
const root = ReactDOM.createRoot(document.getElementById('root')); const root = ReactDOM.createRoot(document.getElementById('root'));
root.render( root.render(
<Provider store={store}>
<App /> <App />
</Provider>
); );
// If you want to start measuring performance in your app, pass a function // If you want to start measuring performance in your app, pass a function
......
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { apiBurger } from "../api/apiBurger";
const namespace = 'ingredients'
export const getIngredients = createAsyncThunk(
`${namespace}/getIngredients`,
async () => {
return await apiBurger.getIngredients()
}
)
export const ingredientsSlice = createSlice({
name: namespace,
initialState: {
ingredients: [],
basket: {},
totalPrice: 200,
loading: false,
prices: {}
},
reducers: {
addIngredient(state, action) {
try {
state.basket = {
...state.basket,
[action.payload]: state.basket[action.payload] + 1 || 1
}
state.totalPrice += state.prices[action.payload]
} catch (err) {
console.log(err);
}
},
removeIngredient(state, action) {
try {
state.basket = {
...state.basket,
[action.payload]: state.basket[action.payload] > 0
? state.basket[action.payload] - 1
: 0
}
state.totalPrice -= state.prices[action.payload]
} catch (err) {
console.log(err)
}
}
},
extraReducers: builder => {
builder
.addCase(getIngredients.pending, (state) => {
state.loading = true
})
.addCase(getIngredients.rejected, (state) => {
state.loading = false
})
.addCase(getIngredients.fulfilled, (state, action) => {
state.loading = false
state.ingredients = action.payload
action.payload && action.payload.forEach(ing => {
state.basket[ing.name] = 0
})
action.payload && action.payload.forEach(ing => {
state.prices[ing.name] = ing.price
})
})
}
})
export const {
addIngredient,
removeIngredient
} = ingredientsSlice.actions
\ No newline at end of file
import { configureStore } from "@reduxjs/toolkit";
import { ingredientsSlice } from "./ingredients.slice";
export const store = configureStore({
reducer: {
ingredients: ingredientsSlice.reducer
},
devTools: true
})
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment