feat: Kategorien-Verwaltung auf der Admin-Seite
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -32,6 +32,43 @@
|
||||
<p id="apply-rules-result"></p>
|
||||
</section>
|
||||
|
||||
<section class="admin-section">
|
||||
<h2>Kategorien</h2>
|
||||
<p class="muted">Kategorien für Buchungen, Fixposten und Szenario-Modifikatoren.
|
||||
Löschen ist bewusst nicht vorgesehen (Kategorien hängen an Buchungen und Regeln).</p>
|
||||
<table>
|
||||
<thead><tr><th>Name</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{% for c in categories %}
|
||||
<tr id="cat-row-{{ c.id }}">
|
||||
<td>{{ c.name }}</td>
|
||||
<td><button type="button" onclick="toggleEdit('cat', {{ c.id }}, true)">Bearbeiten</button></td>
|
||||
</tr>
|
||||
<tr id="cat-edit-{{ c.id }}" hidden>
|
||||
<td colspan="2">
|
||||
<form hx-ext="json-form" hx-patch="/api/categories/{{ c.id }}" hx-swap="none"
|
||||
hx-on::after-request="if(event.detail.successful){window.location.reload()}">
|
||||
<label>Name <input type="text" name="name" value="{{ c.name }}" required maxlength="100"></label>
|
||||
<button type="submit">Speichern</button>
|
||||
<button type="button" onclick="toggleEdit('cat', {{ c.id }}, false)">Abbrechen</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="2">Noch keine Kategorien.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<fieldset>
|
||||
<legend>Neue Kategorie anlegen</legend>
|
||||
<form hx-ext="json-form" hx-post="/api/categories" hx-swap="none"
|
||||
hx-on::after-request="if(event.detail.successful){window.location.reload()}">
|
||||
<label>Name <input type="text" name="name" required maxlength="100"></label>
|
||||
<button type="submit">Anlegen</button>
|
||||
</form>
|
||||
</fieldset>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
function handleApplyRulesResult(event) {
|
||||
var result = document.getElementById('apply-rules-result');
|
||||
|
||||
@@ -148,14 +148,14 @@
|
||||
<span class="muted">— die zur Art passenden Felder werden aktiv, gesperrte Felder sind ausgegraut.</span>
|
||||
</div>
|
||||
<label>Ziel-Typ
|
||||
<select name="target_type" data-type="str" onchange="onModTargetTypeChange(this)" title="Für diese Eintragsart nicht relevant">
|
||||
<select name="target_type" data-type="str" onchange="onModTargetTypeChange(this)">
|
||||
<option value="category">Kategorie</option>
|
||||
<option value="recurring">Wiederkehrender Posten</option>
|
||||
</select>
|
||||
</label>
|
||||
<span class="mod-target-category">
|
||||
<label>Kategorie
|
||||
<select name="target_id" data-type="int" title="Für diese Eintragsart nicht relevant">
|
||||
<select name="target_id" data-type="int">
|
||||
{% for c in categories %}<option value="{{ c.id }}">{{ c.name }}</option>{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
@@ -167,7 +167,7 @@
|
||||
</select>
|
||||
</label>
|
||||
</span>
|
||||
<label><span class="value-label">Prozentsatz (%)</span> <input type="text" name="value" data-type="amount" value="0" title="Für diese Eintragsart nicht relevant"></label>
|
||||
<label><span class="value-label">Prozentsatz (%)</span> <input type="text" name="value" data-type="amount" value="0"></label>
|
||||
<label>Endet am <input type="date" name="end_date" disabled title="Für diese Eintragsart nicht relevant"></label>
|
||||
<label>Name <input type="text" name="name" disabled title="Für diese Eintragsart nicht relevant"></label>
|
||||
<label>Betrag <input type="text" name="amount" data-type="amount" placeholder="5000,00" disabled title="Für diese Eintragsart nicht relevant"></label>
|
||||
@@ -222,7 +222,11 @@
|
||||
catSpan.style.display = isCategory ? '' : 'none';
|
||||
recSpan.style.display = isCategory ? 'none' : '';
|
||||
catSelect.disabled = !isCategory;
|
||||
if (catSelect.disabled) { catSelect.title = 'Für diese Eintragsart nicht relevant'; }
|
||||
else { catSelect.removeAttribute('title'); }
|
||||
recSelect.disabled = isCategory;
|
||||
if (recSelect.disabled) { recSelect.title = 'Für diese Eintragsart nicht relevant'; }
|
||||
else { recSelect.removeAttribute('title'); }
|
||||
}
|
||||
|
||||
function onEntryArtChange(select) {
|
||||
@@ -234,7 +238,11 @@
|
||||
var isMod = art !== 'einmal';
|
||||
function setDisabled(name, disabled) {
|
||||
var el = form.querySelector('[name="' + name + '"]');
|
||||
if (el) { el.disabled = disabled; }
|
||||
if (el) {
|
||||
el.disabled = disabled;
|
||||
if (disabled) { el.title = 'Für diese Eintragsart nicht relevant'; }
|
||||
else { el.removeAttribute('title'); }
|
||||
}
|
||||
}
|
||||
setDisabled('value', !(art === 'percent' || art === 'absolute'));
|
||||
var valueLabel = form.querySelector('.value-label');
|
||||
@@ -249,11 +257,21 @@
|
||||
var typeSelect = form.querySelector('[name="target_type"]');
|
||||
if (typeSelect) {
|
||||
typeSelect.disabled = !isMod;
|
||||
if (!isMod) { typeSelect.title = 'Für diese Eintragsart nicht relevant'; }
|
||||
else { typeSelect.removeAttribute('title'); }
|
||||
var isCategory = typeSelect.value === 'category';
|
||||
var catSelect = form.querySelector('.mod-target-category select');
|
||||
var recSelect = form.querySelector('.mod-target-recurring select');
|
||||
if (catSelect) { catSelect.disabled = !isMod || !isCategory; }
|
||||
if (recSelect) { recSelect.disabled = !isMod || isCategory; }
|
||||
if (catSelect) {
|
||||
catSelect.disabled = !isMod || !isCategory;
|
||||
if (catSelect.disabled) { catSelect.title = 'Für diese Eintragsart nicht relevant'; }
|
||||
else { catSelect.removeAttribute('title'); }
|
||||
}
|
||||
if (recSelect) {
|
||||
recSelect.disabled = !isMod || isCategory;
|
||||
if (recSelect.disabled) { recSelect.title = 'Für diese Eintragsart nicht relevant'; }
|
||||
else { recSelect.removeAttribute('title'); }
|
||||
}
|
||||
}
|
||||
// POST-Ziel nur beim Anlege-Formular wechseln (Edit-Formulare patchen fix).
|
||||
if (form.hasAttribute('data-planned-url')) {
|
||||
|
||||
Reference in New Issue
Block a user