diff --git a/backend/sql/create_tables.sql b/backend/sql/create_tables.sql index f22296d8..89970a36 100644 --- a/backend/sql/create_tables.sql +++ b/backend/sql/create_tables.sql @@ -159,10 +159,21 @@ CREATE MATERIALIZED VIEW stock AS SELECT item.name - ,item.category - ,sum(trans_items.quantity) as quantity + ,item.category + ,sum( + CASE + WHEN transaction.trans_type = 'donation' THEN trans_items.quantity + ELSE trans_items.quantity * -1 + END + ) + as quantity FROM trans_items JOIN item ON trans_items.item_id = item.item_id -GROUP BY item.name, item.category +JOIN transaction + ON trans_items.trans_id = transaction.trans_id +GROUP BY item.name, item.category WITH DATA; + +--Adding extention for fuzzy searching +CREATE EXTENSION pg_trgm; diff --git a/backend/sql/dummy_data2.sql b/backend/sql/dummy_data2.sql index 22e6b52c..222ccf9b 100644 --- a/backend/sql/dummy_data2.sql +++ b/backend/sql/dummy_data2.sql @@ -37,7 +37,10 @@ WHERE email = 'johndoe@example.com'; INSERT INTO Transaction (person_id, date, trans_type, site) VALUES ((SELECT MAX(person_id) FROM Person), NOW(), 'donation', 1), -- Add old transaction with goods that have been expiring. - ((SELECT MIN(person_id) FROM Person), NOW() - INTERVAL '3 weeks', 'donation', 1); + ((SELECT MIN(person_id) FROM Person), NOW() - INTERVAL '3 weeks', 'donation', 1), + -- Other Transaction types + ((SELECT MIN(person_id) FROM Person), NOW(), 'purchase', 1), + ((SELECT MAX(person_id) FROM Person), NOW(), 'throw out', 1); -- Insert test data for Trans_items INSERT INTO Trans_items (trans_id, item_id, quantity, expiration) @@ -45,11 +48,15 @@ VALUES ((SELECT MIN(trans_id) FROM Transaction), 4, 5, NOW() + INTERVAL '1 week' ((SELECT MIN(trans_id) FROM Transaction), 3, 10, NOW() + INTERVAL '2 weeks'), -- Donate goods that expired 3 weeks ago, 1 week ago, and 1 week from now. -- Old apples - ((SELECT MAX(trans_id) FROM Transaction), 1, 7, NOW() - INTERVAL '3 weeks'), + ((SELECT MAX(trans_id)-2 FROM Transaction), 1, 7, NOW() - INTERVAL '3 weeks'), -- Old oranges - ((SELECT MAX(trans_id) FROM Transaction), 2, 7, NOW() - INTERVAL '1 week'), + ((SELECT MAX(trans_id)-2 FROM Transaction), 2, 7, NOW() - INTERVAL '1 week'), -- Nearly old Milk - ((SELECT MAX(trans_id) FROM Transaction), 3, 7, NOW() + INTERVAL '1 day'); + ((SELECT MAX(trans_id)-2 FROM Transaction), 3, 7, NOW() + INTERVAL '1 day'), + -- purchase trans item + ((SELECT MAX(trans_id)-1 FROM Transaction), 3, 2, null), + -- throw out for trans item + ((SELECT MAX(trans_id) FROM Transaction), 3, 1, null); -- Insert test data for Shelf_contents INSERT INTO Shelf_contents (trans_item_id, shelf_id, store_date, quantity) diff --git a/backend/src/app.js b/backend/src/app.js index 306397a3..d92b8493 100644 --- a/backend/src/app.js +++ b/backend/src/app.js @@ -101,6 +101,7 @@ app.get('/wastemanagement', summaryRouter); app.get('/soontoexpire', summaryRouter); app.get('/stock', stockRouter); +app.get('/available', stockRouter); app.get('/checkout_success', function(req, res, next) { res.render('checkout_success', { title: 'Success' }); diff --git a/backend/src/routes/stock.ts b/backend/src/routes/stock.ts index dd7d0891..7ee9c3dd 100644 --- a/backend/src/routes/stock.ts +++ b/backend/src/routes/stock.ts @@ -14,13 +14,50 @@ router.get('/stock', ensureAuthenticated, function (req: any, res: any) { const errors = []; res.post('Unauthenticated'); } else { - stock.findAll().then((cur_stock: typeof stock[]) => { + if(req.query.item !== undefined) { + const filter = ` word_similarity('${req.query.item}', name) > 0.5 ORDER BY name <->> '${req.query.item}'`; + stock.findAll({ + where: Sequelize.literal(filter), + }).then((cur_stock: typeof stock[]) => { + if (cur_stock == null) { + res.send("No Items Found"); + } else { + res.json(cur_stock); + } + }); + } else { + stock.findAll().then((cur_stock: typeof stock[]) => { if (cur_stock == null) { console.log("THIS IS ERROR " + cur_stock); } else { - res.json(JSON.stringify(cur_stock)); + res.json(cur_stock); } - }); + }); + } + } +}); + +router.get('/available', ensureAuthenticated, function (req: any, res: any) { + if (!req.isAuthenticated()) { + const errors = []; + res.post('Unauthenticated'); + } else { + console.log(req.query); + if(req.query.item !== undefined) { + stock.findOne({ + where: { + name: req.query.item + }, + }).then((cur_stock: typeof stock[]) => { + if (cur_stock == null) { + res.send("No Items Found"); + } else { + res.send(cur_stock); + } + }); + } else { + res.send("bad request"); + } } }); diff --git a/frontend/src/Components/Stock/StockChart.tsx b/frontend/src/Components/Stock/StockChart.tsx new file mode 100644 index 00000000..3aea32be --- /dev/null +++ b/frontend/src/Components/Stock/StockChart.tsx @@ -0,0 +1,122 @@ +import React, { useState } from "react"; +import { Table, TableBody, TableHead, TableRow, TableContainer, Paper, TableCell, Grid, ToggleButton, ToggleButtonGroup, } from "@mui/material"; +import { Box } from "@mui/system"; +// import StockBarChart from "./StockChart" + +type StockProps = { + sortedStockList: { + name: string; + category: string; + quantity: number; + }[]; +}; + + +const Stock = ({ sortedStockList }: StockProps) => { + const [alignment, setAlignment] = useState("table"); //Used for the purpose of MUI toggle button + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [activeComponent, setActiveComponent] = useState("tables"); + const [tableVisibility, setTableVisibility] = useState(true); //based on boolean data visibility changes, Tables have default visibility + // const [chartVisibility, setChartVisibility] = useState(false); //based on boolean data visibility changes,Chart is initially not visible + + const handleAlignmentChange = ( + event: React.MouseEvent, + newAlignment: string + ) => { + setAlignment(newAlignment); + }; + + /** + * Sets tables as active component and makes the tables visible + */ + function handleTablesClick() { + setActiveComponent("tables"); + setTableVisibility(true); + // setChartVisibility(false); + } + /** + * Sets tables as active component and makes the chart visible + */ + function handleChartClick() { + setActiveComponent("chart"); + setTableVisibility(false); + // setChartVisibility(true); + } + return ( +
+ + + Table + + + Chart + + + {tableVisibility && ( // if tableVisibility state is set true, the following tables will be visible. + + Stock +
+ {/* Total Checkout: {purchaseTotal} | Total Quantity Checked Out:{" "} + {purchaseQuantityTotal} */} + {/*Purchase Table*/} + + + + + + # + + Name + + + category + + + Quantity + + + + + {sortedStockList.map((row, index) => ( + + {index + 1} + {row.name} + {row.category} + {row.quantity} + + ))} + +
+
+
+
+ )} +
+ ); +} + +export default Stock; \ No newline at end of file diff --git a/frontend/src/pages/stock.tsx b/frontend/src/pages/stock.tsx index bd408c23..0bec910b 100644 --- a/frontend/src/pages/stock.tsx +++ b/frontend/src/pages/stock.tsx @@ -1,14 +1,49 @@ -import React from "react"; -// import { useAppSelector,useAppDispatch } from '../hooks' -// import { login } from '../redux-features/user'; +import React, { useEffect, useState } from "react"; +import StockChart from "../Components/Stock/StockChart"; +import axiosInstance from "../util/axiosInstance"; +type cur_stock = { + name: string; + category: string; + quantity: number; +}; const Stock = () => { + const [stockList, setStockList] = useState([]); + const [sortedStockList, setSortedStockList] = useState([]); + // const [sortedExpiredFeedList, setSortedExpiredFeedList] = useState( + // [] + // ); + useEffect(() => { + axiosInstance.get("stock").then((res: any) => { + setStockList(res); + }); + }, []); + + useEffect(() => { + if (stockList.length > 0) { + setSortedStockList( + stockList.sort() + ); + + // sort feeds by latest date to oldest future expiring date + // setSortedExpiredFeedList( + // feedList.sort((a, b) => { + // return ( + // new Date(b.expiry_date).getTime() - + // new Date(a.expiry_date).getTime() + // ); + // }) + // ); + } + }, [stockList]); return ( -
- stock -
); + + ); }; export default Stock;