Repository navigation
web IA: Today-first home - #69
Merged
Merged
Conversation
/ 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.
This was referenced Aug 19, 2026
Closed
TSVRN9
enabled auto-merge (squash)
August 19, 2026 15:31
This was referenced Aug 19, 2026
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #64. Part of epic #63.
What changed
/becomes a dashboard, matching the epic's design decision ("the first screen is your day"):aria-pressed), same<li>/link/button markup as before.getByRolematching./todaystays the full detail view (log list, split bar, exports) — a "Full day →" link from the dashboard deep-links there.primaryNav— no label was renamed, so every existinggetByRole("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— puremacroCalorieBreakdown(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/todayrender the same component against the samecomputeDailyTotalsoutput.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
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.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).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.