summaryrefslogtreecommitdiff
path: root/web/app.js
diff options
context:
space:
mode:
Diffstat (limited to 'web/app.js')
-rw-r--r--web/app.js83
1 files changed, 36 insertions, 47 deletions
diff --git a/web/app.js b/web/app.js
index 894aaec..fc6864c 100644
--- a/web/app.js
+++ b/web/app.js
@@ -583,7 +583,7 @@ export function setTaskFilterTab(tab) {
}
export function getActiveMainTab() {
- return localStorage.getItem('activeMainTab') ?? 'queue';
+ return localStorage.getItem('activeMainTab') ?? 'stories';
}
export function setActiveMainTab(tab) {
@@ -1310,7 +1310,7 @@ let lastHistoryFetch = 0;
function getActiveTab() {
const active = document.querySelector('.tab.active');
- return active ? active.dataset.tab : 'queue';
+ return active ? active.dataset.tab : 'stories';
}
function getRefreshInterval() {
@@ -1327,26 +1327,11 @@ async function fetchHealth() {
function renderActiveTab(allTasks) {
const activeTab = getActiveTab();
switch (activeTab) {
- case 'queue':
- renderQueuePanel(allTasks);
- break;
- case 'interrupted':
- renderInterruptedPanel(allTasks);
- break;
- case 'ready':
- renderReadyPanel(allTasks);
- break;
- case 'running':
- renderRunningView(allTasks);
- if (Date.now() - lastHistoryFetch > 60_000) {
- lastHistoryFetch = Date.now();
- fetchRecentExecutions(BASE_PATH, fetch)
- .then(execs => renderRunningHistory(execs))
- .catch(() => {
- const histEl = document.querySelector('.running-history');
- if (histEl) histEl.innerHTML = '<p class="task-meta">Could not load execution history.</p>';
- });
- }
+ case 'stories':
+ // Guard against yanking the board out from under an in-progress
+ // HTML5 drag (see draggingStoryId) — the next poll tick after the
+ // drag ends will pick up any server-side change.
+ if (!draggingStoryId) renderStoriesPanel();
break;
case 'stats':
Promise.all([
@@ -1357,21 +1342,9 @@ function renderActiveTab(allTasks) {
.then(([execs, agentData, dashStats]) => renderStatsPanel(allTasks, execs, agentData, dashStats))
.catch(() => {});
break;
- case 'stories':
- // Guard against yanking the board out from under an in-progress
- // HTML5 drag (see draggingStoryId) — the next poll tick after the
- // drag ends will pick up any server-side change.
- if (!draggingStoryId) renderStoriesPanel();
- break;
case 'drops':
renderDropsPanel();
break;
- case 'budget':
- renderBudgetPanel();
- break;
- case 'roles':
- renderRolesPanel();
- break;
case 'settings':
renderSettingsPanel();
break;
@@ -1444,28 +1417,30 @@ if (typeof window !== 'undefined') {
-function renderSettingsPanel() {
+async function renderSettingsPanel() {
const panel = document.querySelector('[data-panel="settings"]');
if (!panel) return;
panel.innerHTML = '';
+
+ // ── General settings ──
const section = document.createElement('div');
section.className = 'stats-section';
section.style.padding = '1rem';
const heading = document.createElement('h2');
- heading.textContent = 'User Settings';
+ heading.textContent = 'Settings';
section.appendChild(heading);
const refreshLabel = document.createElement('label');
refreshLabel.style.display = 'block';
refreshLabel.style.marginBottom = '0.5rem';
refreshLabel.textContent = 'Auto-Refresh Interval';
-
+
const refreshSelect = document.createElement('select');
refreshSelect.className = 'agent-selector';
refreshSelect.style.width = '100%';
-
+
const options = [
{ label: '5 seconds', value: '5000' },
{ label: '10 seconds (default)', value: '10000' },
@@ -1485,12 +1460,22 @@ function renderSettingsPanel() {
refreshSelect.addEventListener('change', () => {
localStorage.setItem('refreshInterval', refreshSelect.value);
- startPolling(); // restart with new interval
+ startPolling();
});
refreshLabel.appendChild(refreshSelect);
section.appendChild(refreshLabel);
panel.appendChild(section);
+
+ // ── Role Configuration ──
+ const rolesSection = document.createElement('div');
+ rolesSection.className = 'stats-section';
+ rolesSection.style.padding = '1rem';
+ const rolesHeading = document.createElement('h2');
+ rolesHeading.textContent = 'Role Configuration';
+ rolesSection.appendChild(rolesHeading);
+ panel.appendChild(rolesSection);
+ await renderRolesPanel(rolesSection);
}
// ── WebSocket (real-time events) ──────────────────────────────────────────────
@@ -4627,7 +4612,7 @@ async function handleActivateRoleVersionClick(roleName, version, btn) {
const body = await res.json().catch(() => ({}));
throw new Error(body.error || `HTTP ${res.status}`);
}
- await renderRolesPanel();
+ await renderSettingsPanel();
} catch (err) {
btn.disabled = false;
btn.textContent = original;
@@ -4705,15 +4690,17 @@ function renderRoleCard(roleName, versions, doc = document) {
return card;
}
-async function renderRolesPanel() {
- const panel = document.querySelector('[data-panel="roles"]');
- if (!panel) return;
+async function renderRolesPanel(container = document.querySelector('[data-panel="roles"]')) {
+ if (!container) return;
try {
const namesRes = await fetch(`${BASE_PATH}/api/roles`);
const names = namesRes.ok ? await namesRes.json() : [];
if (!names || names.length === 0) {
- panel.innerHTML = '<div class="task-empty">No role configs yet.</div>';
+ const empty = document.createElement('div');
+ empty.className = 'task-empty';
+ empty.textContent = 'No role configs yet.';
+ container.appendChild(empty);
return;
}
@@ -4721,12 +4708,14 @@ async function renderRolesPanel() {
fetch(`${BASE_PATH}/api/roles/${encodeURIComponent(name)}/versions`).then(r => r.ok ? r.json() : []),
));
- panel.innerHTML = '';
names.forEach((name, i) => {
- panel.appendChild(renderRoleCard(name, versionsByRole[i] || []));
+ container.appendChild(renderRoleCard(name, versionsByRole[i] || []));
});
} catch (err) {
- panel.innerHTML = `<div class="panel-fetch-error">Failed to load roles: ${err.message}</div>`;
+ const errEl = document.createElement('div');
+ errEl.className = 'panel-fetch-error';
+ errEl.textContent = `Failed to load roles: ${err.message}`;
+ container.appendChild(errEl);
}
}