feat: semi-finalist tracker dashboard, account reminders, search + UX fixes
- Add getSemiFinalistStats query with per-category/per-award breakdown - Add sendAccountReminders mutation with invite token generation and dedup - Add SemiFinalistTracker dashboard widget with progress bars and remind buttons - Add ACCOUNT_REMINDER email template - Extend project search to match team member name/email (7 locations) - Fix Passed count deduplication: count distinct projects, not round-state rows - Fix role switcher: visible pills above user section, auto-refresh session on mount Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import Link from 'next/link'
|
||||
import type { Route } from 'next'
|
||||
import { usePathname } from 'next/navigation'
|
||||
@@ -164,13 +164,21 @@ const ROLE_SWITCH_OPTIONS: Record<string, { label: string; path: string; icon: t
|
||||
export function AdminSidebar({ user }: AdminSidebarProps) {
|
||||
const pathname = usePathname()
|
||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
|
||||
const { data: session, status: sessionStatus } = useSession()
|
||||
const { data: session, status: sessionStatus, update: updateSession } = useSession()
|
||||
const isAuthenticated = sessionStatus === 'authenticated'
|
||||
const { data: avatarUrl } = trpc.avatar.getUrl.useQuery(undefined, {
|
||||
enabled: isAuthenticated,
|
||||
})
|
||||
const { currentEdition } = useEdition()
|
||||
|
||||
// Auto-refresh session on mount to pick up role changes without requiring re-login
|
||||
useEffect(() => {
|
||||
if (isAuthenticated) {
|
||||
updateSession()
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isAuthenticated])
|
||||
|
||||
const isSuperAdmin = user.role === 'SUPER_ADMIN'
|
||||
const roleLabel = roleLabels[user.role || ''] || 'User'
|
||||
|
||||
@@ -307,6 +315,26 @@ 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>
|
||||
|
||||
Reference in New Issue
Block a user