diff --git a/client/src/components/Indices/filtersMixin.js b/client/src/components/Indices/filtersMixin.js index 719297f4a4a3..845935c09513 100644 --- a/client/src/components/Indices/filtersMixin.js +++ b/client/src/components/Indices/filtersMixin.js @@ -4,14 +4,39 @@ export default { components: { IndexFilter, }, + props: { + inputDebounceDelay: { + type: Number, + default: 500, + }, + }, data() { return { filter: "", + implicitFilter: null, + helpHtml: null, }; }, computed: { isFiltered() { - return !!this.filter; + return Boolean(this.filter); + }, + effectiveFilter() { + let filter = this.filter; + const implicitFilter = this.implicitFilter; + if (implicitFilter && filter) { + filter = `${implicitFilter} ${filter}`; + } else if (implicitFilter) { + filter = implicitFilter; + } + return filter; + }, + filterAttrs() { + return { + "debounce-delay": this.inputDebounceDelay, + placeholder: this.titleSearch, + "help-html": this.helpHtml, + }; }, }, methods: { diff --git a/client/src/components/Indices/filtersMixin.test.js b/client/src/components/Indices/filtersMixin.test.js index 32d692b1c243..358b02dbcab6 100644 --- a/client/src/components/Indices/filtersMixin.test.js +++ b/client/src/components/Indices/filtersMixin.test.js @@ -43,4 +43,16 @@ describe("filtersMixin.js", () => { wrapper.vm.appendTagFilter("name", "foobar"); expect(wrapper.vm.filter).toBe("name:'foobar'"); }); + + it("should have an effective filter that combines implicit and explicit filter", async () => { + wrapper.vm.implicitFilter = "tag:cowdog"; + wrapper.vm.appendTagFilter("name", "foobar"); + expect(wrapper.vm.filter).toBe("name:'foobar'"); + expect(wrapper.vm.effectiveFilter).toBe("tag:cowdog name:'foobar'"); + }); + + it("should just use implicit filter as effective if filter is empty", async () => { + wrapper.vm.implicitFilter = "tag:cowdog"; + expect(wrapper.vm.effectiveFilter).toBe("tag:cowdog"); + }); }); diff --git a/client/src/components/admin/Jobs.vue b/client/src/components/admin/JobsList.vue similarity index 78% rename from client/src/components/admin/Jobs.vue rename to client/src/components/admin/JobsList.vue index b526ed769813..9bb2a511c3a2 100644 --- a/client/src/components/admin/Jobs.vue +++ b/client/src/components/admin/JobsList.vue @@ -5,7 +5,7 @@ {{ message }} Job Lock - + Job Overview

Below unfinished jobs are displayed (in the 'new', 'queued', 'running', or 'upload' states) and recently @@ -36,9 +36,7 @@ - - - + @@ -56,11 +54,10 @@

Unfinished Jobs

- - + @@ -105,14 +105,42 @@ import { commonJobFields } from "./JobFields"; import { errorMessageAsString } from "utils/simple-error"; import { jobsProvider } from "components/providers/JobProvider"; import Heading from "components/Common/Heading"; +import filtersMixin from "components/Indices/filtersMixin"; function cancelJob(jobId, message) { const url = `${getAppRoot()}api/jobs/${jobId}`; return axios.delete(url, { data: { message: message } }); } +const helpHtml = `
+

This textbox box can be used to filter the jobs displayed. + +

Text entered here will be searched against job user, tool ID, job runner, and handler. Additionally, +advanced filtering tags can be used to refine the search more precisely. Tags are of the form +<tag_name>:<tag_value> or <tag_name>:'<tag_value>'. +For instance to search just for jobs with cat1 in the tool name, tool:cat1 can be used. +Notice by default the search is not case-sensitive. + +

If the quoted version of tag is used, the search is case sensitive and only full matches will be +returned. So tool:'cat1' would show only jobs from the cat1 tool exactly.

+ +

The available tags are: +

+
user
+
This filters the job index to contain only jobs executed by matching user(s). You may also just click on a user in the list of jobs to filter on that exact user using this directly.
+
handler
+
This filters the job index to contain only jobs executed on matching handler(s). You may also just click on a handler in the list of jobs to filter on that exact user using this directly.
+
runner
+
This filters the job index to contain only jobs executed on matching job runner(s). You may also just click on a runner in the list of jobs to filter on that exact user using this directly.
+
tool
+
This filters the job index to contain only jobs from the matching tool(s). You may also just click on a tool in the list of jobs to filter on that exact user using this directly.
+
+
+`; + export default { components: { JobLock, JobsTable, Heading }, + mixins: [filtersMixin], data() { return { jobs: [], @@ -130,13 +158,14 @@ export default { allSelected: false, indeterminate: false, stopMessage: "", - filter: "", message: "", status: "info", loading: true, busy: true, cutoffMin: 5, showAllRunning: false, + titleSearch: `search jobs`, + helpHtml: helpHtml, }; }, computed: { @@ -205,6 +234,9 @@ export default { const dateRangeMin = new Date(Date.now() - cutoff * 60 * 1000).toISOString(); params.date_range_min = `${dateRangeMin}`; } + if (this.filter) { + params.search = this.filter; + } const ctx = { root: getAppRoot(), ...params, diff --git a/client/src/components/admin/JobsTable.vue b/client/src/components/admin/JobsTable.vue index b6072d4d5087..9df6ac9597e8 100644 --- a/client/src/components/admin/JobsTable.vue +++ b/client/src/components/admin/JobsTable.vue @@ -1,16 +1,16 @@