diff --git a/dashboard/api.js b/dashboard/api.js index 886e71f..b2bf437 100644 --- a/dashboard/api.js +++ b/dashboard/api.js @@ -67,6 +67,7 @@ const api = { linkKanbanTasks: (parentId, childId) => api.post('/api/kanban/links', { parent_id: parentId, child_id: childId }), unlinkKanbanTasks: (parentId, childId) => api.del(`/api/kanban/links?parent_id=${encodeURIComponent(parentId)}&child_id=${encodeURIComponent(childId)}`), dispatchKanban: () => api.post('/api/kanban/dispatch', {}), + dispatchKanbanTask: (id) => api.post(`/api/kanban/tasks/${encodeURIComponent(id)}/dispatch`, {}), specifyKanbanTask: (id) => api.post(`/api/kanban/tasks/${encodeURIComponent(id)}/specify`, {}), decomposeKanbanTask: (id) => api.post(`/api/kanban/tasks/${encodeURIComponent(id)}/decompose`, {}), // Goals diff --git a/dashboard/pages/kanban.js b/dashboard/pages/kanban.js index fee2bb3..333e437 100644 --- a/dashboard/pages/kanban.js +++ b/dashboard/pages/kanban.js @@ -1,5 +1,9 @@ let kanbanData = null; +function getAllKanbanTasks() { + return Object.values(kanbanData?.columns || {}).flat(); +} + async function renderKanban() { const content = document.getElementById('pageContent'); content.innerHTML = ` @@ -53,7 +57,7 @@ function renderKanbanBoard() { if (!board || !kanbanData) return; const columnsObj = kanbanData.columns || {}; const columns = Object.keys(columnsObj); - const allTasks = kanbanData.tasks || Object.values(columnsObj).flat(); + const allTasks = getAllKanbanTasks(); const filterText = (document.getElementById('kanbanFilterInput')?.value || '').toLowerCase(); const filterPriority = document.getElementById('kanbanFilterPriority')?.value || 'all'; const filterCategory = document.getElementById('kanbanFilterCategory')?.value || 'all'; @@ -147,7 +151,12 @@ function showAddKanbanTask() {
- +
@@ -206,31 +215,68 @@ async function onKanbanDrop(e, status) { } } -function showKanbanDetail(id) { - const task = kanbanData?.tasks?.find(t => t.id === id); +const KANBAN_AGENTS = ['opencode', 'hermes', 'gemini']; + +function closeKanbanDetailModal() { + if (window._kanbanDetailPoll) { clearInterval(window._kanbanDetailPoll); window._kanbanDetailPoll = null; } + closeModal(); +} + +async function showKanbanDetail(id) { + const task = getAllKanbanTasks().find(t => t.id === id); if (!task) return; + renderKanbanDetailModal(task); + + if (window._kanbanDetailPoll) clearInterval(window._kanbanDetailPoll); + if (task.status === 'in_progress') { + window._kanbanDetailPoll = setInterval(async () => { + try { + const fresh = await api.getKanbanTask(id); + if (fresh.status !== 'in_progress') { + clearInterval(window._kanbanDetailPoll); + window._kanbanDetailPoll = null; + renderKanban(); + } + if (document.getElementById('kanbanDetailModal')) renderKanbanDetailModal(fresh); + } catch {} + }, 3000); + } +} + +function renderKanbanDetailModal(task) { const modal = document.getElementById('modalContainer'); + const canDispatch = KANBAN_AGENTS.includes(task.assignee) && task.status !== 'in_progress' && task.status !== 'done'; + const comments = task.comments || []; modal.innerHTML = ` -