refactor: implement icon rail sidebar for Design System v4
72px icon rail with hover flyouts, pin-to-expand toggle (260px), keyboard accessible, mobile hamburger overlay. Flat TopBar styling (no blur/glass). New BrandLogo mark (gradient square + lightning bolt). BrandWordmark uses solid text color instead of gradient. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -35,7 +35,7 @@ export function TopBar() {
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside)
|
||||
}, [userMenuOpen])
|
||||
|
||||
// ⌘K / Ctrl+K global shortcut
|
||||
// Cmd+K / Ctrl+K global shortcut
|
||||
const handleGlobalKeyDown = useCallback((e: KeyboardEvent) => {
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
|
||||
e.preventDefault()
|
||||
@@ -55,12 +55,10 @@ export function TopBar() {
|
||||
return (
|
||||
<>
|
||||
<header
|
||||
className="topbar relative z-10 flex items-center gap-4 border-b px-4"
|
||||
className="topbar relative z-10 flex items-center gap-4 px-4"
|
||||
style={{
|
||||
background: 'rgba(16, 17, 20, 0.6)',
|
||||
backdropFilter: 'var(--glass-blur-strong)',
|
||||
WebkitBackdropFilter: 'var(--glass-blur-strong)',
|
||||
borderColor: 'var(--glass-border)',
|
||||
background: '#0f1118',
|
||||
borderBottom: '1px solid #1e2130',
|
||||
}}
|
||||
>
|
||||
{/* Logo area */}
|
||||
@@ -68,10 +66,9 @@ export function TopBar() {
|
||||
to="/"
|
||||
className="flex items-center gap-2.5 pr-4 transition-all duration-200"
|
||||
>
|
||||
<BrandLogo size="sm" className="h-7 w-7 shrink-0" />
|
||||
<span className="text-sm font-heading font-bold tracking-tight whitespace-nowrap">
|
||||
<span className="text-foreground">Resolution</span>
|
||||
<span className="text-gradient-brand">Flow</span>
|
||||
<BrandLogo size="sm" />
|
||||
<span className="text-sm font-heading font-bold tracking-tight whitespace-nowrap text-[#f0f2f5]">
|
||||
ResolutionFlow
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
@@ -84,17 +81,28 @@ export function TopBar() {
|
||||
className="hidden sm:relative sm:block w-full text-left"
|
||||
style={{ maxWidth: '480px' }}
|
||||
>
|
||||
<Search size={16} className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" />
|
||||
<div className="w-full rounded-lg border border-border bg-card py-2 pl-9 pr-14 text-[0.8125rem] text-muted-foreground cursor-pointer hover:border-primary/30 transition-colors">
|
||||
Search flows, sessions, tags…
|
||||
<Search size={16} className="absolute left-3 top-1/2 -translate-y-1/2 text-[#848b9b]" />
|
||||
<div
|
||||
className="w-full rounded-md py-2 pl-9 pr-14 text-[0.8125rem] text-[#848b9b] cursor-pointer transition-colors"
|
||||
style={{
|
||||
background: '#14161d',
|
||||
border: '1px solid #1e2130',
|
||||
}}
|
||||
onMouseEnter={(e) => { (e.currentTarget as HTMLElement).style.borderColor = '#2a2f3d' }}
|
||||
onMouseLeave={(e) => { (e.currentTarget as HTMLElement).style.borderColor = '#1e2130' }}
|
||||
>
|
||||
Search flows, sessions, tags...
|
||||
</div>
|
||||
<span className="absolute right-3 top-1/2 -translate-y-1/2 rounded border border-border bg-background px-1.5 py-0.5 font-label text-[0.625rem] text-muted-foreground">
|
||||
{navigator.platform?.toLowerCase().includes('mac') ? '⌘K' : 'Ctrl+K'}
|
||||
<span
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 rounded px-1.5 py-0.5 font-mono text-[0.625rem] text-[#4f5666]"
|
||||
style={{ background: '#0c0d10', border: '1px solid #1e2130' }}
|
||||
>
|
||||
{navigator.platform?.toLowerCase().includes('mac') ? '\u2318K' : 'Ctrl+K'}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setCommandPaletteOpen(true)}
|
||||
className="sm:hidden rounded-lg p-2 text-muted-foreground hover:bg-card hover:text-foreground transition-colors"
|
||||
className="sm:hidden rounded-lg p-2 text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
|
||||
title="Search"
|
||||
>
|
||||
<Search size={18} />
|
||||
@@ -107,14 +115,14 @@ export function TopBar() {
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={() => setQuickLaunchOpen(true)}
|
||||
className="rounded-lg p-2 text-muted-foreground hover:bg-card hover:text-foreground transition-colors"
|
||||
className="rounded-lg p-2 text-[#848b9b] hover:bg-[#14161d] hover:text-[#e2e5eb] transition-colors"
|
||||
title="Quick Launch"
|
||||
>
|
||||
<Zap size={18} />
|
||||
</button>
|
||||
<Link
|
||||
to="/guides"
|
||||
className="rounded-lg p-2 text-muted-foreground hover:bg-card hover:text-foreground transition-colors"
|
||||
className="rounded-lg p-2 text-[#848b9b] hover:bg-[#14161d] hover:text-[#e2e5eb] transition-colors"
|
||||
title="User Guides"
|
||||
>
|
||||
<HelpCircle size={18} />
|
||||
@@ -125,18 +133,22 @@ export function TopBar() {
|
||||
<div className="relative ml-2" ref={menuRef}>
|
||||
<button
|
||||
onClick={() => setUserMenuOpen(!userMenuOpen)}
|
||||
className="flex h-8 w-8 items-center justify-center rounded-[10px] bg-gradient-brand text-xs font-heading font-bold text-primary-foreground hover:opacity-90 transition-opacity"
|
||||
className="flex h-8 w-8 items-center justify-center rounded-[10px] text-xs font-heading font-bold text-white hover:opacity-90 transition-opacity"
|
||||
style={{ background: 'linear-gradient(135deg, #06b6d4, #22d3ee)' }}
|
||||
title={user?.name || user?.email || 'User'}
|
||||
>
|
||||
{initials}
|
||||
</button>
|
||||
|
||||
{userMenuOpen && (
|
||||
<div className="absolute right-0 z-50 mt-2 w-56 rounded-lg border border-border bg-card p-1 shadow-xl animate-scale-in">
|
||||
<div className="border-b border-border px-3 py-2.5 mb-1">
|
||||
<p className="text-sm font-medium text-foreground truncate">{user?.name || user?.email}</p>
|
||||
<div
|
||||
className="absolute right-0 z-50 mt-2 w-56 rounded-lg p-1 shadow-xl animate-scale-in"
|
||||
style={{ background: '#14161d', border: '1px solid #1e2130' }}
|
||||
>
|
||||
<div className="px-3 py-2.5 mb-1" style={{ borderBottom: '1px solid #1e2130' }}>
|
||||
<p className="text-sm font-medium text-[#e2e5eb] truncate">{user?.name || user?.email}</p>
|
||||
{effectiveRole && effectiveRole !== 'engineer' && (
|
||||
<span className="mt-1 inline-flex items-center gap-1 text-xs text-muted-foreground">
|
||||
<span className="mt-1 inline-flex items-center gap-1 text-xs text-[#848b9b]">
|
||||
<Shield size={10} />
|
||||
{effectiveRole === 'super_admin' ? 'Super Admin' : effectiveRole === 'owner' ? 'Owner' : 'Viewer'}
|
||||
</span>
|
||||
@@ -145,7 +157,7 @@ export function TopBar() {
|
||||
<Link
|
||||
to="/account"
|
||||
onClick={() => setUserMenuOpen(false)}
|
||||
className="flex items-center gap-2 rounded-md px-3 py-2 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
className="flex items-center gap-2 rounded-md px-3 py-2 text-sm text-[#848b9b] hover:bg-[#191c25] hover:text-[#e2e5eb]"
|
||||
>
|
||||
<Settings size={14} />
|
||||
Account
|
||||
@@ -154,18 +166,18 @@ export function TopBar() {
|
||||
<Link
|
||||
to="/admin"
|
||||
onClick={() => setUserMenuOpen(false)}
|
||||
className="flex items-center gap-2 rounded-md px-3 py-2 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
className="flex items-center gap-2 rounded-md px-3 py-2 text-sm text-[#848b9b] hover:bg-[#191c25] hover:text-[#e2e5eb]"
|
||||
>
|
||||
<Shield size={14} />
|
||||
Admin Panel
|
||||
</Link>
|
||||
)}
|
||||
<div className="border-t border-border mt-1 pt-1">
|
||||
<div className="mt-1 pt-1" style={{ borderTop: '1px solid #1e2130' }}>
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className={cn(
|
||||
'flex w-full items-center gap-2 rounded-md px-3 py-2 text-sm',
|
||||
'text-muted-foreground hover:bg-accent hover:text-foreground'
|
||||
'text-[#848b9b] hover:bg-[#191c25] hover:text-[#e2e5eb]'
|
||||
)}
|
||||
>
|
||||
<LogOut size={14} />
|
||||
|
||||
Reference in New Issue
Block a user