Skip to content

web IA: Today-first home - #69

Merged
TSVRN9 merged 2 commits into
mainfrom
ux/today-first-home
Aug 19, 2026
Merged

TSVRN9 merged 2 commits into
mainfrom
ux/today-first-home

Conversation

@TSVRN9

@TSVRN9 TSVRN9 commented Aug 19, 2026

Copy link
Copy Markdown
Owner

Closes #64. Part of epic #63.

What changed

/ becomes a dashboard, matching the epic's design decision ("the first screen is your day"):

  • Today's device-local macro stats up top (empty state when nothing's logged today).
  • The four dining halls as compact cards below — favorite-star text-content contract intact (★/☆, aria-pressed), same <li>/link/button markup as before.
  • Quick links to Rank ("Compare dishes") and Favorites ("Favorite dishes") — labels deliberately avoid the literal substrings "Rank dishes" / "Favorites" so they don't collide with the primary nav's own links under Playwright's substring getByRole matching.
  • /today stays the full detail view (log list, split bar, exports) — a "Full day →" link from the dashboard deep-links there.
  • Primary nav reordered so "Today's macros" leads and the "Dining Halls" home link follows the rest of primaryNav — no label was renamed, so every existing getByRole("link", { name: "Dining Halls" | ... }) locator still resolves to the same element.

Reuse, not duplication

Extracted out of /today (which used to compute this inline):

  • web/src/lib/macroShares.ts — pure macroCalorieBreakdown(totals): per-macro % of calories + total, used by both the stat grid and /today's split bar.
  • web/src/lib/MacroStats.svelte — the 4-up Calories/Protein/Carbs/Fat grid, no card wrapper, no bar (bar stays /today-only per scope). Both / and /today render the same component against the same computeDailyTotals output.

Data residency

Home reads IndexedDbLogStorage (today's entries only) + computeDailyTotals — device-local, no auth gate, zero new server calls. Covered by a dedicated e2e test that asserts zero **/api/** requests fire on /.

Tests

  • New: web/e2e/home-dashboard.spec.ts — empty state + halls reachable, macro stats update after logging (asserted directly on /, not after a deep-link), quick-link name-collision guard, zero-API-calls guard.
  • Modified with fresh red evidence: vertical-slice.spec.ts (strengthened — now asserts totals visible on the dashboard itself before following the deep link to /today), shell.spec.ts (new nav-order assertion).
  • Unchanged, verified still green: favorites.spec.ts, halls-menu.spec.ts, rank.spec.ts, filters.spec.ts, today.spec.ts, friends-notifications.spec.ts — the hall <li> markup they depend on was preserved verbatim.

Full evidence (red + green logs, mutation proofs) posted on #64.

Verification

  • PORT=4305 npx playwright test — 31/31 passed.
  • pnpm --filter web check — 0 errors/warnings.
  • pnpm --filter web build — clean.

Not merging — leaving for review per dispatch instructions.

/ becomes a dashboard: today's device-local macro stats up top (empty
state when nothing's logged, zero server calls), the four halls as
compact cards (favorite-star text-content contract intact), then
quick links to Rank/Favorites. /today stays the full detail view (log
list, split bar, exports). Nav reorders Today's macros ahead of the
Dining Halls home link without touching any label, so existing
getByRole nav locators keep resolving.

Extracted the shared macro-calorie-share math (macroShares.ts) and the
4-up stat grid (MacroStats.svelte) out of /today so the home dashboard
reuses the same computation and markup instead of duplicating it.

New: web/e2e/home-dashboard.spec.ts. Updated with fresh red evidence:
vertical-slice.spec.ts, shell.spec.ts. favorites.spec.ts and
halls-menu.spec.ts needed no changes — the hall <li> markup they
depend on was preserved verbatim.
@TSVRN9
TSVRN9 enabled auto-merge (squash) August 19, 2026 15:31
@TSVRN9
TSVRN9 merged commit ab7c6f4 into main Aug 19, 2026
6 of 7 checks passed
TSVRN9 added a commit that referenced this pull request Aug 19, 2026
* web: social activity feed (pings + sightings)

Evolves /notifications into a unified, chronological activity feed
(pings + favorited-food sightings), with "ping a friend" as a
first-class action there instead of buried in /friends' accepted-
friends list. /friends stays scoped to search/request/accept.

- No schema changes: `pings` has no read_at column, so ping unread
  state is derived from a device-local "last viewed the feed" marker
  (web/src/lib/feedLastSeen.ts, localStorage, same shape as
  preferences.ts) rather than a server flag. Sightings keep their
  existing server-persisted read_at.
- Closes #62: sighting mark-as-read is now a real <button> (keyboard-
  reachable via focus+Enter), alongside the pre-existing onmouseenter
  for mouse users, which is unchanged.
