Skip to content

#288 Allow updating of registration faction & alignment#298

Merged
ianpaschal merged 4 commits intomainfrom
288-allow-update-registration-faction-alignment
Feb 9, 2026
Merged

#288 Allow updating of registration faction & alignment#298
ianpaschal merged 4 commits intomainfrom
288-allow-update-registration-faction-alignment

Conversation

@ianpaschal
Copy link
Owner

@ianpaschal ianpaschal commented Feb 9, 2026

Resolves #288


Summary by CodeRabbit

  • New Features

    • Users can now edit tournament registrations via a new Edit action and an update operation; client UI includes an Edit dialog and a mutation hook.
    • Registration detail type exposed in the public API.
  • Bug Fixes

    • Registration detail visibility refined so alignment/faction are only exposed when allowed.
    • Edit eligibility tightened for competitor edits (requires published tournaments/correct roles).
  • UX

    • Form props renamed to existingValues and details fields render logic updated to respect existingValues.

@vercel
Copy link

vercel bot commented Feb 9, 2026

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

Project Deployment Actions Updated (UTC)
combat-command Ready Ready Preview, Comment Feb 9, 2026 11:15pm

@coderabbitai
Copy link

coderabbitai bot commented Feb 9, 2026

Warning

Rate limit exceeded

@ianpaschal has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 40 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

Walkthrough

Adds edit support for tournament registrations: new backend mutation, detailFields schema extraction, Edit action added and gated by role/status, UI form and hooks for editing, and public API/type exports adjusted. (50 words)

Changes

Cohort / File(s) Summary
Backend mutation & table/types
convex/_model/tournamentRegistrations/mutations/updateTournamentRegistration.ts, convex/_model/tournamentRegistrations/table.ts, convex/_model/tournamentRegistrations/types.ts, convex/_model/tournamentRegistrations/index.ts, convex/tournamentRegistrations.ts
Add updateTournamentRegistration mutation, extract detailFields schema, move listId into table, add indices, and export new registration types and mutation args/handler.
Available-actions & visibility helpers
convex/_model/tournamentRegistrations/_helpers/getAvailableActions.ts, convex/_model/tournamentRegistrations/_helpers/deepenTournamentRegistration.ts, convex/_model/tournamentCompetitors/_helpers/getAvailableActions.ts
Introduce Edit action (enum and logic), gate captain/user edit by tournament published status, and wrap registration details with visibility-aware fields.
Create mutation adjustment
convex/_model/tournamentRegistrations/mutations/createTournamentRegistration.ts
Switch to explicit userId, tournamentId, and details: v.optional(detailFields) in args; stop spreading editableFields.
Frontend: registration edit flow & hooks
src/components/TournamentRegistrationProvider/actions/useEditAction.tsx, src/components/TournamentRegistrationProvider/TournamentRegistrationContextMenu.tsx, src/components/TournamentRegistrationProvider/TournamentRegistrationProvider.hooks.ts, src/services/tournamentRegistrations.ts
Add useEditAction hook, wire it into provider/hook and context menu, add useUpdateTournamentRegistration mutation hook and ties to the new backend mutation.
Frontend: forms and types
src/components/TournamentRegistrationForm/TournamentRegistrationForm.tsx, src/components/TournamentRegistrationForm/components/DetailsFields/DetailsFields.tsx, src/api.ts
Change existingValues prop type to TournamentRegistration, pass existingValues into DetailsFields, adjust visibility logic to consider existingValues, and export TournamentRegistrationDetails from API.
Frontend: competitor form prop rename
src/components/TournamentCompetitorForm/TournamentCompetitorForm.tsx, src/components/TournamentCompetitorProvider/actions/useEditAction.tsx
Rename tournamentCompetitor prop to existingValues and update usages to match new prop name.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly and concisely summarizes the primary change: enabling updates to registration faction and alignment fields.
Linked Issues check ✅ Passed All functional requirements from issue #288 are met: Edit option added to context menu, proper permission checks implemented (TO/player/captain), and faction/alignment fields are editable via the registration form.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing the edit functionality for tournament registrations; no unrelated modifications detected beyond the stated objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 288-allow-update-registration-faction-alignment

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

