feat(mentor): mentoring-specific Round Overview card grid (§B)

Renders above Round Details when round.roundType === 'MENTORING':
  - Top-line counts: requested + assigned (with awaiting badge)
  - Request window: countdown pill (amber <48h, red <12h)
  - Mentor pool: size + avg load + 'View all' link to /admin/mentors
  - Workspace activity: msgs / files / milestones / last activity

Round Details panel now shows 'Mentor Pool: N members' (linked) instead
of an always-empty 'Jury Group' row on MENTORING rounds.

Plan: docs/superpowers/plans/2026-04-28-pr5-mentor-round-overview.md
This commit is contained in:
Matt
2026-04-28 15:26:31 +02:00
parent f9bffabf05
commit a0a2c5f06a
2 changed files with 232 additions and 1 deletions

View File

@@ -91,6 +91,7 @@ import { ProjectStatesTable } from '@/components/admin/round/project-states-tabl
// SubmissionWindowManager removed — round dates + file requirements in Config are sufficient
import { FileRequirementsEditor } from '@/components/admin/round/file-requirements-editor'
import { FilteringDashboard } from '@/components/admin/round/filtering-dashboard'
import { MentoringRoundOverview } from '@/components/admin/round/mentoring-round-overview'
import { RankingDashboard } from '@/components/admin/round/ranking-dashboard'
import { CoverageReport } from '@/components/admin/assignment/coverage-report'
import { AssignmentPreviewSheet } from '@/components/admin/assignment/assignment-preview-sheet'
@@ -582,6 +583,14 @@ export default function RoundDetailPage() {
const isFiltering = round?.roundType === 'FILTERING'
const isEvaluation = round?.roundType === 'EVALUATION'
const isMentoring = round?.roundType === 'MENTORING'
// Mentor pool size — used by Round Details panel below to replace the
// always-empty "Jury Group" row on MENTORING rounds.
const { data: mentorPool } = trpc.mentor.getMentorPool.useQuery(
{},
{ enabled: isMentoring },
)
const mentorPoolSize = mentorPool?.poolSize ?? 0
const hasJury = ['EVALUATION', 'LIVE_FINAL', 'DELIBERATION'].includes(round?.roundType ?? '')
const hasAwards = roundAwards.length > 0
const isSimpleAdvance = ['INTAKE', 'SUBMISSION', 'MENTORING'].includes(round?.roundType ?? '')
@@ -1469,6 +1478,9 @@ export default function RoundDetailPage() {
/>
)}
{/* Mentoring-specific stats \u2014 only on MENTORING rounds */}
{isMentoring && <MentoringRoundOverview roundId={roundId} />}
{/* Round Info + Project Breakdown */}
<div className="grid gap-4 sm:grid-cols-2">
<AnimatedCard index={2}>
@@ -1482,7 +1494,9 @@ export default function RoundDetailPage() {
{ label: 'Status', value: <span className="font-medium">{statusCfg.label}</span> },
{ label: 'Position', value: <span className="font-medium">{`Round ${(round.sortOrder ?? 0) + 1}${competition?.rounds ? ` of ${competition.rounds.length}` : ''}`}</span> },
...(round.purposeKey ? [{ label: 'Purpose', value: <span className="font-medium">{round.purposeKey}</span> }] : []),
{ label: 'Jury Group', value: <span className="font-medium">{juryGroup ? juryGroup.name : '\u2014'}</span> },
isMentoring
? { label: 'Mentor Pool', value: <Link href="/admin/mentors" className="font-medium hover:underline">{mentorPoolSize} member{mentorPoolSize === 1 ? '' : 's'}</Link> }
: { label: 'Jury Group', value: <span className="font-medium">{juryGroup ? juryGroup.name : '\u2014'}</span> },
{ label: 'Opens', value: <span className="font-medium">{round.windowOpenAt ? new Date(round.windowOpenAt).toLocaleString() : '\u2014'}</span> },
{ label: 'Closes', value: <span className="font-medium">{round.windowCloseAt ? new Date(round.windowCloseAt).toLocaleString() : '\u2014'}</span> },
].map((row, i) => (