feat(KB-048): add collapsible list sections to dashboard
- Add section expansion state management with localStorage persistence - Update section headers with chevron toggle controls - Implement conditional task row rendering based on section state - Add Expand All / Collapse All toolbar controls - Add CSS styles for chevron rotation animation and section headers - Add comprehensive tests for collapsible section behavior
This commit is contained in:
@@ -37,7 +37,7 @@ export function createTask(input: TaskCreateInput): Promise<Task> {
|
||||
});
|
||||
}
|
||||
|
||||
export function updateTask(id: string, updates: { title?: string; description?: string; prompt?: string; dependencies?: string[] }): Promise<Task> {
|
||||
export function updateTask(id: string, updates: { title?: string; description?: string; prompt?: string; dependencies?: string[]; modelProvider?: string; modelId?: string; validatorModelProvider?: string; validatorModelId?: string }): Promise<Task> {
|
||||
return api<Task>(`/tasks/${id}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(updates),
|
||||
@@ -63,6 +63,10 @@ export function retryTask(id: string): Promise<Task> {
|
||||
return api<Task>(`/tasks/${id}/retry`, { method: "POST" });
|
||||
}
|
||||
|
||||
export function duplicateTask(id: string): Promise<Task> {
|
||||
return api<Task>(`/tasks/${id}/duplicate`, { method: "POST" });
|
||||
}
|
||||
|
||||
export function pauseTask(id: string): Promise<Task> {
|
||||
return api<Task>(`/tasks/${id}/pause`, { method: "POST" });
|
||||
}
|
||||
@@ -71,6 +75,14 @@ export function unpauseTask(id: string): Promise<Task> {
|
||||
return api<Task>(`/tasks/${id}/unpause`, { method: "POST" });
|
||||
}
|
||||
|
||||
export function approvePlan(id: string): Promise<Task> {
|
||||
return api<Task>(`/tasks/${id}/approve-plan`, { method: "POST" });
|
||||
}
|
||||
|
||||
export function rejectPlan(id: string): Promise<Task> {
|
||||
return api<Task>(`/tasks/${id}/reject-plan`, { method: "POST" });
|
||||
}
|
||||
|
||||
export function fetchConfig(): Promise<{ maxConcurrent: number }> {
|
||||
return api<{ maxConcurrent: number }>("/config");
|
||||
}
|
||||
@@ -106,6 +118,20 @@ export function fetchAgentLogs(taskId: string): Promise<AgentLogEntry[]> {
|
||||
return api<AgentLogEntry[]>(`/tasks/${taskId}/logs`);
|
||||
}
|
||||
|
||||
export function addSteeringComment(id: string, text: string): Promise<Task> {
|
||||
return api<Task>(`/tasks/${id}/steer`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ text }),
|
||||
});
|
||||
}
|
||||
|
||||
export function requestSpecRevision(id: string, feedback: string): Promise<Task> {
|
||||
return api<Task>(`/tasks/${id}/spec/revise`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ feedback }),
|
||||
});
|
||||
}
|
||||
|
||||
// --- Models API ---
|
||||
|
||||
/** Available AI model info returned by the models endpoint */
|
||||
@@ -151,3 +177,216 @@ export function logoutProvider(provider: string): Promise<{ success: boolean }>
|
||||
body: JSON.stringify({ provider }),
|
||||
});
|
||||
}
|
||||
|
||||
// --- GitHub Import API ---
|
||||
|
||||
/** GitHub issue returned by the fetch endpoint */
|
||||
export interface GitHubIssue {
|
||||
number: number;
|
||||
title: string;
|
||||
body: string | null;
|
||||
html_url: string;
|
||||
labels: Array<{ name: string }>;
|
||||
}
|
||||
|
||||
/** Fetch open GitHub issues from a repository */
|
||||
export function apiFetchGitHubIssues(
|
||||
owner: string,
|
||||
repo: string,
|
||||
limit?: number,
|
||||
labels?: string[]
|
||||
): Promise<GitHubIssue[]> {
|
||||
return api<GitHubIssue[]>("/github/issues/fetch", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ owner, repo, limit, labels }),
|
||||
});
|
||||
}
|
||||
|
||||
/** Import a specific GitHub issue as a kb task */
|
||||
export function apiImportGitHubIssue(owner: string, repo: string, issueNumber: number): Promise<Task> {
|
||||
return api<Task>("/github/issues/import", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ owner, repo, issueNumber }),
|
||||
});
|
||||
}
|
||||
|
||||
// --- Git Remote Detection API ---
|
||||
|
||||
/** Git remote info returned by the remotes endpoint */
|
||||
export interface GitRemote {
|
||||
name: string;
|
||||
owner: string;
|
||||
repo: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
/** Fetch GitHub remotes from the current git repository */
|
||||
export function fetchGitRemotes(): Promise<GitRemote[]> {
|
||||
return api<GitRemote[]>("/git/remotes");
|
||||
}
|
||||
|
||||
// --- PR Management API ---
|
||||
|
||||
/** PR info returned by PR endpoints */
|
||||
export interface PrInfo {
|
||||
url: string;
|
||||
number: number;
|
||||
status: "open" | "closed" | "merged";
|
||||
title: string;
|
||||
headBranch: string;
|
||||
baseBranch: string;
|
||||
commentCount: number;
|
||||
lastCommentAt?: string;
|
||||
lastCheckedAt?: string;
|
||||
}
|
||||
|
||||
/** Create a GitHub PR for a task */
|
||||
export function createPr(
|
||||
id: string,
|
||||
params: { title: string; body?: string; base?: string }
|
||||
): Promise<PrInfo> {
|
||||
return api<PrInfo>(`/tasks/${id}/pr/create`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(params),
|
||||
});
|
||||
}
|
||||
|
||||
/** Fetch cached PR status for a task */
|
||||
export function fetchPrStatus(id: string): Promise<{ prInfo: PrInfo; stale: boolean }> {
|
||||
return api<{ prInfo: PrInfo; stale: boolean }>(`/tasks/${id}/pr/status`);
|
||||
}
|
||||
|
||||
/** Force refresh PR status from GitHub */
|
||||
export function refreshPrStatus(id: string): Promise<PrInfo> {
|
||||
return api<PrInfo>(`/tasks/${id}/pr/refresh`, {
|
||||
method: "POST",
|
||||
});
|
||||
}
|
||||
|
||||
// --- Git Management API ---
|
||||
|
||||
/** Current git status */
|
||||
export interface GitStatus {
|
||||
branch: string;
|
||||
commit: string;
|
||||
isDirty: boolean;
|
||||
ahead: number;
|
||||
behind: number;
|
||||
}
|
||||
|
||||
/** Git commit info */
|
||||
export interface GitCommit {
|
||||
hash: string;
|
||||
shortHash: string;
|
||||
message: string;
|
||||
author: string;
|
||||
date: string;
|
||||
parents: string[];
|
||||
}
|
||||
|
||||
/** Git branch info */
|
||||
export interface GitBranch {
|
||||
name: string;
|
||||
isCurrent: boolean;
|
||||
remote?: string;
|
||||
lastCommitDate?: string;
|
||||
}
|
||||
|
||||
/** Git worktree info */
|
||||
export interface GitWorktree {
|
||||
path: string;
|
||||
branch?: string;
|
||||
isMain: boolean;
|
||||
isBare: boolean;
|
||||
taskId?: string;
|
||||
}
|
||||
|
||||
/** Result of a fetch operation */
|
||||
export interface GitFetchResult {
|
||||
fetched: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
/** Result of a pull operation */
|
||||
export interface GitPullResult {
|
||||
success: boolean;
|
||||
message: string;
|
||||
conflict?: boolean;
|
||||
}
|
||||
|
||||
/** Result of a push operation */
|
||||
export interface GitPushResult {
|
||||
success: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
/** Fetch current git status */
|
||||
export function fetchGitStatus(): Promise<GitStatus> {
|
||||
return api<GitStatus>("/git/status");
|
||||
}
|
||||
|
||||
/** Fetch recent commits */
|
||||
export function fetchGitCommits(limit?: number): Promise<GitCommit[]> {
|
||||
const query = limit ? `?limit=${limit}` : "";
|
||||
return api<GitCommit[]>(`/git/commits${query}`);
|
||||
}
|
||||
|
||||
/** Fetch diff for a specific commit */
|
||||
export function fetchCommitDiff(hash: string): Promise<{ stat: string; patch: string }> {
|
||||
return api<{ stat: string; patch: string }>(`/git/commits/${hash}/diff`);
|
||||
}
|
||||
|
||||
/** Fetch all local branches */
|
||||
export function fetchGitBranches(): Promise<GitBranch[]> {
|
||||
return api<GitBranch[]>("/git/branches");
|
||||
}
|
||||
|
||||
/** Fetch all worktrees */
|
||||
export function fetchGitWorktrees(): Promise<GitWorktree[]> {
|
||||
return api<GitWorktree[]>("/git/worktrees");
|
||||
}
|
||||
|
||||
/** Create a new branch */
|
||||
export function createBranch(name: string, base?: string): Promise<void> {
|
||||
return api<void>("/git/branches", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ name, base }),
|
||||
});
|
||||
}
|
||||
|
||||
/** Checkout an existing branch */
|
||||
export function checkoutBranch(name: string): Promise<void> {
|
||||
return api<void>(`/git/branches/${encodeURIComponent(name)}/checkout`, {
|
||||
method: "POST",
|
||||
});
|
||||
}
|
||||
|
||||
/** Delete a branch */
|
||||
export function deleteBranch(name: string, force?: boolean): Promise<void> {
|
||||
const query = force ? "?force=true" : "";
|
||||
return api<void>(`/git/branches/${encodeURIComponent(name)}${query}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
|
||||
/** Fetch from remote */
|
||||
export function fetchRemote(remote?: string): Promise<GitFetchResult> {
|
||||
return api<GitFetchResult>("/git/fetch", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ remote }),
|
||||
});
|
||||
}
|
||||
|
||||
/** Pull current branch */
|
||||
export function pullBranch(): Promise<GitPullResult> {
|
||||
return api<GitPullResult>("/git/pull", {
|
||||
method: "POST",
|
||||
});
|
||||
}
|
||||
|
||||
/** Push current branch */
|
||||
export function pushBranch(): Promise<GitPushResult> {
|
||||
return api<GitPushResult>("/git/push", {
|
||||
method: "POST",
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user