Web: add filter bar for job status
This commit is contained in:
parent
9fd4d55fdb
commit
163611ff19
@ -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;
|
|
||||||
}
|
|
||||||
|
@ -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) {
|
||||||
|
18
web/app/src/components/StatusFilterBar.vue
Normal file
18
web/app/src/components/StatusFilterBar.vue
Normal 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>
|
Loading…
x
Reference in New Issue
Block a user