Repository navigation
Application portal: merge hacker, mentor/judge application and staff portal - #9
Merged
Merged
Conversation
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ly blurb Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Validate that the resume URL is a drive.google.com file link and update the label and placeholder to say it must be a .pdf Google Drive link. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign-in goes to /portal, which looks up the person's application and opens /portal/mentor, /portal/judge or /portal/hybrid. Adds role-specific run of show and handbook names, a Judging Portal placeholder, DocuSign embedded waiver signing (edge function), and routing tests. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Run of show from the DataHacks main venue spreadsheet (public + judging rows), with required mentor office hours and judge events, and the venue under the heading - Demo handbook content from the DataHacks 2026 Hacker Handbook - Status pill beside the greeting; labels without trailing periods - Dev ?preview mode gets a simulated DocuSign so the waiver lock/unlock flow can be tried locally; waiver page shows submission status; keep close/decline messages - public/dev/ is git-ignored for local test files Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n can be accessed through 'The End' section of sidebar or 'Join the adventure' button
…abel
Mentor/judge submissions now insert into public.applications with
applicant_type ('mentor' | 'judge' | 'hybrid'), status and submitted_at.
Hacker applications set applicant_type 'hacker'. Removes the
"APPLICATION" label under the mentor/judge heading.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The callback used to save whatever ?role= was in the link, so anyone could make themselves a judge. Roles now come back from the profile row, which a database trigger sets, and the migration locks profiles, applications and resumes to their owners. Portal paths live in one file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l' into merge-final
… into merge-final # Conflicts: # src/App.tsx
… into merge-final # Conflicts: # CLAUDE.md # src/App.tsx # src/pages/AuthCallback.tsx
… accepted applications Staff applications were sent without signing in, so the portal had to find them by matching the typed email to the Google account later. Now everyone signs in first: the application is tied to the account, the staff portal reads it directly, and the lookup function is gone. An accepted mentor/judge/hybrid application now sets the person's role, kept in sync by the database. All the SQL is one migration that builds the database from scratch or brings the live one up to date, replacing the two earlier files. Also restores jsdom's localStorage in tests, which Node 25+ hides. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…agram Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The live project already has events, per-event roles (event_roles), an attendee roster with check-in codes, and triggers that grant roles when an application is accepted. None of it is in this repo, and the earlier migration would have dropped its organizer access. Now the portal reads roles from event_roles, sends the current event with every application (so accepted people get onto the check-in roster), and the migration only opens applications to signed-in mentors/judges, closes the anonymous route, and stops the browser setting profile role or email. Applied to the live project on 2026-10-21 after a rolled-back dry run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hema dump Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
Everyone lands in /portal after Google sign-in. Before acceptance the sidebar has only Status (the default) and Application; acceptance opens the handbook, run of show and liability waiver, plus Judging for judges and, on the day, a Mentors tab for hackers showing who's checked in. Contacts sit in the sidebar footer. People who haven't applied pick hacker or mentor/judge and fill the form inside the portal; both forms now ask for a phone number. The Minecraft hacker pages are gone and their old addresses forward here. /portal/organizer lists hacker and mentor/judge applications by status, with search, CSV export and the full application. Accept/waitlist/reject only stages a decision; releasing them takes typing the event code, applies them in one database call (roles and check-in codes follow from the existing triggers) and emails everyone through the send-email function. Organizers can also email any group from the console. Dev ?preview runs it on sample data. The new migration adds phone and the staged-decision columns, release_decisions and mentor_roster, and lets organizers read resumes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
…ion dates Two migrations were dated tomorrow (2026-10-23), which would make the CLI refuse any migration created today; they're renamed to today, keeping their order. Neither is recorded in the CLI's migration history yet, so the rename is safe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
…uction - Baseline: the shared schema captured from production (public schema with the check-in tables, functions, triggers, policies and grants, plus our storage policies and Realtime membership) in one migration; the four hand-applied migrations move to supabase/archive/. A second migration adds the resumes bucket and the DH26 event row if missing. Both applied to the new staging project, which then matched production exactly: schema, grants, storage policies and Realtime. - Grants are reset per object before production's exact grants, because a fresh project's default privileges would otherwise give the API roles more. - supabase-ci (PRs): migration rules, new migrations tried on staging in a rolled-back transaction, edge functions type-checked. - supabase-deploy (main): staging push, lint and functions, then production dry run, push and functions; production stays locked until SUPABASE_PROD_BOOTSTRAPPED is set after the one-time history repair. - supabase-drift and supabase-backup run nightly; the backup is encrypted with BACKUP_PASSPHRASE before it leaves the runner. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
…et already ran Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
Database changes through GitHub Actions: staging first, then production
A week of testing runs against a separate event, so the real DH27 starts clean. Event codes must be 2-10 letters or digits, hence DH27BETA rather than DH27-betaTest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
The descent now runs landing, last year, tracks, challenges, prizes, sponsors, speakers, FAQ, with the footer as bedrock underneath. Each section is its own layer of the world: Overworld, sulfur caves, lush caves, the mineshaft, the deep dark, the stronghold, the End city, the End. Mentors & judges, the run of show and the application leave the scroll. They still open as their own pages for old links, and /datacraft/trials and /stronghold redirect to them, but the public site links to neither: the run of show lives in the portal now, and the nav and hero have Apply as a hacker and Apply to mentor or judge buttons that go straight to Google. Challenges is a new placeholder section for the design team. Lush caves and the End city borrow the closest existing backdrops until they have their own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
Site shell: reorder sections, one layer per section, apply buttons in the nav
Two simple pixel-block backdrops (moss, clay and glow berries; end stone, purpur and end rods) made by scripts/generate-basic-layer-art.py to the same rules as the other reliefs, replacing the borrowed tiles until the design team draws the real ones. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
The school suggestions now come from the open Hipo university-domains-list (MIT licensed), filtered to the US and Canada: 2,494 schools, A-Z. Typing still accepts a school that isn't listed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
The browser's datalist popup can't be styled, so the school field now uses its own searchable list: it opens after two letters, puts matches that start with what was typed first, and works with the arrow keys, Enter and Escape. Schools not on the list can still be typed in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
"UCSD", "uc san diego" and "san diego uc" all find University of California, San Diego; "standford" finds Stanford. Common nicknames (UCLA, SDSU, Cal Poly, UBC, U of T, ...) jump to the right school, UC / CSU / Cal State expand to the full system name, filler words are ignored, and a typo or two is forgiven. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
scripts/dev/create-dummy-applicant.mjs makes an @example.com test account and submits its application signed in as that person, so every database rule applies. /dev/test-accounts (npm run dev only, left out of builds) opens a dummy applicant's portal in a tab that keeps its own login, so several can be open next to the organizer console. The shared test password lives in .env.local. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
Test tabs shared one storage key, and Supabase syncs logins between tabs that share a key, so each new test tab pushed its login into the others. Their requests then went out as the wrong person, found no application, and showed the empty form. Each dummy now has its own key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
Portal pages showed the main site's title. Now the tab says where you are: "Status · DataHacks Application Portal", "Hacker Application · ...", "Organizer Console · DataHacks" and so on, and the site's title comes back when you leave the portal. Also lists the six new dummy mentors and judges on the dev test-accounts page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
Picking accept, waitlist or reject now moves the row into that tab at once, marked "Not released" (and what the applicant still sees, if they'd already heard). Releasing stays the separate step that tells people. Also takes the portal role away when a released acceptance is changed, so someone moved from accepted to rejected loses the accepted-only pages. Their check-in code already went. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
docs/testing/portal-test-plan.md is the checklist for teammates and test agents: both application forms (including bad input), the organizer console, check-in, and phone width, with a bug report format. The dummy-applicant script can now make an account with no application, and ?go= on the dev test-accounts page drops a tester straight onto a form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
Hackers now get a page per topic instead of one handbook: Resources, Transportation, Travel, Reimbursement, Run of show, Food, Mentorship, Prizes, Tracks & challenges, Hardware, Devpost, then Mentors (open now for testing) and the waiver. Content stays in handbookContent; Travel, Reimbursement and Hardware have marked drafts. Mentors and judges keep their handbook, and old hacker handbook links open Resources. Pages load inside the portal frame with a dark fallback, the page behind the portal is dark, and the other pages are fetched in the background. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
Pasting "+1 858 555 0100" saved (185) 855-5010. A leading country code 1 is now dropped once there are 11 digits. Found while checking a test agent's report. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
Signing out clears every saved login in the browser so the next person starts clean. In a dev test tab that also signed the organizer out of the console in the next tab. Test tabs now clear only their own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
"contact_email" puts a real address on a dummy's application, so a release or blast can be checked end to end in an inbox you own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
- ci: type-check, test and build on every pull request. It already caught a tab-title test that only passed with a local .env, so the titles now live in their own module. - deploy: give every portal page its own entry (the organizer console, the apply forms and the hacker pages were missing, so they were served as 404s). Hacker pages come from the same list the sidebar uses. - DH26 gets its dates (April 18-19, 2026). With none, the check-in app sorted it first and checked people in to last year's event. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
DH27BETA test data stays next to the real DH27, so: - Resumes in storage were readable by an organizer of any event; now only by organizers of an event the resume was submitted to (migration). - An application can no longer move to another event, by its applicant or an organizer of both events (migration, trigger). - The signed-waiver flag in localStorage was one key for every event and every account on the browser; it is now per event and per person. - Staging a decision also filters on the current event. - docs/event-isolation.md: how each piece is scoped, and the checklist for switching EVENT_CODE to DH27. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
scripts/backup/restore-drill.sh restores a backup (a file, the newest nightly artifact, or a fresh dump taken exactly like supabase-backup) into a throwaway Postgres 17 container, compares row counts and row checksums with production, spot-checks a DH27BETA application, and deletes everything. supabase-restore-drill runs it every Sunday. docs/backups.md covers what is and isn't backed up and how to restore in an emergency. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
None in production lacked one (0 of 24 on 2026-10-23), so make it a rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EgBz2EXk65565W9vj1xQ75
Keep each event's data to itself (DH27BETA vs DH27)
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.
Brings the application portal together in one branch: Elisa's hacker portal update (#8), Satyash's mentor/judge application (#7), and Satyash's mentor/judge/hybrid portal (
Mentor/Judge/Mentor+Judge-pages, which had no PR). Merging this closes #7 and #8.What changed on top of the three branches
events, per-eventevent_roles, anattendeesroster with check-in codes, and triggers that grant roles when an application is accepted. The portal now reads roles fromevent_rolesand sends the current event (EVENT_CODEinsrc/portal/event.ts) with every application, so accepted people reach the check-in roster. Before this, the sign-in link's?role=was saved on the profile; now the browser can't set a role or a status./portal. Someone coming from the mentor/judge sign-in pages without one goes to the application form. Everyone else goes to the hacker dashboard.supabase/migrations/20261021000000_application_portal.sql, replaces the two earlier SQL files. It lets signed-in mentors and judges apply, closes the anonymous route, and stops the browser setting profile role or email. It has already been applied to the live project, after a dry run that was rolled back.src/portal/paths.ts), and the deploy workflow writes entry pages for the new routes.localStorageagain, which Node 25+ hides. 24 staff-portal tests were failing on newer Node because of this.Before this works on the live site
Add
https://datahacks.ds3ucsd.com/**under Supabase → Authentication → URL Configuration → Redirect URLs. The list is empty, so Google sign-in on the real site can't return to/auth/callback.Testing
?preview, and "Create an Account" opens the hacker sign-in.Remaining loose ends are in
docs/application-portal-todo.md: a single entrance and one portal for hackers too, DocuSign credentials, real contacts, waiver records in Supabase, and Evan's branch.🤖 Generated with Claude Code