From 3eb52c78c354a0109d91cb900344bad3a2c2691d Mon Sep 17 00:00:00 2001 From: Justin Walrath Date: Sat, 17 Jun 2023 20:58:38 -0400 Subject: [PATCH] [#7] Stubbed out a bunch of basic mongo calls and configured app to use mongo instead of mocked object. --- client/mongodbnotes.txt | 39 ++++++++++- client/package-lock.json | 68 +++++++++++++++++-- client/package.json | 1 + client/src/base/App.jsx | 2 +- client/src/data/context/BudgetContext.jsx | 14 +++- client/src/views/BudgetPeriodCard.jsx | 12 ++-- server/src/controllers/AccountController.js | 13 ++++ .../src/controllers/BudgetPeriodController.js | 19 ++++++ server/src/controllers/UserController.js | 7 ++ .../{UserManager.js => AccountManager.js} | 0 server/src/managers/BudgetPeriodManager.js | 22 ++++++ server/src/server.js | 22 ++---- server/src/stores/BudgetPeriodStore.js | 23 +++++++ 13 files changed, 208 insertions(+), 34 deletions(-) create mode 100644 server/src/controllers/AccountController.js create mode 100644 server/src/controllers/BudgetPeriodController.js create mode 100644 server/src/controllers/UserController.js rename server/src/managers/{UserManager.js => AccountManager.js} (100%) create mode 100644 server/src/managers/BudgetPeriodManager.js create mode 100644 server/src/stores/BudgetPeriodStore.js diff --git a/client/mongodbnotes.txt b/client/mongodbnotes.txt index ed0929b..1fb2e6d 100644 --- a/client/mongodbnotes.txt +++ b/client/mongodbnotes.txt @@ -7,7 +7,7 @@ db.createCollection("budgetPeriods") users: [{ _id: - username: “maximx1” + username: "jules" }] accounts: [{ @@ -20,5 +20,38 @@ budgets: [{ }] budgetPeriods: [{ - -}] \ No newline at end of file + user: ObjectId("63faac6823cd0682fe8c8ed6"), + startDate: new Date("2023-03-03"), + endDate: new Date("2023-03-10"), + leftover: 23.22, + cashflows: [ + { + account: ObjectId("63fab01a23cd0682fe8c8ed7"), + dueDate: new Date("2023-03-04"), + postingDate: new Date("2023-03-04"), + amount: 22.23, + isIncome: false + } + ], + verified: true, + adhocPurchases: [ + { + postingDate: new Date("2023-03-05"), + amount: 1.99, + description: "", + isIncome: false + } + ] +}] + +$push operator for adding to arrays + +2023-03-07 + +{ + user: ObjectId("63faac6823cd0682fe8c8ed6"), + start: { $lte: "2023-03-07T05:00:00.000Z" }, + end: { $gt: "2023-03-07T05:00:00.000Z" } +} + +http://localhost:3001/jules/period/in/2023-03-01..2023-04-01 \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json index 19ff457..d2804b1 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -11,6 +11,7 @@ "@testing-library/jest-dom": "^5.16.1", "@testing-library/react": "^12.1.2", "@testing-library/user-event": "^13.5.0", + "axios": "^1.3.4", "bootstrap": "^5.1.3", "gridjs": "^5.0.2", "gridjs-react": "^5.0.2", @@ -4424,6 +4425,29 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -7466,9 +7490,9 @@ "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==" }, "node_modules/follow-redirects": { - "version": "1.14.7", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", - "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "funding": [ { "type": "individual", @@ -12938,6 +12962,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -19283,6 +19312,28 @@ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.0.tgz", "integrity": "sha512-btWy2rze3NnxSSxb7LtNhPYYFrRoFBfjiGzmSc/5Hu47wApO2KNXjP/w7Nv2Uz/Fyr/pfEiwOkcXhDxu0jz5FA==" }, + "axios": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, "axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -21546,9 +21597,9 @@ "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==" }, "follow-redirects": { - "version": "1.14.7", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", - "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==" + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" }, "fork-ts-checker-webpack-plugin": { "version": "6.5.0", @@ -25354,6 +25405,11 @@ } } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", diff --git a/client/package.json b/client/package.json index ca84a1f..6ddcdeb 100644 --- a/client/package.json +++ b/client/package.json @@ -6,6 +6,7 @@ "@testing-library/jest-dom": "^5.16.1", "@testing-library/react": "^12.1.2", "@testing-library/user-event": "^13.5.0", + "axios": "^1.3.4", "bootstrap": "^5.1.3", "gridjs": "^5.0.2", "gridjs-react": "^5.0.2", diff --git a/client/src/base/App.jsx b/client/src/base/App.jsx index 5585db6..9a41eaa 100644 --- a/client/src/base/App.jsx +++ b/client/src/base/App.jsx @@ -8,7 +8,7 @@ const AppImpl = () => { const context = useContext(BudgetContext); useEffect(() => { - context.api.loadData(mockedData.budgetPeriods); + context.api.getCurrentPeriod(); }, []); return ( diff --git a/client/src/data/context/BudgetContext.jsx b/client/src/data/context/BudgetContext.jsx index 26afc04..0a94163 100644 --- a/client/src/data/context/BudgetContext.jsx +++ b/client/src/data/context/BudgetContext.jsx @@ -1,4 +1,5 @@ import { useReducer, createContext } from 'react'; +import axios from 'axios'; import BudgetReducer from './BudgetReducer'; const BudgetContext = createContext(); @@ -9,8 +10,17 @@ const BudgetState = (props) => { }, [state, dispatch] = useReducer(BudgetReducer, initialState), api = { - loadData: (data) => { - dispatch({ type: 'budget/populate', data }); + getCurrentPeriod: () => { + axios.get(`http://localhost:3001/jules/period/for/${(new Date()).toISOString()}`, { + headers: { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET', + crossorigin: true + } + }).then(response => { + dispatch({ type: 'budget/populate', data: [{ ...response.data, id: response.data.id }] }); + }); + }, updateBillPaid: (periodId, billId) => { dispatch({ type: 'budget/period/bills/update', data: { periodId, billId }}); diff --git a/client/src/views/BudgetPeriodCard.jsx b/client/src/views/BudgetPeriodCard.jsx index e18bb63..c6c46d3 100644 --- a/client/src/views/BudgetPeriodCard.jsx +++ b/client/src/views/BudgetPeriodCard.jsx @@ -9,10 +9,10 @@ const BudgetPeriodCard = ({periodData, isActive}) => { const cardRef = useRef(), context = useContext(BudgetContext), [bankBalance, setBankBalance] = useState(0), - [projectedLeftover, setProjectedLeftover] = useState(0), - paidBills = periodData.bills.filter((bill) => bill.paid), - unpaidBills = periodData.bills.filter((bill) => !bill.paid), - [incomes, setIncomes] = useState(periodData.income), + [projectedLeftover, setProjectedLeftover] = useState(periodData), + paidBills = periodData.cashflows.filter((f) => !f.isIncome && f.postingDate != null), + unpaidBills = periodData.cashflows.filter((f) => !f.isIncome && f.postingDate == null), + [incomes, setIncomes] = useState(periodData.cashflows.filter((f) => f.isIncome)), [paidIncomes, setPaidIncomes] = useState([]), [unpaidIncomes, setUnpaidIncomes] = useState([]), moveTransItemAction = (row) => { @@ -52,9 +52,9 @@ const BudgetPeriodCard = ({periodData, isActive}) => {
- {periodData.startDate.toDateString().split(' ').slice(1).join(' ')} + {(new Date(periodData.startDate)).toLocaleDateString()} - - { periodData.endDate.toDateString().split(' ').slice(1).join(' ')} + {(new Date(periodData.endDate)).toLocaleDateString()}
Current Balance diff --git a/server/src/controllers/AccountController.js b/server/src/controllers/AccountController.js new file mode 100644 index 0000000..3ff263b --- /dev/null +++ b/server/src/controllers/AccountController.js @@ -0,0 +1,13 @@ +const accountManager = require('../managers/AccountManager'), + accountStore = require('../stores/AccountStore'); + +exports.load = (app) => { + app.get('/accounts', (req, res) => { + accountStore.getAccounts().then(data => res.json({ accounts: data })); + }); + + app.get('/:username/accounts', (req, res) => { + const { username } = req.params; + accountManager.getUserAccounts(username).then(data => res.json(data)); + }); +}; \ No newline at end of file diff --git a/server/src/controllers/BudgetPeriodController.js b/server/src/controllers/BudgetPeriodController.js new file mode 100644 index 0000000..603affd --- /dev/null +++ b/server/src/controllers/BudgetPeriodController.js @@ -0,0 +1,19 @@ +const budgetPeriodManager = require('../managers/BudgetPeriodManager'); + +exports.load = (app) => { + // app.get('/:username/period/current', (req, res) => { + // const { username } = req.params; + // budgetPeriodManager.getBudgetPeriodForUserByDateInPeriod(username, new Date()).then(data => res.json(data)); + // }); + + app.get('/:username/period/for/:date', (req, res) => { + const { username, date } = req.params; + res.header("Access-Control-Allow-Origin", "*"); + budgetPeriodManager.getBudgetPeriodForUserByDateInPeriod(username, new Date(date)).then(data => res.json(data)); + }); + + app.get('/:username/period/in/:start..:end', (req, res) => { + const { username, start, end } = req.params; + budgetPeriodManager.getBudgetPeriodsForUserWithinDateRange(username, new Date(start), new Date(end)).then(data => res.json(data)); + }); +}; \ No newline at end of file diff --git a/server/src/controllers/UserController.js b/server/src/controllers/UserController.js new file mode 100644 index 0000000..9a1a2f8 --- /dev/null +++ b/server/src/controllers/UserController.js @@ -0,0 +1,7 @@ +const userStore = require('../stores/UserStore'); + +exports.load = (app) => { + app.get('/users', (req, res) => { + userStore.getUsers().then(data => res.json({ users: data })); + }); +}; \ No newline at end of file diff --git a/server/src/managers/UserManager.js b/server/src/managers/AccountManager.js similarity index 100% rename from server/src/managers/UserManager.js rename to server/src/managers/AccountManager.js diff --git a/server/src/managers/BudgetPeriodManager.js b/server/src/managers/BudgetPeriodManager.js new file mode 100644 index 0000000..f685ea7 --- /dev/null +++ b/server/src/managers/BudgetPeriodManager.js @@ -0,0 +1,22 @@ +const userStore = require('../stores/UserStore'), + budgetPeriodStore = require('../stores/BudgetPeriodStore'); + +exports.getBudgetPeriodsForUserWithinDateRange = (username, startDate, endDate) => { + return new Promise((resolve, reject) => { + userStore.findUser(username).then((user) => { + budgetPeriodStore.getBudgetPeriodsForUserWithinDateRange(user._id, startDate, endDate).then(budgetPeriods => { + resolve(budgetPeriods); + }); + }); + }); +}; + +exports.getBudgetPeriodForUserByDateInPeriod = (username, date) => { + return new Promise((resolve, reject) => { + userStore.findUser(username).then((user) => { + budgetPeriodStore.getBudgetPeriodForUserByDateInPeriod(user._id, date).then(accounts => { + resolve(accounts); + }); + }); + }); +}; \ No newline at end of file diff --git a/server/src/server.js b/server/src/server.js index c20c249..5c18b2d 100644 --- a/server/src/server.js +++ b/server/src/server.js @@ -1,22 +1,12 @@ const express = require('express'), port = process.env.PORT || 3001, - app = express(), - userManager = require('./managers/UserManager'), - userStore = require('./stores/UserStore'), - accountStore = require('./stores/AccountStore'); + app = express(); -app.get('/users', (req, res) => { - userStore.getUsers().then(data => res.json({ users: data })); -}); - -app.get('/:username/accounts', (req, res) => { - const { username } = req.params; - userManager.getUserAccounts(username).then(data => res.json(data)); -}); - -app.get('/accounts', (req, res) => { - accountStore.getAccounts().then(data => res.json({ accounts: data })); -}); +[ + require('./controllers/UserController'), + require('./controllers/AccountController'), + require('./controllers/BudgetPeriodController') +].forEach(c => c.load(app)); app.listen(port, () => { console.log(`===> Server started: http://localhost:${port}/`); diff --git a/server/src/stores/BudgetPeriodStore.js b/server/src/stores/BudgetPeriodStore.js new file mode 100644 index 0000000..3310a55 --- /dev/null +++ b/server/src/stores/BudgetPeriodStore.js @@ -0,0 +1,23 @@ +const mongo = require('./Mongo'); + +exports.getBudgetPeriods = () => { + return mongo.listAll('budgetPeriods'); +}; + +exports.getBudgetPeriodsForUserWithinDateRange = (userId, startDate, endDate) => { + return mongo.find('budgetPeriods', { + user: userId, + startDate: { $gte: startDate }, + endDate: { $lt: endDate } + }); +}; + +exports.getBudgetPeriodForUserByDateInPeriod = (userId, date) => { + return mongo.findOne('budgetPeriods', { + user: userId, + startDate: { $lte: date }, + endDate: { $gt: date } + }); +}; + +// db.budgetPeriods.insert({ cashflows: [ { account: ObjectId("63fab01a23cd0682fe8c8ed7"), dueDate: new Date("2023-03-04"), postingDate: new Date("2023-03-04"), amount: 22.23, isIncome: false }], verified: true, adhocPurchases: [ { postingDate: new Date("2023-03-05"), amount: 1.99, description: "", isIncome: false }] }) \ No newline at end of file -- 2.34.1