aboutsummaryrefslogtreecommitdiffstats
path: root/src/expense/static/script.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/expense/static/script.js')
-rw-r--r--src/expense/static/script.js165
1 files changed, 165 insertions, 0 deletions
diff --git a/src/expense/static/script.js b/src/expense/static/script.js
new file mode 100644
index 0000000..810dc0e
--- /dev/null
+++ b/src/expense/static/script.js
@@ -0,0 +1,165 @@
+/**
+ * Category type in Javascript
+ * @typedef {Object} Category
+ * @property {Number} id
+ * @property {string} name
+ */
+
+/**
+ * Expense Type needed for summary
+ * @typedef {Object} Expense
+ * @property {Category} category
+ * @property {Number} amount
+ */
+
+/***
+ * The main function
+ */
+async function main() {
+ const data = await getData()
+ const root = document.getElementById("root")
+ // Extract Labels
+ const labels = uniq(data.map(
+ ({ category }) => category),
+ ({ id }) => id
+ ).sort((a, b) => a.id - b.id)
+ createGraphs(root, data, labels)
+ generateTable(root, data, labels)
+}
+
+/**
+ * Crete the Table
+ * @param {HTMLElement} root
+ * @param {Expense[]} data
+ * @param {str[]} labels
+ */
+function generateTable(root, data, labels) {
+ const summary = group_cat(data, labels)
+
+ const table = document.createElement("table")
+ table.style.margin = "1rem auto"
+ root.append(table)
+
+ table.createTHead()
+ table.createTFoot()
+
+ const row = table.tHead.insertRow()
+ row.append(...["Category", "Amount"].map(element => {
+ const header = document.createElement("th")
+ header.append(element)
+ return header
+ }))
+
+ summary.forEach(element => {
+ const row = table.insertRow()
+
+ const link = document.createElement("a")
+ row.insertCell().append(link)
+ link.href = `/cat/${element.category.id}`
+ link.append(element.category.name)
+
+ row.insertCell().append(element.amount.toFixed(2))
+ })
+
+ const footer = table.tFoot.insertRow();
+ [
+ "Overall",
+ summary.reduce((partialSum, { amount }) => partialSum + amount, 0).toFixed(2)
+ ].forEach(element => {
+ footer.insertCell().append(element)
+ })
+}
+
+/**
+ * Create The Pie Charts
+ * @param {HTMLElement} root
+ * @param {Expense[]} data
+ * @param {str[]} labels
+ */
+function createGraphs(root, data, labels) {
+ const graphBar = document.createElement("div")
+ root.append(graphBar)
+ graphBar.style.display = "grid"
+ graphBar.style.gridAutoFlow = "column"
+ pieChart(graphBar, group_cat(data.filter(({ amount }) => amount < 0), labels), "Inflow")
+ pieChart(graphBar, group_cat(data.filter(({ amount }) => amount > 0), labels), "Outflow")
+}
+
+/**
+ * Fetches the summary Data
+ * @returns {Promise<Expense[]>}
+ */
+async function getData() {
+ const res = await fetch("/summary.json")
+ const raw = await res.json()
+
+ const data = raw.map(({ amount, category }) => ({ amount, category }))
+ return data
+}
+
+/**
+ * Group the data by category
+ * @param {Expense[]} data
+ * @param {Category[]} labels
+ * @returns {Expense[]}
+ */
+function group_cat(data, labels = []) {
+ return data.reduce((accumulator, { category, amount }) => {
+ const index = accumulator.find(element => element.category.id == category.id)
+ if (index !== undefined) {
+ index.amount += amount
+ return accumulator
+ } else {
+ return [...accumulator, { category, amount }]
+ }
+ }, labels.map(category => ({ category, amount: 0 })))
+}
+
+/**
+ * Create a pie chart from the data
+ * @param {element} root
+ * @param {any[]} data
+ * @param {str} title
+ */
+function pieChart(root, data, title = "") {
+ const ctx = document.createElement('canvas')
+
+ new Chart(ctx, {
+ type: "pie",
+ data: {
+ labels: data.map(({ category }) => category.name),
+ datasets: [{
+ data: data.map(({ amount }) => amount)
+ }]
+ },
+ options: {
+ plugins: {
+ legend: {
+ position: 'right',
+ },
+ title: {
+ display: true,
+ text: title
+ }
+ }
+ }
+ })
+ root.append(ctx)
+}
+
+/**
+ * get the unique value in an array
+ * @param {any[]} arr
+ * @param {(any) => any} key
+ * @returns any[]
+ */
+function uniq(arr, key) {
+ var seen = new Set()
+ return arr.filter((item) => {
+ var k = key(item)
+ return seen.has(k) ? false : seen.add(k)
+ })
+}
+
+
+window.onload = main \ No newline at end of file