WIP: Save current work before CHORUS rebrand
- Agent roles integration progress - Various backend and frontend updates - Storybook cache cleanup 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -12,10 +12,13 @@ import {
|
||||
ComputerDesktopIcon,
|
||||
UserCircleIcon,
|
||||
ChevronDownIcon,
|
||||
AdjustmentsHorizontalIcon
|
||||
AdjustmentsHorizontalIcon,
|
||||
ChatBubbleLeftRightIcon
|
||||
} from '@heroicons/react/24/outline';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import UserProfile from './auth/UserProfile';
|
||||
import { ThemeToggle } from './ThemeToggle';
|
||||
import HiveLogo from '../assets/Hive_symbol.png';
|
||||
|
||||
interface NavigationItem {
|
||||
name: string;
|
||||
@@ -31,6 +34,7 @@ const navigation: NavigationItem[] = [
|
||||
{ name: 'Cluster', href: '/cluster', icon: ComputerDesktopIcon },
|
||||
{ name: 'Executions', href: '/executions', icon: PlayIcon },
|
||||
{ name: 'Agents', href: '/agents', icon: UserGroupIcon },
|
||||
{ name: 'Bzzz Chat', href: '/bzzz-chat', icon: ChatBubbleLeftRightIcon },
|
||||
{ name: 'Analytics', href: '/analytics', icon: ChartBarIcon },
|
||||
{ name: 'Settings', href: '/settings', icon: AdjustmentsHorizontalIcon },
|
||||
];
|
||||
@@ -67,7 +71,7 @@ export default function Layout({ children }: LayoutProps) {
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex">
|
||||
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 flex">
|
||||
{/* Mobile sidebar overlay */}
|
||||
{sidebarOpen && (
|
||||
<div className="fixed inset-0 z-40 lg:hidden">
|
||||
@@ -75,15 +79,15 @@ export default function Layout({ children }: LayoutProps) {
|
||||
className="fixed inset-0 bg-gray-600 bg-opacity-75"
|
||||
onClick={() => setSidebarOpen(false)}
|
||||
/>
|
||||
<div className="fixed inset-y-0 left-0 flex flex-col w-64 bg-white shadow-xl">
|
||||
<div className="flex items-center justify-between p-4 border-b">
|
||||
<div className="fixed inset-y-0 left-0 flex flex-col w-64 bg-white dark:bg-gray-800 shadow-xl">
|
||||
<div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="flex items-center space-x-2">
|
||||
<span className="text-2xl">🐝</span>
|
||||
<span className="text-lg font-semibold text-gray-900">Hive</span>
|
||||
<img src={HiveLogo} alt="Hive" className="h-8 w-8 object-contain" />
|
||||
<span className="text-lg font-semibold text-gray-900 dark:text-white">Hive</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setSidebarOpen(false)}
|
||||
className="text-gray-400 hover:text-gray-600"
|
||||
className="text-gray-400 hover:text-gray-600 dark:text-gray-300 dark:hover:text-white"
|
||||
>
|
||||
<XMarkIcon className="h-6 w-6" />
|
||||
</button>
|
||||
@@ -96,13 +100,13 @@ export default function Layout({ children }: LayoutProps) {
|
||||
className={`
|
||||
group flex items-center px-2 py-2 text-sm font-medium rounded-md transition-colors
|
||||
${item.current
|
||||
? 'bg-blue-100 text-blue-900'
|
||||
: 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'
|
||||
? 'bg-blue-100 dark:bg-blue-900 text-blue-900 dark:text-blue-100'
|
||||
: 'text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-white'
|
||||
}
|
||||
`}
|
||||
onClick={() => setSidebarOpen(false)}
|
||||
>
|
||||
<item.icon className={`mr-3 h-5 w-5 ${item.current ? 'text-blue-500' : 'text-gray-400'}`} />
|
||||
<item.icon className={`mr-3 h-5 w-5 ${item.current ? 'text-blue-500' : 'text-gray-400 dark:text-gray-500'}`} />
|
||||
{item.name}
|
||||
</Link>
|
||||
))}
|
||||
@@ -113,10 +117,10 @@ export default function Layout({ children }: LayoutProps) {
|
||||
|
||||
{/* Desktop sidebar */}
|
||||
<div className="hidden lg:flex lg:flex-shrink-0">
|
||||
<div className="flex flex-col w-64 bg-white border-r border-gray-200">
|
||||
<div className="flex items-center px-6 py-4 border-b">
|
||||
<span className="text-2xl mr-2">🐝</span>
|
||||
<span className="text-xl font-semibold text-gray-900">Hive</span>
|
||||
<div className="flex flex-col w-64 bg-white dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700">
|
||||
<div className="flex items-center px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<img src={HiveLogo} alt="Hive" className="h-8 w-8 object-contain mr-2" />
|
||||
<span className="text-xl font-semibold text-gray-900 dark:text-white">Hive</span>
|
||||
</div>
|
||||
<nav className="flex-1 px-4 py-4 space-y-1">
|
||||
{navigationWithCurrent.map((item) => (
|
||||
@@ -126,20 +130,20 @@ export default function Layout({ children }: LayoutProps) {
|
||||
className={`
|
||||
group flex items-center px-2 py-2 text-sm font-medium rounded-md transition-colors
|
||||
${item.current
|
||||
? 'bg-blue-100 text-blue-900'
|
||||
: 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'
|
||||
? 'bg-blue-100 dark:bg-blue-900 text-blue-900 dark:text-blue-100'
|
||||
: 'text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-white'
|
||||
}
|
||||
`}
|
||||
>
|
||||
<item.icon className={`mr-3 h-5 w-5 ${item.current ? 'text-blue-500' : 'text-gray-400'}`} />
|
||||
<item.icon className={`mr-3 h-5 w-5 ${item.current ? 'text-blue-500' : 'text-gray-400 dark:text-gray-500'}`} />
|
||||
{item.name}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{/* Status indicator */}
|
||||
<div className="border-t p-4">
|
||||
<div className="flex items-center space-x-2 text-sm text-gray-500">
|
||||
<div className="border-t border-gray-200 dark:border-gray-700 p-4">
|
||||
<div className="flex items-center space-x-2 text-sm text-gray-500 dark:text-gray-400">
|
||||
<div className="w-2 h-2 bg-green-400 rounded-full"></div>
|
||||
<span>All systems operational</span>
|
||||
</div>
|
||||
@@ -150,41 +154,44 @@ export default function Layout({ children }: LayoutProps) {
|
||||
{/* Main content */}
|
||||
<div className="flex-1 flex flex-col">
|
||||
{/* Header */}
|
||||
<div className="bg-white border-b border-gray-200 px-4 py-2">
|
||||
<div className="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 px-4 py-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-2">
|
||||
<button
|
||||
onClick={() => setSidebarOpen(true)}
|
||||
className="lg:hidden text-gray-400 hover:text-gray-600"
|
||||
className="lg:hidden text-gray-400 hover:text-gray-600 dark:text-gray-300 dark:hover:text-white"
|
||||
>
|
||||
<Bars3Icon className="h-6 w-6" />
|
||||
</button>
|
||||
<div className="lg:hidden flex items-center space-x-2">
|
||||
<span className="text-2xl">🐝</span>
|
||||
<span className="text-lg font-semibold text-gray-900">Hive</span>
|
||||
<span className="text-lg font-semibold text-gray-900 dark:text-white">Hive</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* User menu */}
|
||||
<div className="relative" ref={userMenuRef}>
|
||||
<button
|
||||
onClick={() => setUserMenuOpen(!userMenuOpen)}
|
||||
className="flex items-center space-x-2 text-sm text-gray-700 hover:text-gray-900 focus:outline-none"
|
||||
>
|
||||
<UserCircleIcon className="h-8 w-8 text-gray-400" />
|
||||
<span className="hidden sm:block">{user?.name || user?.full_name || user?.username}</span>
|
||||
<ChevronDownIcon className="h-4 w-4" />
|
||||
</button>
|
||||
{/* Theme toggle and User menu */}
|
||||
<div className="flex items-center space-x-3">
|
||||
<ThemeToggle />
|
||||
<div className="relative" ref={userMenuRef}>
|
||||
<button
|
||||
onClick={() => setUserMenuOpen(!userMenuOpen)}
|
||||
className="flex items-center space-x-2 text-sm text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white focus:outline-none"
|
||||
>
|
||||
<UserCircleIcon className="h-8 w-8 text-gray-400 dark:text-gray-500" />
|
||||
<span className="hidden sm:block">{user?.name || user?.full_name || user?.username}</span>
|
||||
<ChevronDownIcon className="h-4 w-4" />
|
||||
</button>
|
||||
|
||||
{/* User dropdown */}
|
||||
{userMenuOpen && (
|
||||
<div className="absolute right-0 mt-2 z-50">
|
||||
<UserProfile
|
||||
isDropdown={true}
|
||||
onClose={() => setUserMenuOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{/* User dropdown */}
|
||||
{userMenuOpen && (
|
||||
<div className="absolute right-0 mt-2 z-50">
|
||||
<UserProfile
|
||||
isDropdown={true}
|
||||
onClose={() => setUserMenuOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
32
frontend/src/components/ThemeToggle.tsx
Normal file
32
frontend/src/components/ThemeToggle.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import { SunIcon, MoonIcon } from '@heroicons/react/24/outline';
|
||||
import { useTheme } from '../contexts/ThemeContext';
|
||||
|
||||
interface ThemeToggleProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const ThemeToggle: React.FC<ThemeToggleProps> = ({ className = '' }) => {
|
||||
const { isDarkMode, toggleDarkMode } = useTheme();
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={toggleDarkMode}
|
||||
className={`
|
||||
inline-flex items-center justify-center p-2 rounded-md
|
||||
text-gray-600 hover:text-gray-900 hover:bg-gray-100
|
||||
dark:text-gray-300 dark:hover:text-white dark:hover:bg-gray-700
|
||||
transition-colors duration-200
|
||||
${className}
|
||||
`}
|
||||
aria-label={isDarkMode ? 'Switch to light mode' : 'Switch to dark mode'}
|
||||
title={isDarkMode ? 'Switch to light mode' : 'Switch to dark mode'}
|
||||
>
|
||||
{isDarkMode ? (
|
||||
<SunIcon className="h-5 w-5" />
|
||||
) : (
|
||||
<MoonIcon className="h-5 w-5" />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user