diff options
| author | Claudomator Agent <agent@claudomator.local> | 2026-07-06 10:01:55 +0000 |
|---|---|---|
| committer | Claudomator Agent <agent@claudomator.local> | 2026-07-06 10:01:55 +0000 |
| commit | 7642af5cd7a512b1f6fab22db83ea5feb78b9502 (patch) | |
| tree | bd17f6b70b73ae05e9f314151da847d66e291fba /web | |
| parent | 2f83075719bbca0eb3682c61dbf4fe397894baff (diff) | |
feat(web): add renderTasksBoard and wire tasks tab into renderActiveTab (task 7)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'web')
| -rw-r--r-- | web/app.js | 60 |
1 files changed, 60 insertions, 0 deletions
@@ -1390,6 +1390,11 @@ function renderActiveTab(allTasks) { // drag ends will pick up any server-side change. if (!draggingStoryId) renderStoriesPanel(); break; + case 'tasks': { + const panel = document.querySelector('[data-panel="tasks"]'); + if (panel) renderTasksBoard(allTasks, panel); + break; + } case 'stats': Promise.all([ fetchRecentExecutions(BASE_PATH, fetch), @@ -3743,6 +3748,61 @@ function renderStoriesBoard(stories, container) { container.appendChild(board); } +// ── Tasks tab: unified board ───────────────────────────────────────────────── + +function renderTasksBoard(tasks, container) { + const groups = groupTasksByColumn(tasks); + let board = container.querySelector('.tasks-board'); + if (!board) { + board = document.createElement('div'); + board.className = 'tasks-board'; + container.appendChild(board); + } + + for (const col of TASK_COLUMNS) { + let colEl = board.querySelector(`[data-column-key="${col.key}"]`); + if (!colEl) { + colEl = document.createElement('div'); + colEl.className = 'tasks-column'; + colEl.dataset.columnKey = col.key; + + const header = document.createElement('div'); + header.className = 'tasks-column-header'; + const title = document.createElement('span'); + title.textContent = col.label; + const count = document.createElement('span'); + count.className = 'tasks-column-count'; + header.append(title, count); + colEl.appendChild(header); + + const list = document.createElement('div'); + list.className = 'tasks-column-list'; + colEl.appendChild(list); + + board.appendChild(colEl); + } + + const countEl = colEl.querySelector('.tasks-column-count'); + countEl.textContent = String(groups[col.key].length); + + const list = colEl.querySelector('.tasks-column-list'); + renderTasksIntoContainer(groups[col.key], list, `No tasks in ${col.label.toLowerCase()}.`); + } + + // Attach/refresh the inline log tail for every visible card that has one + // (createTaskCard omits it for PENDING/QUEUED — see Task 4). + board.querySelectorAll('.task-log-tail').forEach((logArea) => { + const taskId = logArea.dataset.logTarget; + if (taskId) ensureTaskLogStream(taskId, logArea); + }); + + // Elapsed timers update on every poll tick (same convention the old + // Running-tab card used — no separate ticking interval). + board.querySelectorAll('.task-elapsed[data-started-at]').forEach((el) => { + el.textContent = formatElapsed(el.dataset.startedAt || null); + }); +} + // ── Stories tab: epic swimlanes ─────────────────────────────────────────────── function renderSwimlane(lane) { |
