feat(fitness): redesign measure page with muscle-man map, inline edit, and desktop 2-col layout

- Weight + body fat cards share a unified .metric-card component with wheel
  + keyboard (Arrow/Shift+Arrow) stepping. Side-by-side on tablet and up.
- Replaced body-parts accordion with a prominent card showing a cropped
  muscle-front silhouette and overlay dots/bands marking which regions
  have measurements. Shoulders + chest render as dotted tape-measure
  bands; other parts as dots. "Last measured" now relative (N days ago).
- Desktop layout: .main-col (form + period tracker) left, history on
  right. Two columns center together at wider widths instead of drifting
  apart. Fitness layout detects measure index and bumps max-width to
  1400px, matching nutrition.
- Inline history edit: pencil swaps the row for a compact date/kg/%
  form (Enter saves, Escape cancels) via PUT /api/fitness/measurements.
  Full-edit link preserved for body-parts tweaks.
- Body-parts history heading renamed to "Past measurements" /
  "Frühere Messungen" to avoid collision with the period tracker's
  own history.
- "Profil bearbeiten" moved to the top-left of the main column.
- Same-sides toggle in the body-parts flow now uses the shared Toggle
  component.
This commit is contained in:
2026-04-23 11:06:28 +02:00
parent ae8c699640
commit def176db4d
5 changed files with 651 additions and 263 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "homepage",
"version": "1.45.1",
"version": "1.46.0",
"private": true,
"type": "module",
"scripts": {
+1
View File
@@ -311,6 +311,7 @@ const translations: Translations = {
general: { en: 'General', de: 'Allgemein' },
body_fat_pct: { en: 'Body Fat (%)', de: 'Körperfett (%)' },
history: { en: 'History', de: 'Verlauf' },
past_measurements: { en: 'Past measurements', de: 'Frühere Messungen' },
// SetTable
set_header: { en: 'SET', de: 'SATZ' },
+4 -1
View File
@@ -68,6 +68,9 @@
!$page.url.pathname.startsWith(`/fitness/${s.nutrition}/food`) &&
!$page.url.pathname.startsWith(`/fitness/${s.nutrition}/meals`)
);
const isMeasureIndex = $derived(
/^\/fitness\/(measure|messen)\/?$/.test($page.url.pathname)
);
/** @param {number} secs */
function formatElapsed(secs) {
const m = Math.floor(secs / 60);
@@ -100,7 +103,7 @@
<UserHeader {user} />
{/snippet}
<div class="fitness-content" style:--fitness-max-width={isNutritionPage ? '1400px' : null}>
<div class="fitness-content" style:--fitness-max-width={isNutritionPage || isMeasureIndex ? '1400px' : null}>
{@render children()}
</div>
</Header>
File diff suppressed because it is too large Load Diff
@@ -8,6 +8,7 @@
import { toast } from '$lib/js/toast.svelte';
import DatePicker from '$lib/components/DatePicker.svelte';
import SaveFab from '$lib/components/SaveFab.svelte';
import Toggle from '$lib/components/Toggle.svelte';
import { bodyPartAccent } from '$lib/js/fitnessBodyParts';
let { data } = $props();
@@ -427,10 +428,9 @@
</button>
</div>
{/if}
<label class="same-toggle">
<input type="checkbox" bind:checked={pv.same} />
<span>{t('same_both_sides', lang)}</span>
</label>
<div class="same-toggle">
<Toggle bind:checked={pv.same} label={t('same_both_sides', lang)} />
</div>
{:else}
<div class="stepper" onwheel={(e) => onWheel(e, step.key, null)}>
<button type="button" class="step-btn" onclick={() => bump(step.key, null, -0.5)} aria-label="-0.5">
@@ -859,17 +859,6 @@
.same-toggle {
display: inline-flex;
align-items: center;
gap: 0.45rem;
font-size: 0.78rem;
color: var(--color-text-secondary);
cursor: pointer;
user-select: none;
}
.same-toggle input {
accent-color: var(--color-primary);
width: 0.95rem;
height: 0.95rem;
}
.panel { display: none; }