added a loading icon on task show page to avoid blank page when loadi… (#411)
* added a loading icon on task show page to avoid blank page when loading large datasets w/ burnettk * fixed broken tests w/ burnettk --------- Co-authored-by: jasquat <jasquat@users.noreply.github.com>
This commit is contained in:
parent
43beb916a3
commit
cdaf2ea6c5
|
@ -1904,6 +1904,12 @@ paths:
|
||||||
description: The unique id of an existing process instance.
|
description: The unique id of an existing process instance.
|
||||||
schema:
|
schema:
|
||||||
type: integer
|
type: integer
|
||||||
|
- name: with_form_data
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: Include task data for forms
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- Tasks
|
- Tasks
|
||||||
|
|
|
@ -300,7 +300,9 @@ def task_assign(
|
||||||
return make_response(jsonify({"ok": True}), 200)
|
return make_response(jsonify({"ok": True}), 200)
|
||||||
|
|
||||||
|
|
||||||
def task_show(process_instance_id: int, task_guid: str = "next") -> flask.wrappers.Response:
|
def task_show(
|
||||||
|
process_instance_id: int, task_guid: str = "next", with_form_data: bool = False
|
||||||
|
) -> flask.wrappers.Response:
|
||||||
process_instance = _find_process_instance_by_id_or_raise(process_instance_id)
|
process_instance = _find_process_instance_by_id_or_raise(process_instance_id)
|
||||||
|
|
||||||
if process_instance.status == ProcessInstanceStatus.suspended.value:
|
if process_instance.status == ProcessInstanceStatus.suspended.value:
|
||||||
|
@ -314,20 +316,8 @@ def task_show(process_instance_id: int, task_guid: str = "next") -> flask.wrappe
|
||||||
process_instance.process_model_identifier,
|
process_instance.process_model_identifier,
|
||||||
)
|
)
|
||||||
|
|
||||||
form_schema_file_name = ""
|
|
||||||
form_ui_schema_file_name = ""
|
|
||||||
|
|
||||||
task_model = _get_task_model_from_guid_or_raise(task_guid, process_instance_id)
|
task_model = _get_task_model_from_guid_or_raise(task_guid, process_instance_id)
|
||||||
task_definition = task_model.task_definition
|
task_definition = task_model.task_definition
|
||||||
extensions = TaskService.get_extensions_from_task_model(task_model)
|
|
||||||
task_model.signal_buttons = TaskService.get_ready_signals_with_button_labels(process_instance_id, task_model.guid)
|
|
||||||
|
|
||||||
if "properties" in extensions:
|
|
||||||
properties = extensions["properties"]
|
|
||||||
if "formJsonSchemaFilename" in properties:
|
|
||||||
form_schema_file_name = properties["formJsonSchemaFilename"]
|
|
||||||
if "formUiSchemaFilename" in properties:
|
|
||||||
form_ui_schema_file_name = properties["formUiSchemaFilename"]
|
|
||||||
|
|
||||||
can_complete = False
|
can_complete = False
|
||||||
try:
|
try:
|
||||||
|
@ -336,71 +326,89 @@ def task_show(process_instance_id: int, task_guid: str = "next") -> flask.wrappe
|
||||||
except (HumanTaskNotFoundError, UserDoesNotHaveAccessToTaskError, HumanTaskAlreadyCompletedError):
|
except (HumanTaskNotFoundError, UserDoesNotHaveAccessToTaskError, HumanTaskAlreadyCompletedError):
|
||||||
can_complete = False
|
can_complete = False
|
||||||
|
|
||||||
task_draft_data = TaskService.task_draft_data_from_task_model(task_model)
|
|
||||||
|
|
||||||
saved_form_data = None
|
|
||||||
if task_draft_data is not None:
|
|
||||||
saved_form_data = task_draft_data.get_saved_form_data()
|
|
||||||
|
|
||||||
task_model.data = task_model.get_data()
|
|
||||||
task_model.saved_form_data = saved_form_data
|
|
||||||
task_model.process_model_display_name = process_model.display_name
|
task_model.process_model_display_name = process_model.display_name
|
||||||
task_model.process_model_identifier = process_model.id
|
task_model.process_model_identifier = process_model.id
|
||||||
task_model.typename = task_definition.typename
|
task_model.typename = task_definition.typename
|
||||||
task_model.can_complete = can_complete
|
task_model.can_complete = can_complete
|
||||||
task_process_identifier = task_model.bpmn_process.bpmn_process_definition.bpmn_identifier
|
|
||||||
task_model.name_for_display = TaskService.get_name_for_display(task_definition)
|
task_model.name_for_display = TaskService.get_name_for_display(task_definition)
|
||||||
|
|
||||||
process_model_with_form = process_model
|
if with_form_data:
|
||||||
|
task_process_identifier = task_model.bpmn_process.bpmn_process_definition.bpmn_identifier
|
||||||
|
process_model_with_form = process_model
|
||||||
|
|
||||||
refs = SpecFileService.get_references_for_process(process_model_with_form)
|
refs = SpecFileService.get_references_for_process(process_model_with_form)
|
||||||
all_processes = [i.identifier for i in refs]
|
all_processes = [i.identifier for i in refs]
|
||||||
if task_process_identifier not in all_processes:
|
if task_process_identifier not in all_processes:
|
||||||
top_bpmn_process = TaskService.bpmn_process_for_called_activity_or_top_level_process(task_model)
|
top_bpmn_process = TaskService.bpmn_process_for_called_activity_or_top_level_process(task_model)
|
||||||
bpmn_file_full_path = ProcessInstanceProcessor.bpmn_file_full_path_from_bpmn_process_identifier(
|
bpmn_file_full_path = ProcessInstanceProcessor.bpmn_file_full_path_from_bpmn_process_identifier(
|
||||||
top_bpmn_process.bpmn_process_definition.bpmn_identifier
|
top_bpmn_process.bpmn_process_definition.bpmn_identifier
|
||||||
)
|
)
|
||||||
relative_path = os.path.relpath(bpmn_file_full_path, start=FileSystemService.root_path())
|
relative_path = os.path.relpath(bpmn_file_full_path, start=FileSystemService.root_path())
|
||||||
process_model_relative_path = os.path.dirname(relative_path)
|
process_model_relative_path = os.path.dirname(relative_path)
|
||||||
process_model_with_form = ProcessModelService.get_process_model_from_relative_path(process_model_relative_path)
|
process_model_with_form = ProcessModelService.get_process_model_from_relative_path(
|
||||||
|
process_model_relative_path
|
||||||
if task_definition.typename == "UserTask":
|
|
||||||
if not form_schema_file_name:
|
|
||||||
raise (
|
|
||||||
ApiError(
|
|
||||||
error_code="missing_form_file",
|
|
||||||
message=(
|
|
||||||
f"Cannot find a form file for process_instance_id: {process_instance_id}, task_guid:"
|
|
||||||
f" {task_guid}"
|
|
||||||
),
|
|
||||||
status_code=400,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
form_dict = _prepare_form_data(
|
form_schema_file_name = ""
|
||||||
form_schema_file_name,
|
form_ui_schema_file_name = ""
|
||||||
task_model,
|
extensions = TaskService.get_extensions_from_task_model(task_model)
|
||||||
process_model_with_form,
|
task_model.signal_buttons = TaskService.get_ready_signals_with_button_labels(
|
||||||
|
process_instance_id, task_model.guid
|
||||||
)
|
)
|
||||||
|
|
||||||
if task_model.data:
|
if "properties" in extensions:
|
||||||
_update_form_schema_with_task_data_as_needed(form_dict, task_model)
|
properties = extensions["properties"]
|
||||||
|
if "formJsonSchemaFilename" in properties:
|
||||||
|
form_schema_file_name = properties["formJsonSchemaFilename"]
|
||||||
|
if "formUiSchemaFilename" in properties:
|
||||||
|
form_ui_schema_file_name = properties["formUiSchemaFilename"]
|
||||||
|
|
||||||
if form_dict:
|
task_draft_data = TaskService.task_draft_data_from_task_model(task_model)
|
||||||
task_model.form_schema = form_dict
|
|
||||||
|
|
||||||
if form_ui_schema_file_name:
|
saved_form_data = None
|
||||||
ui_form_contents = _prepare_form_data(
|
if task_draft_data is not None:
|
||||||
form_ui_schema_file_name,
|
saved_form_data = task_draft_data.get_saved_form_data()
|
||||||
|
|
||||||
|
task_model.data = task_model.get_data()
|
||||||
|
task_model.saved_form_data = saved_form_data
|
||||||
|
if task_definition.typename == "UserTask":
|
||||||
|
if not form_schema_file_name:
|
||||||
|
raise (
|
||||||
|
ApiError(
|
||||||
|
error_code="missing_form_file",
|
||||||
|
message=(
|
||||||
|
f"Cannot find a form file for process_instance_id: {process_instance_id}, task_guid:"
|
||||||
|
f" {task_guid}"
|
||||||
|
),
|
||||||
|
status_code=400,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
form_dict = _prepare_form_data(
|
||||||
|
form_schema_file_name,
|
||||||
task_model,
|
task_model,
|
||||||
process_model_with_form,
|
process_model_with_form,
|
||||||
)
|
)
|
||||||
if ui_form_contents:
|
|
||||||
task_model.form_ui_schema = ui_form_contents
|
|
||||||
|
|
||||||
_munge_form_ui_schema_based_on_hidden_fields_in_task_data(task_model)
|
if task_model.data:
|
||||||
JinjaService.render_instructions_for_end_user(task_model, extensions)
|
_update_form_schema_with_task_data_as_needed(form_dict, task_model)
|
||||||
task_model.extensions = extensions
|
|
||||||
|
if form_dict:
|
||||||
|
task_model.form_schema = form_dict
|
||||||
|
|
||||||
|
if form_ui_schema_file_name:
|
||||||
|
ui_form_contents = _prepare_form_data(
|
||||||
|
form_ui_schema_file_name,
|
||||||
|
task_model,
|
||||||
|
process_model_with_form,
|
||||||
|
)
|
||||||
|
if ui_form_contents:
|
||||||
|
task_model.form_ui_schema = ui_form_contents
|
||||||
|
|
||||||
|
_munge_form_ui_schema_based_on_hidden_fields_in_task_data(task_model)
|
||||||
|
JinjaService.render_instructions_for_end_user(task_model, extensions)
|
||||||
|
task_model.extensions = extensions
|
||||||
|
|
||||||
return make_response(jsonify(task_model), 200)
|
return make_response(jsonify(task_model), 200)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ class TestForGoodErrors(BaseTest):
|
||||||
assert len(human_tasks) > 0, "No human tasks found for process."
|
assert len(human_tasks) > 0, "No human tasks found for process."
|
||||||
human_task = human_tasks[0]
|
human_task = human_tasks[0]
|
||||||
response = client.get(
|
response = client.get(
|
||||||
f"/v1.0/tasks/{process_instance_id}/{human_task.task_id}",
|
f"/v1.0/tasks/{process_instance_id}/{human_task.task_id}?with_form_data=true",
|
||||||
headers=self.logged_in_headers(with_super_admin_user),
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
|
@ -55,7 +55,7 @@ class TestTasksController(BaseTest):
|
||||||
assert len(human_tasks) == 1
|
assert len(human_tasks) == 1
|
||||||
human_task = human_tasks[0]
|
human_task = human_tasks[0]
|
||||||
response = client.get(
|
response = client.get(
|
||||||
f"/v1.0/tasks/{process_instance_id}/{human_task.task_id}",
|
f"/v1.0/tasks/{process_instance_id}/{human_task.task_id}?with_form_data=true",
|
||||||
headers=self.logged_in_headers(with_super_admin_user),
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
)
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
@ -173,7 +173,7 @@ class TestTasksController(BaseTest):
|
||||||
assert json_results[1]["task"]["title"] == "Manual Task"
|
assert json_results[1]["task"]["title"] == "Manual Task"
|
||||||
|
|
||||||
response = client.put(
|
response = client.put(
|
||||||
f"/v1.0/tasks/{process_instance_id}/{json_results[1]['task']['id']}",
|
f"/v1.0/tasks/{process_instance_id}/{json_results[1]['task']['id']}?with_form_data=true",
|
||||||
headers=headers,
|
headers=headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -204,7 +204,7 @@ class TestTasksController(BaseTest):
|
||||||
|
|
||||||
# Complete task as the finance user.
|
# Complete task as the finance user.
|
||||||
response = client.put(
|
response = client.put(
|
||||||
f"/v1.0/tasks/{process_instance_id}/{json_results[0]['task']['id']}",
|
f"/v1.0/tasks/{process_instance_id}/{json_results[0]['task']['id']}?with_form_data=true",
|
||||||
headers=self.logged_in_headers(finance_user),
|
headers=self.logged_in_headers(finance_user),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -363,7 +363,7 @@ class TestTasksController(BaseTest):
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
response = client.get(
|
response = client.get(
|
||||||
f"/v1.0/tasks/{process_instance_id}/{task_id}",
|
f"/v1.0/tasks/{process_instance_id}/{task_id}?with_form_data=true",
|
||||||
headers=self.logged_in_headers(with_super_admin_user),
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
)
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
@ -380,7 +380,7 @@ class TestTasksController(BaseTest):
|
||||||
|
|
||||||
# ensure draft data is deleted after submitting the task
|
# ensure draft data is deleted after submitting the task
|
||||||
response = client.get(
|
response = client.get(
|
||||||
f"/v1.0/tasks/{process_instance_id}/{task_id}",
|
f"/v1.0/tasks/{process_instance_id}/{task_id}?with_form_data=true",
|
||||||
headers=self.logged_in_headers(with_super_admin_user),
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
)
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
|
@ -51,8 +51,8 @@ export interface SignalButton {
|
||||||
event: EventDefinition;
|
event: EventDefinition;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: merge with ProcessInstanceTask
|
// Task withouth task data and form info - just the basics
|
||||||
export interface Task {
|
export interface BasicTask {
|
||||||
id: number;
|
id: number;
|
||||||
guid: string;
|
guid: string;
|
||||||
process_instance_id: number;
|
process_instance_id: number;
|
||||||
|
@ -60,25 +60,30 @@ export interface Task {
|
||||||
bpmn_name?: string;
|
bpmn_name?: string;
|
||||||
bpmn_process_direct_parent_guid: string;
|
bpmn_process_direct_parent_guid: string;
|
||||||
bpmn_process_definition_identifier: string;
|
bpmn_process_definition_identifier: string;
|
||||||
data: any;
|
|
||||||
state: string;
|
state: string;
|
||||||
typename: string;
|
typename: string;
|
||||||
properties_json: TaskPropertiesJson;
|
properties_json: TaskPropertiesJson;
|
||||||
task_definition_properties_json: TaskDefinitionPropertiesJson;
|
task_definition_properties_json: TaskDefinitionPropertiesJson;
|
||||||
|
|
||||||
event_definition?: EventDefinition;
|
|
||||||
|
|
||||||
process_model_display_name: string;
|
process_model_display_name: string;
|
||||||
process_model_identifier: string;
|
process_model_identifier: string;
|
||||||
name_for_display: string;
|
name_for_display: string;
|
||||||
can_complete: boolean;
|
can_complete: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: merge with ProcessInstanceTask
|
||||||
|
// Currently used like TaskModel in backend
|
||||||
|
export interface Task extends BasicTask {
|
||||||
|
data: any;
|
||||||
form_schema: any;
|
form_schema: any;
|
||||||
form_ui_schema: any;
|
form_ui_schema: any;
|
||||||
signal_buttons: SignalButton[];
|
signal_buttons: SignalButton[];
|
||||||
|
|
||||||
|
event_definition?: EventDefinition;
|
||||||
saved_form_data?: any;
|
saved_form_data?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Currently used like ApiTask in backend
|
||||||
export interface ProcessInstanceTask {
|
export interface ProcessInstanceTask {
|
||||||
id: string;
|
id: string;
|
||||||
task_id: string;
|
task_id: string;
|
||||||
|
|
|
@ -2,15 +2,7 @@ import React, { useEffect, useState } from 'react';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import validator from '@rjsf/validator-ajv8';
|
import validator from '@rjsf/validator-ajv8';
|
||||||
|
|
||||||
import {
|
import { Grid, Column, Button, ButtonSet, Loading } from '@carbon/react';
|
||||||
TabList,
|
|
||||||
Tab,
|
|
||||||
Tabs,
|
|
||||||
Grid,
|
|
||||||
Column,
|
|
||||||
Button,
|
|
||||||
ButtonSet,
|
|
||||||
} from '@carbon/react';
|
|
||||||
|
|
||||||
import { useDebouncedCallback } from 'use-debounce';
|
import { useDebouncedCallback } from 'use-debounce';
|
||||||
import { Form } from '../rjsf/carbon_theme';
|
import { Form } from '../rjsf/carbon_theme';
|
||||||
|
@ -21,7 +13,7 @@ import {
|
||||||
modifyProcessIdentifierForPathParam,
|
modifyProcessIdentifierForPathParam,
|
||||||
recursivelyChangeNullAndUndefined,
|
recursivelyChangeNullAndUndefined,
|
||||||
} from '../helpers';
|
} from '../helpers';
|
||||||
import { EventDefinition, Task } from '../interfaces';
|
import { BasicTask, EventDefinition, Task } from '../interfaces';
|
||||||
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
||||||
import InstructionsForEndUser from '../components/InstructionsForEndUser';
|
import InstructionsForEndUser from '../components/InstructionsForEndUser';
|
||||||
import TypeaheadWidget from '../rjsf/custom_widgets/TypeaheadWidget/TypeaheadWidget';
|
import TypeaheadWidget from '../rjsf/custom_widgets/TypeaheadWidget/TypeaheadWidget';
|
||||||
|
@ -29,8 +21,8 @@ import DateRangePickerWidget from '../rjsf/custom_widgets/DateRangePicker/DateRa
|
||||||
import { DATE_RANGE_DELIMITER } from '../config';
|
import { DATE_RANGE_DELIMITER } from '../config';
|
||||||
|
|
||||||
export default function TaskShow() {
|
export default function TaskShow() {
|
||||||
const [task, setTask] = useState<Task | null>(null);
|
const [basicTask, setBasicTask] = useState<BasicTask | null>(null);
|
||||||
const [userTasks] = useState(null);
|
const [taskWithTaskData, setTaskWithTaskData] = useState<Task | null>(null);
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [formButtonsDisabled, setFormButtonsDisabled] = useState(false);
|
const [formButtonsDisabled, setFormButtonsDisabled] = useState(false);
|
||||||
|
@ -48,7 +40,7 @@ export default function TaskShow() {
|
||||||
|
|
||||||
// if a user can complete a task then the for-me page should
|
// if a user can complete a task then the for-me page should
|
||||||
// always work for them so use that since it will work in all cases
|
// always work for them so use that since it will work in all cases
|
||||||
const navigateToInterstitial = (myTask: Task) => {
|
const navigateToInterstitial = (myTask: BasicTask) => {
|
||||||
navigate(
|
navigate(
|
||||||
`/admin/process-instances/for-me/${modifyProcessIdentifierForPathParam(
|
`/admin/process-instances/for-me/${modifyProcessIdentifierForPathParam(
|
||||||
myTask.process_model_identifier
|
myTask.process_model_identifier
|
||||||
|
@ -57,20 +49,29 @@ export default function TaskShow() {
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const processResult = (result: Task) => {
|
const processBasicTaskResult = (result: BasicTask) => {
|
||||||
setTask(result);
|
setBasicTask(result);
|
||||||
|
if (!result.can_complete) {
|
||||||
|
navigateToInterstitial(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const processTaskWithDataResult = (result: Task) => {
|
||||||
|
setTaskWithTaskData(result);
|
||||||
|
|
||||||
// convert null back to undefined so rjsf doesn't attempt to incorrectly validate them
|
// convert null back to undefined so rjsf doesn't attempt to incorrectly validate them
|
||||||
const taskDataToUse = result.saved_form_data || result.data;
|
const taskDataToUse = result.saved_form_data || result.data;
|
||||||
setTaskData(recursivelyChangeNullAndUndefined(taskDataToUse, undefined));
|
setTaskData(recursivelyChangeNullAndUndefined(taskDataToUse, undefined));
|
||||||
setFormButtonsDisabled(false);
|
setFormButtonsDisabled(false);
|
||||||
if (!result.can_complete) {
|
|
||||||
navigateToInterstitial(result);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
HttpService.makeCallToBackend({
|
HttpService.makeCallToBackend({
|
||||||
path: `/tasks/${params.process_instance_id}/${params.task_id}`,
|
path: `/tasks/${params.process_instance_id}/${params.task_id}`,
|
||||||
successCallback: processResult,
|
successCallback: processBasicTaskResult,
|
||||||
|
failureCallback: addError,
|
||||||
|
});
|
||||||
|
HttpService.makeCallToBackend({
|
||||||
|
path: `/tasks/${params.process_instance_id}/${params.task_id}?with_form_data=true`,
|
||||||
|
successCallback: processTaskWithDataResult,
|
||||||
failureCallback: addError,
|
failureCallback: addError,
|
||||||
});
|
});
|
||||||
// FIXME: not sure what to do about addError. adding it to this array causes the page to endlessly reload
|
// FIXME: not sure what to do about addError. adding it to this array causes the page to endlessly reload
|
||||||
|
@ -83,7 +84,7 @@ export default function TaskShow() {
|
||||||
// https://github.com/sartography/spiff-arena/blob/182f56a1ad23ce780e8f5b0ed00efac3e6ad117b/spiffworkflow-frontend/src/routes/TaskShow.tsx#L329
|
// https://github.com/sartography/spiff-arena/blob/182f56a1ad23ce780e8f5b0ed00efac3e6ad117b/spiffworkflow-frontend/src/routes/TaskShow.tsx#L329
|
||||||
const autoSaveTaskData = (formData: any, successCallback?: Function) => {
|
const autoSaveTaskData = (formData: any, successCallback?: Function) => {
|
||||||
// save-draft gets called when a manual task form loads but there's no data to save so don't do it
|
// save-draft gets called when a manual task form loads but there's no data to save so don't do it
|
||||||
if (task?.typename === 'ManualTask') {
|
if (taskWithTaskData?.typename === 'ManualTask') {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
let successCallbackToUse = successCallback;
|
let successCallbackToUse = successCallback;
|
||||||
|
@ -180,7 +181,7 @@ export default function TaskShow() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSignalSubmit = (event: EventDefinition) => {
|
const handleSignalSubmit = (event: EventDefinition) => {
|
||||||
if (formButtonsDisabled || !task) {
|
if (formButtonsDisabled || !taskWithTaskData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setFormButtonsDisabled(true);
|
setFormButtonsDisabled(true);
|
||||||
|
@ -195,58 +196,6 @@ export default function TaskShow() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const buildTaskNavigation = () => {
|
|
||||||
let userTasksElement;
|
|
||||||
let selectedTabIndex = 0;
|
|
||||||
if (userTasks) {
|
|
||||||
userTasksElement = (userTasks as any).map(function getUserTasksElement(
|
|
||||||
userTask: any,
|
|
||||||
index: number
|
|
||||||
) {
|
|
||||||
const taskUrl = `/tasks/${params.process_instance_id}/${userTask.id}`;
|
|
||||||
if (userTask.id === params.task_id) {
|
|
||||||
selectedTabIndex = index;
|
|
||||||
return <Tab selected>{userTask.name_for_display}</Tab>;
|
|
||||||
}
|
|
||||||
if (userTask.state === 'COMPLETED') {
|
|
||||||
return (
|
|
||||||
<Tab
|
|
||||||
onClick={() => navigate(taskUrl)}
|
|
||||||
data-qa={`form-nav-${userTask.name}`}
|
|
||||||
>
|
|
||||||
{userTask.name_for_display}
|
|
||||||
</Tab>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (userTask.state === 'FUTURE') {
|
|
||||||
return <Tab formButtonsDisabled>{userTask.name_for_display}</Tab>;
|
|
||||||
}
|
|
||||||
if (userTask.state === 'READY') {
|
|
||||||
return (
|
|
||||||
<Tab
|
|
||||||
onClick={() => navigate(taskUrl)}
|
|
||||||
data-qa={`form-nav-${userTask.name}`}
|
|
||||||
>
|
|
||||||
{userTask.name_for_display}
|
|
||||||
</Tab>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<Tabs
|
|
||||||
title="Steps in this process instance involving people"
|
|
||||||
selectedIndex={selectedTabIndex}
|
|
||||||
>
|
|
||||||
<TabList aria-label="List of tabs" contained>
|
|
||||||
{userTasksElement}
|
|
||||||
</TabList>
|
|
||||||
</Tabs>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const formatDateString = (dateString?: string) => {
|
const formatDateString = (dateString?: string) => {
|
||||||
let dateObject = new Date();
|
let dateObject = new Date();
|
||||||
if (dateString) {
|
if (dateString) {
|
||||||
|
@ -406,14 +355,14 @@ export default function TaskShow() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const formElement = () => {
|
const formElement = () => {
|
||||||
if (!task) {
|
if (!taskWithTaskData) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let formUiSchema;
|
let formUiSchema;
|
||||||
let jsonSchema = task.form_schema;
|
let jsonSchema = taskWithTaskData.form_schema;
|
||||||
let reactFragmentToHideSubmitButton = null;
|
let reactFragmentToHideSubmitButton = null;
|
||||||
if (task.typename === 'ManualTask') {
|
if (taskWithTaskData.typename === 'ManualTask') {
|
||||||
jsonSchema = {
|
jsonSchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
required: [],
|
required: [],
|
||||||
|
@ -430,10 +379,10 @@ export default function TaskShow() {
|
||||||
'ui:widget': 'hidden',
|
'ui:widget': 'hidden',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
} else if (task.form_ui_schema) {
|
} else if (taskWithTaskData.form_ui_schema) {
|
||||||
formUiSchema = task.form_ui_schema;
|
formUiSchema = taskWithTaskData.form_ui_schema;
|
||||||
}
|
}
|
||||||
if (task.state !== 'READY') {
|
if (taskWithTaskData.state !== 'READY') {
|
||||||
formUiSchema = Object.assign(formUiSchema || {}, {
|
formUiSchema = Object.assign(formUiSchema || {}, {
|
||||||
'ui:readonly': true,
|
'ui:readonly': true,
|
||||||
});
|
});
|
||||||
|
@ -445,12 +394,12 @@ export default function TaskShow() {
|
||||||
reactFragmentToHideSubmitButton = <div />;
|
reactFragmentToHideSubmitButton = <div />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task.state === 'READY') {
|
if (taskWithTaskData.state === 'READY') {
|
||||||
let submitButtonText = 'Submit';
|
let submitButtonText = 'Submit';
|
||||||
let closeButton = null;
|
let closeButton = null;
|
||||||
if (task.typename === 'ManualTask') {
|
if (taskWithTaskData.typename === 'ManualTask') {
|
||||||
submitButtonText = 'Continue';
|
submitButtonText = 'Continue';
|
||||||
} else if (task.typename === 'UserTask') {
|
} else if (taskWithTaskData.typename === 'UserTask') {
|
||||||
closeButton = (
|
closeButton = (
|
||||||
<Button
|
<Button
|
||||||
id="close-button"
|
id="close-button"
|
||||||
|
@ -474,7 +423,7 @@ export default function TaskShow() {
|
||||||
</Button>
|
</Button>
|
||||||
{closeButton}
|
{closeButton}
|
||||||
<>
|
<>
|
||||||
{task.signal_buttons.map((signal) => (
|
{taskWithTaskData.signal_buttons.map((signal) => (
|
||||||
<Button
|
<Button
|
||||||
name="signal.signal"
|
name="signal.signal"
|
||||||
disabled={formButtonsDisabled}
|
disabled={formButtonsDisabled}
|
||||||
|
@ -532,35 +481,52 @@ export default function TaskShow() {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (task) {
|
const getLoadingIcon = () => {
|
||||||
|
const style = { margin: '50px 0 50px 50px' };
|
||||||
|
return (
|
||||||
|
<Loading
|
||||||
|
description="Active loading indicator"
|
||||||
|
withOverlay={false}
|
||||||
|
style={style}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const pageElements = [];
|
||||||
|
if (basicTask) {
|
||||||
let statusString = '';
|
let statusString = '';
|
||||||
if (task.state !== 'READY') {
|
if (basicTask.state !== 'READY') {
|
||||||
statusString = ` ${task.state}`;
|
statusString = ` ${basicTask.state}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
pageElements.push(
|
||||||
<>
|
<ProcessBreadcrumb
|
||||||
<ProcessBreadcrumb
|
hotCrumbs={[
|
||||||
hotCrumbs={[
|
[
|
||||||
[
|
`Process Instance Id: ${params.process_instance_id}`,
|
||||||
`Process Instance Id: ${params.process_instance_id}`,
|
`/admin/process-instances/for-me/${modifyProcessIdentifierForPathParam(
|
||||||
`/admin/process-instances/for-me/${modifyProcessIdentifierForPathParam(
|
basicTask.process_model_identifier
|
||||||
task.process_model_identifier
|
)}/${params.process_instance_id}`,
|
||||||
)}/${params.process_instance_id}`,
|
],
|
||||||
],
|
[`Task: ${basicTask.name_for_display || basicTask.id}`],
|
||||||
[`Task: ${task.name_for_display || task.id}`],
|
]}
|
||||||
]}
|
/>
|
||||||
/>
|
);
|
||||||
<div>{buildTaskNavigation()}</div>
|
pageElements.push(
|
||||||
<h3>
|
<h3>
|
||||||
Task: {task.name_for_display} ({task.process_model_display_name})
|
Task: {basicTask.name_for_display} (
|
||||||
{statusString}
|
{basicTask.process_model_display_name}){statusString}
|
||||||
</h3>
|
</h3>
|
||||||
<InstructionsForEndUser task={task} />
|
|
||||||
{formElement()}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (basicTask && taskData) {
|
||||||
|
pageElements.push(<InstructionsForEndUser task={taskWithTaskData} />);
|
||||||
|
pageElements.push(formElement());
|
||||||
|
} else {
|
||||||
|
pageElements.push(getLoadingIcon());
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
// typescript gets angry if we return an array of elements not in a tag
|
||||||
|
// eslint-disable-next-line react/jsx-no-useless-fragment
|
||||||
|
return <>{pageElements}</>;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue