Inline filtering results, select-all across pages, country flags, settings RBAC, and inline role changes
- Round detail: add skeleton loading for filtering stats, inline results table with expandable rows, pagination, override/reinstate, CSV export, and tooltip on AI summaries button (removes need for separate results page) - Projects: add select-all-across-pages with Gmail-style banner, show country flags with tooltip instead of country codes (table + card views), add listAllIds backend endpoint - Settings: allow PROGRAM_ADMIN access to settings page, restrict infrastructure tabs (AI, Email, Storage, Security, Webhooks) to SUPER_ADMIN only - Members: add inline role change via dropdown submenu in user actions, enforce role hierarchy (only super admins can modify admin/super-admin roles) in both backend and UI Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -74,7 +74,7 @@ export default function MemberDetailPage() {
|
||||
|
||||
const [name, setName] = useState('')
|
||||
const [role, setRole] = useState<string>('JURY_MEMBER')
|
||||
const [status, setStatus] = useState<string>('INVITED')
|
||||
const [status, setStatus] = useState<string>('NONE')
|
||||
const [expertiseTags, setExpertiseTags] = useState<string[]>([])
|
||||
const [maxAssignments, setMaxAssignments] = useState<string>('')
|
||||
const [showSuperAdminConfirm, setShowSuperAdminConfirm] = useState(false)
|
||||
@@ -96,7 +96,7 @@ export default function MemberDetailPage() {
|
||||
id: userId,
|
||||
name: name || null,
|
||||
role: role as 'SUPER_ADMIN' | 'JURY_MEMBER' | 'MENTOR' | 'OBSERVER' | 'PROGRAM_ADMIN',
|
||||
status: status as 'INVITED' | 'ACTIVE' | 'SUSPENDED',
|
||||
status: status as 'NONE' | 'INVITED' | 'ACTIVE' | 'SUSPENDED',
|
||||
expertiseTags,
|
||||
maxAssignments: maxAssignments ? parseInt(maxAssignments) : null,
|
||||
})
|
||||
@@ -180,11 +180,11 @@ export default function MemberDetailPage() {
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<p className="text-muted-foreground">{user.email}</p>
|
||||
<Badge variant={user.status === 'ACTIVE' ? 'success' : user.status === 'SUSPENDED' ? 'destructive' : 'secondary'}>
|
||||
{user.status}
|
||||
{user.status === 'NONE' ? 'Not Invited' : user.status}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
{user.status === 'INVITED' && (
|
||||
{(user.status === 'NONE' || user.status === 'INVITED') && (
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleSendInvitation}
|
||||
@@ -235,6 +235,7 @@ export default function MemberDetailPage() {
|
||||
setRole(v)
|
||||
}
|
||||
}}
|
||||
disabled={!isSuperAdmin && (user.role === 'SUPER_ADMIN' || user.role === 'PROGRAM_ADMIN')}
|
||||
>
|
||||
<SelectTrigger id="role">
|
||||
<SelectValue />
|
||||
@@ -243,7 +244,9 @@ export default function MemberDetailPage() {
|
||||
{isSuperAdmin && (
|
||||
<SelectItem value="SUPER_ADMIN">Super Admin</SelectItem>
|
||||
)}
|
||||
<SelectItem value="PROGRAM_ADMIN">Program Admin</SelectItem>
|
||||
{isSuperAdmin && (
|
||||
<SelectItem value="PROGRAM_ADMIN">Program Admin</SelectItem>
|
||||
)}
|
||||
<SelectItem value="JURY_MEMBER">Jury Member</SelectItem>
|
||||
<SelectItem value="MENTOR">Mentor</SelectItem>
|
||||
<SelectItem value="OBSERVER">Observer</SelectItem>
|
||||
@@ -257,6 +260,7 @@ export default function MemberDetailPage() {
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="NONE">Not Invited</SelectItem>
|
||||
<SelectItem value="INVITED">Invited</SelectItem>
|
||||
<SelectItem value="ACTIVE">Active</SelectItem>
|
||||
<SelectItem value="SUSPENDED">Suspended</SelectItem>
|
||||
@@ -379,6 +383,16 @@ export default function MemberDetailPage() {
|
||||
<UserActivityLog userId={userId} />
|
||||
|
||||
{/* Status Alert */}
|
||||
{user.status === 'NONE' && (
|
||||
<Alert>
|
||||
<Mail className="h-4 w-4" />
|
||||
<AlertTitle>Not Yet Invited</AlertTitle>
|
||||
<AlertDescription>
|
||||
This member was added to the platform via project import but hasn't been
|
||||
invited yet. Send them an invitation using the button above.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
{user.status === 'INVITED' && (
|
||||
<Alert>
|
||||
<Mail className="h-4 w-4" />
|
||||
|
||||
Reference in New Issue
Block a user