feat: observer UX overhaul — reports, projects, charts, session & email
All checks were successful
Build and Push Docker Image / build (push) Successful in 11m2s
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:
@@ -12,6 +12,9 @@ import {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
@@ -315,26 +318,6 @@ export function AdminSidebar({ user }: AdminSidebarProps) {
|
||||
)}
|
||||
</nav>
|
||||
|
||||
{/* Role Switcher — visible above user section */}
|
||||
{switchableRoles.length > 0 && (
|
||||
<div className="border-t px-3 py-2">
|
||||
<p className="mb-1.5 flex items-center gap-1.5 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground/60">
|
||||
<ArrowRightLeft className="h-3 w-3" />
|
||||
Switch View
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{switchableRoles.map(([, opt]) => (
|
||||
<Link key={opt.path} href={opt.path as Route} onClick={() => setIsMobileMenuOpen(false)}>
|
||||
<Button size="sm" variant="outline" className="h-7 gap-1.5 px-2.5 text-xs">
|
||||
<opt.icon className="h-3 w-3" />
|
||||
{opt.label}
|
||||
</Button>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* User Profile Section */}
|
||||
<div className="border-t p-3">
|
||||
<DropdownMenu>
|
||||
@@ -393,23 +376,41 @@ export function AdminSidebar({ user }: AdminSidebarProps) {
|
||||
{switchableRoles.length > 0 && (
|
||||
<>
|
||||
<DropdownMenuSeparator className="my-1" />
|
||||
<div className="px-2 py-1.5">
|
||||
<p className="flex items-center gap-1.5 text-[11px] font-medium uppercase tracking-wider text-muted-foreground/60">
|
||||
<ArrowRightLeft className="h-3 w-3" />
|
||||
Switch View
|
||||
</p>
|
||||
</div>
|
||||
{switchableRoles.map(([, opt]) => (
|
||||
<DropdownMenuItem key={opt.path} asChild>
|
||||
<Link
|
||||
href={opt.path as Route}
|
||||
className="flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-2"
|
||||
>
|
||||
<opt.icon className="h-4 w-4 text-muted-foreground" />
|
||||
<span>{opt.label}</span>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
{switchableRoles.length <= 2 ? (
|
||||
// Flat list for 1-2 roles
|
||||
switchableRoles.map(([, opt]) => (
|
||||
<DropdownMenuItem key={opt.path} asChild>
|
||||
<Link
|
||||
href={opt.path as Route}
|
||||
className="flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-2"
|
||||
>
|
||||
<opt.icon className="h-4 w-4 text-muted-foreground" />
|
||||
<span>{opt.label}</span>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
))
|
||||
) : (
|
||||
// Submenu for 3+ roles
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger className="flex items-center gap-2.5 rounded-md px-2 py-2">
|
||||
<ArrowRightLeft className="h-4 w-4 text-muted-foreground" />
|
||||
<span>Switch View</span>
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuSubContent className="min-w-[160px]">
|
||||
{switchableRoles.map(([, opt]) => (
|
||||
<DropdownMenuItem key={opt.path} asChild>
|
||||
<Link
|
||||
href={opt.path as Route}
|
||||
className="flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-2"
|
||||
>
|
||||
<opt.icon className="h-4 w-4 text-muted-foreground" />
|
||||
<span>{opt.label}</span>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuSub>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user