feat: clickable status badges, observer status alignment, CSV export all
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m36s

- Admin projects: status summary badges are clickable to filter by round state
  with ring highlight, opacity fade, and clear button
- Add roundStates filter param to project.list backend query
  (filters by latest round state per project, consistent with counts)
- Observer status dropdown now uses ProjectRoundState values
  (Pending/In Progress/Completed/Passed/Rejected/Withdrawn)
- Observer status derived from latest ProjectRoundState instead of stale Project.status
- Observer CSV export fetches all matching projects, not just current page
- Add PENDING and PASSED styles to StatusBadge component

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 17:19:12 +01:00
parent ffe12a9e85
commit 0d94ee1fe8
7 changed files with 114 additions and 42 deletions

View File

@@ -231,7 +231,7 @@ export function EvaluationPanel({ roundId, programId }: { roundId: string; progr
{projects
.filter((p) => {
const s = p.observerStatus ?? p.status
return s !== 'NOT_REVIEWED' && s !== 'SUBMITTED'
return s !== 'PENDING'
})
.slice(0, 6)
.map((p) => (

View File

@@ -141,11 +141,19 @@ export function ObserverProjectsContent() {
{ refetchInterval: 30_000 },
)
const utils = trpc.useUtils()
const handleRequestCsvData = useCallback(async () => {
setCsvLoading(true)
try {
const allData = await new Promise<typeof projectsData>((resolve) => {
resolve(projectsData)
const allData = await utils.analytics.getAllProjects.fetch({
roundId: roundFilter !== 'all' ? roundFilter : undefined,
search: debouncedSearch || undefined,
status: statusFilter !== 'all' ? statusFilter : undefined,
sortBy,
sortDir,
page: 1,
perPage: 100,
exportAll: true,
})
if (!allData?.projects) {
@@ -158,7 +166,7 @@ export function ObserverProjectsContent() {
teamName: p.teamName ?? '',
country: p.country ?? '',
roundName: p.roundName ?? '',
status: p.status,
status: p.observerStatus ?? p.status,
averageScore: p.averageScore !== null ? p.averageScore.toFixed(2) : '',
evaluationCount: p.evaluationCount,
}))
@@ -174,7 +182,7 @@ export function ObserverProjectsContent() {
setCsvLoading(false)
return undefined
}
}, [projectsData])
}, [utils, roundFilter, debouncedSearch, statusFilter, sortBy, sortDir])
const SortIcon = ({ column }: { column: 'title' | 'score' | 'evaluations' }) => {
if (sortBy !== column)
@@ -251,13 +259,12 @@ export function ObserverProjectsContent() {
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Statuses</SelectItem>
<SelectItem value="SUBMITTED">Submitted</SelectItem>
<SelectItem value="NOT_REVIEWED">Not Reviewed</SelectItem>
<SelectItem value="UNDER_REVIEW">Under Review</SelectItem>
<SelectItem value="REVIEWED">Reviewed</SelectItem>
<SelectItem value="SEMIFINALIST">Semi-finalist</SelectItem>
<SelectItem value="FINALIST">Finalist</SelectItem>
<SelectItem value="PENDING">Pending</SelectItem>
<SelectItem value="IN_PROGRESS">In Progress</SelectItem>
<SelectItem value="COMPLETED">Completed</SelectItem>
<SelectItem value="PASSED">Passed</SelectItem>
<SelectItem value="REJECTED">Rejected</SelectItem>
<SelectItem value="WITHDRAWN">Withdrawn</SelectItem>
</SelectContent>
</Select>
</div>

View File

@@ -28,9 +28,11 @@ const STATUS_STYLES: Record<string, { variant: BadgeProps['variant']; className?
NOT_REVIEWED: { variant: 'secondary', className: 'bg-slate-500/10 text-slate-600 border-slate-200 dark:text-slate-400' },
REVIEWED: { variant: 'default', className: 'bg-emerald-500/10 text-emerald-700 border-emerald-200 dark:text-emerald-400' },
// Evaluation statuses
// Round state statuses
PENDING: { variant: 'secondary', className: 'bg-slate-500/10 text-slate-600 border-slate-200 dark:text-slate-400' },
IN_PROGRESS: { variant: 'default', className: 'bg-blue-500/10 text-blue-700 border-blue-200 dark:text-blue-400' },
COMPLETED: { variant: 'default', className: 'bg-emerald-500/10 text-emerald-700 border-emerald-200 dark:text-emerald-400' },
PASSED: { variant: 'default', className: 'bg-green-500/10 text-green-700 border-green-200 dark:text-green-400' },
// User statuses
NONE: { variant: 'secondary', className: 'bg-slate-500/10 text-slate-500 border-slate-200 dark:text-slate-400' },