diff --git a/package.json b/package.json index 0a87e26b..c66c6927 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homepage", - "version": "1.46.3", + "version": "1.46.4", "private": true, "type": "module", "scripts": { diff --git a/src/lib/js/fitnessI18n.ts b/src/lib/js/fitnessI18n.ts index 814ff026..31990b67 100644 --- a/src/lib/js/fitnessI18n.ts +++ b/src/lib/js/fitnessI18n.ts @@ -312,6 +312,7 @@ const translations: Translations = { body_fat_pct: { en: 'Body Fat (%)', de: 'Körperfett (%)' }, history: { en: 'History', de: 'Verlauf' }, past_measurements: { en: 'Past measurements', de: 'Frühere Messungen' }, + show_more: { en: 'Show more', de: 'Mehr anzeigen' }, // SetTable set_header: { en: 'SET', de: 'SATZ' }, diff --git a/src/routes/fitness/[measure=fitnessMeasure]/+page.server.ts b/src/routes/fitness/[measure=fitnessMeasure]/+page.server.ts index b43fc702..01652253 100644 --- a/src/routes/fitness/[measure=fitnessMeasure]/+page.server.ts +++ b/src/routes/fitness/[measure=fitnessMeasure]/+page.server.ts @@ -3,7 +3,7 @@ import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async ({ fetch }) => { const [latestRes, listRes, goalRes, periodRes, shareRes] = await Promise.all([ fetch('/api/fitness/measurements/latest'), - fetch('/api/fitness/measurements?limit=200'), + fetch('/api/fitness/measurements?limit=10'), fetch('/api/fitness/goal'), fetch('/api/fitness/period').catch(() => null), fetch('/api/fitness/period/share').catch(() => null) diff --git a/src/routes/fitness/[measure=fitnessMeasure]/+page.svelte b/src/routes/fitness/[measure=fitnessMeasure]/+page.svelte index 502533d0..af76d8cb 100644 --- a/src/routes/fitness/[measure=fitnessMeasure]/+page.svelte +++ b/src/routes/fitness/[measure=fitnessMeasure]/+page.svelte @@ -27,6 +27,9 @@ let latest = $state(data.latest ? { ...data.latest } : {}); // svelte-ignore state_referenced_locally let measurements = $state(data.measurements?.measurements ? [...data.measurements.measurements] : []); + // svelte-ignore state_referenced_locally + let measurementsTotal = $state(/** @type {number} */ (data.measurements?.total ?? measurements.length)); + let loadingMore = $state(false); let showWeightHistory = $state(false); // Profile fields (sex, height, birth year) — stored in FitnessGoal @@ -105,6 +108,27 @@ { label: t('r_calf', lang), key: 'rightCalf', value: latestBp.rightCalf } ]); + async function loadMore() { + if (loadingMore) return; + loadingMore = true; + try { + const res = await fetch(`/api/fitness/measurements?limit=20&offset=${measurements.length}`); + if (res.ok) { + const body = await res.json(); + const next = Array.isArray(body?.measurements) ? body.measurements : []; + const existing = new Set(measurements.map((/** @type {any} */ m) => m._id)); + const fresh = next.filter((/** @type {any} */ m) => !existing.has(m._id)); + measurements = [...measurements, ...fresh]; + if (typeof body?.total === 'number') measurementsTotal = body.total; + } else { + toast.error(lang === 'en' ? 'Failed to load more' : 'Laden fehlgeschlagen'); + } + } catch { + toast.error(lang === 'en' ? 'Failed to load more' : 'Laden fehlgeschlagen'); + } + loadingMore = false; + } + /** @param {string} id */ async function deleteMeasurement(id) { if (!await confirm(t('delete_measurement_confirm', lang))) return; @@ -112,6 +136,7 @@ const res = await fetch(`/api/fitness/measurements/${id}`, { method: 'DELETE' }); if (res.ok) { measurements = measurements.filter((m) => m._id !== id); + measurementsTotal = Math.max(0, measurementsTotal - 1); try { const latestRes = await fetch('/api/fitness/measurements/latest'); if (latestRes.ok) latest = await latestRes.json(); @@ -309,6 +334,7 @@ if (latestRes.ok) latest = await latestRes.json(); } catch {} measurements = [created.measurement ?? created, ...measurements]; + measurementsTotal = measurementsTotal + 1; resetForm(); toast.success(lang === 'en' ? 'Measurement saved' : 'Messung gespeichert'); } else { @@ -571,6 +597,12 @@ {/each} + {#if showWeightHistory && measurements.length < measurementsTotal} + + {/if} {/if} @@ -1136,6 +1168,35 @@ flex-direction: column; gap: 0.4rem; } + .show-more { + align-self: stretch; + margin-top: 0.5rem; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.4rem; + padding: 0.55rem 1rem; + border: 1px dashed var(--color-border); + border-radius: var(--radius-pill); + background: transparent; + color: var(--color-text-secondary); + font-size: 0.8rem; + font-weight: 600; + cursor: pointer; + transition: border-color var(--transition-fast, 120ms), color var(--transition-fast, 120ms), background var(--transition-fast, 120ms); + } + .show-more:hover:not(:disabled) { + border-color: var(--color-primary); + color: var(--color-primary); + background: color-mix(in oklab, var(--color-primary) 6%, transparent); + } + .show-more:disabled { opacity: 0.5; cursor: not-allowed; } + .show-more-count { + font-size: 0.7rem; + font-weight: 500; + color: var(--color-text-tertiary); + font-variant-numeric: tabular-nums; + } .history-item { background: var(--color-surface); border-radius: 8px;