aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Powers <markppowers0@gmail.com>2020-10-07 08:45:32 -0500
committerMark Powers <markppowers0@gmail.com>2020-10-07 08:45:32 -0500
commit98da11b940c194f5e4bd423cb57d252df6185715 (patch)
treea6b55f19fe08be5cc68033fee2b72c53ac269170
parent0712bdcc1e9c67bdde0a89c94ca5d72822ead854 (diff)
Add budget goals
-rw-r--r--package-lock.json5
-rw-r--r--package.json3
-rw-r--r--src/index.html35
-rw-r--r--src/index.js18
-rw-r--r--src/login.html15
-rw-r--r--src/main.js22
-rw-r--r--src/server.js41
7 files changed, 131 insertions, 8 deletions
diff --git a/package-lock.json b/package-lock.json
index 8d36346..ef7d304 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -656,6 +656,11 @@
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
+ "uplot": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/uplot/-/uplot-1.1.0.tgz",
+ "integrity": "sha512-xyl01BMw7xWNRa0d1MkCexJZ1bkQsh2TxWg5RjNWysvL9CRABsGonPVQo6jvYL1gz6TzfHADXWBzqZElnM1w1g=="
+ },
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
diff --git a/package.json b/package.json
index 4bc399b..1d1cbdc 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
"jsonwebtoken": "^8.5.1",
"mysql2": "^1.6.5",
"readline-sync": "^1.4.10",
- "sequelize": "^5.19.5"
+ "sequelize": "^5.19.5",
+ "uplot": "^1.1.0"
}
}
diff --git a/src/index.html b/src/index.html
index ef86835..08c5007 100644
--- a/src/index.html
+++ b/src/index.html
@@ -18,6 +18,7 @@
<div>
<button v-bind:class="{ bold: activeTab == 0 }" v-on:click="setTab(0)">Ledger</button>
<button v-bind:class="{ bold: activeTab == 1 }" v-on:click="setTab(1)">Summary</button>
+ <button v-bind:class="{ bold: activeTab == 2 }" v-on:click="setTab(2)">Goals</button>
</div>
<!-- Ledger -->
@@ -64,7 +65,6 @@
<th>Amount</th>
<th>Category</th>
<th>Subcategory</th>
-
</tr>
<tr>
<td></td>
@@ -148,6 +148,39 @@
</table>
</div>
</div>
+
+ <!-- Goals -->
+ <div v-if="activeTab == 2">
+ <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')">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')">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>
</div>
</body>
diff --git a/src/index.js b/src/index.js
index f783076..bf64586 100644
--- a/src/index.js
+++ b/src/index.js
@@ -72,6 +72,24 @@ function setUpModels() {
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,
+ }
+ }),
"users": database.define('user', {
username: {
type: Sequelize.STRING,
diff --git a/src/login.html b/src/login.html
index d1dbe1d..ec41762 100644
--- a/src/login.html
+++ b/src/login.html
@@ -7,9 +7,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" type="text/css" href="/css/styles.css">
<script>
+ let usernameEl = document.getElementById('username');
+ let passwordEl = document.getElementById('password');
function sendPost(){
- let username = document.getElementById('username').value;
- let password = document.getElementById('password').value;
+ let username = usernameEl.value;
+ let password = passwordEl.value;
fetch(new Request("/login", {
method: 'POST',
headers: {
@@ -23,6 +25,13 @@
window.location = "/";
});
}
+ let onEnterEvent = function(event) {
+ if (event.keyCode === 13) {
+ sendPost()
+ }
+ }
+ usernameEl.addEventListener("keyup", onEnterEvent);
+ passwordEl.addEventListener("keyup", onEnterEvent);
</script>
</head>
@@ -30,7 +39,7 @@
<div>
<h1>Login</h1>
<div class="form">
- <input type="text" placeholder="Enter Username" name="username" id="username" required>
+ <input on type="text" placeholder="Enter Username" name="username" id="username" required>
<input type="password" placeholder="Enter Password" name="password" id="password" required>
<button onclick="sendPost()">Log in</button>
</div>
diff --git a/src/main.js b/src/main.js
index b5e6b86..78b918e 100644
--- a/src/main.js
+++ b/src/main.js
@@ -6,6 +6,8 @@ window.onload = function () {
transactions: [],
summary: {username : ""},
selTodoType: "all",
+ total_to_allocate: 0,
+ goals: []
},
methods: {
setTab: function (value) {
@@ -26,6 +28,15 @@ window.onload = function () {
category: "",
subcategory: "",
}
+ this.ng = {
+ name: "",
+ total: "",
+ amount: 0
+ }
+ this.na = {
+ selected: "",
+ amount: ""
+ }
},
requestThenUpdate: function (request) {
fetch(request)
@@ -81,6 +92,8 @@ window.onload = function () {
},
created() {
this.clearData();
+ 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())
@@ -160,6 +173,12 @@ window.onload = function () {
// 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){
@@ -173,6 +192,9 @@ window.onload = function () {
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
});
diff --git a/src/server.js b/src/server.js
index 53db084..69d3968 100644
--- a/src/server.js
+++ b/src/server.js
@@ -14,7 +14,7 @@ server.use(bodyParser.json());
//server.use(bodyParser.urlencoded({ extended: true }));
function listen(port) {
- server.listen(port, () => console.info(`Listening on port ${port}!`));
+ server.listen(port, () => console.info(`Listening: http://localhost:${port} `));
}
function hashWithSalt(password, salt){
@@ -36,7 +36,6 @@ function setUpRoutes(models, jwtFunctions, database) {
try {
const decryptedUserId = jwtFunctions.verify(cookie);
var user = await models.users.findOne({ where: { username: decryptedUserId } });
- // .then((user, error) => {
if (user) {
res.locals.user = user.get({ plain: true });
} else {
@@ -44,7 +43,6 @@ function setUpRoutes(models, jwtFunctions, database) {
res.redirect('/login');
return;
}
- // });
} catch (e) {
res.status(400).send(e.message);
}
@@ -128,6 +126,43 @@ 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 })
+ res.status(200).send(result);
+ next();
+ } catch (e) {
+ console.log(e)
+ res.status(400).send(e.message);
+ }
+ })
+ server.post(`/goals`, async (req, res, next) => {
+ try {
+ let item = req.body;
+ console.log(item);
+ item.username = res.locals.user.username
+ await models.goals.create(item);
+ var result = await database.query("SELECT * FROM goals WHERE username = '" + res.locals.user.username + "' ORDER BY `name` DESC", { type: database.QueryTypes.SELECT })
+ res.status(200).send(result);
+ } catch (e) {
+ console.log(e);
+ res.status(400).send(e.message);
+ }
+ })
+ server.post(`/allocate`, async (req, res, next) => {
+ try {
+ let name = req.body.name;
+ let amount = req.body.amount;
+ var toUpdate = await models.goals.findOne({ where: { name: name, username:res.locals.user.username } });
+ var update = {amount: toUpdate.amount + amount}
+ await toUpdate.update(update);
+ var result = await await database.query("SELECT * FROM goals WHERE username = '" + res.locals.user.username + "' ORDER BY `name` DESC", { type: database.QueryTypes.SELECT })
+ res.status(200).send(result);
+ } catch (e) {
+ console.log(e);
+ res.status(400).send(e.message);
+ }
+ })
server.get(`/summary`, async (req, res, next) => {
try {
res.status(200).send({