From 5c41afed85bff725e02171267d10305842fceeba Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Wed, 7 Jan 2026 23:06:43 -0500 Subject: [PATCH] Phase 8: Execution Search & Filtering Added comprehensive search and filtering for execution history: - Search bar to filter by command text, execution ID, or workflow name - Status filter dropdown (All, Running, Completed, Failed, Waiting) - Real-time client-side filtering as user types - Filter statistics showing X of Y executions - Clear Filters button to reset all filters - Extracts command text from logs for quick command searches - Maintains all executions in memory for instant filtering - Terminal-themed filter UI matching existing aesthetic Co-Authored-By: Claude Sonnet 4.5 --- public/index.html | 132 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 113 insertions(+), 19 deletions(-) diff --git a/public/index.html b/public/index.html index ec120e5..26f82a5 100644 --- a/public/index.html +++ b/public/index.html @@ -809,6 +809,37 @@

Execution History

+ + +
+
+ +
+ + +
+ + +
+ + +
+
+ +
+ + +
+
+
Loading...
@@ -920,6 +951,7 @@ let currentUser = null; let ws = null; let workers = []; + let allExecutions = []; // Store all loaded executions for filtering async function loadUser() { try { @@ -1080,6 +1112,13 @@ const data = await response.json(); const executions = data.executions || data; // Handle old and new API format + // Store executions for filtering + if (append) { + allExecutions = allExecutions.concat(executions); + } else { + allExecutions = executions; + } + // Dashboard view (always first 5) if (!append) { const dashHtml = executions.length === 0 ? @@ -1094,25 +1133,8 @@ document.getElementById('dashExecutions').innerHTML = dashHtml; } - // Full execution list - const fullHtml = executions.length === 0 ? - '
No executions yet
' : - executions.map(e => ` -
- ${e.status} - ${e.workflow_name || '[Quick Command]'} -
- Started by ${e.started_by} at ${new Date(e.started_at).toLocaleString()} - ${e.completed_at ? ` • Completed at ${new Date(e.completed_at).toLocaleString()}` : ''} -
-
- `).join(''); - - if (append) { - document.getElementById('executionList').innerHTML += fullHtml; - } else { - document.getElementById('executionList').innerHTML = fullHtml; - } + // Apply filters and render + renderFilteredExecutions(); // Add "Load More" button if there are more executions if (data.hasMore) { @@ -1125,6 +1147,78 @@ } } + function renderFilteredExecutions() { + const searchTerm = (document.getElementById('executionSearch')?.value || '').toLowerCase(); + const statusFilter = document.getElementById('statusFilter')?.value || ''; + + // Filter executions + let filtered = allExecutions.filter(e => { + // Status filter + if (statusFilter && e.status !== statusFilter) return false; + + // Search filter (search in workflow name, execution ID, and logs) + if (searchTerm) { + const workflowName = (e.workflow_name || '[Quick Command]').toLowerCase(); + const executionId = e.id.toLowerCase(); + + // Try to extract command from logs if it's a quick command + let commandText = ''; + try { + const logs = typeof e.logs === 'string' ? JSON.parse(e.logs) : e.logs; + if (logs && logs.length > 0 && logs[0].command) { + commandText = logs[0].command.toLowerCase(); + } + } catch (err) { + // Ignore parsing errors + } + + const matchFound = workflowName.includes(searchTerm) || + executionId.includes(searchTerm) || + commandText.includes(searchTerm); + + if (!matchFound) return false; + } + + return true; + }); + + // Update filter stats + const statsEl = document.getElementById('filterStats'); + if (statsEl) { + if (searchTerm || statusFilter) { + statsEl.textContent = `Showing ${filtered.length} of ${allExecutions.length} executions`; + } else { + statsEl.textContent = ''; + } + } + + // Render filtered results + const fullHtml = filtered.length === 0 ? + '
No executions match your filters
' : + filtered.map(e => ` +
+ ${e.status} + ${e.workflow_name || '[Quick Command]'} +
+ Started by ${e.started_by} at ${new Date(e.started_at).toLocaleString()} + ${e.completed_at ? ` • Completed at ${new Date(e.completed_at).toLocaleString()}` : ''} +
+
+ `).join(''); + + document.getElementById('executionList').innerHTML = fullHtml; + } + + function filterExecutions() { + renderFilteredExecutions(); + } + + function clearFilters() { + document.getElementById('executionSearch').value = ''; + document.getElementById('statusFilter').value = ''; + renderFilteredExecutions(); + } + async function loadMoreExecutions() { executionOffset += executionLimit; await loadExecutions(true);