feat(activity): search/filter on both Activity-tab panes
CI / lint (push) Successful in 4s
CI / frontend-build (push) Successful in 21s
CI / backend-lint-and-test (push) Successful in 39s
CI / integration (push) Successful in 3m17s

Recent failures gains a client-side search over the already-loaded 24h
rows (task/queue/target/error), shown as a filtered/total count alongside
the existing error-type chips. All recent activity gains a debounced
server-side task-name search (new `task` ILIKE param on /runs) so it
spans the full history, not just the loaded page. LIKE wildcards are
escaped so task names' literal underscores match literally.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-10 11:34:42 -04:00
parent 14c244bd3d
commit 70d4017cf6
4 changed files with 80 additions and 7 deletions
@@ -21,8 +21,15 @@
<v-card class="mb-4">
<CardHeading icon="mdi-alert-circle-outline" title="Recent failures (last 24h)">
<v-spacer />
<span class="text-caption fc-muted">
{{ failureCount }} failures
<v-text-field
v-model="failureSearch"
placeholder="Search task, queue, target, error…"
density="compact" hide-details clearable
prepend-inner-icon="mdi-magnify"
style="max-width: 280px;"
/>
<span class="text-caption fc-muted ml-3">
{{ filteredFailures.length }} / {{ failureCount }}
</span>
</CardHeading>
<v-card-text>
@@ -77,6 +84,14 @@
<v-card>
<CardHeading icon="mdi-format-list-bulleted" title="All recent activity">
<v-spacer />
<v-text-field
v-model="filterTask"
placeholder="Search task…"
density="compact" hide-details clearable
prepend-inner-icon="mdi-magnify"
style="max-width: 220px;"
@update:model-value="onTaskSearch"
/>
<v-select
v-model="filterQueue"
:items="queueOptions" density="compact" hide-details
@@ -170,6 +185,8 @@ const store = useSystemActivityStore()
const filterQueue = ref(null)
const filterStatus = ref(null)
const filterErrorType = ref(null)
const filterTask = ref(null) // server-side task-name search (All activity)
const failureSearch = ref('') // client-side search over loaded failures
const queueOptions = [
{ title: 'All queues', value: null },
@@ -225,9 +242,18 @@ const errorTypes = computed(() => {
const failureCount = computed(() => (store.failures?.recent || []).length)
const filteredFailures = computed(() => {
const all = store.failures?.recent || []
if (!filterErrorType.value) return all
return all.filter(r => r.error_type === filterErrorType.value)
let all = store.failures?.recent || []
if (filterErrorType.value) {
all = all.filter(r => r.error_type === filterErrorType.value)
}
const q = (failureSearch.value || '').trim().toLowerCase()
if (q) {
all = all.filter(r =>
[r.task_name, r.queue, r.target_id, r.error_type]
.some(v => String(v ?? '').toLowerCase().includes(q)),
)
}
return all
})
function toggleErrorTypeFilter(name) {
@@ -240,9 +266,23 @@ function toggleErrorTypeFilter(name) {
}
function onFilterChange() {
store.setFilter({ queue: filterQueue.value, status: filterStatus.value })
store.setFilter({
queue: filterQueue.value,
status: filterStatus.value,
task: (filterTask.value || '').trim() || null,
})
store.loadRuns({ reset: true })
}
// Server-side search hits the full task_run history, not just the loaded
// page — debounce so we don't fire a query per keystroke (matches the
// 300ms idiom in TagsView/AllowlistTable).
let taskSearchDebounce = null
function onTaskSearch() {
if (taskSearchDebounce) clearTimeout(taskSearchDebounce)
taskSearchDebounce = setTimeout(onFilterChange, 300)
}
function onLoadMore() { store.loadRuns({ reset: false }) }
function onRefresh() { store.loadRuns({ reset: true }) }