222 lines
6.6 KiB
Plaintext
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); |