diff options
Diffstat (limited to 'web/app.js')
| -rw-r--r-- | web/app.js | 383 |
1 files changed, 0 insertions, 383 deletions
@@ -611,90 +611,6 @@ export function computeExecutionStats(executions) { }; } -// ── Stories ─────────────────────────────────────────────────────────────────── - -const STORY_STATUS_LABELS = { - PENDING: 'Pending', - IN_PROGRESS: 'In Progress', - SHIPPABLE: 'Shippable', - DEPLOYED: 'Deployed', - VALIDATING: 'Validating', - REVIEW_READY: 'Review Ready', - NEEDS_FIX: 'Needs Fix', -}; - -export function storyStatusLabel(status) { - return STORY_STATUS_LABELS[status] || status; -} - -export function renderStoryCard(story, doc = document) { - const card = doc.createElement('div'); - card.className = 'story-card'; - card.dataset.storyId = story.id; - - const header = doc.createElement('div'); - header.className = 'story-card-header'; - - const name = doc.createElement('span'); - name.className = 'story-name'; - name.textContent = story.name; - header.appendChild(name); - - const badge = doc.createElement('span'); - badge.className = 'story-status-badge'; - badge.dataset.status = story.status; - badge.textContent = storyStatusLabel(story.status); - header.appendChild(badge); - - card.appendChild(header); - - const meta = doc.createElement('div'); - meta.className = 'story-meta'; - - const project = doc.createElement('span'); - project.className = 'story-project'; - project.textContent = story.project_id || '—'; - meta.appendChild(project); - - if (story.branch_name) { - const branch = doc.createElement('span'); - branch.className = 'story-branch'; - branch.textContent = story.branch_name; - meta.appendChild(branch); - } - - card.appendChild(meta); - - // Ship button for SHIPPABLE stories. - if (story.status === 'SHIPPABLE') { - const shipBtn = doc.createElement('button'); - shipBtn.className = 'btn-primary story-ship-btn'; - shipBtn.textContent = 'Ship'; - shipBtn.addEventListener('click', async (e) => { - e.stopPropagation(); - shipBtn.disabled = true; - shipBtn.textContent = 'Shipping…'; - try { - const res = await fetch(`${API_BASE}/api/stories/${story.id}/ship`, { method: 'POST' }); - if (!res.ok) { - const body = await res.json().catch(() => ({})); - alert(body.error || `Ship failed (${res.status})`); - shipBtn.disabled = false; - shipBtn.textContent = 'Ship'; - } else { - renderStoriesPanel(); - } - } catch { - shipBtn.disabled = false; - shipBtn.textContent = 'Ship'; - } - }); - card.appendChild(shipBtn); - } - - return card; -} - export function updateFilterTabs() { const current = getTaskFilterTab(); document.querySelectorAll('.filter-tab[data-filter]').forEach(el => { @@ -1370,9 +1286,6 @@ function renderActiveTab(allTasks) { .then(([execs, agentData, dashStats]) => renderStatsPanel(allTasks, execs, agentData, dashStats)) .catch(() => {}); break; - case 'stories': - renderStoriesPanel(); - break; case 'drops': renderDropsPanel(); break; @@ -3073,217 +2986,6 @@ async function fetchDrops() { // ── Stories panel ───────────────────────────────────────────────────────────── -async function renderStoriesPanel() { - const panel = document.querySelector('[data-panel="stories"]'); - if (!panel) return; - - let stories; - try { - const res = await fetch(`${BASE_PATH}/api/stories`); - stories = res.ok ? await res.json() : []; - } catch { - panel.innerHTML = '<p class="task-meta" style="padding:1rem">Failed to load stories.</p>'; - return; - } - - panel.innerHTML = ''; - - const toolbar = document.createElement('div'); - toolbar.className = 'stories-toolbar'; - const btnNew = document.createElement('button'); - btnNew.className = 'btn-primary'; - btnNew.textContent = 'New Story'; - btnNew.addEventListener('click', openStoryModal); - toolbar.appendChild(btnNew); - panel.appendChild(toolbar); - - if (!stories || stories.length === 0) { - const empty = document.createElement('p'); - empty.className = 'task-empty'; - empty.textContent = 'No stories yet. Create one to get started.'; - panel.appendChild(empty); - return; - } - - const list = document.createElement('div'); - list.className = 'stories-list'; - for (const story of stories) { - const card = renderStoryCard(story); - card.addEventListener('click', () => openStoryDetail(story)); - list.appendChild(card); - } - panel.appendChild(list); -} - -function openStoryDetail(story) { - const modal = document.getElementById('story-detail-modal'); - if (!modal) return; - - document.getElementById('story-detail-name').textContent = story.name; - - const body = document.getElementById('story-detail-body'); - body.innerHTML = ''; - - function addRow(label, value) { - const row = document.createElement('div'); - row.className = 'meta-item'; - const lbl = document.createElement('div'); - lbl.className = 'meta-label'; - lbl.textContent = label; - const val = document.createElement('div'); - val.className = 'meta-value'; - val.textContent = value || '—'; - row.appendChild(lbl); - row.appendChild(val); - body.appendChild(row); - } - - const badge = document.createElement('span'); - badge.className = 'story-status-badge'; - badge.dataset.status = story.status; - badge.textContent = storyStatusLabel(story.status); - - const statusRow = document.createElement('div'); - statusRow.className = 'meta-item'; - const statusLbl = document.createElement('div'); - statusLbl.className = 'meta-label'; - statusLbl.textContent = 'Status'; - statusRow.appendChild(statusLbl); - statusRow.appendChild(badge); - body.appendChild(statusRow); - - addRow('Project', story.project_id); - addRow('Branch', story.branch_name); - addRow('Created', story.created_at ? new Date(story.created_at).toLocaleString() : '—'); - - // Load tasks for this story. - const tasksSection = document.createElement('div'); - tasksSection.className = 'story-detail-tasks'; - tasksSection.innerHTML = '<p class="task-meta">Loading tasks…</p>'; - body.appendChild(tasksSection); - - fetch(`${API_BASE}/api/stories/${story.id}/tasks`) - .then(r => r.ok ? r.json() : []) - .then(async tasks => { - tasksSection.innerHTML = ''; - const topLevel = tasks.filter(t => !t.parent_task_id); - if (topLevel.length === 0) { - tasksSection.innerHTML = '<p class="task-meta">No tasks yet.</p>'; - return; - } - const ol = document.createElement('ol'); - ol.className = 'story-detail-task-list'; - for (const t of topLevel) { - const li = document.createElement('li'); - li.className = `story-detail-task story-detail-task-${t.state.toLowerCase()}`; - li.textContent = `${STATE_EMOJI[t.state] || '•'} ${t.name}`; - const subs = tasks.filter(s => s.parent_task_id === t.id); - if (subs.length > 0) { - const ul = document.createElement('ul'); - ul.className = 'story-detail-subtask-list'; - for (const s of subs) { - const sli = document.createElement('li'); - sli.className = `subtask-item subtask-${s.state.toLowerCase()}`; - sli.textContent = `${STATE_EMOJI[s.state] || '•'} ${s.name}`; - ul.appendChild(sli); - } - li.appendChild(ul); - } - ol.appendChild(li); - } - tasksSection.appendChild(ol); - }) - .catch(() => { tasksSection.innerHTML = '<p class="task-meta">Could not load tasks.</p>'; }); - - modal.showModal(); -} - -function openStoryModal() { - const modal = document.getElementById('story-modal'); - if (!modal) return; - - // Reset form state - document.getElementById('story-goal').value = ''; - const planArea = document.getElementById('story-plan-area'); - planArea.innerHTML = ''; - planArea.setAttribute('hidden', ''); - - const btnElaborate = document.getElementById('btn-story-elaborate'); - btnElaborate.disabled = false; - btnElaborate.textContent = 'Elaborate with AI ✦'; - - const btnApprove = document.getElementById('btn-story-approve'); - btnApprove.setAttribute('hidden', ''); - btnApprove._elaboratedPlan = null; - - // Populate project dropdown - fetch(`${BASE_PATH}/api/projects`) - .then(r => r.ok ? r.json() : []) - .then(projects => { - const sel = document.getElementById('story-project'); - sel.innerHTML = ''; - for (const p of projects) { - const opt = document.createElement('option'); - opt.value = p.id; - opt.textContent = p.name; - sel.appendChild(opt); - } - }) - .catch(() => {}); - - modal.showModal(); -} - -function renderElaboratedPlan(plan) { - const planArea = document.getElementById('story-plan-area'); - planArea.innerHTML = ''; - planArea.removeAttribute('hidden'); - - const nameEl = document.createElement('p'); - nameEl.className = 'story-plan-name'; - nameEl.textContent = `Story: ${plan.name}`; - planArea.appendChild(nameEl); - - if (plan.branch_name) { - const branchEl = document.createElement('p'); - branchEl.className = 'story-plan-branch'; - branchEl.textContent = `Branch: ${plan.branch_name}`; - planArea.appendChild(branchEl); - } - - if (plan.tasks && plan.tasks.length > 0) { - const tasksHeader = document.createElement('p'); - tasksHeader.className = 'story-plan-section'; - tasksHeader.textContent = `Tasks (${plan.tasks.length}):`; - planArea.appendChild(tasksHeader); - - const taskList = document.createElement('ol'); - taskList.className = 'story-plan-tasks'; - for (const t of plan.tasks) { - const li = document.createElement('li'); - li.textContent = t.name; - if (t.subtasks && t.subtasks.length > 0) { - const subList = document.createElement('ul'); - for (const s of t.subtasks) { - const subLi = document.createElement('li'); - subLi.textContent = s.name; - subList.appendChild(subLi); - } - li.appendChild(subList); - } - taskList.appendChild(li); - } - planArea.appendChild(taskList); - } - - if (plan.validation && plan.validation.type) { - const valHeader = document.createElement('p'); - valHeader.className = 'story-plan-section'; - valHeader.textContent = `Validation: ${plan.validation.type}`; - planArea.appendChild(valHeader); - } -} - async function renderDropsPanel() { const panel = document.querySelector('[data-panel="drops"] .drops-panel'); if (!panel) return; @@ -3404,90 +3106,5 @@ if (typeof document !== 'undefined') { } - // Story modal - const storyModal = document.getElementById('story-modal'); - if (storyModal) { - document.getElementById('btn-close-story-modal').addEventListener('click', () => storyModal.close()); - - document.getElementById('btn-story-elaborate').addEventListener('click', async () => { - const btn = document.getElementById('btn-story-elaborate'); - const goal = document.getElementById('story-goal').value.trim(); - const projectId = document.getElementById('story-project').value; - - if (!goal) { - const errEl = document.createElement('p'); - errEl.className = 'form-error'; - errEl.textContent = 'Please enter a goal before elaborating.'; - storyModal.querySelector('.story-modal-body').appendChild(errEl); - return; - } - - storyModal.querySelectorAll('.form-error').forEach(el => el.remove()); - btn.disabled = true; - btn.textContent = 'Elaborating…'; - - try { - const res = await fetch(`${BASE_PATH}/api/stories/elaborate`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ goal, project_id: projectId }), - }); - if (!res.ok) { - const err = await res.json().catch(() => ({ error: res.statusText })); - throw new Error(err.error || res.statusText); - } - const plan = await res.json(); - renderElaboratedPlan(plan); - - const btnApprove = document.getElementById('btn-story-approve'); - btnApprove._elaboratedPlan = { ...plan, project_id: projectId }; - btnApprove.removeAttribute('hidden'); - } catch (err) { - const errEl = document.createElement('p'); - errEl.className = 'form-error'; - errEl.textContent = `Elaboration failed: ${err.message}`; - storyModal.querySelector('.story-modal-body').appendChild(errEl); - } finally { - btn.disabled = false; - btn.textContent = 'Elaborate with AI ✦'; - } - }); - - document.getElementById('btn-story-approve').addEventListener('click', async () => { - const btn = document.getElementById('btn-story-approve'); - const plan = btn._elaboratedPlan; - if (!plan) return; - - btn.disabled = true; - btn.textContent = 'Approving…'; - - try { - const res = await fetch(`${BASE_PATH}/api/stories/approve`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(plan), - }); - if (!res.ok) { - const err = await res.json().catch(() => ({ error: res.statusText })); - throw new Error(err.error || res.statusText); - } - storyModal.close(); - renderStoriesPanel(); - } catch (err) { - const errEl = document.createElement('p'); - errEl.className = 'form-error'; - errEl.textContent = `Approve failed: ${err.message}`; - storyModal.querySelector('.story-modal-body').appendChild(errEl); - btn.disabled = false; - btn.textContent = 'Approve & Queue'; - } - }); - } - - // Story detail modal - const storyDetailModal = document.getElementById('story-detail-modal'); - if (storyDetailModal) { - document.getElementById('btn-close-story-detail').addEventListener('click', () => storyDetailModal.close()); - } }); } |
