feat(voting): let projects target award categories - #149
Conversation
Expose year award categories and ordered project nominations through the shared project contracts and Worker APIs. Validate zero-to-two distinct same-year categories, preserve nomination-free ideas, and batch nomination writes with project membership changes across create, update, and claim flows. Keep nomination sets immutable once effective voting opens while allowing unrelated permitted edits, and cover persistence, validation, claims, and voting-open behavior with focused tests.
Recreate the D1 vote insert and update triggers so projects with no nominations remain open to every category while restricted projects accept only their nominated categories. Map rejected vote writes to a validation error and expose nomination eligibility on each compact ballot selection. Preserve historical conflicting votes in place so they remain visible and movable, and cover eligible casts, atomic rejected casts and moves, withdrawn picks, migration safety, and the retired admin nomination API.
Add an explicit all-categories default and an accessible focused mode for selecting one or two project award categories. Include nomination choices in form payloads and dirty-state protection, with responsive category cards, native controls, empty-state guidance, and a visible two-selection limit. Initialize edit and claim flows from the correct nomination state, and lock saved targeting while effective voting is open without blocking unrelated edits. Cover new, edit, idea, claim, keyboard, limit, discard, empty, and read-only behavior with focused app tests.
Keep every award category visible on project details while limiting cast and move controls to categories entered by the project team. Give excluded, own-project, selected, replacement, pending, and confirmation states distinct copy and styling. Count only active nomination-eligible selections toward ballot completion and personal pick badges. Preserve withdrawn and active ineligible selections with explicit replacement guidance, backed by focused component and route coverage.
Add D1 triggers that reject nomination inserts, updates, and deletes while voting is effectively live. This closes the enable-voting race and prevents category cascades from turning a restricted project into an all-category project. Map the database constraint through project writes and category deletion, including explicit all-category behavior for live project creation and idea claims. Extend Worker and migration coverage for atomic failures, preserved rows, and existing ballot behavior.
Remove the decorative purple stripe from the left edge of the award targeting panel while preserving the panel's existing outline, gradient, and layout.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d71154c. Configure here.
|
|
||
| const users = options.data?.users ?? []; | ||
| const categories = options.data?.categories ?? []; | ||
| const nominationsLocked = Boolean(project && !claim && nominationsReadOnly); |
There was a problem hiding this comment.
Create and claim ignore voting lock
Medium Severity
nominationsLocked is true only when an existing project is being edited, so create and claim still allow focused award targeting after voting opens. Those writes are rejected by the database lock, so teams can fill in one or two categories and then hit a conflict instead of seeing targeting frozen like the edit form.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit d71154c. Configure here.


Project teams can now leave their project open to every award category or focus it on one or two specific awards when creating, editing, or claiming it. Existing projects with no nominations remain eligible for all categories, so this requires no backfill or manual migration.
During voting, every award stays visible on the project page, but categories the team did not enter are clearly unavailable. Existing picks that no longer match a project's nominations remain visible and replaceable without counting toward ballot completion, while new casts and moves are enforced against nomination eligibility in D1.
The database migrations preserve existing nominations and votes, then freeze nomination rows once voting is live—including category-deletion cascades. The most useful review path is the project write lifecycle first, followed by the vote and nomination triggers, then the form and ballot state changes.