// ======================================== // HistoryPage Component // ======================================== // CLI execution history page with filtering and bulk actions import * as React from 'react'; import { useIntl } from 'react-intl'; import { Terminal, SearchX, RefreshCw, Trash2, AlertTriangle, Search, X, } from 'lucide-react'; import { cn } from '@/lib/utils'; import { useHistory } from '@/hooks/useHistory'; import { ConversationCard } from '@/components/shared/ConversationCard'; import { CliStreamPanel } from '@/components/shared/CliStreamPanel'; import { NativeSessionPanel } from '@/components/shared/NativeSessionPanel'; import { Button } from '@/components/ui/Button'; import { Input } from '@/components/ui/Input'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, } from '@/components/ui/Dialog'; import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuLabel, } from '@/components/ui/Dropdown'; import type { CliExecution } from '@/lib/api'; /** * HistoryPage component - Display CLI execution history */ export function HistoryPage() { const { formatMessage } = useIntl(); const [searchQuery, setSearchQuery] = React.useState(''); const [toolFilter, setToolFilter] = React.useState(undefined); const [deleteDialogOpen, setDeleteDialogOpen] = React.useState(false); const [deleteType, setDeleteType] = React.useState<'single' | 'tool' | 'all' | null>(null); const [deleteTarget, setDeleteTarget] = React.useState(null); const [selectedExecution, setSelectedExecution] = React.useState(null); const [isPanelOpen, setIsPanelOpen] = React.useState(false); const [nativeExecutionId, setNativeExecutionId] = React.useState(null); const [isNativePanelOpen, setIsNativePanelOpen] = React.useState(false); const { executions, isLoading, isFetching, error, refetch, deleteExecution, deleteByTool, deleteAll, isDeleting, } = useHistory({ filter: { search: searchQuery || undefined, tool: toolFilter }, }); const tools = React.useMemo(() => { const toolSet = new Set(executions.map((e) => e.tool)); return Array.from(toolSet).sort(); }, [executions]); // Filter handlers const handleClearSearch = () => { setSearchQuery(''); }; const handleClearFilters = () => { setSearchQuery(''); setToolFilter(undefined); }; const hasActiveFilters = searchQuery.length > 0 || toolFilter !== undefined; // Card click handler - open execution details panel const handleCardClick = (execution: CliExecution) => { setSelectedExecution(execution.id); setIsPanelOpen(true); }; // View native session handler const handleViewNative = (execution: CliExecution) => { setNativeExecutionId(execution.id); setIsNativePanelOpen(true); }; // Delete handlers const handleDeleteClick = (id: string) => { setDeleteType('single'); setDeleteTarget(id); setDeleteDialogOpen(true); }; const handleDeleteByTool = (tool: string) => { setDeleteType('tool'); setDeleteTarget(tool); setDeleteDialogOpen(true); }; const handleDeleteAll = () => { setDeleteType('all'); setDeleteTarget(null); setDeleteDialogOpen(true); }; const handleConfirmDelete = async () => { try { if (deleteType === 'single' && deleteTarget) { await deleteExecution(deleteTarget); } else if (deleteType === 'tool' && deleteTarget) { await deleteByTool(deleteTarget); } else if (deleteType === 'all') { await deleteAll(); } setDeleteDialogOpen(false); setDeleteType(null); setDeleteTarget(null); } catch (err) { console.error('Failed to delete:', err); } }; return (
{/* Header */}

{formatMessage({ id: 'history.title' })}

{formatMessage({ id: 'history.description' })}

{formatMessage({ id: 'history.deleteBy' })} {tools.map((tool) => ( handleDeleteByTool(tool)}> {formatMessage({ id: 'history.deleteAllTool' }, { tool })} ))} {formatMessage({ id: 'history.deleteAll' })}
{/* Error alert */} {error && (

{formatMessage({ id: 'common.errors.loadFailed' })}

{error.message}

)} {/* Filters */}
{/* Search input */}
setSearchQuery(e.target.value)} className="pl-9 pr-9" /> {searchQuery && ( )}
{/* Tool filter */} setToolFilter(undefined)}> {formatMessage({ id: 'history.filterAllTools' })} {tools.map((tool) => ( setToolFilter(tool)} className={toolFilter === tool ? 'bg-accent' : ''} > {tool} ))} {/* Clear filters */} {hasActiveFilters && ( )}
{/* Executions list */} {isLoading ? (
{Array.from({ length: 5 }).map((_, i) => (
))}
) : executions.length === 0 ? (

{hasActiveFilters ? formatMessage({ id: 'history.empty.filtered' }) : formatMessage({ id: 'history.empty.title' })}

{hasActiveFilters ? formatMessage({ id: 'history.empty.filteredMessage' }) : formatMessage({ id: 'history.empty.message' })}

{hasActiveFilters && ( )}
) : (
{executions.map((execution) => ( ))}
)} {/* CLI Stream Panel */} {/* Native Session Panel */} {/* Delete Confirmation Dialog */} {deleteType === 'all' ? formatMessage({ id: 'history.dialog.deleteAllTitle' }) : formatMessage({ id: 'history.dialog.deleteTitle' })} {deleteType === 'all' && formatMessage({ id: 'history.dialog.deleteAllMessage' })} {deleteType === 'tool' && formatMessage({ id: 'history.dialog.deleteToolMessage' }, { tool: deleteTarget })} {deleteType === 'single' && formatMessage({ id: 'history.dialog.deleteMessage' })}
); } export default HistoryPage;