From 163611ff1945fc715a4d7c4343ed0e90a5fa74a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 16 May 2022 18:15:51 +0200 Subject: [PATCH] Web: add filter bar for job status --- web/app/src/assets/base.css | 34 ++++++++++++------ web/app/src/components/JobsTable.vue | 41 ++++++++++++++++------ web/app/src/components/StatusFilterBar.vue | 18 ++++++++++ 3 files changed, 71 insertions(+), 22 deletions(-) create mode 100644 web/app/src/components/StatusFilterBar.vue diff --git a/web/app/src/assets/base.css b/web/app/src/assets/base.css index 6521da9e..1269c431 100644 --- a/web/app/src/assets/base.css +++ b/web/app/src/assets/base.css @@ -283,13 +283,33 @@ footer { .indicator { --indicator-color: var(--color-background); + --indicator-size: 6px; background-color: var(--indicator-color); - border: 3px solid var(--indicator-color); + border: calc(var(--indicator-size)/2) solid var(--indicator-color); border-radius: 50%; display: block; - height: 6px; - width: 6px; + height: var(--indicator-size); + 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 { @@ -351,11 +371,3 @@ footer { .tabulator-row.active-row.tabulator-row-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; -} diff --git a/web/app/src/components/JobsTable.vue b/web/app/src/components/JobsTable.vue index 3f170cde..7bbae2f9 100644 --- a/web/app/src/components/JobsTable.vue +++ b/web/app/src/components/JobsTable.vue @@ -2,6 +2,11 @@

Jobs

+
@@ -10,21 +15,24 @@ import { TabulatorFull as Tabulator } from 'tabulator-tables'; import * as datetime from "@/datetime"; import * as API from '@/manager-api' -import { toTitleCase } from '@/strings'; +import { indicator } from '@/statusindicator'; import { apiClient } from '@/stores/api-query-count'; import JobActionsBar from '@/components/JobActionsBar.vue' +import StatusFilterBar from '@/components/StatusFilterBar.vue' export default { name: 'JobsTable', props: ["activeJobID"], emits: ["tableRowClicked"], components: { - JobActionsBar, + JobActionsBar, StatusFilterBar, }, data: () => { 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() { @@ -111,16 +119,19 @@ export default { // "Down-cast" to JobUpdate to only get those fields, just for debugging things: // data.jobs = data.jobs.map((j) => API.JobUpdate.constructFromObject(j)); this.tabulator.setData(data.jobs); + this._refreshAvailableStatuses(); }, processJobUpdate(jobUpdate) { // updateData() will only overwrite properties that are actually set on // jobUpdate, and leave the rest as-is. this.tabulator.updateData([jobUpdate]) .then(this.sortData); + this._refreshAvailableStatuses(); }, processNewJob(jobUpdate) { this.tabulator.addData([jobUpdate]) .then(this.sortData); + this._refreshAvailableStatuses(); }, onRowClick(event, row) { @@ -132,23 +143,31 @@ export default { // Depending on which cell was clicked, take a different action. const columnName = event.target.getAttribute("tabulator-field"); if (columnName == "status") { - this._toggleStatusFilter(rowData.status); + this.toggleStatusFilter(rowData.status); return; } this.$emit("tableRowClicked", rowData); }, - - _toggleStatusFilter(status) { - if (!this.filteredStatuses.delete(status)) { - this.filteredStatuses.add(status); + toggleStatusFilter(status) { + const asSet = new Set(this.shownStatuses); + if (!asSet.delete(status)) { + asSet.add(status); } + this.shownStatuses = Array.from(asSet).sort(); this.tabulator.refreshFilter(); }, _filterByStatus(job) { - if (this.filteredStatuses.size) { - return this.filteredStatuses.has(job.status); + if (this.shownStatuses.length == 0) { + 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) { diff --git a/web/app/src/components/StatusFilterBar.vue b/web/app/src/components/StatusFilterBar.vue new file mode 100644 index 00000000..faff6a4c --- /dev/null +++ b/web/app/src/components/StatusFilterBar.vue @@ -0,0 +1,18 @@ + + +