2022-10-12 10:21:49 -04:00
|
|
|
import { useContext, useEffect, useState } from 'react';
|
2022-11-17 11:52:57 -05:00
|
|
|
import { useNavigate, useParams } from 'react-router-dom';
|
2022-11-16 18:16:55 -05:00
|
|
|
|
|
|
|
// FIXME: npm install @rjsf/validator-ajv8 and use it as soon as
|
|
|
|
// rawErrors is fixed.
|
2022-11-16 22:50:19 -05:00
|
|
|
// https://react-jsonschema-form.readthedocs.io/en/latest/usage/validation/
|
2022-11-16 18:26:35 -05:00
|
|
|
// https://github.com/rjsf-team/react-jsonschema-form/issues/2309 links to a codesandbox that might be useful to fork
|
|
|
|
// if we wanted to file a defect against rjsf to show the difference between validator-ajv6 and validator-ajv8.
|
|
|
|
// https://github.com/rjsf-team/react-jsonschema-form/blob/main/docs/api-reference/uiSchema.md talks about rawErrors
|
2022-11-16 18:16:55 -05:00
|
|
|
import validator from '@rjsf/validator-ajv6';
|
2022-11-16 13:52:44 -05:00
|
|
|
|
2022-11-17 11:52:57 -05:00
|
|
|
import {
|
|
|
|
TabList,
|
|
|
|
Tab,
|
|
|
|
Tabs,
|
|
|
|
Grid,
|
|
|
|
Column,
|
2022-11-25 12:28:08 -05:00
|
|
|
Button,
|
2022-11-17 11:52:57 -05:00
|
|
|
// @ts-ignore
|
|
|
|
} from '@carbon/react';
|
2022-10-12 10:21:49 -04:00
|
|
|
|
2022-10-12 16:03:28 -04:00
|
|
|
import ReactMarkdown from 'react-markdown';
|
|
|
|
import remarkGfm from 'remark-gfm';
|
2022-11-16 18:16:55 -05:00
|
|
|
// eslint-disable-next-line import/no-named-as-default
|
2022-11-16 13:59:09 -05:00
|
|
|
import Form from '../themes/carbon';
|
2022-10-12 10:21:49 -04:00
|
|
|
import HttpService from '../services/HttpService';
|
|
|
|
import ErrorContext from '../contexts/ErrorContext';
|
2022-11-22 10:56:40 -05:00
|
|
|
import { modifyProcessIdentifierForPathParam } from '../helpers';
|
2022-12-05 16:06:08 -05:00
|
|
|
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
|
|
|
import { PermissionsToCheck } from '../interfaces';
|
|
|
|
import { usePermissionFetcher } from '../hooks/PermissionService';
|
2022-10-12 10:21:49 -04:00
|
|
|
|
|
|
|
export default function TaskShow() {
|
|
|
|
const [task, setTask] = useState(null);
|
|
|
|
const [userTasks, setUserTasks] = useState(null);
|
|
|
|
const params = useParams();
|
|
|
|
const navigate = useNavigate();
|
|
|
|
|
|
|
|
const setErrorMessage = (useContext as any)(ErrorContext)[1];
|
|
|
|
|
2022-12-05 16:06:08 -05:00
|
|
|
const { targetUris } = useUriListForPermissions();
|
|
|
|
const permissionRequestData: PermissionsToCheck = {
|
2022-12-16 11:39:07 -05:00
|
|
|
[targetUris.processInstanceTaskListDataPath]: ['GET'],
|
2022-12-05 16:06:08 -05:00
|
|
|
};
|
|
|
|
const { ability, permissionsLoaded } = usePermissionFetcher(
|
|
|
|
permissionRequestData
|
|
|
|
);
|
|
|
|
|
2022-10-12 10:21:49 -04:00
|
|
|
useEffect(() => {
|
2022-12-05 16:06:08 -05:00
|
|
|
if (permissionsLoaded) {
|
|
|
|
const processResult = (result: any) => {
|
|
|
|
setTask(result);
|
2022-12-16 11:39:07 -05:00
|
|
|
if (ability.can('GET', targetUris.processInstanceTaskListDataPath)) {
|
2022-12-05 16:06:08 -05:00
|
|
|
HttpService.makeCallToBackend({
|
|
|
|
path: `/task-data/${modifyProcessIdentifierForPathParam(
|
|
|
|
result.process_model_identifier
|
|
|
|
)}/${params.process_instance_id}`,
|
|
|
|
successCallback: setUserTasks,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-11-16 11:27:10 -05:00
|
|
|
HttpService.makeCallToBackend({
|
2022-12-05 16:06:08 -05:00
|
|
|
path: `/tasks/${params.process_instance_id}/${params.task_id}`,
|
|
|
|
successCallback: processResult,
|
|
|
|
// This causes the page to continuously reload
|
|
|
|
// failureCallback: setErrorMessage,
|
2022-11-16 11:27:10 -05:00
|
|
|
});
|
2022-12-05 16:06:08 -05:00
|
|
|
}
|
|
|
|
}, [params, permissionsLoaded, ability, targetUris]);
|
2022-10-12 10:21:49 -04:00
|
|
|
|
|
|
|
const processSubmitResult = (result: any) => {
|
2022-10-18 16:41:13 -04:00
|
|
|
setErrorMessage(null);
|
2022-10-12 10:21:49 -04:00
|
|
|
if (result.ok) {
|
|
|
|
navigate(`/tasks`);
|
|
|
|
} else if (result.process_instance_id) {
|
|
|
|
navigate(`/tasks/${result.process_instance_id}/${result.id}`);
|
|
|
|
} else {
|
|
|
|
setErrorMessage(`Received unexpected error: ${result.message}`);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleFormSubmit = (event: any) => {
|
2022-10-18 16:41:13 -04:00
|
|
|
setErrorMessage(null);
|
2022-10-12 10:21:49 -04:00
|
|
|
const dataToSubmit = event.formData;
|
|
|
|
delete dataToSubmit.isManualTask;
|
|
|
|
HttpService.makeCallToBackend({
|
|
|
|
path: `/tasks/${params.process_instance_id}/${params.task_id}`,
|
|
|
|
successCallback: processSubmitResult,
|
|
|
|
failureCallback: setErrorMessage,
|
|
|
|
httpMethod: 'PUT',
|
|
|
|
postBody: dataToSubmit,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const buildTaskNavigation = () => {
|
|
|
|
let userTasksElement;
|
2022-11-17 11:52:57 -05:00
|
|
|
let selectedTabIndex = 0;
|
2022-10-12 10:21:49 -04:00
|
|
|
if (userTasks) {
|
|
|
|
userTasksElement = (userTasks as any).map(function getUserTasksElement(
|
2022-11-17 11:52:57 -05:00
|
|
|
userTask: any,
|
|
|
|
index: number
|
2022-10-12 10:21:49 -04:00
|
|
|
) {
|
|
|
|
const taskUrl = `/tasks/${params.process_instance_id}/${userTask.id}`;
|
|
|
|
if (userTask.id === params.task_id) {
|
2022-11-17 11:52:57 -05:00
|
|
|
selectedTabIndex = index;
|
|
|
|
return <Tab selected>{userTask.title}</Tab>;
|
2022-10-12 10:21:49 -04:00
|
|
|
}
|
|
|
|
if (userTask.state === 'COMPLETED') {
|
|
|
|
return (
|
2022-11-17 11:52:57 -05:00
|
|
|
<Tab
|
|
|
|
onClick={() => navigate(taskUrl)}
|
|
|
|
data-qa={`form-nav-${userTask.name}`}
|
|
|
|
>
|
|
|
|
{userTask.title}
|
|
|
|
</Tab>
|
2022-10-12 10:21:49 -04:00
|
|
|
);
|
|
|
|
}
|
|
|
|
if (userTask.state === 'FUTURE') {
|
2022-11-17 11:52:57 -05:00
|
|
|
return <Tab disabled>{userTask.title}</Tab>;
|
2022-10-12 10:21:49 -04:00
|
|
|
}
|
|
|
|
if (userTask.state === 'READY') {
|
|
|
|
return (
|
2022-11-17 11:52:57 -05:00
|
|
|
<Tab
|
|
|
|
onClick={() => navigate(taskUrl)}
|
|
|
|
data-qa={`form-nav-${userTask.name}`}
|
|
|
|
>
|
|
|
|
{userTask.title}
|
|
|
|
</Tab>
|
2022-10-12 10:21:49 -04:00
|
|
|
);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
});
|
2022-12-05 16:06:08 -05:00
|
|
|
return (
|
|
|
|
<Tabs
|
|
|
|
title="Steps in this process instance involving people"
|
|
|
|
selectedIndex={selectedTabIndex}
|
|
|
|
>
|
|
|
|
<TabList aria-label="List of tabs" contained>
|
|
|
|
{userTasksElement}
|
|
|
|
</TabList>
|
|
|
|
</Tabs>
|
|
|
|
);
|
2022-10-12 10:21:49 -04:00
|
|
|
}
|
2022-12-05 16:06:08 -05:00
|
|
|
return null;
|
2022-10-12 10:21:49 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
const formElement = (taskToUse: any) => {
|
|
|
|
let formUiSchema;
|
|
|
|
let taskData = taskToUse.data;
|
|
|
|
let jsonSchema = taskToUse.form_schema;
|
|
|
|
let reactFragmentToHideSubmitButton = null;
|
|
|
|
if (taskToUse.type === 'Manual Task') {
|
|
|
|
taskData = {};
|
|
|
|
jsonSchema = {
|
|
|
|
type: 'object',
|
|
|
|
required: [],
|
|
|
|
properties: {
|
|
|
|
isManualTask: {
|
|
|
|
type: 'boolean',
|
|
|
|
title: 'Is ManualTask',
|
|
|
|
default: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
formUiSchema = {
|
|
|
|
isManualTask: {
|
|
|
|
'ui:widget': 'hidden',
|
|
|
|
},
|
|
|
|
};
|
|
|
|
} else if (taskToUse.form_ui_schema) {
|
|
|
|
formUiSchema = JSON.parse(taskToUse.form_ui_schema);
|
|
|
|
}
|
|
|
|
if (taskToUse.state !== 'READY') {
|
|
|
|
formUiSchema = Object.assign(formUiSchema || {}, {
|
|
|
|
'ui:readonly': true,
|
|
|
|
});
|
|
|
|
|
|
|
|
// It doesn't seem as if Form allows for removing the default submit button
|
|
|
|
// so passing a blank fragment or children seem to do it though
|
|
|
|
//
|
|
|
|
// from: https://github.com/rjsf-team/react-jsonschema-form/issues/1602
|
|
|
|
reactFragmentToHideSubmitButton = <div />;
|
|
|
|
}
|
|
|
|
|
2022-12-16 16:51:49 -05:00
|
|
|
if (taskToUse.type === 'Manual Task' && taskToUse.state === 'READY') {
|
2022-11-25 12:28:08 -05:00
|
|
|
reactFragmentToHideSubmitButton = (
|
|
|
|
<div>
|
|
|
|
<Button type="submit">Continue</Button>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-10-12 10:21:49 -04:00
|
|
|
return (
|
2022-11-17 11:52:57 -05:00
|
|
|
<Grid fullWidth condensed>
|
|
|
|
<Column md={5} lg={8} sm={4}>
|
|
|
|
<Form
|
|
|
|
formData={taskData}
|
|
|
|
onSubmit={handleFormSubmit}
|
|
|
|
schema={jsonSchema}
|
|
|
|
uiSchema={formUiSchema}
|
|
|
|
validator={validator}
|
|
|
|
>
|
|
|
|
{reactFragmentToHideSubmitButton}
|
|
|
|
</Form>
|
|
|
|
</Column>
|
|
|
|
</Grid>
|
2022-10-12 10:21:49 -04:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2022-10-12 16:03:28 -04:00
|
|
|
const instructionsElement = (taskToUse: any) => {
|
|
|
|
let instructions = '';
|
2022-10-31 16:51:27 -04:00
|
|
|
if (taskToUse.properties.instructionsForEndUser) {
|
2022-10-12 16:03:28 -04:00
|
|
|
instructions = taskToUse.properties.instructionsForEndUser;
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
<div className="markdown">
|
|
|
|
<ReactMarkdown remarkPlugins={[remarkGfm]}>
|
|
|
|
{instructions}
|
|
|
|
</ReactMarkdown>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2022-12-05 16:06:08 -05:00
|
|
|
if (task) {
|
2022-10-12 10:21:49 -04:00
|
|
|
const taskToUse = task as any;
|
|
|
|
let statusString = '';
|
|
|
|
if (taskToUse.state !== 'READY') {
|
|
|
|
statusString = ` ${taskToUse.state}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<main>
|
2022-10-12 16:03:28 -04:00
|
|
|
<div>{buildTaskNavigation()}</div>
|
2022-10-12 10:21:49 -04:00
|
|
|
<h3>
|
|
|
|
Task: {taskToUse.title} ({taskToUse.process_model_display_name})
|
|
|
|
{statusString}
|
|
|
|
</h3>
|
2022-10-12 16:03:28 -04:00
|
|
|
{instructionsElement(taskToUse)}
|
2022-10-12 10:21:49 -04:00
|
|
|
{formElement(taskToUse)}
|
|
|
|
</main>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|