88 lines
3.2 KiB
HTML
88 lines
3.2 KiB
HTML
<!doctype html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>{% block title %}Finanzberatung{% endblock %}</title>
|
|
<link rel="stylesheet" href="/static/style.css">
|
|
<script src="/static/htmx.min.js"></script>
|
|
</head>
|
|
<body>
|
|
<nav>
|
|
<a href="/">Übersicht</a>
|
|
<a href="/import">Import</a>
|
|
<a href="/buchungen">Buchungen</a>
|
|
<a href="/salden">Salden</a>
|
|
<a href="/planung">Planung</a>
|
|
<a href="/szenarien">Szenarien</a>
|
|
<a href="/admin">Admin</a>
|
|
<a href="/hilfe">Hilfe</a>
|
|
<a href="http://{{ request.url.hostname or '127.0.0.1' }}:8097" target="_blank" rel="noopener">Grafana</a>
|
|
<form method="post" action="/logout">
|
|
<button type="submit">Logout</button>
|
|
</form>
|
|
</nav>
|
|
<main>
|
|
{% block content %}{% endblock %}
|
|
</main>
|
|
<script>
|
|
document.body.addEventListener('htmx:responseError', function (e) {
|
|
alert('Fehler: ' + (e.detail.xhr.responseText || e.detail.xhr.status));
|
|
});
|
|
|
|
// Minimale JSON-Kodierung für htmx-Formulare/Selects, die gegen die JSON-APIs
|
|
// (POST/PATCH /api/...) senden. Felder mit data-type="int" werden als Zahl
|
|
// kodiert, leere Werte als null, alles andere bleibt String.
|
|
htmx.defineExtension('json-form', {
|
|
onEvent: function (name, evt) {
|
|
if (name === 'htmx:configRequest') {
|
|
evt.detail.headers['Content-Type'] = 'application/json';
|
|
}
|
|
},
|
|
encodeParameters: function (xhr, parameters, elt) {
|
|
var out = {};
|
|
for (var k in parameters) {
|
|
var v = parameters[k];
|
|
var field = elt.matches && elt.matches('[name="' + k + '"]')
|
|
? elt
|
|
: elt.querySelector('[name="' + k + '"]');
|
|
var kind = field && field.dataset ? field.dataset.type : null;
|
|
if (v === '') {
|
|
out[k] = null;
|
|
} else if (kind === 'int') {
|
|
out[k] = parseInt(v, 10);
|
|
} else if (kind === 'amount') {
|
|
// Deutsche Betragseingabe: "1.234,56" -> "1234.56". Ohne Komma
|
|
// bleibt der Wert unveraendert (Punkt-Eingaben funktionieren weiter).
|
|
var s = String(v).replace(/[\s€]/g, '');
|
|
if (s.indexOf(',') !== -1) {
|
|
s = s.replace(/\./g, '').replace(',', '.');
|
|
}
|
|
out[k] = s;
|
|
} else {
|
|
out[k] = v;
|
|
}
|
|
}
|
|
// Checkboxen mit data-type="bool" werden unabhaengig vom parameters-Objekt
|
|
// ausgelesen, weil unmarkierte Checkboxen beim Serialisieren komplett
|
|
// fehlen wuerden (kein Feld => kein "false" moeglich).
|
|
if (elt.querySelectorAll) {
|
|
elt.querySelectorAll('[data-type="bool"]').forEach(function (field) {
|
|
out[field.name] = field.checked;
|
|
});
|
|
}
|
|
xhr.overrideMimeType('text/json');
|
|
return JSON.stringify(out);
|
|
}
|
|
});
|
|
|
|
function toggleEdit(prefix, id, editing) {
|
|
// UX-Regel: Zeilen werden versteckt (hidden), nie aus dem DOM entfernt.
|
|
document.getElementById(prefix + '-row-' + id).hidden = editing;
|
|
document.getElementById(prefix + '-edit-' + id).hidden = !editing;
|
|
}
|
|
</script>
|
|
<footer class="version">Finanzberatungs-Tool v{{ app_version }}</footer>
|
|
</body>
|
|
</html>
|