feat(*): Add basic graphing

This commit is contained in:
Josh Creek
2023-01-01 19:33:33 +00:00
parent a3f11af180
commit ff33ba4088
8 changed files with 198 additions and 32 deletions
+1
View File
@@ -0,0 +1 @@
export const transactions: Array<BankTransaction> = [];
+129
View File
@@ -0,0 +1,129 @@
import { transactions } from "./bankData";
import Chart from 'chart.js/auto';
export default function generateCharts(): void {
// const filter: Filter = { year: 2022, category: 'Eating out', excludeIncome: true};
const filter: Filter = { year: 2022, excludeIncome: true};
// const filter: Filter = { year: 2022, month: 1, excludeIncome: true};
const chartData = getChartData(transactions, filter);
console.log('chartData', chartData);
const ctx = document.getElementById('myChart') as HTMLCanvasElement;
const myChart = new Chart(ctx, {
type: 'bar',
data: chartData,
options: {
responsive: true,
scales: {
x: {
stacked: true,
},
y: {
stacked: true
}
}
}
});
}
function getRandomColor(): string {
// Generate a random color in hexadecimal format
const color = Math.floor(Math.random() * 0x1000000).toString(16);
return '#' + ('000000' + color).slice(-6);
}
function getChartData(transactions: BankTransaction[], filters: { year?: number, month?: number, category?: string, excludeIncome?: boolean } | null | undefined): ChartData {
// Return an empty chart data object if no filters are provided
if (!filters) {
return {
labels: [],
datasets: []
};
}
// Filter the transactions based on the specified filters
const filteredTransactions = transactions.filter(transaction => {
if (filters.year && transaction.dateYear !== filters.year) {
return false;
}
if (filters.month && transaction.dateMonth !== filters.month) {
return false;
}
if (filters.category && transaction.category !== filters.category) {
return false;
}
return true;
});
// Group the transactions by year or month, depending on the filters
const groupedTransactions: { [key: string]: BankTransaction[] } = {};
if (filters.year && !filters.month) {
// Group the transactions by month
for (const transaction of filteredTransactions) {
if (filters.excludeIncome && transaction.amount < 0) {
continue; // Skip transactions with a positive amount if excludeIncome is true
}
const month = `${transaction.dateYear}-${transaction.dateMonth}`;
if (!groupedTransactions[month]) {
groupedTransactions[month] = [];
}
groupedTransactions[month].push(transaction);
}
} else {
// Group the transactions by year
for (const transaction of filteredTransactions) {
if (filters.excludeIncome && transaction.amount < 0) {
continue; // Skip transactions with a positive amount if excludeIncome is true
}
const year = transaction.dateYear.toString();
if (!groupedTransactions[year]) {
groupedTransactions[year] = [];
}
groupedTransactions[year].push(transaction);
}
}
// Create the chart data object
const chartData: ChartData = {
labels: [],
datasets: []
};
// Add a dataset for each category
const categories: { [key: string]: string } = {};
for (const year in groupedTransactions) {
for (let index = 0; index < groupedTransactions[year].length; index++) {
const transaction = groupedTransactions[year][index];
if (!categories[transaction.category]) {
categories[transaction.category] = getRandomColor();
chartData.datasets.push({
label: transaction.category,
data: [],
backgroundColor: categories[transaction.category]
});
}
}
console.log('here');
// Populate the chart data with the transaction amounts
for (const year in groupedTransactions) {
chartData.labels.push(year);
for (let i = 0; i < chartData.datasets.length; i++) {
chartData.datasets[i].data.push(0);
}
for (const transaction of groupedTransactions[year]) {
for (let i = 0; i < chartData.datasets.length; i++) {
if (chartData.datasets[i].label === transaction.category) {
chartData.datasets[i].data[chartData.labels.length - 1] += transaction.amount;
break;
}
}
}
}
return chartData;
}
}
+7 -20
View File
@@ -1,5 +1,6 @@
import loadServiceWorker from './loadServiceWorker';
import Chart from 'chart.js/auto';
import loadBankData from './loadBankData';
import generateCharts from './generateCharts';
require('./assets/favicon.ico');
require('./assets/android-chrome-192x192.png');
@@ -12,26 +13,12 @@ require('./styles/buttons.scss');
require('./styles/modal.scss');
require('./styles/spinner.scss');
const ctx: HTMLCanvasElement = <HTMLCanvasElement>document.getElementById('myChart');
if (localStorage.hasOwnProperty('bankData')) {
loadBankData(JSON.parse(localStorage.getItem('bankData')).values);
generateCharts();
}
new Chart(ctx, {
type: 'bar',
data: {
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
if (process.env.NODE_ENV === 'production') {
loadServiceWorker();
+29
View File
@@ -0,0 +1,29 @@
import { transactions } from "./bankData";
export default function loadBankData(data: Array<any>):void {
// Set bankData array to be whatever this new data is
for (let index = 0; index < data.length; index++) {
const dateParts: Array<string> = data[index][0].split("/");
const date: Date = new Date(Number(dateParts[2]), Number(dateParts[1])-1, Number(dateParts[0])); // JS months are zero-indexed
const year: number = date.getFullYear();
const month: number = date.getMonth();
const day: number = date.getDay();
const transaction: BankTransaction = {
date: data[index][0],
dateYear: year,
dateMonth: month + 1, // JS months are zero-indexed
dateDay: day,
time: data[index][1],
type: data[index][2],
name: data[index][3],
category: data[index][5],
amount: Number(data[index][6]) * -1, // Invert money out being negative and money in being positive
currency: data[index][7],
localAmount: Number(data[index][8]) * -1, // Invert money out being negative and money in being positive
localCurrency: data[index][9],
};
transactions.push(transaction);
}
}
+3 -12
View File
@@ -45,11 +45,6 @@
<canvas id="myChart"></canvas>
</div>
<footer class="footer-container">
<hr>
<span class="footer-items">Copyright &copy; 2022 Josh Creek</span>
@@ -118,7 +113,7 @@
* Enables user interaction after all libraries are loaded.
*/
function maybeEnableButtons() {
if (gapiInited && gisInited) {
if (gapiInited && gisInited && !localStorage.hasOwnProperty('bankData')) {
document.getElementById('authorize_button').style.visibility = 'visible';
}
}
@@ -132,7 +127,7 @@
throw (resp);
}
document.getElementById('signout_button').style.visibility = 'visible';
document.getElementById('authorize_button').innerText = 'Refresh';
document.getElementById('authorize_button').innerText = 'Refresh Google Sheets data';
await listMajors();
};
@@ -177,15 +172,11 @@
return;
}
const range = response.result;
localStorage.setItem('bankData', JSON.stringify(range));
if (!range || !range.values || range.values.length == 0) {
document.getElementById('content').innerText = 'No values found.';
return;
}
// Flatten to string to display
const output = range.values.reduce(
(str, row) => `${str}${row[0]}, ${row[4]}\n`,
'Name, Major:\n');
document.getElementById('content').innerText = output;
}
</script>
<script async defer src="https://apis.google.com/js/api.js" onload="gapiLoaded()"></script>
+15
View File
@@ -0,0 +1,15 @@
type BankTransaction = {
date: string;
dateYear: number;
dateMonth: number;
dateDay: number;
time: string;
type: string;
name: string;
// emoji here
category: string;
amount: number;
currency: string;
localAmount: number;
localCurrency: string; // Actually the foreign currency
};
+8
View File
@@ -0,0 +1,8 @@
type ChartData = {
labels: string[];
datasets: {
label: string;
backgroundColor: string;
data: number[];
}[];
};
+6
View File
@@ -0,0 +1,6 @@
type Filter = {
year?: number;
month?: number;
category?: string;
excludeIncome?: boolean;
};