From 937982dbccaf0f353d455107c29c9d32bef0e633 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Tue, 30 Mar 2021 09:47:17 +0300 Subject: [PATCH 1/5] Added filters & sorters for job list --- cvat-ui/src/components/task-page/job-list.tsx | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/cvat-ui/src/components/task-page/job-list.tsx b/cvat-ui/src/components/task-page/job-list.tsx index a7cd55a7cc93..55cde2d16a47 100644 --- a/cvat-ui/src/components/task-page/job-list.tsx +++ b/cvat-ui/src/components/task-page/job-list.tsx @@ -7,6 +7,7 @@ import { RouteComponentProps } from 'react-router'; import { withRouter } from 'react-router-dom'; import { Row, Col } from 'antd/lib/grid'; import { LoadingOutlined, QuestionCircleOutlined, CopyOutlined } from '@ant-design/icons'; +import { ColumnFilterItem } from 'antd/lib/table/interface'; import Table from 'antd/lib/table'; import Button from 'antd/lib/button'; import Text from 'antd/lib/typography/Text'; @@ -101,6 +102,46 @@ function JobListComponent(props: Props & RouteComponentProps): JSX.Element { } = props; const { jobs, id: taskId } = taskInstance; + + function sorter(path: string) { + return (obj1: any, obj2: any): number => { + let currentObj1 = obj1; + let currentObj2 = obj2; + let field1: string | null = null; + let field2: string | null = null; + for (const pathSegment of path.split('.')) { + field1 = currentObj1 && pathSegment in currentObj1 ? currentObj1[pathSegment] : null; + field2 = currentObj2 && pathSegment in currentObj2 ? currentObj2[pathSegment] : null; + currentObj1 = currentObj1 && pathSegment in currentObj1 ? currentObj1[pathSegment] : null; + currentObj2 = currentObj2 && pathSegment in currentObj2 ? currentObj2[pathSegment] : null; + } + + if (field1 && field2) { + return field1.localeCompare(field2); + } + + if (field1 === null) { + return 1; + } + + return -1; + }; + } + + function collectUsers(path: string): ColumnFilterItem[] { + return Array.from( + new Set( + jobs.map((job: any) => { + if (job[path] === null) { + return null; + } + + return job[path].username; + }), + ), + ).map((value: string | null) => ({ text: value || 'Is Empty', value: value || false })); + } + const columns = [ { title: 'Job', @@ -152,6 +193,13 @@ function JobListComponent(props: Props & RouteComponentProps): JSX.Element { ); }, + sorter: sorter('status.status'), + filters: [ + { text: 'annotation', value: 'annotation' }, + { text: 'validation', value: 'validation' }, + { text: 'completed', value: 'completed' }, + ], + onFilter: (value: string | number | boolean, record: any) => record.status.status === value, }, { title: 'Started on', @@ -180,6 +228,10 @@ function JobListComponent(props: Props & RouteComponentProps): JSX.Element { }} /> ), + sorter: sorter('assignee.assignee.username'), + filters: collectUsers('assignee'), + onFilter: (value: string | number | boolean, record: any) => + (record.assignee.assignee?.username || false) === value, }, { title: 'Reviewer', @@ -196,6 +248,10 @@ function JobListComponent(props: Props & RouteComponentProps): JSX.Element { }} /> ), + sorter: sorter('reviewer.reviewer.username'), + filters: collectUsers('reviewer'), + onFilter: (value: string | number | boolean, record: any) => + (record.reviewer.reviewer?.username || false) === value, }, ]; @@ -207,13 +263,14 @@ function JobListComponent(props: Props & RouteComponentProps): JSX.Element { const created = moment(props.taskInstance.createdDate); + const now = moment(moment.now()); acc.push({ key: job.id, job: job.id, frames: `${job.startFrame}-${job.stopFrame}`, status: job, started: `${created.format('MMMM Do YYYY HH:MM')}`, - duration: `${moment.duration(moment(moment.now()).diff(created)).humanize()}`, + duration: `${moment.duration(now.diff(created)).humanize()}`, assignee: job, reviewer: job, }); From 9cbddfa5bae33c4979b39cf7d3ce5d991a64b379 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Tue, 30 Mar 2021 10:15:04 +0300 Subject: [PATCH 2/5] Added tooltip --- cvat-ui/src/components/tasks-page/styles.scss | 10 +++ cvat-ui/src/components/tasks-page/top-bar.tsx | 77 +++++++++++++++++-- 2 files changed, 79 insertions(+), 8 deletions(-) diff --git a/cvat-ui/src/components/tasks-page/styles.scss b/cvat-ui/src/components/tasks-page/styles.scss index 73ad1e575b4b..3b457fd33790 100644 --- a/cvat-ui/src/components/tasks-page/styles.scss +++ b/cvat-ui/src/components/tasks-page/styles.scss @@ -154,6 +154,16 @@ @extend .cvat-text-color; } +.cvat-tasks-search-tooltip { + span { + color: white; + } + + strong::after { + content: ' - '; + } +} + #cvat-create-task-button { padding: 0 30px; } diff --git a/cvat-ui/src/components/tasks-page/top-bar.tsx b/cvat-ui/src/components/tasks-page/top-bar.tsx index b3c64d3209ee..c9c51b75b7ec 100644 --- a/cvat-ui/src/components/tasks-page/top-bar.tsx +++ b/cvat-ui/src/components/tasks-page/top-bar.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2020 Intel Corporation +// Copyright (C) 2020-2021 Intel Corporation // // SPDX-License-Identifier: MIT @@ -9,6 +9,9 @@ import { PlusOutlined } from '@ant-design/icons'; import Button from 'antd/lib/button'; import Input from 'antd/lib/input'; import Text from 'antd/lib/typography/Text'; +import Paragraph from 'antd/lib/typography/Paragraph'; + +import CVATTooltip from 'components/common/cvat-tooltip'; interface VisibleTopBarProps { onSearch: (value: string) => void; @@ -25,13 +28,71 @@ export default function TopBarComponent(props: VisibleTopBarProps): JSX.Element Tasks - + + + owner: admin + + all tasks created by the user who has the substring + admin + in their username + + + + assignee: employee + + all tasks which are assigned to a user who has the substring + admin + in their username + + + + name: training + + all tasks with the substring + training + in its name + + + + mode: annotation + + annotation tasks are tasks with images, interpolation tasks are tasks with + videos + + + + status: annotation + annotation, validation, or completed + + + id: 5 + the task with id 5 + + + + Filters can be combined (to the exclusion of id) using the keyword AND. Example: + + mode: interpolation AND owner: admin + + + + + Search within all the string fields by default + + + )} + > + +