Skip to content

feat(public-screens): all read-only screens wired to read API#28

Merged
themightychris merged 10 commits into
mainfrom
feat/public-screens
May 16, 2026
Merged

feat(public-screens): all read-only screens wired to read API#28
themightychris merged 10 commits into
mainfrom
feat/public-screens

Conversation

@themightychris
Copy link
Copy Markdown
Member

Summary

  • Replaces every <ComingSoon /> placeholder from web-shell with a real screen wired to the read API: Home, ProjectsIndex, ProjectDetail (+ permalink variants for updates and buzz), PeopleIndex, PersonDetail, HelpWantedIndex, ProjectUpdatesFeed, ProjectBuzzFeed, TagsOverview, TagsNamespace, TagDetail, Volunteer, Sponsor. /people/members; /search?q=/projects?q= (the dedicated search page is deferred per behaviors/app-shell.md).
  • New typed apps/web/src/lib/api.ts fetcher around the response envelope from specs/api/conventions.md. TanStack Query is the cache + loading layer; queryCache.onError wires 5xx and network failures to useNetworkError().showError() (closes one of the two web-shell absorptions). URL query params are the source of truth for filter / sort / page / search state on every index screen, so back / forward / share-links work cleanly.
  • Replaces mockSearch in useSearch.ts with three parallel calls to /api/projects, /api/people, /api/tags (perPage=4 each) per behaviors/app-shell.md; SearchBox renders results grouped by type. Enter submits to /projects?q=… (closes the other web-shell absorption).
  • Reusable building blocks: <ProjectCard>, <PersonCard>, <HelpWantedCard>, <StageBadge> + <StageProgressBar> (palette + descriptions from behaviors/project-stages.md), <TagChip> (namespace color-coding), <FacetSidebar> (Topics / Tech / Events / Stages tabs), <ActivityCard> (update vs buzz discriminator + mergeActivity() per behaviors/activity-feed.md), <MarkdownView> (sets dangerouslySetInnerHTML on the server-rendered sanitized HTML — no client-side markdown lib in the bundle), <PersonAvatar>, <Pagination>.
  • ProjectDetail reads permissions from the API response to gate UI: anonymous gets a "Sign in to contribute" CTA and "Sign in to express interest" replacements on help-wanted role cards (the actual auth-gated mutations land in authoring-screens).
  • Bug fix on the way through: useAuth now parses the { person, accountLevel } envelope shape the auth-jwt-substrate endpoint actually returns; the old code crashed the header on first paint as soon as the API was reachable.

Test plan

  • npm run lint — green
  • npm run type-check — green
  • npm run build — green; bundle contains no remark / marked / markdown-it / micromark (grep on dist/assets/*.js returns nothing)
  • npm run -w apps/web test — 27 / 27 pass; new smoke tests render each major screen against fixture API responses and verify documented Display Rules
  • npm run -w packages/shared test — 52 / 52 pass
  • Browser validation against dev servers (vite on 5174, fastify on 3001): home, projects-index, help-wanted, members, tags-overview all render with empty-state copy from each spec; NetworkErrorBanner appears with API stopped; /api/projects?q=, /api/people?q=, /api/tags?q= all answer 200 with the expected envelope so SearchBox typeahead is wired through
  • API workspace tests — not re-run (no apps/api/ changes; the suite ran in PR feat(read-api): GET endpoints for projects, people, tags + FTS + serializers #22)
  • Manual QA of mobile sheet (< md hamburger → sheet) — issue Validate mobile sheet in browser (hamburger → sheet < md) #16

🤖 Generated with Claude Code

npm install -w apps/web @tanstack/react-query
API layer wraps the response envelope from specs/api/conventions.md
into typed Promise calls. ApiError carries status/code/fields/traceId so
screens can branch on 404 vs validation vs server. The TanStack Query
client wires queryCache.onError to NetworkErrorBanner.showError() for
5xx and network failures.
MarkdownView sets dangerouslySetInnerHTML on the server-rendered
sanitized HTML (overviewHtml, bioHtml, descriptionHtml) per
specs/behaviors/markdown-rendering.md — no client-side markdown lib
in the bundle. StageBadge / StageProgressBar carry the seven-stage
palette and descriptions from specs/behaviors/project-stages.md. TagChip
color-codes by namespace per specs/behaviors/tags.md. FacetSidebar drives
Topics/Tech/Events/Stages from metadata.facets. ActivityCard discriminates
update vs buzz cards per specs/behaviors/activity-feed.md and exports
mergeActivity() for the home + project-detail mixed feeds.
Replaces ComingSoon placeholders in App.tsx with:
- Home: hero, featured projects grid, get-involved tiles, activity stream
  with All/Updates/Buzz filter chips (client-side merge), help-wanted rail
- ProjectsIndex: URL-driven filters (q, tag*, stage*, sort, page,
  helpWanted, includeDeleted-for-staff), facet sidebar (Topics/Tech/Events/
  Stages tabs), debounced search, sortable, paginated
- ProjectDetail: overview + open help-wanted + merged activity feed +
  members sidebar + tags + share + info; respects /projects/:slug/updates/
  :number and /projects/:slug/buzz/:buzzSlug anchor variants; action
  buttons gated by response.permissions, with "Sign in to ..." replacements
  for anonymous callers
- PeopleIndex: grid with Topics/Tech facet sidebar, q+sort+page URL state
- PersonDetail: profile, projects sorted by maintainer/joinedAt, recent
  authored updates
- HelpWantedIndex: Tech/Topics facets + commitmentMax radio filter
- ProjectUpdatesFeed, ProjectBuzzFeed: global reverse-chron feeds
- TagsOverview, TagsNamespace, TagDetail: full taxonomy browse
- Volunteer, Sponsor: static content + live project count / featured roles
- /people redirects to /members; /search redirects to /projects?q=…
  (the dedicated search page is deferred per behaviors/app-shell.md)

URL state is the source of truth via useSearchParams so the queryKey
re-fetches and back/forward + share-links work cleanly.
Replaces the mockSearch stub from web-shell with three parallel calls
to /api/projects, /api/people, /api/tags (perPage=4 each) per
specs/behaviors/app-shell.md. Results render in grouped sections
(Projects / Members / Tags). 5xx and network failures surface via
useNetworkError().showError(). Enter routes to /projects?q=… per the
deferred-/search behavior in app-shell.md.
renderScreen wires a fresh QueryClient + NetworkErrorProvider +
TooltipProvider + MemoryRouter around the screen under test, plus
mockOk/mockPaginated helpers matching the response envelope.

Smoke tests cover the Display Rules in each screen spec:
- Home: hero headline + volunteer CTA + get-involved cards
- ProjectsIndex: header with totalItems badge, project card with stage +
  help-wanted badge, Add Project hidden for anonymous
- ProjectDetail: title, server-rendered overview HTML, sign-in CTA for
  anonymous, project-info sidebar with users-site + chat-channel links
- HelpWantedIndex: role with commitment chip + sign-in CTA, all four
  commitment radio options
- useSearch: confirms the three parallel calls to /api/projects,
  /api/people, /api/tags with perPage=4 (replaces mockSearch stub
  validation in web-shell)

AppHeader test wraps in NetworkErrorProvider now that SearchBox depends
on useNetworkError().
The session endpoint returns { person: …|null, accountLevel: … } per
specs/api/auth.md — not a bare AuthPerson — so fetchMe() reaches into
data.person. Without this the header crashes on first render with
"Cannot read properties of undefined (reading 'charAt')" when accountLevel
is present but no person is attached.
@themightychris themightychris merged commit b17ebbb into main May 16, 2026
1 check passed
@themightychris themightychris deleted the feat/public-screens branch May 16, 2026 22:47
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.

1 participant