Feature: Ausgaben-Formular redesigned — Kategorie-Kacheln, €-Prefix, Wiederholungs-Toggle, SW by-v607
This commit is contained in:
parent
c96e98917c
commit
dfd68f2a07
4 changed files with 173 additions and 40 deletions
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue