'use client';
import React from 'react';
import Link from 'next/link';
import { usePathname, useRouter } from 'next/navigation';
import { Bell, Bot, ChevronDown, FolderOpen, ListTodo, LogOut, Mail, Menu, MessageSquare, Puzzle, Settings, Store, Wrench, X } from 'lucide-react';
import { logout } from '@/lib/api';
import { LanguageSwitcher } from '@/components/LanguageSwitcher';
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
import { Button } from '@/components/ui/button';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { appConnectionStatusLabel } from '@/lib/i18n/common';
import { pickAppText } from '@/lib/i18n/core';
import { useAppI18n } from '@/lib/i18n/provider';
import { useChatStore } from '@/lib/store';
type NavItem = {
key: 'chat' | 'tasks' | 'notifications' | 'skills' | 'files' | 'tools' | 'agents' | 'outlook' | 'marketplace' | 'settings';
href: string;
icon: React.ComponentType<{ className?: string }>;
matchPrefixes?: string[];
};
const NAV_ITEMS: NavItem[] = [
{ key: 'chat', href: '/', icon: MessageSquare },
{ key: 'tasks', href: '/tasks', icon: ListTodo, matchPrefixes: ['/tasks', '/cron'] },
{ key: 'notifications', href: '/notifications', icon: Bell, matchPrefixes: ['/notifications'] },
{ key: 'skills', href: '/skills', icon: Puzzle },
{ key: 'files', href: '/files', icon: FolderOpen, matchPrefixes: ['/files'] },
{ key: 'tools', href: '/mcp', icon: Wrench, matchPrefixes: ['/mcp'] },
{ key: 'agents', href: '/agents', icon: Bot, matchPrefixes: ['/agents'] },
{ key: 'outlook', href: '/outlook', icon: Mail, matchPrefixes: ['/outlook'] },
{ key: 'marketplace', href: '/marketplace', icon: Store, matchPrefixes: ['/marketplace'] },
{
key: 'settings',
href: '/settings',
icon: Settings,
matchPrefixes: ['/settings', '/status', '/logs'],
},
];
function ConnectionDot() {
const { locale } = useAppI18n();
const wsStatus = useChatStore((s) => s.wsStatus);
const beaverReady = useChatStore((s) => s.beaverReady);
const isOnline = wsStatus === 'connected' && beaverReady === true;
const isChecking = wsStatus === 'connected' && beaverReady === null;
const isConnecting = wsStatus === 'connecting' || isChecking;
const isOffline = wsStatus === 'disconnected' || (wsStatus === 'connected' && beaverReady === false);
const color = isOnline
? 'bg-[#869683]'
: isConnecting
? 'bg-[#8B7E77]'
: 'bg-[#5F5550]';
const label = appConnectionStatusLabel(wsStatus, beaverReady, locale);
return (
{label}
);
}
const Header = () => {
const { locale } = useAppI18n();
const pathname = usePathname();
const router = useRouter();
const [mobileMenuOpen, setMobileMenuOpen] = React.useState(false);
const user = useChatStore((s) => s.user);
const isAuthLoading = useChatStore((s) => s.isAuthLoading);
const setUser = useChatStore((s) => s.setUser);
const navLabel = React.useCallback((key: NavItem['key']) => {
if (key === 'chat') return pickAppText(locale, '对话', 'Chat');
if (key === 'tasks') return 'Task';
if (key === 'notifications') return pickAppText(locale, '通知', 'Notifications');
if (key === 'skills') return pickAppText(locale, '技能', 'Skills');
if (key === 'files') return pickAppText(locale, '文件', 'Files');
if (key === 'tools') return pickAppText(locale, '工具', 'Tools');
if (key === 'agents') return pickAppText(locale, '智能体', 'Agents');
if (key === 'outlook') return 'Outlook';
if (key === 'marketplace') return pickAppText(locale, '市场', 'Marketplace');
return pickAppText(locale, '配置', 'Settings');
}, [locale]);
const handleLogout = async () => {
await logout();
setUser(null);
router.replace('/login');
router.refresh();
};
React.useEffect(() => {
setMobileMenuOpen(false);
}, [pathname]);
React.useEffect(() => {
if (!mobileMenuOpen) return;
const previousOverflow = document.body.style.overflow;
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
setMobileMenuOpen(false);
}
};
document.body.style.overflow = 'hidden';
document.addEventListener('keydown', handleKeyDown);
return () => {
document.body.style.overflow = previousOverflow;
document.removeEventListener('keydown', handleKeyDown);
};
}, [mobileMenuOpen]);
const userInitial = (user?.username || user?.email || '?').trim().charAt(0).toUpperCase();
const renderNavLinks = (compact = false) =>
NAV_ITEMS.map((item) => {
const isActive =
item.href === '/'
? pathname === '/'
: item.matchPrefixes?.some((prefix) => pathname.startsWith(prefix)) ?? pathname.startsWith(item.href);
const Icon = item.icon;
return (
setMobileMenuOpen(false) : undefined}
className={`flex h-11 shrink-0 items-center gap-2 rounded-full text-sm font-medium transition-colors ${
compact ? 'justify-start rounded-lg border border-transparent bg-background px-4' : 'px-4'
} ${
isActive
? 'bg-primary text-primary-foreground'
: compact
? 'text-[#4F4642] hover:border-[#E6E1DE] hover:bg-muted hover:text-[#0B0B0B]'
: 'text-[#4F4642] hover:bg-[#F7F5F4] hover:text-[#0B0B0B]'
}`}
>
{navLabel(item.key)}
);
});
return (
<>
Beaver
{user ? (
{userInitial}
{pickAppText(locale, `${user.username},你好!`, `Hi, ${user.username}`)}
{pickAppText(locale, '当前已登录到你的工作区实例。', 'You are currently signed in to your workspace instance.')}
) : !isAuthLoading ? null : null}
{mobileMenuOpen && (
<>