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

finished lesson 68

parent d0dadb30
This diff is collapsed.
......@@ -3,12 +3,14 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@reduxjs/toolkit": "^1.9.1",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.2.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.2.1",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-redux": "^8.0.5",
"react-router-dom": "^6.4.4",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
......
......@@ -4,8 +4,15 @@ import Checkout from "./containers/Checkout/Checkout";
import ContactData from "./containers/Checkout/ContactData/ContactData";
import Layout from "./components/Layout/Layout";
import Orders from "./containers/Orders/Orders";
import { getIngredients } from "./store/ingredients.slice";
import { useDispatch } from "react-redux";
import { useEffect } from "react";
function App() {
const dispatch = useDispatch()
useEffect(() => {
dispatch(getIngredients())
}, [])
return (
<BrowserRouter>
<Routes>
......
......@@ -16,6 +16,14 @@ class ApiBurger {
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()
\ No newline at end of file
import React from 'react';
import './BuildControls.css';
import BuildControl from './BuildControl/BuildControl'
import { useSelector } from 'react-redux';
const BuildControls = props => {
const ingredients = useSelector(state => state.ingredients.basket)
return (
<div className='BuildControls'>
<p>Current Price: <strong>{props.price} KZT</strong></p>
{Object.keys(props.ingredients).map(ingType => {
{Object.keys(ingredients).map(ingType => {
return <BuildControl
key={ingType}
type={ingType}
......
import React from 'react';
import { useSelector } from 'react-redux';
import './Burger.css';
import Ingredient from './Ingredient/Ingredient'
// const Burger = (props) => {
//const ingredientKeys = Object.keys(props.ingredients) // ['salad', 'bacon'...]
// props = {ingredients} {ingredients}
const Burger = ({ingredients}) => {
const Burger = () => {
const ingredients = useSelector(state => state.ingredients.basket)
const ingredientKeys = Object.keys(ingredients) // ['salad', 'bacon'...]
let ingList = []
ingredientKeys.forEach(igKey => {
......
......@@ -110,3 +110,10 @@
background: linear-gradient(#bf3813, #c45e38);
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}) => {
<div className='Seeds2' />
</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:
return null
return <div className={type} />
}
}
......
import React from "react";
import { useSelector } from "react-redux";
import Button from "../../UI/Button/Button";
import './OrderSummary.css'
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 => {
return (
<li key={igKey}>
<span style={{textTransform: 'capitalize'}}>
{igKey}
</span> : {props.ingredients[igKey]}
</span> : {ingredients[igKey]}
</li>
)
})
......@@ -22,7 +25,7 @@ const OrderSummary = (props) => {
<ul>
{ingredientSummary}
</ul>
<p><strong>Total price: </strong>{props.price} KZT</p>
<p><strong>Total price: </strong>{totalPrice} KZT</p>
<p>Continue to checkout</p>
<Button btnType={'Danger'} clicked={props.purchaseCancelled}>CANCEL</Button>
<Button btnType={'Success'} clicked={props.purchaseContinued}>CONTINUE</Button>
......
import React from 'react';
import React, { useEffect } 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 Burger from '../../components/Burger/Burger'
import OrderSummary from '../../components/Burger/OrderSummary/OrderSummary';
import Modal from '../../components/UI/Modal/Modal';
import {INGREDIENT_PRICES} from "../../constants/ingredients_prices";
import { addIngredient, removeIngredient } from '../../store/ingredients.slice';
const BurgerBuilder = () => {
const dispatch = useDispatch()
const navigate = useNavigate()
const [ingredients, setIngredients] = useState({
salad: 0,
bacon: 0,
cheese: 0,
meat: 0
})
const [totalPrice, setTotalPrice] = useState(200)
const ingredients = useSelector(state => state.ingredients.basket)
const totalPrice = useSelector(state => state.ingredients.totalPrice)
const [purchasable, setPurchasable] = useState(false)
const [purchasing, setPurchasing] = useState(false)
const addIngredientHandler = (type) => {
const oldCount = ingredients[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)
dispatch(addIngredient(type))
}
const removeIngredientHandler = (type) => {
const oldCount = ingredients[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)
dispatch(removeIngredient(type))
}
const disabledInfo = {...ingredients}
......@@ -57,7 +35,7 @@ const BurgerBuilder = () => {
disabledInfo[key] = disabledInfo[key] <= 0
}
const updatePurchaseState = (ingredients) => {
const updatePurchaseState = () => {
const sum = Object.keys(ingredients)
.map(igKey => ingredients[igKey])
.reduce((sum, el) => sum + el, 0)
......@@ -65,6 +43,10 @@ const BurgerBuilder = () => {
setPurchasable(sum > 0)
}
useEffect(() => {
updatePurchaseState()
}, [ingredients])
const purchaseHandler = () => {
setPurchasing(true)
}
......@@ -73,9 +55,7 @@ const BurgerBuilder = () => {
}
const purchaseContinueHandler = () => {
const params = new createSearchParams(ingredients)
//salad=0&meat=0&cheese=0&bacon=0
navigate({pathname: '/checkout' , search: params.toString()})
navigate({pathname: '/checkout'})
}
return (
......@@ -85,15 +65,13 @@ const BurgerBuilder = () => {
closed={purchaseCancelHandler}
>
<OrderSummary
ingredients={ingredients}
price={totalPrice}
purchaseCancelled={purchaseCancelHandler}
purchaseContinued={purchaseContinueHandler}
/>
</Modal>
<Burger ingredients={ingredients} />
<Burger />
<BuildControls
ingredients={ingredients}
price={totalPrice}
ingredientAdded={addIngredientHandler}
ingredientRemoved={removeIngredientHandler}
......
import React from "react";
import { useRef } from "react";
import { NavLink, Outlet, useNavigate, useSearchParams } from "react-router-dom";
import { Outlet, useNavigate } from "react-router-dom";
import CheckoutSummary from "../../components/Order/CheckoutSummary/CheckoutSummary";
import { parseSearch } from "../../helper/parseSearch";
import {INGREDIENT_PRICES} from "../../constants/ingredients_prices";
import { useSelector } from "react-redux";
const Checkout = () => {
const navigate = useNavigate()
const [searchParams, setSearchParams] = useSearchParams()
const parsed = parseSearch(searchParams)
const ingredients = useRef(parsed)
const ingredients = useSelector(state => state.ingredients.basket)
const checkoutCancelledHandler = () => {
navigate(-1)
}
const getTotalPrice = (ingredients) => {
return Object.keys(ingredients).reduce((total, ingName) => {
total += INGREDIENT_PRICES[ingName] * ingredients[ingName]
return total
}, 0)
}
const checkoutContinuedHandler = () => {
const price = getTotalPrice(ingredients.current)
navigate('contact-data', {state: {ingredients: ingredients.current, price}})
navigate('contact-data')
}
return (
......
......@@ -4,10 +4,13 @@ import Button from "../../../components/UI/Button/Button";
import {useLocation, useNavigate} from "react-router-dom";
import {apiBurger} from "../../../api/apiBurger";
import Spinner from "../../../components/UI/Spinner/Spinner";
import { useSelector } from 'react-redux';
const ContactData = () => {
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 [customer, setCustomer] = useState({
name: '',
......@@ -19,10 +22,9 @@ const ContactData = () => {
const orderHandler = async (event) => {
event.preventDefault()
setLoading(true)
const {ingredients, price} = location.state
const order = {
ingredients: ingredients,
price: price,
price: totalPrice,
customer: {...customer}
}
try {
......
......@@ -3,10 +3,14 @@ import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import {store} from './store/store'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<App />
<Provider store={store}>
<App />
</Provider>
);
// 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