Skip to content

web IA: Today-first home #64

Closed
Closed

Description

@TSVRN9

Part of #63.

What to build

Make the landing experience Today-first: / shows today's macro summary (the stat panel pattern from /today), a quick-log path, and the halls (as the route into menus) — the halls list stops being the whole home page. Concretely:

  • / becomes a dashboard: macros-so-far stats at top (device-local data, works signed-out), then the four halls as compact cards (keep favorite stars + their text-content contract — several specs read state from star text), then quick links to Rank/Favorites.
  • /today remains the full detail view (log entries, split bar, exports) — the home stats deep-link to it.
  • Navigation copy/order reflects the identity: Today's macros is primary, not one button among ten.

Constraints

  • Reuse the landed component classes and the /today stat markup patterns — no new CSS in app.css unless a genuinely new component is needed (then document it in the same comment style).
  • vertical-slice.spec.ts, favorites.spec.ts, shell.spec.ts, halls-menu.spec.ts navigate from / — update them coherently, never weaken assertions to make them pass; every changed spec needs fresh red evidence (mutation or write-first).
  • Data residency: all macro data on this page is device-local; zero new server calls.
  • Red-green TDD with pasted red evidence; PORT-isolated Playwright runs.

Acceptance criteria

  • / renders macro stats from today's device-local log (empty state when nothing logged), halls reachable in one tap, favorite-star contract intact
  • New/updated specs each have documented red evidence
  • Full Playwright suite green, pnpm --filter web check clean, build clean, CI green

Activity

  1. TSVRN9 commented on Aug 19, 2026

    @TSVRN9
    OwnerAuthor

    Implemented on branch ux/today-first-home, PR #69 (not merged — leaving for review).

    Per-criterion results

    / renders macro stats from today's device-local log (empty state when nothing logged), halls reachable in one tap, favorite-star contract intact — DONE.

    • web/src/routes/+page.svelte reads today's entries via IndexedDbLogStorage.getEntriesForDate + computeDailyTotals (same as /today), renders the shared MacroStats component, or an .empty-state block ("Nothing logged yet today.") when entries.length === 0.
    • Halls render as the same <li class="card"> / favorite <button aria-label="favorite">★|☆</button> / <a href="/halls/{slug}"> markup as before — verbatim, unmoved except into a new <section>. aria-pressed and the ★/☆ text contract are untouched.
    • Zero new server calls: DINING_HALLS is a static array (no fetch), the macro read is IndexedDB-only. Covered by a dedicated test (home-dashboard.spec.ts) that asserts zero **/api/** requests fire on /.

    New/updated specs each have documented red evidence — DONE, see below.

    Full Playwright suite green, pnpm --filter web check clean, build clean, CI green — DONE (CI itself not observable from here; local lanes all green, see Verification).

    Red evidence (before implementation)

    PORT=4305 npx playwright test run with the specs written/modified but the app code unchanged — 5 failures, all expected:

    1) home-dashboard.spec.ts › home shows an empty state when nothing is logged today...
       Error: expect(locator).toBeVisible() failed
       Locator: getByText('Nothing logged yet today.')
       Error: element(s) not found
    
    2) home-dashboard.spec.ts › logging a dish updates today's macro stats on the home dashboard itself
       Error: expect(locator).toBeVisible() failed
       Locator: getByText('Calories: 127')
       Error: element(s) not found
    
    3) shell.spec.ts › Today's macros precedes the Dining Halls home link in primary nav
       Error: expect(received).toBeLessThan(expected)
       Expected: < 0
       Received:   1
    
    4) vertical-slice.spec.ts › browse a hall's menu, log a dish, and see it reflected in today's macro totals
       Error: expect(locator).toBeVisible() failed
       Locator: getByText('Calories: 127')
       Error: element(s) not found
    
    5) home-dashboard.spec.ts › quick links reach Rank and Favorites without ambiguity against the primary nav
       Test timeout of 30000ms exceeded.
       waiting for getByRole('link', { name: 'Compare dishes' })
    
    26 passed, 5 failed
    

    (The 4th home-dashboard.spec.ts test — zero API requests — passed even pre-implementation, since the old home page also made no server calls; that's expected, not a gap.)

    Green evidence (after implementation)

    Running 31 tests using 4 workers
    ...
    31 passed (19.0s)
    

    Mutation evidence for the two modified pre-existing specs

    • shell.spec.ts nav-order assertion: reverted +layout.svelte's reorder (git stash on just that file) and re-ran the new test alone → red with the exact same "Expected: < 0, Received: 1" failure as the original red run. Restored, re-ran → green.
    • vertical-slice.spec.ts strengthened assertion: temporarily set style="display: none" on the home dashboard's macro-stats wrapper and re-ran → red:
      Error: expect(locator).toBeVisible() failed
      Locator:  getByText('Calories: 127')
      Received: hidden
      
      Reverted the mutation, re-ran → green.

    Verification

    • PORT=4305 npx playwright test — 31/31 passed.
    • pnpm --filter web check — COMPLETED 398 FILES 0 ERRORS 0 WARNINGS 0 FILES_WITH_PROBLEMS.
    • pnpm --filter web build — clean (adapter-auto's "could not detect a production environment" notice is pre-existing/benign, unrelated to this change).

    Notes for reviewers

    • favorites.spec.ts and halls-menu.spec.ts needed zero changes — deliberately, not an oversight. They depend only on the hall <li>/star/link markup, which was preserved verbatim just moved under a new <section>. Confirmed still green in the full run above.
    • Extracted web/src/lib/macroShares.ts (pure macroCalorieBreakdown) and web/src/lib/MacroStats.svelte (the 4-up stat grid, no card wrapper, no split bar) out of /today so / and /today share the same computation/markup instead of duplicating it — the "extraction to a shared $lib module" the issue flagged as in-scope if needed. /today's own split bar and card wrapper stayed put; ran today.spec.ts alone right after the extraction to prove it was behavior-neutral before touching anything else.
    • Quick-link labels are "Compare dishes" (→ /rank) and "Favorite dishes" (→ /favorites) rather than the nav's own "Rank dishes" / "Favorites" — chosen specifically to avoid Playwright's case-insensitive substring getByRole matching turning into a strict-mode ambiguity against the primary nav, which is always present in the DOM alongside these. Covered by home-dashboard.spec.ts's "quick links reach Rank and Favorites without ambiguity" test.
    • No new app.css classes — reused .card, .stat, .empty-state, .page-title, .section-title, .label-rule, .btn/.btn-secondary throughout.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-agentFully specified, ready for an AFK agent

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions