From d141383db74feb6a0b8e5ac2df541b0c2e3c2de1 Mon Sep 17 00:00:00 2001 From: Akansha-star Date: Sat, 12 Jun 2021 20:31:20 +0200 Subject: [PATCH] Code to add checkout functionality. Added the ability to update the cart items quantity. Linked the "Checkout" button to the checkout page. Created a form in the Checkout page. The "Place order" button has been linked to the Order completed page. Order complete page shows user info and order info. Cart clear implemented on Order complete. --- src/App.js | 12 +- src/Checkout/Checkout.js | 248 ++++++++++++++++++++++++- src/OrderCompleted/OrderCompleted.js | 59 +++++- src/OrderCompleted/components/Card.js | 40 ++++ src/OrderCompleted/components/Hr.js | 6 + src/OrderCompleted/components/Total.js | 57 ++++++ src/OrderCompleted/constants.js | 1 + src/ShoppingCart/ShoppingCart.js | 29 ++- src/ShoppingCart/components/Card.js | 14 +- src/ShoppingCart/components/Total.js | 41 ++-- src/common/PokemonCard/PokemonCard.jsx | 10 +- src/common/pokemonStorage.js | 70 ++++++- 12 files changed, 544 insertions(+), 43 deletions(-) create mode 100644 src/OrderCompleted/components/Card.js create mode 100644 src/OrderCompleted/components/Hr.js create mode 100644 src/OrderCompleted/components/Total.js create mode 100644 src/OrderCompleted/constants.js diff --git a/src/App.js b/src/App.js index 1aa8b96..65078a5 100644 --- a/src/App.js +++ b/src/App.js @@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react"; import styled from "styled-components"; import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom"; import { Overview } from "./Overview"; -import { Checkout } from "./Checkout/Checkout"; +import { CheckOut } from "./CheckOut/CheckOut"; import { Details } from "./Details"; import { OrderCompleted } from "./OrderCompleted/OrderCompleted"; import { ShoppingCart } from "./ShoppingCart/ShoppingCart"; @@ -11,7 +11,6 @@ const Title = styled.h1` font-size: 1.5em; text-align: center; color: palevioletred; - border-bottom: 1px solid #d0d1d3; `; const NavBar = styled.nav` @@ -82,11 +81,14 @@ const App = () => {
  • Order Completed
  • +
  • + Shopping Cart +
  • - Pokemon ecommerce + Pokemon E-Commerce @@ -99,7 +101,7 @@ const App = () => { - + diff --git a/src/Checkout/Checkout.js b/src/Checkout/Checkout.js index baceed3..f4c1770 100644 --- a/src/Checkout/Checkout.js +++ b/src/Checkout/Checkout.js @@ -1,3 +1,247 @@ -export function Checkout() { - return
    Checkout
    ; +import { useState } from "react"; +import { useHistory } from "react-router-dom"; +import styled from "styled-components"; +import { addOrderDetails } from "../common/pokemonStorage"; + +const SubmitButton = styled.button` + grid-column-start: first; + grid-column-end: second; + grid-row-start: r6; + grid-row-end: r6; + font-family: inherit; + font-size: 100%; + line-height: 1.15; + font-weight: 700; + margin: 14px 14px 14px 14px; + background-color: black; + color: white; + outline: none; + display: inline-flex; + justify-content: center; + border: 0; + cursor: pointer; + padding: 24px 0; + + align-self: end; + &:hover { + opacity: 0.8; + } +`; +const LayoutStyle = styled.div` + display: grid; + grid-template-columns: [first] 50% [second] 50%; + grid-template-rows: [r1] 65px [r2] 65px [r3] 65px [r4] 65px [r5] 65px [r6] 80px; + max-width: 800px; + margin: auto; +`; + +const PanelStyle1 = styled.div` + grid-column-start: first; + grid-column-end: second; + grid-row-start: r1; + grid-row-end: r1; + margin-right: 12px; + margin-left: 12px; + margin-top: 0; + margin-bottom: 0; + background-color: white; +`; + +const PanelStyle2 = styled.div` + grid-column-start: first; + grid-column-end: first; + grid-row-start: r2; + grid-row-end: r2; + margin-right: 12px; + margin-left: 12px; + margin-top: 0; + margin-bottom: 0; + background-color: white; +`; + +const PanelStyle3 = styled.div` + grid-column-start: second; + grid-column-end: second; + grid-row-start: r2; + grid-row-end: r2; + margin-right: 12px; + margin-left: 12px; + margin-top: 0; + margin-bottom: 0; + background-color: white; +`; + +const PanelStyle4 = styled.div` + grid-column-start: first; + grid-column-end: second; + grid-row-start: r3; + grid-row-end: r3; + margin-right: 12px; + margin-left: 12px; + margin-top: 0; + margin-bottom: 0; + background-color: white; +`; + +const PanelStyle5 = styled.div` + grid-column-start: first; + grid-column-end: second; + grid-row-start: r4; + grid-row-end: r4; + margin-right: 12px; + margin-left: 12px; + margin-top: 0; + margin-bottom: 0; + background-color: white; +`; + +const PanelStyle6 = styled.div` + grid-column-start: first; + grid-column-end: second; + grid-row-start: r5; + grid-row-end: r5; + margin-right: 12px; + margin-left: 12px; + margin-top: 0; + margin-bottom: 0; + background-color: white; +`; + +const Input = styled.input` + width: 100%; + font-family: "Source Sans Pro", sans-serif; + padding: 8px 8px; + margin: 5px auto 5px auto; + display: block; + text-align: center; + font-size: 18px; + color: black; + font-weight: 300; +`; + +export function CheckOut() { + const [contact, setContact] = useState({ + fName: "", + lName: "", + email: "", + address: "", + creditcard: "", + }); + const history = useHistory(); + const handleSubmit = () => { + addOrderDetails({ + fName: contact.fName, + lName: contact.lName, + email: contact.email, + address: contact.address, + creditcard: contact.creditcard, + }); + history.push(`/order-completed`); + }; + + function handleChange(event) { + const { name, value } = event.target; + + setContact((prevValue) => { + if (name === "fName") { + return { + fName: value, + lName: prevValue.lName, + email: prevValue.email, + address: prevValue.address, + creditcard: prevValue.creditcard, + }; + } + if (name === "lName") { + return { + fName: prevValue.fName, + lName: value, + email: prevValue.email, + address: prevValue.address, + creditcard: prevValue.creditcard, + }; + } + if (name === "email") { + return { + fName: prevValue.fName, + lName: prevValue.lName, + email: value, + address: prevValue.address, + creditcard: prevValue.creditcard, + }; + } + if (name === "address") { + return { + fName: prevValue.fName, + lName: prevValue.lName, + email: prevValue.email, + address: value, + creditcard: prevValue.creditcard, + }; + } + if (name === "creditcard") { + return { + fName: prevValue.fName, + lName: prevValue.lName, + email: prevValue.email, + address: prevValue.address, + creditcard: value, + }; + } + return ""; + }); + } + + return ( + + +

    + Hello {contact.fName} {contact.lName} +

    +

    {contact.email}

    +
    + + + + + + + + + + + + + + + + + Submit +
    + ); } diff --git a/src/OrderCompleted/OrderCompleted.js b/src/OrderCompleted/OrderCompleted.js index ecb8cf3..d90d48f 100644 --- a/src/OrderCompleted/OrderCompleted.js +++ b/src/OrderCompleted/OrderCompleted.js @@ -1,3 +1,60 @@ +import styled from "styled-components"; +import { GRAY } from "./constants"; +import { Card } from "./components/Card"; +import { Total } from "./components/Total"; +import { + getCartItems, + getOrderDetails, + getCounter, +} from "../common/pokemonStorage"; + +const LayoutStyle = styled.div` + display: grid; + grid-template-columns: 2fr 1fr; + max-width: 1024px; + background-color: ${GRAY}; + margin: auto; +`; + +const PanelStyle = styled.div` + margin: 1rem; + background-color: white; + padding: 1rem; +`; + +const ScrollStyle = styled.div` + overflow-y: scroll; + max-height: 500px; +`; + export function OrderCompleted() { - return
    OrderCompleted
    ; + const cart = getCartItems(); + const order = getOrderDetails(); + console.log("order", order); + const cartCounter = getCounter(); + const total = cart.reduce( + (acc, current) => acc + current.price * current.quantity, + 0 + ); + + return ( +
    +

    + {order.fName} {order.lName} +

    + + +

    Order Details: (Total {cartCounter} articles)

    + + {cart.map((item) => ( + + ))} + +
    + + + +
    +
    + ); } diff --git a/src/OrderCompleted/components/Card.js b/src/OrderCompleted/components/Card.js new file mode 100644 index 0000000..9c33ef1 --- /dev/null +++ b/src/OrderCompleted/components/Card.js @@ -0,0 +1,40 @@ +import styled from "styled-components"; +import { GRAY } from "../constants"; +import { HRStyle } from "./Hr"; + +const CardStyle = styled.div` + display: grid; + grid-template-columns: 1fr 3fr 1fr; + img { + width: 100px; + background-color: ${GRAY}; + } + div { + padding: 1rem; + } +`; + +const CardPriceStyle = styled.div` + padding: 1rem; + display: flex; + flex-direction: column; + align-items: flex-end; +`; + + + +export const Card = ({ name, img, price, quantity }) => ( + <> + + {name} +
    + {name} +
    + +

    {quantity}

    +

    {price * quantity}

    +
    +
    + + +); diff --git a/src/OrderCompleted/components/Hr.js b/src/OrderCompleted/components/Hr.js new file mode 100644 index 0000000..cfb9b66 --- /dev/null +++ b/src/OrderCompleted/components/Hr.js @@ -0,0 +1,6 @@ +import styled from "styled-components"; +import { GRAY } from "../constants"; + +export const HRStyle = styled.hr` + border: 0.5px solid ${GRAY}; +`; diff --git a/src/OrderCompleted/components/Total.js b/src/OrderCompleted/components/Total.js new file mode 100644 index 0000000..1870991 --- /dev/null +++ b/src/OrderCompleted/components/Total.js @@ -0,0 +1,57 @@ +import { useHistory } from "react-router-dom"; +import styled from "styled-components"; +import { HRStyle } from "./Hr"; +import {} from "module"; +import { emptyStorage } from "../../common/pokemonStorage"; + +const TotalItemStyle = styled.div` + display: flex; + justify-content: space-between; +`; + +const CheckoutButton = styled.button` + font-family: inherit; + font-size: 100%; + line-height: 1.15; + font-weight: 700; + margin: 24px 0 32px 0; + width: 100%; + background-color: black; + color: white; + outline: none; + display: inline-flex; + justify-content: center; + border: 0; + cursor: pointer; + padding: 24px 0; + + align-self: end; + &:hover { + opacity: 0.8; + } +`; + +export const Total = ({ total }) => { + const history = useHistory(); + + const redirect = () => { + emptyStorage(); + history.push(`/`); + }; + return ( +
    +

    Total

    + + Subtotal + {(total * 0.8).toFixed(2)} + + + + Total (VAT included) + {total} + + + Thank You +
    + ); +}; diff --git a/src/OrderCompleted/constants.js b/src/OrderCompleted/constants.js new file mode 100644 index 0000000..e412307 --- /dev/null +++ b/src/OrderCompleted/constants.js @@ -0,0 +1 @@ +export const GRAY = `#efeff0`; diff --git a/src/ShoppingCart/ShoppingCart.js b/src/ShoppingCart/ShoppingCart.js index 1f1a7f8..2b0b87c 100644 --- a/src/ShoppingCart/ShoppingCart.js +++ b/src/ShoppingCart/ShoppingCart.js @@ -3,7 +3,12 @@ import { useState } from "react"; import { GRAY } from "./constants"; import { Card } from "./components/Card"; import { Total } from "./components/Total"; -import { getCartItems, removeFromCart } from "../common/pokemonStorage"; +import { + getCartItems, + removeFromCart, + addToCart, + getCounter, +} from "../common/pokemonStorage"; const LayoutStyle = styled.div` display: grid; @@ -26,9 +31,26 @@ const ScrollStyle = styled.div` export function ShoppingCart() { const [cart, setCart] = useState(getCartItems()); + const [cartCounter, setCartCounter] = useState(getCounter()); + const [total, setTotal] = useState( + cart.reduce((acc, current) => acc + current.price * current.quantity, 0) + ); + + const setCartTotal = () => { + setTotal( + cart.reduce((acc, current) => acc + current.price * current.quantity, 0) + ); + }; const handleRemove = (name) => { setCart(removeFromCart(name)); + setCartCounter(getCounter()); + setCartTotal(); + }; + const handleAdd = (item) => { + setCart(addToCart(item)); + setCartCounter(getCounter()); + setCartTotal(); }; if (cart.length === 0) { return ( @@ -43,19 +65,20 @@ export function ShoppingCart() { return ( -

    Place you order ({cart.length} article)

    +

    Place you order ({cartCounter} article)

    {cart.map((item) => ( handleAdd(item)} onRemove={() => handleRemove(item.name)} /> ))}
    - acc + current.price, 0)} /> +
    ); diff --git a/src/ShoppingCart/components/Card.js b/src/ShoppingCart/components/Card.js index 3c476fd..e3702af 100644 --- a/src/ShoppingCart/components/Card.js +++ b/src/ShoppingCart/components/Card.js @@ -21,26 +21,32 @@ const CardPriceStyle = styled.div` align-items: flex-end; `; -const RemoveStyle = styled.a` +const ButtonStyle = styled.a` color: black; + font-size: 25px; cursor: pointer; &:hover { text-decoration: underline; } `; -export const Card = ({ name, img, type, price, quantity, onRemove }) => ( +export const Card = ({ name, img, type, price, quantity, onAdd, onRemove }) => ( <> {name}
    {name}

    {type}

    - Remove + + + + + + - +

    {quantity}

    -

    {price}

    +

    {price * quantity}

    diff --git a/src/ShoppingCart/components/Total.js b/src/ShoppingCart/components/Total.js index 7900a0c..3f7c64f 100644 --- a/src/ShoppingCart/components/Total.js +++ b/src/ShoppingCart/components/Total.js @@ -1,3 +1,4 @@ +import { useHistory } from "react-router-dom"; import styled from "styled-components"; import { HRStyle } from "./Hr"; @@ -21,26 +22,32 @@ const CheckoutButton = styled.button` border: 0; cursor: pointer; padding: 24px 0; - align-self: end; &:hover { opacity: 0.8; } `; -export const Total = ({ total }) => ( -
    -

    Total

    - - Subtotal - {(total * 0.8).toFixed(2)} - - - - Total (VAT included) - {total} - - - Checkout -
    -); +export const Total = ({ total }) => { + const history = useHistory(); + + const redirect = () => { + history.push(`/checkout`); + }; + return ( +
    +

    Total

    + + Subtotal + {(total * 0.8).toFixed(2)} + + + + Total (VAT included) + {total} + + + Checkout +
    + ); +}; diff --git a/src/common/PokemonCard/PokemonCard.jsx b/src/common/PokemonCard/PokemonCard.jsx index 9e12d63..aeac218 100644 --- a/src/common/PokemonCard/PokemonCard.jsx +++ b/src/common/PokemonCard/PokemonCard.jsx @@ -45,15 +45,11 @@ const DetailsButton = styled.button` } `; -export const PokemonCard = ({ - name, image, click, price, -}) => ( - +export const PokemonCard = ({ name, image, click, price }) => ( + {name} {name} - {price || '???'} € + {price || "???"} € Details ); diff --git a/src/common/pokemonStorage.js b/src/common/pokemonStorage.js index 7fb7ea4..2547ac2 100644 --- a/src/common/pokemonStorage.js +++ b/src/common/pokemonStorage.js @@ -1,9 +1,34 @@ const STORAGE_KEY = "cart"; +const COUNTER_KEY = "counter"; +const ORDER_KEY = "order"; + +export const getOrderDetails = () => { + try { + const orderStorage = localStorage.getItem(ORDER_KEY); + return orderStorage ? JSON.parse(orderStorage) : []; + } catch (e) { + return []; + } +}; export const getCartItems = () => { try { const cartStorage = localStorage.getItem(STORAGE_KEY); - return JSON.parse(cartStorage); + return cartStorage ? JSON.parse(cartStorage) : []; + } catch (e) { + return []; + } +}; + +export const getCounter = () => { + try { + const counterStorage = localStorage.getItem(COUNTER_KEY); + if (counterStorage) { + /* eslint-disable */ + const count = parseInt(counterStorage); + return count; + } + return 0; } catch (e) { return []; } @@ -11,13 +36,50 @@ export const getCartItems = () => { export const addToCart = (item) => { const cartStorage = getCartItems(); + const currentCounter = getCounter(); + const foundIndex = cartStorage.findIndex((itm) => itm.name === item.name); + if (foundIndex > -1) { + /* eslint-disable */ + let indexValue = cartStorage[foundIndex]; + item["quantity"] = indexValue["quantity"] + 1; + cartStorage.splice(foundIndex, 1); + } cartStorage.push(item); localStorage.setItem(STORAGE_KEY, JSON.stringify(cartStorage)); + localStorage.setItem(COUNTER_KEY, currentCounter + 1); + return cartStorage; +}; + +export const addOrderDetails = (orderDetail) => { + localStorage.removeItem(ORDER_KEY); + localStorage.setItem(ORDER_KEY, JSON.stringify(orderDetail)); }; export const removeFromCart = (name) => { const cartStorage = getCartItems(); - const filteredCart = cartStorage.filter((c) => c.name !== name); - localStorage.setItem(STORAGE_KEY, JSON.stringify(filteredCart)); - return filteredCart; + const currentCounter = getCounter(); + console.log("currentCounter", currentCounter); + if (currentCounter > 0) { + localStorage.setItem(COUNTER_KEY, currentCounter - 1); + } + const foundIndex = cartStorage.findIndex((itm) => itm.name === name); + if (foundIndex > -1) { + /* eslint-disable */ + if (cartStorage[foundIndex]["quantity"] > 1) { + cartStorage[foundIndex]["quantity"] = + cartStorage[foundIndex]["quantity"] - 1; + localStorage.setItem(STORAGE_KEY, JSON.stringify(cartStorage)); + return cartStorage; + } else { + const filteredCart = cartStorage.filter((c) => c.name !== name); + localStorage.setItem(STORAGE_KEY, JSON.stringify(filteredCart)); + return filteredCart; + } + } +}; + +export const emptyStorage = () => { + localStorage.removeItem(ORDER_KEY); + localStorage.removeItem(COUNTER_KEY); + localStorage.removeItem(STORAGE_KEY); };