| status | done | ||
|---|---|---|---|
| depends |
|
||
| specs |
|
||
| issues |
|
||
| pr | 105 |
Last open piece of #83. The route was a <ComingSoon /> placeholder; the API endpoint (POST /api/projects/:slug/buzz) is fully implemented + tested. This plan ships the SPA form that closes the loop.
After this lands, #83's engineering work is done — only the legacy-content port stays as a follow-up content PR.
- api/projects-buzz.md — request shape, validation, the
duplicate_url409. - screens/project-detail.md — the "Log Buzz" affordance points here.
apps/web/src/screens/ProjectBuzzNew.tsx:
- Route at
/projects/:slug/buzz/new(replace<ComingSoon />in App.tsx). - Fields per spec: headline (1-200 chars, required), url (HTTPS, required), publishedAt (date, required, defaults to today, max=today), summary (≤2000 chars markdown, optional).
- Submit calls existing
api.projects.postBuzz(slug, input)helper. - Success →
navigate(/projects/${slug}#activity)so the new buzz appears in the project's activity feed. - Failure surfaces inline field errors via
ApiError.fields; the spec'dduplicate_url409 maps to a field-level error on the URL input. - Anonymous callers redirect to
/login?return=…so the post-login flow drops them back on the form.
apps/web/tests/ProjectBuzzNew.test.tsx:
- Anonymous → login redirect carries return-to query
- Signed-in: form renders all required fields
- Submit disabled until headline + url filled
- Submit enabled once both are filled
- Successful submit navigates to project page
-
/projects/:slug/buzz/newrenders the form when signed in. - Anonymous callers redirect to
/login?return=. - Successful POST navigates to
/projects/:slug. -
duplicate_url409 surfaces inline on the URL field via theApiError.codecheck — unit test covers the success path, the error-path mapping is one-line and matchesProjectEdit's established pattern. -
npm run type-check && npm run lint && npm testclean.
- Image upload deferred. Spec's request shape allows an optional
imageUpload.keyfrom a prior upload endpoint. That upload surface doesn't exist for general media in v1 (per the spec note). Out of scope here — surfaces in the form would just be dead UI. - No anchor scroll on
#activity. The project-detail screen doesn't have an#activityelement today. The navigate target stays as-is so the new buzz appears at the top of the activity feed (which is the default scroll position anyway). If a follow-up wants smooth scroll-to-anchor, the existinganchor="update"pattern works.
Two commits: plan-open, impl + tests.
Surprises:
useAuthexposesloading, notisLoading. Caught on first type-check — a small TanStack-Query convention mismatch with the project's own auth hook. Renamed at the destructure site.- The lingering
ComingSoonimport. Removing the last<ComingSoon />usage at/projects/:slug/buzz/newleft the import unused. ESLint caught it in the local sweep this time (lesson from PR #103's CI surprise). Bundled into this commit since it's the same logical change. - Submit-enabled gating. Using a derived
disabled(rather than separate validation state) keeps the affordance honest — the button literally can't be clicked until headline + url are non-empty. Nativerequired+type="url"handles deeper validation at submit time.
- #83 is now fully closed engineering-wise. Four phased PRs:
- phase 1 — ProjectDetail Share/stage-modal/Edit-on-GitHub + /contact (PR #102)
- phase 2 — PersonDetail slackHandle + email (PR #103)
- phase 3 — /pages/:slug bundled markdown (PR #104)
- phase 4 — this PR (#105)
- Legacy-content port is the only remaining open thread under #83
— port the real Mission/Leadership/CoC/Hackathons copy from
codeforphilly.org/site-root/pages/. Tracked as — content task, no engineering blocker. Will file an issue when the content owner is identified. - Image upload for buzz — spec allows an optional
imageUpload.keyattached to the POST. Needs a general-media upload endpoint that doesn't exist for v1 (perspecs/api/projects-buzz.md). None — out of v1 scope.