349 lines
10 KiB
JavaScript
349 lines
10 KiB
JavaScript
var gui = require('gui');
|
|
var fs = require('fs');
|
|
|
|
var rh = 14;
|
|
var sp = 4;
|
|
var y = sp;
|
|
|
|
|
|
let projectData = [
|
|
{
|
|
name: "Current",
|
|
entries: [
|
|
{
|
|
date: 1704067200000,
|
|
subject: '(Savings) Initial Deposit',
|
|
amount: -500,
|
|
targetAccount: "Savings"
|
|
},
|
|
{
|
|
date: 1704067200000,
|
|
subject: 'Salary',
|
|
amount: 2182.15
|
|
},
|
|
{
|
|
date: 1704153600000,
|
|
subject: 'Rent',
|
|
amount: -800
|
|
},
|
|
{
|
|
date: 1704240000000,
|
|
subject: 'Groceries',
|
|
amount: -24.19
|
|
}
|
|
],
|
|
},
|
|
{
|
|
name: "Savings",
|
|
entries: [
|
|
{
|
|
date: 1704067200000,
|
|
subject: 'Initial Deposit',
|
|
amount: 500
|
|
}
|
|
],
|
|
}
|
|
|
|
];
|
|
|
|
function getAccounts(data) {
|
|
const resultData = [];
|
|
for (let i = 0; i < data.length; i++) {
|
|
resultData.push(data[i].name);
|
|
}
|
|
return resultData
|
|
}
|
|
|
|
function toListViewEntries(entries, visibleCols) {
|
|
let cols = visibleCols;
|
|
return entries.map(e => {
|
|
const amount = parseFloat(e.amount);
|
|
const subject = ("" + e.subject).substr(0, cols - 20).padEnd(cols - 20, ' ');
|
|
const sum = ((amount >= 0 ? "+" : "") + amount.toFixed(2)).substr(0, 10).padStart(10, ' ');
|
|
const d = new Date(e.date);
|
|
const date = d.getDay().toString().padStart(2, '0') + "-" + (d.getMonth() + 1).toString().padStart(2, '0') + "-" + d.getFullYear();
|
|
let s = (date.substr(0, 10)) + " " + subject + " " + sum;
|
|
return s;
|
|
});
|
|
}
|
|
|
|
function getSummation(entries) {
|
|
return entries.reduce((sum, e) => sum + parseFloat(e.amount), 0);
|
|
}
|
|
|
|
function toDateByComponents(s) {
|
|
const parts = s.split("-");
|
|
if (parts.length !== 3) return null;
|
|
const day = parseInt(parts[0]);
|
|
const month = parseInt(parts[1]) - 1;
|
|
const year = parseInt(parts[2]);
|
|
if (isNaN(day) || isNaN(month) || isNaN(year)) return null;
|
|
return new Date(year, month, day).getTime();
|
|
}
|
|
|
|
function subWindow(data) {
|
|
let gadgets = [];
|
|
let removable = false;
|
|
|
|
const innerData = {
|
|
date: Date.now(),
|
|
subject: '',
|
|
amount: 0,
|
|
targetAccount: null
|
|
}
|
|
|
|
if (data) {
|
|
innerData.date = data.date;
|
|
innerData.subject = data.subject;
|
|
innerData.amount = data.amount;
|
|
innerData.targetAccount = data.targetAccount;
|
|
removable = true;
|
|
}
|
|
|
|
let d = new Date(innerData.date);
|
|
let formattedDate = d.getDay() + "-" + (d.getMonth() + 1) + "-" + d.getFullYear();
|
|
|
|
gadgets.push({
|
|
kind: 'string', id: 1, label: 'Subject:',
|
|
left: sp + 80, top: y, width: 400 - sp - 80 - sp, height: rh,
|
|
value: innerData.subject
|
|
});
|
|
gadgets.push({
|
|
kind: 'string', id: 2, label: 'Amount:',
|
|
left: sp + 80, top: sp + rh + sp, width: 100, height: rh,
|
|
value: innerData.amount.toFixed(2)
|
|
});
|
|
gadgets.push({
|
|
kind: 'string', id: 3, label: 'Date:',
|
|
left: sp + 80, top: sp + rh + sp + rh + sp, width: 150, height: rh,
|
|
value: formattedDate
|
|
});
|
|
gadgets.push({ kind: 'checkbox', id: 6, label: "Account:", left: sp + 80, top: sp + rh + sp + rh + sp + rh + sp + 2, width: rh, height: rh, value: 0 });
|
|
|
|
const accounts = getAccounts(projectData);
|
|
|
|
gadgets.push({ kind: 'cycle', id: 7, left: sp + 160 + rh + sp, top: sp + rh + sp + rh + sp + rh + sp, width: 120, height: rh, items: accounts, value: 0 });
|
|
|
|
gadgets.push({ kind: 'button', id: 4, left: sp + 80, top: sp + rh + sp + rh + sp + rh + sp + rh + sp, width: 80, label: "Save", height: rh });
|
|
gadgets.push({ kind: 'button', id: 5, left: sp + 80 + sp + 80, top: sp + rh + sp + rh + sp + rh + sp + rh + sp, width: 80, label: "Remove", height: rh });
|
|
|
|
let win = gui.createWindow({
|
|
title: 'Entry Details',
|
|
width: 400,
|
|
height: sp + rh + sp + rh + sp + rh + sp + rh + sp + rh + sp,
|
|
left: 30,
|
|
top: 30,
|
|
gadgets: gadgets
|
|
});
|
|
|
|
if (innerData.targetAccount) {
|
|
gui.set(win, 6, 1);
|
|
let i = -1;
|
|
for (i = 0; i < accounts.length; i++) {
|
|
if (accounts[i] === innerData.targetAccount) {
|
|
break;
|
|
}
|
|
}
|
|
gui.set(win, 7, i);
|
|
gui.setDisabled(win, 7, false);
|
|
} else {
|
|
gui.setDisabled(win, 7, true);
|
|
}
|
|
|
|
gui.setDisabled(win, 5, !removable);
|
|
|
|
while (true) {
|
|
var evt = gui.waitEvent(win);
|
|
if (!evt) continue;
|
|
|
|
if (evt.type === 'close') {
|
|
gui.closeWindow(win);
|
|
return null;
|
|
}
|
|
if (evt.type === 'gadgetup') {
|
|
if (evt.id === 6) {
|
|
gui.setDisabled(win, 7, !gui.get(win, 6));
|
|
}
|
|
if (evt.id === 4) {
|
|
innerData.subject = gui.get(win, 1);
|
|
innerData.amount = parseFloat(gui.get(win, 2));
|
|
innerData.date = toDateByComponents(gui.get(win, 3));
|
|
|
|
if (gui.get(win, 6)) {
|
|
const targetAccountIndex = gui.get(win, 7);
|
|
if (targetAccountIndex !== null && targetAccountIndex !== undefined) {
|
|
innerData.targetAccount = data[targetAccountIndex].name;
|
|
}
|
|
}
|
|
|
|
gui.closeWindow(win);
|
|
return innerData;
|
|
}
|
|
if (evt.id === 5) {
|
|
gui.closeWindow(win);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function mainWindow() {
|
|
|
|
let gadgets = [];
|
|
|
|
const cycleId = 1;
|
|
const listViewId = 2;
|
|
const balanceId = 3;
|
|
const addButtonId = 4;
|
|
|
|
cols = 56;
|
|
|
|
gadgets.push({
|
|
kind: 'cycle', id: cycleId,
|
|
left: sp, top: y, width: 120, height: rh, border: false,
|
|
items: getAccounts(projectData), value: 0
|
|
});
|
|
|
|
gadgets.push({ kind: 'button', id: addButtonId, left: 500 - 80 - sp, top: y, width: 80, label: "+Transfer", height: rh });
|
|
|
|
gadgets.push({
|
|
kind: 'listview', id: listViewId, left: sp, top: y + rh + sp, width: 500 - sp - sp, height: 186,
|
|
flex: true,
|
|
items: toListViewEntries(projectData[0].entries, cols), value: 0
|
|
});
|
|
|
|
gadgets.push({
|
|
kind: 'text', id: balanceId, label: 'Balance:',
|
|
left: 100, top: y + rh + sp + 186, width: 400 - sp, height: rh,
|
|
value: getSummation(projectData[0].entries).toFixed(2)
|
|
});
|
|
|
|
|
|
|
|
var win = gui.createWindow({
|
|
title: 'Budget',
|
|
width: 500,
|
|
height: 14 + 4 + 4 + 200 + 4,
|
|
left: 20,
|
|
top: 15,
|
|
gadgets: gadgets
|
|
});
|
|
|
|
/* Set up menu bar */
|
|
gui.setMenu(win, [
|
|
{
|
|
title: 'Project',
|
|
items: [
|
|
{ label: 'New...', id: 101, key: 'N' },
|
|
{ label: 'Open...', id: 102, key: 'O' },
|
|
{ label: 'Save...', id: 103, key: 'S' },
|
|
{ label: '---' },
|
|
{ label: 'Quit', id: 199, key: 'Q' }
|
|
]
|
|
},
|
|
]);
|
|
|
|
let currentAccountIndex = 0;
|
|
|
|
function updateView() {
|
|
gui.set(win, listViewId, toListViewEntries(projectData[currentAccountIndex].entries, cols));
|
|
gui.set(win, balanceId, getSummation(projectData[currentAccountIndex].entries).toFixed(2));
|
|
}
|
|
|
|
function doNew() {
|
|
projectData = [
|
|
{
|
|
name: "Current",
|
|
entries: []
|
|
}
|
|
];
|
|
|
|
|
|
|
|
currentAccountIndex = 0;
|
|
gui.set(win, cycleId, getAccounts(projectData));
|
|
gui.set(win, cycleId, 0);
|
|
updateView();
|
|
}
|
|
|
|
function doOpen() {
|
|
var r = gui.fileRequest({ title: 'Open File', pattern: '#?.json' });
|
|
if (r) {
|
|
projectData = JSON.parse(fs.readFileSync(r.file));
|
|
}
|
|
}
|
|
|
|
function doSave() {
|
|
var r = gui.fileRequest({ title: 'Save File', pattern: '#?.json', save: true });
|
|
if (r) {
|
|
fs.writeFileSync(r.file, JSON.stringify(projectData));
|
|
}
|
|
}
|
|
|
|
while (true) {
|
|
var evt = gui.waitEvent(win);
|
|
if (!evt) continue;
|
|
|
|
if (evt.type === 'close') break;
|
|
|
|
if (evt.type === 'menu') {
|
|
if (evt.id === 101) doNew();
|
|
else if (evt.id === 102) doOpen();
|
|
else if (evt.id === 103) doSave();
|
|
else if (evt.id === 199) break;
|
|
}
|
|
|
|
if (evt.type === 'gadgetup') {
|
|
|
|
if (evt.id === cycleId) {
|
|
currentAccountIndex = evt.code;
|
|
updateView();
|
|
}
|
|
|
|
if (evt.id === listViewId) {
|
|
const entry = subWindow(projectData[currentAccountIndex].entries[evt.code]);
|
|
if (entry != null) {
|
|
if (entry === -1) {
|
|
projectData[currentAccountIndex].entries.splice(evt.code, 1);
|
|
} else {
|
|
projectData[currentAccountIndex].entries[evt.code] = entry;
|
|
}
|
|
if (entry.targetAccount) {
|
|
const targetAccount = projectData.find(d => d.name === entry.targetAccount);
|
|
if (targetAccount) {
|
|
targetAccount.entries.push({
|
|
date: entry.date,
|
|
subject: "(" + projectData[currentAccountIndex].name + ")" + entry.subject,
|
|
amount: -entry.amount
|
|
});
|
|
}
|
|
}
|
|
updateView();
|
|
}
|
|
}
|
|
if (evt.id === addButtonId) {
|
|
const entry = subWindow();
|
|
if (entry != null) {
|
|
projectData[currentAccountIndex].entries.push(entry);
|
|
|
|
if (entry.targetAccount) {
|
|
const targetAccount = projectData.find(d => d.name === entry.targetAccount);
|
|
if (targetAccount) {
|
|
targetAccount.entries.push({
|
|
date: entry.date,
|
|
subject: "(" + projectData[currentAccountIndex].name + ")" + entry.subject,
|
|
amount: -entry.amount
|
|
});
|
|
}
|
|
}
|
|
updateView();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
gui.closeWindow(win);
|
|
}
|
|
|
|
mainWindow(); |