2026-02-14 15:26:42 +01:00
|
|
|
'use client'
|
|
|
|
|
|
|
|
|
|
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 { AnimatedCard } from '@/components/shared/animated-container'
|
|
|
|
|
import { GeographicSummaryCard } from '@/components/charts/geographic-summary-card'
|
2026-02-20 22:45:56 +01:00
|
|
|
import { useEditionContext } from '@/components/observer/observer-edition-context'
|
2026-02-14 15:26:42 +01:00
|
|
|
import {
|
|
|
|
|
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
|
|
|
Globe,
|
|
|
|
|
Activity,
|
2026-02-20 22:45:56 +01:00
|
|
|
Clock,
|
|
|
|
|
CheckCircle,
|
2026-03-04 20:18:50 +01:00
|
|
|
ClipboardList,
|
|
|
|
|
Upload,
|
|
|
|
|
Users,
|
2026-03-06 10:39:21 +01:00
|
|
|
Trophy,
|
2026-02-14 15:26:42 +01:00
|
|
|
} from 'lucide-react'
|
|
|
|
|
import { cn } from '@/lib/utils'
|
|
|
|
|
|
2026-03-04 20:18:50 +01:00
|
|
|
import { IntakePanel } from '@/components/observer/dashboard/intake-panel'
|
|
|
|
|
import { FilteringPanel } from '@/components/observer/dashboard/filtering-panel'
|
|
|
|
|
import { EvaluationPanel } from '@/components/observer/dashboard/evaluation-panel'
|
|
|
|
|
import { SubmissionPanel } from '@/components/observer/dashboard/submission-panel'
|
|
|
|
|
import { MentoringPanel } from '@/components/observer/dashboard/mentoring-panel'
|
|
|
|
|
import { LiveFinalPanel } from '@/components/observer/dashboard/live-final-panel'
|
|
|
|
|
import { DeliberationPanel } from '@/components/observer/dashboard/deliberation-panel'
|
|
|
|
|
import { PreviousRoundSection } from '@/components/observer/dashboard/previous-round-section'
|
|
|
|
|
|
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> = {
|
2026-03-04 20:18:50 +01:00
|
|
|
'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 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
|
|
|
|
2026-03-04 20:18:50 +01:00
|
|
|
const CATEGORY_ICONS: Record<string, { icon: typeof Activity; color: string }> = {
|
|
|
|
|
round: { icon: Clock, color: 'text-teal-500' },
|
|
|
|
|
evaluation: { icon: CheckCircle, color: 'text-blue-500' },
|
|
|
|
|
project: { icon: ClipboardList, color: 'text-emerald-500' },
|
|
|
|
|
file: { icon: Upload, color: 'text-violet-500' },
|
|
|
|
|
deliberation: { icon: Users, color: 'text-amber-500' },
|
|
|
|
|
system: { icon: Activity, color: 'text-slate-400' },
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function RoundPanel({ roundType, roundId, programId }: { roundType: string; roundId: string; programId: string }) {
|
|
|
|
|
switch (roundType) {
|
|
|
|
|
case 'INTAKE':
|
|
|
|
|
return <IntakePanel roundId={roundId} programId={programId} />
|
|
|
|
|
case 'FILTERING':
|
|
|
|
|
return <FilteringPanel roundId={roundId} />
|
|
|
|
|
case 'EVALUATION':
|
|
|
|
|
return <EvaluationPanel roundId={roundId} programId={programId} />
|
|
|
|
|
case 'SUBMISSION':
|
|
|
|
|
return <SubmissionPanel roundId={roundId} programId={programId} />
|
|
|
|
|
case 'MENTORING':
|
|
|
|
|
return <MentoringPanel roundId={roundId} />
|
|
|
|
|
case 'LIVE_FINAL':
|
|
|
|
|
return <LiveFinalPanel roundId={roundId} />
|
|
|
|
|
case 'DELIBERATION':
|
|
|
|
|
return <DeliberationPanel />
|
|
|
|
|
default:
|
|
|
|
|
return (
|
|
|
|
|
<Card className="p-6 text-center text-muted-foreground">
|
|
|
|
|
<p>Select a round to view details.</p>
|
|
|
|
|
</Card>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-06 10:39:21 +01:00
|
|
|
type RoundOverviewItem = {
|
|
|
|
|
roundId: string
|
|
|
|
|
roundName: string
|
|
|
|
|
roundType: string
|
|
|
|
|
roundStatus: string
|
|
|
|
|
totalProjects: number
|
|
|
|
|
completionRate: number
|
|
|
|
|
specialAwardId?: string | null
|
|
|
|
|
specialAwardName?: string | null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function RoundNode({
|
|
|
|
|
round,
|
|
|
|
|
isSelected,
|
|
|
|
|
onClick,
|
|
|
|
|
}: {
|
|
|
|
|
round: RoundOverviewItem
|
|
|
|
|
isSelected: boolean
|
|
|
|
|
onClick: () => void
|
|
|
|
|
}) {
|
|
|
|
|
const isActive = round.roundStatus === 'ROUND_ACTIVE'
|
|
|
|
|
return (
|
|
|
|
|
<button type="button" onClick={onClick} className="text-left focus:outline-none">
|
|
|
|
|
<Card className={cn(
|
2026-03-06 13:37:50 +01:00
|
|
|
'w-44 shrink-0 border-2 border-border/60 shadow-sm transition-all cursor-pointer hover:shadow-md',
|
2026-03-06 10:39:21 +01:00
|
|
|
isSelected && 'ring-2 ring-brand-teal shadow-md',
|
|
|
|
|
)}>
|
|
|
|
|
<CardContent className="p-3 space-y-2">
|
|
|
|
|
<div className="flex items-center gap-1.5">
|
|
|
|
|
<p className="text-xs font-semibold leading-tight truncate flex-1" title={round.roundName}>
|
|
|
|
|
{round.roundName}
|
|
|
|
|
</p>
|
|
|
|
|
{isActive && (
|
|
|
|
|
<span className="relative flex h-2.5 w-2.5 shrink-0">
|
|
|
|
|
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75" />
|
|
|
|
|
<span className="relative inline-flex rounded-full h-2.5 w-2.5 bg-emerald-500" />
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex flex-wrap gap-1">
|
|
|
|
|
<Badge variant="outline" className="text-[10px] px-1.5 py-0">
|
|
|
|
|
{round.roundType.replace(/_/g, ' ')}
|
|
|
|
|
</Badge>
|
|
|
|
|
<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>
|
|
|
|
|
</div>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
</button>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function PipelineView({
|
|
|
|
|
rounds,
|
|
|
|
|
selectedRoundId,
|
|
|
|
|
onSelectRound,
|
|
|
|
|
}: {
|
|
|
|
|
rounds: RoundOverviewItem[]
|
|
|
|
|
selectedRoundId: string
|
|
|
|
|
onSelectRound: (id: string) => void
|
|
|
|
|
}) {
|
|
|
|
|
// Split main pipeline from award tracks
|
|
|
|
|
const mainRounds = rounds.filter((r) => !r.specialAwardId)
|
|
|
|
|
const awardGroups = new Map<string, { name: string; rounds: RoundOverviewItem[] }>()
|
|
|
|
|
for (const r of rounds) {
|
|
|
|
|
if (!r.specialAwardId) continue
|
|
|
|
|
if (!awardGroups.has(r.specialAwardId)) {
|
|
|
|
|
awardGroups.set(r.specialAwardId, { name: r.specialAwardName ?? 'Special Award', rounds: [] })
|
|
|
|
|
}
|
|
|
|
|
awardGroups.get(r.specialAwardId)!.rounds.push(r)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="space-y-4">
|
|
|
|
|
{/* Main Competition Pipeline */}
|
|
|
|
|
{mainRounds.length > 0 && (
|
|
|
|
|
<div className="flex items-stretch gap-0 overflow-x-auto py-1 -my-1">
|
|
|
|
|
{mainRounds.map((round, idx) => (
|
|
|
|
|
<div key={round.roundId} className="flex items-center">
|
|
|
|
|
<RoundNode
|
|
|
|
|
round={round}
|
|
|
|
|
isSelected={selectedRoundId === round.roundId}
|
|
|
|
|
onClick={() => onSelectRound(round.roundId)}
|
|
|
|
|
/>
|
|
|
|
|
{idx < mainRounds.length - 1 && (
|
|
|
|
|
<div className="h-px w-6 shrink-0 border-t-2 border-brand-teal" />
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Award Tracks */}
|
|
|
|
|
{awardGroups.size > 0 && (
|
2026-03-06 13:37:50 +01:00
|
|
|
<div className="space-y-3 pt-4">
|
2026-03-06 10:39:21 +01:00
|
|
|
{Array.from(awardGroups.entries()).map(([awardId, group]) => (
|
|
|
|
|
<div
|
|
|
|
|
key={awardId}
|
|
|
|
|
className="rounded-lg border border-amber-200/80 bg-amber-50/30 p-3"
|
|
|
|
|
>
|
|
|
|
|
<div className="flex items-center gap-2 mb-3">
|
|
|
|
|
<div className="flex items-center justify-center h-6 w-6 rounded-full bg-amber-100 shrink-0">
|
|
|
|
|
<Trophy className="h-3.5 w-3.5 text-amber-600" />
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-xs font-semibold text-amber-800">{group.name}</p>
|
|
|
|
|
<Badge variant="outline" className="text-[10px] px-1.5 py-0 border-amber-300 text-amber-700">
|
|
|
|
|
Award Track
|
|
|
|
|
</Badge>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-stretch gap-0 overflow-x-auto">
|
|
|
|
|
{group.rounds.map((round, idx) => (
|
|
|
|
|
<div key={round.roundId} className="flex items-center">
|
|
|
|
|
<RoundNode
|
|
|
|
|
round={round}
|
|
|
|
|
isSelected={selectedRoundId === round.roundId}
|
|
|
|
|
onClick={() => onSelectRound(round.roundId)}
|
|
|
|
|
/>
|
|
|
|
|
{idx < group.rounds.length - 1 && (
|
|
|
|
|
<div className="h-px w-6 shrink-0 border-t-2 border-amber-400" />
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</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
|
|
|
export function ObserverDashboardContent({ userName }: { userName?: string }) {
|
2026-03-04 20:18:50 +01:00
|
|
|
const {
|
|
|
|
|
programs,
|
|
|
|
|
selectedProgramId,
|
|
|
|
|
selectedRoundId,
|
|
|
|
|
setSelectedRoundId,
|
|
|
|
|
selectedRoundType,
|
|
|
|
|
rounds,
|
|
|
|
|
activeRoundId,
|
|
|
|
|
} = useEditionContext()
|
2026-02-14 15:26:42 +01:00
|
|
|
|
2026-03-04 20:18:50 +01:00
|
|
|
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
|
|
|
)
|
|
|
|
|
|
2026-02-20 22:45:56 +01:00
|
|
|
const selectedProgram = programs.find((p) => p.id === selectedProgramId)
|
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 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: geoData } = trpc.analytics.getGeographicDistribution.useQuery(
|
|
|
|
|
{ programId: selectedProgramId },
|
|
|
|
|
{ enabled: !!selectedProgramId, refetchInterval: 30_000 },
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const { data: activityFeed } = trpc.analytics.getActivityFeed.useQuery(
|
2026-03-04 20:18:50 +01:00
|
|
|
{ limit: 15, roundId: selectedRoundId || undefined },
|
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
|
|
|
{ refetchInterval: 30_000 },
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const countryCount = geoData ? geoData.length : 0
|
|
|
|
|
const avgScore = stats ? computeAvgScore(stats.scoreDistribution) : '—'
|
|
|
|
|
|
2026-02-14 15:26:42 +01:00
|
|
|
return (
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
{/* Header */}
|
2026-02-20 22:45:56 +01:00
|
|
|
<div>
|
|
|
|
|
<h1 className="text-2xl font-semibold tracking-tight">Dashboard</h1>
|
|
|
|
|
<p className="text-muted-foreground">Welcome, {userName || 'Observer'}</p>
|
2026-02-14 15:26:42 +01:00
|
|
|
</div>
|
|
|
|
|
|
2026-02-21 00:26:04 +01:00
|
|
|
{/* Stats Strip */}
|
2026-02-14 15:26:42 +01:00
|
|
|
{statsLoading ? (
|
2026-02-21 00:26:04 +01:00
|
|
|
<Card className="p-4">
|
|
|
|
|
<Skeleton className="h-10 w-full" />
|
|
|
|
|
</Card>
|
2026-02-14 15:26:42 +01:00
|
|
|
) : stats ? (
|
2026-02-21 00:26:04 +01:00
|
|
|
<Card className="p-0 overflow-hidden">
|
|
|
|
|
<div className="grid grid-cols-3 md:grid-cols-6 divide-x divide-border">
|
|
|
|
|
{[
|
|
|
|
|
{ value: stats.projectCount, label: 'Projects' },
|
2026-03-06 13:37:50 +01:00
|
|
|
{ value: stats.activeRoundName ?? `${stats.activeRoundCount} Active`, label: 'Active Rounds', isText: !!stats.activeRoundName },
|
2026-02-21 00:26:04 +01:00
|
|
|
{ value: avgScore, label: 'Avg Score' },
|
|
|
|
|
{ value: `${stats.completionRate}%`, label: 'Completion' },
|
|
|
|
|
{ value: stats.jurorCount, label: 'Jurors' },
|
|
|
|
|
{ value: countryCount, label: 'Countries' },
|
|
|
|
|
].map((stat) => (
|
|
|
|
|
<div key={stat.label} className="px-4 py-3.5 text-center">
|
2026-02-21 10:12:21 +01:00
|
|
|
<p className={`font-semibold leading-tight ${
|
|
|
|
|
'isText' in stat && stat.isText ? 'text-sm truncate' : 'text-xl tabular-nums'
|
|
|
|
|
}`}>{stat.value}</p>
|
2026-02-21 00:26:04 +01:00
|
|
|
<p className="text-[11px] text-muted-foreground mt-0.5">{stat.label}</p>
|
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-21 00:26:04 +01:00
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</Card>
|
2026-02-14 15:26:42 +01:00
|
|
|
) : null}
|
|
|
|
|
|
2026-03-04 20:18:50 +01:00
|
|
|
{/* Clickable Pipeline */}
|
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={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>
|
2026-03-04 20:18:50 +01:00
|
|
|
<CardDescription>Click a round to view its details</CardDescription>
|
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
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
{overviewLoading || !competitionId ? (
|
|
|
|
|
<div className="flex gap-4 overflow-x-auto pb-2">
|
|
|
|
|
{[...Array(4)].map((_, i) => (
|
2026-03-04 20:18:50 +01:00
|
|
|
<Skeleton key={i} className="h-32 w-44 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 ? (
|
2026-03-06 10:39:21 +01:00
|
|
|
<PipelineView
|
|
|
|
|
rounds={roundOverview.rounds}
|
|
|
|
|
selectedRoundId={selectedRoundId}
|
|
|
|
|
onSelectRound={setSelectedRoundId}
|
|
|
|
|
/>
|
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
|
|
|
|
2026-03-04 20:18:50 +01:00
|
|
|
{/* Main Content: Round Panel + Activity Feed */}
|
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-1 gap-6 lg:grid-cols-3">
|
2026-03-04 20:18:50 +01:00
|
|
|
{/* Left: Round-specific panel */}
|
|
|
|
|
<div className="lg:col-span-2">
|
|
|
|
|
{selectedRoundId && selectedRoundType ? (
|
|
|
|
|
<RoundPanel
|
|
|
|
|
roundType={selectedRoundType}
|
|
|
|
|
roundId={selectedRoundId}
|
|
|
|
|
programId={selectedProgramId}
|
|
|
|
|
/>
|
|
|
|
|
) : (
|
|
|
|
|
<Card className="p-6 text-center text-muted-foreground">
|
|
|
|
|
<p>Select a round from the pipeline above.</p>
|
2026-02-20 23:09:06 +01:00
|
|
|
</Card>
|
2026-03-04 20:18:50 +01:00
|
|
|
)}
|
2026-02-20 23:09:06 +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
|
|
|
|
2026-03-04 20:18:50 +01:00
|
|
|
{/* Right: Activity Feed */}
|
2026-02-20 23:09:06 +01:00
|
|
|
<AnimatedCard index={9}>
|
2026-03-04 20:18:50 +01:00
|
|
|
<Card className="h-full flex flex-col">
|
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
|
|
|
<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>
|
2026-03-04 20:18:50 +01:00
|
|
|
<CardContent className="flex-1 overflow-hidden">
|
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
|
|
|
{activityFeed && activityFeed.length > 0 ? (
|
2026-03-04 20:18:50 +01:00
|
|
|
<div className="max-h-[600px] overflow-y-auto -mr-2 pr-2 space-y-3">
|
|
|
|
|
{activityFeed.slice(0, 8).map((item) => {
|
|
|
|
|
const iconDef = CATEGORY_ICONS[item.category ?? 'system'] ?? CATEGORY_ICONS.system
|
|
|
|
|
const IconComponent = iconDef.icon
|
|
|
|
|
const iconColor = iconDef.color
|
2026-02-20 22:45:56 +01:00
|
|
|
return (
|
|
|
|
|
<div key={item.id} className="flex items-start gap-3">
|
|
|
|
|
<IconComponent className={cn('mt-0.5 h-4 w-4 shrink-0', iconColor)} />
|
|
|
|
|
<p className="min-w-0 flex-1 text-sm leading-snug">
|
2026-03-04 20:18:50 +01:00
|
|
|
{item.description}
|
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>
|
2026-02-20 22:45:56 +01:00
|
|
|
<span className="shrink-0 text-[11px] tabular-nums text-muted-foreground">
|
|
|
|
|
{relativeTime(item.createdAt)}
|
|
|
|
|
</span>
|
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-20 22:45:56 +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
|
|
|
</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-20 23:09:06 +01:00
|
|
|
|
2026-03-04 20:18:50 +01:00
|
|
|
{/* Previous Round Comparison */}
|
|
|
|
|
{selectedRoundId && (
|
|
|
|
|
<PreviousRoundSection currentRoundId={selectedRoundId} />
|
|
|
|
|
)}
|
|
|
|
|
|
2026-02-20 23:09:06 +01:00
|
|
|
{/* Full-width Map */}
|
|
|
|
|
<AnimatedCard index={11}>
|
|
|
|
|
{selectedProgramId ? (
|
|
|
|
|
<GeographicSummaryCard programId={selectedProgramId} />
|
|
|
|
|
) : (
|
|
|
|
|
<Card>
|
|
|
|
|
<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-[300px] w-full rounded-md" />
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
)}
|
|
|
|
|
</AnimatedCard>
|
2026-02-14 15:26:42 +01:00
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|