From 0345f90a7baceae507f417abe30736cc95cdc0cf Mon Sep 17 00:00:00 2001 From: Mark Powers Date: Fri, 9 Oct 2020 22:42:56 -0500 Subject: Begin refactor to use server side templates --- src/index.html | 210 ------------------------------------------- src/index.js | 90 ++----------------- src/login.html | 49 ---------- src/main.js | 216 --------------------------------------------- src/models.js | 85 ++++++++++++++++++ src/server.js | 26 ++++-- src/static/main.js | 216 +++++++++++++++++++++++++++++++++++++++++++++ src/static/styles.css | 72 +++++++++++++++ src/styles.css | 72 --------------- src/templates.js | 27 ++++++ src/templates/index.html | 210 +++++++++++++++++++++++++++++++++++++++++++ src/templates/login.html | 49 ++++++++++ src/templates/summary.html | 45 ++++++++++ 13 files changed, 730 insertions(+), 637 deletions(-) delete mode 100644 src/index.html delete mode 100644 src/login.html delete mode 100644 src/main.js create mode 100644 src/models.js create mode 100644 src/static/main.js create mode 100644 src/static/styles.css delete mode 100644 src/styles.css create mode 100644 src/templates.js create mode 100644 src/templates/index.html create mode 100644 src/templates/login.html create mode 100644 src/templates/summary.html (limited to 'src') diff --git a/src/index.html b/src/index.html deleted file mode 100644 index cba242d..0000000 --- a/src/index.html +++ /dev/null @@ -1,210 +0,0 @@ - - - - - Budget - - - - - - - - - - -
-

{{summary.username}}'s budget

-
- - - - -
- - -
-
- - - - - - -
- - - - - - - - - - - - - - - - - - - - -
DateWhereAmountCategorySubcategory
{{i+1}}{{transaction.when.substring(0,10)}}{{transaction.where}}{{transaction.amount}}{{transaction.category}}{{transaction.subcategory}} - - -
-
- - - - - - - - - - - - - - - - - - - -
DateWhereAmountCategorySubcategory
- - - - - - - - - -
- - -
-
-

Weekly

- - - - - - - - - - - - - - - -
YearWeekInOutNet
{{data.y}}{{data.w}}{{data.in}}{{data.out}}{{data.net}} -
-
-
-

Monthly

- - - - - - - - - - - - - - - -
YearMonthInOutNet
{{data.y}}{{data.m}}{{data.in}}{{data.out}}{{data.net}} -
-
-
-

Yearly

- - - - - - - - - - - - - -
YearInOutNet
{{data.y}}{{data.in}}{{data.out}}{{data.net}} -
-
-
- - -
-
-
- New Goal - - - -
-
- Allocate funds - - out of {{total_to_allocate}} - - -
- - - - - - - - - -
NameAmountTotalRemaining
{{i+1}}{{goal.name}}{{goal.amount}}{{goal.total}}{{goal.total - goal.amount}}
-
-
- - -
-
-
- New Expected - - - - -
- - - - - - - - -
NameTotalPeriod
{{i+1}}{{e.name}}{{e.total}}{{e.days}}
-
-
-
- - - \ No newline at end of file diff --git a/src/index.js b/src/index.js index 0cbba31..8e18272 100644 --- a/src/index.js +++ b/src/index.js @@ -4,12 +4,13 @@ const fs = require('fs'); const path = require('path'); const jwt = require('jsonwebtoken'); - const config = JSON.parse(fs.readFileSync(path.join(__dirname, 'config.json'))); - const dbCreds = config.database; const secret = config.jwt_secret; +const models = require('./models'); +const templates = require('./templates'); + const jwtFunctions = { sign: function (message) { return jwt.sign({ value: message }, secret); @@ -44,90 +45,11 @@ async function sync(alter, force, callback) { await database.sync({ alter, force, logging: console.log }); } -function setUpModels() { - const models = { - "transaction": database.define('transaction', { - when: { - type: Sequelize.DATE, - allowNull: false, - }, - amount: { - type: Sequelize.DECIMAL, - allowNull: false, - }, - where: { - type: Sequelize.STRING, - allowNull: false, - }, - category: { - type: Sequelize.STRING, - allowNull: false, - }, - subcategory: { - type: Sequelize.STRING, - allowNull: false, - }, - username: { - type: Sequelize.STRING, - allowNull: false, - }, - }), - "goals": database.define('goal', { - username: { - type: Sequelize.STRING, - allowNull: false, - }, - name: { - type: Sequelize.STRING, - allowNull: false, - }, - total: { - type: Sequelize.DECIMAL, - allowNull: false, - }, - amount: { - type: Sequelize.DECIMAL, - allowNull: false, - } - }), - "expected": database.define('expected', { - username: { - type: Sequelize.STRING, - allowNull: false, - }, - name: { - type: Sequelize.STRING, - allowNull: false, - }, - total: { - type: Sequelize.DECIMAL, - allowNull: false, - }, - days: { - type: Sequelize.INTEGER, - allowNull: false, - } - }), - "users": database.define('user', { - username: { - type: Sequelize.STRING, - allowNull: false, - }, - password: { - type: Sequelize.STRING, - allowNull: false, - }, - salt: { - type: Sequelize.STRING, - allowNull: false, - },}), - } - return models; -} -const models = setUpModels(); sync(); -server.setUpRoutes(models, jwtFunctions, database); +server.setUpRoutes(models.setUpModels(database), + jwtFunctions, + database, templates.setUpTemplates()); server.listen(config.port); diff --git a/src/login.html b/src/login.html deleted file mode 100644 index ec41762..0000000 --- a/src/login.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - - Mark's Database - Login - - - - - - - -
-

Login

-
- - - -
-
- - - \ No newline at end of file diff --git a/src/main.js b/src/main.js deleted file mode 100644 index 0a381b0..0000000 --- a/src/main.js +++ /dev/null @@ -1,216 +0,0 @@ -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: { - - } - }); -} \ No newline at end of file diff --git a/src/models.js b/src/models.js new file mode 100644 index 0000000..23d2032 --- /dev/null +++ b/src/models.js @@ -0,0 +1,85 @@ +const Sequelize = require('sequelize'); +function setUpModels(database) { + const models = { + "transaction": database.define('transaction', { + when: { + type: Sequelize.DATE, + allowNull: false, + }, + amount: { + type: Sequelize.DECIMAL, + allowNull: false, + }, + where: { + type: Sequelize.STRING, + allowNull: false, + }, + category: { + type: Sequelize.STRING, + allowNull: false, + }, + subcategory: { + type: Sequelize.STRING, + allowNull: false, + }, + username: { + type: Sequelize.STRING, + allowNull: false, + }, + }), + "goals": database.define('goal', { + username: { + type: Sequelize.STRING, + allowNull: false, + }, + name: { + type: Sequelize.STRING, + allowNull: false, + }, + total: { + type: Sequelize.DECIMAL, + allowNull: false, + }, + amount: { + type: Sequelize.DECIMAL, + allowNull: false, + } + }), + "expected": database.define('expected', { + username: { + type: Sequelize.STRING, + allowNull: false, + }, + name: { + type: Sequelize.STRING, + allowNull: false, + }, + total: { + type: Sequelize.DECIMAL, + allowNull: false, + }, + days: { + type: Sequelize.INTEGER, + allowNull: false, + } + }), + "users": database.define('user', { + username: { + type: Sequelize.STRING, + allowNull: false, + }, + password: { + type: Sequelize.STRING, + allowNull: false, + }, + salt: { + type: Sequelize.STRING, + allowNull: false, + }, + }), + } + return models; +} +module.exports = { + setUpModels +} \ No newline at end of file diff --git a/src/server.js b/src/server.js index f72f21b..ffbc373 100644 --- a/src/server.js +++ b/src/server.js @@ -11,7 +11,7 @@ const config = JSON.parse(fs.readFileSync(path.join(__dirname, 'config.json'))); const server = express(); server.use(cookieParser()) server.use(bodyParser.json()); -//server.use(bodyParser.urlencoded({ extended: true })); +server.use(bodyParser.urlencoded({ extended: true })); function listen(port) { server.listen(port, () => console.info(`Listening: http://localhost:${port} `)); @@ -23,7 +23,7 @@ function hashWithSalt(password, salt){ return hash.digest("base64"); }; -function setUpRoutes(models, jwtFunctions, database) { +function setUpRoutes(models, jwtFunctions, database, templates) { // Authentication routine server.use(async function (req, res, next) { if (!req.path.toLowerCase().startsWith("/login")) { @@ -58,8 +58,19 @@ function setUpRoutes(models, jwtFunctions, database) { server.get('/', (req, res) => res.sendFile(__dirname + "/index.html")) server.get('/login', (req, res) => res.sendFile(__dirname + "/login.html")) - server.get('/styles.css', (req, res) => res.sendFile(__dirname + "/styles.css")) - server.get('/main.js', (req, res) => res.sendFile(__dirname + "/main.js")) + + server.get('/summary', async (req, res) => { + var ledger = await database.query("SELECT * FROM transactions WHERE username = '" + res.locals.user.username + "' ORDER BY `when` DESC", { type: database.QueryTypes.SELECT }) + ledger.forEach((element, i) => { + element.when = element.when.toString().substring(0, 10); + element.index = i+1 + }); + var name = res.locals.user.username + let body = templates["summary"]({ name, ledger }) + res.status(200).send(body) + }) + + server.use('/static', express.static(path.join(__dirname, '/static'))) server.post('/login', async (req, res, next) => { const user = await models.users.findOne({ where: { username: req.body.username} }) @@ -90,9 +101,11 @@ function setUpRoutes(models, jwtFunctions, database) { let item = req.body; console.log(item); item.username = res.locals.user.username + if(!item.when){ + item.when = new Date().toLocaleDateString(); + } await models.transaction.create(item); - var result = await database.query("SELECT * FROM transactions WHERE username = '" + res.locals.user.username + "' ORDER BY `when` DESC", { type: database.QueryTypes.SELECT }) - res.status(200).send(result); + res.redirect("/summary") } catch (e) { console.log(e); res.status(400).send(e.message); @@ -126,6 +139,7 @@ function setUpRoutes(models, jwtFunctions, database) { res.status(400).send(e.message); } }) + server.get(`/goals`, async (req, res, next) => { try { var result = await database.query("SELECT * FROM goals WHERE username = '" + res.locals.user.username + "' ORDER BY `name` DESC", { type: database.QueryTypes.SELECT }) diff --git a/src/static/main.js b/src/static/main.js new file mode 100644 index 0000000..0a381b0 --- /dev/null +++ b/src/static/main.js @@ -0,0 +1,216 @@ +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: { + + } + }); +} \ No newline at end of file diff --git a/src/static/styles.css b/src/static/styles.css new file mode 100644 index 0000000..4eedd0b --- /dev/null +++ b/src/static/styles.css @@ -0,0 +1,72 @@ +td { + border: 1px solid lightgrey; + min-width: 3em; +} + +table { + max-width: 100%; +} + +li { + cursor: pointer; + text-decoration: underline; +} + +tr:nth-child(2n+1) { + background-color: lightgray; +} +tr { + width: 100%; +} + +.bold { + font-weight: bold; +} + +#data { + width: 100%; +} + +.border { + border: 1px solid lightgrey; +} + +textarea { + border-radius: 4px; + width: 60%; + height: 10em; + display: block; +} + +pre { + white-space: pre-line; +} + +.net-negative { + /* color: red; */ + background-color: lightcoral; +} +.net-positive { + /* color: green; */ + background-color: lightgreen +} +.summary-panel { + float:left; + padding-right: 2em; +} +@media only screen and (max-width: 600px) { + .newItem td { + display:block; + } + .table-index { + display: none; + } + button { + font-size: 32px; + } + input { + font-size: 32px; + display: block; + width: 100%; + } +} \ No newline at end of file diff --git a/src/styles.css b/src/styles.css deleted file mode 100644 index 4eedd0b..0000000 --- a/src/styles.css +++ /dev/null @@ -1,72 +0,0 @@ -td { - border: 1px solid lightgrey; - min-width: 3em; -} - -table { - max-width: 100%; -} - -li { - cursor: pointer; - text-decoration: underline; -} - -tr:nth-child(2n+1) { - background-color: lightgray; -} -tr { - width: 100%; -} - -.bold { - font-weight: bold; -} - -#data { - width: 100%; -} - -.border { - border: 1px solid lightgrey; -} - -textarea { - border-radius: 4px; - width: 60%; - height: 10em; - display: block; -} - -pre { - white-space: pre-line; -} - -.net-negative { - /* color: red; */ - background-color: lightcoral; -} -.net-positive { - /* color: green; */ - background-color: lightgreen -} -.summary-panel { - float:left; - padding-right: 2em; -} -@media only screen and (max-width: 600px) { - .newItem td { - display:block; - } - .table-index { - display: none; - } - button { - font-size: 32px; - } - input { - font-size: 32px; - display: block; - width: 100%; - } -} \ No newline at end of file diff --git a/src/templates.js b/src/templates.js new file mode 100644 index 0000000..2c02905 --- /dev/null +++ b/src/templates.js @@ -0,0 +1,27 @@ +const fs = require('fs'); +const path = require('path'); +const handlebars = require("handlebars"); + +function setUpTemplates(){ + let templates = {}; + + { + const templateContent = fs.readFileSync(path.join(__dirname, 'templates/login.html')).toString() + templates["login"] = handlebars.compile(templateContent); + } + { + const templateContent = fs.readFileSync(path.join(__dirname, 'templates/index.html')).toString() + // templates["index"] = handlebars.compile(templateContent); + } + { + const templateContent = fs.readFileSync(path.join(__dirname, 'templates/summary.html')).toString() + templates["summary"] = handlebars.compile(templateContent); + } + + return templates +} + + +module.exports = { + setUpTemplates +}; diff --git a/src/templates/index.html b/src/templates/index.html new file mode 100644 index 0000000..525f8fe --- /dev/null +++ b/src/templates/index.html @@ -0,0 +1,210 @@ + + + + + Budget + + + + + + + + + + +
+

