From 28bd306b59c3ca3b25ea2ea229b6896deb0606cb Mon Sep 17 00:00:00 2001 From: Justin Walrath Date: Sat, 25 Feb 2023 13:54:37 -0500 Subject: [PATCH] [#1] Stubbed out a functional context to begin prototyping --- package-lock.json | 1 + package.json | 1 + src/base/App.jsx | 20 ++++++++++++++++++-- src/data/context/BudgetContext.jsx | 27 +++++++++++++++++++++++++++ src/data/context/BudgetReducer.js | 21 +++++++++++++++++++++ src/index.jsx | 2 +- src/mocked/MockedData.js | 22 +++++++++++++++++++++- src/views/BudgetCardView.jsx | 13 ++++++++----- src/views/BudgetPeriodCard.jsx | 23 +++++++++++++---------- src/views/BudgetPeriodCard.scss | 2 +- src/views/BudgetPeriodTransTable.jsx | 19 ++++++++++++++++--- 11 files changed, 128 insertions(+), 23 deletions(-) create mode 100644 src/data/context/BudgetContext.jsx create mode 100644 src/data/context/BudgetReducer.js diff --git a/package-lock.json b/package-lock.json index 650d33a..19ff457 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "bootstrap": "^5.1.3", "gridjs": "^5.0.2", "gridjs-react": "^5.0.2", + "lodash": "^4.17.21", "react": "^17.0.2", "react-bootstrap": "^2.1.2", "react-dom": "^17.0.2", diff --git a/package.json b/package.json index 104941f..ca84a1f 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "bootstrap": "^5.1.3", "gridjs": "^5.0.2", "gridjs-react": "^5.0.2", + "lodash": "^4.17.21", "react": "^17.0.2", "react-bootstrap": "^2.1.2", "react-dom": "^17.0.2", diff --git a/src/base/App.jsx b/src/base/App.jsx index 2662d2e..5585db6 100644 --- a/src/base/App.jsx +++ b/src/base/App.jsx @@ -1,10 +1,26 @@ +import { useEffect, useContext } from 'react'; import mockedData from '../mocked/MockedData'; import BudgetCardView from '../views/BudgetCardView'; +import { BudgetContext, BudgetState } from '../data/context/BudgetContext'; import './App.css'; -function App() { +const AppImpl = () => { + const context = useContext(BudgetContext); + + useEffect(() => { + context.api.loadData(mockedData.budgetPeriods); + }, []); + return ( - + + ); +}; + +const App = (props) => { + return ( + + + ); } diff --git a/src/data/context/BudgetContext.jsx b/src/data/context/BudgetContext.jsx new file mode 100644 index 0000000..26afc04 --- /dev/null +++ b/src/data/context/BudgetContext.jsx @@ -0,0 +1,27 @@ +import { useReducer, createContext } from 'react'; +import BudgetReducer from './BudgetReducer'; + +const BudgetContext = createContext(); + +const BudgetState = (props) => { + const initialState = { + budgetPeriods: [] + }, + [state, dispatch] = useReducer(BudgetReducer, initialState), + api = { + loadData: (data) => { + dispatch({ type: 'budget/populate', data }); + }, + updateBillPaid: (periodId, billId) => { + dispatch({ type: 'budget/period/bills/update', data: { periodId, billId }}); + } + }; + + return ( + + {props.children} + + ); +}; + +export { BudgetState, BudgetContext }; \ No newline at end of file diff --git a/src/data/context/BudgetReducer.js b/src/data/context/BudgetReducer.js new file mode 100644 index 0000000..4f96a86 --- /dev/null +++ b/src/data/context/BudgetReducer.js @@ -0,0 +1,21 @@ +const BudgetReducer = (state, action) => { + if (action.type === 'budget/populate') { + return { + ...state, + budgetPeriods: action.data + }; + } + if (action.type === 'budget/period/bills/update') { + return { + ...state, + budgetPeriods: state.budgetPeriods.map(bp => ({...bp, + bills: bp.bills.map(b => ({...b, + paid: (bp.id === action.data.periodId && b.id === action.data.billId) ? !b.paid : b.paid + })) + })) + }; + } + return {...state }; +}; + +export default BudgetReducer; \ No newline at end of file diff --git a/src/index.jsx b/src/index.jsx index b5a595f..a462cb7 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -8,4 +8,4 @@ ReactDOM.render( , document.getElementById('root') -); +); \ No newline at end of file diff --git a/src/mocked/MockedData.js b/src/mocked/MockedData.js index 0cd43da..77010ba 100644 --- a/src/mocked/MockedData.js +++ b/src/mocked/MockedData.js @@ -1,26 +1,30 @@ const mockedData = { - ranges: [{ + budgetPeriods: [{ id: 1, locked: true, startDate: new Date('1-7-2022 0:0:0'), endDate: new Date('1-20-2022 23:59:59'), startingAmount: 0.00, bills: [{ + id: 1, description: 'Amazon purchase', amount: 46.00, paid: false }, { + id: 2, description: 'Bill 1', amount: 40.00, paid: true }, { + id: 3, description: 'Bill 2', amount: 50.00, paid: true }, { + id: 4, description: 'Bill 3', amount: 30.00, paid: true @@ -39,21 +43,25 @@ const mockedData = { endDate: new Date('2-3-2022 23:59:59'), startingAmount: 0.00, bills: [{ + id: 5, description: 'Amazon purchase', amount: 46.00, paid: false }, { + id: 6, description: 'Bill 1', amount: 40.00, paid: true }, { + id: 7, description: 'Bill 2', amount: 50.00, paid: true }, { + id: 8, description: 'Bill 3', amount: 30.00, paid: true @@ -72,21 +80,25 @@ const mockedData = { endDate: new Date('2-17-2022 23:59:59'), startingAmount: 0.00, bills: [{ + id: 9, description: 'Amazon purchase', amount: 46.00, paid: false }, { + id: 10, description: 'Bill 1', amount: 40.00, paid: true }, { + id: 11, description: 'Bill 2', amount: 50.00, paid: true }, { + id: 12, description: 'Bill 3', amount: 30.00, paid: true @@ -105,21 +117,25 @@ const mockedData = { endDate: new Date('3-3-2022 23:59:59'), startingAmount: 0.00, bills: [{ + id: 13, description: 'Amazon purchase', amount: 46.00, paid: false }, { + id: 14, description: 'Bill 1', amount: 40.00, paid: true }, { + id: 15, description: 'Bill 2', amount: 50.00, paid: true }, { + id: 16, description: 'Bill 3', amount: 30.00, paid: true @@ -138,21 +154,25 @@ const mockedData = { endDate: new Date('3-17-2022 23:59:59'), startingAmount: 0.00, bills: [{ + id: 17, description: 'Amazon purchase', amount: 46.00, paid: false }, { + id: 18, description: 'Bill 1', amount: 40.00, paid: true }, { + id: 19, description: 'Bill 2', amount: 50.00, paid: true }, { + id: 20, description: 'Bill 3', amount: 30.00, paid: true diff --git a/src/views/BudgetCardView.jsx b/src/views/BudgetCardView.jsx index 80fcaec..ed68779 100644 --- a/src/views/BudgetCardView.jsx +++ b/src/views/BudgetCardView.jsx @@ -1,13 +1,16 @@ -import BudgetPeriodCard from "./BudgetPeriodCard"; +import _ from 'lodash'; +import BudgetPeriodCard from './BudgetPeriodCard'; +import { BudgetContext } from '../data/context/BudgetContext' +import { useContext } from 'react'; import './BudgetCardView.scss'; -const BudgetCardView = ({budgetPeriods}) => { - const activePeriod = budgetPeriods.find(p => !p.locked); +const BudgetCardView = () => { + const context = useContext(BudgetContext), + activePeriod = context?.budgetPeriods?.find?.(p => !p.locked); return (
- {budgetPeriods.map(p => { - + {context?.budgetPeriods?.map?.(p => { return ( ); diff --git a/src/views/BudgetPeriodCard.jsx b/src/views/BudgetPeriodCard.jsx index f76ed0d..e18bb63 100644 --- a/src/views/BudgetPeriodCard.jsx +++ b/src/views/BudgetPeriodCard.jsx @@ -1,19 +1,24 @@ -import { useState, useEffect, useRef } from 'react'; +import { useState, useEffect, useRef, useContext } from 'react'; import BudgetPeriodTransTable from './BudgetPeriodTransTable'; import Lock from '../resources/Lock.svg'; import OpenLock from '../resources/OpenLock.svg'; import './BudgetPeriodCard.scss'; +import { BudgetContext } from '../data/context/BudgetContext'; const BudgetPeriodCard = ({periodData, isActive}) => { const cardRef = useRef(), + context = useContext(BudgetContext), [bankBalance, setBankBalance] = useState(0), [projectedLeftover, setProjectedLeftover] = useState(0), - [bills, setBills] = useState(periodData.bills), - [paidBills, setPaidBills] = useState([]), - [unpaidBills, setUnpaidBills] = useState([]), + paidBills = periodData.bills.filter((bill) => bill.paid), + unpaidBills = periodData.bills.filter((bill) => !bill.paid), [incomes, setIncomes] = useState(periodData.income), [paidIncomes, setPaidIncomes] = useState([]), - [unpaidIncomes, setUnpaidIncomes] = useState([]); + [unpaidIncomes, setUnpaidIncomes] = useState([]), + moveTransItemAction = (row) => { + const data = row.cells[2].data + context.api.updateBillPaid(periodData.id, data.id); + }; useEffect(() => { if(isActive) { @@ -21,8 +26,6 @@ const BudgetPeriodCard = ({periodData, isActive}) => { cardRef.current.parentElement.scrollBy(-40, 0); } }, []); - useEffect(() => { setPaidBills(bills.filter((bill) => bill.paid)); }, [bills]); - useEffect(() => { setUnpaidBills(bills.filter((bill) => !bill.paid)); }, [bills]); useEffect(() => { setPaidIncomes(incomes.filter((income) => income.paid)); }, [incomes]); useEffect(() => { setUnpaidIncomes(incomes.filter((income) => !income.paid)); }, [incomes]); useEffect(() => { @@ -61,9 +64,9 @@ const BudgetPeriodCard = ({periodData, isActive}) => { Projected Leftover { '$' + parseFloat(projectedLeftover).toFixed(2) }
- - - + + + ); diff --git a/src/views/BudgetPeriodCard.scss b/src/views/BudgetPeriodCard.scss index 0720368..3bbd5dd 100644 --- a/src/views/BudgetPeriodCard.scss +++ b/src/views/BudgetPeriodCard.scss @@ -3,7 +3,7 @@ border-radius: 5px; border: 1px solid #ccc; margin: 1em; - width: 300px; + width: 400px; .card-header { height: 3em; background-color: #ccc; diff --git a/src/views/BudgetPeriodTransTable.jsx b/src/views/BudgetPeriodTransTable.jsx index 1552d32..c227473 100644 --- a/src/views/BudgetPeriodTransTable.jsx +++ b/src/views/BudgetPeriodTransTable.jsx @@ -1,11 +1,24 @@ import { Grid } from 'gridjs-react'; +import { h } from 'gridjs'; import './BudgetPeriodTransTable.scss'; import '../../node_modules/gridjs/dist/theme/mermaid.css' -const BudgetPeriodTransTable = ({title, transList, isBill}) => { - const columns = ['Description', 'Amount'], +const BudgetPeriodTransTable = ({title, transList, isBill, actionHandler}) => { + const columns = [ + 'Description', + 'Amount', + { + name: 'Actions', + formatter: (cell, row) => { + return h('button', { + className: 'py-2 mb-4 px-4 border rounded-md text-white bg-blue-600', + onClick: () => actionHandler(row) + }, row.cells[2].data.paid ? 'Unpay' : 'Pay'); + } + } + ], toCurrency = (amount) => { return '$' + (isBill === true ? '-' : '') + amount; }, - tableData = transList.map(t => { return [t.description, toCurrency(t.amount)]}); + tableData = transList.map(t => { return [t.description, toCurrency(t.amount), t]}); return (