Commit dbeea4e9 authored by Pavel Mishakov's avatar Pavel Mishakov

69 done

parent 7b91a650
...@@ -12,7 +12,7 @@ const Burger = () => { ...@@ -12,7 +12,7 @@ const Burger = () => {
const {ingredients} = useAppSelector(state => state.burger) const {ingredients} = useAppSelector(state => state.burger)
Object.keys(ingredients) Object.keys(ingredients)
.forEach((igKey: string) => { .forEach((igKey: string) => {
let amount: number = ingredients[igKey as keyof typeof ingredients] const amount: number = ingredients[igKey as keyof typeof ingredients]
for (let i = 0; i < amount; i++) { for (let i = 0; i < amount; i++) {
ingList.push(<Ingredient key={igKey + i} type={igKey as keyof typeof ingredients} />) ingList.push(<Ingredient key={igKey + i} type={igKey as keyof typeof ingredients} />)
} }
......
import { useEffect, useRef, useState } from "react";
import { TIngredients } from "../../types/TIngredients";
import { CheckoutSummary } from "../CheckoutSummary/CheckoutSummary"; import { CheckoutSummary } from "../CheckoutSummary/CheckoutSummary";
import { useNavigate, useSearchParams } from "react-router-dom"; import { useNavigate, useSearchParams } from "react-router-dom";
import { getTotalPrice } from "../../utils/getTotalPrice";
export function Checkout() { export function Checkout() {
const [searchParams] = useSearchParams() const [searchParams] = useSearchParams()
const ingredients = useRef(Object.fromEntries(searchParams) as unknown as TIngredients)
const [price, setPrice] = useState<number>(0)
const navigate = useNavigate(); const navigate = useNavigate();
const checkoutCancelledHandler = () => { const checkoutCancelledHandler = () => {
navigate('/'); navigate('/');
}; };
useEffect(() => {
const tempPrice = getTotalPrice({...ingredients.current})
setPrice(tempPrice)
}, [ingredients.current])
const checkoutContinuedHandler = () => { const checkoutContinuedHandler = () => {
navigate({ navigate({
pathname: 'contact-data', pathname: 'contact-data',
search: searchParams.toString() search: searchParams.toString()
}, {state: { });
ingredients: ingredients.current,
price
}});
}; };
return <CheckoutSummary return <CheckoutSummary
price={price}
ingredients={ingredients.current}
checkoutContinued={checkoutContinuedHandler} checkoutContinued={checkoutContinuedHandler}
checkoutCancelled={checkoutCancelledHandler} checkoutCancelled={checkoutCancelledHandler}
/>; />;
......
'use client'
import { useAppSelector } from "../../hooks/useAppSelector";
import Burger from "../Burger/Burger"; import Burger from "../Burger/Burger";
import { TIngredients } from "../../types/TIngredients";
import Button from "../UI/Button/Button"; import Button from "../UI/Button/Button";
import styles from "./CheckoutSummary.module.css"; import styles from "./CheckoutSummary.module.css";
import { MouseEventHandler } from "react"; import { MouseEventHandler } from "react";
import { Outlet } from "react-router-dom"; import { Outlet } from "react-router-dom";
interface Props { interface Props {
price: number
ingredients: TIngredients;
checkoutContinued: MouseEventHandler<HTMLButtonElement> checkoutContinued: MouseEventHandler<HTMLButtonElement>
checkoutCancelled: MouseEventHandler<HTMLButtonElement> checkoutCancelled: MouseEventHandler<HTMLButtonElement>
} }
export function CheckoutSummary({ price, ingredients, checkoutContinued, checkoutCancelled }: Props) { export function CheckoutSummary({ checkoutContinued, checkoutCancelled }: Props) {
const {totalPrice} = useAppSelector(state => state.burger)
return ( return (
<div className={styles.CheckoutSummary}> <div className={styles.CheckoutSummary}>
<h1>We hope it tastes well!</h1> <h1>We hope it tastes well!</h1>
<h2>Price: {price}</h2> <h2>Price: {totalPrice}</h2>
<div className={styles.CheckoutSummaryBurger}> <div className={styles.CheckoutSummaryBurger}>
<Burger ingredients={ingredients} /> <Burger />
</div> </div>
<Button btnType="Danger" onClick={checkoutCancelled}> <Button btnType="Danger" onClick={checkoutCancelled}>
CANCEL CANCEL
......
'use client'
import { ChangeEvent, FormEvent, useState } from 'react'; import { ChangeEvent, FormEvent, useState } from 'react';
import Button from '../UI/Button/Button'; import Button from '../UI/Button/Button';
import styles from './ContactData.module.css'; import styles from './ContactData.module.css';
import { axiosOrder } from '../../axios/axiosOrder';
import { TCustomerData } from '../../types/TCustomerData'; import { TCustomerData } from '../../types/TCustomerData';
import { useLocation, useNavigate } from 'react-router'; import { useNavigate } from 'react-router';
import Spinner from '../UI/Spinner/Spinner'; import Spinner from '../UI/Spinner/Spinner';
import { useAppSelector } from '../../hooks/useAppSelector';
import { useAppDispatch } from '../../hooks/useAppDispatch';
import { createOrder } from '../../stores/newStore/ordersSlice';
import { shallowEqual } from 'react-redux';
export function ContactData() { export function ContactData() {
const location = useLocation() const {ingredients, totalPrice} = useAppSelector(state => state.burger)
const {isLoading} = useAppSelector(state => state.orders, shallowEqual)
const dispatch = useAppDispatch()
const navigate = useNavigate() const navigate = useNavigate()
const [isLoading, setIsLoading] = useState<boolean>(false)
const [isSubmited, setIsSubmited] = useState<boolean>(false) const [isSubmited, setIsSubmited] = useState<boolean>(false)
const [customerData, setCustomerData] = useState<TCustomerData>({ const [customerData, setCustomerData] = useState<TCustomerData>({
name: '', name: '',
...@@ -18,6 +23,7 @@ export function ContactData() { ...@@ -18,6 +23,7 @@ export function ContactData() {
postal: '' postal: ''
}) })
const inputHandler = (e: ChangeEvent<HTMLInputElement>) => { const inputHandler = (e: ChangeEvent<HTMLInputElement>) => {
setCustomerData(prevState => ({ ...prevState, [e.target.name]: e.target.value })) setCustomerData(prevState => ({ ...prevState, [e.target.name]: e.target.value }))
} }
...@@ -26,20 +32,16 @@ export function ContactData() { ...@@ -26,20 +32,16 @@ export function ContactData() {
e.preventDefault() e.preventDefault()
if (isSubmited) return if (isSubmited) return
try { try {
setIsLoading(true) const order = {
const { ingredients, price } = location.state
const response = await axiosOrder.post('orders.json', {
customer: { ...customerData }, customer: { ...customerData },
ingredients, ingredients,
price price: totalPrice
}) }
console.log(response.data) dispatch(createOrder(order))
setIsSubmited(true) setIsSubmited(true)
} catch (err) { } catch (err) {
console.log(err) console.log(err)
setIsSubmited(false) setIsSubmited(false)
} finally {
setIsLoading(false)
} }
} }
const goBack = () => { const goBack = () => {
......
import { axiosOrder } from '../../axios/axiosOrder'; 'use client'
import type { TFirebaseData } from "../../types/TFirebaseData";
import { TOrder } from '../../types/TOrder'; import { useEffect } from 'react';
import { useAppSelector } from '../../hooks/useAppSelector';
import OrderItem from '../OrderItem/OrderItem'; import OrderItem from '../OrderItem/OrderItem';
import { useLoaderData } from 'react-router'; import { useAppDispatch } from '../../hooks/useAppDispatch';
import { getOrders } from '../../stores/newStore/ordersSlice';
export const fetchOrders = async () => {
const response = await axiosOrder.get('orders.json');
const data: TFirebaseData<TOrder> = response.data
const fetchedOrders: TOrder[] = Object.keys(data).map(id => {
return { ...data[id], id };
});
return fetchedOrders
}
const Orders = () => { const Orders = () => {
const orders: TOrder[] = useLoaderData() const {orders} = useAppSelector(state => state.orders)
const dispatch = useAppDispatch()
useEffect(() => {
dispatch(getOrders())
}, [dispatch])
return ( return (
<> <>
{orders.map(order => { {orders.map(order => {
......
...@@ -6,9 +6,7 @@ import BurgerBuilder from './components/BurgerBuilder/BurgerBuilder.tsx' ...@@ -6,9 +6,7 @@ import BurgerBuilder from './components/BurgerBuilder/BurgerBuilder.tsx'
import { Checkout } from './components/Checkout/Checkout.tsx' import { Checkout } from './components/Checkout/Checkout.tsx'
import { ContactData } from './components/ContactData/ContactData.tsx' import { ContactData } from './components/ContactData/ContactData.tsx'
import Layout from './components/layouts/Layout/Layout.tsx' import Layout from './components/layouts/Layout/Layout.tsx'
import { fetchOrders } from './components/Orders/Orders.tsx' import { lazy } from 'react'
import { lazy, Suspense } from 'react'
import Spinner from './components/UI/Spinner/Spinner.tsx'
import { Provider } from 'react-redux' import { Provider } from 'react-redux'
import store from './stores/newStore/store.ts' import store from './stores/newStore/store.ts'
const Orders = lazy(() => import('./components/Orders/Orders.tsx')) const Orders = lazy(() => import('./components/Orders/Orders.tsx'))
...@@ -35,11 +33,8 @@ const router = createBrowserRouter([ ...@@ -35,11 +33,8 @@ const router = createBrowserRouter([
{ {
path: '/orders', path: '/orders',
element: ( element: (
<Suspense fallback={<Spinner />}>
<Orders /> <Orders />
</Suspense>
), ),
loader: fetchOrders
} }
] ]
}, },
......
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"
import { axiosOrder } from "../../axios/axiosOrder"
import { TFirebaseData } from "../../types/TFirebaseData"
import { TOrder } from "../../types/TOrder"
import { AxiosRequestConfig, AxiosResponse } from "axios"
type State = {
orders: TOrder[]
isLoading: boolean
error: Error | null,
}
const initialState: State = {
orders: [],
error: null,
isLoading: false
}
const namespace = 'orders'
export const getOrders = createAsyncThunk(
`${namespace}/getOrders`,
async (): Promise<TFirebaseData<TOrder>> => {
const response = await axiosOrder.get('orders.json')
return response.data
}
)
export const createOrder = createAsyncThunk(
`${namespace}/createOrder`,
async (payload: Omit<TOrder, 'id'>) => {
await axiosOrder.post<AxiosRequestConfig, AxiosResponse, Omit<TOrder, 'id'>>('orders.json', payload);
}
)
const orderSlice = createSlice({
name: namespace,
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(getOrders.pending, (state) => {
state.isLoading = true
state.error = null
})
.addCase(getOrders.rejected, (state, action) => {
state.isLoading = false
state.error = action.payload as Error
})
.addCase(getOrders.fulfilled, (state, action) => {
state.isLoading = false
const fetchedOrders: TOrder[] = Object.keys(action.payload).map(id => {
return { ...action.payload[id] as TOrder, id };
});
state.orders = fetchedOrders
state.error = null
})
.addCase(createOrder.pending, (state) => {
state.isLoading = true;
state.error = null;
})
.addCase(createOrder.rejected, (state, action) => {
state.error = action.error as Error;
state.isLoading = false
})
.addCase(createOrder.fulfilled, (state) => {
state.isLoading = false;
})
}
})
export default orderSlice.reducer
\ No newline at end of file
import { configureStore } from "@reduxjs/toolkit"; import { configureStore } from "@reduxjs/toolkit";
import burgerReducer from './burgerSlice' import burgerReducer from './burgerSlice'
import ordersReducer from './ordersSlice'
const store = configureStore({ const store = configureStore({
reducer: { reducer: {
burger: burgerReducer burger: burgerReducer,
orders: ordersReducer
} }
}) })
......
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