❤️ Share

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🤖 Fix all issues with AI agents
In
`@convex/_model/tournamentRegistrations/_helpers/deepenTournamentRegistration.ts`:
- Around line 38-41: visibleDetails is manually constructed and must stay in
sync with the separately defined detailFields array; add an inline comment above
the visibleDetails declaration explaining that any new fields added to
detailFields require adding corresponding keys here with their specific
visibility logic (e.g., use alignmentsVisible/factionsVisible and
details?.alignment/details?.faction), and that dynamic iteration is not used
because each field has unique reveal flags like
alignmentsRevealed/factionsRevealed.

In `@convex/_model/tournamentRegistrations/_helpers/getAvailableActions.ts`:
- Around line 7-10: Add a section comment and JSDoc for the
TournamentRegistrationActionKey.Edit enum member to match the style of other
members: add a comment like "// ---- Edit Actions ----" (or reuse existing
section naming convention) and a JSDoc block above Edit describing the action
and explicitly listing the roles that can use it (TO, Captain, Player). Update
the enum declaration for TournamentRegistrationActionKey so the Edit member has
the new comment and JSDoc to improve clarity and consistency with other members.

In
`@convex/_model/tournamentRegistrations/mutations/updateTournamentRegistration.ts`:
- Around line 39-53: The code's comment claims a "captain" can edit a competitor
but the logic only allows tournament organizers and the registration owner
(authorizedUserIds includes tournamentRegistration.userId); either update the
comment to match the current behavior or implement captain authorization:
retrieve the team's captain (e.g., call a helper like getTeamCaptainByTeamId or
similar using tournamentRegistration.teamId), add that captain's userId to
authorizedUserIds (alongside tournamentOrganizers.map(...)), and ensure
getTournamentOrganizersByTournament, tournamentRegistration, authorizedUserIds,
and userId are used consistently; if you choose not to implement captain support
now, change the comment text to only mention organizers and the registration
owner and add a TODO referencing captain support.
- Around line 55-59: The patch currently spreads the entire updated object into
ctx.db.patch which will overwrite ownership fields (userId, tournamentId,
tournamentCompetitorId) and cause unnecessary writes; instead construct the
patch payload to include only mutable fields (e.g., details) and modifiedAt —
either pick/assign allowed keys from updated or delete the ownership keys from
updated before calling ctx.db.patch so ctx.db.patch(id, { /* only mutable fields
*/, modifiedAt: Date.now() }) is used rather than spreading the whole updated
object.
- Around line 27-30: Replace the incorrect error code thrown when a registration
is missing: in the updateTournamentRegistration mutation where you check the
result of ctx.db.get(id) (the tournamentRegistration variable), change the
ConvexError call to use getErrorMessage('TOURNAMENT_REGISTRATION_NOT_FOUND')
instead of 'TOURNAMENT_COMPETITOR_NOT_FOUND' so the error matches the resource.
- Around line 13-16: updateTournamentRegistrationArgs is too permissive because
it spreads editableFields (which contains userId, tournamentId,
tournamentCompetitorId) allowing clients to reassign identity fields; replace
the args schema to only accept details (the editable faction/alignment object)
so the mutation and the DB patch in updateTournamentRegistration only update
details, not identity fields. Change the incorrect error code
TOURNAMENT_COMPETITOR_NOT_FOUND to a registration-specific code such as
TOURNAMENT_REGISTRATION_NOT_FOUND in the lookup/throw path. Finally, fix the
authorization comment that references "tournament competitor's captain" to match
the implemented checks in updateTournamentRegistration (either remove captain
mention or add the captain authorization logic if intended).

In
`@src/components/TournamentRegistrationForm/components/DetailsFields/DetailsFields.tsx`:
- Around line 38-39: The visibility logic uses the truthiness of existingValues
(a DeepPartial) causing showAlignmentField and showFactionField to always be
true in edit mode; change each check to look for the specific field on
existingValues first (e.g., existingValues.alignment and existingValues.faction)
and fall back to tournament.registrationDetails?.alignment and
tournament.registrationDetails?.faction respectively so the field only shows
when the registration or the tournament requires/contains that particular value;
update the boolean assignments for showAlignmentField and showFactionField
accordingly.

