From 1b5e9a2cef26e2851c19ede32f7d53fc9cf5e4b7 Mon Sep 17 00:00:00 2001 From: jveebs <83558932+jveebs@users.noreply.github.com> Date: Sun, 23 Apr 2023 14:40:53 -0400 Subject: [PATCH] Expenses by Category Chart & Add transaction widget --- src/components/AddExpenseForm.js | 48 ++++++---- src/components/CategorizedExpenses.js | 93 +++++++++++++++++++ ...egorizedBudget.js => CategorizedIncome.js} | 9 +- src/components/Income | 0 src/pages/Dashboard.jsx | 9 +- src/utils/utils.js | 4 +- 6 files changed, 134 insertions(+), 29 deletions(-) create mode 100644 src/components/CategorizedExpenses.js rename src/components/{CategorizedBudget.js => CategorizedIncome.js} (85%) create mode 100644 src/components/Income diff --git a/src/components/AddExpenseForm.js b/src/components/AddExpenseForm.js index e4240c0..e83537a 100644 --- a/src/components/AddExpenseForm.js +++ b/src/components/AddExpenseForm.js @@ -7,11 +7,10 @@ import { getSessionKey } from '../utils/utils.js' const AddExpenseForm = (props) => { const { dispatch } = useContext(AppContext); - const [name, setName] = useState(''); const [cost, setCost] = useState(''); const [category, setCategory] = useState(''); const [categoryList, setCategoryList] = useState([]); - const [isExpense, setIsExpense] = useState(true); + const [transactionType, setTransactionType] = useState('expenses'); useEffect(() => { try { @@ -26,18 +25,23 @@ const AddExpenseForm = (props) => { if (data.status != 200) { console.log(data.error); } else { - isExpense - ? setCategoryList(Object.keys(data.expenses)) - : setCategoryList(Object.keys(data.income)); + getCategoryList(); } }) } catch(error) { console.error(error); } - }, []) - + }, [transactionType]) + const toggleTransactionType = () => { - setIsExpense(!isExpense); + if (transactionType == "expenses") { + setTransactionType("income"); + } else { + setTransactionType("expenses"); + } + } + + const getCategoryList = () => { fetch('https://api.bb.gabefarrell.com/w/budget', { method: 'GET', headers: { @@ -49,9 +53,19 @@ const AddExpenseForm = (props) => { if (data.status != 200) { console.log(data.error); } else { - isExpense - ? setCategoryList(Object.keys(data.expenses)) - : setCategoryList(Object.keys(data.income)); + let categories = []; + + Object.values(data.expenses).map((transactions) => { + transactions.forEach(transaction => { + if (transaction.type == transactionType && !categories.includes(transaction.category)) { + categories.push(transaction.category) + } + }); + }); + + if (categories.length == 0) categories = ["Uncategorized"]; + setCategoryList(categories); + setCategory(categories[0]) } }) }; @@ -64,7 +78,6 @@ const AddExpenseForm = (props) => { let currency = "USD" let whole = 0; let decimal = 0; - let type = isExpense ? 'expenses' : 'income'; if (cost.includes(".")) { @@ -77,10 +90,12 @@ const AddExpenseForm = (props) => { formData.append('currency', currency); formData.append('whole', whole); formData.append('decimal', decimal); - formData.append('type', type) + formData.append('type', transactionType) + + console.log(transactionType); try { - fetch(`https://api.bb.gabefarrell.com/w/transactions?whole=${whole}&decimal=${decimal}¤cy=${currency}&category=${category}&type=${type}`, { + fetch(`https://api.bb.gabefarrell.com/w/transactions?whole=${whole}&decimal=${decimal}¤cy=${currency}&category=${category}&type=${transactionType}`, { method: 'POST', body: formData, headers: { @@ -99,7 +114,6 @@ const AddExpenseForm = (props) => { console.error(error); } - setName(''); setCost(''); }; @@ -148,13 +162,13 @@ const AddExpenseForm = (props) => {
diff --git a/src/components/CategorizedExpenses.js b/src/components/CategorizedExpenses.js new file mode 100644 index 0000000..0f3ffbb --- /dev/null +++ b/src/components/CategorizedExpenses.js @@ -0,0 +1,93 @@ +import { Doughnut } from "react-chartjs-2"; +import { Chart as ChartJS, ArcElement, Tooltip, Legend, defaults } from 'chart.js'; + +import { AppProvider } from "../context/AppContext"; +import { useState, useEffect } from 'react' +import { calculateValue, getSessionKey } from '../utils/utils.js' +import { Minimize } from "@material-ui/icons"; + +ChartJS.register(ArcElement, Tooltip, Legend); + +export default function CategorizedExpenses() { + const [chartData, setChartData] = useState(null); + + useEffect(() => { + async function getChartData() { + try { + fetch('https://api.bb.gabefarrell.com/w/budget', { + method: 'GET', + headers: { + 'x-session-key' : getSessionKey(), + } + }) + .then(response => response.json()) + .then(data => { + if (data.status != 200) { + console.log(data.error); + } else { + let categories = []; + let values = []; + + Object.values(data.expenses).map((transactions) => { + let cost = 0; + categories.push(transactions[0].category) + transactions.forEach(transaction => { + cost += calculateValue(transaction.amount); + }); + + values.push(cost); + }); + if (categories.length == 0) categories = [`No expenses`]; + + const chartData = { + labels: categories, + datasets: [{ + data: values, + label: "Category", + backgroundColor: [ + '#FFC857', + '#ED8146', + '#DB3A34', + '#5672C7', + ], + borderColor: [ + "white" + ], + borderWidth: 2, + }], + } + + setChartData(chartData); + } + }) + .catch(error => { + console.log(error); + }); + } catch(error) { + console.error(error); + } + } + getChartData(); + }); + + if (!chartData) { + return

Loading...

+ } + + return ( +
+

Expenses by Category

+ +
+ ) +} \ No newline at end of file diff --git a/src/components/CategorizedBudget.js b/src/components/CategorizedIncome.js similarity index 85% rename from src/components/CategorizedBudget.js rename to src/components/CategorizedIncome.js index bd5c4e4..9ff679e 100644 --- a/src/components/CategorizedBudget.js +++ b/src/components/CategorizedIncome.js @@ -8,7 +8,7 @@ import { Minimize } from "@material-ui/icons"; ChartJS.register(ArcElement, Tooltip, Legend); -export default function CategorizedBudget() { +export default function CategorizedIncome() { const [chartData, setChartData] = useState(null); useEffect(() => { @@ -26,12 +26,11 @@ export default function CategorizedBudget() { console.log(data.error); } else { - console.log(typeof data.expenses_by_category); const chartData = { - labels: Object.keys(data.expenses).length > 0 ? Object.keys(data.expenses) : [ "no expenses"], + labels: Object.keys(data.income).length > 0 ? Object.keys(data.income) : [ "no income"], datasets: [ { - data: Object.values(data.expenses_by_category).map(category => { + data: Object.values(data.income_by_category).map(category => { return calculateValue(category); }), backgroundColor: [ @@ -66,7 +65,7 @@ export default function CategorizedBudget() { return (
-

Expenses by Category

+

Income by Category

- +
-
- -
+
diff --git a/src/utils/utils.js b/src/utils/utils.js index d547956..ef5a8cc 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -43,6 +43,6 @@ export function handleLogout() { window.location.href='/welcome'; } -export function calculateValue(category) { - return category.whole + category.decimal * 0.01; +export function calculateValue(amount) { + return amount.whole + amount.decimal * 0.01; } \ No newline at end of file