'use client' import { useMemo, useState } from 'react' import Link from 'next/link' import { trpc } from '@/lib/trpc/client' import { Card, CardContent, CardHeader, CardTitle, } from '@/components/ui/card' import { Input } from '@/components/ui/input' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Skeleton } from '@/components/ui/skeleton' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { ArrowUpDown, Search, Users } from 'lucide-react' type SortKey = 'name' | 'load' | 'capacity' | 'lastActivity' function formatRelativePast(date: Date | string | null): string { if (!date) return '—' const d = typeof date === 'string' ? new Date(date) : date const ms = Date.now() - d.getTime() const days = Math.floor(ms / 86_400_000) const hours = Math.floor(ms / 3_600_000) if (days >= 1) return `${days}d ago` if (hours >= 1) return `${hours}h ago` const minutes = Math.floor(ms / 60_000) return `${Math.max(0, minutes)}m ago` } type Mentor = { id: string name: string | null email: string country: string | null expertiseTags: string[] currentAssignments: number completedAssignments: number maxAssignments: number | null capacityRemaining: number | null lastActivityAt: Date | string | null } export default function MentorsListPage() { const [search, setSearch] = useState('') const [sortKey, setSortKey] = useState('load') const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc') const { data, isLoading } = trpc.mentor.getMentorPool.useQuery({}) const filtered = useMemo(() => { if (!data) return [] const q = search.trim().toLowerCase() let rows: Mentor[] = data.mentors if (q) { rows = rows.filter((m) => [m.name ?? '', m.email, m.country ?? '', ...m.expertiseTags] .join(' ') .toLowerCase() .includes(q), ) } rows = [...rows].sort((a, b) => { let av: string | number = 0 let bv: string | number = 0 switch (sortKey) { case 'name': av = (a.name ?? '').toLowerCase() bv = (b.name ?? '').toLowerCase() break case 'load': av = a.currentAssignments bv = b.currentAssignments break case 'capacity': av = a.capacityRemaining ?? Number.MAX_SAFE_INTEGER bv = b.capacityRemaining ?? Number.MAX_SAFE_INTEGER break case 'lastActivity': av = a.lastActivityAt ? new Date(a.lastActivityAt).getTime() : 0 bv = b.lastActivityAt ? new Date(b.lastActivityAt).getTime() : 0 break } if (av < bv) return sortDir === 'asc' ? -1 : 1 if (av > bv) return sortDir === 'asc' ? 1 : -1 return 0 }) return rows }, [data, search, sortKey, sortDir]) const toggleSort = (key: SortKey) => { if (sortKey === key) setSortDir((d) => (d === 'asc' ? 'desc' : 'asc')) else { setSortKey(key) setSortDir(key === 'name' ? 'asc' : 'desc') } } const SortHeader = ({ k, children, align = 'left', }: { k: SortKey children: React.ReactNode align?: 'left' | 'right' }) => ( ) return (

Mentors

All users with the MENTOR role and their current workload.

Pool size
{data?.poolSize ?? '—'}
Active assignments
{data?.totalCurrentAssignments ?? '—'}
Average load
{data && data.poolSize > 0 ? (data.totalCurrentAssignments / data.poolSize).toFixed(1) : '—'}
Mentor list
setSearch(e.target.value)} placeholder="Search by name, email, country, or expertise tag…" className="pl-9" />
{isLoading ? (
{[1, 2, 3, 4, 5].map((i) => ( ))}
) : filtered.length === 0 ? (
{search ? 'No matching mentors.' : 'No mentors yet.'}
) : (
Mentor Expertise Country Active Completed Capacity Last activity {filtered.map((m) => (
{m.name ?? 'Unnamed'}
{m.email}
{m.expertiseTags.slice(0, 4).map((tag) => ( {tag} ))} {m.expertiseTags.length > 4 && ( +{m.expertiseTags.length - 4} )}
{m.country ?? '—'} {m.currentAssignments} {m.completedAssignments} {m.capacityRemaining != null ? m.capacityRemaining : '∞'} {formatRelativePast(m.lastActivityAt)}
))}
)}
) }