Web: add filter bar for job status

This commit is contained in:
Sybren A. Stüvel 2022-05-16 18:15:51 +02:00
parent 9fd4d55fdb
commit 163611ff19
3 changed files with 71 additions and 22 deletions

View File

@ -283,13 +283,33 @@ footer {
.indicator { .indicator {
--indicator-color: var(--color-background); --indicator-color: var(--color-background);
--indicator-size: 6px;
background-color: var(--indicator-color); background-color: var(--indicator-color);
border: 3px solid var(--indicator-color); border: calc(var(--indicator-size)/2) solid var(--indicator-color);
border-radius: 50%; border-radius: 50%;
display: block; display: block;
height: 6px; height: var(--indicator-size);
width: 6px; width: var(--indicator-size);
}
ul.status-filter-bar {
align-items: center;
display: flex;
list-style-type: none;
padding: 0;
margin: 0.3rem 0;
}
ul.status-filter-bar .status-filter-indicator {
margin: 2px;
opacity: 33%;
cursor: pointer;
}
ul.status-filter-bar .status-filter-indicator.active {
opacity: 100%;
}
ul.status-filter-bar .status-filter-indicator .indicator {
--indicator-size: 15px;
} }
.status-active { .status-active {
@ -351,11 +371,3 @@ footer {
.tabulator-row.active-row.tabulator-row-even { .tabulator-row.active-row.tabulator-row-even {
background-color: var(--table-color-background-row-active-even); background-color: var(--table-color-background-row-active-even);
} }
ul.status-filter-bar {
align-items: center;
display: flex;
list-style-type: none;
margin: 0;
padding: 0;
}

View File

@ -2,6 +2,11 @@
<div> <div>
<h2 class="column-title">Jobs</h2> <h2 class="column-title">Jobs</h2>
<job-actions-bar /> <job-actions-bar />
<status-filter-bar
:availableStatuses="availableStatuses"
:activeStatuses="shownStatuses"
@click="toggleStatusFilter"
/>
<div class="job-list" id="flamenco_job_list"></div> <div class="job-list" id="flamenco_job_list"></div>
</div> </div>
</template> </template>
@ -10,21 +15,24 @@
import { TabulatorFull as Tabulator } from 'tabulator-tables'; import { TabulatorFull as Tabulator } from 'tabulator-tables';
import * as datetime from "@/datetime"; import * as datetime from "@/datetime";
import * as API from '@/manager-api' import * as API from '@/manager-api'
import { toTitleCase } from '@/strings'; import { indicator } from '@/statusindicator';
import { apiClient } from '@/stores/api-query-count'; import { apiClient } from '@/stores/api-query-count';
import JobActionsBar from '@/components/JobActionsBar.vue' import JobActionsBar from '@/components/JobActionsBar.vue'
import StatusFilterBar from '@/components/StatusFilterBar.vue'
export default { export default {
name: 'JobsTable', name: 'JobsTable',
props: ["activeJobID"], props: ["activeJobID"],
emits: ["tableRowClicked"], emits: ["tableRowClicked"],
components: { components: {
JobActionsBar, JobActionsBar, StatusFilterBar,
}, },
data: () => { data: () => {
return { return {
filteredStatuses: new Set(), shownStatuses: [],
availableStatuses: [], // Will be filled after data is loaded from the backend.
indicator: indicator, // So that the template can use this function too.
}; };
}, },
mounted() { mounted() {
@ -111,16 +119,19 @@ export default {
// "Down-cast" to JobUpdate to only get those fields, just for debugging things: // "Down-cast" to JobUpdate to only get those fields, just for debugging things:
// data.jobs = data.jobs.map((j) => API.JobUpdate.constructFromObject(j)); // data.jobs = data.jobs.map((j) => API.JobUpdate.constructFromObject(j));
this.tabulator.setData(data.jobs); this.tabulator.setData(data.jobs);
this._refreshAvailableStatuses();
}, },
processJobUpdate(jobUpdate) { processJobUpdate(jobUpdate) {
// updateData() will only overwrite properties that are actually set on // updateData() will only overwrite properties that are actually set on
// jobUpdate, and leave the rest as-is. // jobUpdate, and leave the rest as-is.
this.tabulator.updateData([jobUpdate]) this.tabulator.updateData([jobUpdate])
.then(this.sortData); .then(this.sortData);
this._refreshAvailableStatuses();
}, },
processNewJob(jobUpdate) { processNewJob(jobUpdate) {
this.tabulator.addData([jobUpdate]) this.tabulator.addData([jobUpdate])
.then(this.sortData); .then(this.sortData);
this._refreshAvailableStatuses();
}, },
onRowClick(event, row) { onRowClick(event, row) {
@ -132,23 +143,31 @@ export default {
// Depending on which cell was clicked, take a different action. // Depending on which cell was clicked, take a different action.
const columnName = event.target.getAttribute("tabulator-field"); const columnName = event.target.getAttribute("tabulator-field");
if (columnName == "status") { if (columnName == "status") {
this._toggleStatusFilter(rowData.status); this.toggleStatusFilter(rowData.status);
return; return;
} }
this.$emit("tableRowClicked", rowData); this.$emit("tableRowClicked", rowData);
}, },
toggleStatusFilter(status) {
_toggleStatusFilter(status) { const asSet = new Set(this.shownStatuses);
if (!this.filteredStatuses.delete(status)) { if (!asSet.delete(status)) {
this.filteredStatuses.add(status); asSet.add(status);
} }
this.shownStatuses = Array.from(asSet).sort();
this.tabulator.refreshFilter(); this.tabulator.refreshFilter();
}, },
_filterByStatus(job) { _filterByStatus(job) {
if (this.filteredStatuses.size) { if (this.shownStatuses.length == 0) {
return this.filteredStatuses.has(job.status);
}
return true; return true;
}
return this.shownStatuses.indexOf(job.status) >= 0;
},
_refreshAvailableStatuses() {
const statuses = new Set();
for (let row of this.tabulator.getData()) {
statuses.add(row.status);
}
this.availableStatuses = Array.from(statuses).sort();
}, },
_reformatRow(jobID) { _reformatRow(jobID) {

View File

@ -0,0 +1,18 @@
<script setup>
import { ref } from 'vue'
import { indicator } from '@/statusindicator';
defineProps(['availableStatuses', 'activeStatuses']);
const emit = defineEmits(['click'])
</script>
<template>
<ul class="status-filter-bar">
<li v-for="status in availableStatuses" class="status-filter-indicator"
:data-status="status"
:class="{active: activeStatuses.indexOf(status) >= 0}"
@click="emit('click', status)"
v-html="indicator(status)"
></li>
</ul>
</template>