veritas-kanban/web/src/components/task/diff/FileTree.tsx
Brad Groux 39eccf3556 feat: Sprint US-1200 Refactoring batch — 13 tasks complete
Completed refactors:
- RF-02: Fix dependency vulnerabilities (xlsx → exceljs, Hono updates)
- RF-05: Add React error boundaries (FeatureErrorBoundary wrapper)
- RF-06: Server error handling middleware (AppError classes, asyncHandler)
- RF-10: Split shared types.ts into domain modules (6 files)
- RF-11: Consolidate frontend API layer (hooks now use api.ts)
- RF-13: TaskConfigContext — eliminate prop drilling
- RF-14: Split god components (GitSection, TaskDetailPanel, CreateTaskDialog, DiffViewer)
- RF-16: Frontend accessibility (ARIA labels, sr-only text)
- RF-17: Modularize CLI (899 → commands/ structure)
- RF-18: Modularize MCP (843 → tools/ structure)
- RF-19: Create shared API client library
- RF-21: Server performance (batch loading, memory limits, timeouts, graceful shutdown)
- RF-23: Extract shared utilities (path, format, constants)

Stats: ~59 files changed, significant code reduction through modularization
2026-01-28 06:08:59 -06:00

64 lines
2 KiB
TypeScript

import {
FileCode,
FilePlus,
FileMinus,
FileEdit,
MessageSquare,
} from 'lucide-react';
import type { FileChange } from '@/lib/api';
import type { ReviewComment } from '@veritas-kanban/shared';
import { cn } from '@/lib/utils';
const statusIcons: Record<FileChange['status'], React.ReactNode> = {
added: <FilePlus className="h-4 w-4 text-green-500" />,
modified: <FileEdit className="h-4 w-4 text-amber-500" />,
deleted: <FileMinus className="h-4 w-4 text-red-500" />,
renamed: <FileCode className="h-4 w-4 text-blue-500" />,
};
interface FileTreeProps {
files: FileChange[];
selectedFile: string | null;
onSelectFile: (path: string) => void;
comments: ReviewComment[];
}
export function FileTree({ files, selectedFile, onSelectFile, comments }: FileTreeProps) {
const commentsByFile = comments.reduce((acc, c) => {
acc[c.file] = (acc[c.file] || 0) + 1;
return acc;
}, {} as Record<string, number>);
return (
<div className="space-y-1">
{files.map((file) => (
<button
key={file.path}
onClick={() => onSelectFile(file.path)}
className={cn(
'w-full flex items-center gap-2 px-2 py-1.5 text-sm rounded-md text-left',
'hover:bg-muted transition-colors',
selectedFile === file.path && 'bg-muted'
)}
>
{statusIcons[file.status]}
<span className="truncate flex-1 font-mono text-xs">{file.path}</span>
<span className="flex items-center gap-1 text-xs">
{commentsByFile[file.path] && (
<span className="flex items-center gap-0.5 text-amber-500">
<MessageSquare className="h-3 w-3" />
{commentsByFile[file.path]}
</span>
)}
{file.additions > 0 && (
<span className="text-green-500">+{file.additions}</span>
)}
{file.deletions > 0 && (
<span className="text-red-500">-{file.deletions}</span>
)}
</span>
</button>
))}
</div>
);
}