2026-02-14 15:26:42 +01:00
|
|
|
'use client'
|
|
|
|
|
|
Observer platform redesign Phase 4: migrate charts to Tremor, redesign all pages
- Migrate 9 chart components from Nivo to @tremor/react (BarChart, AreaChart, DonutChart, ScatterChart)
- Remove @nivo/*, @react-spring/web dependencies (45 packages removed)
- Redesign dashboard: 6 stat tiles, competition pipeline, score distribution, juror workload, activity feed
- Add new /observer/projects page with search, filters, sorting, pagination, CSV export
- Restructure reports page from 5 tabs to 3 (Progress, Jurors, Scores & Analytics) with per-tab CSV export
- Redesign project detail: breadcrumb nav, score card header, 3-tab layout (Overview/Evaluations/Files)
- Update loading skeletons to match new layouts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 21:45:01 +01:00
|
|
|
import { ScatterChart } from '@tremor/react'
|
2026-02-14 15:26:42 +01:00
|
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
|
|
|
|
import { Badge } from '@/components/ui/badge'
|
|
|
|
|
import {
|
|
|
|
|
Table,
|
|
|
|
|
TableBody,
|
|
|
|
|
TableCell,
|
|
|
|
|
TableHead,
|
|
|
|
|
TableHeader,
|
|
|
|
|
TableRow,
|
|
|
|
|
} from '@/components/ui/table'
|
|
|
|
|
import { AlertTriangle } from 'lucide-react'
|
Observer platform redesign Phase 4: migrate charts to Tremor, redesign all pages
- Migrate 9 chart components from Nivo to @tremor/react (BarChart, AreaChart, DonutChart, ScatterChart)
- Remove @nivo/*, @react-spring/web dependencies (45 packages removed)
- Redesign dashboard: 6 stat tiles, competition pipeline, score distribution, juror workload, activity feed
- Add new /observer/projects page with search, filters, sorting, pagination, CSV export
- Restructure reports page from 5 tabs to 3 (Progress, Jurors, Scores & Analytics) with per-tab CSV export
- Redesign project detail: breadcrumb nav, score card header, 3-tab layout (Overview/Evaluations/Files)
- Update loading skeletons to match new layouts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 21:45:01 +01:00
|
|
|
import { BRAND_DARK_BLUE, BRAND_RED } from './chart-theme'
|
2026-02-14 15:26:42 +01:00
|
|
|
|
|
|
|
|
interface JurorMetric {
|
|
|
|
|
userId: string
|
|
|
|
|
name: string
|
|
|
|
|
evaluationCount: number
|
|
|
|
|
averageScore: number
|
|
|
|
|
stddev: number
|
|
|
|
|
deviationFromOverall: number
|
|
|
|
|
isOutlier: boolean
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface JurorConsistencyProps {
|
|
|
|
|
data: {
|
|
|
|
|
overallAverage: number
|
|
|
|
|
jurors: JurorMetric[]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function JurorConsistencyChart({ data }: JurorConsistencyProps) {
|
2026-02-20 13:42:31 +01:00
|
|
|
if (!data?.jurors?.length) {
|
|
|
|
|
return (
|
|
|
|
|
<Card>
|
|
|
|
|
<CardContent className="flex items-center justify-center py-12">
|
|
|
|
|
<p className="text-muted-foreground">No juror consistency data available</p>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 15:26:42 +01:00
|
|
|
const outlierCount = data.jurors.filter((j) => j.isOutlier).length
|
|
|
|
|
|
Observer platform redesign Phase 4: migrate charts to Tremor, redesign all pages
- Migrate 9 chart components from Nivo to @tremor/react (BarChart, AreaChart, DonutChart, ScatterChart)
- Remove @nivo/*, @react-spring/web dependencies (45 packages removed)
- Redesign dashboard: 6 stat tiles, competition pipeline, score distribution, juror workload, activity feed
- Add new /observer/projects page with search, filters, sorting, pagination, CSV export
- Restructure reports page from 5 tabs to 3 (Progress, Jurors, Scores & Analytics) with per-tab CSV export
- Redesign project detail: breadcrumb nav, score card header, 3-tab layout (Overview/Evaluations/Files)
- Update loading skeletons to match new layouts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 21:45:01 +01:00
|
|
|
const scatterData = data.jurors.map((j) => ({
|
|
|
|
|
'Average Score': parseFloat(j.averageScore.toFixed(2)),
|
|
|
|
|
'Std Deviation': parseFloat(j.stddev.toFixed(2)),
|
|
|
|
|
category: j.isOutlier ? 'Outlier' : 'Normal',
|
|
|
|
|
name: j.name,
|
|
|
|
|
evaluations: j.evaluationCount,
|
|
|
|
|
size: Math.max(8, Math.min(20, j.evaluationCount * 2)),
|
|
|
|
|
}))
|
|
|
|
|
|
2026-02-14 15:26:42 +01:00
|
|
|
return (
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
{/* Scatter: Average Score vs Standard Deviation */}
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle className="flex items-center justify-between">
|
|
|
|
|
<span>Juror Scoring Patterns</span>
|
|
|
|
|
<span className="text-sm font-normal text-muted-foreground">
|
|
|
|
|
Overall Avg: {data.overallAverage.toFixed(2)}
|
|
|
|
|
{outlierCount > 0 && (
|
|
|
|
|
<Badge variant="destructive" className="ml-2">
|
|
|
|
|
{outlierCount} outlier{outlierCount > 1 ? 's' : ''}
|
|
|
|
|
</Badge>
|
|
|
|
|
)}
|
|
|
|
|
</span>
|
|
|
|
|
</CardTitle>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
Observer platform redesign Phase 4: migrate charts to Tremor, redesign all pages
- Migrate 9 chart components from Nivo to @tremor/react (BarChart, AreaChart, DonutChart, ScatterChart)
- Remove @nivo/*, @react-spring/web dependencies (45 packages removed)
- Redesign dashboard: 6 stat tiles, competition pipeline, score distribution, juror workload, activity feed
- Add new /observer/projects page with search, filters, sorting, pagination, CSV export
- Restructure reports page from 5 tabs to 3 (Progress, Jurors, Scores & Analytics) with per-tab CSV export
- Redesign project detail: breadcrumb nav, score card header, 3-tab layout (Overview/Evaluations/Files)
- Update loading skeletons to match new layouts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 21:45:01 +01:00
|
|
|
<ScatterChart
|
|
|
|
|
data={scatterData}
|
|
|
|
|
x="Average Score"
|
|
|
|
|
y="Std Deviation"
|
|
|
|
|
category="category"
|
|
|
|
|
size="size"
|
|
|
|
|
colors={[BRAND_DARK_BLUE, BRAND_RED] as string[]}
|
|
|
|
|
className="h-[400px]"
|
|
|
|
|
/>
|
2026-02-14 15:26:42 +01:00
|
|
|
<p className="text-xs text-muted-foreground mt-2 text-center">
|
Observer platform overhaul: Nivo charts, round-type stats, UX improvements
Phase 1: Fix 6 backend data bugs in analytics.ts (roundName filtering,
unscored projects, criteria scores, activeRoundCount scoping, email
privacy leaks in juror consistency + workload)
Phase 2-3: Migrate all 9 chart components from Recharts to Nivo
(@nivo/bar, @nivo/line, @nivo/pie, @nivo/scatterplot) with shared brand
theme, scoreGradient colors, and STATUS_COLORS map. Fixes scatter plot
outlier coloring and pie chart label visibility bugs.
Phase 4: Add round-type-aware stats (getRoundTypeStats backend +
RoundTypeStatsCards component) showing appropriate metrics per round
type (intake/filtering/evaluation/submission/mentoring/live/deliberation).
Phase 5: UX improvements — Stage→Round terminology, clickable dashboard
round links, URL-based round selection (?round=), round type indicators
in selectors, accessible Toggle-based cross-round comparison, sortable
project table columns (title/score/evaluations), brand score colors on
dashboard bar chart with aria labels.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 21:44:38 +01:00
|
|
|
Dot size represents number of evaluations. Red dots indicate outlier
|
|
|
|
|
jurors (2+ points from mean).
|
2026-02-14 15:26:42 +01:00
|
|
|
</p>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
|
|
{/* Juror details table */}
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle>Juror Consistency Details</CardTitle>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<Table>
|
|
|
|
|
<TableHeader>
|
|
|
|
|
<TableRow>
|
|
|
|
|
<TableHead>Juror</TableHead>
|
|
|
|
|
<TableHead className="text-right">Evaluations</TableHead>
|
|
|
|
|
<TableHead className="text-right">Avg Score</TableHead>
|
|
|
|
|
<TableHead className="text-right">Std Dev</TableHead>
|
Observer platform overhaul: Nivo charts, round-type stats, UX improvements
Phase 1: Fix 6 backend data bugs in analytics.ts (roundName filtering,
unscored projects, criteria scores, activeRoundCount scoping, email
privacy leaks in juror consistency + workload)
Phase 2-3: Migrate all 9 chart components from Recharts to Nivo
(@nivo/bar, @nivo/line, @nivo/pie, @nivo/scatterplot) with shared brand
theme, scoreGradient colors, and STATUS_COLORS map. Fixes scatter plot
outlier coloring and pie chart label visibility bugs.
Phase 4: Add round-type-aware stats (getRoundTypeStats backend +
RoundTypeStatsCards component) showing appropriate metrics per round
type (intake/filtering/evaluation/submission/mentoring/live/deliberation).
Phase 5: UX improvements — Stage→Round terminology, clickable dashboard
round links, URL-based round selection (?round=), round type indicators
in selectors, accessible Toggle-based cross-round comparison, sortable
project table columns (title/score/evaluations), brand score colors on
dashboard bar chart with aria labels.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 21:44:38 +01:00
|
|
|
<TableHead className="text-right">
|
|
|
|
|
Deviation from Mean
|
|
|
|
|
</TableHead>
|
2026-02-14 15:26:42 +01:00
|
|
|
<TableHead className="text-center">Status</TableHead>
|
|
|
|
|
</TableRow>
|
|
|
|
|
</TableHeader>
|
|
|
|
|
<TableBody>
|
|
|
|
|
{data.jurors.map((juror) => (
|
Observer platform overhaul: Nivo charts, round-type stats, UX improvements
Phase 1: Fix 6 backend data bugs in analytics.ts (roundName filtering,
unscored projects, criteria scores, activeRoundCount scoping, email
privacy leaks in juror consistency + workload)
Phase 2-3: Migrate all 9 chart components from Recharts to Nivo
(@nivo/bar, @nivo/line, @nivo/pie, @nivo/scatterplot) with shared brand
theme, scoreGradient colors, and STATUS_COLORS map. Fixes scatter plot
outlier coloring and pie chart label visibility bugs.
Phase 4: Add round-type-aware stats (getRoundTypeStats backend +
RoundTypeStatsCards component) showing appropriate metrics per round
type (intake/filtering/evaluation/submission/mentoring/live/deliberation).
Phase 5: UX improvements — Stage→Round terminology, clickable dashboard
round links, URL-based round selection (?round=), round type indicators
in selectors, accessible Toggle-based cross-round comparison, sortable
project table columns (title/score/evaluations), brand score colors on
dashboard bar chart with aria labels.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 21:44:38 +01:00
|
|
|
<TableRow
|
|
|
|
|
key={juror.userId}
|
|
|
|
|
className={juror.isOutlier ? 'bg-destructive/5' : ''}
|
|
|
|
|
>
|
2026-02-14 15:26:42 +01:00
|
|
|
<TableCell>
|
Observer platform overhaul: Nivo charts, round-type stats, UX improvements
Phase 1: Fix 6 backend data bugs in analytics.ts (roundName filtering,
unscored projects, criteria scores, activeRoundCount scoping, email
privacy leaks in juror consistency + workload)
Phase 2-3: Migrate all 9 chart components from Recharts to Nivo
(@nivo/bar, @nivo/line, @nivo/pie, @nivo/scatterplot) with shared brand
theme, scoreGradient colors, and STATUS_COLORS map. Fixes scatter plot
outlier coloring and pie chart label visibility bugs.
Phase 4: Add round-type-aware stats (getRoundTypeStats backend +
RoundTypeStatsCards component) showing appropriate metrics per round
type (intake/filtering/evaluation/submission/mentoring/live/deliberation).
Phase 5: UX improvements — Stage→Round terminology, clickable dashboard
round links, URL-based round selection (?round=), round type indicators
in selectors, accessible Toggle-based cross-round comparison, sortable
project table columns (title/score/evaluations), brand score colors on
dashboard bar chart with aria labels.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 21:44:38 +01:00
|
|
|
<p className="font-medium">{juror.name}</p>
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell className="text-right tabular-nums">
|
|
|
|
|
{juror.evaluationCount}
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell className="text-right tabular-nums">
|
|
|
|
|
{juror.averageScore.toFixed(2)}
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell className="text-right tabular-nums">
|
|
|
|
|
{juror.stddev.toFixed(2)}
|
2026-02-14 15:26:42 +01:00
|
|
|
</TableCell>
|
|
|
|
|
<TableCell className="text-right tabular-nums">
|
|
|
|
|
{juror.deviationFromOverall.toFixed(2)}
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell className="text-center">
|
|
|
|
|
{juror.isOutlier ? (
|
|
|
|
|
<Badge variant="destructive" className="gap-1">
|
|
|
|
|
<AlertTriangle className="h-3 w-3" />
|
|
|
|
|
Outlier
|
|
|
|
|
</Badge>
|
|
|
|
|
) : (
|
|
|
|
|
<Badge variant="secondary">Normal</Badge>
|
|
|
|
|
)}
|
|
|
|
|
</TableCell>
|
|
|
|
|
</TableRow>
|
|
|
|
|
))}
|
|
|
|
|
</TableBody>
|
|
|
|
|
</Table>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|