diff --git a/package-lock.json b/package-lock.json index c1011c5a..568f8126 100644 --- a/package-lock.json +++ b/package-lock.json @@ -73,6 +73,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz", "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==", "dev": true, + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.2", @@ -1218,6 +1219,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.79.tgz", "integrity": "sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==", "devOptional": true, + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -1267,6 +1269,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1515,6 +1518,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001587", "electron-to-chromium": "^1.4.668", @@ -1982,6 +1986,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -3549,6 +3554,7 @@ "version": "18.3.0", "resolved": "https://registry.npmjs.org/react/-/react-18.3.0.tgz", "integrity": "sha512-RPutkJftSAldDibyrjuku7q11d3oy6wKOyPe5K1HA/HwwrXcEqBdHsLypkC2FFYjP7bPUa6gbzSBhw4sY2JcDg==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -3578,6 +3584,7 @@ "version": "9.1.1", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.1.tgz", "integrity": "sha512-5ynfGDzxxsoV73+4czQM56qF43vsmgJsO22rmAvU5tZT2z5Xow/A2uhhxwXuGTxgdReF3zcp7A80gma2onRs1A==", + "peer": true, "dependencies": { "@types/use-sync-external-store": "^0.0.3", "use-sync-external-store": "^1.0.0" @@ -3612,7 +3619,8 @@ "node_modules/redux": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", - "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==" + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "peer": true }, "node_modules/redux-thunk": { "version": "3.1.0", @@ -4209,6 +4217,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.10.tgz", "integrity": "sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==", "dev": true, + "peer": true, "dependencies": { "esbuild": "^0.20.1", "postcss": "^8.4.38", diff --git a/src/ConferenceEvent.jsx b/src/ConferenceEvent.jsx index 612a4648..092c3f0b 100644 --- a/src/ConferenceEvent.jsx +++ b/src/ConferenceEvent.jsx @@ -3,14 +3,19 @@ import "./ConferenceEvent.css"; import TotalCost from "./TotalCost"; import { useSelector, useDispatch } from "react-redux"; import { incrementQuantity, decrementQuantity } from "./venueSlice"; +import { incrementAvQuantity, decrementAvQuantity } from "./avSlice"; +import { toggleMealSelection } from "./mealsSlice"; + const ConferenceEvent = () => { const [showItems, setShowItems] = useState(false); const [numberOfPeople, setNumberOfPeople] = useState(1); const venueItems = useSelector((state) => state.venue); + const avItems = useSelector((state) => state.av); const dispatch = useDispatch(); - const remainingAuditoriumQuantity = 3 - venueItems.find(item => item.name === "Auditorium Hall (Capacity:200)").quantity; - + const mealsItems = useSelector((state) => state.meals); + const remainingAuditoriumQuantity = 3 - (venueItems.find(item => item.name === "Auditorium Hall (Capacity:200)")?.quantity || 0); + const handleToggleItems = () => { console.log("handleToggleItems called"); setShowItems(!showItems); @@ -18,184 +23,277 @@ const ConferenceEvent = () => { const handleAddToCart = (index) => { if (venueItems[index].name === "Auditorium Hall (Capacity:200)" && venueItems[index].quantity >= 3) { - return; + return; } dispatch(incrementQuantity(index)); - }; - - const handleRemoveFromCart = (index) => { + }; + + const handleRemoveFromCart = (index) => { if (venueItems[index].quantity > 0) { dispatch(decrementQuantity(index)); } - }; + }; + const handleIncrementAvQuantity = (index) => { + dispatch(incrementAvQuantity(index)); }; const handleDecrementAvQuantity = (index) => { + dispatch(decrementAvQuantity(index)); }; const handleMealSelection = (index) => { - + const item = mealsItems[index]; + if (item.selected && item.type === "mealForPeople") { + const newNumberOfPeople = item.selected ? numberOfPeople : 0; + dispatch(toggleMealSelection(index, newNumberOfPeople)); + } else { + dispatch(toggleMealSelection(index)); + } + }; + + const calculateTotalCost = (section) => { + let totalCost = 0; + if (section === "venue") { + venueItems.forEach((item) => { + totalCost += item.cost * item.quantity; + }); + } else if (section === "av") { + avItems.forEach((item) => { + totalCost += item.cost * item.quantity; + }); + } else if (section === "meals") { + mealsItems.forEach((item) => { + if (item.selected) { + totalCost += item.cost * numberOfPeople; + } + }); + } + return totalCost; }; const getItemsFromTotalCost = () => { - const items = []; + const items = []; + venueItems.forEach((item) => { + if (item.quantity > 0) { + items.push({...item, type: "venue" }); + } + }); + avItems.forEach((item) => { + if (item.quantity > 0 && !items.some((i) => i.name === item.name && i.type === "av")) { + items.push({ ...item, type: "av" }); + } + }); + mealsItems.forEach((item) => { + if (item.selected) { + const itemForDisplay = { ...item, type: "meals" }; + if (item.numberOfPeople) { + itemForDisplay.numberOfPeople = numberOfPeople; + } + items.push(itemForDisplay); + } + }); + return items; + }; + + const venueTotalCost = calculateTotalCost("venue"); + const avTotalCost = calculateTotalCost("av"); + const mealsTotalCost = calculateTotalCost("meals"); + + const totalCosts = { + venue: venueTotalCost, + av: avTotalCost, + meals: mealsTotalCost, }; const items = getItemsFromTotalCost(); const ItemsDisplay = ({ items }) => { - + console.log(items); + return ( + <> +
+ {items.length === 0 &&

No items selected

} + + + + + + + + + + + {items.map((item, index) => ( + + + + + + + ))} + +
NameUnit CostQuantitySubtotal
{item.name}${item.cost} + {item.type === "meals" || item.numberOfPeople + ? numberOfPeople + : item.quantity} + + ${item.type === "meals" || item.numberOfPeople + ? item.cost * numberOfPeople + : item.cost * item.quantity} +
+
+ + ); }; - const calculateTotalCost = (section) => { - let totalCost = 0; - if (section === "venue") { - venueItems.forEach((item) => { - totalCost += item.cost * item.quantity; - }); - } - return totalCost; - }; - const venueTotalCost = calculateTotalCost("venue"); const navigateToProducts = (idType) => { - if (idType == '#venue' || idType == '#addons' || idType == '#meals') { - if (showItems) { // Check if showItems is false - setShowItems(!showItems); // Toggle showItems to true only if it's currently false - } + if (idType === '#venue' || idType === '#addons' || idType === '#meals') { + if (showItems) { + setShowItems(!showItems); } } + }; return ( - <> - -
Conference Expense Planner
-
-
- navigateToProducts("#venue")} >Venue - navigateToProducts('#addons')}>Add-ons - navigateToProducts('#meals')}>Meals + <> + +
+ {!showItems ? ( +
+
+
+

Venue Room Selection

+
+
+ {venueItems.map((item, index) => ( +
+
+ {item.name} +
+
{item.name}
+
${item.cost}
+
+ {venueItems[index].name === "Auditorium Hall (Capacity:200)" ? ( + <> + + + {venueItems[index].quantity > 0 ? ` ${venueItems[index].quantity}` : "0"} + + + + ) : ( +
+ + + {venueItems[index].quantity > 0 ? ` ${venueItems[index].quantity}` : "0"} + + +
+ )} +
- + ))}
- -
- {!showItems - ? - ( -
-
-
- -

Venue Room Selection

-
-
- {venueItems.map((item, index) => ( -
-
- {item.name} +
Total Cost: ${venueTotalCost}
-
{item.name}
-
${item.cost}
-
- {venueItems[index].name === "Auditorium Hall (Capacity:200)" ? ( - - <> - - - {venueItems[index].quantity > 0 ? ` ${venueItems[index].quantity}` : "0"} - - - - ) : ( -
- - - {venueItems[index].quantity > 0 ? ` ${venueItems[index].quantity}` : "0"} - - - - -
- )} -
-
- ))} -
-
Total Cost: ${venueTotalCost}
-
- - {/*Necessary Add-ons*/} -
- - -
- -

Add-ons Selection

- -
-
- -
-
Total Cost:
- -
- - {/* Meal Section */} - -
- -
- -

Meals Selection

-
- -
- -
-
- -
-
Total Cost:
- - -
-
- ) : ( -
- } /> -
- ) - } - - +
+
+

Add-ons Selection

+
+
+ {avItems.map((item, index) => ( +
+
+ {item.name} +
+
{item.name}
+
${item.cost}
+
+ + {item.quantity} + +
+
+ ))} +
+
Total Cost: ${avTotalCost}
+
+
+
+

Meals Selection

+
+
+ + setNumberOfPeople(parseInt(e.target.value))} + min="1" + /> +
+
+ {mealsItems.map((item, index) => ( +
+
+ handleMealSelection(index)} + /> + +
+
${item.cost}
+
+ ))} +
+
Total Cost: ${mealsTotalCost}
+
- - + ) : ( +
+ } /> +
+ )} +
+ ); }; diff --git a/src/TotalCost.jsx b/src/TotalCost.jsx index 845abca9..a3a2a756 100644 --- a/src/TotalCost.jsx +++ b/src/TotalCost.jsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react'; import "./TotalCost.css"; const TotalCost = ({ totalCosts, ItemsDisplay }) => { +const total_amount = totalCosts.venue + totalCosts.av + totalCosts.meals; return ( @@ -12,15 +13,14 @@ const TotalCost = ({ totalCosts, ItemsDisplay }) => {

