feat: add tests and implementation for issue discovery and queue pages

- Implemented `DiscoveryPage` with session management and findings display.
- Added tests for `DiscoveryPage` to ensure proper rendering and functionality.
- Created `QueuePage` for managing issue execution queues with stats and actions.
- Added tests for `QueuePage` to verify UI elements and translations.
- Introduced `useIssues` hooks for fetching and managing issue data.
- Added loading skeletons and error handling for better user experience.
- Created `vite-env.d.ts` for TypeScript support in Vite environment.
This commit is contained in:
catlog22
2026-01-31 21:20:10 +08:00
parent 6d225948d1
commit 1bd082a725
79 changed files with 5870 additions and 449 deletions

View File

@@ -4,6 +4,7 @@
// Root layout component combining Header, Sidebar, and MainContent
import { useState, useCallback, useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { cn } from '@/lib/utils';
import { Header } from './Header';
import { Sidebar } from './Sidebar';
@@ -12,13 +13,12 @@ import { CliStreamMonitor } from '@/components/shared/CliStreamMonitor';
import { NotificationPanel } from '@/components/notification';
import { AskQuestionDialog } from '@/components/a2ui/AskQuestionDialog';
import { useNotificationStore, selectCurrentQuestion } from '@/stores';
import { useWorkflowStore } from '@/stores/workflowStore';
import { useWebSocketNotifications } from '@/hooks';
export interface AppShellProps {
/** Initial sidebar collapsed state */
defaultCollapsed?: boolean;
/** Current project path to display in header */
projectPath?: string;
/** Callback for refresh action */
onRefresh?: () => void;
/** Whether refresh is in progress */
@@ -32,11 +32,32 @@ const SIDEBAR_COLLAPSED_KEY = 'ccw-sidebar-collapsed';
export function AppShell({
defaultCollapsed = false,
projectPath = '',
onRefresh,
isRefreshing = false,
children,
}: AppShellProps) {
// Workspace initialization from URL query parameter
const switchWorkspace = useWorkflowStore((state) => state.switchWorkspace);
const projectPath = useWorkflowStore((state) => state.projectPath);
const location = useLocation();
// Initialize workspace from URL path parameter on mount
useEffect(() => {
// Only initialize if no workspace is currently set
if (projectPath) return;
// Read path from URL query parameter
const searchParams = new URLSearchParams(location.search);
const pathParam = searchParams.get('path');
if (pathParam) {
console.log('[AppShell] Initializing workspace from URL:', pathParam);
switchWorkspace(pathParam).catch((error) => {
console.error('[AppShell] Failed to initialize workspace:', error);
});
}
}, [location.search, projectPath, switchWorkspace]);
// Sidebar collapse state (persisted)
const [sidebarCollapsed, setSidebarCollapsed] = useState(() => {
if (typeof window !== 'undefined') {
@@ -120,7 +141,6 @@ export function AppShell({
{/* Header - fixed at top */}
<Header
onMenuClick={handleMenuClick}
projectPath={projectPath}
onRefresh={onRefresh}
isRefreshing={isRefreshing}
onCliMonitorClick={handleCliMonitorClick}

View File

@@ -22,7 +22,6 @@ import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/Button';
import { Badge } from '@/components/ui/Badge';
import { useTheme } from '@/hooks';
import { LanguageSwitcher } from './LanguageSwitcher';
import { WorkspaceSelector } from '@/components/workspace/WorkspaceSelector';
import { useCliStreamStore, selectActiveExecutionCount } from '@/stores/cliStreamStore';
import { useNotificationStore } from '@/stores';
@@ -30,8 +29,6 @@ import { useNotificationStore } from '@/stores';
export interface HeaderProps {
/** Callback to toggle mobile sidebar */
onMenuClick?: () => void;
/** Current project path */
projectPath?: string;
/** Callback for refresh action */
onRefresh?: () => void;
/** Whether refresh is in progress */
@@ -42,7 +39,6 @@ export interface HeaderProps {
export function Header({
onMenuClick,
projectPath = '',
onRefresh,
isRefreshing = false,
onCliMonitorClick,
@@ -112,7 +108,7 @@ export function Header({
</Button>
{/* Workspace selector */}
{projectPath && <WorkspaceSelector />}
<WorkspaceSelector />
{/* Notification badge */}
<Button
@@ -147,9 +143,6 @@ export function Header({
</Button>
)}
{/* Language switcher */}
<LanguageSwitcher compact />
{/* Theme toggle */}
<Button
variant="ghost"

View File

@@ -12,6 +12,8 @@ import {
Workflow,
RefreshCw,
AlertCircle,
ListTodo,
Search,
Sparkles,
Terminal,
Brain,
@@ -25,8 +27,6 @@ import {
GitFork,
Shield,
History,
Folder,
Network,
} from 'lucide-react';
import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/Button';
@@ -60,13 +60,13 @@ const navItemDefinitions: Omit<NavItem, 'label'>[] = [
{ path: '/orchestrator', icon: Workflow },
{ path: '/loops', icon: RefreshCw },
{ path: '/issues', icon: AlertCircle },
{ path: '/issues/queue', icon: ListTodo },
{ path: '/issues/discovery', icon: Search },
{ path: '/skills', icon: Sparkles },
{ path: '/commands', icon: Terminal },
{ path: '/memory', icon: Brain },
{ path: '/prompts', icon: History },
{ path: '/hooks', icon: GitFork },
{ path: '/explorer', icon: Folder },
{ path: '/graph', icon: Network },
{ path: '/settings', icon: Settings },
{ path: '/settings/rules', icon: Shield },
{ path: '/help', icon: HelpCircle },
@@ -110,13 +110,13 @@ export function Sidebar({
'/orchestrator': 'main.orchestrator',
'/loops': 'main.loops',
'/issues': 'main.issues',
'/issues/queue': 'main.issueQueue',
'/issues/discovery': 'main.issueDiscovery',
'/skills': 'main.skills',
'/commands': 'main.commands',
'/memory': 'main.memory',
'/prompts': 'main.prompts',
'/hooks': 'main.hooks',
'/explorer': 'main.explorer',
'/graph': 'main.graph',
'/settings': 'main.settings',
'/settings/rules': 'main.rules',
'/help': 'main.help',