mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-28 09:23:08 +08:00
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:
@@ -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}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user