Add Reviews column to Projects tab showing evaluation submission progress
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m36s

Backend: getProjectRoundStates now includes assignment counts and submitted
evaluation counts per project. Frontend: new Reviews column shows X/Y
(submitted/total) with green highlight when all reviews are complete.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-23 20:38:43 +01:00
parent 5ece50268b
commit 91563f3f47
2 changed files with 42 additions and 3 deletions

View File

@@ -328,7 +328,7 @@ export function ProjectStatesTable({ competitionId, roundId }: ProjectStatesTabl
{/* Table */}
<div className="border rounded-lg overflow-hidden">
{/* Header */}
<div className="grid grid-cols-[40px_1fr_140px_160px_120px_100px_48px] gap-2 px-4 py-2.5 bg-muted/40 text-xs font-medium text-muted-foreground border-b">
<div className="grid grid-cols-[40px_1fr_140px_160px_120px_80px_100px_48px] gap-2 px-4 py-2.5 bg-muted/40 text-xs font-medium text-muted-foreground border-b">
<div>
<Checkbox
checked={filtered.length > 0 && filtered.every((ps: any) => selectedIds.has(ps.projectId))}
@@ -339,6 +339,7 @@ export function ProjectStatesTable({ competitionId, roundId }: ProjectStatesTabl
<div>Category</div>
<div>Country</div>
<div>State</div>
<div>Reviews</div>
<div>Entered</div>
<div />
</div>
@@ -347,10 +348,13 @@ export function ProjectStatesTable({ competitionId, roundId }: ProjectStatesTabl
{filtered.map((ps: any) => {
const cfg = stateConfig[ps.state as ProjectState] || stateConfig.PENDING
const StateIcon = cfg.icon
const total = ps.totalAssignments ?? 0
const submitted = ps.submittedCount ?? 0
const allDone = total > 0 && submitted === total
return (
<div
key={ps.id}
className="grid grid-cols-[40px_1fr_140px_160px_120px_100px_48px] gap-2 px-4 py-3 items-center border-b last:border-b-0 hover:bg-muted/30 text-sm"
className="grid grid-cols-[40px_1fr_140px_160px_120px_80px_100px_48px] gap-2 px-4 py-3 items-center border-b last:border-b-0 hover:bg-muted/30 text-sm"
>
<div>
<Checkbox
@@ -381,6 +385,15 @@ export function ProjectStatesTable({ competitionId, roundId }: ProjectStatesTabl
{cfg.label}
</Badge>
</div>
<div className="text-xs tabular-nums">
{total > 0 ? (
<span className={allDone ? 'text-green-600 font-medium' : 'text-muted-foreground'}>
{submitted}/{total}
</span>
) : (
<span className="text-muted-foreground"></span>
)}
</div>
<div className="text-xs text-muted-foreground">
{ps.enteredAt ? new Date(ps.enteredAt).toLocaleDateString() : '—'}
</div>

View File

@@ -703,7 +703,7 @@ export async function getProjectRoundStates(
roundId: string,
prisma: PrismaClient | any,
) {
return prisma.projectRoundState.findMany({
const states = await prisma.projectRoundState.findMany({
where: { roundId },
include: {
project: {
@@ -714,11 +714,37 @@ export async function getProjectRoundStates(
competitionCategory: true,
country: true,
status: true,
assignments: {
where: { roundId },
select: {
id: true,
isCompleted: true,
evaluation: { select: { status: true } },
},
},
},
},
},
orderBy: { enteredAt: 'desc' },
})
// Compute evaluation progress per project
return states.map((ps: any) => {
const assignments = ps.project?.assignments ?? []
const totalAssignments = assignments.length
const submittedCount = assignments.filter(
(a: any) => a.evaluation?.status === 'SUBMITTED'
).length
return {
...ps,
totalAssignments,
submittedCount,
project: {
...ps.project,
assignments: undefined, // strip raw assignments from response
},
}
})
}
export async function getProjectRoundState(