feat: Import-Seite mit Drag-and-Drop und Planungs-Seite

Ersetzt die Platzhalter-Routen aus Task 12 durch echte GUI-Seiten: /import
mit Drop-Zone (Fetch-Upload), gepollter Importliste (Status-Badges,
aufklappbarer Vorschau samt Saldo-/Duplikat-Anzeige, Uebernehmen/Verwerfen)
und /planung mit den vier Abschnitten Fixposten (inkl. Vorschlaege),
Einmalposten, Kredite (Tilgungsplan-Aufklapper) und Szenarien
(Kredit-Zuordnung, Modifikatoren, Durchrechnen mit Ergebnisanzeige).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 19:05:58 +02:00
parent 3847398055
commit afbbcbe9a5
8 changed files with 681 additions and 15 deletions

View File

@@ -2,5 +2,68 @@
{% block title %}Import Finanzberatung{% endblock %}
{% block content %}
<h1>Import</h1>
<p>Der Kontoauszug-Import (Upload, Vorschau, Bestätigen) folgt in einem späteren Ausbauschritt.</p>
<div id="dropzone">
Kontoauszug-PDF hier ablegen oder klicken zum Hochladen
<input type="file" id="file-input" accept="application/pdf" style="display:none">
</div>
<p id="upload-message"></p>
<h2>Importe</h2>
<div id="imports" hx-get="/import/list" hx-trigger="every 5s, refresh" hx-swap="innerHTML">
{% include "_import_list.html" %}
</div>
<script>
(function () {
var dropzone = document.getElementById('dropzone');
var fileInput = document.getElementById('file-input');
var message = document.getElementById('upload-message');
function refreshImports() {
htmx.trigger(document.getElementById('imports'), 'refresh');
}
function uploadFile(file) {
if (!file) return;
var fd = new FormData();
fd.append('file', file);
message.textContent = 'Lade hoch: ' + file.name + ' …';
fetch('/api/imports/upload', { method: 'POST', body: fd })
.then(function (resp) {
return resp.json().then(function (data) { return { ok: resp.ok, data: data }; });
})
.then(function (result) {
if (result.ok) {
message.textContent = 'Hochgeladen: ' + file.name + ' Status: ' + result.data.status;
} else {
message.textContent = 'Fehler beim Hochladen: ' + (result.data.detail || 'unbekannter Fehler');
}
refreshImports();
})
.catch(function (err) {
message.textContent = 'Fehler beim Hochladen: ' + err;
});
}
dropzone.addEventListener('click', function () { fileInput.click(); });
fileInput.addEventListener('change', function () {
uploadFile(fileInput.files[0]);
fileInput.value = '';
});
dropzone.addEventListener('dragover', function (e) {
e.preventDefault();
dropzone.classList.add('dragover');
});
dropzone.addEventListener('dragleave', function () {
dropzone.classList.remove('dragover');
});
dropzone.addEventListener('drop', function (e) {
e.preventDefault();
dropzone.classList.remove('dragover');
var file = e.dataTransfer.files && e.dataTransfer.files[0];
uploadFile(file);
});
})();
</script>
{% endblock %}