Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
'use client'
import { useEffect , useState } from 'react'
import { useParams , useRouter } from 'next/navigation'
import Link from 'next/link'
import { trpc } from '@/lib/trpc/client'
2026-03-06 10:39:21 +01:00
import { directSessionUpdate } from '@/lib/session-update'
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import {
Card ,
CardContent ,
CardDescription ,
CardHeader ,
CardTitle ,
} from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import {
Select ,
SelectContent ,
SelectItem ,
SelectTrigger ,
SelectValue ,
} from '@/components/ui/select'
import { Skeleton } from '@/components/ui/skeleton'
import { Alert , AlertDescription , AlertTitle } from '@/components/ui/alert'
import {
Table ,
TableBody ,
TableCell ,
TableHead ,
TableHeader ,
TableRow ,
} from '@/components/ui/table'
import { toast } from 'sonner'
2026-03-02 10:46:52 +01:00
import { Tabs , TabsContent , TabsList , TabsTrigger } from '@/components/ui/tabs'
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
import { TagInput } from '@/components/shared/tag-input'
import { UserActivityLog } from '@/components/shared/user-activity-log'
2026-03-02 10:46:52 +01:00
import { EvaluationEditSheet } from '@/components/admin/evaluation-edit-sheet'
2026-02-08 23:01:33 +01:00
import {
AlertDialog ,
AlertDialogAction ,
AlertDialogCancel ,
AlertDialogContent ,
AlertDialogDescription ,
AlertDialogFooter ,
AlertDialogHeader ,
AlertDialogTitle ,
} from '@/components/ui/alert-dialog'
2026-05-07 17:28:43 +02:00
import {
Dialog ,
DialogContent ,
DialogDescription ,
DialogFooter ,
DialogHeader ,
DialogTitle ,
} from '@/components/ui/dialog'
2026-03-05 02:11:16 +01:00
import { UserAvatar } from '@/components/shared/user-avatar'
2026-04-07 21:08:32 -04:00
import { Checkbox } from '@/components/ui/checkbox'
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
import {
ArrowLeft ,
Save ,
Mail ,
User ,
Shield ,
Loader2 ,
AlertCircle ,
2026-03-02 10:46:52 +01:00
ClipboardList ,
Eye ,
ThumbsUp ,
ThumbsDown ,
2026-03-05 13:29:56 +01:00
Globe ,
Building2 ,
FileText ,
FolderOpen ,
2026-03-06 10:39:21 +01:00
LogIn ,
Calendar ,
Clock ,
2026-05-07 17:28:43 +02:00
Link as LinkIcon ,
Copy ,
Check ,
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
} from 'lucide-react'
2026-03-05 13:49:43 +01:00
import { getCountryName , getCountryFlag } from '@/lib/countries'
2026-03-06 10:39:21 +01:00
import { formatRelativeTime } from '@/lib/utils'
function getRoleHomePath ( role : string ) : string {
switch ( role ) {
case 'JURY_MEMBER' : return '/jury'
case 'APPLICANT' : return '/applicant'
case 'MENTOR' : return '/mentor'
case 'OBSERVER' : return '/observer'
default : return '/admin'
}
}
const statusVariant : Record < string , 'default' | 'success' | 'destructive' | 'secondary' > = {
ACTIVE : 'success' ,
SUSPENDED : 'destructive' ,
INVITED : 'secondary' ,
NONE : 'secondary' ,
}
const roleColors : Record < string , 'default' | 'outline' | 'secondary' > = {
JURY_MEMBER : 'default' ,
MENTOR : 'secondary' ,
OBSERVER : 'outline' ,
PROGRAM_ADMIN : 'default' ,
SUPER_ADMIN : 'default' ,
APPLICANT : 'secondary' ,
AUDIENCE : 'outline' ,
}
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
export default function MemberDetailPage() {
const params = useParams ( )
const router = useRouter ( )
2026-02-16 16:16:23 +01:00
const utils = trpc . useUtils ( )
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
const userId = params . id as string
2026-02-05 13:45:34 +01:00
const { data : user , isLoading , error , refetch } = trpc . user . get . useQuery ( { id : userId } )
2026-02-08 23:01:33 +01:00
const { data : currentUser } = trpc . user . me . useQuery ( )
const isSuperAdmin = currentUser ? . role === 'SUPER_ADMIN'
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
const updateUser = trpc . user . update . useMutation ( )
const sendInvitation = trpc . user . sendInvitation . useMutation ( )
2026-05-07 17:28:43 +02:00
const generateAccessLink = trpc . user . generateAccessLink . useMutation ( )
2026-03-06 10:39:21 +01:00
const startImpersonation = trpc . user . startImpersonation . useMutation ( )
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
2026-05-07 17:28:43 +02:00
const [ accessLinkOpen , setAccessLinkOpen ] = useState ( false )
const [ accessLink , setAccessLink ] = useState < {
url : string
2026-05-07 17:35:22 +02:00
kind : 'setup' | 'magic_login'
2026-05-07 17:28:43 +02:00
expiresAt : Date
} | null > ( null )
const [ linkCopied , setLinkCopied ] = useState ( false )
const handleGenerateAccessLink = async ( ) = > {
try {
const result = await generateAccessLink . mutateAsync ( { userId } )
setAccessLink ( {
url : result.url ,
kind : result.kind ,
expiresAt : new Date ( result . expiresAt ) ,
} )
setLinkCopied ( false )
setAccessLinkOpen ( true )
} catch ( error ) {
toast . error (
error instanceof Error ? error . message : 'Failed to generate access link'
)
}
}
const handleCopyAccessLink = async ( ) = > {
if ( ! accessLink ) return
try {
await navigator . clipboard . writeText ( accessLink . url )
setLinkCopied ( true )
toast . success ( 'Link copied to clipboard' )
} catch {
toast . error ( 'Could not copy — please select and copy the link manually' )
}
}
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
// Mentor assignments (only fetched for mentors)
const { data : mentorAssignments } = trpc . mentor . listAssignments . useQuery (
{ mentorId : userId , page : 1 , perPage : 50 } ,
{ enabled : user?.role === 'MENTOR' }
)
2026-03-02 10:46:52 +01:00
// Juror evaluations (only fetched for jury members)
const isJuror = user ? . role === 'JURY_MEMBER' || user ? . roles ? . includes ( 'JURY_MEMBER' )
const { data : jurorEvaluations } = trpc . evaluation . getJurorEvaluations . useQuery (
{ userId } ,
{ enabled : ! ! user && ! ! isJuror }
)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const [ selectedEvaluation , setSelectedEvaluation ] = useState < any > ( null )
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
const [ name , setName ] = useState ( '' )
2026-02-14 15:26:42 +01:00
const [ email , setEmail ] = useState ( '' )
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
const [ role , setRole ] = useState < string > ( 'JURY_MEMBER' )
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>
2026-02-10 23:07:38 +01:00
const [ status , setStatus ] = useState < string > ( 'NONE' )
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
const [ expertiseTags , setExpertiseTags ] = useState < string [ ] > ( [ ] )
const [ maxAssignments , setMaxAssignments ] = useState < string > ( '' )
2026-02-08 23:01:33 +01:00
const [ showSuperAdminConfirm , setShowSuperAdminConfirm ] = useState ( false )
const [ pendingSuperAdminRole , setPendingSuperAdminRole ] = useState ( false )
2026-04-07 21:08:32 -04:00
const [ additionalRoles , setAdditionalRoles ] = useState < string [ ] > ( [ ] )
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
useEffect ( ( ) = > {
if ( user ) {
setName ( user . name || '' )
2026-02-14 15:26:42 +01:00
setEmail ( user . email || '' )
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
setRole ( user . role )
setStatus ( user . status )
setExpertiseTags ( user . expertiseTags || [ ] )
setMaxAssignments ( user . maxAssignments ? . toString ( ) || '' )
2026-04-07 21:08:32 -04:00
setAdditionalRoles ( user . roles ? . filter ( ( r : string ) = > r !== user . role ) || [ ] )
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
}
} , [ user ] )
const handleSave = async ( ) = > {
try {
refactor(awards): remove AWARD_MASTER role, fold features into jury chair flow
The AWARD_MASTER role split sponsor jurors into a parallel UI that hid
project files (only showed when the award was anchored to an evaluation
round) and duplicated the jury voting path with no real difference in
authority — tie-break and finalize were already governed by AwardJuror.isChair
regardless of the user's global role. Inviting a juror via the award page
defaulted to AWARD_MASTER, randomly fragmenting jury panels.
This collapses the role into JURY_MEMBER + isChair:
- specialAward.getMyAwardDetail now returns evaluation scores, chair
visibility into other jurors' votes, and juror roster
- specialAward.submitVote accepts an optional justification per vote
- specialAward.confirmWinner moves from awardMasterProcedure to
protectedProcedure (juror+chair check inside)
- bulkInviteJurors creates JURY_MEMBER accounts and, when the award has
a juryGroupId, also adds them to that JuryGroup so they appear on
the round-page jury panel
- jury award page renders justification, eval-score badges, and a
chair tools panel with vote tally + finalize-winner CTA
- juryGroup.list includes attached SpecialAwards; the jury-list UI
shows a trophy pill alongside round pills
- (award-master) route group, awardMasterProcedure, AWARD_MASTER role
enum value, and AWARD_MASTER_DECISION decisionMode are deleted
- migration demotes any residual AWARD_MASTER users to JURY_MEMBER and
recreates the UserRole enum without the value
Coup de Coeur on prod: Didier (the sponsor juror added today as
AWARD_MASTER by the buggy invite form) was migrated to JURY_MEMBER and
attached to the existing "Coup de Coeur" JuryGroup; the SpecialAward
itself was linked to that group (juryGroupId was NULL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 15:21:09 +02:00
const allRoles = [ role , . . . additionalRoles . filter ( ( r ) = > r !== role ) ] as Array < 'SUPER_ADMIN' | 'PROGRAM_ADMIN' | 'JURY_MEMBER' | 'MENTOR' | 'OBSERVER' | 'APPLICANT' | 'AUDIENCE' >
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
await updateUser . mutateAsync ( {
id : userId ,
2026-02-14 15:26:42 +01:00
email : email || undefined ,
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
name : name || null ,
2026-04-07 21:08:32 -04:00
role : role as typeof allRoles [ number ] ,
roles : allRoles ,
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>
2026-02-10 23:07:38 +01:00
status : status as 'NONE' | 'INVITED' | 'ACTIVE' | 'SUSPENDED' ,
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
expertiseTags ,
maxAssignments : maxAssignments ? parseInt ( maxAssignments ) : null ,
} )
2026-02-16 16:16:23 +01:00
utils . user . get . invalidate ( { id : userId } )
utils . user . list . invalidate ( )
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
toast . success ( 'Member updated successfully' )
} catch ( error ) {
toast . error ( error instanceof Error ? error . message : 'Failed to update member' )
}
}
const handleSendInvitation = async ( ) = > {
try {
await sendInvitation . mutateAsync ( { userId } )
toast . success ( 'Invitation email sent successfully' )
refetch ( )
2026-02-16 16:16:23 +01:00
utils . user . list . invalidate ( )
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
} catch ( error ) {
toast . error ( error instanceof Error ? error . message : 'Failed to send invitation' )
}
}
2026-03-06 10:39:21 +01:00
const handleImpersonate = async ( ) = > {
try {
const result = await startImpersonation . mutateAsync ( { targetUserId : userId } )
const ok = await directSessionUpdate ( { impersonate : userId } )
if ( ! ok ) {
toast . error ( 'Failed to update session for impersonation' )
return
}
window . location . href = getRoleHomePath ( result . targetRole )
} catch ( error ) {
toast . error ( error instanceof Error ? error . message : 'Failed to start impersonation' )
}
}
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
if ( isLoading ) {
return (
< div className = "space-y-6" >
< Skeleton className = "h-9 w-32" / >
2026-03-06 10:39:21 +01:00
< div className = "flex items-center gap-4" >
< Skeleton className = "h-16 w-16 rounded-full" / >
< div className = "space-y-2" >
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
< Skeleton className = "h-6 w-48" / >
< Skeleton className = "h-4 w-72" / >
2026-03-06 10:39:21 +01:00
< / div >
< / div >
< div className = "grid gap-6 lg:grid-cols-3" >
< div className = "lg:col-span-2 space-y-6" >
< Skeleton className = "h-48 w-full" / >
< / div >
< Skeleton className = "h-64 w-full" / >
< / div >
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
< / div >
)
}
2026-02-05 13:45:34 +01:00
if ( error || ! user ) {
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
return (
< div className = "space-y-6" >
< Alert variant = "destructive" >
< AlertCircle className = "h-4 w-4" / >
2026-02-05 13:45:34 +01:00
< AlertTitle > Error Loading Member < / AlertTitle >
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
< AlertDescription >
2026-02-05 13:45:34 +01:00
{ error ? . message || 'The member you\'re looking for does not exist.' }
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
< / AlertDescription >
< / Alert >
2026-03-06 14:25:56 +01:00
< Button onClick = { ( ) = > router . back ( ) } >
< ArrowLeft className = "mr-2 h-4 w-4" / >
Back
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
< / Button >
< / div >
)
}
2026-03-06 10:39:21 +01:00
const displayRoles = user . roles ? . length ? user . roles : [ user . role ]
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
return (
< div className = "space-y-6" >
2026-03-06 10:39:21 +01:00
{ /* Back nav */ }
2026-03-06 14:25:56 +01:00
< Button variant = "ghost" className = "-ml-4" onClick = { ( ) = > router . back ( ) } >
< ArrowLeft className = "mr-2 h-4 w-4" / >
Back
2026-03-06 10:39:21 +01:00
< / Button >
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
2026-03-06 10:39:21 +01:00
{ /* Header Hero */ }
< div className = "flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4" >
2026-03-05 02:11:16 +01:00
< div className = "flex items-center gap-4" >
< UserAvatar user = { user } avatarUrl = { user . avatarUrl } size = "lg" / >
< div >
< h1 className = "text-2xl font-semibold tracking-tight" >
{ user . name || 'Unnamed Member' }
< / h1 >
2026-03-06 10:39:21 +01:00
< p className = "text-muted-foreground" > { user . email } < / p >
< div className = "flex items-center gap-2 mt-1.5" >
< Badge variant = { statusVariant [ user . status ] || 'secondary' } >
2026-03-05 02:11:16 +01:00
{ user . status === 'NONE' ? 'Not Invited' : user . status }
< / Badge >
2026-03-06 10:39:21 +01:00
{ displayRoles . map ( ( r ) = > (
< Badge key = { r } variant = { roleColors [ r ] || 'secondary' } className = "text-xs" >
{ r . replace ( /_/g , ' ' ) }
< / Badge >
) ) }
2026-03-05 02:11:16 +01:00
< / div >
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
< / div >
< / div >
2026-03-06 10:39:21 +01:00
< div className = "flex items-center gap-2 shrink-0" >
{ ( user . status === 'NONE' || user . status === 'INVITED' ) && (
< Button
variant = "outline"
onClick = { handleSendInvitation }
disabled = { sendInvitation . isPending }
>
{ sendInvitation . isPending ? (
< Loader2 className = "mr-2 h-4 w-4 animate-spin" / >
) : (
< Mail className = "mr-2 h-4 w-4" / >
) }
{ user . status === 'INVITED' ? 'Resend Invite' : 'Send Invitation' }
< / Button >
) }
2026-05-07 17:28:43 +02:00
{ user . status !== 'SUSPENDED' && (
< Button
variant = "outline"
onClick = { handleGenerateAccessLink }
disabled = { generateAccessLink . isPending }
title = "Generate a one-time link to share manually if email isn't reaching them"
>
{ generateAccessLink . isPending ? (
< Loader2 className = "mr-2 h-4 w-4 animate-spin" / >
) : (
< LinkIcon className = "mr-2 h-4 w-4" / >
) }
Copy Access Link
< / Button >
) }
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
< Button
variant = "outline"
2026-03-06 10:39:21 +01:00
onClick = { handleImpersonate }
disabled = { startImpersonation . isPending }
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
>
2026-03-06 10:39:21 +01:00
{ startImpersonation . isPending ? (
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
< Loader2 className = "mr-2 h-4 w-4 animate-spin" / >
) : (
2026-03-06 10:39:21 +01:00
< LogIn className = "mr-2 h-4 w-4" / >
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
) }
2026-03-06 10:39:21 +01:00
Impersonate
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
< / Button >
2026-03-06 10:39:21 +01:00
< / div >
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
< / div >
2026-03-02 10:46:52 +01:00
< Tabs defaultValue = "profile" className = "space-y-6" >
< TabsList >
< TabsTrigger value = "profile" >
< User className = "h-4 w-4 mr-1" / >
Profile
< / TabsTrigger >
{ isJuror && (
< TabsTrigger value = "evaluations" >
< ClipboardList className = "h-4 w-4 mr-1" / >
Evaluations
{ jurorEvaluations && jurorEvaluations . length > 0 && (
< Badge variant = "secondary" className = "ml-1.5 text-xs px-1.5 py-0" >
{ jurorEvaluations . length }
< / Badge >
) }
< / TabsTrigger >
) }
< / TabsList >
< TabsContent value = "profile" className = "space-y-6" >
2026-03-06 10:39:21 +01:00
< div className = "grid gap-6 lg:grid-cols-3" >
{ /* Left column: Profile info + Projects */ }
< div className = "lg:col-span-2 space-y-6" >
{ /* Profile Details (read-only) */ }
{ ( user . nationality || user . country || user . institution || user . bio ) && (
< Card >
< CardHeader className = "pb-3" >
< CardTitle className = "flex items-center gap-2 text-base" >
< div className = "rounded-lg bg-blue-500/10 p-1.5" >
< Globe className = "h-4 w-4 text-blue-500" / >
< / div >
Profile Details
< / CardTitle >
< CardDescription > Information provided during onboarding < / CardDescription >
< / CardHeader >
< CardContent >
< div className = "grid gap-4 sm:grid-cols-2" >
{ user . nationality && (
< div className = "flex items-start gap-3 rounded-lg border p-3" >
< span className = "text-xl mt-0.5 shrink-0" role = "img" > { getCountryFlag ( user . nationality ) } < / span >
< div >
< p className = "text-xs font-medium text-muted-foreground" > Nationality < / p >
< p className = "text-sm font-medium" > { getCountryName ( user . nationality ) } < / p >
< / div >
< / div >
) }
{ user . country && (
< div className = "flex items-start gap-3 rounded-lg border p-3" >
< span className = "text-xl mt-0.5 shrink-0" role = "img" > { getCountryFlag ( user . country ) } < / span >
< div >
< p className = "text-xs font-medium text-muted-foreground" > Country of Residence < / p >
< p className = "text-sm font-medium" > { getCountryName ( user . country ) } < / p >
< / div >
< / div >
) }
{ user . institution && (
< div className = "flex items-start gap-3 rounded-lg border p-3" >
< Building2 className = "h-5 w-5 mt-0.5 text-muted-foreground shrink-0" / >
< div >
< p className = "text-xs font-medium text-muted-foreground" > Institution / Organization < / p >
< p className = "text-sm font-medium" > { user . institution } < / p >
< / div >
< / div >
) }
{ user . bio && (
< div className = "sm:col-span-2 rounded-lg border p-3" >
< div className = "flex items-start gap-3" >
< FileText className = "h-5 w-5 mt-0.5 text-muted-foreground shrink-0" / >
< div >
< p className = "text-xs font-medium text-muted-foreground" > Bio < / p >
< p className = "text-sm whitespace-pre-line mt-1" > { user . bio } < / p >
< / div >
< / div >
< / div >
) }
< / div >
< / CardContent >
< / Card >
2026-03-05 13:29:56 +01:00
) }
2026-03-06 10:39:21 +01:00
{ /* Projects */ }
{ user . teamMemberships && user . teamMemberships . length > 0 && (
< Card >
< CardHeader className = "pb-3" >
< CardTitle className = "flex items-center gap-2 text-base" >
< div className = "rounded-lg bg-emerald-500/10 p-1.5" >
< FolderOpen className = "h-4 w-4 text-emerald-500" / >
< / div >
Projects ( { user . teamMemberships . length } )
< / CardTitle >
< / CardHeader >
< CardContent className = "p-0" >
< div className = "divide-y" >
{ user . teamMemberships . map ( ( tm ) = > (
< Link
key = { tm . id }
href = { ` /admin/projects/ ${ tm . project . id } ` }
className = "flex items-center justify-between px-6 py-3 hover:bg-muted/50 transition-colors"
>
< div className = "min-w-0" >
< p className = "font-medium text-sm truncate" > { tm . project . title } < / p >
{ tm . project . teamName && (
< p className = "text-xs text-muted-foreground" > { tm . project . teamName } < / p >
) }
< / div >
< div className = "flex items-center gap-2 shrink-0 ml-2" >
{ tm . project . competitionCategory && (
< Badge variant = "outline" className = "text-xs" >
{ tm . project . competitionCategory . replace ( '_' , ' ' ) }
< / Badge >
) }
< Badge variant = "secondary" className = "text-xs" >
{ tm . role === 'LEAD' ? 'Lead' : tm . role === 'ADVISOR' ? 'Advisor' : 'Member' }
< / Badge >
< / div >
< / Link >
) ) }
< / div >
< / CardContent >
< / Card >
2026-03-05 13:29:56 +01:00
) }
2026-03-06 10:39:21 +01:00
{ /* Jury Groups */ }
{ user . juryGroupMemberships && user . juryGroupMemberships . length > 0 && (
< Card >
< CardHeader className = "pb-3" >
< CardTitle className = "flex items-center gap-2 text-base" >
< div className = "rounded-lg bg-violet-500/10 p-1.5" >
< Shield className = "h-4 w-4 text-violet-500" / >
< / div >
Jury Groups ( { user . juryGroupMemberships . length } )
< / CardTitle >
< / CardHeader >
< CardContent >
< div className = "flex flex-wrap gap-2" >
{ user . juryGroupMemberships . map ( ( m : { id : string ; role : string ; juryGroup : { id : string ; name : string } } ) = > (
< Badge key = { m . id } variant = "outline" className = "text-sm py-1.5 px-3" >
{ m . juryGroup . name }
< span className = "ml-1.5 text-xs text-muted-foreground" >
( { m . role === 'CHAIR' ? 'Chair' : m . role === 'OBSERVER' ? 'Observer' : 'Member' } )
< / span >
< / Badge >
) ) }
< / div >
< / CardContent >
< / Card >
2026-03-05 13:29:56 +01:00
) }
2026-03-06 10:39:21 +01:00
{ /* Mentor Assignments */ }
{ user . role === 'MENTOR' && mentorAssignments && mentorAssignments . assignments . length > 0 && (
< Card >
< CardHeader className = "pb-3" >
< CardTitle className = "flex items-center gap-2 text-base" >
< div className = "rounded-lg bg-amber-500/10 p-1.5" >
< ClipboardList className = "h-4 w-4 text-amber-500" / >
< / div >
Mentored Projects
< / CardTitle >
< CardDescription >
{ mentorAssignments . assignments . length } project { mentorAssignments . assignments . length !== 1 ? 's' : '' } assigned
< / CardDescription >
< / CardHeader >
< CardContent >
< Table >
< TableHeader >
< TableRow >
< TableHead > Project < / TableHead >
< TableHead > Category < / TableHead >
< TableHead > Status < / TableHead >
< TableHead > Assigned < / TableHead >
< / TableRow >
< / TableHeader >
< TableBody >
{ mentorAssignments . assignments . map ( ( assignment ) = > (
< TableRow key = { assignment . id } >
< TableCell >
< Link
href = { ` /admin/projects/ ${ assignment . project . id } ` }
className = "font-medium hover:underline"
>
{ assignment . project . title }
< / Link >
{ assignment . project . teamName && (
< p className = "text-sm text-muted-foreground" > { assignment . project . teamName } < / p >
) }
< / TableCell >
< TableCell >
{ assignment . project . competitionCategory ? (
< Badge variant = "outline" > { assignment . project . competitionCategory . replace ( '_' , ' ' ) } < / Badge >
) : '-' }
< / TableCell >
< TableCell >
< Badge variant = "secondary" > { assignment . project . status ? ? 'SUBMITTED' } < / Badge >
< / TableCell >
< TableCell className = "text-sm text-muted-foreground" >
{ new Date ( assignment . assignedAt ) . toLocaleDateString ( ) }
< / TableCell >
< / TableRow >
) ) }
< / TableBody >
< / Table >
< / CardContent >
< / Card >
2026-03-05 13:29:56 +01:00
) }
2026-03-06 10:39:21 +01:00
{ /* Activity Log */ }
< UserActivityLog userId = { userId } / >
2026-03-05 13:29:56 +01:00
< / div >
2026-03-06 10:39:21 +01:00
{ /* Right sidebar: Edit form + Quick info */ }
< div className = "space-y-6" >
{ /* Quick Info Card */ }
< Card >
< CardHeader className = "pb-3" >
< CardTitle className = "flex items-center gap-2 text-base" >
< div className = "rounded-lg bg-slate-500/10 p-1.5" >
< Clock className = "h-4 w-4 text-slate-500" / >
< / div >
Quick Info
< / CardTitle >
< / CardHeader >
< CardContent className = "space-y-3" >
< div className = "flex items-center justify-between text-sm" >
< span className = "text-muted-foreground" > Created < / span >
< span > { user . createdAt ? new Date ( user . createdAt ) . toLocaleDateString ( ) : '-' } < / span >
2026-03-05 13:29:56 +01:00
< / div >
2026-03-06 10:39:21 +01:00
< div className = "flex items-center justify-between text-sm" >
< span className = "text-muted-foreground" > Last Login < / span >
< span >
{ user . lastLoginAt ? (
< span title = { new Date ( user . lastLoginAt ) . toLocaleString ( ) } >
{ formatRelativeTime ( user . lastLoginAt ) }
< / span >
) : 'Never' }
< / span >
2026-03-05 13:29:56 +01:00
< / div >
2026-03-06 10:39:21 +01:00
{ user . _count && ! [ 'APPLICANT' , 'AUDIENCE' ] . includes ( user . role ) && (
< >
< div className = "flex items-center justify-between text-sm" >
< span className = "text-muted-foreground" > Jury Assignments < / span >
< span className = "font-semibold" > { user . _count . assignments } < / span >
< / div >
< div className = "flex items-center justify-between text-sm" >
< span className = "text-muted-foreground" > Mentor Assignments < / span >
< span className = "font-semibold" > { user . _count . mentorAssignments } < / span >
< / div >
< / >
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>
2026-02-10 23:07:38 +01:00
) }
2026-03-06 10:39:21 +01:00
< / CardContent >
< / Card >
{ /* Status Alerts */ }
{ user . status === 'NONE' && (
< Alert >
< Mail className = "h-4 w-4" / >
< AlertTitle > Not Yet Invited < / AlertTitle >
< AlertDescription >
This member was added via import but hasn & apos ; t been invited yet .
< / AlertDescription >
< / Alert >
) }
{ user . status === 'INVITED' && (
< Alert >
< Mail className = "h-4 w-4" / >
< AlertTitle > Invitation Pending < / AlertTitle >
< AlertDescription >
This member hasn & apos ; t accepted their invitation yet .
< / AlertDescription >
< / Alert >
) }
{ /* Basic Info Edit */ }
< Card >
< CardHeader className = "pb-3" >
< CardTitle className = "flex items-center gap-2 text-base" >
< div className = "rounded-lg bg-blue-500/10 p-1.5" >
< User className = "h-4 w-4 text-blue-500" / >
< / div >
Edit Details
< / CardTitle >
< / CardHeader >
< CardContent className = "space-y-4" >
< div className = "space-y-2" >
< Label htmlFor = "email" > Email < / Label >
< Input
id = "email"
type = "email"
value = { email }
onChange = { ( e ) = > setEmail ( e . target . value ) }
/ >
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
< / div >
2026-03-06 10:39:21 +01:00
< div className = "space-y-2" >
< Label htmlFor = "name" > Name < / Label >
< Input
id = "name"
value = { name }
onChange = { ( e ) = > setName ( e . target . value ) }
placeholder = "Enter name"
/ >
< / div >
< div className = "space-y-2" >
< Label htmlFor = "role" > Role < / Label >
< Select
value = { role }
onValueChange = { ( v ) = > {
if ( v === 'SUPER_ADMIN' ) {
setPendingSuperAdminRole ( true )
setShowSuperAdminConfirm ( true )
} else {
setRole ( v )
}
} }
disabled = { ! isSuperAdmin && ( user . role === 'SUPER_ADMIN' || user . role === 'PROGRAM_ADMIN' ) }
>
< SelectTrigger id = "role" >
< SelectValue / >
< / SelectTrigger >
< SelectContent >
{ isSuperAdmin && < SelectItem value = "SUPER_ADMIN" > Super 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 >
< SelectItem value = "APPLICANT" > Applicant < / SelectItem >
< SelectItem value = "AUDIENCE" > Audience < / SelectItem >
< / SelectContent >
< / Select >
< / div >
2026-04-07 21:08:32 -04:00
< div className = "space-y-2" >
< Label > Additional Roles < / Label >
< p className = "text-xs text-muted-foreground" >
Grant additional dashboard access beyond the primary role
< / p >
< div className = "grid grid-cols-2 gap-2" >
refactor(awards): remove AWARD_MASTER role, fold features into jury chair flow
The AWARD_MASTER role split sponsor jurors into a parallel UI that hid
project files (only showed when the award was anchored to an evaluation
round) and duplicated the jury voting path with no real difference in
authority — tie-break and finalize were already governed by AwardJuror.isChair
regardless of the user's global role. Inviting a juror via the award page
defaulted to AWARD_MASTER, randomly fragmenting jury panels.
This collapses the role into JURY_MEMBER + isChair:
- specialAward.getMyAwardDetail now returns evaluation scores, chair
visibility into other jurors' votes, and juror roster
- specialAward.submitVote accepts an optional justification per vote
- specialAward.confirmWinner moves from awardMasterProcedure to
protectedProcedure (juror+chair check inside)
- bulkInviteJurors creates JURY_MEMBER accounts and, when the award has
a juryGroupId, also adds them to that JuryGroup so they appear on
the round-page jury panel
- jury award page renders justification, eval-score badges, and a
chair tools panel with vote tally + finalize-winner CTA
- juryGroup.list includes attached SpecialAwards; the jury-list UI
shows a trophy pill alongside round pills
- (award-master) route group, awardMasterProcedure, AWARD_MASTER role
enum value, and AWARD_MASTER_DECISION decisionMode are deleted
- migration demotes any residual AWARD_MASTER users to JURY_MEMBER and
recreates the UserRole enum without the value
Coup de Coeur on prod: Didier (the sponsor juror added today as
AWARD_MASTER by the buggy invite form) was migrated to JURY_MEMBER and
attached to the existing "Coup de Coeur" JuryGroup; the SpecialAward
itself was linked to that group (juryGroupId was NULL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 15:21:09 +02:00
{ ( [ 'JURY_MEMBER' , 'OBSERVER' , 'MENTOR' ] as const )
2026-04-07 21:08:32 -04:00
. filter ( ( r ) = > r !== role )
. map ( ( r ) = > (
< label key = { r } className = "flex items-center gap-2 text-sm cursor-pointer" >
< Checkbox
checked = { additionalRoles . includes ( r ) }
onCheckedChange = { ( checked ) = > {
if ( checked ) {
setAdditionalRoles ( ( prev ) = > [ . . . prev , r ] )
} else {
setAdditionalRoles ( ( prev ) = > prev . filter ( ( x ) = > x !== r ) )
}
} }
/ >
{ r . replace ( /_/g , ' ' ) }
< / label >
) ) }
< / div >
< / div >
2026-03-06 10:39:21 +01:00
< div className = "space-y-2" >
< Label htmlFor = "status" > Status < / Label >
< Select value = { status } onValueChange = { setStatus } >
< SelectTrigger id = "status" >
< SelectValue / >
< / SelectTrigger >
< SelectContent >
< SelectItem value = "NONE" > Not Invited < / SelectItem >
< SelectItem value = "INVITED" > Invited < / SelectItem >
< SelectItem value = "ACTIVE" > Active < / SelectItem >
< SelectItem value = "SUSPENDED" > Suspended < / SelectItem >
< / SelectContent >
< / Select >
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
< / div >
2026-03-06 10:39:21 +01:00
{ /* Expertise & Capacity for non-applicant */ }
{ ! [ 'APPLICANT' , 'AUDIENCE' ] . includes ( user . role ) && (
< >
< div className = "border-t pt-4 space-y-2" >
< Label > Expertise Tags < / Label >
< TagInput
value = { expertiseTags }
onChange = { setExpertiseTags }
placeholder = "Select expertise tags..."
maxTags = { 15 }
/ >
< / div >
< div className = "space-y-2" >
< Label htmlFor = "maxAssignments" > Max Assignments < / Label >
< Input
id = "maxAssignments"
type = "number"
min = "1"
max = "100"
value = { maxAssignments }
onChange = { ( e ) = > setMaxAssignments ( e . target . value ) }
placeholder = "Unlimited"
/ >
< / div >
< / >
) }
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
2026-03-06 10:39:21 +01:00
< Button onClick = { handleSave } disabled = { updateUser . isPending } className = "w-full" >
{ updateUser . isPending ? (
< Loader2 className = "mr-2 h-4 w-4 animate-spin" / >
) : (
< Save className = "mr-2 h-4 w-4" / >
) }
Save Changes
< / Button >
< / CardContent >
< / Card >
< / div >
< / div >
2026-03-02 10:46:52 +01:00
< / TabsContent >
{ /* Evaluations Tab */ }
{ isJuror && (
< TabsContent value = "evaluations" className = "space-y-4" >
{ ! jurorEvaluations || jurorEvaluations . length === 0 ? (
< Card >
< CardContent className = "flex flex-col items-center justify-center py-12 text-center" >
< ClipboardList className = "h-12 w-12 text-muted-foreground/30" / >
< p className = "mt-2 text-muted-foreground" > No evaluations submitted yet < / p >
< / CardContent >
< / Card >
) : (
( ( ) = > {
const byRound = new Map < string , typeof jurorEvaluations > ( )
for ( const ev of jurorEvaluations ) {
const key = ev . roundName
if ( ! byRound . has ( key ) ) byRound . set ( key , [ ] )
byRound . get ( key ) ! . push ( ev )
}
return Array . from ( byRound . entries ( ) ) . map ( ( [ roundName , evals ] ) = > (
< Card key = { roundName } >
< CardHeader >
< CardTitle className = "text-base" > { roundName } < / CardTitle >
< CardDescription > { evals . length } evaluation { evals . length !== 1 ? 's' : '' } < / CardDescription >
< / CardHeader >
< CardContent >
< Table >
< TableHeader >
< TableRow >
< TableHead > Project < / TableHead >
< TableHead > Score < / TableHead >
< TableHead > Decision < / TableHead >
< TableHead > Status < / TableHead >
< TableHead > Submitted < / TableHead >
< TableHead className = "w-10" > < / TableHead >
< / TableRow >
< / TableHeader >
< TableBody >
{ evals . map ( ( ev ) = > (
< TableRow key = { ev . assignmentId } >
< TableCell className = "font-medium" >
< Link
href = { ` /admin/projects/ ${ ev . projectId } ` }
className = "hover:underline text-primary"
>
{ ev . projectTitle }
< / Link >
< / TableCell >
< TableCell >
{ ev . evaluation . globalScore !== null && ev . evaluation . globalScore !== undefined
? < span className = "font-medium" > { ev . evaluation . globalScore } / 10 < / span >
: < span className = "text-muted-foreground" > - < / span > }
< / TableCell >
< TableCell >
{ ev . evaluation . binaryDecision !== null && ev . evaluation . binaryDecision !== undefined ? (
ev . evaluation . binaryDecision ? (
< div className = "flex items-center gap-1 text-green-600" >
< ThumbsUp className = "h-4 w-4" / >
< span className = "text-sm" > Yes < / span >
< / div >
) : (
< div className = "flex items-center gap-1 text-red-600" >
< ThumbsDown className = "h-4 w-4" / >
< span className = "text-sm" > No < / span >
< / div >
)
) : (
< span className = "text-muted-foreground" > - < / span >
) }
< / TableCell >
< TableCell >
< Badge variant = { ev . evaluation . status === 'SUBMITTED' ? 'default' : 'secondary' } >
{ ev . evaluation . status . replace ( '_' , ' ' ) }
< / Badge >
< / TableCell >
< TableCell className = "text-sm text-muted-foreground" >
{ ev . evaluation . submittedAt
? new Date ( ev . evaluation . submittedAt ) . toLocaleDateString ( )
: '-' }
< / TableCell >
< TableCell >
< Button
variant = "ghost"
size = "sm"
onClick = { ( ) = > setSelectedEvaluation ( {
. . . ev ,
user : user ,
evaluation : ev.evaluation ,
} ) }
>
< Eye className = "h-4 w-4" / >
< / Button >
< / TableCell >
< / TableRow >
) ) }
< / TableBody >
< / Table >
< / CardContent >
< / Card >
) )
} ) ( )
) }
< EvaluationEditSheet
assignment = { selectedEvaluation }
open = { ! ! selectedEvaluation }
onOpenChange = { ( open ) = > { if ( ! open ) setSelectedEvaluation ( null ) } }
onSaved = { ( ) = > utils . evaluation . getJurorEvaluations . invalidate ( { userId } ) }
/ >
< / TabsContent >
) }
< / Tabs >
2026-02-08 23:01:33 +01:00
{ /* Super Admin Confirmation Dialog */ }
< AlertDialog open = { showSuperAdminConfirm } onOpenChange = { setShowSuperAdminConfirm } >
< AlertDialogContent >
< AlertDialogHeader >
< AlertDialogTitle > Grant Super Admin Access ? < / AlertDialogTitle >
< AlertDialogDescription >
This will grant < strong > { name || user ? . name || 'this user' } < / strong > full Super Admin
access , including user management , system settings , and all administrative
capabilities . This action should only be performed for trusted administrators .
< / AlertDialogDescription >
< / AlertDialogHeader >
< AlertDialogFooter >
2026-03-06 10:39:21 +01:00
< AlertDialogCancel onClick = { ( ) = > setPendingSuperAdminRole ( false ) } >
2026-02-08 23:01:33 +01:00
Cancel
< / AlertDialogCancel >
< AlertDialogAction
onClick = { ( ) = > {
setRole ( 'SUPER_ADMIN' )
setPendingSuperAdminRole ( false )
setShowSuperAdminConfirm ( false )
} }
className = "bg-red-600 hover:bg-red-700"
>
Confirm Super Admin
< / AlertDialogAction >
< / AlertDialogFooter >
< / AlertDialogContent >
< / AlertDialog >
2026-05-07 17:28:43 +02:00
< Dialog open = { accessLinkOpen } onOpenChange = { setAccessLinkOpen } >
< DialogContent className = "sm:max-w-lg" >
< DialogHeader >
< DialogTitle className = "flex items-center gap-2" >
< LinkIcon className = "h-4 w-4" / >
Access link ready
< / DialogTitle >
< DialogDescription >
2026-05-07 17:35:22 +02:00
{ accessLink ? . kind === 'magic_login'
? ` Share this with ${ user . name || user . email } via Slack, WhatsApp, or any channel that reaches them. Clicking it will sign them in directly (their existing password is preserved) and take them to their dashboard. `
: ` Share this with ${ user . name || user . email } via Slack, WhatsApp, or any channel that reaches them. They'll be walked through setting a password and onboarding. ` }
2026-05-07 17:28:43 +02:00
< / DialogDescription >
< / DialogHeader >
< div className = "space-y-3" >
< div className = "rounded-md border bg-muted/40 p-3" >
< Input
readOnly
value = { accessLink ? . url ? ? '' }
onFocus = { ( e ) = > e . currentTarget . select ( ) }
className = "font-mono text-xs bg-background"
/ >
< / div >
< div className = "flex items-center justify-between gap-2 rounded-md border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-900 dark:border-amber-900 dark:bg-amber-950/30 dark:text-amber-100" >
< div className = "flex items-center gap-2" >
< Clock className = "h-3.5 w-3.5 shrink-0" / >
< span >
Expires { accessLink ? . expiresAt . toLocaleString ( undefined , { dateStyle : 'medium' , timeStyle : 'short' } ) }
{ ' · ' } consumed on first successful login
< / span >
< / div >
< / div >
< p className = "text-xs text-muted-foreground" >
Don & apos ; t paste this in a public channel . Anyone with the link
can sign in as this user until it & apos ; s consumed .
< / p >
< / div >
< DialogFooter className = "gap-2 sm:gap-2" >
< Button variant = "outline" onClick = { ( ) = > setAccessLinkOpen ( false ) } >
Close
< / Button >
< Button onClick = { handleCopyAccessLink } >
{ linkCopied ? (
< >
< Check className = "mr-2 h-4 w-4" / >
Copied
< / >
) : (
< >
< Copy className = "mr-2 h-4 w-4" / >
Copy link
< / >
) }
< / Button >
< / DialogFooter >
< / DialogContent >
< / Dialog >
Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination
- Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence
- Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility
- Founding Date Field: add foundedAt to Project model with CSV import support
- Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate
- Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility
- Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures
- Reusable pagination component extracted to src/components/shared/pagination.tsx
- Old /admin/users and /admin/mentors routes redirect to /admin/members
- Prisma migration for all schema additions (additive, no data loss)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:58:29 +01:00
< / div >
)
}