diff --git a/.gitignore b/.gitignore index b6e47617d..5d725c7f9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ __pycache__/ *.py[cod] *$py.class +#node_modules +node_modules + # C extensions *.so diff --git a/package-lock.json b/package-lock.json index ca840cc2f..c094db7fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "bootstrap": "^5.2.3", "react": "^18.2.0", "react-dom": "^18.2.0", "react-icons": "^4.7.1", @@ -3078,6 +3079,16 @@ } } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -5358,6 +5369,24 @@ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, + "node_modules/bootstrap": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz", + "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", diff --git a/src/App.js b/src/App.js index 5d1f338a3..26f248246 100644 --- a/src/App.js +++ b/src/App.js @@ -1,16 +1,40 @@ import React from 'react'; -import 'bootstrap/dist/css/bootstrap.min.css';  +import 'bootstrap/dist/css/bootstrap.min.css'; + + import { AppProvider } from './context/AppContext'; import CartValue from './components/CartValue'; import ExpenseList from './components/ExpenseList'; import ItemSelected from './components/ItemSelected'; import Location from './components/Location'; -function App() { +const App = () => { return ( -
-
+ +
+

Shopping App

+
+
+ +
+
+ +
+
+

Shopping Cart

+
+
+ +
+
+

Add Items

+
+
+ +
+
+
+
); -} - -export default App; +}; +export default App; \ No newline at end of file diff --git a/src/components/CartValue.js b/src/components/CartValue.js index b1d063540..835d4c313 100644 --- a/src/components/CartValue.js +++ b/src/components/CartValue.js @@ -2,6 +2,16 @@ import React, { useContext } from 'react'; import { AppContext } from '../context/AppContext'; const CartValue = () => { + const { expenses, Location } = useContext(AppContext); + const totalExpenses = expenses.reduce((total, item) => { + return (total += (item.unitprice * item.quantity)); + }, 0); + + return ( +
+ Cart Value: {Location}{totalExpenses} +
+ ); }; -export default CartValue; +export default CartValue; \ No newline at end of file diff --git a/src/components/ExpenseItem.js b/src/components/ExpenseItem.js index 067d1824e..d4441bac1 100644 --- a/src/components/ExpenseItem.js +++ b/src/components/ExpenseItem.js @@ -3,6 +3,29 @@ import { AppContext } from '../context/AppContext'; import { FaTimesCircle } from 'react-icons/fa'; const ExpenseItem = (props) => { + const { dispatch, Location } = useContext(AppContext); + + const handleDeleteItem = () => { + const item = { + name: props.name, + }; + + dispatch({ + type: 'DELETE_ITEM', + payload: item, + }); + }; + + + return ( + + {props.name} + {props.quantity} + {Location}{parseInt(props.unitprice)} + {Location}{parseInt(props.quantity) * parseInt(props.unitprice)} + + + ); }; -export default ExpenseItem; +export default ExpenseItem; \ No newline at end of file diff --git a/src/components/ExpenseList.js b/src/components/ExpenseList.js index 3c7a8af08..79f87e1ad 100644 --- a/src/components/ExpenseList.js +++ b/src/components/ExpenseList.js @@ -1,7 +1,28 @@ import React, { useContext } from 'react'; +import ExpenseItem from './ExpenseItem'; import { AppContext } from '../context/AppContext'; const ExpenseList = () => { + const { expenses } = useContext(AppContext); + + return ( + + + + + + + + + + + + {expenses.map((expense) => ( + + ))} + +
ItemsQuantityUnit PriceItems PriceRemove
+ ); }; -export default ExpenseList; +export default ExpenseList; \ No newline at end of file diff --git a/src/components/ItemSelected.js b/src/components/ItemSelected.js index 14045b414..b501f29e3 100644 --- a/src/components/ItemSelected.js +++ b/src/components/ItemSelected.js @@ -2,6 +2,76 @@ import React, { useContext, useState } from 'react'; import { AppContext } from '../context/AppContext'; const ItemSelected = (props) => { + const { dispatch } = useContext(AppContext); + + const [name, setName] = useState(''); + const [quantity, setQuantity] = useState(''); + const [action, setAction] = useState(''); + + + const submitEvent = () => { + + const item = { + name: name, + quantity: parseInt(quantity), + }; + + if (action === "Reduce") { + dispatch({ + type: 'RED_QUANTITY', + payload: item, + }); + } else { + dispatch({ + type: 'ADD_QUANTITY', + payload: item, + }); + } + }; + + return ( +
+
+ +
+
+ +
+ + +
+ +
+ + + + setQuantity(event.target.value)}> + + + +
+
+ +
+ ); }; -export default ItemSelected; +export default ItemSelected; \ No newline at end of file diff --git a/src/components/Location.js b/src/components/Location.js index c947c4258..98fde1a62 100644 --- a/src/components/Location.js +++ b/src/components/Location.js @@ -2,6 +2,27 @@ import React, { useContext } from 'react'; import { AppContext } from '../context/AppContext'; const Location = () => { + const { dispatch } = useContext(AppContext); + + const changeLocation = (val) => { + dispatch({ + type: 'CHG_LOCATION', + payload: val, + }) + } + + + return ( +
Location { + + } +
+ ); }; export default Location; \ No newline at end of file diff --git a/src/context/AppContext.js b/src/context/AppContext.js index 44355a62b..a4d6fa515 100644 --- a/src/context/AppContext.js +++ b/src/context/AppContext.js @@ -1 +1,100 @@ -import React, { createContext, useReducer } from 'react'; \ No newline at end of file +import React, { createContext, useReducer } from 'react'; + +// 5. The reducer - this is used to update the state, based on the action +export const AppReducer = (state, action) => { + let new_expenses = []; + switch (action.type) { + case 'ADD_QUANTITY': + let updatedqty = false; + state.expenses.map((expense) => { + if (expense.name === action.payload.name) { + expense.quantity = expense.quantity + action.payload.quantity; + updatedqty = true; + } + new_expenses.push(expense); + return true; + }) + state.expenses = new_expenses; + action.type = "DONE"; + return { + ...state, + }; + + case 'RED_QUANTITY': + state.expenses.map((expense) => { + if (expense.name === action.payload.name) { + expense.quantity = expense.quantity - action.payload.quantity; + } + expense.quantity = expense.quantity < 0 ? 0 : expense.quantity; + new_expenses.push(expense); + return true; + }) + state.expenses = new_expenses; + action.type = "DONE"; + return { + ...state, + }; + case 'DELETE_ITEM': + state.expenses.map((expense) => { + if (expense.name === action.payload.name) { + expense.quantity = 0; + } + new_expenses.push(expense); + return true; + }) + state.expenses = new_expenses; + action.type = "DONE"; + return { + ...state, + }; + case 'CHG_LOCATION': + action.type = "DONE"; + state.Location = action.payload; + return { + ...state + } + + default: + return state; + } +}; + +// 1. Sets the initial state when the app loads +const initialState = { + expenses: [ + { id: "Shirt", name: 'Shirt', quantity: 0, unitprice: 500 }, + { id: "Jeans", name: 'Jeans', quantity: 0, unitprice: 300 }, + { id: "Dress", name: 'Dress', quantity: 0, unitprice: 400 }, + { id: "Dinner set", name: 'Dinner set', quantity: 0, unitprice: 600 }, + { id: "Bags", name: 'Bags', quantity: 0, unitprice: 200 }, + ], + Location: '£' +}; + +// 2. Creates the context this is the thing our components import and use to get the state +export const AppContext = createContext(); + +// 3. Provider component - wraps the components we want to give access to the state +// Accepts the children, which are the nested(wrapped) components +export const AppProvider = (props) => { + // 4. Sets up the app state. takes a reducer, and an initial state + const [state, dispatch] = useReducer(AppReducer, initialState); + + const totalExpenses = state.expenses.reduce((total, item) => { + return (total = total + (item.unitprice * item.quantity)); + }, 0); + state.CartValue = totalExpenses; + + return ( + + {props.children} + + ); +}; \ No newline at end of file