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-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<!-- Filters -->
|
<!-- Filters (compact bar with popover) -->
|
||||||
<v-row class="mb-4">
|
<div class="d-flex align-center flex-wrap ga-2 mb-4">
|
||||||
<v-col cols="12" md="3">
|
<v-menu :close-on-content-click="false" location="bottom start">
|
||||||
<v-select
|
<template v-slot:activator="{ props }">
|
||||||
v-model="filterStatus"
|
<v-btn v-bind="props" variant="outlined" size="small" prepend-icon="mdi-filter-variant">
|
||||||
:items="statusOptions"
|
Filters
|
||||||
label="Status"
|
<v-chip v-if="activeFilterCount" class="ml-2" size="x-small" color="primary">
|
||||||
density="compact"
|
{{ activeFilterCount }}
|
||||||
variant="outlined"
|
</v-chip>
|
||||||
clearable
|
<v-icon end>mdi-chevron-down</v-icon>
|
||||||
/>
|
</v-btn>
|
||||||
</v-col>
|
</template>
|
||||||
<v-col cols="12" md="3">
|
<v-card min-width="320" class="pa-4">
|
||||||
<v-select
|
<v-select
|
||||||
v-model="filterSourceId"
|
v-model="filterStatus"
|
||||||
:items="sourceOptions"
|
:items="statusOptions"
|
||||||
label="Source"
|
label="Status"
|
||||||
density="compact"
|
density="compact"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
clearable
|
clearable
|
||||||
/>
|
class="mb-2"
|
||||||
</v-col>
|
hide-details
|
||||||
<v-col cols="12" md="3">
|
/>
|
||||||
<v-text-field
|
<v-select
|
||||||
v-model="filterFromDate"
|
v-model="filterSourceId"
|
||||||
label="From Date"
|
:items="sourceOptions"
|
||||||
type="date"
|
label="Source"
|
||||||
density="compact"
|
density="compact"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
clearable
|
clearable
|
||||||
/>
|
class="mb-2"
|
||||||
</v-col>
|
hide-details
|
||||||
<v-col cols="12" md="3">
|
/>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="filterToDate"
|
v-model="filterFromDate"
|
||||||
label="To Date"
|
label="From Date"
|
||||||
type="date"
|
type="date"
|
||||||
density="compact"
|
density="compact"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
clearable
|
clearable
|
||||||
/>
|
class="mb-2"
|
||||||
</v-col>
|
hide-details
|
||||||
</v-row>
|
/>
|
||||||
|
<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 -->
|
<!-- Active filter chips -->
|
||||||
<v-row class="mb-4">
|
<v-chip
|
||||||
<v-col cols="12">
|
v-if="filterStatus"
|
||||||
<v-switch
|
size="small"
|
||||||
v-model="filterExcludeSuperseded"
|
closable
|
||||||
label="Hide superseded failures"
|
@click:close="filterStatus = null"
|
||||||
density="compact"
|
>
|
||||||
hide-details
|
Status: {{ statusLabel(filterStatus) }}
|
||||||
color="primary"
|
</v-chip>
|
||||||
/>
|
<v-chip
|
||||||
</v-col>
|
v-if="filterSourceId"
|
||||||
</v-row>
|
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 -->
|
<!-- Downloads Table -->
|
||||||
<v-card>
|
<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 = [
|
const headers = [
|
||||||
{ title: 'Status', key: 'status', width: 120 },
|
{ title: 'Status', key: 'status', width: 120 },
|
||||||
{ title: 'Source', key: 'source', width: 180 },
|
{ title: 'Source', key: 'source', width: 180 },
|
||||||
|
|||||||
Reference in New Issue
Block a user