Comprehensive admin UI stats audit: fix 16 display bugs

HIGH fixes:
- H1: Competition detail project count no longer double-counts across rounds
- H2: Rounds page header stats use unfiltered round set
- H3: Rounds page "eval" label corrected to "asgn" (assignment count)
- H4: Observer reports project count uses distinct analytics count
- H5: Awards eligibility count filters to only eligible=true (backend)
- H6: Round detail projectCount derived from projectStates for consistency
- H7: Deliberation hasVoted derived from votes array (was always undefined)

MEDIUM fixes:
- M1: Reports page round status badges use correct ROUND_ACTIVE/ROUND_CLOSED enums
- M2: Observer reports badges use ROUND_ prefix instead of stale STAGE_ prefix
- M3: Deliberation list status badges use correct VOTING/TALLYING/RUNOFF enums
- M4: Competition list/detail round count excludes special-award rounds (backend)
- M5: Messages page shows actual recipient count instead of hardcoded "1 user"

LOW fixes:
- L2: Observer analytics jurorCount scoped to round when roundId provided
- L3: Analytics round-scoped project count uses ProjectRoundState not assignments
- L4: JuryGroup delete audit log reports member count (not assignment count)
- L5: Project rankings include unevaluated projects at bottom instead of hiding

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt
2026-02-19 09:56:09 +01:00
parent d117090fca
commit ae1685179c
12 changed files with 68 additions and 37 deletions

View File

@@ -100,7 +100,8 @@ function OverviewTab({ selectedValue }: { selectedValue: string | null }) {
)
}
const totalProjects = stages.reduce((acc, s) => acc + (s._count?.projects || 0), 0)
// Count distinct projects by collecting unique IDs, not summing per-round states
const totalProjects = overviewStats?.projectCount ?? stages.reduce((acc, s) => acc + (s._count?.projects || 0), 0)
const activeStages = stages.filter((s) => s.status === 'ROUND_ACTIVE').length
const totalPrograms = programs?.length || 0
@@ -285,14 +286,14 @@ function OverviewTab({ selectedValue }: { selectedValue: string | null }) {
<TableCell>
<Badge
variant={
stage.status === 'STAGE_ACTIVE'
stage.status === 'ROUND_ACTIVE'
? 'default'
: stage.status === 'STAGE_CLOSED'
: stage.status === 'ROUND_CLOSED'
? 'secondary'
: 'outline'
}
>
{stage.status === 'STAGE_ACTIVE' ? 'Active' : stage.status === 'STAGE_CLOSED' ? 'Closed' : stage.status}
{stage.status === 'ROUND_ACTIVE' ? 'Active' : stage.status === 'ROUND_CLOSED' ? 'Closed' : stage.status}
</Badge>
</TableCell>
</TableRow>
@@ -312,14 +313,14 @@ function OverviewTab({ selectedValue }: { selectedValue: string | null }) {
<p className="font-medium">{stage.name}</p>
<Badge
variant={
stage.status === 'STAGE_ACTIVE'
stage.status === 'ROUND_ACTIVE'
? 'default'
: stage.status === 'STAGE_CLOSED'
: stage.status === 'ROUND_CLOSED'
? 'secondary'
: 'outline'
}
>
{stage.status === 'STAGE_ACTIVE' ? 'Active' : stage.status === 'STAGE_CLOSED' ? 'Closed' : stage.status}
{stage.status === 'ROUND_ACTIVE' ? 'Active' : stage.status === 'ROUND_CLOSED' ? 'Closed' : stage.status}
</Badge>
</div>
<p className="text-sm text-muted-foreground">{stage.programName}</p>