Skip to content

prod: ship track-based prize structure, submission polish, organizer judging dashboard#571

Closed
0xdevcollins wants to merge 5 commits into
mainfrom
feat/submission-visibility-hidden-until-results
Closed

prod: ship track-based prize structure, submission polish, organizer judging dashboard#571
0xdevcollins wants to merge 5 commits into
mainfrom
feat/submission-visibility-hidden-until-results

Conversation

@0xdevcollins

@0xdevcollins 0xdevcollins commented May 20, 2026

Copy link
Copy Markdown
Collaborator

Why a new PR

Prior PRs #564 and #570 are marked MERGED in GitHub but the commits never landed on `main`. Opening this fresh PR so the entire stack reaches prod.

What ships (5 commits)

80a8270  feat(hackathons): add "hidden until results" submission visibility mode
bf06ced  feat(hackathons): track-based prize structure, submission polish, tracks UI
8a9b4ad  fix(submissions): tighten Zod schema + surface backend debug info
a36a87c  fix(submit-page): hydrate Phase A fields + stop wiping user input on re-render
08b9ca2  feat(judging): organizer dashboard — coverage, preview, per-track results (#570)

Submitter side

  • Track picker + per-track answers (prompt, custom questions, required artifacts) on the submission form.
  • Phase A polish fields: tagline, builtWith chips, screenshots, license, code-attestation.
  • Submit page memoizes `initialData` so user input no longer gets wiped by parent re-renders, and now hydrates Phase A fields + trackEntries on edit (closes the "values keep switching to empty" report).
  • Tightened Zod schema mirrors backend max lengths so validation fires inline (projectName 100, description 5000, URL 500).
  • Toast surfaces the backend `debug` field on dev/staging so "Data validation failed" shows the real Prisma reason.

Organizer side

  • Track CRUD settings tab with per-row bulk-opt-in for retrofitting existing submissions.
  • New Rewards step: 3-card prize structure picker, per-tier kind toggle + track dropdown, inline Manage Tracks dialog.
  • "Hidden until results" submission visibility mode.

Organizer judging dashboard

  • `CoverageMatrix` on Overview — heatmap, surfaces idle judges + orphan submissions.
  • `AllocationPreviewCard` on Results — read-only allocator dry-run with publish blockers, EXCLUSIVE stacking made visible.
  • `TrackResultsSection` on Results — per-track collapsible standings with prize chips.

Public hackathon page

  • Overview prize section partitioned into Overall + Track.
  • Sidebar tier list shows TRACK · prefix with resolved track names.
  • Winners tab gets per-track winner cards.

Pairs with

Backend PR (BE `main` is missing the dashboard endpoints; this FE references them).

Risk

All FE changes. No backend mutations from FE that didn't already exist. New components render `null` when their data isn't available — graceful degradation if backend isn't deployed first.

Test plan

  • `tsc --noEmit` clean
  • `next lint` clean
  • Smoke on prod: open the live hackathon's submission form, verify tagline / builtWith / license / attestation / tracks are visible and persist.
  • As organizer: verify Coverage matrix on Overview, Allocator preview + per-track sections on Results.

Deploy order

  1. Deploy backend first.
  2. Deploy frontend.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Track-based prize structures: Organizations can now configure hackathon prizes by overall standings, per-track winners, or tracks-only mode
    • Track opt-in for submissions: Participants can select and compete in specific tracks with custom requirements
    • Submission polish fields: Added taglines, built-with tags, screenshots, license, and code attestation
    • Track winners display: Per-track competition rankings and winners now visible to participants
    • Judging dashboard: New coverage matrix and allocation preview for organizers to review judging progress

0xdevcollins and others added 5 commits May 15, 2026 08:55
Surfaces the new HIDDEN_UNTIL_RESULTS option (added in the nestjs PR) in
the organizer settings tab. Reorders the three visibility options so the
recommended "Shortlisted only" leads, the new "Hidden until results are
announced" sits in the middle, and "All submissions" comes last. Rewrites
the copy on the "All submissions" choice that incorrectly claimed
disqualified projects would be shown -- they never were on the backend,
and Phase 2 makes that an explicit guarantee. Aligns the form's default
and API-fallback value with the backend default (ACCEPTED_SHORTLISTED,
not ALL) so organizers don't see a misleading initial selection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…cks UI

Wires the frontend for the new track-based prize flow:

- New TracksSettingsTab with full CRUD: name/slug/description/eligibility/
  prompt/customQuestions/requiredArtifacts; per-row bulk-opt-in action with
  confirmation dialog for retrofitting existing submissions
- RewardsTab gains a 3-card prize structure picker, per-tier kind toggle and
  track dropdown, amber "tracks unbound" banner, and an inline Manage Tracks
  dialog embedding the settings table
- SubmissionForm: track picker + per-track answers (prompt / custom
  questions / required artifacts), tagline, builtWith chips, screenshots,
  license, code attestation, with soft compliance gate for already-submitted
  submissions. trackIds hydrate from trackEntries on edit so bulk-opted-in
  submitters don't strip themselves out
- SubmissionDetailModal renders tagline, screenshots, built-with, license
  badge, and per-track answers
- Public hackathon page: Overview splits prizes into Overall/Track sections;
  sidebar tier list shows TRACK prefix and looks up track names; Winners tab
  gets a Track Winners section with per-track cards
- API client: lib/api/hackathons/tracks.ts with listTracks /
  listOrganizerTracks / createTrack / updateTrack / deleteTrack /
  bulkOptInAllSubmissions, plus types for HackathonTrack,
  TrackCustomQuestion, TrackRequiredArtifact, TrackAnswer,
  SubmissionTrackEntry, BulkOptInResult
- Hackathon provider/hooks expose trackWinners and per-track entries

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add max constraints that previously only existed on the backend DTO so
  validation fires inline (projectName 100, description 5000, URL 500).
- ApiErrorField gains an optional `debug` field that the backend Prisma
  filter populates outside production.
- useSubmission's error formatter prefers `debug` over the generic field
  message when present, so toasts show the real Prisma reason behind
  "Data validation failed" instead of a blank "validation: …" line.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…re-render

The submit page mapped `initialData` inline on every render, which (a)
recreated the object reference on every render so the form's reset
effect fired continuously and wiped values the user was typing, and (b)
dropped the Phase A fields entirely (tagline, builtWith, screenshots,
license, codeAttestedAt) plus trackEntries. The combined effect was the
documented symptom — only logo and videoUrl survived the save because
those round-tripped through the type-narrowed object literal, while
tagline / builtWith / license kept appearing to "switch to empty".

- Memoize `initialData` against `mySubmission` so the reference only
  changes when the underlying submission actually changes.
- Pass through Phase A fields and trackEntries so the form can hydrate
  the saved values, and so a follow-up save doesn't write back empties.
- Widen SubmissionFormContent's `initialData` prop to accept the raw
  server-side extras (trackEntries, codeAttestedAt) that the form
  already consumes via cast — keeps the parent's hydration explicit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ults (#570)

Pairs with boundless-nestjs feat(judging) organizer dashboard upgrades.
Adds three new components to the organizer judging page; no changes to
existing components, judges, or scoring flows.

- `CoverageMatrix` — heatmap on the Overview tab. Rows are
  submissions, columns are judges. Surfaces idle judges (mostly-empty
  columns) and orphan submissions (rows with 0-1 scores) at a glance —
  both block a defensible publish.

- `AllocationPreviewCard` — sits above the Publish button on the
  Results tab. Read-only allocator dry-run showing overall placements
  + per-track winners exactly as publish-results would commit. Calls
  out EXCLUSIVE stacking effects (track leader losing to overall),
  plus surfaces publish gates (deadline, completeness, partner
  allocation) so the organizer sees blockers without attempting to
  publish.

- `TrackResultsSection` — per-track collapsible standings on the
  Results tab. Each section is scoped to a track's opt-ins, sorted by
  averageScore, with the bound prize tier shown as a chip. The leader
  is highlighted as the current pick — soft preview only; the
  Allocation Preview above shows the authoritative EXCLUSIVE-stacked
  outcome.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented May 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
boundless-kd16 Ready Ready Preview, Comment May 20, 2026 8:15am

@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: be98d2d4-74a7-4187-a9d2-9a55053c61f4

📥 Commits

Reviewing files that changed from the base of the PR and between 1e3907e and 08b9ca2.

📒 Files selected for processing (28)
  • app/(landing)/hackathons/[slug]/components/sidebar/PoolAndAction.tsx
  • app/(landing)/hackathons/[slug]/components/tabs/contents/Overview.tsx
  • app/(landing)/hackathons/[slug]/components/tabs/contents/Winners.tsx
  • app/(landing)/hackathons/[slug]/components/tabs/index.tsx
  • app/(landing)/hackathons/[slug]/submit/page.tsx
  • app/(landing)/organizations/[id]/hackathons/[hackathonId]/judging/page.tsx
  • app/(landing)/organizations/[id]/hackathons/[hackathonId]/settings/page.tsx
  • components/hackathons/submissions/SubmissionDetailModal.tsx
  • components/hackathons/submissions/SubmissionForm.tsx
  • components/hackathons/winners/WinnersTab.tsx
  • components/organization/hackathons/judging/AllocationPreviewCard.tsx
  • components/organization/hackathons/judging/CoverageMatrix.tsx
  • components/organization/hackathons/judging/TrackResultsSection.tsx
  • components/organization/hackathons/new/NewHackathonTab.tsx
  • components/organization/hackathons/new/tabs/RewardsTab.tsx
  • components/organization/hackathons/new/tabs/schemas/rewardsSchema.ts
  • components/organization/hackathons/settings/SubmissionVisibilitySettingsTab.tsx
  • components/organization/hackathons/settings/TracksSettingsTab.tsx
  • hooks/hackathon/use-hackathon-queries.ts
  • hooks/hackathon/use-submission.ts
  • lib/api/api.ts
  • lib/api/hackathons.ts
  • lib/api/hackathons/index.ts
  • lib/api/hackathons/judging.ts
  • lib/api/hackathons/tracks.ts
  • lib/providers/hackathonProvider.tsx
  • types/hackathon/core.ts
  • types/hackathon/participant.ts

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.


📝 Walkthrough

Walkthrough

This PR extends hackathon prize structures to support per-track prizes alongside overall prizes, and enriches submissions with track opt-in and Phase A polish fields (tagline, screenshots, license attestation). Organizers can configure prize structures, manage tracks, and view per-track judging results, while participants see track-aware prizes and can opt into specific tracks.

Changes

Track Data Model and API

Layer / File(s) Summary
Track and prize-structure type contracts
types/hackathon/core.ts, types/hackathon/participant.ts, lib/api/hackathons.ts
PrizeTier, HackathonRewards, and Hackathon types extended with optional kind (OVERALL | TRACK) and trackId on tiers. New HackathonPrizeStructure union type (OVERALL_ONLY | OVERALL_AND_TRACKS | TRACKS_ONLY) and tracksMaxPerSubmission cap added. ParticipantSubmission and CreateSubmissionRequest extended with trackEntries, trackIds, trackAnswers for opt-in, and Phase A fields (tagline, builtWith, screenshots, license, codeAttested). New SubmissionTrackEntry interface and HackathonTrackWinner types for track-based winners.
Track API functions and endpoints
lib/api/hackathons/tracks.ts
New module exporting listTracks, listOrganizerTracks, createTrack, updateTrack, deleteTrack, and bulkOptInAllSubmissions with associated request/response types and validation.
Judging coverage and allocation-preview API
lib/api/hackathons/judging.ts
JudgingResult extended with trackIds array. New JudgingCoverage, AllocationPreview data models and getJudgingCoverage, getAllocationPreview API functions for organizer dashboards.
Prize and tier schema validation
components/organization/hackathons/new/tabs/schemas/rewardsSchema.ts
prizeTierSchema and rewardsSchema updated with prizeStructureSchema, prizeTierKindSchema, and cross-field superRefine rules enforcing track/overall tier consistency and structure requirements.
API error handling enhancements
lib/api/api.ts, hooks/hackathon/use-submission.ts
ApiErrorField extended with optional debug field. getApiErrorMessage improved to handle debug field and provide better fallback messages.

Data Loading and Context

Layer / File(s) Summary
React Query hooks for tracks and winners
hooks/hackathon/use-hackathon-queries.ts
Added hackathonKeys.tracks cache key builder, new useHackathonWinnersWithTracks hook bundling overall and track winners, and useHackathonTracks hook for fetching hackathon tracks with enabled gating.
HackathonDataProvider context updates
lib/providers/hackathonProvider.tsx
Provider updated to fetch combined winners/trackWinners via useHackathonWinnersWithTracks and distribute trackWinners through context alongside existing overall winners.

Public Prize and Winners Display

Layer / File(s) Summary
Total prize pool with track-aware tier labels
app/(landing)/hackathons/[slug]/components/sidebar/PoolAndAction.tsx
PoolAndAction fetches track metadata when prizeStructure is track-enabled, builds track→name lookup, and renders tiers with distinct "Track ·" prefix for TRACK tiers using resolved track names, while overallIdx counter preserves correct 1st/2nd/3rd place labels for interleaved overall tiers.
Overview page prizes partitioned by overall and track
app/(landing)/hackathons/[slug]/components/tabs/contents/Overview.tsx
Overview partitions prizeTiers into overall vs track tiers and renders separate "Overall Prizes" and "Track Prizes" sections, using trackById lookup to enrich track cards with track name/type/description (with fallbacks and loading states).
Winners page with track-winners section
app/(landing)/hackathons/[slug]/components/tabs/contents/Winners.tsx
Winners tab now derives hasOverall and hasTracks from data and shows "Coming Soon" only when both are empty. New "Track Prizes" section renders each track label with Layers icon and adapts trackWinners into GeneralWinnerCard shape.
Tab navigation reflecting combined winners count
app/(landing)/hackathons/[slug]/components/tabs/index.tsx
HackathonTabs reads trackWinners from context, enables winners tab if either overall or track winners exist, and computes badge from combined winner counts.
WinnersTab with track-winner card rendering
components/hackathons/winners/WinnersTab.tsx
WinnersTabProps extended with trackWinners array. New "Track Winners" section with TrackWinnerCard component displaying track name, formatted prize, participant avatars with profile links, team name, and project links.

Submission Entry with Tracks and Phase A Polish

Layer / File(s) Summary
Submission form schema with tracks and polish
components/hackathons/submissions/SubmissionForm.tsx
Base Zod schema extended with trackIds (array), trackAnswers (per-track prompt/custom/artifact answers), Phase A fields (tagline, builtWith, screenshots, license, codeAttested boolean), stricter URL validation, and tightened empty-field handling.
Submission form fields and logic for tracks and polish
components/hackathons/submissions/SubmissionForm.tsx
Form component fetches available tracks (OPT_IN filtered), renders track checkbox UI with cap, renders per-track answer inputs (prompt, custom questions, artifacts) when tracks selected. Submit handler filters empty trackIds, constructs trackAnswers only for selected tracks, normalizes Phase A to undefined when empty, and enforces license/codeAttested compliance for new submissions. Review step extended with Tracks summary and "License & attestation" section.
SubmissionFormContent initialData expansion
components/hackathons/submissions/SubmissionForm.tsx
SubmissionFormContentProps.initialData expanded to include trackEntries (with nested trackAnswers) and codeAttestedAt for edit-mode hydration. Form.reset refactored to derive trackIds from trackEntries, reconstruct trackAnswers, and map Phase A fields.
Submit page initialData memoization
app/(landing)/hackathons/[slug]/submit/page.tsx
Submit page builds memoized initialData from mySubmission including project/team data, Phase A fields, and trackEntries, stabilizing object identity for SubmissionFormContent.
SubmissionDetailModal with track entries and polish display
components/hackathons/submissions/SubmissionDetailModal.tsx
Modal content expanded with optional tagline, screenshots gallery, per-track answer details (filtered to tracks with content), builtWith chips, and License badge in footer. Footer flex layout updated to allow wrapping.

Organizer Prize and Track Configuration

Layer / File(s) Summary
Prize tier kind and track binding UI
components/organization/hackathons/new/tabs/RewardsTab.tsx
PrizeTierComponent extended with availableTracks and tracksEnabled props, rendering conditional tier kind selector (Overall vs Track) and track ID dropdown (when kind is Track).
RewardsTab prize structure picker and track management
components/organization/hackathons/new/tabs/RewardsTab.tsx
RewardsTab watches prizeStructure to compute tracksEnabled and detect unbound active tracks. New "Prize structure" picker (Overall only, Overall + Tracks, Tracks only) with conditional guidance and manage-tracks button. Internal track state with listOrganizerTracks fallback. Inline manage-tracks dialog triggers refetchTracks on close to sync tier binding dropdowns. normalizedDefaultValues preserves kind and trackId from draft initialData.
RewardsTab integration with organizer/hackathon context
components/organization/hackathons/new/NewHackathonTab.tsx
NewHackathonTab passes organizationId and hackathonId into RewardsTab for track fetching and management.
TracksSettingsTab for track CRUD operations
components/organization/hackathons/settings/TracksSettingsTab.tsx
New tab component listing organizer-specific tracks in a table with edit/delete/unarchive actions and a create/edit dialog. Form includes track metadata (name, slug, description, type, display order, eligibility) and "Phase B" fields (prompt, custom questions, required artifacts with sanitization). Delete uses alert dialog with backend-aware messaging. Bulk "Opt in all submissions" action with confirmation dialog and result summary.
Settings page Tracks tab integration
app/(landing)/organizations/[id]/hackathons/[hackathonId]/settings/page.tsx
Settings page imports Layers icon and TracksSettingsTab, renders new tracks tab trigger/content, and expands Rewards tab initialData with prizeStructure and tracksMaxPerSubmission.
Submission visibility settings defaults update
components/organization/hackathons/settings/SubmissionVisibilitySettingsTab.tsx
Form defaults submissionStatusVisibility to ACCEPTED_SHORTLISTED (instead of ALL), and supports new HIDDEN_UNTIL_RESULTS option with updated radio labels/descriptions.

Organizer Judging Dashboard with Track Results

Layer / File(s) Summary
CoverageMatrix heatmap for judge-submission scoring coverage
components/organization/hackathons/judging/CoverageMatrix.tsx
New component fetching judges-vs-submissions scoring coverage and rendering collapsible heatmap table with judge columns (initials, scored count), per-submission rows (project name, per-cell scored markers with tooltips), and submission status (Orphan, partial, covered). Includes completion percentage and error handling with re-fetch on org/hackathon/refreshKey change.
TrackResultsSection for per-track standings
components/organization/hackathons/judging/TrackResultsSection.tsx
New component grouping JudgingResults by trackIds, sorting results by averageScore per track, and rendering collapsible section per visible (non-archived) track. Shows prize badge, entry count badge, current leader preview (with score), and either empty-state or JudgingResultsTable when expanded.
AllocationPreviewCard for publish gate feedback
components/organization/hackathons/judging/AllocationPreviewCard.tsx
New component fetching allocator preview and computing publish blockers (deadline, completeness, reviewed count, partner contributions). Renders status badge (can publish or blocked), warning panel listing blockers, "Overall placements" table (rank, project, score, formatted prize, Override/Computed label), and per-track "Track winners" sections (track name, optional prize, winner details, runners-up).
Judging page CoverageMatrix and TrackResultsSection wiring
app/(landing)/organizations/[id]/hackathons/[hackathonId]/judging/page.tsx
Judging page fetches hackathon and tracks via hooks, computes prizeByTrackId mapping from prizeTiers. Overview tab renders CoverageMatrix with submissions page as refresh key. Results tab replaces prior "Finalize Competition" CTA with AllocationPreviewCard, and replaces "Final Winners" rendering with conditional TrackResultsSection (when both tracks and results present) using computed prize mapping.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • boundlessfi/boundless#566: Closely related PR implementing the same track-aware prizes/winners plumbing with identical component and hook updates.
  • boundlessfi/boundless#564: Directly related PR that implements track-based prize/tier and winners UI changes in the same components using the same approach.
  • boundlessfi/boundless#555: Related PR updating submission-visibility enum and defaults to include HIDDEN_UNTIL_RESULTS and ACCEPTED_SHORTLISTED.

Suggested reviewers

  • Benjtalkshow

🐰 Tracks upon tracks, through the prize thicket,
Submissions bloom in polish and cricket,
Judges hop through coverage grids with glee,
While organizers tend the judges tree,
All bouncing toward results, so fair and free! 🌟

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/submission-visibility-hidden-until-results

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@0xdevcollins

Copy link
Copy Markdown
Collaborator Author

Superseded by the fresh PR off main; main had already picked up the foundation work via other PRs, leaving only the dashboard work as the real delta.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant