Conversation
Promoted via gh issue create: - #127 express-rate-limit - #128 helmet - #129 ADMIN_SESSION_SECRET startup validation - #130 req.user.chain capture in admin middlewares - #131 audit-log on payment / payout / continuation - #132 tsconfig.app.json strict-mode tightening (phased) - #133 server boot smoke test - #134 shared csvCell util for future CSV exports
User reported that signing into the Dogfooding program as an admin showed
'You need to be a team member on a Stadium project to apply' — even though
the server (requireTeamMemberOrAdminByBodyProject) does let admins through.
The block was purely client-side: ProgramDetailPage only let users open the
apply modal if they were on a project's team list.
The server-side gate already accepts admins as long as the request body
references a real project_id. So the client just needs to give admins a way
to pick which project they're applying on behalf of.
Changes:
- Detect admin status via the existing isAdmin() helper (env-configured
VITE_ADMIN_ADDRESSES list).
- When admin, fetch the full project list (api.getProjects({ limit: 500 }))
in addition to the wallet's own team-membership query.
- The 'must be a team member' gate now only fires for non-admin users.
- Apply button copy switches to 'APPLY ON BEHALF OF…' when the admin has no
team membership; a small 'ADMIN MODE' hint sits below the button so it's
obvious why the picker shows everything.
- Modal mount + projects prop both read from a unified projectsForApply
derived list (admin gets allProjects, regular user gets myProjects).
Verified:
- npm run lint: 0 warnings
- npm run build (tsc + vite): clean
- Server-side gate unchanged — requireTeamMemberOrAdminByBodyProject
already accepts admins, so the API surface posture is preserved.
…llet rate-limit User reported 'rate limit exceeded' and 'Couldn't load admins' toasts when opening AdminProgramPage (e.g. /admin/programs/bitrefill-2026). Root cause: the page mounts 4 admin sections (Admins, Sponsors, Signups, AuditLog) and each section calls getAdminBearerHeaders() in its own mount effect. When the admin session cache is empty, all four cache-misses race and each launches a separate provider.signIn() — Talisman / Polkadot-JS sees 4 SIWS sign requests in a <50ms burst and returns 'rate limit exceeded'. Fix: store the in-flight sign promise on a ref so concurrent callers await the same operation instead of starting parallel signs. After the first one resolves, the bearer is cached and later callers hit the cache path. Effect: - One wallet popup per session (was up to 4 on AdminProgramPage) - 'Rate limit exceeded' toast no longer fires from the burst - No behavior change for non-concurrent callers (single ProgramAdminsSection, one-off admin mutations, etc.) Verified: lint clean, tsc + vite build clean.
…lf-of-any-project fix(admin): admin apply-on-behalf-of + wallet sign rate-limit dedupe
…imary nav Closes #137, closes #138. - Hero <p> on / now reads 'Stuff people have built here.' - M2 Incubator description in mockPrograms updated to the mentorship/milestone framing. - Generic Dogfooding description appends the ongoing-application CTA. - M2 tab removed from the primary nav (TABS in Navigation.tsx); /m2-program route is preserved. Mock fixtures only. Supabase prod rows for M2 / generic Dogfooding need a manual description update via the dashboard.
…y default) Closes #139. - Hidden SoundCloud HTML5 Widget iframe for soundcloud.com/pommeshdrms, always mounted (outside the collapsed/expanded branches) so audio survives the rack's auto-collapse after the first interaction. - On widget READY: setVolume(0) then play() so muted autoplay works within browser autoplay policies. - Mute/unmute toggle in the expanded view's first row, between the % LCD and the collapse chevron. Volume2/VolumeX lucide icons matching the chevron's lcd styling. aria-pressed reflects state. - iframe allow='autoplay; encrypted-media' to suppress Chromium's encrypted-media permissions-policy warning emitted by SoundCloud's widget EME registration. - Lazy-loaded SC widget script with idempotent guard.
Moves the mute/unmute toggle out of the brightness row into a dedicated AUDIO row at the bottom of the rack (mirrors BRIGHTNESS / AUTO / PALETTE). - 'pommeshdrms' artist name displayed. - SC link → https://soundcloud.com/pommeshdrms. - IG link → https://www.instagram.com/pommes_hdrms (matching handle). - Both external links open in a new tab with rel='noopener noreferrer'. - Mute toggle keeps its existing aria-pressed / aria-label contract.
…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.
Replace favicon set with a minimal three-pixel mark on a mid-grey background, designed on a 16x16 grid for crisp small-size rendering. Adds favicon.svg, fixes broken .public/ paths in index.html, and fills in site.webmanifest name/theme.
…dCloud Pulls metadata via the widget's getCurrentSound and renders a dim, truncated 'title · genre' tag next to the artist name. Refreshed on the widget READY event and on each PLAY (track change). Falls back to nothing when the widget is offline or a track has no genre.
…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.
…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).
feat(brightness-rack): minimal SoundCloud audio player, muted by default (#139)
feat(programs): Denver programs + Past-programs surface + public projects from signups
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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
Promotes 19 commits from
developtomain(production branch). Draft — review before merging; do not expect this to update prod on its own (see Deploy note).Highlights since
main(#126)/programs, and public project cards on program pages derived from signups (PII-free).Production currently serves a build older than
main—mainalready has the/api/programsrouter but the live server returns 404 for it. So merging this PR will not make prod live by itself; the Railway server and Vercel client must actually redeploy from the updated branch. Please verify the Railway service's tracked branch + trigger a deploy after merge.Data already in production Supabase
The Denver program rows, the #137 copy fixes, and all 10 PitchOff! signups are already written to the prod Supabase project (
hxojfhlrtffcvksxkvwf). Once the server redeploys with this code,/programs+ the project cards + admin signup detail will render immediately — no further data step needed.Post-deploy verification
GET /api/programs/pitchoff-2026-denver/projects→ 200 with project cards (currently 404)./programs→ Past programs shows both Denver events (COMPLETED)./programs/pitchoff-2026-denver→ project cards (names + repo/docs/tags, no contact info)./admin/programs/pitchoff-2026-denver→ 10 signups; expand a row → full submission incl. Telegram.Risk
Large promotion. All constituent PRs (#136, #141, #142, #144) were reviewed + tested individually (server tests, client build/lint, stadium-tester). No squash — preserves history.