feat: observer UX overhaul — reports, projects, charts, session & email
All checks were successful
Build and Push Docker Image / build (push) Successful in 11m2s

- Observer projects: default sort by status (rejected last), sortable status column
- Observer projects: search by country, institution, geographic zone
- Observer project detail: vertical timeline connectors between rounds
- Fix React key warning in ExpandableJurorTable and FilteringReportTabs
- Fix ScoreBadge text always white for better contrast on all backgrounds
- Remove misleading /30 denominator from heatmap juror reviewed count
- INTAKE stats: show Start-ups, Business Concepts, Countries (not States/Categories)
- DiversityMetrics: extractCountry() for country-only display in charts
- Fix nested button hydration error in filtering report mobile view
- Color project titles by outcome in filtering report (green/red/amber)
- Redesign CrossStageComparisonChart: funnel viz + metrics table with attrition %
- Center doughnut chart in StatusBreakdownChart
- Remove redundant RoundTypeStatsCards from evaluation report
- Move evaluation tab bar below overview header, rename to "Juror Assignments"
- Dev email override system (DEV_EMAIL_OVERRIDE env var)
- Session refresh on role change without re-login
- Role switcher in user dropdown menu
- formatCategory() utility for consistent category display
- Activity feed max height constraint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 13:37:50 +01:00
parent e7b99fff63
commit a556732b46
23 changed files with 2108 additions and 326 deletions

View File

@@ -128,7 +128,7 @@ function RoundNode({
return (
<button type="button" onClick={onClick} className="text-left focus:outline-none">
<Card className={cn(
'w-44 shrink-0 border shadow-sm transition-all cursor-pointer hover:shadow-md',
'w-44 shrink-0 border-2 border-border/60 shadow-sm transition-all cursor-pointer hover:shadow-md',
isSelected && 'ring-2 ring-brand-teal shadow-md',
)}>
<CardContent className="p-3 space-y-2">
@@ -219,7 +219,7 @@ function PipelineView({
{/* Award Tracks */}
{awardGroups.size > 0 && (
<div className="space-y-3 pt-1">
<div className="space-y-3 pt-4">
{Array.from(awardGroups.entries()).map(([awardId, group]) => (
<div
key={awardId}
@@ -313,7 +313,7 @@ export function ObserverDashboardContent({ userName }: { userName?: string }) {
<div className="grid grid-cols-3 md:grid-cols-6 divide-x divide-border">
{[
{ value: stats.projectCount, label: 'Projects' },
{ value: stats.activeRoundName ?? `${stats.activeRoundCount} Active`, label: 'Active Round', isText: !!stats.activeRoundName },
{ value: stats.activeRoundName ?? `${stats.activeRoundCount} Active`, label: 'Active Rounds', isText: !!stats.activeRoundName },
{ value: avgScore, label: 'Avg Score' },
{ value: `${stats.completionRate}%`, label: 'Completion' },
{ value: stats.jurorCount, label: 'Jurors' },