aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/index.js2
-rw-r--r--src/server.js73
-rw-r--r--src/static/main.js222
-rw-r--r--src/templates.js2
-rw-r--r--src/templates/index.html210
-rw-r--r--src/templates/ledger-edit.html31
-rw-r--r--src/templates/ledger.html5
7 files changed, 95 insertions, 450 deletions
diff --git a/src/index.js b/src/index.js
index 8e18272..2f3763c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -22,7 +22,7 @@ const jwtFunctions = {
const database = new Sequelize(dbCreds.database, dbCreds.user, dbCreds.password, {
logging(str) {
- console.debug(`DB:${str}`);
+ console.debug(`DB: ${str}`);
},
dialectOptions: {
charset: 'utf8mb4',
diff --git a/src/server.js b/src/server.js
index 487fe0c..b7b5ce4 100644
--- a/src/server.js
+++ b/src/server.js
@@ -69,6 +69,13 @@ function setUpRoutes(models, jwtFunctions, database, templates) {
let body = templates["ledger"]({ name, ledger })
res.status(200).send(body)
})
+ server.get('/ledger/edit/:id', async (req, res) => {
+ let ledger = await database.query(`SELECT * FROM transactions WHERE username = '${res.locals.user.username}' and id='${req.params.id}' ORDER BY \`when\` DESC`, { type: database.QueryTypes.SELECT })
+ let ledger_item = ledger[0]
+ let name = res.locals.user.username
+ let body = templates["ledger-edit"]({ name, item: ledger_item })
+ res.status(200).send(body)
+ })
server.get('/goals', async (req, res) => {
let goals = await database.query(`SELECT * FROM goals WHERE username = '${res.locals.user.username}' ORDER BY \`name\` DESC`, { type: database.QueryTypes.SELECT })
goals.forEach((element, i) => {
@@ -95,33 +102,15 @@ function setUpRoutes(models, jwtFunctions, database, templates) {
})
server.get(`/summary`, async (req, res, next) => {
try {
- let data = {
- week: {
- out: await database.query(`SELECT year(\`when\`) as y, week(\`when\`) as w, sum(amount) as s FROM transactions where username = '${res.locals.user.username}' and amount > 0 group by year(\`when\`), WEEK(\`when\`);`, { type: database.QueryTypes.SELECT }),
- in: await database.query(`SELECT year(\`when\`)as y, week(\`when\`) as w, sum(amount) as s FROM transactions where username = '${res.locals.user.username}' and amount < 0 group by year(\`when\`), WEEK(\`when\`);`, { type: database.QueryTypes.SELECT }),
- net: await database.query(`SELECT year(\`when\`) as y, week(\`when\`) as w, sum(amount) as s FROM transactions where username = '${res.locals.user.username}' group by year(\`when\`), WEEK(\`when\`);`, { type: database.QueryTypes.SELECT }),
- },
- month: {
- out: await database.query(`SELECT year(\`when\`) as y, month(\`when\`) as m, sum(amount) as s FROM transactions where username = '${res.locals.user.username}' and amount > 0 group by year(\`when\`), month(\`when\`);`, { type: database.QueryTypes.SELECT }),
- in: await database.query(`SELECT year(\`when\`) as y, month(\`when\`) as m, sum(amount) as s FROM transactions where username = '${res.locals.user.username}' and amount < 0 group by year(\`when\`), month(\`when\`);`, { type: database.QueryTypes.SELECT }),
- net: await database.query(`SELECT year(\`when\`) as y, month(\`when\`) as m, sum(amount) as s FROM transactions where username = '${res.locals.user.username}' group by year(\`when\`), month(\`when\`);`, { type: database.QueryTypes.SELECT }),
- },
- year: {
- out: await database.query(`SELECT year(\`when\`) as y, sum(amount) as s FROM transactions where username = '${res.locals.user.username}' and amount > 0 group by year(\`when\`);`, { type: database.QueryTypes.SELECT }),
- in: await database.query(`SELECT year(\`when\`) as y, sum(amount) as s FROM transactions where username = '${res.locals.user.username}' and amount < 0 group by year(\`when\`);`, { type: database.QueryTypes.SELECT }),
- net: await database.query(`SELECT year(\`when\`) as y, sum(amount) as s FROM transactions where username = '${res.locals.user.username}' group by year(\`when\`);`, { type: database.QueryTypes.SELECT }),
- },
- name: res.locals.user.username
- };
- data = formatSummary(data)
+ let data = await formatSummary(database, res.locals.user.username)
let body = templates["summary"](data)
res.status(200).send(body);
-
} catch (e) {
console.log(e)
res.status(400).send(e.message);
}
})
+
server.post('/login', async (req, res, next) => {
@@ -140,7 +129,6 @@ function setUpRoutes(models, jwtFunctions, database, templates) {
server.post(`/transaction`, async (req, res, next) => {
try {
let item = req.body;
- console.log(item);
item.username = res.locals.user.username
if (!item.when) {
item.when = new Date().toLocaleDateString();
@@ -187,6 +175,28 @@ function setUpRoutes(models, jwtFunctions, database, templates) {
res.status(400).send(e.message);
}
})
+ server.post(`/transaction/:id`, async (req, res, next) => {
+ try {
+ let id = req.params.id;
+ let update = req.body;
+ if(update.when.length == 0){
+ delete update.when
+ }
+ var toUpdate = await models.transaction.findOne({ where: { id: id, username:res.locals.user.username } });
+ await toUpdate.update(update);
+ res.redirect(`/ledger`)
+ } catch (e) {
+ console.log(e);
+ res.status(400).send(e.message);
+ }
+ })
+
+ server.delete('/ledger/:id', async (req, res) => {
+ let id = req.params.id;
+ console.log(id, res.locals.user.username)
+ await models.transaction.destroy({ where: { id, username: res.locals.user.username } });
+ res.redirect('/ledger')
+ })
}
var findOrCreateWeek = function (summary, el) {
@@ -220,7 +230,24 @@ var findOrCreateYear = function (summary, el) {
return item
}
-function formatSummary(response) {
+async function formatSummary(database, username) {
+ let response = {
+ week: {
+ out: await database.query(`SELECT year(\`when\`) as y, week(\`when\`) as w, sum(amount) as s FROM transactions where username = '${username}' and amount > 0 group by year(\`when\`), WEEK(\`when\`);`, { type: database.QueryTypes.SELECT }),
+ in: await database.query(`SELECT year(\`when\`)as y, week(\`when\`) as w, sum(amount) as s FROM transactions where username = '${username}' and amount < 0 group by year(\`when\`), WEEK(\`when\`);`, { type: database.QueryTypes.SELECT }),
+ net: await database.query(`SELECT year(\`when\`) as y, week(\`when\`) as w, sum(amount) as s FROM transactions where username = '${username}' group by year(\`when\`), WEEK(\`when\`);`, { type: database.QueryTypes.SELECT }),
+ },
+ month: {
+ out: await database.query(`SELECT year(\`when\`) as y, month(\`when\`) as m, sum(amount) as s FROM transactions where username = '${username}' and amount > 0 group by year(\`when\`), month(\`when\`);`, { type: database.QueryTypes.SELECT }),
+ in: await database.query(`SELECT year(\`when\`) as y, month(\`when\`) as m, sum(amount) as s FROM transactions where username = '${username}' and amount < 0 group by year(\`when\`), month(\`when\`);`, { type: database.QueryTypes.SELECT }),
+ net: await database.query(`SELECT year(\`when\`) as y, month(\`when\`) as m, sum(amount) as s FROM transactions where username = '${username}' group by year(\`when\`), month(\`when\`);`, { type: database.QueryTypes.SELECT }),
+ },
+ year: {
+ out: await database.query(`SELECT year(\`when\`) as y, sum(amount) as s FROM transactions where username = '${username}' and amount > 0 group by year(\`when\`);`, { type: database.QueryTypes.SELECT }),
+ in: await database.query(`SELECT year(\`when\`) as y, sum(amount) as s FROM transactions where username = '${username}' and amount < 0 group by year(\`when\`);`, { type: database.QueryTypes.SELECT }),
+ net: await database.query(`SELECT year(\`when\`) as y, sum(amount) as s FROM transactions where username = '${username}' group by year(\`when\`);`, { type: database.QueryTypes.SELECT }),
+ },
+ };
let summary = {}
summary.week = [];
summary.month = [];
@@ -295,7 +322,7 @@ function formatSummary(response) {
return a.y - b.y;
})
- summary.name = response.name
+ summary.name = username
return summary
}
diff --git a/src/static/main.js b/src/static/main.js
index 0a381b0..1bcf5e5 100644
--- a/src/static/main.js
+++ b/src/static/main.js
@@ -1,216 +1,8 @@
-window.onload = function () {
- var transactionData = new Vue({
- el: '#data',
- data: {
- activeTab: "ledger",
- transactions: [],
- summary: {username : ""},
- selTodoType: "all",
- total_to_allocate: 0,
- goals: [],
- expected: []
- },
- methods: {
- setTab: function (value) {
- this.activeTab = value;
- },
- clearData: function () {
- this.m = {
- when: new Date().toLocaleDateString(),
- where: "",
- amount: "",
- category: "",
- subcategory: "",
- }
- this.em = {
- when: new Date().toLocaleDateString(),
- where: "",
- amount: "",
- category: "",
- subcategory: "",
- }
- this.ng = {
- name: "",
- total: "",
- amount: 0
- }
- this.e = {
- name: "",
- total: "",
- days: 7
- }
- this.na = {
- selected: "",
- amount: ""
- }
- },
- requestThenUpdate: function (request, app, field) {
- fetch(request)
- .then(response => response.json())
- .then(response => {
- app[field] = response
- });
- },
- post: function (obj, path, save_to) {
- console.log(obj);
- console.log(path);
- this.requestThenUpdate(new Request(path, {
- method: 'POST',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(obj)
- }), this, save_to);
- this.clearData();
- },
- remove: function (obj, save_to) {
- if (confirm(`Delete transaction?`)) {
- this.requestThenUpdate(new Request("/transaction", {
- method: 'delete',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(obj)
- }), this, save_to)
- }
- },
- prepareEntryEdit: function(transaction){
- this.em.id=transaction.id;
- this.em.where=transaction.where;
- this.em.when=transaction.when;
- this.em.amount=transaction.amount;
- this.em.category=transaction.category;
- this.em.subcategory=transaction.subcategory;
- this.activeTab='ledger-edit';
- },
- updateMany: function (obj, save_to) {
- update = {}
- update = obj;
- this.requestThenUpdate(new Request("/transaction", {
- method: 'put',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ id: obj.id, update: update })
- }), this, save_to)
- },
- },
- created() {
- this.clearData();
- fetch(new Request(`/expected`)).then(response => response.json())
- .then(response => this.expected = response);
- fetch(new Request(`/goals`)).then(response => response.json())
- .then(response => this.goals = response);
- fetch(new Request(`/transaction`)).then(response => response.json())
- .then(response => this.transactions = response);
- fetch(new Request(`/summary`)).then(response => response.json())
- .then(response => {
- var findOrCreateWeek = function(t, el){
- var item = t.summary.week.find( el2 => {
- return el.y == el2.y && el.w == el2.w
- })
- if(!item){
- item = {y : el.y, w : el.w, in: 0, out: 0, net: 0}
- t.summary.week.push(item);
- }
- return item
- }
- var findOrCreateMonth = function(t, el){
- var item = t.summary.month.find( el2 => {
- return el.y == el2.y && el.m == el2.m
- })
- if(!item){
- item = {y : el.y, m : el.m, in: 0, out: 0, net: 0}
- t.summary.month.push(item);
- }
- return item
- }
- var findOrCreateYear = function(t, el){
- var item = t.summary.year.find( el2 => {
- return el.y == el2.y
- })
- if(!item){
- item = {y : el.y, in: 0, out: 0, net: 0}
- t.summary.year.push(item);
- }
- return item
- }
-
- this.summary.week = [];
- this.summary.month = [];
- this.summary.year = [];
-
- response.week.in.forEach(el => {
- findOrCreateWeek(this, el).in = Math.abs(el.s)
- })
- response.week.out.forEach(el => {
- findOrCreateWeek(this, el).out = Math.abs(el.s)
- })
- response.week.net.forEach(el => {
- var item = findOrCreateWeek(this, el);
- item.net = el.s
- // Note we flip these since income is negative
- item.negative = el.s > 0
- item.positive = el.s < 0
- })
-
- response.month.in.forEach(el => {
- findOrCreateMonth(this, el).in = Math.abs(el.s)
- })
- response.month.out.forEach(el => {
- findOrCreateMonth(this, el).out = Math.abs(el.s)
- })
- response.month.net.forEach(el => {
- var item = findOrCreateMonth(this, el);
- item.net = el.s
- // Note we flip these since income is negative
- item.negative = el.s > 0
- item.positive = el.s < 0
- })
-
- response.year.in.forEach(el => {
- findOrCreateYear(this, el).in = Math.abs(el.s)
- })
- response.year.out.forEach(el => {
- findOrCreateYear(this, el).out = Math.abs(el.s)
- })
- response.year.net.forEach(el => {
- var item = findOrCreateYear(this, el);
- item.net = el.s
- // Note we flip these since income is negative
- item.negative = el.s > 0
- item.positive = el.s < 0
-
- // -= since its flipped
- this.total_to_allocate -= item.net
- })
- this.goals.forEach(el => {
- this.total_to_allocate -= el.amount
- })
-
- this.summary.week.sort(function(a, b){
- if ( a.y == b.y ){ return a.w - b.w; }
- return a.y-b.y;
- })
- this.summary.month.sort(function(a, b){
- if ( a.y == b.y ){ return a.m - b.m; }
- return a.y-b.y;
- })
- this.summary.year.sort(function(a, b){
- return a.y-b.y;
- })
-
- seriesX = this.summary.month.map(el => el.y)
-
-
- this.summary.username = response.username
- });
- },
- computed: {
-
- }
- });
+function remove(id){
+ if (confirm(`Delete transaction?`)) {
+ let request = new Request(`/ledger/${id}`, {
+ method: 'delete',
+ })
+ fetch(request).then(r => window.location = "/ledger");
+ }
} \ No newline at end of file
diff --git a/src/templates.js b/src/templates.js
index 6e3b541..f7bfdfb 100644
--- a/src/templates.js
+++ b/src/templates.js
@@ -10,9 +10,9 @@ function loadTemplate(templates, name, filepath){
function setUpTemplates(){
let templates = {};
- loadTemplate(templates, "index", path.join(__dirname, 'templates/index.html'))
loadTemplate(templates, "login", path.join(__dirname, 'templates/login.html'))
loadTemplate(templates, "ledger", path.join(__dirname, 'templates/ledger.html'))
+ loadTemplate(templates, "ledger-edit", path.join(__dirname, 'templates/ledger-edit.html'))
loadTemplate(templates, "goals", path.join(__dirname, 'templates/goals.html'))
loadTemplate(templates, "expected", path.join(__dirname, 'templates/expected.html'))
loadTemplate(templates, "summary", path.join(__dirname, 'templates/summary.html'))
diff --git a/src/templates/index.html b/src/templates/index.html
deleted file mode 100644
index 525f8fe..0000000
--- a/src/templates/index.html
+++ /dev/null
@@ -1,210 +0,0 @@
-<!doctype html>
-<html lang="en">
-
-<head>
- <title>Budget</title>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
- <link rel="shortcut icon" href="/favicon.ico">
- <!-- <script src="https://cdn.jsdelivr.net/npm/vue"></script> -->
- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
- <script src="static/main.js"></script>
- <link rel="stylesheet" type="text/css" href="static/styles.css">
-</head>
-
-<body>
- <div id="data">
- <h1 v-if="summary.username">{{summary.username}}'s budget</h1>
- <div>
- <button v-bind:class="{ bold: activeTab == 'ledger' }" v-on:click="setTab('ledger')">Ledger</button>
- <button v-bind:class="{ bold: activeTab == 'summary' }" v-on:click="setTab('summary')">Summary</button>
- <button v-bind:class="{ bold: activeTab == 'goals' }" v-on:click="setTab('goals')">Goals</button>
- <button v-bind:class="{ bold: activeTab == 'expected' }" v-on:click="setTab('expected')">Expected</button>
- </div>
-
- <!-- Ledger -->
- <div v-if="activeTab == 'ledger'">
- <div class="newItem">
- <input v-model="m.when" placeholder="Date" type="date">
- <input v-model="m.where" placeholder="Where">
- <input v-model="m.amount" placeholder="Amount" type="number" step="0.01">
- <input v-model="m.category" placeholder="Category">
- <input v-model="m.subcategory" placeholder="Subcategory">
- <button v-on:click="post(m, '/transaction', 'transactions')">Add</button>
- </div>
- <table class="table">
- <tr class="table-header">
- <th class="table-index"></th>
- <th>Date</th>
- <th>Where</th>
- <th>Amount</th>
- <th>Category</th>
- <th>Subcategory</th>
- <th></th>
- </tr>
- <tr v-for="(transaction, i) in transactions">
- <td class="table-index">{{i+1}}</td>
- <td>{{transaction.when.substring(0,10)}}</td>
- <td>{{transaction.where}}</td>
- <td>{{transaction.amount}}</td>
- <td>{{transaction.category}}</td>
- <td>{{transaction.subcategory}}</td>
- <td>
- <button v-on:click="prepareEntryEdit(transaction)">&#9881;</button>
- <button v-on:click="remove(transaction, 'transactions')">X</button>
- </td>
- </tr>
-
- </table>
- </div>
-
- <table v-if="activeTab == 'ledger-edit'">
- <tr>
- <th></th>
- <th>Date</th>
- <th>Where</th>
- <th>Amount</th>
- <th>Category</th>
- <th>Subcategory</th>
- </tr>
- <tr>
- <td></td>
- <td>
- <input v-model="em.when" placeholder="Date" type="date">
- </td>
- <td>
- <input v-model="em.where" placeholder="Where">
- </td>
- <td>
- <input v-model="em.amount" placeholder="Amount" type="number" step="0.01">
- </td>
- <td>
- <input v-model="em.category" placeholder="Category">
- </td>
- <td>
- <input v-model="em.subcategory" placeholder="Subcategory">
- </td>
- <td><button v-on:click="updateMany(em, 'transactions');activeTab=0;">Update</button></td>
- </tr>
- </table>
-
- <!-- Summary -->
- <div v-if="activeTab == 'summary'">
- <div class="summary-panel">
- <h2>Weekly</h2>
- <table class="table">
- <tr>
- <th>Year</th>
- <th>Week</th>
- <th>In</th>
- <th>Out</th>
- <th>Net</th>
- </tr>
- <tr v-for="(data, i) in summary.week">
- <td>{{data.y}}</td>
- <td>{{data.w}}</td>
- <td>{{data.in}}</td>
- <td>{{data.out}}</td>
- <td v-bind:class="{'net-negative': data.negative, 'net-positive':data.positive}">{{data.net}}
- </td>
- </tr>
- </table>
- </div>
- <div class="summary-panel">
- <h2>Monthly</h2>
- <table class="table">
- <tr>
- <th>Year</th>
- <th>Month</th>
- <th>In</th>
- <th>Out</th>
- <th>Net</th>
- </tr>
- <tr v-for="(data, i) in summary.month">
- <td>{{data.y}}</td>
- <td>{{data.m}}</td>
- <td>{{data.in}}</td>
- <td>{{data.out}}</td>
- <td v-bind:class="{'net-negative': data.negative, 'net-positive':data.positive}">{{data.net}}
- </td v-bind:class="{'net-negative': data.negative, 'net-positive':data.positive}">
- </tr>
- </table>
- </div>
- <div class="summary-panel">
- <h2>Yearly</h2>
- <table class="table">
- <tr>
- <th>Year</th>
- <th>In</th>
- <th>Out</th>
- <th>Net</th>
- </tr>
- <tr v-for="(data, i) in summary.year">
- <td>{{data.y}}</td>
- <td>{{data.in}}</td>
- <td>{{data.out}}</td>
- <td v-bind:class="{'net-negative': data.negative, 'net-positive':data.positive}">{{data.net}}
- </td v-bind:class="{'net-negative': data.negative, 'net-positive':data.positive}">
- </tr>
- </table>
- </div>
- </div>
-
- <!-- Goals -->
- <div v-if="activeTab == 'goals'">
- <div>
- <div class="newItem">
- <span>New Goal</span>
- <input v-model="ng.name" placeholder="Name">
- <input v-model="ng.total" placeholder="Total" type="number" step="0.01">
- <button v-on:click="post(ng, '/goals', 'goals')">Add</button>
- </div>
- <div class="newAllocation">
- <span>Allocate funds</span>
- <select v-model="na.name">
- <option v-for="option in goals" v-bind:value="option.name">
- {{ option.name }}
- </option>
- <input v-model="na.amount" type="number">
- <span>out of {{total_to_allocate}}</span>
- </select>
- <button v-on:click="post(na, '/allocate', 'goals')">Add</button>
- </div>
- <table>
- <tr><th></th><th>Name</th><th>Amount</th><th>Total</th><th>Remaining</th></tr>
- <tr v-for="(goal, i) in goals">
- <td class="table-index">{{i+1}}</td>
- <td>{{goal.name}}</td>
- <td>{{goal.amount}}</td>
- <td>{{goal.total}}</td>
- <td>{{goal.total - goal.amount}}</td>
- </tr>
- </table>
- </div>
- </div>
-
- <!-- Expected -->
- <div v-if="activeTab == 'expected'">
- <div>
- <div class="newItem">
- <span>New Expected</span>
- <input v-model="e.name" placeholder="Name">
- <input v-model="e.total" placeholder="Total" type="number" step="0.01">
- <input v-model="e.days" placeholder="Period (days)" type="number" step="1">
- <button v-on:click="post(e, '/expected', 'expected')">Add</button>
- </div>
- <table>
- <tr><th></th><th>Name</th><th>Total</th><th>Period</th></tr>
- <tr v-for="(e, i) in expected">
- <td class="table-index">{{i+1}}</td>
- <td>{{e.name}}</td>
- <td>{{e.total}}</td>
- <td>{{e.days}}</td>
- </tr>
- </table>
- </div>
- </div>
- </div>
-</body>
-
-</html> \ No newline at end of file
diff --git a/src/templates/ledger-edit.html b/src/templates/ledger-edit.html
new file mode 100644
index 0000000..2acc2c2
--- /dev/null
+++ b/src/templates/ledger-edit.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+ <title>{{name}}'s Budget</title>
+ <meta charset="UTF-8">
+ <link rel="stylesheet" type="text/css" href="static/styles.css">
+</head>
+
+<body>
+ <h1>{{name}}'s Budget</h1>
+ <div>
+ <a href="/ledger"><button class="bold">Ledger</button></a>
+ <a href="/summary"><button>Summary</button></a>
+ <a href="/goals"><button>Goals</button></a>
+ <a href="/expected"><button>Expected</button></a>
+ </div>
+ <form method="post" action="/transaction/{{item.id}}">
+ <input id="datePicker" name="when" placeholder="date" type="date">
+ <input name="where" placeholder="where" type="text" value="{{item.where}}">
+ <input name="amount" placeholder="amount" type="number" step="0.01" value="{{item.amount}}">
+ <input name="category" placeholder="category" type="text" value="{{item.category}}">
+ <input name="subcategory" placeholder="tags (csv)" type="text" value="{{item.subcategory}}">
+ <input type="submit" value="Save">
+ </form>
+ <script>
+ document.getElementById('datePicker').value = new Date().toLocaleDateString();
+ </script>
+</body>
+</html>
+
diff --git a/src/templates/ledger.html b/src/templates/ledger.html
index 0ac9b0e..f5c46de 100644
--- a/src/templates/ledger.html
+++ b/src/templates/ledger.html
@@ -5,6 +5,7 @@
<title>{{name}}'s Budget</title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="static/styles.css">
+ <script src="/static/main.js"></script>
</head>
<body>
@@ -31,6 +32,8 @@
<th>Amount</th>
<th>Category</th>
<th>Tags</th>
+ <th></th>
+ <th></th>
</tr>
{{#each ledger}}
<tr>
@@ -40,6 +43,8 @@
<td>{{this.amount}}</td>
<td>{{this.category}}</td>
<td>{{this.subcategory}}</td>
+ <td><a href="/ledger/edit/{{this.id}}"><button>&#9881;</button></a></td>
+ <td><button onclick="remove({{this.id}})">X</button></td>
</tr>
{{/each}}
</table>