* chore: update Google Fonts to Bricolage Grotesque, IBM Plex Sans, JetBrains Mono Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: update Tailwind config to Slate & Ice theme colors and fonts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: update CSS variables and glass-card utilities for Slate & Ice theme - Replace all color variables with Slate & Ice palette - Add glass system vars (--glass-bg, --glass-blur, --shadow-float) - Replace legacy glass-card with new variable-driven glass classes - Add breatheGlow, bellWobble, slideDown, fadeInRight keyframes - Update font references to IBM Plex Sans and Bricolage Grotesque Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: recolor BrandLogo to cyan gradient, split BrandWordmark for gradient Flow text Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: update TopBar with glassmorphism backdrop and cyan accent styling Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: update Sidebar with glassmorphism backdrop Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add ambient atmosphere gradient orbs behind app shell Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: update QuickStats and SessionsPanel with glass-card styling Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add WeeklyCalendar, QuickActions, OpenSessions, RecentActivity dashboard components Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: redesign dashboard layout with calendar, open sessions, and glass-card panels New layout: greeting → calendar+actions → sessions+stats → activity Replaces old QuickStats and SessionsPanel with new dashboard components Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: replace remaining purple hex references with ice-cyan accent Sweep of hardcoded purple hex values (#818cf8, #6366f1) replaced with new cyan accent (#06b6d4) in QuickActions, RecentActivity, QuickLaunch, and SVG brand assets. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update CLAUDE.md branding and design system for Slate & Ice Modern Updated Last Updated date, branding section (fonts, colors, glass utilities, atmosphere orbs), component styling rules, and Design System section to reflect the new ice-cyan glassmorphism theme. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: add Slate & Ice Modern design doc and implementation plan Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: redesign login page with Slate & Ice Modern design system Apply glassmorphism styling, atmosphere orbs, branded wordmark, and consistent design tokens to match the updated app shell aesthetic. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: raise TopBar z-index so profile dropdown renders above main content Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add AI assistant with in-session copilot and standalone chat with RAG Implements three-phase AI assistant feature: - Phase 0: RAG infrastructure with pgvector embeddings, Voyage AI integration, tree chunking service, and semantic search over team's flow library - Phase 1: In-session copilot panel during flow navigation with contextual AI help, current step awareness, and suggested related flows - Phase 2: Standalone AI chat page with persistent conversation history, pin/delete, and configurable retention policies (account-level) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add account management, email verification, AI fixes, and user guides - Profile settings, account transfer, delete/leave account flows - Email verification with JWT tokens and Resend integration - AI assistant/copilot fixes: markdown rendering, shared RAG helpers, token tracking, input refocus, model_validate usage - User guides hub + detail pages with 13 topic guides - Sidebar and top bar navigation for guides Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: prevent stale chunk errors after deployments - Set Cache-Control no-cache on index.html in nginx so browsers always fetch fresh chunk references after a deploy - Auto-reload on chunk load failures (stale deploy detection) with loop prevention via sessionStorage - Show friendly "App Updated" message if auto-reload doesn't resolve it Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add email verification toggle to admin settings Adds platform-level toggle to enable/disable email verification. When disabled, the verification banner is hidden and the send endpoint returns 403. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
180 lines
7.2 KiB
TypeScript
180 lines
7.2 KiB
TypeScript
import { useState, useRef, useEffect, useCallback } from 'react'
|
|
import { Link, useNavigate } from 'react-router-dom'
|
|
import { Search, Zap, LogOut, Shield, Settings, HelpCircle } from 'lucide-react'
|
|
import { useAuthStore } from '@/store/authStore'
|
|
import { usePermissions } from '@/hooks/usePermissions'
|
|
import { BrandLogo } from '@/components/common/BrandLogo'
|
|
import { CommandPalette } from './CommandPalette'
|
|
import { QuickLaunch } from './QuickLaunch'
|
|
import { NotificationsPanel } from './NotificationsPanel'
|
|
import { cn } from '@/lib/utils'
|
|
|
|
export function TopBar() {
|
|
const navigate = useNavigate()
|
|
const { user, logout } = useAuthStore()
|
|
const { effectiveRole, isSuperAdmin } = usePermissions()
|
|
|
|
const [userMenuOpen, setUserMenuOpen] = useState(false)
|
|
const [commandPaletteOpen, setCommandPaletteOpen] = useState(false)
|
|
const [quickLaunchOpen, setQuickLaunchOpen] = useState(false)
|
|
const menuRef = useRef<HTMLDivElement>(null)
|
|
|
|
const handleLogout = async () => {
|
|
setUserMenuOpen(false)
|
|
await logout()
|
|
navigate('/login')
|
|
}
|
|
|
|
useEffect(() => {
|
|
const handleClickOutside = (e: MouseEvent) => {
|
|
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
|
|
setUserMenuOpen(false)
|
|
}
|
|
}
|
|
if (userMenuOpen) document.addEventListener('mousedown', handleClickOutside)
|
|
return () => document.removeEventListener('mousedown', handleClickOutside)
|
|
}, [userMenuOpen])
|
|
|
|
// ⌘K / Ctrl+K global shortcut
|
|
const handleGlobalKeyDown = useCallback((e: KeyboardEvent) => {
|
|
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
|
|
e.preventDefault()
|
|
setCommandPaletteOpen(prev => !prev)
|
|
}
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
document.addEventListener('keydown', handleGlobalKeyDown)
|
|
return () => document.removeEventListener('keydown', handleGlobalKeyDown)
|
|
}, [handleGlobalKeyDown])
|
|
|
|
const initials = user?.name
|
|
? user.name.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2)
|
|
: user?.email?.[0]?.toUpperCase() || '?'
|
|
|
|
return (
|
|
<>
|
|
<header
|
|
className="topbar relative z-10 flex items-center gap-4 border-b px-4"
|
|
style={{
|
|
background: 'rgba(16, 17, 20, 0.6)',
|
|
backdropFilter: 'var(--glass-blur-strong)',
|
|
WebkitBackdropFilter: 'var(--glass-blur-strong)',
|
|
borderColor: 'var(--glass-border)',
|
|
}}
|
|
>
|
|
{/* Logo area */}
|
|
<Link
|
|
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>
|
|
</span>
|
|
</Link>
|
|
|
|
{/* Spacer - push search to center */}
|
|
<div className="flex-1" />
|
|
|
|
{/* Search trigger */}
|
|
<button
|
|
onClick={() => setCommandPaletteOpen(true)}
|
|
className="relative 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…
|
|
</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>
|
|
</button>
|
|
|
|
{/* Spacer - push actions to right */}
|
|
<div className="flex-1" />
|
|
|
|
{/* Action buttons */}
|
|
<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"
|
|
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"
|
|
title="User Guides"
|
|
>
|
|
<HelpCircle size={18} />
|
|
</Link>
|
|
<NotificationsPanel />
|
|
|
|
{/* User avatar & menu */}
|
|
<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"
|
|
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>
|
|
{effectiveRole && effectiveRole !== 'engineer' && (
|
|
<span className="mt-1 inline-flex items-center gap-1 text-xs text-muted-foreground">
|
|
<Shield size={10} />
|
|
{effectiveRole === 'super_admin' ? 'Super Admin' : effectiveRole === 'owner' ? 'Owner' : 'Viewer'}
|
|
</span>
|
|
)}
|
|
</div>
|
|
<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"
|
|
>
|
|
<Settings size={14} />
|
|
Account
|
|
</Link>
|
|
{isSuperAdmin && (
|
|
<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"
|
|
>
|
|
<Shield size={14} />
|
|
Admin Panel
|
|
</Link>
|
|
)}
|
|
<div className="border-t border-border mt-1 pt-1">
|
|
<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'
|
|
)}
|
|
>
|
|
<LogOut size={14} />
|
|
Logout
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
{/* Command Palette */}
|
|
<CommandPalette open={commandPaletteOpen} onClose={() => setCommandPaletteOpen(false)} />
|
|
<QuickLaunch open={quickLaunchOpen} onClose={() => setQuickLaunchOpen(false)} />
|
|
</>
|
|
)
|
|
}
|