Sprint C: Listen-Familie konsolidiert (Notes/Expenses/Health), SW by-v1104
Neue zentrale CSS-Datei lists.css (~280 Zeilen) mit Listen-Komponenten: - .list-shell, .list-filter-bar, .list-search-wrap - .list-group-header - .list-item-card + Modifier: --clickable, --milestone, --inactive - .list-item-date-col + sub-elements (für Diary-Style) - .list-item-meta-badge mit --meta-color (für Expenses/Health Icons) - .list-item-body, .list-item-title, .list-item-text, .list-item-meta-row - .list-item-chips + .list-item-chip mit --chip-color - .list-item-micro-badges + .list-item-micro-badge - .list-item-thumb (+ .list-item-thumb-count Overlay) - .list-item-amount (+ --positive/--negative/--neutral) - .list-item-actions + .list-item-action-btn (+ --danger) - .list-reminders-banner + .list-reminder-item (+ --urgent/--warning/--success) - .list-fab (FAB mit safe-area-inset) MIGRATIONEN: notes.js — 10+ Klassen ersetzt: - .notes-card → .list-item-card list-item-card--clickable - .notes-rubrik-chip → .list-item-chip mit --chip-color - .notes-card-meta → .list-item-meta-row - .notes-action-btn → .list-item-action-btn - .notes-group-label → .list-group-header - Notes-spezifische Klassen als Modifier behalten (vertikales Layout, pre-wrap text, Top-Zeile mit Actions rechts oben) - Alte CSS-Definitionen im Inline-<style> als TODO markiert expenses.js — komplette Item-Card-Migration: - .exp-entry → .list-item-card list-item-card--clickable - .exp-entry-icon-badge mit --kat-color → .list-item-meta-badge --meta-color - .exp-entry-betrag → .list-item-amount list-item-amount--negative - .exp-entry-del → .list-item-action-btn list-item-action-btn--danger - .exp-recurring-card--inaktiv → .list-item-card--inactive - .exp-fab → .list-fab - UI.moneyInput + UI.parseMoney in beide Forms integriert (€-Prefix, Komma-Dezimal) - Hero-Card + Statistik/Kacheln behalten (spezifisch) health.js — 9 Card-Renderings migriert: - Impfungen/Tierarzt/Gewicht/Läufigkeit/Medikamente/Allergien/ Dokumente/Tierarztpraxis/Befunde - .health-card → .list-item-card list-item-card--clickable - Health-Ampel parallel behalten (.health-card-ampel + Linie links) - Reminder-Banner: .health-reminder-* → .list-reminders-banner + .list-reminder-item--urgent/--warning - Gewicht-Wert: .list-item-amount für kg-Anzeigen - Form-Modals + KI-Buttons + Transponder-Chip unangetastet (anderer Scope) Tests 19/19 grün. Kein visueller Diff erwartet — Modifier-Klassen bewahren spezifische Layouts.
This commit is contained in:
parent
1de39536af
commit
9a066cb24c
9 changed files with 484 additions and 157 deletions
|
|
@ -87,7 +87,7 @@ window.Page_expenses = (() => {
|
|||
</div>
|
||||
${_dogSelectorHtml()}
|
||||
<div id="exp-content"></div>
|
||||
<button class="exp-fab" id="exp-fab" title="Neue Ausgabe">
|
||||
<button class="list-fab" id="exp-fab" title="Neue Ausgabe">
|
||||
${UI.icon('plus')}
|
||||
</button>
|
||||
`;
|
||||
|
|
@ -283,28 +283,28 @@ window.Page_expenses = (() => {
|
|||
const datum = new Date(e.datum + 'T00:00:00')
|
||||
.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit' });
|
||||
const dogBadge = e.dog_name
|
||||
? `<span class="exp-dog-badge">${UI.icon('paw-print')} ${_esc(e.dog_name)}</span>`
|
||||
? `<span>${UI.icon('paw-print')} ${_esc(e.dog_name)}</span>`
|
||||
: '';
|
||||
const notiz = e.notiz
|
||||
? `<span class="exp-entry-notiz">${_esc(e.notiz)}</span>`
|
||||
? `<div class="list-item-text">${_esc(e.notiz)}</div>`
|
||||
: '';
|
||||
return `
|
||||
<div class="exp-entry" data-id="${e.id}">
|
||||
<div class="exp-entry-icon-badge" style="--kat-color:${k.color}">
|
||||
<div class="list-item-card list-item-card--clickable exp-entry" data-id="${e.id}">
|
||||
<div class="list-item-meta-badge" style="--meta-color:${k.color}">
|
||||
${UI.icon(k.icon)}
|
||||
</div>
|
||||
<div class="exp-entry-body">
|
||||
<div class="exp-entry-head">
|
||||
<span class="exp-entry-datum">${datum}</span>
|
||||
<span class="exp-entry-kat">${k.label}</span>
|
||||
${dogBadge}
|
||||
</div>
|
||||
<div class="list-item-body">
|
||||
<div class="list-item-title">${k.label}</div>
|
||||
${notiz}
|
||||
<div class="list-item-meta-row">
|
||||
<span>${datum}</span>
|
||||
${dogBadge ? `· ${dogBadge}` : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div class="exp-entry-right">
|
||||
<div class="exp-entry-betrag">${_fmt(e.betrag)}</div>
|
||||
<button class="exp-entry-del" data-del="${e.id}" title="Löschen"
|
||||
aria-label="Eintrag löschen">
|
||||
<div class="list-item-amount list-item-amount--negative">${_fmt(e.betrag)}</div>
|
||||
<div class="list-item-actions">
|
||||
<button class="list-item-action-btn list-item-action-btn--danger exp-entry-del"
|
||||
data-del="${e.id}" title="Löschen" aria-label="Eintrag löschen">
|
||||
${UI.icon('trash')}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -313,15 +313,15 @@ window.Page_expenses = (() => {
|
|||
|
||||
return `
|
||||
<div class="exp-month-group">
|
||||
<div class="exp-month-header">
|
||||
<span class="exp-month-title">${titel}</span>
|
||||
<span class="exp-month-summe">${_fmt(summe)}</span>
|
||||
<div class="list-group-header" style="display:flex;justify-content:space-between;align-items:baseline">
|
||||
<span>${titel}</span>
|
||||
<span style="text-transform:none;font-weight:700;color:var(--c-text)">${_fmt(summe)}</span>
|
||||
</div>
|
||||
${rows}
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
el.innerHTML = `<div class="exp-list">${html}</div><div style="height:80px"></div>`;
|
||||
el.innerHTML = `<div class="list-shell">${html}</div><div style="height:80px"></div>`;
|
||||
|
||||
// Klick auf Zeile → Bearbeiten (nur wenn nicht Löschen-Button)
|
||||
el.querySelectorAll('.exp-entry').forEach(row => {
|
||||
|
|
@ -372,30 +372,26 @@ window.Page_expenses = (() => {
|
|||
.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' })
|
||||
: '—';
|
||||
return `
|
||||
<div class="exp-recurring-card${r.aktiv ? '' : ' exp-recurring-card--inaktiv'}" data-rid="${r.id}">
|
||||
<div class="exp-entry-icon-badge" style="--kat-color:${k.color}">${UI.icon(k.icon)}</div>
|
||||
<div class="exp-entry-body">
|
||||
<div class="exp-entry-head">
|
||||
<span class="exp-entry-kat">${k.label}</span>
|
||||
<span class="exp-recurring-freq">${HAEUFIGKEIT_LABEL[r.haeufigkeit] || r.haeufigkeit}</span>
|
||||
${r.dog_name ? `<span class="exp-dog-badge">${UI.icon('paw-print')} ${_esc(r.dog_name)}</span>` : ''}
|
||||
</div>
|
||||
${r.notiz ? `<div class="exp-entry-notiz">${_esc(r.notiz)}</div>` : ''}
|
||||
<div class="exp-recurring-next">
|
||||
${UI.icon('calendar')} Nächste Buchung: <strong>${naechste}</strong>
|
||||
${!r.aktiv ? '<span class="exp-badge-inaktiv">Pausiert</span>' : ''}
|
||||
<div class="list-item-card${r.aktiv ? '' : ' list-item-card--inactive'}" data-rid="${r.id}">
|
||||
<div class="list-item-meta-badge" style="--meta-color:${k.color}">${UI.icon(k.icon)}</div>
|
||||
<div class="list-item-body">
|
||||
<div class="list-item-title">${k.label}</div>
|
||||
${r.notiz ? `<div class="list-item-text">${_esc(r.notiz)}</div>` : ''}
|
||||
<div class="list-item-meta-row">
|
||||
<span>${HAEUFIGKEIT_LABEL[r.haeufigkeit] || r.haeufigkeit}</span>
|
||||
· <span>${UI.icon('calendar')} ${naechste}</span>
|
||||
${r.dog_name ? `· <span>${UI.icon('paw-print')} ${_esc(r.dog_name)}</span>` : ''}
|
||||
${!r.aktiv ? '· <span>Pausiert</span>' : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div class="exp-entry-right">
|
||||
<div class="exp-entry-betrag">${_fmt(r.betrag)}</div>
|
||||
<div style="display:flex;gap:var(--space-1);margin-top:var(--space-1)">
|
||||
<button class="exp-icon-btn exp-recurring-toggle" data-rid="${r.id}" data-aktiv="${r.aktiv}"
|
||||
title="${r.aktiv ? 'Pausieren' : 'Aktivieren'}">
|
||||
${UI.icon(r.aktiv ? 'pause' : 'play')}
|
||||
</button>
|
||||
<button class="exp-icon-btn exp-icon-btn--danger exp-recurring-del" data-rid="${r.id}"
|
||||
title="Löschen">${UI.icon('trash')}</button>
|
||||
</div>
|
||||
<div class="list-item-amount list-item-amount--negative">${_fmt(r.betrag)}</div>
|
||||
<div class="list-item-actions">
|
||||
<button class="list-item-action-btn exp-recurring-toggle" data-rid="${r.id}" data-aktiv="${r.aktiv}"
|
||||
title="${r.aktiv ? 'Pausieren' : 'Aktivieren'}">
|
||||
${UI.icon(r.aktiv ? 'pause' : 'play')}
|
||||
</button>
|
||||
<button class="list-item-action-btn list-item-action-btn--danger exp-recurring-del" data-rid="${r.id}"
|
||||
title="Löschen">${UI.icon('trash')}</button>
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
|
@ -407,7 +403,7 @@ window.Page_expenses = (() => {
|
|||
</button>
|
||||
</div>
|
||||
${recurring.length
|
||||
? `<div class="exp-list">${cards}</div>`
|
||||
? `<div class="list-shell">${cards}</div>`
|
||||
: UI.emptyState({ icon: UI.icon('arrows-clockwise'),
|
||||
title: 'Keine Daueraufträge',
|
||||
text: 'Erfasse regelmäßige Ausgaben wie Hundesteuer oder Versicherung.' })}
|
||||
|
|
@ -458,9 +454,8 @@ window.Page_expenses = (() => {
|
|||
<select class="form-control" name="kategorie">${katOptions}</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Betrag (€)</label>
|
||||
<input class="form-control" type="number" name="betrag" step="0.01" min="0.01"
|
||||
value="${r?.betrag || ''}" placeholder="0,00" required>
|
||||
<label class="form-label">Betrag</label>
|
||||
${UI.moneyInput({ name: 'betrag', value: r?.betrag ?? '', required: true })}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Häufigkeit</label>
|
||||
|
|
@ -501,7 +496,7 @@ window.Page_expenses = (() => {
|
|||
const fd = UI.formData(e.target);
|
||||
const payload = {
|
||||
kategorie: fd.kategorie,
|
||||
betrag: parseFloat(fd.betrag),
|
||||
betrag: UI.parseMoney(fd.betrag),
|
||||
haeufigkeit: fd.haeufigkeit,
|
||||
startdatum: fd.startdatum,
|
||||
notiz: fd.notiz || null,
|
||||
|
|
@ -710,12 +705,7 @@ window.Page_expenses = (() => {
|
|||
<div class="grid-2">
|
||||
<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>
|
||||
${UI.moneyInput({ name: 'betrag', value: entry?.betrag ?? '', required: true })}
|
||||
</div>
|
||||
<div class="form-group" style="margin-bottom:0">
|
||||
<label class="form-label">Datum</label>
|
||||
|
|
@ -810,7 +800,7 @@ window.Page_expenses = (() => {
|
|||
const fd = UI.formData(ev.target);
|
||||
const payload = {
|
||||
kategorie: fd.kategorie,
|
||||
betrag: parseFloat(fd.betrag),
|
||||
betrag: UI.parseMoney(fd.betrag),
|
||||
datum: fd.datum,
|
||||
notiz: fd.notiz || null,
|
||||
dog_id: fd.dog_id ? parseInt(fd.dog_id) : null,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue