Commit Graph

705 Commits

Author SHA1 Message Date
Matt
a2c6baf718 feat(finale): full ceremony UI — admin console, jury phases+notes, deliberation flow, audience voting, big-screen view
- Admin Ceremony tab: phase driver with real server timers + overtime, run
  order with category grouping + send-to-screens, audience windows + QR
  dialog, override slides, timing log, results reveal builder/stepper
- Admin Deliberation tab: per-category session creation from the round's
  jury group, open/close voting, tally/runoff/override/finalize
- Jury live page: ON_DECK/PRESENTING/QA/SCORING aware, autosaved notes,
  vote comments; LiveVotingForm fixed (criteria score >10 rejection bug,
  permanent lock-out after submit) and made revisable
- Jury deliberation page: identityless submitVote, review-before-rank
  context (finale scores editable, ceremony notes, docs link)
- Jury nav: Live Ceremony + per-category Deliberation links
- Public: rebuilt QR audience voting page (anonymous-safe, favorite-pick
  windows, change-until-close), new /live/ceremony/[roundId] projector view
  with animated reveal, confetti, audience QR slide, override slides

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 18:34:50 +02:00
Matt
c9dc1bfabd feat(finale): deliberation jury identity resolution, rankable projects, score-revision path, session sync
- submitVote resolves the caller's JuryGroupMember participant row server-side
  (was comparing JuryGroupMember id to User id — every juror got FORBIDDEN)
- getSessionWithVotes now includes category projects so the ranking form has
  data before finalize
- liveVoting.vote accepts any finale-ordered project (revision during
  deliberation); timed window still applies to the live project
- live.sendToScreens keeps LiveVotingSession.currentProjectId/status in sync

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 18:15:45 +02:00
Matt
4e6904fa12 feat(finale): reveal controller + public ceremony-state endpoint (no-leak guaranteed)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 18:12:00 +02:00
Matt
45b007334e feat(finale): vote comments, by-round session lookup, my-finale-inputs query
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 18:10:32 +02:00
Matt
6d2fa3369f feat(finale): audience favorite-vote windows with category gating + IP cap
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 18:09:02 +02:00
Matt
6eccfc694e feat(finale): per-project phase machine, server timers, overtime log, juror notes
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 18:07:02 +02:00
Matt
97ef3e59ac feat(finale): server-stamped phase timer helper
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 18:04:51 +02:00
Matt
a66bd728cd feat(finale): schema for phases, audience windows, favorite votes, notes, reveal
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 18:04:25 +02:00
Matt
64f88890f5 fix(auth): make audience vote, live-scores and ceremony routes public
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 18:02:08 +02:00
Matt
dcd85c9b13 docs(finale): implementation plan for grand-finale ceremony system
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 18:01:12 +02:00
Matt
3be1fcd24a docs(finale): approved design spec for grand-finale ceremony system (option C)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 17:54:31 +02:00
Matt
d38fe7887a feat(final-docs): optional-mode banner/panel + admin 'Documents shown to judges' picker
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m31s
- finalist banner/panel render an optional-uploads state (settle to green via
  hasRequired ? allRequiredUploaded : allUploaded; 'optional' copy when nothing required)
- ReviewDocsPicker admin card on the LIVE_FINAL round page to curate judge-visible docs
2026-06-10 15:02:19 +02:00
Matt
28ca7bb0a6 feat(final-docs): judge-doc curation — reviewVisibleRequirementIds filter, picker helper, admin procedures
- listFinalistDocumentsForReview filters prior-round files by the round's
  reviewVisibleRequirementIds (finale uploads always shown; null = show all)
- listReviewVisibilityOptions: distinct prior-round slots + file counts for the picker
- finalist.getReviewDocSettings / setReviewVisibleRequirements (adminProcedure, audited,
  preserves sibling configJson keys)
2026-06-10 15:00:49 +02:00
Matt
d89f67ba57 feat(final-docs): hasRequired/allUploaded on FinalDocumentStatus for optional-uploads mode 2026-06-10 14:58:39 +02:00
Matt
2c311bc65a feat(finals): prominent finalist-docs banner on jury dashboard + gate nav to finals jury
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m37s
Grand-Final jurors couldn't find the finalist documents review: the only entry
point was a top-nav text link (hidden in the hamburger on mobile), with nothing
on the dashboard. Add a prominent dashboard banner (shown only to finals-jury
members) linking to /jury/finals-documents, and gate the nav "Finalist Documents"
link to members so other jurors don't hit a dead "No access" page.

- finalist.canReviewDocuments: lightweight boolean procedure (self-resolves active
  program) so the nav can gate the link without fetching the full payload
- jury-nav: show "Finalist Documents" only when canReviewDocuments
- jury dashboard: FinalsJuryBanner server component, gated via userCanReviewFinals,
  rendered above content regardless of assignment state

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 14:56:02 +02:00
Matt
85937ec942 docs(final-docs): implementation plan for judge-doc curation + optional uploads
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 22:06:20 +02:00
Matt
81352d7bd2 docs(final-docs): spec for judge-doc curation + optional revised uploads
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 21:59:48 +02:00
Matt
8a4184d20f feat(final-docs): judges see all teams' prior-round files; revised uploads behind admin toggle
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m47s
The finals jury needs the teams' EXISTING submissions (pitch deck, exec summary,
business plan, videos from prior rounds) — which all 9 teams already have. So:

- listFinalistDocumentsForReview now returns ALL of each finalist team's files
  across every round (labeled by doc type + round; finale uploads flagged
  'Revised for finals'), with presigned URLs. NOT gated — judges always see.
- Revised re-uploads are now an admin toggle (Round.configJson.allowFinalistRevisedUploads,
  default OFF): gates the banner/panel (getFinalDocumentStatusForProject), the
  upload guard (getUploadUrl/deleteFile), the documents-page round, and the
  reminders (manual + cron). When off, teams aren't prompted/able to upload.
- finalist.get/setRevisedUploadSetting + a Switch on the admin finale overview.
- judge review component rewritten to a per-team labeled file list.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 17:19:09 +02:00
Matt
f8f2d77e3b feat(final-docs): decouple grand-final docs from LIVE_FINAL being ROUND_ACTIVE
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m44s
The Grand Final round = the live event; document upload + judge review happen
in the lead-up BEFORE it opens. So gate them on finalist enrollment + the round
being open-for-docs (DRAFT or ACTIVE, not closed/finalized) instead of requiring
ROUND_ACTIVE. Lets the round stay DRAFT until event time.

- getOpenFinaleRound (was getActiveFinaleRound): status in {DRAFT,ACTIVE}, not finalized
- cron + userCanReviewFinals use the same open-status condition
- getUploadUrl + deleteFile allow a not-yet-closed LIVE_FINAL round
- getMyDashboard openRounds includes the enrolled DRAFT LIVE_FINAL round (finalists only)
- tests: DRAFT now works; CLOSED returns null

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 16:57:24 +02:00
Matt
696d7e9041 fix(final-docs): exclude dropped mentors from doc access; relative in-app reminder link
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m43s
Review follow-ups: mentor.getProjectFinalDocuments now filters droppedAt:null
(a dropped mentor no longer sees a team's final docs); reminder linkUrl is
relative so the in-app notification does client-side nav (email still absolutized).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 16:19:16 +02:00
Matt
f61dcfa89a feat(final-docs): notify mentor when a finalist uploads a Grand Final document
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 16:07:28 +02:00
Matt
146691be00 fix(auth): allow /api/cron paths past middleware (self-guarded by CRON_SECRET)
The middleware matcher intercepts /api/cron/* but the prefix was absent from
publicPaths, so unauthenticated scheduler calls were 307'd to /login and the
cron handlers never ran. All 9 cron routes already enforce x-cron-secret, so
opening the prefix is safe and unblocks the new final-document-reminders cron
(and repairs the existing crons). Same class of gap as the /lunch/pick fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 16:03:55 +02:00
Matt
e4f13aaed4 feat(final-docs): auto pre-deadline reminder cron 2026-06-09 16:00:42 +02:00
Matt
6e1dcc8cbf feat(final-docs): add FinalistConfirmation.finalDocsReminderSentAt
Additive nullable column for the auto document-reminder cron (fires once per
team). Migration hand-authored + applied to dev via db execute (NOT migrate dev,
per drifted-dev-history rule); prod applies it via migrate deploy on next build.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 15:56:46 +02:00
Matt
24c7c4bc6c feat(final-docs): Final Documents panel on team + mentor views
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 15:53:39 +02:00
Matt
8c6a59bad9 feat(final-docs): mentor.getProjectFinalDocuments procedure
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 15:50:42 +02:00
Matt
b66e2071f9 refactor(final-docs): shared review component reachable by jury + admin routes
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 15:47:56 +02:00
Matt
df0be6bb48 feat(final-docs): judge review page + entry points
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 15:44:37 +02:00
Matt
e9e072dda7 feat(final-docs): finalist document review service + procedure with finale-access gate
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 15:36:59 +02:00
Matt
b0a0a71cfe chore(final-docs): script to configure the 4 grand-final file requirements 2026-06-09 15:33:25 +02:00
Matt
61bf5a4032 feat(final-docs): admin manual reminder button
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 15:31:18 +02:00
Matt
26709e2c9b feat(final-docs): manual admin document-reminder blast
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 15:26:50 +02:00
Matt
f3d3a21156 feat(final-docs): GRAND_FINAL_DOCS_REMINDER/SUBMITTED notification types + email template
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 15:23:44 +02:00
Matt
9e058e6ad7 feat(final-docs): finalist upload banner on applicant dashboard 2026-06-09 15:21:08 +02:00
Matt
16e0a08f16 feat(final-docs): applicant.getFinalDocumentStatus procedure 2026-06-09 15:17:47 +02:00
Matt
c53ec23109 fix(final-docs): round-scope file query + guard empty-required edge case 2026-06-09 15:15:22 +02:00
Matt
b1923cf0e1 feat(final-docs): grand-final document status service 2026-06-09 15:09:50 +02:00
Matt
b757aae551 docs(grand-final): implementation plan (4 phases, TDD)
Phase 1: foundation service + finalist banner + manual reminder + 4-doc reconfig
Phase 2: judge review page (finale-access gate, embedded presigned URLs)
Phase 3: mentor-section Final Documents panel (team + mentor)
Phase 4: auto reminder cron + on-upload mentor notification + migration

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 14:57:19 +02:00
Matt
c2afa58606 docs(grand-final): 4-doc set (PDF-only), thin dedicated judge page rationale
Confirmed document set: Final Presentation, Final Business Plan, 1-min Video,
Executive Summary (all required, PDF-only docs), same for both categories.
Judge page stays a thin dedicated page reusing the existing doc viewer because
the finale has 0 assignments / empty jury group (group-based, not assignment-based).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 14:49:26 +02:00
Matt
537de07245 docs(grand-final): spec for final-document upload, mentor surfacing, judge review, notifications
Design for surfacing the already-live Grand Final document upload (PDF + 1-min
video) to the 9 confirmed finalists via a dashboard banner, a read-only judge
review page (Finals Jury group + admins), a Final Documents panel on both the
team and mentor views, and email + in-app reminders (auto cron + manual blast).
Reuses the existing legacy FileRequirement anchor, upload mechanics, and
notification pipeline. Grounded in verified prod state.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 14:31:49 +02:00
Matt
a6fc697e4d fix(auth): allow /lunch/pick public access for accountless external attendees
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m30s
The external dish-picker page is reached via a signed token by attendees who
have no account. The middleware authorized() callback redirected any non
allowlisted path to /login, which is a dead end for accountless users — so the
picker shipped in 8d4f0ba was unreachable in prod (307 → /login). Add
/lunch/pick to publicPaths; data stays gated by token verification in tRPC.

Adds a regression test asserting the path is public and a protected path is not.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 12:18:10 +02:00
Matt
8d4f0bac1e feat(logistics): external attendees self-select lunch dish via tokenized page
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m51s
External lunch attendees had no way to pick their own dish — an admin had to set
it inline and no email was ever sent. (Marine added herself as an external
expecting a dish-selection link and never received one.)

Adds:
- ExternalAttendee.inviteSentAt + additive migration
- HMAC-signed external lunch token (mirrors finalist-token)
- Public no-login picker page /lunch/pick/[token] — dish + allergens + notes,
  gated by the lunch change deadline, read-only after
- tRPC getExternalByToken / setExternalPick (public) + sendExternalInvite (admin)
- Auto-send invite on createExternal when an email is present; per-row resend
  button + status chip (Invited / Picked / no email) in the logistics screen
- Unpicked externals chased by the lunch reminder cron + manual "Send reminders"
- sendExternalDishInviteEmail (branded). Page + email title use the configurable
  venue ("Lunch at {venue}") rather than "grand finale"

Tests: token roundtrip/tamper/expiry, selectUnpickedExternals filter,
get/set-by-token happy + deadline + bad-token, createExternal auto-send,
cron external reminders. Full suite 303 passing; build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 12:04:13 +02:00
Matt
f2c8cc1e80 fix(logistics): Hotels tab crash — Radix SelectItem cannot have empty value
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m58s
The 'Unassigned' rooming option used value="" which throws at runtime
(blank tab behind the error boundary). Use a sentinel value mapped to unassign.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 19:50:18 +02:00
Matt
89ab5cc8e6 test(logistics): remove obsolete single-hotel test (superseded by logistics-hotels)
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m20s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 19:25:41 +02:00
Matt
3bbc80332c feat(logistics): Hotels tab — multi-hotel management + rooming assignment
Rewrites hotels-tab.tsx from the removed single-hotel getHotel/upsertHotel
pattern to the new multi-hotel API: Hotels section (list/add/edit/delete with
occupancy badge) + Rooming section (per-attendee hotel+room+dates assignment,
team-assign shortcut, CSV export).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 19:24:06 +02:00
Matt
9313eb96f0 feat(applicant): My Logistics shows assigned hotel + room
Replace the program-level hotel.findUnique (broken after removing @unique)
with the caller's HotelStay (include hotel) on their AttendingMember.
Returns hotel: {name,address,link,notes}|null and room: {roomNumber,
checkInAt,checkOutAt}|null. MyLogisticsCard renders the Room section
(number + Monaco-time check-in/out) when room is present.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 19:21:29 +02:00
Matt
4cd2651f9c feat(logistics): hotels CRUD + rooming + assignment procedures + travel email
Replace getHotel/upsertHotel with listHotels/createHotel/updateHotel/deleteHotel
(multi-hotel per edition). Add listRooming, assignStay, assignTeamToHotel, and
unassignStay procedures for per-attendee room assignments. Update setFlightStatus
to include attendee's HotelStay in TRAVEL_CONFIRMED notification metadata.
Extend getTravelConfirmedTemplate to render room number and check-in/out dates.
All procedures are adminProcedure and audit-logged. 10 new unit tests green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 19:19:18 +02:00
Matt
75e63eb47f feat(hotel): many hotels per edition + HotelStay (room assignment)
- Hotel.programId no longer unique (many hotels per edition)
- New HotelStay 1:1 with AttendingMember (hotelId, room, check-in/out)
- Program.hotel -> hotels[]

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 19:13:52 +02:00
Matt
200b5e0cb9 docs(logistics): multi-hotel + rooming implementation plan
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 19:09:49 +02:00
Matt
42e6b5f8c0 docs(logistics): multi-hotel + room-assignment design spec
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 19:08:51 +02:00