A 12-week distance bar chart under the profile stats header, viewer-scoped, built on profile-stats (#539). Design records the same no-cache rationale: one indexed grouped aggregate over a 12-week slice (≤12 rows), gap-filled in JS. Validates with `openspec validate --strict`. Specs only; no code. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2.2 KiB
Context
profile-stats added getActivityStats (a single indexed aggregate over stored
columns) and a ProfileStats header. This change adds a per-week breakdown
beneath it. activities has distance, started_at, created_at,
visibility, and owner_id-leading indexes.
Goals / Non-Goals
Goals
- A compact weekly-distance trend on the profile, cheap and viewer-scoped.
Non-Goals
- No per-week elevation/time toggle, no selectable window in v1 (fixed 12 weeks).
- No hover tooltip framework — a native
titleper bar is enough. - No "where I've been" map (separate idea).
Decisions
D1: One grouped aggregate — still no cache
SELECT date_trunc('week', coalesce(started_at, created_at)) AS wk, sum(distance) AS m FROM journal.activities WHERE owner_id = $1 [AND visibility = 'public'] AND coalesce(started_at, created_at) >= now() - interval '12 weeks' GROUP BY wk.
Bounded to 12 weeks and filtered on the owner_id index, this scans a tiny
recent slice and returns ≤ 12 rows — same cost class as the lifetime aggregate
(profile-stats §D1). No cache table; the documented escape hatch there covers
this too.
D2: Gap-fill in JS
The query returns only weeks that have activities. The server fills the result
to a contiguous 12-week series (oldest → newest) with 0 for empty weeks, so
the chart always has a stable 12-bar axis. Week boundaries follow Postgres
date_trunc('week', …) (ISO weeks, Monday start).
D3: Viewer scoping
Identical to profile-stats: publicOnly: !isOwn. Same WHERE clause shape;
remote-ingested rows (owner_id NULL) never appear.
D4: Presentation
A small SVG bar chart (WeeklyDistanceChart) under the stats header: 12 bars,
heights normalized to the max week, each bar carrying a title with its week
distance (formatted via stats.ts). Renders nothing when every week is 0
(no distance in the window). Read-only, no interactivity beyond the native
title.
Risks / Trade-offs
- Fixed 12-week window — a power user with a long history sees only the recent quarter. Acceptable for a cadence glance; a selectable range is a future enhancement.
- Distance-only — elevation/time trends are deferred to keep the chart legible and the query single-column.