Jury management: create, delete, add/remove members from round detail page
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m0s
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m0s
- Added Jury tab to round detail page with full jury management inline - Create new jury groups (auto-assigns to current round) - Delete jury groups with confirmation dialog - Add/remove members with inline member list - Assign/switch jury groups via dropdown selector - Added delete endpoint to juryGroup router (unlinks rounds, deletes members) - Removed CHAIR/OBSERVER role selectors from add-member dialog (all members) - Removed role column from jury-members-table Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -36,14 +36,12 @@ export function AddMemberDialog({ juryGroupId, open, onOpenChange }: AddMemberDi
|
||||
// Search existing user state
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
const [selectedUserId, setSelectedUserId] = useState<string>('')
|
||||
const [role, setRole] = useState<'CHAIR' | 'MEMBER' | 'OBSERVER'>('MEMBER')
|
||||
const [maxAssignments, setMaxAssignments] = useState<string>('')
|
||||
const [capMode, setCapMode] = useState<string>('')
|
||||
|
||||
// Invite new user state
|
||||
const [inviteName, setInviteName] = useState('')
|
||||
const [inviteEmail, setInviteEmail] = useState('')
|
||||
const [inviteRole, setInviteRole] = useState<'CHAIR' | 'MEMBER' | 'OBSERVER'>('MEMBER')
|
||||
const [inviteMaxAssignments, setInviteMaxAssignments] = useState<string>('')
|
||||
const [inviteCapMode, setInviteCapMode] = useState<string>('')
|
||||
const [inviteExpertise, setInviteExpertise] = useState('')
|
||||
@@ -75,7 +73,7 @@ export function AddMemberDialog({ juryGroupId, open, onOpenChange }: AddMemberDi
|
||||
addMember({
|
||||
juryGroupId,
|
||||
userId: newUser.id,
|
||||
role: inviteRole,
|
||||
role: 'MEMBER',
|
||||
maxAssignmentsOverride: inviteMaxAssignments ? parseInt(inviteMaxAssignments, 10) : null,
|
||||
capModeOverride: inviteCapMode && inviteCapMode !== 'DEFAULT' ? (inviteCapMode as 'HARD' | 'SOFT' | 'NONE') : null,
|
||||
})
|
||||
@@ -100,12 +98,10 @@ export function AddMemberDialog({ juryGroupId, open, onOpenChange }: AddMemberDi
|
||||
const resetForm = () => {
|
||||
setSearchQuery('')
|
||||
setSelectedUserId('')
|
||||
setRole('MEMBER')
|
||||
setMaxAssignments('')
|
||||
setCapMode('')
|
||||
setInviteName('')
|
||||
setInviteEmail('')
|
||||
setInviteRole('MEMBER')
|
||||
setInviteMaxAssignments('')
|
||||
setInviteCapMode('')
|
||||
setInviteExpertise('')
|
||||
@@ -122,7 +118,7 @@ export function AddMemberDialog({ juryGroupId, open, onOpenChange }: AddMemberDi
|
||||
addMember({
|
||||
juryGroupId,
|
||||
userId: selectedUserId,
|
||||
role,
|
||||
role: 'MEMBER',
|
||||
maxAssignmentsOverride: maxAssignments ? parseInt(maxAssignments, 10) : null,
|
||||
capModeOverride: capMode && capMode !== 'DEFAULT' ? (capMode as 'HARD' | 'SOFT' | 'NONE') : null,
|
||||
})
|
||||
@@ -215,20 +211,6 @@ export function AddMemberDialog({ juryGroupId, open, onOpenChange }: AddMemberDi
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="role">Group Role</Label>
|
||||
<Select value={role} onValueChange={(val) => setRole(val as typeof role)}>
|
||||
<SelectTrigger id="role">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="MEMBER">Member</SelectItem>
|
||||
<SelectItem value="CHAIR">Chair</SelectItem>
|
||||
<SelectItem value="OBSERVER">Observer</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="capMode">Cap Mode</Label>
|
||||
<Select value={capMode || 'DEFAULT'} onValueChange={setCapMode}>
|
||||
@@ -298,20 +280,6 @@ export function AddMemberDialog({ juryGroupId, open, onOpenChange }: AddMemberDi
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="inviteGroupRole">Group Role</Label>
|
||||
<Select value={inviteRole} onValueChange={(val) => setInviteRole(val as typeof inviteRole)}>
|
||||
<SelectTrigger id="inviteGroupRole">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="MEMBER">Member</SelectItem>
|
||||
<SelectItem value="CHAIR">Chair</SelectItem>
|
||||
<SelectItem value="OBSERVER">Observer</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="inviteCapMode">Cap Mode</Label>
|
||||
<Select value={inviteCapMode || 'DEFAULT'} onValueChange={setInviteCapMode}>
|
||||
|
||||
@@ -29,15 +29,15 @@ import { AddMemberDialog } from './add-member-dialog'
|
||||
interface JuryMember {
|
||||
id: string
|
||||
userId: string
|
||||
role: string
|
||||
role?: string
|
||||
user: {
|
||||
id: string
|
||||
name: string | null
|
||||
email: string
|
||||
}
|
||||
maxAssignmentsOverride: number | null
|
||||
capModeOverride: string | null
|
||||
preferredStartupRatio: number | null
|
||||
maxAssignmentsOverride?: number | null
|
||||
capModeOverride?: string | null
|
||||
preferredStartupRatio?: number | null
|
||||
}
|
||||
|
||||
interface JuryMembersTableProps {
|
||||
@@ -81,7 +81,6 @@ export function JuryMembersTable({ juryGroupId, members }: JuryMembersTableProps
|
||||
<TableRow>
|
||||
<TableHead>Name</TableHead>
|
||||
<TableHead>Email</TableHead>
|
||||
<TableHead className="hidden md:table-cell">Role</TableHead>
|
||||
<TableHead className="hidden sm:table-cell">Max Assignments</TableHead>
|
||||
<TableHead className="hidden lg:table-cell">Cap Mode</TableHead>
|
||||
<TableHead>Actions</TableHead>
|
||||
@@ -90,7 +89,7 @@ export function JuryMembersTable({ juryGroupId, members }: JuryMembersTableProps
|
||||
<TableBody>
|
||||
{members.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={6} className="text-center text-muted-foreground">
|
||||
<TableCell colSpan={5} className="text-center text-muted-foreground">
|
||||
No members yet. Add members to get started.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -103,11 +102,6 @@ export function JuryMembersTable({ juryGroupId, members }: JuryMembersTableProps
|
||||
<TableCell className="text-sm text-muted-foreground">
|
||||
{member.user.email}
|
||||
</TableCell>
|
||||
<TableCell className="hidden md:table-cell">
|
||||
<Badge variant={member.role === 'CHAIR' ? 'default' : 'secondary'}>
|
||||
{member.role}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="hidden sm:table-cell">
|
||||
{member.maxAssignmentsOverride ?? '—'}
|
||||
</TableCell>
|
||||
|
||||
Reference in New Issue
Block a user