Skip to content

Conversation

@eswan18
Copy link
Owner

@eswan18 eswan18 commented Feb 4, 2026

Summary

  • Searchable user picker: Replace email input in Add Member dialog with a searchable combobox showing "Name (username)", filtering out existing members
  • Inline member management: Move member management from standalone page into the Members tab, visible to all private competition members (read-only for non-admins, full management for admins)
  • Competition admin editing: Allow competition admins (not just system admins) to edit their own private competitions via an inline dialog in the dashboard header
  • Prevent private↔public switching: Hide the is_private toggle in edit mode and strip it from the update server action
  • Auth hardening: Add admin authorization check to getEligibleMembers; refactor addCompetitionMemberById to use early returns
  • Error handling: Show error messages in Members tab and invite dialog instead of spinning indefinitely on failure
  • Type narrowing: Replace full Competition type with EditableCompetition (Pick of 6 fields) to eliminate dummy values
  • Schema tests: Extract Zod validation schema to its own file; add 16 unit tests covering name validation, date requirements, and date ordering
  • Cleanup: Remove dead addCompetitionMember function, stale revalidatePath calls, unused imports
  • Fix 1Password autofill: Add autoComplete="off" to competition name input

Test plan

  • Open a private competition dashboard as an admin — verify the Members tab shows members and the Add Member button works
  • Open the Add Member dialog — verify searchable user list loads, excludes existing members, and displays "Name (username)"
  • Open a private competition as a non-admin member — verify the Members tab is visible but read-only (no Add Member button, no role change/remove actions)
  • As a non-admin non-member, verify getEligibleMembers returns an authorization error
  • As a competition admin, open Competition Settings from the header dropdown — verify the edit dialog opens inline and the is_private toggle is hidden
  • As a system admin, create a new competition — verify the is_private toggle is visible
  • Simulate a members fetch failure — verify error message displays instead of infinite spinner
  • Verify the competition name field does not trigger 1Password autofill
  • Run npm run test — all 157 tests pass (including 16 new schema validation tests)

🤖 Generated with Claude Code

eswan18 and others added 4 commits February 3, 2026 23:02
The invite member dialog now shows a searchable combobox of eligible users
(filtered to exclude existing members) instead of a blank email field.
Users display as "Name (username)" and can be filtered by typing.

- Add shadcn Command component (cmdk) for searchable list UI
- Add getEligibleMembers server action to fetch non-member active users
- Add addCompetitionMemberById server action accepting userId directly
- Rewrite InviteMemberDialog with Popover + Command combobox

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The Members tab now renders the member list inline instead of linking
to a separate page. All private competition members can see the tab
(read-only), while admins also get the Add Member button and
role/remove controls. The standalone /members page is removed.

- Fetch members client-side when Members tab is active
- Show Members tab to all private competition members, not just admins
- Add onMemberChange callback to MembersTable and InviteMemberDialog
- Remove Invite/Manage Members links from header dropdown
- Delete app/competitions/[competitionId]/members/ page

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The updateCompetition server action now checks for competition admin
role on private competitions, not just system admin. The dashboard
header opens an inline edit dialog for private competitions instead
of linking to the system admin page.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Hide the is_private toggle in edit mode so only new competitions can
set visibility. Strip is_private from the update server action payload
as a safeguard. Also add autoComplete="off" to the competition name
input to prevent 1Password autofill.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Feb 4, 2026

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

Project Deployment Actions Updated (UTC)
forecasting Ready Ready Preview, Comment Feb 5, 2026 2:56am

eswan18 and others added 3 commits February 3, 2026 23:36
Previously any logged-in user could call getEligibleMembers and get the
full list of active non-member users, leaking the user directory. Now
only competition admins and system admins can access this endpoint.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This function was replaced by addCompetitionMemberById when the invite
dialog switched to a searchable user picker. No callers remain. Also
removes the now-unused sql import from kysely.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
If getCompetitionMembers fails, show the error message instead of
spinning indefinitely. Clears the error on each new fetch attempt.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
eswan18 and others added 2 commits February 3, 2026 23:41
Move auth, user existence, and duplicate-member checks before the
withRLS callback so they return error() directly, matching the pattern
used by other server actions in this file. Removes the throw/catch
string-prefix parsing approach.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The standalone /competitions/[id]/members page was removed when member
management moved into the dashboard Members tab. These revalidatePath
calls targeted a route that no longer exists.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
If getEligibleMembers fails, show the error message inside the
command list instead of spinning indefinitely. Matches the error
handling pattern used in the dashboard members tab.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace the full Competition type with a Pick of the six fields the
form actually reads (id, name, is_private, and the three date fields).
This removes the need for dummy created_at/updated_at/created_by_user_id
values in the competition header.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract the Zod schema to its own file (competition-form-schema.ts) so
it can be tested without pulling in database dependencies. Add 16 tests
covering:
- Name length validation
- Private competitions skipping date requirements and ordering
- Public competitions requiring all three dates
- Date ordering constraints (open < close < end)
- Edge cases (equal dates, missing subset of dates)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
eswan18 and others added 2 commits February 4, 2026 00:03
…petition_members table

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ed variable

Move setMembersError and setEligibleUsersError calls inside
startTransition callbacks to avoid synchronous setState in effect
bodies. Remove unused `result` assignment in removeCompetitionMember.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Bare db queries on competition_members fail because RLS policies
require app.current_user_id to be set. Switch pre-check queries to
use withRLS, and add system admin bypass to addCompetitionMemberById
matching the pattern in getEligibleMembers.

Also fix PropertyKey[] type in test helper to match Zod's issue type.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The auth check and date validation queries used bare db without RLS
context, causing "Competition not found" for system admins and
"Only admins can update" for competition admins. Switch all pre-update
queries to use withRLS.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@eswan18 eswan18 merged commit c1e1a99 into main Feb 5, 2026
3 checks passed
@eswan18 eswan18 deleted the searchable-user-picker branch February 5, 2026 03:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant