Files
actual-importer/views/index.pug

222 lines
6.6 KiB
Plaintext

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 = `
<h3>Pending transactions (${transactions.length})</h3>
`;
const table = document.createElement('table');
const thead = document.createElement('thead');
const tr = document.createElement('tr');
tr.innerHTML = `
<th>#</th>
<th>Import</th>
<th>ID</th>
<th>Kind</th>
<th>Date</th>
<th>From</th>
<th>To</th>
<th>Amount</th>
<th>Title</th>
`;
thead.appendChild(tr);
table.appendChild(thead);
const tbody = document.createElement('tbody');
transactions.forEach((transaction, index) => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${index+1}</td>
<td><input type="checkbox" id="transaction${index}" checked/></td>
<td>${transaction.id}</td>
<td>${transaction.kind}</td>
<td>${transaction.date}</td>
<td>${transaction.from}</td>
<td>${transaction.to}</td>
<td>${transaction.amount}</td>
<td>${transaction.title}</td>
`;
tbody.appendChild(row);
});
table.appendChild(tbody);
div.appendChild(table);
return div;
}
function renderSkipped(skipped) {
const div = document.createElement('div');
div.innerHTML = `
<h3>Skipped CSV rows</h3>
<p>The <tt>:::</tt> is CSV field delimiter</p>
<pre>${skipped.map(d => d.join(" ::: ")).join("\n")}</pre>
`
return div;
}
function renderResult(result) {
const resultWrapper = document.querySelector('#result');
resultWrapper.innerHTML = `
<h2>Import status</h3>
<p>
<ul>
<li>Added: <strong>${result.added.length}</strong></li>
<li>Updated: <strong>${result.updated.length}</strong></li>
<li>Errors: <strong>${result?.errors?.length ?? 0}</strong></li>
</ul>
${(result.errors?.length ?? 0) > 0
? `<p>Errors: <ul>${result.errors?.map(e => `<li>${e.message}</li>`)}</ul></p>`
: ""}
</p>
`;
}
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 = `
<fieldset>
<legend>Add</legend>
<p>
<label for="learn">Learn categories</label>
<input type="checkbox" name="learn" />
</p>
<p>
<label for="transfers">Run transfers</label>
<input type="checkbox" name="transfers" />
</p>
<button type="submit">Add</button>
</fieldset>
`;
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 = `
<fieldset>
<legend>Import</legend>
<button type="submit">Import</button>
</fieldset>
`;
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 = `
<strong>No transactions to import</strong>
`;
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);