import { Suspense } from 'react' import Link from 'next/link' import { prisma } from '@/lib/prisma' export const dynamic = 'force-dynamic' import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Skeleton } from '@/components/ui/skeleton' import { UserAvatar } from '@/components/shared/user-avatar' import { getUserAvatarUrl } from '@/server/utils/avatar-url' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { Plus, Users } from 'lucide-react' import { formatDate } from '@/lib/utils' import { UserActions, UserMobileActions } from '@/components/admin/user-actions' async function UsersContent() { const users = await prisma.user.findMany({ where: { role: { in: ['JURY_MEMBER', 'OBSERVER'] }, }, include: { _count: { select: { assignments: true, }, }, assignments: { select: { evaluation: { select: { status: true }, }, }, }, }, orderBy: [{ role: 'asc' }, { name: 'asc' }], }) // Generate avatar URLs const usersWithAvatars = await Promise.all( users.map(async (user) => ({ ...user, avatarUrl: await getUserAvatarUrl(user.profileImageKey, user.profileImageProvider), })) ) if (usersWithAvatars.length === 0) { return ( No jury members yet Invite jury members to start assigning projects for evaluation Invite Member ) } const statusColors: Record = { ACTIVE: 'success', PENDING: 'secondary', INACTIVE: 'secondary', SUSPENDED: 'destructive', } const roleColors: Record = { JURY_MEMBER: 'default', OBSERVER: 'outline', } return ( <> {/* Desktop table view */} Member Role Expertise Assignments Status Last Login Actions {usersWithAvatars.map((user) => ( {user.name || 'Unnamed'} {user.email} {user.role.replace('_', ' ')} {user.expertiseTags && user.expertiseTags.length > 0 ? ( {user.expertiseTags.slice(0, 2).map((tag) => ( {tag} ))} {user.expertiseTags.length > 2 && ( +{user.expertiseTags.length - 2} )} ) : ( - )} {user._count.assignments} assigned {user.assignments.filter(a => a.evaluation?.status === 'SUBMITTED').length} completed {user.status} {user.lastLoginAt ? ( formatDate(user.lastLoginAt) ) : ( Never )} ))} {/* Mobile card view */} {usersWithAvatars.map((user) => ( {user.name || 'Unnamed'} {user.email} {user.status} Role {user.role.replace('_', ' ')} Assignments {user.assignments.filter(a => a.evaluation?.status === 'SUBMITTED').length}/{user._count.assignments} completed {user.expertiseTags && user.expertiseTags.length > 0 && ( {user.expertiseTags.map((tag) => ( {tag} ))} )} ))} > ) } function UsersSkeleton() { return ( {[...Array(5)].map((_, i) => ( ))} ) } export default function UsersPage() { return ( {/* Header */} Jury Members Manage jury members and observers Invite Member {/* Content */} }> ) }
No jury members yet
Invite jury members to start assigning projects for evaluation
{user.name || 'Unnamed'}
{user.email}
{user._count.assignments} assigned
{user.assignments.filter(a => a.evaluation?.status === 'SUBMITTED').length} completed
Manage jury members and observers