| status | done | |||
|---|---|---|---|---|
| depends | ||||
| specs |
|
|||
| issues |
|
|||
| pr | 102 |
#83 is an umbrella covering several spec-vs-implementation gaps across detail screens. This plan closes the SPA-only quick wins so reviewers aren't sitting on a single 1000-line PR:
- ProjectDetail "Share to Slack" button — adds a button next to the existing "Copy link" that copies a pre-formatted Slack message to the clipboard (project title + URL).
- ProjectDetail "Edit on GitHub" footer link — shown when
developersUrlis a github.com URL. Small, muted link. - ProjectDetail "What does this stage mean?" link + modal — beside the Stage row in the Info sidebar; opens a dialog with the canonical descriptions from behaviors/project-stages.md.
/contactmailto link — replaces the<ComingSoon />placeholder with the minimum the spec requires (mailto:hello@codeforphilly.org).
The Home "Start a Project" routing for signed-in users is already in
place (apps/web/src/screens/Home.tsx:134) — no work needed there
despite the audit listing it.
Out of scope for this phase (separate plans):
- PersonDetail
email+slackHandle— needs a serializer change to surfacePrivateProfile.emailfor self/staff andPerson.slackHandlefor everyone. Cross-cuts backend + frontend; treated as its own plan. /pages/:slugcontent-files (behaviors/app-shell.md Mission/Leadership/CoC/Hackathons) — needsapps/web/src/content/pages/directory with markdown files and the route to read+render. Treated as its own plan./projects/:slug/buzz/newcreate form — needs a real form hooked intoPOST /api/projects/:slug/buzz. Treated as its own plan.
Closes only the SPA quick-wins of #83. Each of the deferred pieces gets its own plan.
- screens/project-detail.md — Share/Info sidebar items + footer "Edit on GitHub" link.
- behaviors/project-stages.md — modal renders the canonical descriptions.
- behaviors/app-shell.md —
/contactminimum is the mailto link.
A second button in the existing Share sidebar (line 392-407 of ProjectDetail.tsx). On click, copies a pre-formatted message to the clipboard:
Check out <project.title> on Code for Philly: https://codeforphilly.org/projects/<slug>
Per spec: "opens a system share or copies a pre-formatted Slack message". Copying is the simpler shape and works in every browser without a native share API.
Below the Info sidebar (the spec says "Footer"), but visually it fits at the bottom of the sidebar — a small muted link. Render only when project.links.developersUrl matches https://github.com/.... URL: same as developersUrl.
Beside the "Stage:" row in the Info sidebar, a "What does this stage mean?" link button opens a <Dialog> (shadcn) listing all seven stages with their canonical descriptions from behaviors/project-stages.md. Highlights the project's current stage.
Stage descriptions go into a small constant in apps/web/src/lib/project-stages.ts (or wherever StageBadge lives) so they stay co-located with rendering.
Replace <ComingSoon /> at the /contact route with a simple page rendering a heading + mailto:hello@codeforphilly.org link, matching the existing Sponsor/static page styling.
- ProjectDetail renders both "Copy link" and "Share to Slack" buttons.
- ProjectDetail renders "Edit on GitHub" only when
developersUrlis a github.com URL. - "What does this stage mean?" opens a modal listing all seven stages with descriptions, highlighting the current stage.
-
/contactis no longer a ComingSoon page — renders mailto link. -
npm run type-check && npm run lint && npm testclean.
navigator.clipboard.writeTextavailability. Modern browsers all support it in secure contexts; the existing Copy link button already uses it, so no new risk.- Stage modal copy drift. The descriptions are duplicated between the spec and the SPA constant. If they diverge, the modal would lie. Mitigation: the constant cites the spec section in a comment so future-me knows where the source of truth lives.
Three commits: plan-open, ProjectDetail enhancements, Contact page.
Surprises:
- Stage descriptions already in
STAGESconstant.StageBadge.tsxexports aSTAGESrecord that already mirrorsspecs/behaviors/project-stages.mdexactly — labels, descriptions, ranks, progress percentages, colors. The new modal just reads from this constant rather than introducing a parallel copy. The spec→ code drift risk is bounded: any update to the spec only needs to touch one place in the SPA. - Home "Start a Project" was already correct. The #83 audit
listed it as a gap (the card should route signed-in users to
/projects/create), but the code atapps/web/src/screens/Home.tsx:134already does exactly that. Marked the audit note as outdated in the plan rather than removing it — useful context for future-me to understand why no Home change appears in this PR. - GitHub URL detection via
new URL(). The naivedevelopersUrl.startsWith('https://github.com/')check would misshttp://github.com/...and accepthttps://github.com.evil.com.URL.parse+ hostname check ('github.com' || endsWith('.github.com')) is the safer shape. - Web Share API not used. Spec said "system share or copy". I
considered
navigator.share(gated by capability detection + fallback to copy) but the copy-only path is simpler, works in every browser, and the difference is invisible to users on desktop. If mobile usage starts mattering, swap in the Share API with copy as fallback.
- Phase 2 — PersonDetail email + slackHandle. Cross-cuts the API
serializer (
apps/api/src/services/serializers/person.tsneeds to surfacePrivateProfile.emailfor self/staff andPerson.slackHandlefor everyone) and the screen. Separate plan because it touches the permissions model. Deferred to plan —plans/screen-gaps-phase2.mdto-be-written. - Phase 3 —
/pages/:slugcontent rendering. Requiresapps/web/src/content/pages/directory with markdown for Mission, Leadership, CoC, Hackathons + a route that reads + renders them. Deferred to plan —plans/static-pages.mdto-be-written. - Phase 4 —
/projects/:slug/buzz/newcreate form. Needs a real form bound toPOST /api/projects/:slug/buzz. The API endpoint exists; just the SPA form is missing. Deferred to plan —plans/buzz-new-form.mdto-be-written. - Web Share API integration. The Share-to-Slack button could
use
navigator.shareon mobile with copy fallback. None — not worth the gate logic until mobile usage data warrants it.