Feature: Ausgaben-Formular redesigned — Kategorie-Kacheln, €-Prefix, Wiederholungs-Toggle, SW by-v607

This commit is contained in:
rene 2026-05-02 10:58:47 +02:00
parent c96e98917c
commit dfd68f2a07
4 changed files with 173 additions and 40 deletions

View file

@ -641,64 +641,106 @@ window.Page_expenses = (() => {
const isEdit = !!entry;
const today = new Date().toISOString().split('T')[0];
const formId = 'exp-form';
const selKat = entry?.kategorie || 'sonstiges';
const dogOptions = (_appState.dogs || []).map(d =>
`<option value="${d.id}"${entry?.dog_id === d.id ? ' selected' : ''}>${_esc(d.name)}</option>`
).join('');
const katOptions = KATEGORIEN.map(k =>
`<option value="${k.id}"${(entry?.kategorie || 'sonstiges') === k.id ? ' selected' : ''}>
${k.label}
</option>`
).join('');
// Kategorie-Kacheln statt Dropdown
const katKacheln = KATEGORIEN.map(k => `
<label class="exp-kat-tile${selKat === k.id ? ' exp-kat-tile--sel' : ''}" data-kat="${k.id}">
<input type="radio" name="kategorie" value="${k.id}" ${selKat === k.id ? 'checked' : ''} style="display:none">
<span class="exp-kat-tile-icon" style="color:${k.color}">${UI.icon(k.icon)}</span>
<span class="exp-kat-tile-label">${k.label}</span>
</label>`).join('');
const body = `
<form id="${formId}">
<div class="form-group">
<label class="form-label">Datum</label>
<input type="date" name="datum" class="form-input"
value="${entry?.datum || today}" required>
</div>
<form id="${formId}" autocomplete="off">
<div class="form-group">
<label class="form-label">Kategorie</label>
<select name="kategorie" class="form-input" required>
${katOptions}
</select>
<div class="exp-kat-grid">${katKacheln}</div>
</div>
<div class="form-group">
<label class="form-label">Betrag ()</label>
<input type="number" name="betrag" class="form-input"
value="${entry?.betrag || ''}"
min="0.01" step="0.01" placeholder="0,00" required>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3)">
<div class="form-group" style="margin-bottom:0">
<label class="form-label">Betrag</label>
<div class="exp-betrag-wrap">
<span class="exp-betrag-prefix"></span>
<input type="number" name="betrag" class="form-control exp-betrag-input"
value="${entry?.betrag || ''}" min="0.01" step="0.01"
placeholder="0,00" required>
</div>
</div>
<div class="form-group" style="margin-bottom:0">
<label class="form-label">Datum</label>
<input type="date" name="datum" class="form-control"
value="${entry?.datum || today}" required>
</div>
</div>
${dogOptions ? `
<div class="form-group">
<label class="form-label">Hund (optional)</label>
<select name="dog_id" class="form-input">
<option value=""> kein Hund zugeordnet </option>
${dogOptions}
<label class="form-label">Hund <span class="form-label-hint">(optional)</span></label>
<select name="dog_id" class="form-control">
<option value=""> kein Hund </option>${dogOptions}
</select>
</div>` : ''}
<div class="form-group">
<label class="form-label">Notiz (optional)</label>
<input type="text" name="notiz" class="form-input"
<label class="form-label">Notiz <span class="form-label-hint">(optional)</span></label>
<input type="text" name="notiz" class="form-control"
value="${_esc(entry?.notiz || '')}"
placeholder="z. B. Impfung, Trockenfutter Vorrat …">
placeholder="z.B. Hundesteuer 2026, Allianz Haftpflicht …">
</div>
${!isEdit ? `
<div class="exp-repeat-section">
<label class="exp-repeat-toggle">
<input type="checkbox" id="exp-wiederholen" name="wiederholen">
<span class="exp-repeat-toggle-box"></span>
<span>${UI.icon('arrows-clockwise')} Automatisch wiederholen</span>
</label>
<div id="exp-repeat-opts" style="display:none;margin-top:var(--space-3)">
<select name="haeufigkeit" class="form-control">
<option value="monatlich">Monatlich</option>
<option value="quartalsweise">Quartalsweise (alle 3 Monate)</option>
<option value="jaehrlich" selected>Jährlich</option>
</select>
<p style="font-size:var(--text-xs);color:var(--c-text-muted);margin:var(--space-2) 0 0">
Der Betrag wird automatisch zum Fälligkeitstermin eingetragen.
</p>
</div>
</div>` : ''}
</form>`;
const footer = isEdit ? `
<button type="button" class="btn btn-danger" id="exp-delete-btn">Löschen</button>
<button type="submit" form="${formId}" class="btn btn-primary">Speichern</button>
` : `
<button type="button" class="btn btn-ghost btn-sm" id="exp-delete-btn"
style="color:var(--c-danger);margin-right:auto">
${UI.icon('trash')}
</button>
<button type="button" class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
<button type="submit" form="${formId}" class="btn btn-primary">Speichern</button>
` : `
<button type="button" class="btn btn-secondary flex-1" onclick="UI.modal.close()">Abbrechen</button>
<button type="submit" form="${formId}" class="btn btn-primary flex-1">Speichern</button>
`;
const modal = UI.modal.open({
title: isEdit ? 'Ausgabe bearbeiten' : 'Neue Ausgabe',
body,
footer,
const modal = UI.modal.open({ title: isEdit ? 'Ausgabe bearbeiten' : 'Neue Ausgabe', body, footer });
// Kategorie-Kacheln interaktiv
modal.querySelectorAll('.exp-kat-tile').forEach(tile => {
tile.addEventListener('click', () => {
modal.querySelectorAll('.exp-kat-tile').forEach(t => t.classList.remove('exp-kat-tile--sel'));
tile.classList.add('exp-kat-tile--sel');
});
});
// Wiederholen-Toggle
modal.querySelector('#exp-wiederholen')?.addEventListener('change', e => {
modal.querySelector('#exp-repeat-opts').style.display = e.target.checked ? 'block' : 'none';
});
if (isEdit) {
@ -718,8 +760,8 @@ window.Page_expenses = (() => {
modal.querySelector(`#${formId}`)?.addEventListener('submit', async (ev) => {
ev.preventDefault();
const fd = UI.formData(ev.target);
const body = {
const fd = UI.formData(ev.target);
const payload = {
kategorie: fd.kategorie,
betrag: parseFloat(fd.betrag),
datum: fd.datum,
@ -729,11 +771,21 @@ window.Page_expenses = (() => {
try {
if (isEdit) {
await API.patch(`/expenses/${entry.id}`, body);
await API.patch(`/expenses/${entry.id}`, payload);
UI.toast.success('Ausgabe aktualisiert.');
} else {
await API.post('/expenses', body);
UI.toast.success('Ausgabe gespeichert.');
await API.post('/expenses', payload);
// Auch als Dauerauftrag anlegen wenn gewünscht
if (fd.wiederholen) {
await API.post('/expenses/recurring', {
...payload,
haeufigkeit: fd.haeufigkeit || 'jaehrlich',
startdatum: fd.datum,
});
UI.toast.success('Ausgabe + Dauerauftrag gespeichert.');
} else {
UI.toast.success('Ausgabe gespeichert.');
}
}
UI.modal.close();
_invalidateCache();