trails/openspec/changes/archive/2026-06-14-profile-weekly-distance/tasks.md
Ullrich Schäfer 2ad9898f9c
openspec: archive profile-stats + profile-weekly-distance, sync specs
Both changes are implemented and merged (#539, #541). Promote their deltas into
openspec/specs/, move the changes to changes/archive/2026-06-14-*, and add the
two capabilities to the CAPABILITIES index.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 16:34:06 +02:00

1.5 KiB

1. Aggregate query

  • 1.1 Added getWeeklyDistance(ownerId, { publicOnly, weeks = 12 }) to activities.server.ts: sum(distance) per week, viewer-scoped.
  • 1.2 Gap-fill done in SQL (generate_series of week-starts LEFT JOINed to the activities) — returns exactly N contiguous { weekStart, distance } rows, oldest→newest, and guarantees the week boundaries match Postgres date_trunc('week', …) (no JS/Postgres boundary drift). Filter conditions live in the JOIN's ON clause so empty weeks survive.

2. Loader & view

  • 2.1 Profile loader fetches it in parallel with the stats, same publicOnly: !isOwn scoping, only when content is visible.
  • 2.2 WeeklyDistanceChart: SVG bars normalized to the busiest week, per-bar title distance (via stats.ts), localized label; renders nothing when every week is 0. Mounted under ProfileStats.

3. i18n

  • 3.1 Added journal.profileStats.weeklyDistance (label) to en + de. Per-bar readout is the language-neutral km distance.

4. Tests & checks

  • 4.1 Gap-fill is in SQL (no JS helper to unit-test) → covered by the e2e (a real activity produces a non-zero bar over a full 12-week axis).
  • 4.2 Component (jsdom): one bar per week incl. zero weeks; nothing when all zero; heights normalized to the busiest week.
  • 4.3 E2E profile-weekly-distance.test.ts: create an activity with distance → the weekly chart renders on the profile. Passes locally.
  • 4.4 typecheck + lint + unit (journal 321) green; new e2e passes locally. Full pnpm test:e2e runs in CI.