From 8efc44394987472e92909a6233712e5f46cebcbb Mon Sep 17 00:00:00 2001 From: Michael Chihlas Date: Sun, 22 Mar 2026 00:26:19 -0400 Subject: [PATCH] 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) --- frontend/src/components/common/BrandLogo.tsx | 57 +- .../src/components/common/BrandWordmark.tsx | 5 +- frontend/src/components/layout/AppLayout.tsx | 70 +-- frontend/src/components/layout/Sidebar.tsx | 511 +++++++++++++----- frontend/src/components/layout/TopBar.tsx | 66 ++- frontend/src/index.css | 1 + frontend/src/store/userPreferencesStore.ts | 4 + 7 files changed, 477 insertions(+), 237 deletions(-) diff --git a/frontend/src/components/common/BrandLogo.tsx b/frontend/src/components/common/BrandLogo.tsx index 9b952c9b..83e287a1 100644 --- a/frontend/src/components/common/BrandLogo.tsx +++ b/frontend/src/components/common/BrandLogo.tsx @@ -5,37 +5,36 @@ interface BrandLogoProps { className?: string } +/** + * Brand logo mark: gradient cyan square with rounded corners + * containing a white lightning bolt. + */ export function BrandLogo({ size = 'sm', className }: BrandLogoProps) { - const sizeClasses = size === 'sm' ? 'h-8 w-8' : 'h-20 w-20' - const strokeBase = size === 'sm' ? 1 : 2 - const strokeThick = size === 'sm' ? 1.25 : 2.5 - const dashArray = size === 'sm' ? '1 1.5' : '2 3' - const nodeR = size === 'sm' ? { outer: 2.5, inner: 2.75 } : { outer: 5, inner: 5.5 } - const hubR = size === 'sm' ? { glow: 5, solid: 3.5 } : { glow: 10, solid: 7 } - const vb = size === 'sm' ? '0 0 40 40' : '0 0 80 80' - const s = size === 'sm' ? 1 : 2 - const gradId = size === 'sm' ? 'logoGradSm' : 'logoGradLg' - const gradEnd = String(40 * (size === 'sm' ? 1 : 2)) + const dim = size === 'sm' ? 30 : 64 return ( - - - - - - - - - - - - - - - - - - - +
+ + + +
) } diff --git a/frontend/src/components/common/BrandWordmark.tsx b/frontend/src/components/common/BrandWordmark.tsx index bd0e9318..a5eadf82 100644 --- a/frontend/src/components/common/BrandWordmark.tsx +++ b/frontend/src/components/common/BrandWordmark.tsx @@ -9,13 +9,12 @@ export function BrandWordmark({ size = 'sm', className }: BrandWordmarkProps) { return ( - Resolution - Flow + ResolutionFlow ) } diff --git a/frontend/src/components/layout/AppLayout.tsx b/frontend/src/components/layout/AppLayout.tsx index cc870ab0..d9ea115f 100644 --- a/frontend/src/components/layout/AppLayout.tsx +++ b/frontend/src/components/layout/AppLayout.tsx @@ -1,6 +1,6 @@ import { useEffect, useState, useCallback } from 'react' import { useLocation, useNavigate, Link } from 'react-router-dom' -import { Menu, X, LayoutGrid, Clock, Network, AlertTriangle, Code2, Wand2, BarChart3, Settings, LogOut, Shield, Library } from 'lucide-react' +import { Menu, X, LayoutGrid, Clock, AlertTriangle, GitBranch, Code2, Wand2, BarChart3, Settings, LogOut, Shield, Layers } from 'lucide-react' import { useAuthStore } from '@/store/authStore' import { usePermissions } from '@/hooks/usePermissions' import { useUserPreferencesStore } from '@/store/userPreferencesStore' @@ -16,7 +16,7 @@ export function AppLayout() { const navigate = useNavigate() const { user, logout } = useAuthStore() const { effectiveRole } = usePermissions() - const sidebarCollapsed = useUserPreferencesStore(s => s.sidebarCollapsed) + const sidebarPinned = useUserPreferencesStore(s => s.sidebarPinned) const [mobileMenuOpen, setMobileMenuOpen] = useState(false) // Close mobile menu on route change @@ -54,8 +54,8 @@ export function AppLayout() { { path: '/', label: 'Dashboard', icon: LayoutGrid }, { path: '/sessions', label: 'Active Sessions', icon: Clock }, { path: '/escalations', label: 'Escalations', icon: AlertTriangle }, - { path: '/trees', label: 'Flows', icon: Network }, - { path: '/step-library', label: 'Step Library', icon: Library }, + { path: '/trees', label: 'Flows', icon: GitBranch }, + { path: '/step-library', label: 'Step Library', icon: Layers }, { path: '/scripts', label: 'Scripts', icon: Code2 }, { path: '/script-builder', label: 'Script Builder', icon: Wand2 }, { path: '/analytics', label: 'Analytics', icon: BarChart3 }, @@ -64,34 +64,8 @@ export function AppLayout() { return ( <> - {/* Atmosphere orbs — ambient light behind glass */}
-
- -
{/* Top Bar - spans full width */} @@ -104,8 +78,9 @@ export function AppLayout() { {/* Mobile hamburger - overlaid on topbar */} +
+ + ) + } + + /* Icon Rail (default) */ return ( diff --git a/frontend/src/components/layout/TopBar.tsx b/frontend/src/components/layout/TopBar.tsx index bc31852f..c463bdb5 100644 --- a/frontend/src/components/layout/TopBar.tsx +++ b/frontend/src/components/layout/TopBar.tsx @@ -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 ( <>
{/* Logo area */} @@ -68,10 +66,9 @@ export function TopBar() { to="/" className="flex items-center gap-2.5 pr-4 transition-all duration-200" > - - - Resolution - Flow + + + ResolutionFlow @@ -84,17 +81,28 @@ export function TopBar() { className="hidden sm:relative sm:block w-full text-left" style={{ maxWidth: '480px' }} > - -
- Search flows, sessions, tags… + +
{ (e.currentTarget as HTMLElement).style.borderColor = '#2a2f3d' }} + onMouseLeave={(e) => { (e.currentTarget as HTMLElement).style.borderColor = '#1e2130' }} + > + Search flows, sessions, tags...
- - {navigator.platform?.toLowerCase().includes('mac') ? '⌘K' : 'Ctrl+K'} + + {navigator.platform?.toLowerCase().includes('mac') ? '\u2318K' : 'Ctrl+K'} @@ -125,18 +133,22 @@ export function TopBar() {
{userMenuOpen && ( -
-
-

{user?.name || user?.email}

+
+
+

{user?.name || user?.email}

{effectiveRole && effectiveRole !== 'engineer' && ( - + {effectiveRole === 'super_admin' ? 'Super Admin' : effectiveRole === 'owner' ? 'Owner' : 'Viewer'} @@ -145,7 +157,7 @@ export function TopBar() { 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]" > Account @@ -154,18 +166,18 @@ export function TopBar() { 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]" > Admin Panel )} -
+