From 42bb6432d3e53042723b43330c22eafbf553e10f Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 25 May 2026 05:09:10 +0000 Subject: refactor(web): remove the create-task modal from the UI (Phase 3) The observability UI no longer creates tasks; chatbots do that via the chatbot MCP server. Removes the New Task button, the #task-modal (manual form + the now-defunct AI-elaborate textbox + validate panel), their handlers (elaborateTask/validateTask/buildValidatePayload/renderValidationResult/ openTaskModal/closeTaskModal/createTask) and the orphaned newTaskButtonShouldShowOnTab helper plus its test. Verified with node --test (267 JS tests pass) and a JS syntax check. Not browser-verified in this environment. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39 --- web/app.js | 274 -------------------------------------- web/index.html | 45 ------- web/test/new-task-button.test.mjs | 24 ---- 3 files changed, 343 deletions(-) delete mode 100644 web/test/new-task-button.test.mjs (limited to 'web') diff --git a/web/app.js b/web/app.js index 09b545b..0a66a11 100644 --- a/web/app.js +++ b/web/app.js @@ -465,9 +465,6 @@ export function filterActiveTasks(tasks) { return tasks.filter(t => _PANEL_ACTIVE_STATES.has(t.state)); } -// The New Task button is always visible regardless of active tab. -export function newTaskButtonShouldShowOnTab(_tab) { return true; } - export function filterTasksByTab(tasks, tab) { if (tab === 'active') return tasks.filter(t => ACTIVE_STATES.has(t.state)); if (tab === 'interrupted') return tasks.filter(t => INTERRUPTED_STATES.has(t.state)); @@ -1642,152 +1639,6 @@ async function submitAnswer(taskId, questionId, answer, banner) { // ── Elaborate (Draft with AI) ───────────────────────────────────────────────── -async function elaborateTask(prompt, workingDir) { - const res = await fetch(`${API_BASE}/api/tasks/elaborate`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ prompt, project_dir: workingDir }), - }); - if (!res.ok) { - let msg = `HTTP ${res.status}`; - try { const body = await res.json(); msg = body.error || msg; } catch {} - throw new Error(msg); - } - return res.json(); -} - -// ── Validate ────────────────────────────────────────────────────────────────── - -async function validateTask(payload) { - const res = await fetch(`${API_BASE}/api/tasks/validate`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(payload), - }); - if (!res.ok) { - let msg = res.statusText; - try { const body = await res.json(); msg = body.error || body.message || msg; } catch {} - throw new Error(msg); - } - return res.json(); -} - -function buildValidatePayload() { - const f = document.getElementById('task-form'); - const name = f.querySelector('[name="name"]').value; - const instructions = f.querySelector('[name="instructions"]').value; - const repository_url = document.getElementById('repository-url').value; - const container_image = document.getElementById('container-image').value; - const allowedToolsEl = f.querySelector('[name="allowed_tools"]'); - const allowed_tools = allowedToolsEl - ? allowedToolsEl.value.split(',').map(s => s.trim()).filter(Boolean) - : []; - return { name, repository_url, agent: { instructions, container_image, allowed_tools } }; -} - -function renderValidationResult(result) { - const container = document.getElementById('validate-result'); - container.removeAttribute('hidden'); - container.dataset.clarity = result.clarity; - - let icon; - if (result.ready === true) { - icon = '✓'; - } else if (result.clarity === 'ambiguous') { - icon = '⚠'; - } else { - icon = '✗'; - } - - container.innerHTML = ''; - - const header = document.createElement('div'); - header.className = 'validate-header'; - const iconSpan = document.createElement('span'); - iconSpan.className = 'validate-icon'; - iconSpan.textContent = icon; - const summarySpan = document.createElement('span'); - summarySpan.textContent = ' ' + (result.summary || ''); - header.append(iconSpan, summarySpan); - container.appendChild(header); - - if (result.questions && result.questions.length > 0) { - const ul = document.createElement('ul'); - ul.className = 'validate-questions'; - for (const q of result.questions) { - const li = document.createElement('li'); - li.className = q.severity === 'blocking' ? 'validate-blocking' : 'validate-minor'; - li.textContent = q.text; - ul.appendChild(li); - } - container.appendChild(ul); - } - - if (result.suggestions && result.suggestions.length > 0) { - const ul = document.createElement('ul'); - ul.className = 'validate-suggestions'; - for (const s of result.suggestions) { - const li = document.createElement('li'); - li.className = 'validate-suggestion'; - li.textContent = s; - ul.appendChild(li); - } - container.appendChild(ul); - } -} - -// ── Task modal ──────────────────────────────────────────────────────────────── - -async function openTaskModal() { - document.getElementById('task-modal').showModal(); -} - -function closeTaskModal() { - document.getElementById('task-modal').close(); - document.getElementById('task-form').reset(); - document.getElementById('elaborate-prompt').value = ''; - const validateResult = document.getElementById('validate-result'); - validateResult.setAttribute('hidden', ''); - validateResult.innerHTML = ''; - validateResult.removeAttribute('data-clarity'); -} - -async function createTask(formData) { - const repository_url = formData.get('repository_url'); - const container_image = formData.get('container_image'); - const elaboratePromptEl = document.getElementById('elaborate-prompt'); - const elaborationInput = elaboratePromptEl ? elaboratePromptEl.value.trim() : ''; - const body = { - name: formData.get('name'), - description: '', - elaboration_input: elaborationInput || undefined, - repository_url: repository_url, - agent: { - instructions: formData.get('instructions'), - container_image: container_image, - max_budget_usd: parseFloat(formData.get('max_budget_usd')), - type: 'container', - }, - timeout: formData.get('timeout'), - priority: formData.get('priority'), - tags: [], - }; - - const res = await fetch(`${API_BASE}/api/tasks`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(body), - }); - - if (!res.ok) { - const text = await res.text(); - throw new Error(text || `HTTP ${res.status}`); - } - - closeTaskModal(); - await poll(); -} - // ── Task side panel ─────────────────────────────────────────────────────────── // Format Go's task.Duration JSON value {"Duration": } to human string. @@ -3545,9 +3396,6 @@ if (typeof document !== 'undefined') { btn.addEventListener('click', () => switchTab(btn.dataset.tab)); }); - // Task modal - document.getElementById('btn-new-task').addEventListener('click', openTaskModal); - document.getElementById('btn-cancel-task').addEventListener('click', closeTaskModal); // Push notifications button const btnNotify = document.getElementById('btn-notifications'); @@ -3555,128 +3403,6 @@ if (typeof document !== 'undefined') { btnNotify.addEventListener('click', () => enableNotifications(btnNotify)); } - // Validate button - document.getElementById('btn-validate').addEventListener('click', async () => { - const btn = document.getElementById('btn-validate'); - const resultDiv = document.getElementById('validate-result'); - btn.disabled = true; - btn.textContent = 'Checking…'; - try { - const payload = buildValidatePayload(); - const result = await validateTask(payload); - renderValidationResult(result); - } catch (err) { - resultDiv.removeAttribute('hidden'); - resultDiv.textContent = 'Validation failed: ' + err.message; - } finally { - btn.disabled = false; - btn.textContent = 'Validate Instructions'; - } - }); - - // Draft with AI button - const btnElaborate = document.getElementById('btn-elaborate'); - btnElaborate.addEventListener('click', async () => { - const prompt = document.getElementById('elaborate-prompt').value.trim(); - if (!prompt) { - const form = document.getElementById('task-form'); - // Remove previous error - const prev = form.querySelector('.form-error'); - if (prev) prev.remove(); - const errEl = document.createElement('p'); - errEl.className = 'form-error'; - errEl.textContent = 'Please enter a description before drafting.'; - form.querySelector('.elaborate-section').appendChild(errEl); - return; - } - - btnElaborate.disabled = true; - btnElaborate.textContent = 'Drafting…'; - - // Remove any previous errors or banners - const form = document.getElementById('task-form'); - form.querySelectorAll('.form-error, .elaborate-banner').forEach(el => el.remove()); - - try { - const repoUrl = document.getElementById('repository-url').value.trim(); - const result = await elaborateTask(prompt, repoUrl); - - // Populate form fields - const f = document.getElementById('task-form'); - if (result.name) - f.querySelector('[name="name"]').value = result.name; - if (result.agent && result.agent.instructions) - f.querySelector('[name="instructions"]').value = result.agent.instructions; - if (result.repository_url || result.agent?.repository_url) { - document.getElementById('repository-url').value = result.repository_url || result.agent.repository_url; - } - if (result.agent && result.agent.container_image) { - document.getElementById('container-image').value = result.agent.container_image; - } - if (result.agent && result.agent.max_budget_usd != null) - f.querySelector('[name="max_budget_usd"]').value = result.agent.max_budget_usd; - if (result.timeout) - f.querySelector('[name="timeout"]').value = result.timeout; - if (result.priority) { - const sel = f.querySelector('[name="priority"]'); - if ([...sel.options].some(o => o.value === result.priority)) { - sel.value = result.priority; - } - } - - // Show success banner - const banner = document.createElement('p'); - banner.className = 'elaborate-banner'; - banner.textContent = 'AI draft ready — review and submit.'; - document.getElementById('task-form').querySelector('.elaborate-section').appendChild(banner); - - // Auto-validate after elaboration - try { - const result = await validateTask(buildValidatePayload()); - renderValidationResult(result); - } catch (_) { - // silent - elaboration already succeeded, validation is bonus - } - } catch (err) { - const errEl = document.createElement('p'); - errEl.className = 'form-error'; - errEl.textContent = `Elaboration failed: ${err.message}`; - document.getElementById('task-form').querySelector('.elaborate-section').appendChild(errEl); - } finally { - btnElaborate.disabled = false; - btnElaborate.textContent = 'Draft with AI ✦'; - } - }); - - document.getElementById('task-form').addEventListener('submit', async e => { - e.preventDefault(); - - // Remove any previous error - const prev = e.target.querySelector('.form-error'); - if (prev) prev.remove(); - - const btn = e.submitter; - btn.disabled = true; - btn.textContent = 'Creating…'; - - try { - const validateResult = document.getElementById('validate-result'); - if (!validateResult.hasAttribute('hidden') && validateResult.dataset.clarity && validateResult.dataset.clarity !== 'clear') { - if (!window.confirm('The validator flagged issues. Create task anyway?')) { - return; - } - } - await createTask(new FormData(e.target)); - } catch (err) { - const errEl = document.createElement('p'); - errEl.className = 'form-error'; - errEl.textContent = err.message; - e.target.appendChild(errEl); - } finally { - btn.disabled = false; - btn.textContent = 'Create & Queue'; - } - }); // Story modal const storyModal = document.getElementById('story-modal'); diff --git a/web/index.html b/web/index.html index 0632cd7..d3bf81b 100644 --- a/web/index.html +++ b/web/index.html @@ -36,7 +36,6 @@ -