commit 4a996f58a04d7397e64e09b83b7deaab1c23f667 Author: macniel Date: Fri May 1 17:03:30 2026 +0200 Dateien nach "/" hochladen diff --git a/Screenshot From 2026-05-01 16-59-52.png b/Screenshot From 2026-05-01 16-59-52.png new file mode 100644 index 0000000..c20bdbf Binary files /dev/null and b/Screenshot From 2026-05-01 16-59-52.png differ diff --git a/index.js b/index.js new file mode 100644 index 0000000..b5a98ff --- /dev/null +++ b/index.js @@ -0,0 +1,349 @@ +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(); \ No newline at end of file diff --git a/project.json b/project.json new file mode 100644 index 0000000..6f5956f --- /dev/null +++ b/project.json @@ -0,0 +1,47 @@ +[ + { + "name": "Current", + "entries": [ + { + "date": 1704067200000, + "subject": "(Savings) Initial Deposit", + "targetAccount": "Savings", + "amount": -500 + }, + { + "date": 1704067200000, + "subject": "Salary", + "amount": 2182.15000000000009 + }, + { + "date": 1704153600000, + "subject": "Rent", + "amount": -800 + }, + { + "date": 1704240000000, + "subject": "Groceries", + "amount": -24.19 + }, + { + "date": 1704240000000, + "subject": "Toiletries", + "amount": -12.95 + } + ] + }, + { + "name": "Savings", + "entries": [ + { + "date": 1704067200000, + "subject": "Initial Deposit", + "amount": 500 + } + ] + }, + { + "name": "Portfolio", + "entries": [] + } +] \ No newline at end of file