From 98ccde12b08ad0b7f53e42de959a72d8382179e3 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Sat, 14 Mar 2026 00:38:07 +0000 Subject: feat: show subtask rollup on BLOCKED tasks waiting for subtasks When a task is BLOCKED due to spawned subtasks (no question), the card footer now fetches and renders a list of subtask names with their state emoji instead of showing the question/answer input UI. The Cancel button remains in both cases. Co-Authored-By: Claude Sonnet 4.6 --- web/app.js | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) (limited to 'web/app.js') diff --git a/web/app.js b/web/app.js index ad60f34..6bcdf57 100644 --- a/web/app.js +++ b/web/app.js @@ -163,7 +163,11 @@ function createTaskCard(task) { footer.appendChild(acceptBtn); footer.appendChild(rejectBtn); } else if (task.state === 'BLOCKED') { - renderQuestionFooter(task, footer); + if (task.question) { + renderQuestionFooter(task, footer); + } else { + renderSubtaskRollup(task.id, footer); + } const cancelBtn = document.createElement('button'); cancelBtn.className = 'btn-cancel'; cancelBtn.textContent = 'Cancel'; @@ -728,6 +732,39 @@ function renderQuestionFooter(task, footer) { } } +const STATE_EMOJI = { + PENDING: '⏳', QUEUED: '🕐', RUNNING: '⚡', COMPLETED: '✅', + FAILED: '❌', CANCELLED: '🚫', TIMED_OUT: '⏱', BUDGET_EXCEEDED: '💸', + READY: '👀', BLOCKED: '⏸', +}; + +async function renderSubtaskRollup(taskId, footer) { + footer.addEventListener('click', (e) => e.stopPropagation()); + const container = document.createElement('div'); + container.className = 'subtask-rollup'; + footer.prepend(container); + + try { + const res = await fetch(`${API_BASE}/api/tasks/${taskId}/subtasks`); + const subtasks = await res.json(); + if (!subtasks || subtasks.length === 0) { + container.textContent = 'Waiting for subtasks…'; + return; + } + const ul = document.createElement('ul'); + ul.className = 'subtask-list'; + for (const st of subtasks) { + const li = document.createElement('li'); + li.className = `subtask-item subtask-${st.state.toLowerCase()}`; + li.textContent = `${STATE_EMOJI[st.state] || '•'} ${st.name}`; + ul.appendChild(li); + } + container.appendChild(ul); + } catch { + container.textContent = 'Could not load subtasks.'; + } +} + async function handleAnswer(taskId, answer, footer) { const btns = footer.querySelectorAll('button, input'); btns.forEach(el => { el.disabled = true; }); -- cgit v1.2.3