- Signed-out: feed shell + sign-in value prop pointing at the header's
  existing "Sign in with Google" control (no duplicate button).
- Carry-over from PR #69's review (issue #66 comment): replaced
  home-dashboard.spec.ts's `**/api/**`-only residency guard with a
  page.on("request") listener flagging any same-origin /api/ path or
  disallowed cross-origin request (fires for no-cors fetches too,
  which the old glob-based route() interception missed). Explicit
  allow-list for the Google Fonts cross-origin request app.css (#42)
  already makes on every page. Proven red-first via mutation
  (temporary no-cors fetch in +page.svelte, reverted -- diff to that
  file is empty).
- Fixed friends-notifications.spec.ts's two known nits: cookie name
  now derived from PUBLIC_SUPABASE_URL instead of hardcoded
  "your-project-ref", and the unawaited-PATCH race (both the
  friendships Accept instance and its unfixed twin on the
  notifications toggle) replaced with expect.poll.

Red-green TDD throughout; see issue #66 for the full red/green log.

* review follow-up: tighten keyboard/badge assertions, fix a refresh() race

- friends-notifications.spec.ts: #62's keyboard test now asserts
  toBeFocused() before pressing Enter (proves the button is actually
  in the tab order, matching #62's "keyboard-only walkthrough"
  wording, not just that it's clickable-when-focused). The alert
  toggle's badge locator is now scoped to its own section instead of
  ".badge".first(), which would have silently started matching the
  wrong badge if the feed's own "New" badges ever preceded it in DOM
  order.
- notifications/+page.svelte: refresh() now resolves friends before
  assigning pings, so a ping can no longer render with the "A friend"
  fallback for one tick before its sender's name loads.

Full suite (45/45), check, and build all still green after these.

* notifications: fix silent watermark stamp, add discriminating feed-unread coverage (#75 review)

F1 (blocking): refresh().then(() => saveFeedLastSeen(...)) ran unconditionally, even when
refresh() early-returned with no session -- a signed-out visit silently stamped the
device-local unread watermark. Now only stamps when a session exists, and stamps from the
newest rendered item's own created_at (Postgres clock) instead of the browser clock.

F2 (blocking): feedLastSeen.ts had no test that would fail if isUnread()'s ping branch were
hardcoded to `return true`, or if the saveFeedLastSeen() call were deleted outright. Added two
tests to friends-notifications.spec.ts: a seeded watermark correctly splits an older/newer ping
pair into read/unread, and a signed-out visit leaves the watermark key unset. Both were shown
red against pristine pre-fix code, and against each of the two named mutations independently
(verified locally, reverted after).

Nits: fixed the sort comparator (previously returned -1 for both orderings of two equal
timestamps, an invalid comparator); sendPing() now checks the insert's error and shows a
failure state instead of an unconditional "Ping sent" (also made pingSent/pingError mutually
exclusive so a fast retry can't show both at once). Added a red-first test for the failure
state.

Optional: feedLastSeen's localStorage key is now scoped by user id, so a second account on the
same browser doesn't inherit the first account's watermark.
@TSVRN9
TSVRN9 deleted the ux/today-first-home branch September 18, 2026 05:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

web IA: Today-first home

1 participant