all repos — piggy @ main

Dead simple finance manager in Go, HTML and JS.

static/js/common.js (view raw)

  1document.addEventListener('DOMContentLoaded', function () {
  2    // navbar
  3    const navObject = document.getElementsByTagName("nav")[0];
  4    for (const page of navPages) {
  5        const a = document.createElement("a");
  6        a.innerText = page.name;
  7        a.href = page.href;
  8        navObject.appendChild(a)
  9    }
 10    // favicon
 11    document.getElementsByTagName("head")[0].innerHTML += favicon;
 12});
 13
 14// Global constants
 15const navPages = [
 16    { name: "Home", href: "/" },
 17    { name: "Bookmakers", href: "/bookmakers" },
 18    { name: "Accounts", href: "/accounts" },
 19    { name: "Records", href: "/records" },
 20];
 21const favicon = `<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>💰</text></svg>">`;
 22
 23const currency = "€";
 24const locale = "it-IT";
 25
 26const flatpickrOptions = {
 27    enableTime: true,
 28    time_24hr: true,
 29    dateFormat: "Z",
 30};
 31
 32// Cell formatters
 33function formatValue(v) {
 34    if (v === undefined) return 0;
 35    return (v / 100).toFixed(2);
 36}
 37
 38function restoreValue(v) {
 39    return Number(v) * 100;
 40}
 41
 42function formatCash(v) {
 43    if (v === 0) return "-";
 44    return `${v > 0 ? "+" : ""}${formatValue(v)}${currency}`;
 45}
 46
 47function formatPercentage(v) {
 48    if (v === 0) return "-";
 49    return formatValue(v) + "%";
 50}
 51
 52function formatDate(dateString) {
 53    return (new Date(dateString)).toLocaleString(locale);
 54}
 55
 56function formatBoolean(value, id) {
 57    const input = document.createElement("input");
 58    input.type = "checkbox";
 59    if (value) input.setAttribute("checked", "");
 60    input.disabled = true;
 61    //input.setAttribute("data-id", id);
 62    //input.onchange = undefined;
 63    return input.outerHTML;
 64}
 65
 66// Input components
 67function newInputText(label, value, name) {
 68    const l = document.createElement("label");
 69    const input = document.createElement("input");
 70    input.className = name;
 71    input.type = "text";
 72    input.placeholder = label;
 73    input.value = value ?? "";
 74    l.innerHTML += label + "<br />";
 75    l.appendChild(input);
 76    return l;
 77}
 78
 79function newInputCheckbox(label, value, name) {
 80    const l = document.createElement("label");
 81    const input = document.createElement("input");
 82    input.className = name;
 83    input.type = "checkbox";
 84    if (value) input.setAttribute("checked", "");
 85    l.appendChild(input);
 86    l.innerHTML += label;
 87    return l;
 88}
 89
 90function newInputDate(label, value, name) {
 91    const l = document.createElement("label");
 92    const input = document.createElement("input");
 93    input.className = name;
 94    input.placeholder = label;
 95    input.value = value ?? "";
 96    flatpickr(input, flatpickrOptions);
 97    l.innerHTML += label + "<br />";
 98    l.appendChild(input);
 99    return l;
100}
101
102function newInputSelect(label, value, name, options) {
103    const l = document.createElement("label");
104    const input = document.createElement("select");
105    input.className = name;
106    input.placeholder = label;
107    for (const o of options) {
108        input.options.add(new Option(o.name, o.id));
109    }
110    input.value = value ?? "";
111    l.innerHTML += label + "<br />";
112    l.appendChild(input);
113    return l;
114}
115
116// Functions
117function getQueryStringID() {
118    return Number(new URLSearchParams(window.location.search).get("id") ?? 0);
119}
120
121async function myConfirm(f, id) {
122    if (confirm("Are you sure?")) return await f(id);
123    return false;
124}
125
126async function handleFetchResult(res) {
127    if (!res.ok) {
128        console.error(await res.text())
129        return
130    }
131
132    return await res.json();
133}
134
135async function myFetch(url) {
136    res = await fetch(url);
137    return await handleFetchResult(res);
138}
139
140async function myFetchPOST(url, body) {
141    const res = await fetch(url, {
142        method: 'POST',
143        headers: { 'Content-Type': 'application/json' },
144        body: body ? JSON.stringify(body) : undefined,
145    });
146    return await handleFetchResult(res);
147}
148
149async function myFetchDELETE(url) {
150    const res = await fetch(url, { method: 'DELETE' });
151    return await handleFetchResult(res);
152}
153
154// API calls
155async function getBookmakers() {
156    return await myFetch('/api/bookmakers');
157}
158
159async function getBookmaker(id) {
160    return await myFetch(`/api/bookmakers/${id}`);
161}
162
163async function saveBookmaker(payload) {
164    return await myFetchPOST("/api/bookmakers", payload);
165}
166
167async function deleteBookmaker(id) {
168    return await myFetchDELETE(`/api/bookmakers/${id}`);
169}
170
171
172async function getAccounts() {
173    return await myFetch('/api/accounts');
174}
175
176async function getAccount(id) {
177    return await myFetch(`/api/accounts/${id}`);
178}
179
180async function saveAccount(payload) {
181    return await myFetchPOST("/api/accounts", payload);
182}
183
184async function deleteAccount(id) {
185    return await myFetchDELETE(`/api/accounts/${id}`);
186}
187
188
189async function getRecords() {
190    return await myFetch('/api/records');
191}
192
193async function getRecord(id) {
194    return await myFetch(`/api/records/${id}`);
195}
196
197async function saveRecord(payload) {
198    return await myFetchPOST("/api/records", payload);
199}
200
201async function deleteRecord(id) {
202    return await myFetchDELETE(`/api/records/${id}`);
203}