Files
fusion/packages/dashboard/app/hooks/useChangedFiles.ts
gsxdsm e21ea42d45 feat(KB-651): add changed files diff viewer for tasks
- Add backend API endpoint to get changed files and diffs for a task worktree
- Add useChangedFiles hook for fetching and managing diff data
- Create ChangedFilesModal component with file list and diff viewer
- Integrate modal into TaskCard with click handler to view changes
- Add frontend API integration and type definitions
- Include unit tests for hook and modal components
- Add changeset for the new feature
- Remove deprecated mission-related test files
2026-04-01 02:01:31 -07:00

67 lines
1.9 KiB
TypeScript

import { useEffect, useState } from "react";
import { fetchTaskFileDiffs, type TaskFileDiff } from "../api";
const ACTIVE_COLUMNS = new Set(["in-progress", "in-review"]);
interface UseChangedFilesResult {
files: TaskFileDiff[];
loading: boolean;
error: string | null;
selectedFile: TaskFileDiff | null;
setSelectedFile: (file: TaskFileDiff) => void;
}
export function useChangedFiles(taskId: string, worktree: string | undefined, column: string): UseChangedFilesResult {
const [files, setFiles] = useState<TaskFileDiff[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [selectedFile, setSelectedFile] = useState<TaskFileDiff | null>(null);
useEffect(() => {
if (!taskId || !worktree || !ACTIVE_COLUMNS.has(column)) {
setFiles([]);
setLoading(false);
setError(null);
setSelectedFile(null);
return;
}
let cancelled = false;
async function load() {
setLoading(true);
setError(null);
try {
const result = await fetchTaskFileDiffs(taskId);
if (cancelled) return;
setFiles(result);
setSelectedFile((current) => {
if (result.length === 0) return null;
if (current) {
const match = result.find((file) => file.path === current.path && file.oldPath === current.oldPath);
if (match) return match;
}
return result[0] ?? null;
});
} catch (err) {
if (cancelled) return;
setFiles([]);
setSelectedFile(null);
setError(err instanceof Error ? err.message : "Failed to load changed files");
} finally {
if (!cancelled) {
setLoading(false);
}
}
}
void load();
return () => {
cancelled = true;
};
}, [taskId, worktree, column]);
return { files, loading, error, selectedFile, setSelectedFile };
}