import { useContext, useEffect, useState } from 'react'; import { Link, useNavigate, useParams } from 'react-router-dom'; import validator from '@rjsf/validator-ajv8'; // @ts-ignore import { Button, Stack } from '@carbon/react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import Form from '../themes/carbon'; import HttpService from '../services/HttpService'; import ErrorContext from '../contexts/ErrorContext'; import { modifyProcessModelPath } from '../helpers'; 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]; useEffect(() => { const processResult = (result: any) => { setTask(result); HttpService.makeCallToBackend({ path: `/process-instances/${modifyProcessModelPath( result.process_model_identifier )}/${params.process_instance_id}/tasks`, successCallback: setUserTasks, }); }; HttpService.makeCallToBackend({ path: `/tasks/${params.process_instance_id}/${params.task_id}`, successCallback: processResult, // This causes the page to continuously reload // failureCallback: setErrorMessage, }); }, [params]); const processSubmitResult = (result: any) => { setErrorMessage(null); 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) => { setErrorMessage(null); 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; if (userTasks) { userTasksElement = (userTasks as any).map(function getUserTasksElement( userTask: any ) { const taskUrl = `/tasks/${params.process_instance_id}/${userTask.id}`; if (userTask.id === params.task_id) { return <span>{userTask.name}</span>; } if (userTask.state === 'COMPLETED') { return ( <Link to={taskUrl} data-qa={`form-nav-${userTask.name}`}> {userTask.name} </Link> ); } if (userTask.state === 'FUTURE') { return <span style={{ color: 'red' }}>{userTask.name}</span>; } if (userTask.state === 'READY') { return ( <Link to={taskUrl} data-qa={`form-nav-${userTask.name}`}> {userTask.name} - Current </Link> ); } return null; }); } return ( <Stack orientation="horizontal" gap={3}> <Button href="/tasks">Go Back To List</Button> {userTasksElement} </Stack> ); }; 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 />; } return ( <Form formData={taskData} onSubmit={handleFormSubmit} schema={jsonSchema} uiSchema={formUiSchema} validator={validator} > {reactFragmentToHideSubmitButton} </Form> ); }; const instructionsElement = (taskToUse: any) => { let instructions = ''; if (taskToUse.properties.instructionsForEndUser) { instructions = taskToUse.properties.instructionsForEndUser; } return ( <div className="markdown"> <ReactMarkdown remarkPlugins={[remarkGfm]}> {instructions} </ReactMarkdown> </div> ); }; if (task) { const taskToUse = task as any; let statusString = ''; if (taskToUse.state !== 'READY') { statusString = ` ${taskToUse.state}`; } return ( <main> <div>{buildTaskNavigation()}</div> <h3> Task: {taskToUse.title} ({taskToUse.process_model_display_name}) {statusString} </h3> {instructionsElement(taskToUse)} {formElement(taskToUse)} </main> ); } return null; }