In `@src/components/TournamentRegistrationForm/TournamentRegistrationForm.tsx`:
- Around line 149-151: TournamentRegistrationForm currently renders
<DetailsFields> whenever existingValues exists, which causes DetailsFields to
show alignment/faction regardless of tournament.registrationDetails; change the
prop passed so DetailsFields receives only the relevant detail slice (e.g., pass
existingValues?.details or explicit props like existingAlignment and
existingFaction instead of the whole existingValues object) and update
DetailsFields to determine visibility by checking specific fields (e.g.,
tournament.registrationDetails?.alignment || existingAlignment) and
(tournament.registrationDetails?.faction || existingFaction) rather than
!!(existingValues || tournament.registrationDetails?.alignment).

In `@src/components/TournamentRegistrationProvider/actions/useEditAction.tsx`:
- Around line 40-48: The onSubmit handler currently spreads forcedValues into
the mutation payload which can leak ownership fields; update the onSubmit in
useEditAction.tsx so it only sends the id and the details object to mutation
(e.g., mutation({ id: subject._id, details: data.details })) instead of
...forcedValues and spreading editable fields, and keep the param destructuring
for nameVisibilityConsent and tournamentCompetitor (rename to
_nameVisibilityConsent/_tournamentCompetitor if desired) so ownership fields
(tournamentCompetitorId, tournamentId, userId) are not sent from the client.
- Around line 18-23: The onSuccess callback passed into
useUpdateTournamentRegistration references close before it's declared; to avoid
the forward reference, call useFormDialog (to get close) before invoking
useUpdateTournamentRegistration in useEditAction so close is bound before being
used, then pass the onSuccess that calls close and shows the toast;
alternatively, capture close into a local function (e.g., handleSuccess) after
close is declared and pass that to useUpdateTournamentRegistration. Ensure
symbols: useUpdateTournamentRegistration, onSuccess, close, useFormDialog, and
subject.displayName are used as described.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/components/TournamentRegistrationForm/TournamentRegistrationForm.tsx (1)

56-61: ⚠️ Potential issue | 🟠 Major

Spreading a full TournamentRegistration into defaultValues leaks non-form fields.

existingValues is now typed as TournamentRegistration, which includes fields like _id, displayName, availableActions, active, confirmed, etc. that are not part of FormData. While react-hook-form won't render them, it will track them as registered values and they'll appear in the submit payload — which is why the onSubmit in useEditAction ends up sending extra fields to the mutation.

Consider mapping existingValues to only the FormData-compatible subset before spreading:

Proposed approach

Either narrow the spread here:

   const form = useForm<FormData>({
     defaultValues: {
       ...defaultValues,
-      ...existingValues,
+      ...(existingValues && {
+        userId: existingValues.userId,
+        tournamentId: existingValues.tournamentId,
+        tournamentCompetitorId: existingValues.tournamentCompetitorId,
+        details: existingValues.details,
+      }),
       ...forcedValues,
     },
     mode: 'onSubmit',
   });

Or handle it at the onSubmit boundary in useEditAction (see related comment on that file).

convex/_model/tournamentRegistrations/table.ts (1)

35-36: 🧹 Nitpick | 🔵 Trivial

Verify whether by_tournament_competitor index is necessary.

In Convex, compound indexes support prefix-based queries. The by_tournament_competitor_user index on [tournamentCompetitorId, userId] can serve queries that filter only by tournamentCompetitorId using the first field as a prefix. According to Convex best practices, you typically only need the compound index unless the single-field index provides different ordering semantics (e.g., different _creationTime placement).

Check your query patterns: if you never need by_tournament_competitor without also considering userId or creation time ordering, this index is redundant and should be removed.

🤖 Fix all issues with AI agents
In `@convex/_model/tournamentRegistrations/table.ts`:
- Around line 19-23: The table currently duplicates the four fields from
editableFields inside defineTable (userId, tournamentId, tournamentCompetitorId,
details); remove the repeated explicit field declarations and instead spread
editableFields into the defineTable call (e.g., use ...editableFields) so the
schema and editable contract stay in sync, keeping any additional non-editable
fields added directly in the defineTable body.

@ianpaschal ianpaschal merged commit ebb0537 into main Feb 9, 2026
3 checks passed
@ianpaschal ianpaschal deleted the 288-allow-update-registration-faction-alignment branch February 10, 2026 00:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow Updating of Faction and Alignment

1 participant