feat(wpcarro/ynabsql): Proof-of-concept demo
Hacked this together during my week-off while I was in Telluride, CO. The git history is quite sloppy; so is some of the code. But it (mostly) works as a demo, and that was the point. Change-Id: Icfbc277090b69a802c00becdbd162652e4e8e156 Reviewed-on: https://cl.tvl.fyi/c/depot/+/7904 Reviewed-by: wpcarro <wpcarro@gmail.com> Tested-by: BuildkiteCI Autosubmit: wpcarro <wpcarro@gmail.com>
This commit is contained in:
parent
9f75973e4a
commit
b3a91ce57b
10 changed files with 3833 additions and 149 deletions
5
users/wpcarro/ynabsql/dataviz/.gitignore
vendored
Normal file
5
users/wpcarro/ynabsql/dataviz/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
/dist
|
||||||
|
/node_modules
|
||||||
|
/.parcel-cache
|
||||||
|
/cdn
|
||||||
|
/data.js
|
3
users/wpcarro/ynabsql/dataviz/.parcelrc
Normal file
3
users/wpcarro/ynabsql/dataviz/.parcelrc
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "@parcel/config-default"
|
||||||
|
}
|
1
users/wpcarro/ynabsql/dataviz/cdn
Symbolic link
1
users/wpcarro/ynabsql/dataviz/cdn
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
/tmp/cdn
|
66
users/wpcarro/ynabsql/dataviz/chart.js
Normal file
66
users/wpcarro/ynabsql/dataviz/chart.js
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
const colors = {
|
||||||
|
red: 'rgb(255, 45, 70)',
|
||||||
|
green: 'rgb(75, 192, 35)',
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Main
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
const mount = document.getElementById('mount');
|
||||||
|
|
||||||
|
const chart = new Chart(mount, {
|
||||||
|
type: 'scatter',
|
||||||
|
data: {
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: 'Revenue',
|
||||||
|
data: data.data.transactions.filter(x => x.Inflow > 0).map(x => ({
|
||||||
|
x: x.Date,
|
||||||
|
y: x.Inflow,
|
||||||
|
metadata: x,
|
||||||
|
})),
|
||||||
|
backgroundColor: colors.green,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Expenses',
|
||||||
|
data: data.data.transactions.filter(x => x.Outflow).map(x => ({
|
||||||
|
x: x.Date,
|
||||||
|
y: x.Outflow,
|
||||||
|
metadata: x,
|
||||||
|
})),
|
||||||
|
backgroundColor: colors.red,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
type: 'time',
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Date',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Amount ($USD)'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
title: function(x) {
|
||||||
|
return `$${x[0].raw.y} (${x[0].raw.metadata.Date.toLocaleDateString()})`;
|
||||||
|
},
|
||||||
|
label: function(x) {
|
||||||
|
const { Category, Payee, Memo } = x.raw.metadata;
|
||||||
|
return `${Payee} - ${Category} (${Memo})`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
File diff suppressed because it is too large
Load diff
1248
users/wpcarro/ynabsql/dataviz/components.jsx
Normal file
1248
users/wpcarro/ynabsql/dataviz/components.jsx
Normal file
File diff suppressed because it is too large
Load diff
|
@ -2,24 +2,28 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="stylesheet" href="https://unpkg.com/terminal.css@0.7.2/dist/terminal.min.css" />
|
<!-- TODO(wpcarro): Cache these locally -->
|
||||||
<link rel="stylesheet" href="https://unpkg.com/terminal.css@0.7.1/dist/terminal.min.css" />
|
<link rel="stylesheet" href="./cdn/terminal.min.css" />
|
||||||
<link rel="stylesheet" href="https://unpkg.com/terminal.css@0.7.1/dist/terminal.min.css" />
|
<style>
|
||||||
|
:root {
|
||||||
|
--page-width: 100em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="container">
|
||||||
<div id="react-mount"></div>
|
<div id="mount"></div>
|
||||||
<!-- <canvas id="mount"></canvas> -->
|
|
||||||
|
|
||||||
<!-- chart.js -->
|
<!-- chart.js -->
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
<script src="./cdn/chart.js"></script>
|
||||||
|
<script src="./cdn/date_fns.js"></script>
|
||||||
|
<script src="./cdn/chartjs-adapter-date-fns.bundle.min.js"></script>
|
||||||
<!-- react.js -->
|
<!-- react.js -->
|
||||||
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
|
<script src="./cdn/react.development.js" crossorigin></script>
|
||||||
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
|
<script src="./cdn/react-dom.development.js" crossorigin></script>
|
||||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
<script src="./cdn/babel.min.js"></script>
|
||||||
<!-- depot JS -->
|
<!-- depot JS -->
|
||||||
<script src="http://localhost:8002/index.js"></script>
|
<script src="./cdn/slx.js"></script>
|
||||||
<script src="./data.js"></script>
|
<script src="./data.js"></script>
|
||||||
<script src="./index.js"></script>
|
|
||||||
<script src="./components.js" type="text/babel"></script>
|
<script src="./components.js" type="text/babel"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
const colors = {
|
|
||||||
red: 'rgb(255, 99, 132)',
|
|
||||||
orange: 'rgb(255, 159, 64)',
|
|
||||||
yellow: 'rgb(255, 205, 86)',
|
|
||||||
green: 'rgb(75, 192, 192)',
|
|
||||||
blue: 'rgb(54, 162, 235)',
|
|
||||||
purple: 'rgb(153, 102, 255)',
|
|
||||||
grey: 'rgb(201, 203, 207)'
|
|
||||||
};
|
|
||||||
|
|
||||||
function randomExpense() {
|
|
||||||
// 10/1000 expenses are O(1,000)
|
|
||||||
// 100/2000 expenses are O(100)
|
|
||||||
// 1,000/2000 expenses are O(10)
|
|
||||||
// 10,000/2000 expenses are O(1)
|
|
||||||
const r = Math.random();
|
|
||||||
|
|
||||||
if (r <= 0.02) {
|
|
||||||
return Math.floor(Math.random() * 5000);
|
|
||||||
} else if (r <= 0.1) {
|
|
||||||
return Math.floor(Math.random() * 1000);
|
|
||||||
} else if (r <= 0.5) {
|
|
||||||
return Math.floor(Math.random() * 100);
|
|
||||||
} else {
|
|
||||||
return Math.floor(Math.random() * 10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Browser starts to choke around 10,000 data points.
|
|
||||||
function generateData() {
|
|
||||||
return Array(2000).fill(0).map(x => ({
|
|
||||||
// select a random day [0, 365]
|
|
||||||
x: Math.floor(Math.random() * 365),
|
|
||||||
// select a random USD amount in the range [1, 5,000]
|
|
||||||
y: randomExpense(),
|
|
||||||
// TODO(wpcarro): Attach transaction to `metadata` key.
|
|
||||||
metadata: { foo: 'bar' },
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Main
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
const mount = document.getElementById('mount');
|
|
||||||
|
|
||||||
new Chart(mount, {
|
|
||||||
type: 'scatter',
|
|
||||||
data: {
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: 'Expenses',
|
|
||||||
data: generateData(),
|
|
||||||
backgroundColor: colors.red,
|
|
||||||
}
|
|
||||||
],
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
plugins: {
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
title: function(x) {
|
|
||||||
return `$${x[0].raw.y}`;
|
|
||||||
},
|
|
||||||
label: function(x) {
|
|
||||||
return JSON.stringify(x.raw.metadata);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
15
users/wpcarro/ynabsql/dataviz/package.json
Normal file
15
users/wpcarro/ynabsql/dataviz/package.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"devDependencies": {
|
||||||
|
"@parcel/validator-typescript": "^2.8.3",
|
||||||
|
"parcel": "^2.8.3",
|
||||||
|
"process": "^0.11.10",
|
||||||
|
"typescript": ">=3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"chart.js": "^4.2.0",
|
||||||
|
"chartjs-adapter-date-fns": "^3.0.0",
|
||||||
|
"date-fns": "^2.29.3",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0"
|
||||||
|
}
|
||||||
|
}
|
1540
users/wpcarro/ynabsql/dataviz/yarn.lock
Normal file
1540
users/wpcarro/ynabsql/dataviz/yarn.lock
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue