diff --git a/component hierarchy.jpg b/component hierarchy.jpg new file mode 100644 index 0000000..6ac5aa6 Binary files /dev/null and b/component hierarchy.jpg differ diff --git a/package-lock.json b/package-lock.json index ea547b5..0460136 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3654,9 +3654,9 @@ } }, "node_modules/vite": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz", - "integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz", + "integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==", "dev": true, "dependencies": { "esbuild": "^0.18.10", diff --git a/src/App.jsx b/src/App.jsx index 03e658b..51895e9 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,20 +1,127 @@ -import './styles/reset.css' -import './styles/index.css' +import "./styles/reset.css"; +import "./styles/index.css"; -import initialStoreItems from './store-items' +import initialStoreItems from "./store-items"; + +import Store from "./components/Store"; +import { useState } from "react"; +import Cart from "./components/Cart"; + +import Filter from "./components/Filter"; +import Sorter from "./components/Sorter"; + +import Detail from "./components/Detail"; export default function App() { + const [storeFruits, setStoreFruits] = useState(initialStoreItems); + const [cartFruits, setCartFruits] = useState([]); + const [detailFruit, setDetailFruit] = useState(null); + + const handleAddToCart = (id) => { + const index = cartFruits.findIndex((fruit) => fruit.id === id); + let updatedCartFruits = []; + let fruit = {}; + if (index === -1) { + fruit = storeFruits.find((fruit) => fruit.id === id); + fruit = { ...fruit, quantity: 1 }; + cartFruits.push(fruit); + } else { + fruit = cartFruits.find((fruit) => fruit.id === id); + fruit.quantity++; + } + updatedCartFruits = cartFruits.map((fruit) => fruit); + setCartFruits(updatedCartFruits); + }; + + const handleQuantityIncrement = (id) => { + const updatedCartFruits = cartFruits.map((fruit) => { + if (fruit.id === id) { + fruit.quantity++; + } + return fruit; + }); + setCartFruits(updatedCartFruits); + }; + + const handleQuantityDecrement = (id) => { + const updatedFruit = cartFruits.find((fruit) => fruit.id === id); + updatedFruit.quantity--; + const updatedCartFruits = cartFruits.filter((fruit) => fruit.quantity > 0); + setCartFruits(updatedCartFruits); + }; + + const calculateTotal = () => { + let total = 0; + for (let i = 0; i < cartFruits.length; i++) { + total += cartFruits[i].quantity * cartFruits[i].price; + } + return total; + }; + + const handleFilter = (event) => { + let filter = event.target.value; + if (filter === "all") { + setStoreFruits(initialStoreItems); + return; + } + let filteredStoreFruits = initialStoreItems.filter( + (fruit) => fruit.type === filter + ); + setStoreFruits(filteredStoreFruits); + }; + + const handleSorter = (event) => { + let sortby = event.target.value; + console.log(sortby); + switch (sortby) { + case "price-asc": + storeFruits.sort((a, b) => a.price - b.price); + break; + case "price-desc": + storeFruits.sort((a, b) => b.price - a.price); + break; + case "alpha": + storeFruits.sort((a, b) => a.name.localeCompare(b.name)); + break; + } + let sortedStoreFruits = storeFruits.map((fruit) => fruit); + setStoreFruits(sortedStoreFruits); + }; + + const handleShowDetails = (fruit) => { + setDetailFruit(fruit); + }; + + const handleDismiss = () => { + setDetailFruit(null); + }; + return ( <>

Greengrocers

+ + + + +
    +

Your Cart

@@ -22,10 +129,11 @@ export default function App() {

Total

- £0.00 + £ {calculateTotal()}
+
Icons made by
- ) + ); } diff --git a/src/components/Cart.jsx b/src/components/Cart.jsx new file mode 100644 index 0000000..f543adb --- /dev/null +++ b/src/components/Cart.jsx @@ -0,0 +1,22 @@ +/* eslint-disable react/prop-types */ +function Cart({fruits, incrementQuantity, decrementQuantity}) { + + return ( + <> + {fruits.map((fruit, index) => ( +
  • + +

    {fruit.name}

    + +

    {fruit.quantity}

    + +
  • + ))} + + ) +} +export default Cart \ No newline at end of file diff --git a/src/components/Detail.jsx b/src/components/Detail.jsx new file mode 100644 index 0000000..b750e9e --- /dev/null +++ b/src/components/Detail.jsx @@ -0,0 +1,32 @@ +/* eslint-disable react/prop-types */ +import '../styles/modal.css' + +function Detail({ fruit, dismiss }) { + + + if (fruit === null) return <> + + return <> +
    +
    + × + + + +
    +

    ID: {fruit.id}

    +

    Name: {fruit.name}

    +

    Price: ${fruit.price}

    +

    Description: {fruit.description}

    +

    Type: {fruit.type}

    +
    + +
    +
    + +} + +export default Detail \ No newline at end of file diff --git a/src/components/Filter.jsx b/src/components/Filter.jsx new file mode 100644 index 0000000..4e91c62 --- /dev/null +++ b/src/components/Filter.jsx @@ -0,0 +1,17 @@ +/* eslint-disable react/prop-types */ + +function Filter({onChangeFilter}) { + return <> +
    +

    Filter by:

    + +
    + +} + +export default Filter \ No newline at end of file diff --git a/src/components/Sorter.jsx b/src/components/Sorter.jsx new file mode 100644 index 0000000..b3930ae --- /dev/null +++ b/src/components/Sorter.jsx @@ -0,0 +1,15 @@ +/* eslint-disable react/prop-types */ + +function Sorter({onChangeSort}) { + return <> +
    +

    Sort by:

    + +
    + +} +export default Sorter \ No newline at end of file diff --git a/src/components/Store.jsx b/src/components/Store.jsx new file mode 100644 index 0000000..5fd7b95 --- /dev/null +++ b/src/components/Store.jsx @@ -0,0 +1,21 @@ +/* eslint-disable react/prop-types */ + +function Store({ fruits, addToCart, showDetails }) { + + return ( + <> + {fruits.map((fruit, index) => ( +
  • + { showDetails(fruit) }} + > + +
  • + ))} + + ) + } + + export default Store \ No newline at end of file diff --git a/src/styles/modal.css b/src/styles/modal.css new file mode 100644 index 0000000..4d2b6be --- /dev/null +++ b/src/styles/modal.css @@ -0,0 +1,38 @@ +.modal { + display: block; + position: fixed; + /* Stay in place */ + z-index: 1; + /* Sit on top */ + left: 25%; + top: 50%; + width: 50%; + height: fit-content; + overflow: auto; +} + +/* Modal Content/Box */ +.modal-content { + background-color: #fefefe; + margin: 1% auto; + /* 1% from the top and centered */ + padding: 20px; + border: 1px solid #888; + width: 100%; + /* Could be more or less, depending on screen size */ +} + +/* The Close Button */ +.close { + color: #aaa; + float: right; + font-size: 28px; + font-weight: bold; +} + +.close:hover, +.close:focus { + color: black; + text-decoration: none; + cursor: pointer; +} \ No newline at end of file