summaryrefslogtreecommitdiff
path: root/web/app.js
diff options
context:
space:
mode:
Diffstat (limited to 'web/app.js')
-rw-r--r--web/app.js383
1 files changed, 0 insertions, 383 deletions
diff --git a/web/app.js b/web/app.js
index 0a66a11..8a2662f 100644
--- a/web/app.js
+++ b/web/app.js
@@ -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());
- }
});
}