feat(ui): collapse Downloads filters into compact bar with popover
Replaces two stacked filter rows (4 fields + superseded switch) with a single "Filters" button that opens a popover containing all controls. Active filters surface as dismissable chips on the bar alongside a "Clear all" button, so what's currently applied stays visible without eating vertical space above the table. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -68,62 +68,120 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Filters -->
|
||||
<v-row class="mb-4">
|
||||
<v-col cols="12" md="3">
|
||||
<v-select
|
||||
v-model="filterStatus"
|
||||
:items="statusOptions"
|
||||
label="Status"
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
clearable
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="3">
|
||||
<v-select
|
||||
v-model="filterSourceId"
|
||||
:items="sourceOptions"
|
||||
label="Source"
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
clearable
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="3">
|
||||
<v-text-field
|
||||
v-model="filterFromDate"
|
||||
label="From Date"
|
||||
type="date"
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
clearable
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="3">
|
||||
<v-text-field
|
||||
v-model="filterToDate"
|
||||
label="To Date"
|
||||
type="date"
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
clearable
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<!-- Filters (compact bar with popover) -->
|
||||
<div class="d-flex align-center flex-wrap ga-2 mb-4">
|
||||
<v-menu :close-on-content-click="false" location="bottom start">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn v-bind="props" variant="outlined" size="small" prepend-icon="mdi-filter-variant">
|
||||
Filters
|
||||
<v-chip v-if="activeFilterCount" class="ml-2" size="x-small" color="primary">
|
||||
{{ activeFilterCount }}
|
||||
</v-chip>
|
||||
<v-icon end>mdi-chevron-down</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-card min-width="320" class="pa-4">
|
||||
<v-select
|
||||
v-model="filterStatus"
|
||||
:items="statusOptions"
|
||||
label="Status"
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
clearable
|
||||
class="mb-2"
|
||||
hide-details
|
||||
/>
|
||||
<v-select
|
||||
v-model="filterSourceId"
|
||||
:items="sourceOptions"
|
||||
label="Source"
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
clearable
|
||||
class="mb-2"
|
||||
hide-details
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="filterFromDate"
|
||||
label="From Date"
|
||||
type="date"
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
clearable
|
||||
class="mb-2"
|
||||
hide-details
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="filterToDate"
|
||||
label="To Date"
|
||||
type="date"
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
clearable
|
||||
class="mb-3"
|
||||
hide-details
|
||||
/>
|
||||
<v-switch
|
||||
v-model="filterExcludeSuperseded"
|
||||
label="Hide superseded failures"
|
||||
density="compact"
|
||||
hide-details
|
||||
color="primary"
|
||||
/>
|
||||
</v-card>
|
||||
</v-menu>
|
||||
|
||||
<!-- Superseded filter -->
|
||||
<v-row class="mb-4">
|
||||
<v-col cols="12">
|
||||
<v-switch
|
||||
v-model="filterExcludeSuperseded"
|
||||
label="Hide superseded failures"
|
||||
density="compact"
|
||||
hide-details
|
||||
color="primary"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<!-- Active filter chips -->
|
||||
<v-chip
|
||||
v-if="filterStatus"
|
||||
size="small"
|
||||
closable
|
||||
@click:close="filterStatus = null"
|
||||
>
|
||||
Status: {{ statusLabel(filterStatus) }}
|
||||
</v-chip>
|
||||
<v-chip
|
||||
v-if="filterSourceId"
|
||||
size="small"
|
||||
closable
|
||||
@click:close="filterSourceId = null"
|
||||
>
|
||||
Source: {{ sourceLabel(filterSourceId) }}
|
||||
</v-chip>
|
||||
<v-chip
|
||||
v-if="filterFromDate"
|
||||
size="small"
|
||||
closable
|
||||
@click:close="filterFromDate = null"
|
||||
>
|
||||
From: {{ filterFromDate }}
|
||||
</v-chip>
|
||||
<v-chip
|
||||
v-if="filterToDate"
|
||||
size="small"
|
||||
closable
|
||||
@click:close="filterToDate = null"
|
||||
>
|
||||
To: {{ filterToDate }}
|
||||
</v-chip>
|
||||
<v-chip
|
||||
v-if="!filterExcludeSuperseded"
|
||||
size="small"
|
||||
closable
|
||||
@click:close="filterExcludeSuperseded = true"
|
||||
>
|
||||
Showing superseded
|
||||
</v-chip>
|
||||
|
||||
<v-btn
|
||||
v-if="activeFilterCount"
|
||||
size="small"
|
||||
variant="text"
|
||||
@click="clearAllFilters"
|
||||
>
|
||||
Clear all
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<!-- Downloads Table -->
|
||||
<v-card>
|
||||
@@ -334,6 +392,32 @@ const sourceOptions = computed(() =>
|
||||
}))
|
||||
)
|
||||
|
||||
const activeFilterCount = computed(() => {
|
||||
let n = 0
|
||||
if (filterStatus.value) n++
|
||||
if (filterSourceId.value) n++
|
||||
if (filterFromDate.value) n++
|
||||
if (filterToDate.value) n++
|
||||
if (!filterExcludeSuperseded.value) n++
|
||||
return n
|
||||
})
|
||||
|
||||
function statusLabel(value) {
|
||||
return statusOptions.find(s => s.value === value)?.title || value
|
||||
}
|
||||
|
||||
function sourceLabel(id) {
|
||||
return sourceOptions.value.find(s => s.value === id)?.title || `#${id}`
|
||||
}
|
||||
|
||||
function clearAllFilters() {
|
||||
filterStatus.value = null
|
||||
filterSourceId.value = null
|
||||
filterFromDate.value = null
|
||||
filterToDate.value = null
|
||||
filterExcludeSuperseded.value = true
|
||||
}
|
||||
|
||||
const headers = [
|
||||
{ title: 'Status', key: 'status', width: 120 },
|
||||
{ title: 'Source', key: 'source', width: 180 },
|
||||
|
||||
Reference in New Issue
Block a user