|
|
|
@ -3,22 +3,25 @@
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
|
|
|
|
|
|
|
import './styles.scss';
|
|
|
|
import './styles.scss';
|
|
|
|
import React, { useEffect } from 'react';
|
|
|
|
import React, { useEffect, useCallback } from 'react';
|
|
|
|
import { useSelector, useDispatch } from 'react-redux';
|
|
|
|
import { useSelector, useDispatch } from 'react-redux';
|
|
|
|
import { useHistory, useParams } from 'react-router';
|
|
|
|
import { useHistory, useParams, useLocation } from 'react-router';
|
|
|
|
import Spin from 'antd/lib/spin';
|
|
|
|
import Spin from 'antd/lib/spin';
|
|
|
|
import { Row, Col } from 'antd/lib/grid';
|
|
|
|
import { Row, Col } from 'antd/lib/grid';
|
|
|
|
import Result from 'antd/lib/result';
|
|
|
|
import Result from 'antd/lib/result';
|
|
|
|
import Button from 'antd/lib/button';
|
|
|
|
import Button from 'antd/lib/button';
|
|
|
|
import Title from 'antd/lib/typography/Title';
|
|
|
|
import Title from 'antd/lib/typography/Title';
|
|
|
|
|
|
|
|
import Pagination from 'antd/lib/pagination';
|
|
|
|
import { PlusOutlined } from '@ant-design/icons';
|
|
|
|
import { PlusOutlined } from '@ant-design/icons';
|
|
|
|
|
|
|
|
|
|
|
|
import { CombinedState, Task } from 'reducers/interfaces';
|
|
|
|
import { CombinedState, Task, TasksQuery } from 'reducers/interfaces';
|
|
|
|
import { getProjectsAsync } from 'actions/projects-actions';
|
|
|
|
import { getProjectsAsync, getProjectTasksAsync } from 'actions/projects-actions';
|
|
|
|
import { cancelInferenceAsync } from 'actions/models-actions';
|
|
|
|
import { cancelInferenceAsync } from 'actions/models-actions';
|
|
|
|
import TaskItem from 'components/tasks-page/task-item';
|
|
|
|
import TaskItem from 'components/tasks-page/task-item';
|
|
|
|
|
|
|
|
import SearchField from 'components/search-field/search-field';
|
|
|
|
import MoveTaskModal from 'components/move-task-modal/move-task-modal';
|
|
|
|
import MoveTaskModal from 'components/move-task-modal/move-task-modal';
|
|
|
|
import ModelRunnerDialog from 'components/model-runner-modal/model-runner-dialog';
|
|
|
|
import ModelRunnerDialog from 'components/model-runner-modal/model-runner-dialog';
|
|
|
|
|
|
|
|
import { useDidUpdateEffect } from 'utils/hooks';
|
|
|
|
import DetailsComponent from './details';
|
|
|
|
import DetailsComponent from './details';
|
|
|
|
import ProjectTopBar from './top-bar';
|
|
|
|
import ProjectTopBar from './top-bar';
|
|
|
|
|
|
|
|
|
|
|
|
@ -30,25 +33,54 @@ export default function ProjectPageComponent(): JSX.Element {
|
|
|
|
const id = +useParams<ParamType>().id;
|
|
|
|
const id = +useParams<ParamType>().id;
|
|
|
|
const dispatch = useDispatch();
|
|
|
|
const dispatch = useDispatch();
|
|
|
|
const history = useHistory();
|
|
|
|
const history = useHistory();
|
|
|
|
|
|
|
|
const { search } = useLocation();
|
|
|
|
const projects = useSelector((state: CombinedState) => state.projects.current).map((project) => project.instance);
|
|
|
|
const projects = useSelector((state: CombinedState) => state.projects.current).map((project) => project.instance);
|
|
|
|
const projectsFetching = useSelector((state: CombinedState) => state.projects.fetching);
|
|
|
|
const projectsFetching = useSelector((state: CombinedState) => state.projects.fetching);
|
|
|
|
const deletes = useSelector((state: CombinedState) => state.projects.activities.deletes);
|
|
|
|
const deletes = useSelector((state: CombinedState) => state.projects.activities.deletes);
|
|
|
|
const taskDeletes = useSelector((state: CombinedState) => state.tasks.activities.deletes);
|
|
|
|
const taskDeletes = useSelector((state: CombinedState) => state.tasks.activities.deletes);
|
|
|
|
const tasksActiveInferences = useSelector((state: CombinedState) => state.models.inferences);
|
|
|
|
const tasksActiveInferences = useSelector((state: CombinedState) => state.models.inferences);
|
|
|
|
const tasks = useSelector((state: CombinedState) => state.tasks.current);
|
|
|
|
const tasks = useSelector((state: CombinedState) => state.tasks.current);
|
|
|
|
|
|
|
|
const tasksCount = useSelector((state: CombinedState) => state.tasks.count);
|
|
|
|
|
|
|
|
const tasksGettingQuery = useSelector((state: CombinedState) => state.projects.tasksGettingQuery);
|
|
|
|
|
|
|
|
|
|
|
|
const [project] = projects.filter((_project) => _project.id === id);
|
|
|
|
const [project] = projects.filter((_project) => _project.id === id);
|
|
|
|
const projectSubsets = [''];
|
|
|
|
const projectSubsets: Array<string> = [];
|
|
|
|
if (project) projectSubsets.push(...project.subsets);
|
|
|
|
for (const task of tasks) {
|
|
|
|
|
|
|
|
if (!projectSubsets.includes(task.instance.subset)) projectSubsets.push(task.instance.subset);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const deleteActivity = project && id in deletes ? deletes[id] : null;
|
|
|
|
const deleteActivity = project && id in deletes ? deletes[id] : null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const onPageChange = useCallback(
|
|
|
|
|
|
|
|
(p: number) => {
|
|
|
|
|
|
|
|
dispatch(getProjectTasksAsync({
|
|
|
|
|
|
|
|
projectId: id,
|
|
|
|
|
|
|
|
page: p,
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
[],
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
useEffect(() => {
|
|
|
|
dispatch(
|
|
|
|
const searchParams: Partial<TasksQuery> = {};
|
|
|
|
getProjectsAsync({
|
|
|
|
for (const [param, value] of new URLSearchParams(search)) {
|
|
|
|
id,
|
|
|
|
searchParams[param] = ['page'].includes(param) ? Number.parseInt(value, 10) : value;
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
);
|
|
|
|
dispatch(getProjectsAsync({ id }, searchParams));
|
|
|
|
}, [id, dispatch]);
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
useDidUpdateEffect(() => {
|
|
|
|
|
|
|
|
const searchParams = new URLSearchParams();
|
|
|
|
|
|
|
|
for (const [name, value] of Object.entries(tasksGettingQuery)) {
|
|
|
|
|
|
|
|
if (value !== null && typeof value !== 'undefined' && !['projectId', 'ordering'].includes(name)) {
|
|
|
|
|
|
|
|
searchParams.append(name, value.toString());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
history.push({
|
|
|
|
|
|
|
|
pathname: `/projects/${id}`,
|
|
|
|
|
|
|
|
search: `?${searchParams.toString()}`,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}, [tasksGettingQuery, id]);
|
|
|
|
|
|
|
|
|
|
|
|
if (deleteActivity) {
|
|
|
|
if (deleteActivity) {
|
|
|
|
history.push('/projects');
|
|
|
|
history.push('/projects');
|
|
|
|
@ -69,14 +101,27 @@ export default function ProjectPageComponent(): JSX.Element {
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const paginationDimensions = {
|
|
|
|
|
|
|
|
md: 22,
|
|
|
|
|
|
|
|
lg: 18,
|
|
|
|
|
|
|
|
xl: 16,
|
|
|
|
|
|
|
|
xxl: 16,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<Row justify='center' align='top' className='cvat-project-page'>
|
|
|
|
<Row justify='center' align='top' className='cvat-project-page'>
|
|
|
|
<Col md={22} lg={18} xl={16} xxl={14}>
|
|
|
|
<Col md={22} lg={18} xl={16} xxl={14}>
|
|
|
|
<ProjectTopBar projectInstance={project} />
|
|
|
|
<ProjectTopBar projectInstance={project} />
|
|
|
|
<DetailsComponent project={project} />
|
|
|
|
<DetailsComponent project={project} />
|
|
|
|
<Row justify='space-between' align='middle' className='cvat-project-page-tasks-bar'>
|
|
|
|
<Row justify='space-between' align='middle' className='cvat-project-page-tasks-bar'>
|
|
|
|
<Col>
|
|
|
|
<Col className='cvat-project-tasks-title-search'>
|
|
|
|
<Title level={3}>Tasks</Title>
|
|
|
|
<Title level={3}>Tasks</Title>
|
|
|
|
|
|
|
|
<SearchField
|
|
|
|
|
|
|
|
query={tasksGettingQuery}
|
|
|
|
|
|
|
|
instance='task'
|
|
|
|
|
|
|
|
skipFields={['ordering', 'projectId']}
|
|
|
|
|
|
|
|
onSearch={(query: TasksQuery) => dispatch(getProjectTasksAsync(query))}
|
|
|
|
|
|
|
|
/>
|
|
|
|
</Col>
|
|
|
|
</Col>
|
|
|
|
<Col>
|
|
|
|
<Col>
|
|
|
|
<Button
|
|
|
|
<Button
|
|
|
|
@ -110,6 +155,19 @@ export default function ProjectPageComponent(): JSX.Element {
|
|
|
|
))}
|
|
|
|
))}
|
|
|
|
</React.Fragment>
|
|
|
|
</React.Fragment>
|
|
|
|
))}
|
|
|
|
))}
|
|
|
|
|
|
|
|
<Row justify='center'>
|
|
|
|
|
|
|
|
<Col {...paginationDimensions}>
|
|
|
|
|
|
|
|
<Pagination
|
|
|
|
|
|
|
|
className='cvat-project-tasks-pagination'
|
|
|
|
|
|
|
|
onChange={onPageChange}
|
|
|
|
|
|
|
|
showSizeChanger={false}
|
|
|
|
|
|
|
|
total={tasksCount}
|
|
|
|
|
|
|
|
pageSize={10}
|
|
|
|
|
|
|
|
current={tasksGettingQuery.page}
|
|
|
|
|
|
|
|
showQuickJumper
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
|
|
</Row>
|
|
|
|
</Col>
|
|
|
|
</Col>
|
|
|
|
<MoveTaskModal />
|
|
|
|
<MoveTaskModal />
|
|
|
|
<ModelRunnerDialog />
|
|
|
|
<ModelRunnerDialog />
|
|
|
|
|