html head title Actual Importer body h1 Import transactions form#prepareForm p label(for="file") CSV File: input(type="file" name="file" required) p label(for="profile") Profile: select(name="profile" required) each profile in profiles option(value=profile selected=(defaultProfile === profile))= profile p label(for="server") Server: select(name="server" required) each server in servers option(value=server selected=(defaultServer === server))= server button(type="submit") Load div#prepare div#result script. const form = document.querySelector("#prepareForm"); function renderTable(transactions) { const div = document.createElement('div'); div.innerHTML = `

Pending transactions (${transactions.length})

`; const table = document.createElement('table'); const thead = document.createElement('thead'); const tr = document.createElement('tr'); tr.innerHTML = ` # Import ID Kind Date From To Amount Title `; thead.appendChild(tr); table.appendChild(thead); const tbody = document.createElement('tbody'); transactions.forEach((transaction, index) => { const row = document.createElement('tr'); row.innerHTML = ` ${index+1} ${transaction.id} ${transaction.kind} ${transaction.date} ${transaction.from} ${transaction.to} ${transaction.amount} ${transaction.title} `; tbody.appendChild(row); }); table.appendChild(tbody); div.appendChild(table); return div; } function renderSkipped(skipped) { const div = document.createElement('div'); div.innerHTML = `

Skipped CSV rows

The ::: is CSV field delimiter

${skipped.map(d => d.join(" ::: ")).join("\n")}
` return div; } function renderResult(result) { const resultWrapper = document.querySelector('#result'); resultWrapper.innerHTML = `

Import status

${(result.errors?.length ?? 0) > 0 ? `

Errors:

` : ""}

`; } async function submitTransactions(config, transactions, opts) { const filtered = transactions.filter((transaction, index) => !!document.querySelector(`#transaction${index}`).checked); if (filtered.length === 0) { return; } try { const response = await fetch("/submit", { method: "POST", headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ server: config.get('server'), transactions: filtered, opts }), }); const result = await response.json(); renderResult(result); } catch (e) { console.error(e); } } function renderPrepare(config, data) { document.querySelector('#result').innerHTML = ''; const prepare = document.querySelector("#prepare"); prepare.innerHTML = ''; const title = document.createElement("h2"); title.innerHTML = "Prepare transactions to submit"; prepare.appendChild(title); prepare.appendChild(renderTable(data.transactions)); prepare.appendChild(renderSkipped(data.skipped)); const addForm = document.createElement("form"); addForm.innerHTML = `
Add

`; addForm.addEventListener("submit", async (e) => { e.preventDefault(); const options = new FormData(addForm); const opts = { mode: 'add', learnCategories: !!options.get('learn'), runTransfers: !!options.get('transfers') }; await submitTransactions(config, data.transactions, opts); prepare.innerHTML = ''; }); const importForm = document.createElement("form"); importForm.innerHTML = `
Import
`; importForm.addEventListener("submit", async (e) => { e.preventDefault(); await submitTransactions(config, data.transactions, { mode: 'import' }); prepare.innerHTML = ''; }); if (data.transactions.length > 0) { prepare.appendChild(addForm); prepare.appendChild(importForm); } else { const message = document.createElement('p'); message.innerHTML = ` No transactions to import `; prepare.appendChild(message); } } async function handlePrepare(e) { e.preventDefault(); try { const config = new FormData(form); const response = await fetch("/prepare", { method: "POST", body: config, }); const data = await response.json(); renderPrepare(config, data); } catch (e) { console.error(e); } } form.addEventListener("submit", handlePrepare);