web/src/connectors/job-detail-view-connector/job-detail-view-connector.tsx (303 lines of code) (raw):
// temporary_disabled_rules
/* eslint-disable @typescript-eslint/no-redeclare, react-hooks/exhaustive-deps */
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useJobById } from 'api/hooks/jobs';
import { FileDocument, User } from 'api/typings';
import { usePageTable } from 'shared';
import { DataColumnProps, INotification, useUuiContext } from '@epam/uui';
import {
TabButton,
FlexRow,
Panel,
ErrorNotification,
Text,
SuccessNotification
} from '@epam/loveship';
import { JobDetailViewHeader } from '../../shared/components/job/job-detail-view-header';
import { useDocumentsInJob } from '../../api/hooks/documents';
import { useDistributeTasksMutation, useTasksForJob, useUsersForTask } from '../../api/hooks/tasks';
import { ApiTask, Task } from '../../api/typings/tasks';
import tasksColumn from './job-tasks-column';
import filesColumn from './job-files-columns';
import { CreateTask } from 'connectors/tasks';
import { noop } from 'lodash';
import { useStartJobMutation } from 'api/hooks/annotations';
import { getError } from 'shared/helpers/get-error';
import { datasets } from 'api/mocks/datatsets';
import { useNotifications } from 'shared/components/notifications';
import { useAsyncSourceTable } from '../../shared/hooks/async-source-table';
import { JobTable } from '../../shared/components/job/job-table-component';
import { Job } from 'api/typings/jobs';
import qs from 'qs';
import { ANNOTATION_PAGE, PREVIOUS_PAGE_JOB } from '../../shared/constants/general';
import { useHistory } from 'react-router-dom';
type JobDetailViewProps = {
jobId: number;
user?: User;
onEditJobClick: (job: Job) => void;
getActiveTab: (tab: string) => void;
onRowClick: (id: number) => void;
onTaskClick: (id: number) => void;
};
export const JobConnector: React.FC<JobDetailViewProps> = ({
jobId,
user,
onEditJobClick,
getActiveTab
}) => {
const {
pageConfig: pageConfigForFiles,
onPageChange,
totalCount: totalFilesCount,
searchText,
tableValue,
onTableValueChange,
onTotalCountChange: onTotalFilesCountChange
} = usePageTable<FileDocument>('original_name');
const history = useHistory();
const handleExtractionJobClick = (id: number) => {
history.push({
pathname: `/documents/${id}`,
search: qs.stringify({ jobId }),
state: {
previousPage: PREVIOUS_PAGE_JOB,
previousPageUrl: history.location.pathname,
previousPageName: job?.name
}
});
};
const handleTaskClick = (id: number) => {
history.push({
pathname: `${ANNOTATION_PAGE}/${id}`,
search: qs.stringify({ jobId }),
state: {
previousPage: PREVIOUS_PAGE_JOB,
previousPageUrl: history.location.pathname,
previousPageName: job?.name
}
});
};
const {
pageConfig: pageConfigForTask,
onPageChange: onTaskPageChange,
totalCount: totalTaskCount,
tableValue: taskTableValue,
onTableValueChange: onTaskTableValueChange,
onTotalCountChange: onTotalTaskCountChange
} = usePageTable<Task>('id');
const [filesIds, setFilesIds] = useState<Array<number>>([]);
const [tabValue, onTabValueChange] = useState('Files');
const tabValueReference = useRef(tabValue);
tabValueReference.current = tabValue;
useEffect(() => {
getActiveTab(tabValue);
}, [tabValue]);
const { data: job, refetch: refetchJob } = useJobById({ jobId }, { refetchInterval: 10000 });
const svc = useUuiContext();
const { notifyError, notifySuccess } = useNotifications();
const { data: files, isFetching: fetchingFiles } = useDocumentsInJob(
{
page: pageConfigForFiles.page,
filesIds,
size: pageConfigForFiles.pageSize,
searchText
},
{ cacheTime: 0 }
);
useEffect(() => {
if (files?.pagination.total) {
onTotalFilesCountChange(files?.pagination.total);
}
}, [files]);
const {
data: tasks,
refetch: refetchTasks,
isFetching: taskFetching
} = useTasksForJob(
{
user_id: user?.id,
page: pageConfigForTask.page,
size: pageConfigForTask.pageSize,
jobId,
jobType: job?.type
},
{ cacheTime: 0 }
);
useEffect(() => {
if (tasks?.pagination.total) {
onTotalTaskCountChange(tasks?.pagination.total);
}
}, [tasks]);
const startJobMutation = useStartJobMutation();
useEffect(() => {
if (job?.files) {
setFilesIds(job.files);
}
}, [job, setFilesIds]);
const { dataSource: filesDataSource } = useAsyncSourceTable<FileDocument, number>(
fetchingFiles,
files?.data ?? [],
pageConfigForFiles.page,
pageConfigForFiles.pageSize,
filesIds,
searchText
);
const { dataSource: taskDataSource } = useAsyncSourceTable<ApiTask, number>(
taskFetching,
tasks?.data ?? [],
pageConfigForTask.page,
pageConfigForTask.pageSize,
jobId,
user?.id,
tasks
);
const taskView = taskDataSource.useView(taskTableValue, onTaskTableValueChange, {
getRowOptions: (item: ApiTask) => ({
isSelectable: true,
onClick: () => {
if (tabValueReference.current === 'Tasks') {
handleTaskClick(item.id);
}
}
}),
sortBy: (task, sorting) => {
switch (sorting.field) {
case 'id':
return task.id;
case 'name':
return task.user.name;
case 'file_name':
return task.file.name;
case 'status':
return task.status;
case 'is_validation':
return task.is_validation;
case 'pages':
return task.pages.length;
}
}
});
const filesView = filesDataSource.useView(tableValue, onTableValueChange, {
getRowOptions: (item: FileDocument) => ({
isSelectable: true,
onClick: () => {
if (tabValue === 'Files') {
handleExtractionJobClick(item.id);
}
}
})
});
const columnsFiles: DataColumnProps<FileDocument>[] = useMemo(() => {
return filesColumn;
}, []);
const columnsTasks: DataColumnProps<ApiTask>[] = useMemo(() => {
return tasksColumn;
}, []);
const { data: users } = useUsersForTask({ jobId }, {});
const distributeTaskMutation = useDistributeTasksMutation();
const onDistributeTaskClick = async () => {
try {
if (job && users) {
await distributeTaskMutation.mutateAsync({ job, datasets, users });
notifySuccess(<Text>Success</Text>);
}
} catch (error) {
notifyError(<Text>{getError(error)}</Text>);
}
};
const onCreateNewTaskClick = useCallback(async () => {
svc.uuiModals
.show((props) => (
<CreateTask
{...props}
jobId={job?.id}
fileIds={job?.files || []}
annotatorIds={job?.annotators.map(({ id }) => id) || []}
handleCloseModal={() => {
svc.uuiModals.closeAll();
}}
/>
))
.then(noop)
.catch(noop);
}, [job]);
const onStartJob = async () => {
try {
await startJobMutation.mutateAsync({ jobId });
refetchTasks();
svc.uuiNotifications.show(
(props: INotification) => (
<SuccessNotification {...props}>
<Text>Job started successfully!</Text>
</SuccessNotification>
),
{ duration: 2 }
);
refetchJob();
} catch (err: any) {
const error = err as Error;
svc.uuiNotifications.show(
(props: INotification) => (
<ErrorNotification {...props}>
<Text>{getError(error)}</Text>
</ErrorNotification>
),
{ duration: 2 }
);
}
};
return (
<>
<Panel>
<JobDetailViewHeader
type={job?.type || ''}
name={job?.name ? job.name : ''}
onCreateNewTaskClick={onCreateNewTaskClick}
onStartJob={onStartJob}
onEditJobClick={onEditJobClick}
job={job}
onDistributeTaskClick={onDistributeTaskClick}
/>
<FlexRow vPadding={'12'}>
<TabButton
caption={'Files'}
isLinkActive={tabValue === 'Files'}
onClick={() => onTabValueChange('Files')}
size="36"
/>
{job?.type === 'AnnotationJob' ||
job?.type === 'ExtractionWithAnnotationJob' ? (
<TabButton
caption="Tasks"
isLinkActive={tabValue === 'Tasks'}
onClick={() => onTabValueChange('Tasks')}
size="36"
/>
) : (
false
)}
</FlexRow>
{tabValue === `Files` && (
<JobTable
page={pageConfigForFiles.page}
size={pageConfigForFiles.pageSize}
view={filesView}
value={tableValue}
onValueChange={onTableValueChange}
columns={columnsFiles}
totalCount={totalFilesCount}
onPageChange={onPageChange}
/>
)}
{tabValue === `Tasks` && (
<JobTable
page={pageConfigForTask.page}
size={pageConfigForTask.pageSize}
view={taskView}
value={taskTableValue}
onValueChange={onTaskTableValueChange}
columns={columnsTasks}
totalCount={totalTaskCount}
onPageChange={onTaskPageChange}
/>
)}
</Panel>
</>
);
};