{{summary.username}}'s budget

+
+ + + + +
+ + +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
DateWhereAmountCategorySubcategory
{{i+1}}{{transaction.when.substring(0,10)}}{{transaction.where}}{{transaction.amount}}{{transaction.category}}{{transaction.subcategory}} + + +
+
+ + + + + + + + + + + + + + + + + + + +
DateWhereAmountCategorySubcategory
+ + + + + + + + + +
+ + +
+
+

Weekly

+ + + + + + + + + + + + + + + +
YearWeekInOutNet
{{data.y}}{{data.w}}{{data.in}}{{data.out}}{{data.net}} +
+
+
+

Monthly

+ + + + + + + + + + + + + + + +
YearMonthInOutNet
{{data.y}}{{data.m}}{{data.in}}{{data.out}}{{data.net}} +
+
+
+

Yearly

+ + + + + + + + + + + + + +
YearInOutNet
{{data.y}}{{data.in}}{{data.out}}{{data.net}} +
+
+
+ + +
+
+
+ New Goal + + + +
+
+ Allocate funds + + out of {{total_to_allocate}} + + +
+ + + + + + + + + +
NameAmountTotalRemaining
{{i+1}}{{goal.name}}{{goal.amount}}{{goal.total}}{{goal.total - goal.amount}}
+
+
+ + +
+
+
+ New Expected + + + + +
+ + + + + + + + +
NameTotalPeriod
{{i+1}}{{e.name}}{{e.total}}{{e.days}}
+
+
+
+ + + \ No newline at end of file diff --git a/src/templates/login.html b/src/templates/login.html new file mode 100644 index 0000000..ec41762 --- /dev/null +++ b/src/templates/login.html @@ -0,0 +1,49 @@ + + + + + Mark's Database - Login + + + + + + + +
+

Login

+
+ + + +
+
+ + + \ No newline at end of file diff --git a/src/templates/summary.html b/src/templates/summary.html new file mode 100644 index 0000000..5bf828e --- /dev/null +++ b/src/templates/summary.html @@ -0,0 +1,45 @@ + + + + + {{name}}'s Budget + + + + + +

{{name}}'s Budget

+
+ + + + + + +
+ + + + + + + + + + {{#each ledger}} + + + + + + + + + {{/each}} +
WhenWhereAmountCategoryTags
{{this.index}}{{this.when}}{{this.where}}{{this.amount}}{{this.category}}{{this.subcategory}}
+ + + + -- cgit v1.2.3