Files
kandev/apps/web/app/task/[id]/page-client.tsx
T
2026-01-16 01:59:00 +00:00

140 lines
4.8 KiB
TypeScript

'use client';
import { useCallback, useEffect, useMemo, useState } from 'react';
import '@git-diff-view/react/styles/diff-view.css';
import { TooltipProvider } from '@kandev/ui/tooltip';
import { TaskTopBar } from '@/components/task/task-top-bar';
import { TaskLayout } from '@/components/task/task-layout';
import { DebugOverlay } from '@/components/debug-overlay';
import type { Task } from '@/lib/types/http';
import { DEBUG_UI } from '@/lib/config';
import { getWebSocketClient } from '@/lib/ws/connection';
import { useRepositories } from '@/hooks/use-repositories';
import { useTaskAgent } from '@/hooks/use-task-agent';
import { useTaskMessages } from '@/hooks/use-task-messages';
import { useAppStore } from '@/components/state-provider';
type TaskPageClientProps = {
task: Task | null;
sessionId?: string | null;
};
export default function TaskPage({ task: initialTask, sessionId = null }: TaskPageClientProps) {
const [isMounted, setIsMounted] = useState(false);
const kanbanTask = useAppStore((state) =>
initialTask?.id ? state.kanban.tasks.find((item) => item.id === initialTask.id) ?? null : null
);
const task = useMemo(() => {
if (!initialTask) return null;
if (!kanbanTask) return initialTask;
return {
...initialTask,
title: kanbanTask.title ?? initialTask.title,
description: kanbanTask.description ?? initialTask.description,
column_id: (kanbanTask.columnId as string | undefined) ?? initialTask.column_id,
position: kanbanTask.position ?? initialTask.position,
state: (kanbanTask.state as Task['state'] | undefined) ?? initialTask.state,
repositories: initialTask.repositories,
};
}, [initialTask, kanbanTask]);
const connectionStatus = useAppStore((state) => state.connection.status);
// Custom hooks for state management
const {
isAgentRunning,
isAgentLoading,
worktreePath,
worktreeBranch,
taskSessionId,
taskSessionState,
handleStartAgent,
handleStopAgent,
} = useTaskAgent(task);
const activeSessionId = sessionId ?? taskSessionId;
const isAgentWorking =
taskSessionState !== null
? taskSessionState === 'STARTING' || taskSessionState === 'RUNNING'
: isAgentRunning && (task?.state === 'IN_PROGRESS' || task?.state === 'SCHEDULING');
const { isLoading: isLoadingMessages } = useTaskMessages(task?.id ?? null, activeSessionId);
useEffect(() => {
queueMicrotask(() => setIsMounted(true));
}, []);
const { repositories } = useRepositories(task?.workspace_id ?? null, Boolean(task?.workspace_id));
const repository = useMemo(
() => repositories.find((item) => item.id === task?.repositories?.[0]?.repository_id) ?? null,
[repositories, task?.repositories]
);
const handleSendMessage = useCallback(async (content: string) => {
if (!task?.id) return;
const client = getWebSocketClient();
if (!client) return;
if (!activeSessionId) {
console.error('No active agent session. Start an agent before sending a message.');
return;
}
try {
await client.request(
'message.add',
{ task_id: task.id, task_session_id: activeSessionId, content },
10000
);
} catch (error) {
console.error('Failed to send message:', error);
}
}, [activeSessionId, task]);
if (!isMounted) {
return <div className="h-screen w-full bg-background" />;
}
return (
<TooltipProvider>
<div className="h-screen w-full flex flex-col bg-background">
{DEBUG_UI && (
<DebugOverlay
title="Task Debug"
entries={{
ws_status: connectionStatus,
task_id: task?.id ?? null,
task_session_id: activeSessionId ?? null,
task_state: task?.state ?? null,
task_session_state: taskSessionState ?? null,
is_agent_working: isAgentWorking,
}}
/>
)}
<TaskTopBar
taskId={task?.id ?? null}
activeSessionId={activeSessionId ?? null}
taskTitle={task?.title}
taskDescription={task?.description}
baseBranch={task?.repositories?.[0]?.base_branch ?? undefined}
onStartAgent={handleStartAgent}
onStopAgent={handleStopAgent}
isAgentRunning={isAgentRunning}
isAgentLoading={isAgentLoading}
worktreePath={worktreePath}
worktreeBranch={worktreeBranch}
repositoryPath={repository?.local_path ?? null}
repositoryName={repository?.name ?? null}
/>
<TaskLayout
taskId={task?.id ?? null}
taskDescription={task?.description}
isLoadingMessages={isLoadingMessages}
isAgentWorking={isAgentWorking}
onSendMessage={handleSendMessage}
/>
</div>
</TooltipProvider>
);
}