feat(programs): Denver programs + Past-programs surface + public projects from signups#144
Merged
Conversation
…st-programs surface - Repurpose the dogfooding-2026-berlin fixture/seed as Dogfooding 2026 Denver (slug dogfooding-2026-denver, status completed, luma.com/dogfooding copy). - Add a PitchOff! Denver 2026 program (program_type pitch_off, completed). - Re-point the mock Plata Mia application to the Denver dogfooding id. - ProgramsPage now fetches all programs and renders a 'Past programs' section below the open grid; the rack card badge reflects status (OPEN / COMPLETED / CLOSED) instead of a hardcoded OPEN. Event dates for the Denver dogfooding are left null pending confirmation. PitchOff! submissions + a custom detail layout are tracked as a follow-up.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…om signups
Renders the distinct projects attendees picked (+ interest counts) on the
public /programs/:slug page, aggregated from program_signups without
exposing any attendee PII.
- New public endpoint GET /programs/:slug/projects (no auth) backed by
programSignupService.projectSummaryByProgramId, which groups signups by
the raw_row column whose header matches /project/i and returns
[{ project, count }] sorted by count desc.
- Client api.listProgramProjects + mock aggregation; mock PitchOff signups
fixture so the preview renders the section.
- ProgramDetailPage shows a PROJECTS panel when the aggregate is non-empty.
- Vitest covers the aggregation (grouping, sort, trimming, PII-free shape).
… surrogate The signup CSV parser now handles form exports that collect a Telegram handle instead of an email (e.g. PitchOff!). When no email column exists, it derives a stable surrogate identity '<handle>@telegram.imported' from the Telegram column so rows import + dedupe under the existing NOT NULL email + UNIQUE(program_id, email) schema — no migration. Email-column exports (Luma) are unchanged. - luma-csv.parser.js: TELEGRAM_HEADERS + broader name/timestamp synonyms; EMAIL mode vs TELEGRAM-surrogate mode chosen from the headers; telegram returned on each row (also preserved in raw_row). - Mock importer (preview): mirror the surrogate logic AND capture raw_row (previously dropped), so a preview import populates the public PROJECTS aggregate just like the server. - Tests: 3 new parser cases (surrogate, unusable-handle skip, email wins). Note: the exact 'which project' column is matched heuristically (/project/i); pin it once the real CSV headers are known (#143).
The submissions are per-project builder entries (name, what they built, repo, docs, tools), not vote tallies. Pivot the public projects feature from count-aggregation to project cards. - projectCardsByProgramId: one signup row -> one public-safe card; fields detected from raw_row by header keyword (title/description/repo/docs/ tags), PII columns ignored, non-URL links and empty cards dropped. - GET /programs/:slug/projects returns cards; ProgramDetailPage renders name + blurb + REPO/DOCS links + tool tags. - Mock fixture seeded with a BEST-EFFORT transcription of the PitchOff! Denver responses (names + tools as legible; descriptions + repo/doc URLs left blank pending the real CSV). Preview-only; prod unaffected. - Tests updated for the card shape (PII-free, URL validation, fallbacks). Real data import + exact column pinning tracked in #143.
Collaborator
Author
|
Update: projects pivoted from vote-counts → builder project cards. The PitchOff! submissions turned out to be per-project builder entries (name, what they built, GitHub repo, README/doc link, tools), not vote tallies. So the public PROJECTS section now renders project cards (name · blurb · REPO/DOCS links · tool tags) instead of "N interested".
|
…cards Real responses CSV headers pinned end-to-end: - parser: detect 'Team member name(s)' as name and 'Main Telegram contact' as the surrogate identity (norm now strips parens); + submit/start date synonyms. New test locks the real header shape. - card detector: repo regex now owns the 'Github link or demo URL' column (added \bdemo) and docs no longer matches it, so docs maps to the 'README or project doc link' column; tag split no longer breaks on '/'. - mock fixture: replaced placeholders with the 10 real builder submissions, public-safe fields only (name, what-they-built, repo, docs, tools) — no Telegram/contact. file:// and 'NA' links correctly render as no-link. Verified: server 257 tests; stadium-tester 5/5 (real names + descriptions + repo/docs links + tags render; no PII). Closes the data side of #143.
The public program page shows only project details + contributor names. Everything else from a submission (Telegram contact, prompt choice, dates, network id, etc.) is admin-only — it's stored in raw_row but wasn't shown. - Each admin signup row is now expandable (·DETAILS) into a key/value view of every raw_row field, so admins can see the full submission incl. the real Telegram contact (the public/email column is a surrogate for email-less Tally/Typeform imports, now labelled 'TELEGRAM IMPORT'). - Row header leads with NAME; import hint updated (Luma + Tally/Typeform, email OR telegram identity, extra columns kept admin-only).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three related pieces for surfacing past programs and what was built/pitched at them.
dogfooding-2026-berlin→ Dogfooding 2026 Denver (dogfooding-2026-denver, completed, luma.com/dogfooding copy); add PitchOff! Denver 2026 (pitch_off, completed, 2026-02-21)./programsnow fetches all programs and renders a "Past programs" section below the open grid; the rack card badge reflects status (OPEN / COMPLETED / CLOSED) instead of a hardcoded OPEN./programs/:slugshows the distinct projects attendees picked (+ interest counts), aggregated from the program's signups, PII-free.The PROJECTS feature (new)
GET /programs/:slug/projects— public, no auth. Returns[{ project, count }]sorted by count desc. Raw signups stay admin-gated; only this safe aggregate is public.programSignupService.projectSummaryByProgramId): groupsprogram_signupsby theraw_rowcolumn whose header matches/project/i, counting distinct values. No attendee fields are ever returned./project/i) because the exact CSV header isn't pinned yet. Trivial to hard-pin once the real export lands (see feat: import real PitchOff! submissions CSV + pin the project column #143).api.listProgramProjects(slug)with a mock branch; a representativemockProgramSignups['pitchoff-2026-denver']fixture so the preview renders.ProgramDetailPageshows a PROJECTS panel when non-empty (hidden otherwise).How submissions get there (already shipped, not in this PR)
Admins upload/update each program's CSV at
/admin/programs/:slug(ProgramSignupsSection: file picker → dry-run preview → commit → list/delete). This PR makes the public projection of that data visible.Deferred → #143
Decisions (flag if wrong)
status = completedfor both Denver programs (matchessymbiosis-2025). Say the word forclosed.null(no signal); PitchOff event date 2026-02-21 from the export timestamps.Files changed
client/src/lib/mockPrograms.ts— Denver + PitchOff entries; mock signups fixture;projectSummaryFromMockSignups.client/src/lib/mockProgramApplications.ts— repoint programId.client/src/lib/api.ts—ApiProgramProject+listProgramProjects.client/src/pages/ProgramsPage.tsx— Past-programs section; status-aware badge.client/src/pages/ProgramDetailPage.tsx— PROJECTS section.server/api/services/program-signup.service.js—projectSummaryByProgramId.server/api/controllers/program.controller.js—listProjects.server/api/routes/program.routes.js— publicGET /:slug/projects.server/api/services/__tests__/program-signup.service.test.js— new aggregation tests.server/scripts/seed-dogfooding-program.js— seeds the Denver dogfooding row.Manual Supabase rollout (prod)
(unchanged from before — repurpose the Berlin dogfooding row to Denver, insert PitchOff!; SQL below)
Then import the PitchOff! signups CSV at
/admin/programs/pitchoff-2026-denverto populate the public PROJECTS list.Test plan
npm test(server) — 252 passed (4 new aggregation tests).npm run build(client) — green.npm run lint(client) — clean.stadium-tester@ localhost (mock) — Past-programs 8/8, PROJECTS section 5/5 (incl. a PII-leak check + empty-state check).stadium-tester report (PROJECTS)
Invariants respected
requireProgramAdmin.console.*(client); dark mode only; reused tokens.server/api/**; no new Supabase script.BYPASS_ADMIN_CHECKuntouched.Per CLAUDE.md §6: draft, never merging.