2026-02-14 15:26:42 +01:00
|
|
|
'use client'
|
|
|
|
|
|
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
|
|
|
import { useState, useEffect } from 'react'
|
|
|
|
|
import Link from 'next/link'
|
2026-02-20 18:39:53 +01:00
|
|
|
import type { Route } from 'next'
|
2026-02-14 15:26:42 +01:00
|
|
|
import { trpc } from '@/lib/trpc/client'
|
|
|
|
|
import {
|
|
|
|
|
Card,
|
|
|
|
|
CardContent,
|
|
|
|
|
CardHeader,
|
|
|
|
|
CardTitle,
|
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
|
|
|
CardDescription,
|
2026-02-14 15:26:42 +01:00
|
|
|
} from '@/components/ui/card'
|
|
|
|
|
import { Badge } from '@/components/ui/badge'
|
|
|
|
|
import { Progress } from '@/components/ui/progress'
|
|
|
|
|
import { Skeleton } from '@/components/ui/skeleton'
|
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 {
|
|
|
|
|
Select,
|
|
|
|
|
SelectContent,
|
|
|
|
|
SelectItem,
|
|
|
|
|
SelectTrigger,
|
|
|
|
|
SelectValue,
|
|
|
|
|
} from '@/components/ui/select'
|
2026-02-14 15:26:42 +01:00
|
|
|
import {
|
|
|
|
|
Table,
|
|
|
|
|
TableBody,
|
|
|
|
|
TableCell,
|
|
|
|
|
TableHead,
|
|
|
|
|
TableHeader,
|
|
|
|
|
TableRow,
|
|
|
|
|
} from '@/components/ui/table'
|
|
|
|
|
import { StatusBadge } from '@/components/shared/status-badge'
|
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 { AnimatedCard } from '@/components/shared/animated-container'
|
|
|
|
|
import { GeographicSummaryCard } from '@/components/charts/geographic-summary-card'
|
2026-02-14 15:26:42 +01:00
|
|
|
import {
|
|
|
|
|
ClipboardList,
|
|
|
|
|
BarChart3,
|
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
|
|
|
TrendingUp,
|
|
|
|
|
CheckCircle2,
|
|
|
|
|
Users,
|
|
|
|
|
Globe,
|
2026-02-14 15:26:42 +01:00
|
|
|
ChevronRight,
|
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
|
|
|
Activity,
|
|
|
|
|
RefreshCw,
|
2026-02-14 15:26:42 +01:00
|
|
|
} from 'lucide-react'
|
|
|
|
|
import { cn } from '@/lib/utils'
|
|
|
|
|
|
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
|
|
|
function relativeTime(date: Date | string): string {
|
|
|
|
|
const now = Date.now()
|
|
|
|
|
const then = new Date(date).getTime()
|
|
|
|
|
const diff = Math.floor((now - then) / 1000)
|
|
|
|
|
if (diff < 60) return `${diff}s ago`
|
|
|
|
|
if (diff < 3600) return `${Math.floor(diff / 60)}m ago`
|
|
|
|
|
if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`
|
|
|
|
|
return `${Math.floor(diff / 86400)}d ago`
|
|
|
|
|
}
|
2026-02-14 15:26:42 +01:00
|
|
|
|
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
|
|
|
function computeAvgScore(scoreDistribution: { label: string; count: number }[]): string {
|
|
|
|
|
const midpoints: Record<string, number> = {
|
|
|
|
|
'9-10': 9.5,
|
|
|
|
|
'7-8': 7.5,
|
|
|
|
|
'5-6': 5.5,
|
|
|
|
|
'3-4': 3.5,
|
|
|
|
|
'1-2': 1.5,
|
2026-02-14 15:26:42 +01:00
|
|
|
}
|
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
|
|
|
let total = 0
|
|
|
|
|
let weightedSum = 0
|
|
|
|
|
for (const b of scoreDistribution) {
|
|
|
|
|
const mid = midpoints[b.label]
|
|
|
|
|
if (mid !== undefined) {
|
|
|
|
|
weightedSum += mid * b.count
|
|
|
|
|
total += b.count
|
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
|
|
|
}
|
|
|
|
|
}
|
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
|
|
|
if (total === 0) return '—'
|
|
|
|
|
return (weightedSum / total).toFixed(1)
|
|
|
|
|
}
|
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
|
|
|
|
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 ACTIVITY_DOT_COLORS: Record<string, string> = {
|
|
|
|
|
ROUND_ACTIVATED: 'bg-emerald-500',
|
|
|
|
|
ROUND_CLOSED: 'bg-slate-500',
|
|
|
|
|
EVALUATION_SUBMITTED: 'bg-blue-500',
|
|
|
|
|
ASSIGNMENT_CREATED: 'bg-violet-500',
|
|
|
|
|
PROJECT_ADVANCED: 'bg-teal-500',
|
|
|
|
|
PROJECT_REJECTED: 'bg-rose-500',
|
|
|
|
|
RESULT_LOCKED: 'bg-amber-500',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const STATUS_BADGE_VARIANT: Record<string, 'default' | 'secondary' | 'outline'> = {
|
|
|
|
|
ROUND_ACTIVE: 'default',
|
|
|
|
|
ROUND_CLOSED: 'secondary',
|
|
|
|
|
ROUND_DRAFT: 'outline',
|
|
|
|
|
ROUND_ARCHIVED: 'secondary',
|
|
|
|
|
}
|
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
|
|
|
|
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
|
|
|
export function ObserverDashboardContent({ userName }: { userName?: string }) {
|
|
|
|
|
const [selectedProgramId, setSelectedProgramId] = useState<string>('')
|
|
|
|
|
const [selectedRoundId, setSelectedRoundId] = useState<string>('')
|
2026-02-14 15:26:42 +01:00
|
|
|
|
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 { data: programs } = trpc.program.list.useQuery(
|
|
|
|
|
{ includeStages: true },
|
|
|
|
|
{ refetchInterval: 30_000 },
|
|
|
|
|
)
|
2026-02-14 15:26:42 +01:00
|
|
|
|
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
|
|
|
useEffect(() => {
|
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
|
|
|
if (programs && programs.length > 0 && !selectedProgramId) {
|
|
|
|
|
const firstProgram = programs[0]
|
|
|
|
|
setSelectedProgramId(firstProgram.id)
|
|
|
|
|
const firstRound = (firstProgram.rounds ?? [])[0]
|
|
|
|
|
if (firstRound) setSelectedRoundId(firstRound.id)
|
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
|
|
|
}
|
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
|
|
|
}, [programs, selectedProgramId])
|
|
|
|
|
|
|
|
|
|
const roundIdParam = selectedRoundId || undefined
|
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
|
|
|
|
2026-02-14 15:26:42 +01:00
|
|
|
const { data: stats, isLoading: statsLoading } = trpc.analytics.getDashboardStats.useQuery(
|
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
|
|
|
{ roundId: roundIdParam },
|
|
|
|
|
{ refetchInterval: 30_000 },
|
2026-02-14 15:26:42 +01:00
|
|
|
)
|
|
|
|
|
|
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 selectedProgram = programs?.find((p) => p.id === selectedProgramId)
|
|
|
|
|
const competitionId = (selectedProgram?.rounds ?? [])[0]?.competitionId as string | undefined
|
|
|
|
|
|
|
|
|
|
const { data: roundOverview, isLoading: overviewLoading } = trpc.analytics.getRoundCompletionOverview.useQuery(
|
|
|
|
|
{ competitionId: competitionId! },
|
|
|
|
|
{ enabled: !!competitionId, refetchInterval: 30_000 },
|
|
|
|
|
)
|
2026-02-14 15:26:42 +01:00
|
|
|
|
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 { data: jurorWorkload } = trpc.analytics.getJurorWorkload.useQuery(
|
|
|
|
|
{ programId: selectedProgramId || undefined },
|
|
|
|
|
{ enabled: !!selectedProgramId, refetchInterval: 30_000 },
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const { data: geoData } = trpc.analytics.getGeographicDistribution.useQuery(
|
|
|
|
|
{ programId: selectedProgramId },
|
|
|
|
|
{ enabled: !!selectedProgramId, refetchInterval: 30_000 },
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const { data: projectsData } = trpc.analytics.getAllProjects.useQuery(
|
|
|
|
|
{ perPage: 10 },
|
|
|
|
|
{ refetchInterval: 30_000 },
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const { data: activityFeed } = trpc.analytics.getActivityFeed.useQuery(
|
|
|
|
|
{ limit: 10 },
|
|
|
|
|
{ refetchInterval: 30_000 },
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const countryCount = geoData ? geoData.length : 0
|
|
|
|
|
|
|
|
|
|
const avgScore = stats ? computeAvgScore(stats.scoreDistribution) : '—'
|
|
|
|
|
|
|
|
|
|
const topJurors = (jurorWorkload ?? []).slice(0, 5)
|
|
|
|
|
|
|
|
|
|
const scoreColors: Record<string, string> = {
|
|
|
|
|
'9-10': '#053d57',
|
|
|
|
|
'7-8': '#1e7a8a',
|
|
|
|
|
'5-6': '#557f8c',
|
|
|
|
|
'3-4': '#c4453a',
|
|
|
|
|
'1-2': '#de0f1e',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const maxScoreCount = stats
|
|
|
|
|
? Math.max(...stats.scoreDistribution.map((b) => b.count), 1)
|
|
|
|
|
: 1
|
2026-02-14 15:26:42 +01:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
{/* Header */}
|
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
|
|
|
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
|
|
|
<div>
|
|
|
|
|
<h1 className="text-2xl font-semibold tracking-tight">Dashboard</h1>
|
|
|
|
|
<p className="text-muted-foreground">Welcome, {userName || 'Observer'}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-3">
|
|
|
|
|
<div className="flex items-center gap-1.5 rounded-full border border-emerald-200 bg-emerald-50 px-3 py-1.5 text-xs font-medium text-emerald-700">
|
|
|
|
|
<span className="h-1.5 w-1.5 animate-pulse rounded-full bg-emerald-500" />
|
|
|
|
|
Auto-refresh
|
|
|
|
|
</div>
|
|
|
|
|
<Select
|
|
|
|
|
value={selectedProgramId}
|
|
|
|
|
onValueChange={(val) => {
|
|
|
|
|
setSelectedProgramId(val)
|
|
|
|
|
const prog = programs?.find((p) => p.id === val)
|
|
|
|
|
const firstRound = (prog?.rounds ?? [])[0]
|
|
|
|
|
setSelectedRoundId(firstRound?.id ?? '')
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<SelectTrigger className="w-full sm:w-[220px]">
|
|
|
|
|
<SelectValue placeholder="Select edition" />
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
<SelectContent>
|
|
|
|
|
{(programs ?? []).map((p) => (
|
|
|
|
|
<SelectItem key={p.id} value={p.id}>
|
|
|
|
|
{(p as { year?: number }).year ? `${(p as { year?: number }).year} Edition` : p.name ?? p.id}
|
|
|
|
|
</SelectItem>
|
|
|
|
|
))}
|
|
|
|
|
</SelectContent>
|
|
|
|
|
</Select>
|
|
|
|
|
</div>
|
2026-02-14 15:26:42 +01:00
|
|
|
</div>
|
|
|
|
|
|
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
|
|
|
{/* Six Stat Tiles */}
|
2026-02-14 15:26:42 +01:00
|
|
|
{statsLoading ? (
|
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
|
|
|
<div className="grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-6">
|
|
|
|
|
{[...Array(6)].map((_, i) => (
|
|
|
|
|
<Card key={i} className="p-4">
|
|
|
|
|
<Skeleton className="h-8 w-8 rounded-lg mb-3" />
|
|
|
|
|
<Skeleton className="h-7 w-16 mb-1" />
|
|
|
|
|
<Skeleton className="h-3 w-20" />
|
2026-02-14 15:26:42 +01:00
|
|
|
</Card>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
) : stats ? (
|
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
|
|
|
<div className="grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-6">
|
|
|
|
|
<AnimatedCard index={0}>
|
|
|
|
|
<Card className="group cursor-default p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md">
|
|
|
|
|
<div className="mb-3 inline-flex rounded-lg bg-emerald-100 p-2">
|
|
|
|
|
<ClipboardList className="h-5 w-5 text-emerald-600" />
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-2xl font-bold tabular-nums">{stats.projectCount}</p>
|
|
|
|
|
<p className="text-xs text-muted-foreground">Total Projects</p>
|
|
|
|
|
</Card>
|
|
|
|
|
</AnimatedCard>
|
2026-02-14 15:26:42 +01:00
|
|
|
|
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
|
|
|
<AnimatedCard index={1}>
|
|
|
|
|
<Card className="group cursor-default p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md">
|
|
|
|
|
<div className="mb-3 inline-flex rounded-lg bg-blue-100 p-2">
|
|
|
|
|
<BarChart3 className="h-5 w-5 text-blue-600" />
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-2xl font-bold tabular-nums">{stats.activeRoundCount}</p>
|
|
|
|
|
<p className="text-xs text-muted-foreground">Active Rounds</p>
|
|
|
|
|
</Card>
|
|
|
|
|
</AnimatedCard>
|
|
|
|
|
|
|
|
|
|
<AnimatedCard index={2}>
|
|
|
|
|
<Card className="group cursor-default p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md">
|
|
|
|
|
<div className="mb-3 inline-flex rounded-lg bg-amber-100 p-2">
|
|
|
|
|
<TrendingUp className="h-5 w-5 text-amber-600" />
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-2xl font-bold tabular-nums">{avgScore}</p>
|
|
|
|
|
<p className="text-xs text-muted-foreground">Avg Score</p>
|
|
|
|
|
</Card>
|
|
|
|
|
</AnimatedCard>
|
|
|
|
|
|
|
|
|
|
<AnimatedCard index={3}>
|
|
|
|
|
<Card className="group cursor-default p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md">
|
|
|
|
|
<div className="mb-3 inline-flex rounded-lg bg-teal-100 p-2">
|
|
|
|
|
<CheckCircle2 className="h-5 w-5 text-teal-600" />
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-2xl font-bold tabular-nums">{stats.completionRate}%</p>
|
|
|
|
|
<div className="mt-1">
|
|
|
|
|
<Progress value={stats.completionRate} className="h-1.5" />
|
|
|
|
|
</div>
|
|
|
|
|
<p className="mt-1 text-xs text-muted-foreground">Completion</p>
|
|
|
|
|
</Card>
|
|
|
|
|
</AnimatedCard>
|
|
|
|
|
|
|
|
|
|
<AnimatedCard index={4}>
|
|
|
|
|
<Card className="group cursor-default p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md">
|
|
|
|
|
<div className="mb-3 inline-flex rounded-lg bg-violet-100 p-2">
|
|
|
|
|
<Users className="h-5 w-5 text-violet-600" />
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-2xl font-bold tabular-nums">{stats.jurorCount}</p>
|
|
|
|
|
<p className="text-xs text-muted-foreground">Active Jurors</p>
|
|
|
|
|
</Card>
|
|
|
|
|
</AnimatedCard>
|
|
|
|
|
|
|
|
|
|
<AnimatedCard index={5}>
|
|
|
|
|
<Card className="group cursor-default p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md">
|
|
|
|
|
<div className="mb-3 inline-flex rounded-lg bg-rose-100 p-2">
|
|
|
|
|
<Globe className="h-5 w-5 text-rose-600" />
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-2xl font-bold tabular-nums">{countryCount}</p>
|
|
|
|
|
<p className="text-xs text-muted-foreground">Countries</p>
|
|
|
|
|
</Card>
|
|
|
|
|
</AnimatedCard>
|
2026-02-14 15:26:42 +01:00
|
|
|
</div>
|
|
|
|
|
) : null}
|
|
|
|
|
|
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
|
|
|
{/* Pipeline */}
|
|
|
|
|
<AnimatedCard index={6}>
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader className="pb-3">
|
|
|
|
|
<CardTitle className="flex items-center gap-2.5 text-base">
|
|
|
|
|
<div className="rounded-lg bg-blue-500/10 p-1.5">
|
|
|
|
|
<BarChart3 className="h-4 w-4 text-blue-500" />
|
|
|
|
|
</div>
|
|
|
|
|
Competition Pipeline
|
|
|
|
|
</CardTitle>
|
|
|
|
|
<CardDescription>Round-by-round progression overview</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
{overviewLoading || !competitionId ? (
|
|
|
|
|
<div className="flex gap-4 overflow-x-auto pb-2">
|
|
|
|
|
{[...Array(4)].map((_, i) => (
|
|
|
|
|
<Skeleton key={i} className="h-32 w-40 shrink-0 rounded-lg" />
|
2026-02-14 15:26:42 +01:00
|
|
|
))}
|
|
|
|
|
</div>
|
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
|
|
|
) : roundOverview && roundOverview.rounds.length > 0 ? (
|
|
|
|
|
<div className="flex items-stretch gap-0 overflow-x-auto pb-2">
|
|
|
|
|
{roundOverview.rounds.map((round, idx) => (
|
|
|
|
|
<div key={round.roundName + idx} className="flex items-center">
|
|
|
|
|
<Card className="w-44 shrink-0 border shadow-sm">
|
|
|
|
|
<CardContent className="p-3 space-y-2">
|
|
|
|
|
<p className="text-xs font-semibold leading-tight truncate" title={round.roundName}>
|
|
|
|
|
{round.roundName}
|
|
|
|
|
</p>
|
|
|
|
|
<div className="flex flex-wrap gap-1">
|
|
|
|
|
<Badge variant="outline" className="text-[10px] px-1.5 py-0">
|
|
|
|
|
{round.roundType.replace(/_/g, ' ')}
|
2026-02-20 18:39:53 +01:00
|
|
|
</Badge>
|
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
|
|
|
<Badge
|
|
|
|
|
variant={STATUS_BADGE_VARIANT[round.roundStatus] ?? 'outline'}
|
|
|
|
|
className="text-[10px] px-1.5 py-0"
|
|
|
|
|
>
|
|
|
|
|
{round.roundStatus === 'ROUND_ACTIVE'
|
|
|
|
|
? 'Active'
|
|
|
|
|
: round.roundStatus === 'ROUND_CLOSED'
|
|
|
|
|
? 'Closed'
|
|
|
|
|
: round.roundStatus === 'ROUND_DRAFT'
|
|
|
|
|
? 'Draft'
|
|
|
|
|
: round.roundStatus === 'ROUND_ARCHIVED'
|
|
|
|
|
? 'Archived'
|
|
|
|
|
: round.roundStatus}
|
|
|
|
|
</Badge>
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-xs text-muted-foreground">
|
|
|
|
|
{round.totalProjects} project{round.totalProjects !== 1 ? 's' : ''}
|
|
|
|
|
</p>
|
|
|
|
|
<div className="space-y-1">
|
|
|
|
|
<Progress value={round.completionRate} className="h-1.5" />
|
|
|
|
|
<p className="text-[10px] text-muted-foreground tabular-nums">
|
|
|
|
|
{round.completionRate}% complete
|
|
|
|
|
</p>
|
2026-02-20 18:39:53 +01:00
|
|
|
</div>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
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
|
|
|
{idx < roundOverview.rounds.length - 1 && (
|
|
|
|
|
<div className="h-px w-6 shrink-0 border-t-2 border-brand-teal" />
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2026-02-14 15:26:42 +01:00
|
|
|
))}
|
|
|
|
|
</div>
|
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
|
|
|
) : (
|
|
|
|
|
<p className="text-sm text-muted-foreground">No round data available for this competition.</p>
|
|
|
|
|
)}
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
</AnimatedCard>
|
2026-02-14 15:26:42 +01:00
|
|
|
|
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
|
|
|
{/* Middle Row */}
|
|
|
|
|
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
|
|
|
|
{/* Score Distribution */}
|
|
|
|
|
<AnimatedCard index={7}>
|
|
|
|
|
<Card className="h-full">
|
|
|
|
|
<CardHeader className="pb-3">
|
|
|
|
|
<CardTitle className="flex items-center gap-2.5 text-base">
|
|
|
|
|
<div className="rounded-lg bg-amber-500/10 p-1.5">
|
|
|
|
|
<TrendingUp className="h-4 w-4 text-amber-500" />
|
|
|
|
|
</div>
|
|
|
|
|
Score Distribution
|
|
|
|
|
</CardTitle>
|
|
|
|
|
<CardDescription>Evaluation score buckets</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
{stats ? (
|
|
|
|
|
<div className="space-y-2.5">
|
|
|
|
|
{stats.scoreDistribution.map((bucket) => (
|
|
|
|
|
<div key={bucket.label} className="flex items-center gap-3">
|
|
|
|
|
<span className="w-10 text-right text-xs font-medium tabular-nums text-muted-foreground">
|
|
|
|
|
{bucket.label}
|
|
|
|
|
</span>
|
|
|
|
|
<div className="flex-1 overflow-hidden rounded-full bg-muted" style={{ height: 20 }}>
|
|
|
|
|
<div
|
|
|
|
|
className="h-full rounded-full transition-all duration-500"
|
|
|
|
|
style={{
|
|
|
|
|
width: `${maxScoreCount > 0 ? (bucket.count / maxScoreCount) * 100 : 0}%`,
|
|
|
|
|
backgroundColor: scoreColors[bucket.label] ?? '#557f8c',
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<span className="w-8 text-right text-xs tabular-nums text-muted-foreground">
|
|
|
|
|
{bucket.count}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="space-y-2.5">
|
|
|
|
|
{[...Array(5)].map((_, i) => (
|
|
|
|
|
<Skeleton key={i} className="h-5 w-full" />
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
</AnimatedCard>
|
|
|
|
|
|
|
|
|
|
{/* Juror Workload */}
|
|
|
|
|
<AnimatedCard index={8}>
|
|
|
|
|
<Card className="h-full">
|
|
|
|
|
<CardHeader className="pb-3">
|
|
|
|
|
<CardTitle className="flex items-center gap-2.5 text-base">
|
|
|
|
|
<div className="rounded-lg bg-violet-500/10 p-1.5">
|
|
|
|
|
<Users className="h-4 w-4 text-violet-500" />
|
|
|
|
|
</div>
|
|
|
|
|
Juror Workload
|
|
|
|
|
</CardTitle>
|
|
|
|
|
<CardDescription>Top 5 jurors by assignment</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
{topJurors.length > 0 ? (
|
|
|
|
|
<div className="space-y-4">
|
|
|
|
|
{topJurors.map((juror) => (
|
|
|
|
|
<div key={juror.id} className="space-y-1">
|
|
|
|
|
<div className="flex items-center justify-between text-sm">
|
|
|
|
|
<span className="truncate font-medium" title={juror.name ?? ''}>
|
|
|
|
|
{juror.name ?? 'Unknown'}
|
|
|
|
|
</span>
|
|
|
|
|
<span className="ml-2 shrink-0 text-xs tabular-nums text-muted-foreground">
|
|
|
|
|
{juror.completionRate}%
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<Progress value={juror.completionRate} className="h-1.5" />
|
|
|
|
|
<p className="text-[11px] text-muted-foreground">
|
|
|
|
|
{juror.completed} / {juror.assigned} evaluations
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="space-y-4">
|
|
|
|
|
{[...Array(5)].map((_, i) => (
|
|
|
|
|
<div key={i} className="space-y-1">
|
|
|
|
|
<Skeleton className="h-4 w-full" />
|
|
|
|
|
<Skeleton className="h-1.5 w-full" />
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
2026-02-14 15:26:42 +01:00
|
|
|
</div>
|
|
|
|
|
)}
|
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
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
</AnimatedCard>
|
|
|
|
|
|
|
|
|
|
{/* Project Origins */}
|
|
|
|
|
<AnimatedCard index={9}>
|
|
|
|
|
{selectedProgramId ? (
|
|
|
|
|
<GeographicSummaryCard programId={selectedProgramId} />
|
2026-02-14 15:26:42 +01:00
|
|
|
) : (
|
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
|
|
|
<Card className="h-full">
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle className="flex items-center gap-2 text-base">
|
|
|
|
|
<Globe className="h-5 w-5" />
|
|
|
|
|
Project Origins
|
|
|
|
|
</CardTitle>
|
|
|
|
|
<CardDescription>Geographic distribution of projects</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<Skeleton className="h-[250px] w-full rounded-md" />
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
2026-02-14 15:26:42 +01:00
|
|
|
)}
|
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
|
|
|
</AnimatedCard>
|
|
|
|
|
</div>
|
2026-02-14 15:26:42 +01:00
|
|
|
|
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
|
|
|
{/* Bottom Row */}
|
|
|
|
|
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
|
|
|
|
{/* Recent Projects Table */}
|
|
|
|
|
<AnimatedCard index={10}>
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader className="pb-3">
|
|
|
|
|
<CardTitle className="flex items-center gap-2.5 text-base">
|
|
|
|
|
<div className="rounded-lg bg-emerald-500/10 p-1.5">
|
|
|
|
|
<ClipboardList className="h-4 w-4 text-emerald-500" />
|
|
|
|
|
</div>
|
|
|
|
|
Recent Projects
|
|
|
|
|
</CardTitle>
|
|
|
|
|
<CardDescription>Latest project activity</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent className="p-0">
|
|
|
|
|
{projectsData && projectsData.projects.length > 0 ? (
|
|
|
|
|
<>
|
|
|
|
|
<Table>
|
|
|
|
|
<TableHeader>
|
|
|
|
|
<TableRow>
|
|
|
|
|
<TableHead>Project</TableHead>
|
|
|
|
|
<TableHead>Status</TableHead>
|
|
|
|
|
<TableHead className="text-right">Score</TableHead>
|
|
|
|
|
</TableRow>
|
|
|
|
|
</TableHeader>
|
|
|
|
|
<TableBody>
|
|
|
|
|
{projectsData.projects.map((project) => (
|
|
|
|
|
<TableRow key={project.id}>
|
|
|
|
|
<TableCell className="max-w-[180px]">
|
|
|
|
|
<Link
|
|
|
|
|
href={`/observer/projects/${project.id}` as Route}
|
|
|
|
|
className="block truncate text-sm font-medium hover:underline"
|
|
|
|
|
title={project.title}
|
|
|
|
|
>
|
|
|
|
|
{project.title}
|
|
|
|
|
</Link>
|
|
|
|
|
{project.teamName && (
|
|
|
|
|
<p className="truncate text-[11px] text-muted-foreground">{project.teamName}</p>
|
|
|
|
|
)}
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell>
|
|
|
|
|
<StatusBadge status={project.status} />
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell className="text-right tabular-nums text-sm">
|
|
|
|
|
{project.averageScore !== null ? project.averageScore.toFixed(1) : '—'}
|
|
|
|
|
</TableCell>
|
|
|
|
|
</TableRow>
|
|
|
|
|
))}
|
|
|
|
|
</TableBody>
|
|
|
|
|
</Table>
|
|
|
|
|
<div className="border-t px-4 py-3">
|
|
|
|
|
<Link
|
|
|
|
|
href={"/observer/projects" as Route}
|
|
|
|
|
className="flex items-center gap-1 text-sm font-medium text-brand-teal hover:underline"
|
|
|
|
|
>
|
|
|
|
|
View All <ChevronRight className="h-4 w-4" />
|
|
|
|
|
</Link>
|
2026-02-14 15:26:42 +01:00
|
|
|
</div>
|
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
|
|
|
</>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="space-y-2 p-4">
|
|
|
|
|
{[...Array(5)].map((_, i) => (
|
|
|
|
|
<Skeleton key={i} className="h-10 w-full" />
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
2026-02-14 15:26:42 +01:00
|
|
|
</AnimatedCard>
|
|
|
|
|
|
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
|
|
|
{/* Activity Feed */}
|
|
|
|
|
<AnimatedCard index={11}>
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader className="pb-3">
|
|
|
|
|
<CardTitle className="flex items-center gap-2.5 text-base">
|
|
|
|
|
<div className="rounded-lg bg-blue-500/10 p-1.5">
|
|
|
|
|
<Activity className="h-4 w-4 text-blue-500" />
|
|
|
|
|
</div>
|
|
|
|
|
Activity Feed
|
|
|
|
|
</CardTitle>
|
|
|
|
|
<CardDescription>Recent platform events</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
{activityFeed && activityFeed.length > 0 ? (
|
|
|
|
|
<div className="space-y-3">
|
|
|
|
|
{activityFeed.map((item) => (
|
|
|
|
|
<div key={item.id} className="flex items-start gap-3">
|
|
|
|
|
<span
|
|
|
|
|
className={cn(
|
|
|
|
|
'mt-1.5 h-2 w-2 shrink-0 rounded-full',
|
|
|
|
|
ACTIVITY_DOT_COLORS[item.eventType] ?? 'bg-slate-400',
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
<div className="min-w-0 flex-1">
|
|
|
|
|
<p className="text-sm leading-snug">
|
|
|
|
|
<span className="font-medium">
|
|
|
|
|
{item.eventType.replace(/_/g, ' ').toLowerCase().replace(/^\w/, (c) => c.toUpperCase())}
|
|
|
|
|
</span>
|
|
|
|
|
{item.entityType && (
|
|
|
|
|
<span className="text-muted-foreground"> — {item.entityType.replace(/_/g, ' ').toLowerCase()}</span>
|
|
|
|
|
)}
|
|
|
|
|
</p>
|
|
|
|
|
{item.actorName && (
|
|
|
|
|
<p className="text-[11px] text-muted-foreground">by {item.actorName}</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<span className="shrink-0 text-[11px] tabular-nums text-muted-foreground">
|
|
|
|
|
{relativeTime(item.createdAt)}
|
|
|
|
|
</span>
|
2026-02-14 15:26:42 +01:00
|
|
|
</div>
|
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
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="space-y-3">
|
|
|
|
|
{[...Array(6)].map((_, i) => (
|
|
|
|
|
<div key={i} className="flex items-center gap-3">
|
|
|
|
|
<Skeleton className="h-2 w-2 rounded-full" />
|
|
|
|
|
<Skeleton className="h-4 flex-1" />
|
|
|
|
|
<Skeleton className="h-3 w-12" />
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
2026-02-14 15:26:42 +01:00
|
|
|
</AnimatedCard>
|
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
|
|
|
</div>
|
2026-02-14 15:26:42 +01:00
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|