Feature: Welpenwachstum — SVG-Kurve, Stats-Zeile, Veränderung-Spalte im Gewichtsverlauf — SW by-v487, APP_VER 464
This commit is contained in:
parent
dc737d0c48
commit
e507f4c086
3 changed files with 82 additions and 7 deletions
|
|
@ -3,7 +3,7 @@
|
|||
Router, State-Management, Navigation, Initialisierung.
|
||||
============================================================ */
|
||||
|
||||
const APP_VER = '463'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
const APP_VER = '464'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
|
||||
const App = (() => {
|
||||
|
||||
|
|
|
|||
|
|
@ -465,20 +465,95 @@ window.Page_litters = (() => {
|
|||
el.innerHTML = `<p style="color:var(--c-text-muted);font-size:var(--text-sm)">Noch keine Messungen eingetragen.</p>`;
|
||||
return;
|
||||
}
|
||||
|
||||
// Chronologisch für Chart (API liefert DESC)
|
||||
const asc = [...weights].reverse();
|
||||
const first = asc[0].gewicht_g;
|
||||
const last = asc[asc.length - 1].gewicht_g;
|
||||
const gain = last - first;
|
||||
const days = asc.length > 1
|
||||
? Math.max(1, (new Date(asc[asc.length-1].gemessen_am) - new Date(asc[0].gemessen_am)) / 86400000)
|
||||
: 1;
|
||||
const dailyGain = asc.length > 1 ? (gain / days).toFixed(1) : '—';
|
||||
|
||||
// SVG Sparkline
|
||||
const W = 400, H = 80;
|
||||
const minW = Math.min(...asc.map(w => w.gewicht_g));
|
||||
const maxW = Math.max(...asc.map(w => w.gewicht_g));
|
||||
const range = maxW - minW || 1;
|
||||
const pts = asc.map((w, i) => {
|
||||
const x = asc.length === 1 ? W/2 : (i / (asc.length - 1)) * W;
|
||||
const y = H - 8 - ((w.gewicht_g - minW) / range) * (H - 16);
|
||||
return `${x.toFixed(1)},${y.toFixed(1)}`;
|
||||
}).join(' ');
|
||||
const firstDate = asc[0].gemessen_am?.slice(5) || '';
|
||||
const lastDate = asc[asc.length-1].gemessen_am?.slice(5) || '';
|
||||
|
||||
el.innerHTML = `
|
||||
<!-- Stats-Zeile -->
|
||||
<div style="display:flex;gap:var(--space-3);flex-wrap:wrap;margin-bottom:var(--space-3)">
|
||||
<div style="text-align:center">
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted)">Aktuell</div>
|
||||
<div style="font-size:var(--text-base);font-weight:700;color:var(--c-primary)">${last} g</div>
|
||||
</div>
|
||||
<div style="text-align:center">
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted)">Zunahme</div>
|
||||
<div style="font-size:var(--text-base);font-weight:700;color:${gain >= 0 ? 'var(--c-success)' : 'var(--c-danger)'}">
|
||||
${gain >= 0 ? '+' : ''}${gain} g
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align:center">
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted)">Ø tägl.</div>
|
||||
<div style="font-size:var(--text-base);font-weight:700;color:var(--c-text)">${dailyGain} g</div>
|
||||
</div>
|
||||
<div style="text-align:center">
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted)">Messungen</div>
|
||||
<div style="font-size:var(--text-base);font-weight:700;color:var(--c-text)">${weights.length}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Wachstumskurve -->
|
||||
${asc.length > 1 ? `
|
||||
<div style="background:var(--c-surface);border-radius:var(--radius-md);
|
||||
padding:var(--space-2) var(--space-2) var(--space-1);margin-bottom:var(--space-3)">
|
||||
<svg viewBox="0 0 ${W} ${H}" style="width:100%;height:80px;display:block" preserveAspectRatio="none">
|
||||
<polyline points="${pts}" fill="none" stroke="var(--c-primary)"
|
||||
stroke-width="2" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<!-- Punkte -->
|
||||
${asc.map((w, i) => {
|
||||
const x = (i / (asc.length - 1)) * W;
|
||||
const y = H - 8 - ((w.gewicht_g - minW) / range) * (H - 16);
|
||||
return `<circle cx="${x.toFixed(1)}" cy="${y.toFixed(1)}" r="3"
|
||||
fill="var(--c-primary)" stroke="var(--c-bg)" stroke-width="1.5"/>`;
|
||||
}).join('')}
|
||||
</svg>
|
||||
<div style="display:flex;justify-content:space-between;font-size:10px;color:var(--c-text-muted);margin-top:2px">
|
||||
<span>${firstDate}</span><span>${lastDate}</span>
|
||||
</div>
|
||||
</div>` : ''}
|
||||
|
||||
<!-- Tabelle -->
|
||||
<table style="width:100%;font-size:var(--text-sm);border-collapse:collapse">
|
||||
<thead>
|
||||
<tr style="color:var(--c-text-secondary);font-size:var(--text-xs)">
|
||||
<th style="text-align:left;padding:var(--space-1) var(--space-2)">Datum</th>
|
||||
<th style="text-align:right;padding:var(--space-1) var(--space-2)">Gewicht</th>
|
||||
<th style="text-align:right;padding:var(--space-1) var(--space-2)">Veränderung</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${weights.map((w, i) => `
|
||||
<tr style="border-top:1px solid var(--c-border)${i === 0 ? ';font-weight:var(--weight-semibold)' : ''}">
|
||||
<td style="padding:var(--space-1) var(--space-2)">${_fmtDate(w.gemessen_am)}</td>
|
||||
<td style="padding:var(--space-1) var(--space-2);text-align:right">${w.gewicht_g} g</td>
|
||||
</tr>`).join('')}
|
||||
${weights.map((w, i) => {
|
||||
const prev = weights[i + 1];
|
||||
const diff = prev ? w.gewicht_g - prev.gewicht_g : null;
|
||||
const diffStr = diff === null ? '—'
|
||||
: `<span style="color:${diff >= 0 ? 'var(--c-success)' : 'var(--c-danger)'}">${diff >= 0 ? '+' : ''}${diff} g</span>`;
|
||||
return `
|
||||
<tr style="border-top:1px solid var(--c-border)${i === 0 ? ';font-weight:var(--weight-semibold)' : ''}">
|
||||
<td style="padding:var(--space-1) var(--space-2)">${_fmtDate(w.gemessen_am)}</td>
|
||||
<td style="padding:var(--space-1) var(--space-2);text-align:right">${w.gewicht_g} g</td>
|
||||
<td style="padding:var(--space-1) var(--space-2);text-align:right">${diffStr}</td>
|
||||
</tr>`;
|
||||
}).join('')}
|
||||
</tbody>
|
||||
</table>`;
|
||||
} catch (err) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue