fix(judging): unallocated partner funds become a warning, not a blocker#574
Conversation
Mirrors the backend relaxation. The allocator preview's "Ready to publish" badge previously turned amber whenever any partner contribution had unallocated balance — but the backend gate that backed that signal has been relaxed (the funds stay in escrow post-publish; they're not lost). Split the existing publish-readiness messaging into two lists: - Blockers — the hard gates (deadline, completeness, no reviews). Same red treatment, same ready-to-publish badge logic. - Warnings — informational, never blocks. Renders in blue, calls out the unallocated amount with a note that it remains in escrow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
Disabled knowledge base sources:
📝 WalkthroughWalkthroughAllocationPreviewCard now displays a non-blocking "Heads up" warning when unallocated partner contribution funds exceed a threshold. The warning informs users that publish will succeed while those funds remain in escrow pending release or refund, complementing the existing publish-blocking validation. ChangesUnallocated contribution warning
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~5 minutes Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Production had an older version of AllocationPreviewCard.tsx that treated unallocated partner contributions as a hard blocker. Main has the relaxed version from #574 where unallocated funds are surfaced as a non-blocking warning (the backend gate was relaxed in boundless-nestjs#130). Keep main's version on both conflict regions: - The warnings list (`warnings.push(...)`) instead of pushing to blockers. - The "Heads up" panel that renders warnings as a blue, non-blocking callout below the blockers list. Result: PR #575 (main -> production) is now a clean fast-forward. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…es (#575) * fix(hackathons): single-column teams tab and primary-colored pager (#562) * fix(hackathons): single-column teams tab and primary-colored pager Revert the teams tab grid to a single column and rework the shared Pagination component to match the icon-chevron layout used by the organizer submissions and participants pages, styled with the primary color. * feat(submissions): link submission card avatars to profile pages Wrap the individual avatar on SubmissionCard in a profile link and forward team-member usernames to GroupAvatar so each clustered avatar opens that user's profile in a new tab. * feat(hackathons): track-based prize structure + submission polish + tracks UI (#564) * feat(hackathons): add "hidden until results" submission visibility mode 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> * feat(hackathons): track-based prize structure, submission polish, tracks 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> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Feat/submission visibility hidden until results (#566) * feat(hackathons): add "hidden until results" submission visibility mode 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> * feat(hackathons): track-based prize structure, submission polish, tracks 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> * fix(submissions): tighten Zod schema + surface backend debug info - 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> * fix(submit-page): hydrate Phase A fields + stop wiping user input on 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> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(hackathons): always open submissions in a new tab (#568) * fix(hackathons): single-column teams tab and primary-colored pager Revert the teams tab grid to a single column and rework the shared Pagination component to match the icon-chevron layout used by the organizer submissions and participants pages, styled with the primary color. * feat(submissions): link submission card avatars to profile pages Wrap the individual avatar on SubmissionCard in a profile link and forward team-member usernames to GroupAvatar so each clustered avatar opens that user's profile in a new tab. * fix(hackathons): always open submissions in a new tab Across the hackathon, organizer, judge, and profile surfaces, clicking a submission now opens the project page in a new tab so reviewers and participants do not lose their list/queue context. Switched the remaining anchor tags to next/link Link components. * feat(judging): organizer dashboard — coverage, preview, per-track results (#570) (#572) 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> * feat(judging): unallocated partner funds become a warning, not a blocker (#574) Mirrors the backend relaxation. The allocator preview's "Ready to publish" badge previously turned amber whenever any partner contribution had unallocated balance — but the backend gate that backed that signal has been relaxed (the funds stay in escrow post-publish; they're not lost). Split the existing publish-readiness messaging into two lists: - Blockers — the hard gates (deadline, completeness, no reviews). Same red treatment, same ready-to-publish badge logic. - Warnings — informational, never blocks. Renders in blue, calls out the unallocated amount with a note that it remains in escrow. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Benjtalkshow <chinedubenj@gmail.com>
…es (#578) * fix(hackathons): single-column teams tab and primary-colored pager (#562) * fix(hackathons): single-column teams tab and primary-colored pager Revert the teams tab grid to a single column and rework the shared Pagination component to match the icon-chevron layout used by the organizer submissions and participants pages, styled with the primary color. * feat(submissions): link submission card avatars to profile pages Wrap the individual avatar on SubmissionCard in a profile link and forward team-member usernames to GroupAvatar so each clustered avatar opens that user's profile in a new tab. * feat(hackathons): track-based prize structure + submission polish + tracks UI (#564) * feat(hackathons): add "hidden until results" submission visibility mode 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> * feat(hackathons): track-based prize structure, submission polish, tracks 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> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Feat/submission visibility hidden until results (#566) * feat(hackathons): add "hidden until results" submission visibility mode 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> * feat(hackathons): track-based prize structure, submission polish, tracks 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> * fix(submissions): tighten Zod schema + surface backend debug info - 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> * fix(submit-page): hydrate Phase A fields + stop wiping user input on 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> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(hackathons): always open submissions in a new tab (#568) * fix(hackathons): single-column teams tab and primary-colored pager Revert the teams tab grid to a single column and rework the shared Pagination component to match the icon-chevron layout used by the organizer submissions and participants pages, styled with the primary color. * feat(submissions): link submission card avatars to profile pages Wrap the individual avatar on SubmissionCard in a profile link and forward team-member usernames to GroupAvatar so each clustered avatar opens that user's profile in a new tab. * fix(hackathons): always open submissions in a new tab Across the hackathon, organizer, judge, and profile surfaces, clicking a submission now opens the project page in a new tab so reviewers and participants do not lose their list/queue context. Switched the remaining anchor tags to next/link Link components. * feat(judging): organizer dashboard — coverage, preview, per-track results (#570) (#572) 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> * feat(judging): unallocated partner funds become a warning, not a blocker (#574) Mirrors the backend relaxation. The allocator preview's "Ready to publish" badge previously turned amber whenever any partner contribution had unallocated balance — but the backend gate that backed that signal has been relaxed (the funds stay in escrow post-publish; they're not lost). Split the existing publish-readiness messaging into two lists: - Blockers — the hard gates (deadline, completeness, no reviews). Same red treatment, same ready-to-publish badge logic. - Warnings — informational, never blocks. Renders in blue, calls out the unallocated amount with a note that it remains in escrow. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(rewards): show track winners on the organizer rewards page (#576) The rewards page was filtering winners by `submission.rank` only, so on the track-based prize structure it silently dropped every track winner. For the Boundless × Trustless Work hackathon (3 overall + 5 track tiers), the page rendered 3 of 8 winners and the publish wizard preview showed 3 of 8 prize tiers paired. Same root cause as boundlessfi/boundless-nestjs#132: track winners live on `SubmissionTrackEntry.wonRank`, not on `submission.rank`, and the rewards UI was rank-keyed end to end. Changes: - Extend `Submission` with optional track-winner fields (`isTrackWinner`, `trackId`, `trackName`, `trackPrize`, `trackWonRank`). - `useHackathonRewards` now fetches `getHackathonWinners` in a dedicated effect that runs once results are published. The `trackWinners` payload is stamped onto matching submissions AND returned raw so the page can render a per-track section. - `useHackathonRewards` also preserves `tier.kind` and `tier.trackId` on the mapped `prizeTiers` and re-sorts so track tiers no longer collapse to the 999 fallback rank. - `rewards/page.tsx` winners filter now ORs `s.isTrackWinner` with the rank-based check, and `maxRank` counts only OVERALL tiers so the rank-keyed lookups don't get polluted with synthetic track ranks. - New `TrackWinnersSection` component renders below the existing rank-based `PodiumSection`. Mirrors the public WinnersTab pattern: one card per track with prize chip, project name, team, avatars. - `PublishWinnersWizard` includes track winners in its preview list and threads `kind`/`trackId` through to `WinnersGrid`. - `WinnersGrid` now renders overall + track tiers as separate sections. Overall keeps the 2-1-3 podium re-order; track tiers render in display order. Each tier is matched to its winner via rank OR trackId. Pairs with boundless-nestjs#132 (BE trigger endpoint already respects track winners after that merged). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Benjtalkshow <chinedubenj@gmail.com>
* fix(hackathons): single-column teams tab and primary-colored pager (#562) * fix(hackathons): single-column teams tab and primary-colored pager Revert the teams tab grid to a single column and rework the shared Pagination component to match the icon-chevron layout used by the organizer submissions and participants pages, styled with the primary color. * feat(submissions): link submission card avatars to profile pages Wrap the individual avatar on SubmissionCard in a profile link and forward team-member usernames to GroupAvatar so each clustered avatar opens that user's profile in a new tab. * feat(hackathons): track-based prize structure + submission polish + tracks UI (#564) * feat(hackathons): add "hidden until results" submission visibility mode 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> * feat(hackathons): track-based prize structure, submission polish, tracks 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> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Feat/submission visibility hidden until results (#566) * feat(hackathons): add "hidden until results" submission visibility mode 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> * feat(hackathons): track-based prize structure, submission polish, tracks 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> * fix(submissions): tighten Zod schema + surface backend debug info - 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> * fix(submit-page): hydrate Phase A fields + stop wiping user input on 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> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(hackathons): always open submissions in a new tab (#568) * fix(hackathons): single-column teams tab and primary-colored pager Revert the teams tab grid to a single column and rework the shared Pagination component to match the icon-chevron layout used by the organizer submissions and participants pages, styled with the primary color. * feat(submissions): link submission card avatars to profile pages Wrap the individual avatar on SubmissionCard in a profile link and forward team-member usernames to GroupAvatar so each clustered avatar opens that user's profile in a new tab. * fix(hackathons): always open submissions in a new tab Across the hackathon, organizer, judge, and profile surfaces, clicking a submission now opens the project page in a new tab so reviewers and participants do not lose their list/queue context. Switched the remaining anchor tags to next/link Link components. * feat(judging): organizer dashboard — coverage, preview, per-track results (#570) (#572) 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> * feat(judging): unallocated partner funds become a warning, not a blocker (#574) Mirrors the backend relaxation. The allocator preview's "Ready to publish" badge previously turned amber whenever any partner contribution had unallocated balance — but the backend gate that backed that signal has been relaxed (the funds stay in escrow post-publish; they're not lost). Split the existing publish-readiness messaging into two lists: - Blockers — the hard gates (deadline, completeness, no reviews). Same red treatment, same ready-to-publish badge logic. - Warnings — informational, never blocks. Renders in blue, calls out the unallocated amount with a note that it remains in escrow. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(rewards): show track winners on the organizer rewards page (#576) The rewards page was filtering winners by `submission.rank` only, so on the track-based prize structure it silently dropped every track winner. For the Boundless × Trustless Work hackathon (3 overall + 5 track tiers), the page rendered 3 of 8 winners and the publish wizard preview showed 3 of 8 prize tiers paired. Same root cause as boundlessfi/boundless-nestjs#132: track winners live on `SubmissionTrackEntry.wonRank`, not on `submission.rank`, and the rewards UI was rank-keyed end to end. Changes: - Extend `Submission` with optional track-winner fields (`isTrackWinner`, `trackId`, `trackName`, `trackPrize`, `trackWonRank`). - `useHackathonRewards` now fetches `getHackathonWinners` in a dedicated effect that runs once results are published. The `trackWinners` payload is stamped onto matching submissions AND returned raw so the page can render a per-track section. - `useHackathonRewards` also preserves `tier.kind` and `tier.trackId` on the mapped `prizeTiers` and re-sorts so track tiers no longer collapse to the 999 fallback rank. - `rewards/page.tsx` winners filter now ORs `s.isTrackWinner` with the rank-based check, and `maxRank` counts only OVERALL tiers so the rank-keyed lookups don't get polluted with synthetic track ranks. - New `TrackWinnersSection` component renders below the existing rank-based `PodiumSection`. Mirrors the public WinnersTab pattern: one card per track with prize chip, project name, team, avatars. - `PublishWinnersWizard` includes track winners in its preview list and threads `kind`/`trackId` through to `WinnersGrid`. - `WinnersGrid` now renders overall + track tiers as separate sections. Overall keeps the 2-1-3 podium re-order; track tiers render in display order. Each tier is matched to its winner via rank OR trackId. Pairs with boundless-nestjs#132 (BE trigger endpoint already respects track winners after that merged). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(rewards): polish publish-wizard preview to industry-standard layout (#580) The wizard preview's "3/8 Winners Assigned" line plus the existing WinnerCard layout was confusing — "$300 USDC" double-signed the currency, "0 Comments" was meaningless in a payout preview, and track winners got a "4th Position" ribbon they didn't earn. Redesigned to match the patterns Devpost / Devfolio / ETHGlobal use in their publish flows: WinnersGrid header: - Replace the cryptic "X/Y Winners Assigned" with a callout chip: green check + "All N winners assigned" when complete, amber warning + "X of Y assigned (Z unassigned)" when not. - Show the total prize pool ("1,500 USDC pool") as a sibling chip so the organizer sees the dollar figure they're committing. - Render "Overall Placements" as a sub-header only when track winners also exist (avoids redundant heading on OVERALL_ONLY). WinnerCard: - Drop the dollar sign for non-dollar currencies — `"$300 USDC"` is now `"300 USDC"`, a cleaner industry-standard read. - Track winners get a track-name chip ("Best UI/UX") instead of a synthetic-rank ribbon ("4th Position"). - Drop the "0 Comments" noise; not a meaningful signal at payout time. - Drop the placeholder bitmed.png; surface project name + participant name + category as the primary content. - Cards now have consistent dimensions (no podium scaling for tracks), uniform border + hover treatment, prize chip aligned to the right. Only the publish-wizard preview is touched; the public Winners tab and the rewards podium are unchanged. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(hackathons): add judging dataset to export dropdown (#577) Surface a new "Judging" entry alongside the existing export options (Winners, Submissions, etc.) so organizers can download judging criteria, judges, aggregated results, per-judge scores, and judge comments. Requires the matching backend dataset to be deployed. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Benjtalkshow <chinedubenj@gmail.com>
…sibility (#583) * fix(hackathons): single-column teams tab and primary-colored pager (#562) * fix(hackathons): single-column teams tab and primary-colored pager Revert the teams tab grid to a single column and rework the shared Pagination component to match the icon-chevron layout used by the organizer submissions and participants pages, styled with the primary color. * feat(submissions): link submission card avatars to profile pages Wrap the individual avatar on SubmissionCard in a profile link and forward team-member usernames to GroupAvatar so each clustered avatar opens that user's profile in a new tab. * feat(hackathons): track-based prize structure + submission polish + tracks UI (#564) * feat(hackathons): add "hidden until results" submission visibility mode 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> * feat(hackathons): track-based prize structure, submission polish, tracks 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> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Feat/submission visibility hidden until results (#566) * feat(hackathons): add "hidden until results" submission visibility mode 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> * feat(hackathons): track-based prize structure, submission polish, tracks 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> * fix(submissions): tighten Zod schema + surface backend debug info - 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> * fix(submit-page): hydrate Phase A fields + stop wiping user input on 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> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(hackathons): always open submissions in a new tab (#568) * fix(hackathons): single-column teams tab and primary-colored pager Revert the teams tab grid to a single column and rework the shared Pagination component to match the icon-chevron layout used by the organizer submissions and participants pages, styled with the primary color. * feat(submissions): link submission card avatars to profile pages Wrap the individual avatar on SubmissionCard in a profile link and forward team-member usernames to GroupAvatar so each clustered avatar opens that user's profile in a new tab. * fix(hackathons): always open submissions in a new tab Across the hackathon, organizer, judge, and profile surfaces, clicking a submission now opens the project page in a new tab so reviewers and participants do not lose their list/queue context. Switched the remaining anchor tags to next/link Link components. * feat(judging): organizer dashboard — coverage, preview, per-track results (#570) (#572) 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> * feat(judging): unallocated partner funds become a warning, not a blocker (#574) Mirrors the backend relaxation. The allocator preview's "Ready to publish" badge previously turned amber whenever any partner contribution had unallocated balance — but the backend gate that backed that signal has been relaxed (the funds stay in escrow post-publish; they're not lost). Split the existing publish-readiness messaging into two lists: - Blockers — the hard gates (deadline, completeness, no reviews). Same red treatment, same ready-to-publish badge logic. - Warnings — informational, never blocks. Renders in blue, calls out the unallocated amount with a note that it remains in escrow. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(rewards): show track winners on the organizer rewards page (#576) The rewards page was filtering winners by `submission.rank` only, so on the track-based prize structure it silently dropped every track winner. For the Boundless × Trustless Work hackathon (3 overall + 5 track tiers), the page rendered 3 of 8 winners and the publish wizard preview showed 3 of 8 prize tiers paired. Same root cause as boundlessfi/boundless-nestjs#132: track winners live on `SubmissionTrackEntry.wonRank`, not on `submission.rank`, and the rewards UI was rank-keyed end to end. Changes: - Extend `Submission` with optional track-winner fields (`isTrackWinner`, `trackId`, `trackName`, `trackPrize`, `trackWonRank`). - `useHackathonRewards` now fetches `getHackathonWinners` in a dedicated effect that runs once results are published. The `trackWinners` payload is stamped onto matching submissions AND returned raw so the page can render a per-track section. - `useHackathonRewards` also preserves `tier.kind` and `tier.trackId` on the mapped `prizeTiers` and re-sorts so track tiers no longer collapse to the 999 fallback rank. - `rewards/page.tsx` winners filter now ORs `s.isTrackWinner` with the rank-based check, and `maxRank` counts only OVERALL tiers so the rank-keyed lookups don't get polluted with synthetic track ranks. - New `TrackWinnersSection` component renders below the existing rank-based `PodiumSection`. Mirrors the public WinnersTab pattern: one card per track with prize chip, project name, team, avatars. - `PublishWinnersWizard` includes track winners in its preview list and threads `kind`/`trackId` through to `WinnersGrid`. - `WinnersGrid` now renders overall + track tiers as separate sections. Overall keeps the 2-1-3 podium re-order; track tiers render in display order. Each tier is matched to its winner via rank OR trackId. Pairs with boundless-nestjs#132 (BE trigger endpoint already respects track winners after that merged). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(rewards): polish publish-wizard preview to industry-standard layout (#580) The wizard preview's "3/8 Winners Assigned" line plus the existing WinnerCard layout was confusing — "$300 USDC" double-signed the currency, "0 Comments" was meaningless in a payout preview, and track winners got a "4th Position" ribbon they didn't earn. Redesigned to match the patterns Devpost / Devfolio / ETHGlobal use in their publish flows: WinnersGrid header: - Replace the cryptic "X/Y Winners Assigned" with a callout chip: green check + "All N winners assigned" when complete, amber warning + "X of Y assigned (Z unassigned)" when not. - Show the total prize pool ("1,500 USDC pool") as a sibling chip so the organizer sees the dollar figure they're committing. - Render "Overall Placements" as a sub-header only when track winners also exist (avoids redundant heading on OVERALL_ONLY). WinnerCard: - Drop the dollar sign for non-dollar currencies — `"$300 USDC"` is now `"300 USDC"`, a cleaner industry-standard read. - Track winners get a track-name chip ("Best UI/UX") instead of a synthetic-rank ribbon ("4th Position"). - Drop the "0 Comments" noise; not a meaningful signal at payout time. - Drop the placeholder bitmed.png; surface project name + participant name + category as the primary content. - Cards now have consistent dimensions (no podium scaling for tracks), uniform border + hover treatment, prize chip aligned to the right. Only the publish-wizard preview is touched; the public Winners tab and the rewards podium are unchanged. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(hackathons): add judging dataset to export dropdown (#577) Surface a new "Judging" entry alongside the existing export options (Winners, Submissions, etc.) so organizers can download judging criteria, judges, aggregated results, per-judge scores, and judge comments. Requires the matching backend dataset to be deployed. * fix(rewards): match track winners by submissionId, not participantId (#582) The publish-wizard preview was still showing "3 of 8 winners assigned" even after the track-winner enrichment landed in #576. The enrichment effect was looking up trackWinners by `sub.id`, but the rewards data mapper sets `Submission.id` to the participant ID, not the submission row ID. The Map lookup keyed by `HackathonTrackWinner.submissionId` never matched, so no submissions got `isTrackWinner = true` stamped. For the Boundless × Trustless Work hackathon (3 overall + 5 track winners), the wizard saw only the 3 overall winners — the 5 track winners never made it into the `winners` array. Fix: - Add `submissionId?: string` to the `Submission` type. - Mapper populates it from `submissionData.id || sub.id || sub.submissionId`. The mapper's `id` field stays on the participant ID for compatibility with the existing rank-assignment code that already keys off it. - Track-winner enrichment looks up `byId.get(sub.submissionId)` first, falls back to `byId.get(sub.id)` for older rows where the mapper output predated the new field. After this, the wizard will show "All 8 winners assigned • 1,500 USDC pool" with the three overall placements and five track winners. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Benjtalkshow <chinedubenj@gmail.com>
…, and UI fixes (#585) * fix(hackathons): single-column teams tab and primary-colored pager (#562) * fix(hackathons): single-column teams tab and primary-colored pager Revert the teams tab grid to a single column and rework the shared Pagination component to match the icon-chevron layout used by the organizer submissions and participants pages, styled with the primary color. * feat(submissions): link submission card avatars to profile pages Wrap the individual avatar on SubmissionCard in a profile link and forward team-member usernames to GroupAvatar so each clustered avatar opens that user's profile in a new tab. * feat(hackathons): track-based prize structure + submission polish + tracks UI (#564) * feat(hackathons): add "hidden until results" submission visibility mode 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> * feat(hackathons): track-based prize structure, submission polish, tracks 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> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Feat/submission visibility hidden until results (#566) * feat(hackathons): add "hidden until results" submission visibility mode 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> * feat(hackathons): track-based prize structure, submission polish, tracks 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> * fix(submissions): tighten Zod schema + surface backend debug info - 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> * fix(submit-page): hydrate Phase A fields + stop wiping user input on 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> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(hackathons): always open submissions in a new tab (#568) * fix(hackathons): single-column teams tab and primary-colored pager Revert the teams tab grid to a single column and rework the shared Pagination component to match the icon-chevron layout used by the organizer submissions and participants pages, styled with the primary color. * feat(submissions): link submission card avatars to profile pages Wrap the individual avatar on SubmissionCard in a profile link and forward team-member usernames to GroupAvatar so each clustered avatar opens that user's profile in a new tab. * fix(hackathons): always open submissions in a new tab Across the hackathon, organizer, judge, and profile surfaces, clicking a submission now opens the project page in a new tab so reviewers and participants do not lose their list/queue context. Switched the remaining anchor tags to next/link Link components. * feat(judging): organizer dashboard — coverage, preview, per-track results (#570) (#572) 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> * feat(judging): unallocated partner funds become a warning, not a blocker (#574) Mirrors the backend relaxation. The allocator preview's "Ready to publish" badge previously turned amber whenever any partner contribution had unallocated balance — but the backend gate that backed that signal has been relaxed (the funds stay in escrow post-publish; they're not lost). Split the existing publish-readiness messaging into two lists: - Blockers — the hard gates (deadline, completeness, no reviews). Same red treatment, same ready-to-publish badge logic. - Warnings — informational, never blocks. Renders in blue, calls out the unallocated amount with a note that it remains in escrow. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(rewards): show track winners on the organizer rewards page (#576) The rewards page was filtering winners by `submission.rank` only, so on the track-based prize structure it silently dropped every track winner. For the Boundless × Trustless Work hackathon (3 overall + 5 track tiers), the page rendered 3 of 8 winners and the publish wizard preview showed 3 of 8 prize tiers paired. Same root cause as boundlessfi/boundless-nestjs#132: track winners live on `SubmissionTrackEntry.wonRank`, not on `submission.rank`, and the rewards UI was rank-keyed end to end. Changes: - Extend `Submission` with optional track-winner fields (`isTrackWinner`, `trackId`, `trackName`, `trackPrize`, `trackWonRank`). - `useHackathonRewards` now fetches `getHackathonWinners` in a dedicated effect that runs once results are published. The `trackWinners` payload is stamped onto matching submissions AND returned raw so the page can render a per-track section. - `useHackathonRewards` also preserves `tier.kind` and `tier.trackId` on the mapped `prizeTiers` and re-sorts so track tiers no longer collapse to the 999 fallback rank. - `rewards/page.tsx` winners filter now ORs `s.isTrackWinner` with the rank-based check, and `maxRank` counts only OVERALL tiers so the rank-keyed lookups don't get polluted with synthetic track ranks. - New `TrackWinnersSection` component renders below the existing rank-based `PodiumSection`. Mirrors the public WinnersTab pattern: one card per track with prize chip, project name, team, avatars. - `PublishWinnersWizard` includes track winners in its preview list and threads `kind`/`trackId` through to `WinnersGrid`. - `WinnersGrid` now renders overall + track tiers as separate sections. Overall keeps the 2-1-3 podium re-order; track tiers render in display order. Each tier is matched to its winner via rank OR trackId. Pairs with boundless-nestjs#132 (BE trigger endpoint already respects track winners after that merged). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(rewards): polish publish-wizard preview to industry-standard layout (#580) The wizard preview's "3/8 Winners Assigned" line plus the existing WinnerCard layout was confusing — "$300 USDC" double-signed the currency, "0 Comments" was meaningless in a payout preview, and track winners got a "4th Position" ribbon they didn't earn. Redesigned to match the patterns Devpost / Devfolio / ETHGlobal use in their publish flows: WinnersGrid header: - Replace the cryptic "X/Y Winners Assigned" with a callout chip: green check + "All N winners assigned" when complete, amber warning + "X of Y assigned (Z unassigned)" when not. - Show the total prize pool ("1,500 USDC pool") as a sibling chip so the organizer sees the dollar figure they're committing. - Render "Overall Placements" as a sub-header only when track winners also exist (avoids redundant heading on OVERALL_ONLY). WinnerCard: - Drop the dollar sign for non-dollar currencies — `"$300 USDC"` is now `"300 USDC"`, a cleaner industry-standard read. - Track winners get a track-name chip ("Best UI/UX") instead of a synthetic-rank ribbon ("4th Position"). - Drop the "0 Comments" noise; not a meaningful signal at payout time. - Drop the placeholder bitmed.png; surface project name + participant name + category as the primary content. - Cards now have consistent dimensions (no podium scaling for tracks), uniform border + hover treatment, prize chip aligned to the right. Only the publish-wizard preview is touched; the public Winners tab and the rewards podium are unchanged. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(hackathons): add judging dataset to export dropdown (#577) Surface a new "Judging" entry alongside the existing export options (Winners, Submissions, etc.) so organizers can download judging criteria, judges, aggregated results, per-judge scores, and judge comments. Requires the matching backend dataset to be deployed. * fix(rewards): match track winners by submissionId, not participantId (#582) The publish-wizard preview was still showing "3 of 8 winners assigned" even after the track-winner enrichment landed in #576. The enrichment effect was looking up trackWinners by `sub.id`, but the rewards data mapper sets `Submission.id` to the participant ID, not the submission row ID. The Map lookup keyed by `HackathonTrackWinner.submissionId` never matched, so no submissions got `isTrackWinner = true` stamped. For the Boundless × Trustless Work hackathon (3 overall + 5 track winners), the wizard saw only the 3 overall winners — the 5 track winners never made it into the `winners` array. Fix: - Add `submissionId?: string` to the `Submission` type. - Mapper populates it from `submissionData.id || sub.id || sub.submissionId`. The mapper's `id` field stays on the participant ID for compatibility with the existing rank-assignment code that already keys off it. - Track-winner enrichment looks up `byId.get(sub.submissionId)` first, falls back to `byId.get(sub.id)` for older rows where the mapper output predated the new field. After this, the wizard will show "All 8 winners assigned • 1,500 USDC pool" with the three overall placements and five track winners. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(auth): send absolute callbackURL on Google sign-up (#584) The Google sign-up flow passed `callbackURL: process.env.NEXT_PUBLIC_APP_URL || '/'` to Better Auth. Better Auth treats a relative path as relative to the API host that processed the OAuth callback, not the frontend host. When `NEXT_PUBLIC_APP_URL` wasn't set (or was missing in the runtime env even though available at build time on Next.js), the fallback `'/'` sent users to the API host's root after OAuth completed. The session cookie WAS set on the shared `.boundlessfi.xyz` domain during the callback, but the user landed on a blank API page and thought sign-up had failed. Clearing browser cache (cookies survive — different section in Chrome) didn't drop the cookie, so the next visit to the frontend silently restored their session and they appeared "automatically logged in." Fix: always build an absolute `callbackURL` pointing at the frontend host. Same pattern LoginWrapper already uses — falls back to window.location.origin at runtime, then to the env var at build/SSR, then to the production canonical URL. All three are in the BE's `trustedOrigins` list so Better Auth won't reject the URL. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Benjtalkshow <chinedubenj@gmail.com>
* fix(hackathons): single-column teams tab and primary-colored pager (#562) * fix(hackathons): single-column teams tab and primary-colored pager Revert the teams tab grid to a single column and rework the shared Pagination component to match the icon-chevron layout used by the organizer submissions and participants pages, styled with the primary color. * feat(submissions): link submission card avatars to profile pages Wrap the individual avatar on SubmissionCard in a profile link and forward team-member usernames to GroupAvatar so each clustered avatar opens that user's profile in a new tab. * feat(hackathons): track-based prize structure + submission polish + tracks UI (#564) * feat(hackathons): add "hidden until results" submission visibility mode 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> * feat(hackathons): track-based prize structure, submission polish, tracks 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> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Feat/submission visibility hidden until results (#566) * feat(hackathons): add "hidden until results" submission visibility mode 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> * feat(hackathons): track-based prize structure, submission polish, tracks 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> * fix(submissions): tighten Zod schema + surface backend debug info - 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> * fix(submit-page): hydrate Phase A fields + stop wiping user input on 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> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(hackathons): always open submissions in a new tab (#568) * fix(hackathons): single-column teams tab and primary-colored pager Revert the teams tab grid to a single column and rework the shared Pagination component to match the icon-chevron layout used by the organizer submissions and participants pages, styled with the primary color. * feat(submissions): link submission card avatars to profile pages Wrap the individual avatar on SubmissionCard in a profile link and forward team-member usernames to GroupAvatar so each clustered avatar opens that user's profile in a new tab. * fix(hackathons): always open submissions in a new tab Across the hackathon, organizer, judge, and profile surfaces, clicking a submission now opens the project page in a new tab so reviewers and participants do not lose their list/queue context. Switched the remaining anchor tags to next/link Link components. * feat(judging): organizer dashboard — coverage, preview, per-track results (#570) (#572) 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> * feat(judging): unallocated partner funds become a warning, not a blocker (#574) Mirrors the backend relaxation. The allocator preview's "Ready to publish" badge previously turned amber whenever any partner contribution had unallocated balance — but the backend gate that backed that signal has been relaxed (the funds stay in escrow post-publish; they're not lost). Split the existing publish-readiness messaging into two lists: - Blockers — the hard gates (deadline, completeness, no reviews). Same red treatment, same ready-to-publish badge logic. - Warnings — informational, never blocks. Renders in blue, calls out the unallocated amount with a note that it remains in escrow. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(rewards): show track winners on the organizer rewards page (#576) The rewards page was filtering winners by `submission.rank` only, so on the track-based prize structure it silently dropped every track winner. For the Boundless × Trustless Work hackathon (3 overall + 5 track tiers), the page rendered 3 of 8 winners and the publish wizard preview showed 3 of 8 prize tiers paired. Same root cause as boundlessfi/boundless-nestjs#132: track winners live on `SubmissionTrackEntry.wonRank`, not on `submission.rank`, and the rewards UI was rank-keyed end to end. Changes: - Extend `Submission` with optional track-winner fields (`isTrackWinner`, `trackId`, `trackName`, `trackPrize`, `trackWonRank`). - `useHackathonRewards` now fetches `getHackathonWinners` in a dedicated effect that runs once results are published. The `trackWinners` payload is stamped onto matching submissions AND returned raw so the page can render a per-track section. - `useHackathonRewards` also preserves `tier.kind` and `tier.trackId` on the mapped `prizeTiers` and re-sorts so track tiers no longer collapse to the 999 fallback rank. - `rewards/page.tsx` winners filter now ORs `s.isTrackWinner` with the rank-based check, and `maxRank` counts only OVERALL tiers so the rank-keyed lookups don't get polluted with synthetic track ranks. - New `TrackWinnersSection` component renders below the existing rank-based `PodiumSection`. Mirrors the public WinnersTab pattern: one card per track with prize chip, project name, team, avatars. - `PublishWinnersWizard` includes track winners in its preview list and threads `kind`/`trackId` through to `WinnersGrid`. - `WinnersGrid` now renders overall + track tiers as separate sections. Overall keeps the 2-1-3 podium re-order; track tiers render in display order. Each tier is matched to its winner via rank OR trackId. Pairs with boundless-nestjs#132 (BE trigger endpoint already respects track winners after that merged). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(rewards): polish publish-wizard preview to industry-standard layout (#580) The wizard preview's "3/8 Winners Assigned" line plus the existing WinnerCard layout was confusing — "$300 USDC" double-signed the currency, "0 Comments" was meaningless in a payout preview, and track winners got a "4th Position" ribbon they didn't earn. Redesigned to match the patterns Devpost / Devfolio / ETHGlobal use in their publish flows: WinnersGrid header: - Replace the cryptic "X/Y Winners Assigned" with a callout chip: green check + "All N winners assigned" when complete, amber warning + "X of Y assigned (Z unassigned)" when not. - Show the total prize pool ("1,500 USDC pool") as a sibling chip so the organizer sees the dollar figure they're committing. - Render "Overall Placements" as a sub-header only when track winners also exist (avoids redundant heading on OVERALL_ONLY). WinnerCard: - Drop the dollar sign for non-dollar currencies — `"$300 USDC"` is now `"300 USDC"`, a cleaner industry-standard read. - Track winners get a track-name chip ("Best UI/UX") instead of a synthetic-rank ribbon ("4th Position"). - Drop the "0 Comments" noise; not a meaningful signal at payout time. - Drop the placeholder bitmed.png; surface project name + participant name + category as the primary content. - Cards now have consistent dimensions (no podium scaling for tracks), uniform border + hover treatment, prize chip aligned to the right. Only the publish-wizard preview is touched; the public Winners tab and the rewards podium are unchanged. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(hackathons): add judging dataset to export dropdown (#577) Surface a new "Judging" entry alongside the existing export options (Winners, Submissions, etc.) so organizers can download judging criteria, judges, aggregated results, per-judge scores, and judge comments. Requires the matching backend dataset to be deployed. * fix(rewards): match track winners by submissionId, not participantId (#582) The publish-wizard preview was still showing "3 of 8 winners assigned" even after the track-winner enrichment landed in #576. The enrichment effect was looking up trackWinners by `sub.id`, but the rewards data mapper sets `Submission.id` to the participant ID, not the submission row ID. The Map lookup keyed by `HackathonTrackWinner.submissionId` never matched, so no submissions got `isTrackWinner = true` stamped. For the Boundless × Trustless Work hackathon (3 overall + 5 track winners), the wizard saw only the 3 overall winners — the 5 track winners never made it into the `winners` array. Fix: - Add `submissionId?: string` to the `Submission` type. - Mapper populates it from `submissionData.id || sub.id || sub.submissionId`. The mapper's `id` field stays on the participant ID for compatibility with the existing rank-assignment code that already keys off it. - Track-winner enrichment looks up `byId.get(sub.submissionId)` first, falls back to `byId.get(sub.id)` for older rows where the mapper output predated the new field. After this, the wizard will show "All 8 winners assigned • 1,500 USDC pool" with the three overall placements and five track winners. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(auth): send absolute callbackURL on Google sign-up (#584) The Google sign-up flow passed `callbackURL: process.env.NEXT_PUBLIC_APP_URL || '/'` to Better Auth. Better Auth treats a relative path as relative to the API host that processed the OAuth callback, not the frontend host. When `NEXT_PUBLIC_APP_URL` wasn't set (or was missing in the runtime env even though available at build time on Next.js), the fallback `'/'` sent users to the API host's root after OAuth completed. The session cookie WAS set on the shared `.boundlessfi.xyz` domain during the callback, but the user landed on a blank API page and thought sign-up had failed. Clearing browser cache (cookies survive — different section in Chrome) didn't drop the cookie, so the next visit to the frontend silently restored their session and they appeared "automatically logged in." Fix: always build an absolute `callbackURL` pointing at the frontend host. Same pattern LoginWrapper already uses — falls back to window.location.origin at runtime, then to the env var at build/SSR, then to the production canonical URL. All three are in the BE's `trustedOrigins` list so Better Auth won't reject the URL. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(submissions): unbreak submission detail page + publish hackathon recap (#586) * fix(submissions): wire submission entity type through votes, comments, and rank chip The submission detail page (/projects/[slug]?type=submission) was treating submissions as crowdfunding projects, which broke every interactive surface for hackathon entries: - Sidebar getVoteCounts and the voters tab's getProjectVotes were hardcoded to CROWDFUNDING_CAMPAIGN, so the lookup failed with "Failed to load voting data" and disabled the buttons. - createVote on the voters tab was also hardcoded, so even a click through hit Prisma's "Invalid reference to related record" (P2025) for non-existent campaign rows. - ProjectComments used CommentEntityType.PROJECT, so comments and replies posted against a non-existent project record. - The Follow button hardcoded entityType='PROJECT' with the submission's id, leading to "Failed to update follow status". The follow EntityType enum has no HACKATHON_SUBMISSION value, so the button is hidden on submission pages. - The status badge displayed raw enum values like "SHORTLISTED" and there was no signal that a submission was a winner. All vote/comment surfaces now derive the entity type from the URL, matching the backend's HACKATHON_SUBMISSION enum on both votes and comments. The page mapper surfaces submissionRank and a friendly submissionStatus on the project, and the sidebar header renders a "Winner" / "Rank #N" chip next to the existing status badge. * fix(blog): use object-cover so card and detail covers fill the frame The earlier switch back to object-contain left letterbox bands around banners that aren't an exact 2:1 ratio on both the BlogCard grid and the post-details hero. With properly-sized cover banners, object-cover fills the frame without visible cropping. Authors should size cover images for a 2:1 ratio on the card and the responsive heights on the details page. * feat(blog): publish Boundless x Trustless Work hackathon winners recap Adds the post-hackathon recap announcing the three main winners (Conductor, Crypt, GoPadi), the five honorable mentions, and the Showcase recognitions from the May 16 finale. Featured on the blog index. * chore(deps): npm audit fix to clear js-cookie high-severity advisory GHSA-qjx8-664m-686j (js-cookie per-instance prototype hijack in assign()) was newly flagged as high severity. The pre-push hook runs `npm audit --audit-level=high` and refused to push any branch until this was patched. Lockfile-only update, no package.json changes, semver-compatible. Bundled here because the gate was blocking the parent PR; reviewers can treat this as an unrelated chore commit. --------- Co-authored-by: Collins Ikechukwu <collinschristroa@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(hackathons): single-column teams tab and primary-colored pager (#562) * fix(hackathons): single-column teams tab and primary-colored pager Revert the teams tab grid to a single column and rework the shared Pagination component to match the icon-chevron layout used by the organizer submissions and participants pages, styled with the primary color. * feat(submissions): link submission card avatars to profile pages Wrap the individual avatar on SubmissionCard in a profile link and forward team-member usernames to GroupAvatar so each clustered avatar opens that user's profile in a new tab. * feat(hackathons): track-based prize structure + submission polish + tracks UI (#564) * feat(hackathons): add "hidden until results" submission visibility mode 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> * feat(hackathons): track-based prize structure, submission polish, tracks 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> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Feat/submission visibility hidden until results (#566) * feat(hackathons): add "hidden until results" submission visibility mode 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> * feat(hackathons): track-based prize structure, submission polish, tracks 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> * fix(submissions): tighten Zod schema + surface backend debug info - 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> * fix(submit-page): hydrate Phase A fields + stop wiping user input on 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> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(hackathons): always open submissions in a new tab (#568) * fix(hackathons): single-column teams tab and primary-colored pager Revert the teams tab grid to a single column and rework the shared Pagination component to match the icon-chevron layout used by the organizer submissions and participants pages, styled with the primary color. * feat(submissions): link submission card avatars to profile pages Wrap the individual avatar on SubmissionCard in a profile link and forward team-member usernames to GroupAvatar so each clustered avatar opens that user's profile in a new tab. * fix(hackathons): always open submissions in a new tab Across the hackathon, organizer, judge, and profile surfaces, clicking a submission now opens the project page in a new tab so reviewers and participants do not lose their list/queue context. Switched the remaining anchor tags to next/link Link components. * feat(judging): organizer dashboard — coverage, preview, per-track results (#570) (#572) 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> * feat(judging): unallocated partner funds become a warning, not a blocker (#574) Mirrors the backend relaxation. The allocator preview's "Ready to publish" badge previously turned amber whenever any partner contribution had unallocated balance — but the backend gate that backed that signal has been relaxed (the funds stay in escrow post-publish; they're not lost). Split the existing publish-readiness messaging into two lists: - Blockers — the hard gates (deadline, completeness, no reviews). Same red treatment, same ready-to-publish badge logic. - Warnings — informational, never blocks. Renders in blue, calls out the unallocated amount with a note that it remains in escrow. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(rewards): show track winners on the organizer rewards page (#576) The rewards page was filtering winners by `submission.rank` only, so on the track-based prize structure it silently dropped every track winner. For the Boundless × Trustless Work hackathon (3 overall + 5 track tiers), the page rendered 3 of 8 winners and the publish wizard preview showed 3 of 8 prize tiers paired. Same root cause as boundlessfi/boundless-nestjs#132: track winners live on `SubmissionTrackEntry.wonRank`, not on `submission.rank`, and the rewards UI was rank-keyed end to end. Changes: - Extend `Submission` with optional track-winner fields (`isTrackWinner`, `trackId`, `trackName`, `trackPrize`, `trackWonRank`). - `useHackathonRewards` now fetches `getHackathonWinners` in a dedicated effect that runs once results are published. The `trackWinners` payload is stamped onto matching submissions AND returned raw so the page can render a per-track section. - `useHackathonRewards` also preserves `tier.kind` and `tier.trackId` on the mapped `prizeTiers` and re-sorts so track tiers no longer collapse to the 999 fallback rank. - `rewards/page.tsx` winners filter now ORs `s.isTrackWinner` with the rank-based check, and `maxRank` counts only OVERALL tiers so the rank-keyed lookups don't get polluted with synthetic track ranks. - New `TrackWinnersSection` component renders below the existing rank-based `PodiumSection`. Mirrors the public WinnersTab pattern: one card per track with prize chip, project name, team, avatars. - `PublishWinnersWizard` includes track winners in its preview list and threads `kind`/`trackId` through to `WinnersGrid`. - `WinnersGrid` now renders overall + track tiers as separate sections. Overall keeps the 2-1-3 podium re-order; track tiers render in display order. Each tier is matched to its winner via rank OR trackId. Pairs with boundless-nestjs#132 (BE trigger endpoint already respects track winners after that merged). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(rewards): polish publish-wizard preview to industry-standard layout (#580) The wizard preview's "3/8 Winners Assigned" line plus the existing WinnerCard layout was confusing — "$300 USDC" double-signed the currency, "0 Comments" was meaningless in a payout preview, and track winners got a "4th Position" ribbon they didn't earn. Redesigned to match the patterns Devpost / Devfolio / ETHGlobal use in their publish flows: WinnersGrid header: - Replace the cryptic "X/Y Winners Assigned" with a callout chip: green check + "All N winners assigned" when complete, amber warning + "X of Y assigned (Z unassigned)" when not. - Show the total prize pool ("1,500 USDC pool") as a sibling chip so the organizer sees the dollar figure they're committing. - Render "Overall Placements" as a sub-header only when track winners also exist (avoids redundant heading on OVERALL_ONLY). WinnerCard: - Drop the dollar sign for non-dollar currencies — `"$300 USDC"` is now `"300 USDC"`, a cleaner industry-standard read. - Track winners get a track-name chip ("Best UI/UX") instead of a synthetic-rank ribbon ("4th Position"). - Drop the "0 Comments" noise; not a meaningful signal at payout time. - Drop the placeholder bitmed.png; surface project name + participant name + category as the primary content. - Cards now have consistent dimensions (no podium scaling for tracks), uniform border + hover treatment, prize chip aligned to the right. Only the publish-wizard preview is touched; the public Winners tab and the rewards podium are unchanged. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(hackathons): add judging dataset to export dropdown (#577) Surface a new "Judging" entry alongside the existing export options (Winners, Submissions, etc.) so organizers can download judging criteria, judges, aggregated results, per-judge scores, and judge comments. Requires the matching backend dataset to be deployed. * fix(rewards): match track winners by submissionId, not participantId (#582) The publish-wizard preview was still showing "3 of 8 winners assigned" even after the track-winner enrichment landed in #576. The enrichment effect was looking up trackWinners by `sub.id`, but the rewards data mapper sets `Submission.id` to the participant ID, not the submission row ID. The Map lookup keyed by `HackathonTrackWinner.submissionId` never matched, so no submissions got `isTrackWinner = true` stamped. For the Boundless × Trustless Work hackathon (3 overall + 5 track winners), the wizard saw only the 3 overall winners — the 5 track winners never made it into the `winners` array. Fix: - Add `submissionId?: string` to the `Submission` type. - Mapper populates it from `submissionData.id || sub.id || sub.submissionId`. The mapper's `id` field stays on the participant ID for compatibility with the existing rank-assignment code that already keys off it. - Track-winner enrichment looks up `byId.get(sub.submissionId)` first, falls back to `byId.get(sub.id)` for older rows where the mapper output predated the new field. After this, the wizard will show "All 8 winners assigned • 1,500 USDC pool" with the three overall placements and five track winners. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(auth): send absolute callbackURL on Google sign-up (#584) The Google sign-up flow passed `callbackURL: process.env.NEXT_PUBLIC_APP_URL || '/'` to Better Auth. Better Auth treats a relative path as relative to the API host that processed the OAuth callback, not the frontend host. When `NEXT_PUBLIC_APP_URL` wasn't set (or was missing in the runtime env even though available at build time on Next.js), the fallback `'/'` sent users to the API host's root after OAuth completed. The session cookie WAS set on the shared `.boundlessfi.xyz` domain during the callback, but the user landed on a blank API page and thought sign-up had failed. Clearing browser cache (cookies survive — different section in Chrome) didn't drop the cookie, so the next visit to the frontend silently restored their session and they appeared "automatically logged in." Fix: always build an absolute `callbackURL` pointing at the frontend host. Same pattern LoginWrapper already uses — falls back to window.location.origin at runtime, then to the env var at build/SSR, then to the production canonical URL. All three are in the BE's `trustedOrigins` list so Better Auth won't reject the URL. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(submissions): unbreak submission detail page + publish hackathon recap (#586) * fix(submissions): wire submission entity type through votes, comments, and rank chip The submission detail page (/projects/[slug]?type=submission) was treating submissions as crowdfunding projects, which broke every interactive surface for hackathon entries: - Sidebar getVoteCounts and the voters tab's getProjectVotes were hardcoded to CROWDFUNDING_CAMPAIGN, so the lookup failed with "Failed to load voting data" and disabled the buttons. - createVote on the voters tab was also hardcoded, so even a click through hit Prisma's "Invalid reference to related record" (P2025) for non-existent campaign rows. - ProjectComments used CommentEntityType.PROJECT, so comments and replies posted against a non-existent project record. - The Follow button hardcoded entityType='PROJECT' with the submission's id, leading to "Failed to update follow status". The follow EntityType enum has no HACKATHON_SUBMISSION value, so the button is hidden on submission pages. - The status badge displayed raw enum values like "SHORTLISTED" and there was no signal that a submission was a winner. All vote/comment surfaces now derive the entity type from the URL, matching the backend's HACKATHON_SUBMISSION enum on both votes and comments. The page mapper surfaces submissionRank and a friendly submissionStatus on the project, and the sidebar header renders a "Winner" / "Rank #N" chip next to the existing status badge. * fix(blog): use object-cover so card and detail covers fill the frame The earlier switch back to object-contain left letterbox bands around banners that aren't an exact 2:1 ratio on both the BlogCard grid and the post-details hero. With properly-sized cover banners, object-cover fills the frame without visible cropping. Authors should size cover images for a 2:1 ratio on the card and the responsive heights on the details page. * feat(blog): publish Boundless x Trustless Work hackathon winners recap Adds the post-hackathon recap announcing the three main winners (Conductor, Crypt, GoPadi), the five honorable mentions, and the Showcase recognitions from the May 16 finale. Featured on the blog index. * chore(deps): npm audit fix to clear js-cookie high-severity advisory GHSA-qjx8-664m-686j (js-cookie per-instance prototype hijack in assign()) was newly flagged as high severity. The pre-push hook runs `npm audit --audit-level=high` and refused to push any branch until this was patched. Lockfile-only update, no package.json changes, semver-compatible. Bundled here because the gate was blocking the parent PR; reviewers can treat this as an unrelated chore commit. * fix(blog): drop duplicate h1 from hackathon winners post (#588) BlogPostDetails already renders post.title from frontmatter as the page heading above the cover banner, so the matching `#` heading at the top of the MDX body rendered a second time directly under the banner. Removing the body-level h1 keeps the page to a single title. --------- Co-authored-by: Collins Ikechukwu <collinschristroa@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Why
Pairs with boundless-nestjs#130 which relaxes the backend gate. The previous PR #572 already merged the dashboard, but this small follow-up was orphaned on the merged branch and needs a fresh PR off main.
Change
In
AllocationPreviewCard, split publish-readiness signals into:What organizers see
For an escrow that exceeds the prize pool (e.g. 2074.98 USDC escrow against 1500 USDC pool), the publish flow now shows:
The Publish button works. Funds stay in escrow safely.
Risk
Pure UI change, one file, one logical condition moved between two lists.
Pairs with
boundlessfi/boundless-nestjs#130 (the backend allow-publish change).
Deploy order
🤖 Generated with Claude Code
Summary by CodeRabbit