diff --git a/package-lock.json b/package-lock.json index ea547b5..c946b54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "react-greengrocers", "version": "0.0.0", "dependencies": { + "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -2809,7 +2810,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3059,7 +3059,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -3121,8 +3120,7 @@ "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-refresh": { "version": "0.14.0", diff --git a/package.json b/package.json index 25ab709..2cc6983 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/src/App.jsx b/src/App.jsx index d28c0b3..41daac3 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -2,6 +2,10 @@ import './styles/reset.css' import './styles/index.css' import initialStoreItems from './store-items' +import { useState, useEffect } from 'react' + +import StoreItem from './components/StoreItem.jsx' +import BasketItem from './components/BasketItem.jsx' /* Here's what a store item should look like @@ -12,26 +16,130 @@ import initialStoreItems from './store-items' } What should a cart item look like? 🤔 + + { + id + name + quantity + } */ -console.log(initialStoreItems) + const sortOptions = ['', 'price desc', 'price asc', 'alphabetically desc', 'alphabetically asc'] + const filterOptions = ['', ...new Set(initialStoreItems.map(item => item.type))]; export default function App() { // Setup state here... + const [storeItems, ] = useState(initialStoreItems) + + const [basketItems, setBasketItems] = useState([]) + + const [basketTotal, setBasketTotal] = useState(0) + + const [filter, setFilter] = useState("") + + const [sort, setSort] = useState("") + + let shownStoreItems = storeItems + + if (filter !== "") { + shownStoreItems = shownStoreItems.filter(i => i.type === filter) + } + + if (sort !== "") { + switch (sort) { + case 'price desc': + shownStoreItems = shownStoreItems.sort((a, b) => b.price - a.price); + break; + case 'price asc': + shownStoreItems = shownStoreItems.sort((a, b) => a.price - b.price); + break; + case 'alphabetically desc': + shownStoreItems = shownStoreItems.sort((a, b) => b.name.localeCompare(a.name)); + break; + case 'alphabetically asc': + shownStoreItems = shownStoreItems.sort((a, b) => a.name.localeCompare(b.name)); + break; + } + } + + const updateBasketTotal = () => { + setBasketTotal(Math.round(basketItems.reduce((acc, item) => acc + item.quantity * getItemPrice(item.id), 0) * 100) / 100) + } + + const getItemPrice = (itemId) => { + const item = storeItems.find((storeItem) => storeItem.id === itemId); + return item ? item.price : 0; + }; + + useEffect(() => { + updateBasketTotal() + }, [basketItems]) + + const addToBasket = (item) => { + if (basketItems.some(i => i.id === item.id)) { + setBasketItems(basketItems.map(i => i.id === item.id ? {...i, quantity: i.quantity + 1} : i)) + } else { + const newBasketItem = {id: item.id, name: item.name, quantity: 1} + setBasketItems([...basketItems, newBasketItem]) + } + } + + const increaseQuantity = (basketItem) => { + setBasketItems(basketItems.map(i => i === basketItem ? {...i, quantity: i.quantity + 1} : i)) + } + + const decreaseQuantity = (basketItem) => { + setBasketItems(prevBasketItems => + prevBasketItems.map(i => + i === basketItem + ? i.quantity > 1 + ? { ...i, quantity: i.quantity - 1 } + : null + : i + ).filter(Boolean) + ) + } return ( <>

Greengrocers

+

+ FILTER BY: + + + SORT BY: + +

Your Cart

@@ -39,7 +147,7 @@ export default function App() {

Total

- £0.00 + {basketTotal}
diff --git a/src/components/BasketItem.jsx b/src/components/BasketItem.jsx new file mode 100644 index 0000000..d159811 --- /dev/null +++ b/src/components/BasketItem.jsx @@ -0,0 +1,26 @@ +import PropTypes from 'prop-types' + +function BasketItem({basketItem, increaseQuantity, decreaseQuantity}) { + + return ( +
  • + beetroot +

    {basketItem.name}

    + + {basketItem.quantity} + +
  • + ) +} + +BasketItem.propTypes = { + basketItem: PropTypes.object, + increaseQuantity: PropTypes.func, + decreaseQuantity: PropTypes.func, +} + +export default BasketItem \ No newline at end of file diff --git a/src/components/StoreItem.jsx b/src/components/StoreItem.jsx new file mode 100644 index 0000000..114aea2 --- /dev/null +++ b/src/components/StoreItem.jsx @@ -0,0 +1,20 @@ +import PropTypes from 'prop-types' + +function StoreItem({storeItem, addToBasket}) { + + return ( +
  • +
    + {storeItem.name} +
    + +
  • + ) +} + +StoreItem.propTypes = { + storeItem: PropTypes.object, + addToBasket: PropTypes.func, +} + +export default StoreItem \ No newline at end of file diff --git a/src/store-items.js b/src/store-items.js index 8e8013c..65bea21 100644 --- a/src/store-items.js +++ b/src/store-items.js @@ -3,52 +3,62 @@ const storeItems = [ { id: "001-beetroot", name: "beetroot", - price: 0.35 + price: 0.45, + type: "vegetable" }, { id: "002-carrot", name: "carrot", - price: 0.35 + price: 0.25, + type: "vegetable" }, { id: "003-apple", name: "apple", - price: 0.35 + price: 0.40, + type: "fruit" }, { id: "004-apricot", name: "apricot", - price: 0.35 + price: 0.65, + type: "vegetable" }, { id: "005-avocado", name: "avocado", - price: 0.35 + price: 1.35, + type: "vegetable" }, { id: "006-bananas", name: "bananas", - price: 0.35 + price: 0.30, + type: "fruit" }, { id: "007-bell-pepper", name: "bell pepper", - price: 0.35 + price: 0.50, + type: "vegetable" }, { id: "008-berry", name: "berry", - price: 0.35 + price: 0.15, + type: "berry" }, { id: "009-blueberry", name: "blueberry", - price: 0.35 + price: 0.20, + type: "berry" }, { id: "010-eggplant", name: "eggplant", - price: 0.35 + price: 0.75, + type: "vegetable" } ]