- + ${total_amount}

- -
+
+
-
); }; diff --git a/src/avSlice.js b/src/avSlice.js index cdd79aac..3ab674fe 100644 --- a/src/avSlice.js +++ b/src/avSlice.js @@ -1,17 +1,58 @@ +/* eslint-disable no-undef */ import { createSlice } from "@reduxjs/toolkit"; + + export const avSlice = createSlice({ name: "av", initialState: [ + { + img: "https://pixabay.com/images/download/business-20031_640.jpg", + name: "Projectors", + cost: 200, + quantity: 0, + }, + { + img: "https://pixabay.com/images/download/speakers-4109274_640.jpg", + name: "Speaker", + cost: 35, + quantity: 0, + }, + { + img: "https://pixabay.com/images/download/public-speaking-3926344_640.jpg", + name: "Microphones", + cost: 45, + quantity: 0, + }, + { + img: "https://pixabay.com/images/download/whiteboard-2903269_640.png", + name: "Whiteboards", + cost: 80, + quantity: 0, + }, + { + img: "https://pixabay.com/images/download/signpost-235079_640.jpg", + name: "Signage", + cost: 80, + quantity: 0, + }, ], reducers: { incrementAvQuantity: (state, action) => { + const item = state[action.payload]; + if (item) { + item.quantity++; + } }, decrementAvQuantity: (state, action) => { + const item = statem[action.payload]; + if (item && item.quantity > 0) { + item.quantity--; + } }, }, @@ -20,3 +61,4 @@ export const avSlice = createSlice({ export const { incrementAvQuantity, decrementAvQuantity } = avSlice.actions; export default avSlice.reducer; + diff --git a/src/mealsSlice.js b/src/mealsSlice.js index faf8138a..9e38444e 100644 --- a/src/mealsSlice.js +++ b/src/mealsSlice.js @@ -4,10 +4,15 @@ import { createSlice } from '@reduxjs/toolkit'; export const mealsSlice = createSlice({ name: 'meals', initialState: [ + {name: 'Breakfast', cost: 50, selected: false}, + {name: 'High Tea', cost: 30, selected: false}, + {name: 'Lunch', cost: 65, selected: false}, + {name: 'Dinner', cost: 70, selected: false}, ], reducers: { toggleMealSelection: (state, action) => { + state[action.payload].selected = !state[action.payload].selected; }, }, }); diff --git a/src/store.js b/src/store.js index f05b9f8f..b80f6b6c 100644 --- a/src/store.js +++ b/src/store.js @@ -1,9 +1,13 @@ // store.js import { configureStore } from '@reduxjs/toolkit'; import venueReducer from './venueSlice'; +import avReducer from './avSlice'; +import mealsReducer from './mealsSlice'; export default configureStore({ reducer: { venue: venueReducer, + av: avReducer, + meals: mealsReducer, }, }); diff --git a/src/venueSlice.js b/src/venueSlice.js index 350d353a..506b1260 100644 --- a/src/venueSlice.js +++ b/src/venueSlice.js @@ -4,6 +4,7 @@ import { createSlice } from "@reduxjs/toolkit"; export const venueSlice = createSlice({ name: "venue", initialState: [ + { img: "https://pixabay.com/images/download/chairs-2181916_640.jpg", name: "Conference Room (Capacity:15)",