06/05 Optimize app
This commit is contained in:
@@ -197,6 +197,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Import progress bar (shown only during chunked import) #}
|
||||
<div id="import-progress-wrap" class="d-none mt-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1" style="font-size:12px;color:var(--muted);">
|
||||
<span id="import-progress-label">Importing…</span>
|
||||
<span id="import-progress-pct" class="mono fw-bold">0%</span>
|
||||
</div>
|
||||
<div class="progress" style="height:6px;border-radius:3px;">
|
||||
<div id="import-progress-bar" class="progress-bar bg-success" role="progressbar" style="width:0%;transition:width .3s ease;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Parse errors (non-fatal) #}
|
||||
<div id="parse-warnings" class="alert alert-warning d-none mb-3" style="font-size:12px;">
|
||||
<strong>Warnings</strong> — these rows were skipped:<br>
|
||||
@@ -580,31 +591,56 @@
|
||||
setImportLoading(true);
|
||||
$('import-error').classList.add('d-none');
|
||||
|
||||
fetch('{{ url_for("bank_import.confirm_import") }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': CSRF,
|
||||
},
|
||||
body: JSON.stringify({ account_id: parseInt(accountId), skip_dupes: skipDupes, rows }),
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
const CHUNK = 50;
|
||||
const useChunks = rows.length > CHUNK;
|
||||
let totalImported = 0, totalSkipped = 0;
|
||||
|
||||
function setProgress(done, total) {
|
||||
const pct = Math.round(done / total * 100);
|
||||
$('import-progress-bar').style.width = pct + '%';
|
||||
$('import-progress-pct').textContent = pct + '%';
|
||||
$('import-progress-label').textContent = `Importing… ${done} of ${total}`;
|
||||
}
|
||||
|
||||
async function sendChunks() {
|
||||
if (useChunks) {
|
||||
$('import-progress-wrap').classList.remove('d-none');
|
||||
setProgress(0, rows.length);
|
||||
}
|
||||
|
||||
const chunks = [];
|
||||
for (let i = 0; i < rows.length; i += CHUNK) chunks.push(rows.slice(i, i + CHUNK));
|
||||
let sent = 0;
|
||||
|
||||
for (const chunk of chunks) {
|
||||
const r = await fetch('{{ url_for("bank_import.confirm_import") }}', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'X-CSRFToken': CSRF },
|
||||
body: JSON.stringify({ account_id: parseInt(accountId), skip_dupes: skipDupes, rows: chunk }),
|
||||
});
|
||||
const data = await r.json();
|
||||
if (data.error) throw new Error(data.error);
|
||||
totalImported += data.imported;
|
||||
totalSkipped += data.skipped;
|
||||
sent += chunk.length;
|
||||
if (useChunks) setProgress(sent, rows.length);
|
||||
}
|
||||
}
|
||||
|
||||
sendChunks()
|
||||
.then(() => {
|
||||
setImportLoading(false);
|
||||
if (data.error) {
|
||||
$('import-error').textContent = data.error;
|
||||
$('import-error').classList.remove('d-none');
|
||||
return;
|
||||
}
|
||||
$('import-progress-wrap').classList.add('d-none');
|
||||
$('done-title').textContent =
|
||||
data.imported + ' transaction' + (data.imported !== 1 ? 's' : '') + ' imported successfully';
|
||||
totalImported + ' transaction' + (totalImported !== 1 ? 's' : '') + ' imported successfully';
|
||||
$('done-subtitle').textContent =
|
||||
data.skipped > 0 ? data.skipped + ' duplicate(s) skipped.' : 'All transactions were new.';
|
||||
totalSkipped > 0 ? totalSkipped + ' duplicate(s) skipped.' : 'All transactions were new.';
|
||||
showStep('step-done');
|
||||
})
|
||||
.catch(err => {
|
||||
setImportLoading(false);
|
||||
$('import-error').textContent = 'Request failed: ' + err;
|
||||
$('import-progress-wrap').classList.add('d-none');
|
||||
$('import-error').textContent = 'Import failed: ' + err;
|
||||
$('import-error').classList.remove('d-none');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user