V1 Web PWA · Surface walkthrough

Tara — programs browser

A per-surface walkthrough of the V1 Web PWA customer surface: layout, states, interactions, data, and cross-references.

walked
9sections3 minread

On this page

Context. surface customer · domain tara · route /domains/tara/programs · auth signed-in · source apps/oshun/web/src/app/domains/tara/programs/page.tsx

Last walked. 2026-05-29 automated runtime walk (Playwright headless) — render, /v1 data (2xx), console/page-errors, expected content, screenshot verified; live screen-reader, touch, offline, and telemetry-delivery checks pending a manual AT pass. Evidence: WALKTHROUGH/results/runtime-sweep-2026-05-29.md; content re-verified 2026-06-03 against current source; 2026-06-29 targeted Playwright coverage added for browser lists, selected-card detail, programId deep links, missing programId fallback, local enrollment, current/locked day rows, day detail, header back, milestone state, and scoped axe scans. Evidence: apps/oshun/web/e2e/tara-programs.spec.ts

Purpose#

Browser for multi-day / multi-week Tara meditation programs. Lists enrolled "My programs" and all available programs; selecting one opens ProgramDetailView with daily structure, meditation, quote, intention, activities, milestone markers, and a progress bar.

Entry points#

  • Tara hub — verify the cross-link from /tara
  • Deep link with program selection?programId=<id> pre-selects a program via useBrowserSearchParams().get('programId')
  • Direct URL / bookmark — yes
  • Domain back-stackrouter.back() wired into onBack

Layout regions#

page.tsx is a 'use client' thin wrapper that:

  1. Reads programId from useBrowserSearchParams()
  2. Renders <TaraProgramsBrowserPage onBack={() => router.back()} initialProgramId={initialProgramId} />

Inside TaraProgramsBrowserPage (TaraCollections.tsx):

  • Sticky header — back button, "Programs" heading, "Multi-day structured journeys" subtitle, Calendar icon
  • My programs sectionmyPrograms = SIMULATED_PROGRAMS.filter(enrolled === true); only visible when at least one enrollment exists
  • All programs gridSIMULATED_PROGRAMS cards
  • Program detail view — when selectedProgram !== null, replaces the browser with <ProgramDetailView> showing day-by-day plan, milestones, and progress

States#

  • Browser (no selection)selectedProgram === null; My programs + All programs visible
  • Detail view (selected via card)selectedProgram !== null; browser hidden
  • Detail view (deep link)initialProgramId matches a program; the effect on [initialProgramId] rehydrates selection on URL change
  • Initial program id missingSIMULATED_PROGRAMS.find(...) === null; falls through to the browser
  • No enrolled programs — My programs section hidden
  • Milestones reached — static milestone reached/unreached state in ProgramDetailView
  • Reduced motion — shared Playwright fixture emulates reduced motion; scoped browser/detail axe scans run against settled roots

Interactions#

  • Back (button) — router.back() via onBack

My programs section#

  • Program card — sets selectedProgram; reveals detail view
  • Progress bar inside card — non-interactive progress * 100 fill

All programs grid#

  • Program card — same selection behavior

Program detail view (ProgramDetailView)#

  • Back inside detail — clears selectedProgram
  • Day row — opens the day's meditation, quote, intention, activities
  • Mark day complete — no shipped control exists to flip ProgramDay.completed
  • Milestone celebration — crossing a milestone is not currently route-drivable because day completion cannot be changed in the UI
  • Enroll — non-enrolled program starts locally and locks future days
  • Un-enroll — no shipped un-enroll control exists

Data & contracts#

  • Reads:
    • SIMULATED_PROGRAMS (in-file fixture from TaraCollections.tsx)
  • Writes: enrollment + day completion state are local to the component today
  • Realtime: None.
  • Caching: client-only
  • Auth/role check: shell middleware
  • Query param: programId (string | null) — read once, applied via useEffect([initialProgramId])

Current E2E evidence#

  • apps/oshun/web/e2e/tara-programs.spec.ts opens /domains/tara/programs, verifies the no-selection browser state, two enrolled cards in "My Programs", three cards in "All Programs", card enrollment/current-day/progress state, and selection into tara-prog-001.
  • The same spec verifies tara-prog-001 detail state, reached and unreached milestones, current/completed/locked day rows, the day-4 detail view's quote, intention, meditation, activities, locked day non-entry, and detail back.
  • It opens /domains/tara/programs?programId=tara-prog-002 to prove deep-link hydration, then /domains/tara/programs?programId=tara-prog-missing to prove missing IDs fall back to the browser.
  • It opens non-enrolled tara-prog-003, clicks Start Program, verifies local enrollment/current-day update and future-day locking, and opens day 1.
  • It verifies header router.back() by navigating from /domains/tara to /domains/tara/programs, then clicking the route header back button.
  • It runs scoped axe scans against [data-tara-programs-browser] and [data-tara-program-detail="tara-prog-001"].

Cross-references#

Open questions / known gaps#

  • Confirm whether deep-link ?programId= should update the URL when the user selects a program from a card (current page reads the param on mount but does not propagate selection back to the URL)
  • Document the persistence target for enrollment + day completion. Current enrollment is local component state; no day-complete write exists.
  • Confirm the BFF endpoint for programs once it lands; today SIMULATED_PROGRAMS is local fixture data
  • Verify milestone celebration accessibility if a real day-complete action ships; current UI only displays static reached/unreached milestone chips.