mirror of
https://github.com/sartography/spiff-arena.git
synced 2025-01-27 01:40:48 +00:00
Merge pull request #296 from sartography/feature/better_interstitial_spinner_and_suspend_message
Feature/better interstitial spinner and suspend message
This commit is contained in:
commit
37668b14e1
@ -39,7 +39,9 @@ function run_autofixers() {
|
||||
fi
|
||||
|
||||
python_dirs=$(get_python_dirs)
|
||||
python_files=$(find $python_dirs -type f -name "*.py" ! -name '.null-ls*' ! -name '_null-ls*')
|
||||
# python_files=$(find $python_dirs -type f -name "*.py" ! -name '.null-ls*' ! -name '_null-ls*')
|
||||
# Don't check things in git-ignore:
|
||||
python_files=$(git ls-files $python_dirs | grep .py$)
|
||||
ruff --fix $python_files
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,23 @@
|
||||
|
||||
The follow is a list of enhancements we wish to do complete in the near (or even distant future)
|
||||
|
||||
## BPMN Definitions at save time vs run time
|
||||
Improve performance by pre-processesing the BPMN Specification and generating the internal JSON resprestation so we no longer create the expense of doing this on a per-process basis.
|
||||
## Performance Improvements
|
||||
|
||||
### BPMN Definitions at save time vs run time
|
||||
Improve performance by pre-processing the BPMN Specification and generating the internal JSON representation so we no longer incur the expense of doing this on a per-process basis.
|
||||
This will also allow us to do some early and deep validation as well.
|
||||
|
||||
## End User Experience
|
||||
|
||||
### Administrator / Support Contact Information
|
||||
Allow defining contact information at the process group and process model level, perhaps at some very top level as well - which can be inherited unless overridden.
|
||||
This information could then be displayed when a process is in a non-functional state - such an error, suspended, or terminiated state.
|
||||
It might also be available in the footer or under a help icon when displaying a process instance.
|
||||
|
||||
## Modeler Experience
|
||||
|
||||
### Form Builder
|
||||
Let's invest in a much better Form Builder experience, so that it is trivial to build new forms or modify existing forms
|
||||
|
||||
|
||||
|
||||
|
@ -1753,6 +1753,20 @@ class TestProcessApi(BaseTest):
|
||||
assert json_results[0]["task"]["title"] == "Please Approve"
|
||||
assert json_results[0]["task"]["properties"]["instructionsForEndUser"] == "I am a manual task in another lane"
|
||||
|
||||
# Suspending the task should still report that the user can not complete the task.
|
||||
process_instance = ProcessInstanceModel.query.filter_by(id=process_instance_id).first()
|
||||
processor = ProcessInstanceProcessor(process_instance)
|
||||
processor.suspend()
|
||||
processor.save()
|
||||
|
||||
results = list(_dequeued_interstitial_stream(process_instance_id))
|
||||
json_results = list(map(lambda x: json.loads(x[5:]), results)) # type: ignore
|
||||
assert len(results) == 1
|
||||
assert json_results[0]["task"]["state"] == "READY"
|
||||
assert json_results[0]["task"]["can_complete"] is False
|
||||
assert json_results[0]["task"]["title"] == "Please Approve"
|
||||
assert json_results[0]["task"]["properties"]["instructionsForEndUser"] == "I am a manual task in another lane"
|
||||
|
||||
# Complete task as the finance user.
|
||||
response = client.put(
|
||||
f"/v1.0/tasks/{process_instance_id}/{json_results[0]['task']['id']}",
|
||||
|
@ -15,12 +15,14 @@ type OwnProps = {
|
||||
processInstanceId: number;
|
||||
processInstanceShowPageUrl: string;
|
||||
allowRedirect: boolean;
|
||||
smallSpinner?: boolean;
|
||||
};
|
||||
|
||||
export default function ProcessInterstitial({
|
||||
processInstanceId,
|
||||
allowRedirect,
|
||||
processInstanceShowPageUrl,
|
||||
smallSpinner = false,
|
||||
}: OwnProps) {
|
||||
const [data, setData] = useState<any[]>([]);
|
||||
const [lastTask, setLastTask] = useState<any>(null);
|
||||
@ -98,62 +100,82 @@ export default function ProcessInterstitial({
|
||||
shouldRedirectToProcessInstance,
|
||||
]);
|
||||
|
||||
const getStatus = (): string => {
|
||||
if (processInstance) {
|
||||
return 'LOCKED';
|
||||
}
|
||||
if (!lastTask.can_complete && userTasks.includes(lastTask.type)) {
|
||||
return 'LOCKED';
|
||||
}
|
||||
if (state === 'CLOSED') {
|
||||
return lastTask.state;
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
||||
const getLoadingIcon = () => {
|
||||
if (getStatus() === 'RUNNING') {
|
||||
if (state === 'RUNNING') {
|
||||
let style = { margin: '50px 0 50px 50px' };
|
||||
if (smallSpinner) {
|
||||
style = { margin: '2x 5px 2px 2px' };
|
||||
}
|
||||
return (
|
||||
<Loading
|
||||
description="Active loading indicator"
|
||||
withOverlay={false}
|
||||
style={{ margin: '50px 0 50px 50px' }}
|
||||
small={smallSpinner}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const userMessageForProcessInstance = (
|
||||
pi: ProcessInstance,
|
||||
myTask: ProcessInstanceTask | null = null
|
||||
) => {
|
||||
if (['terminated', 'suspended'].includes(pi.status)) {
|
||||
return (
|
||||
<p>
|
||||
This process instance was {pi.status} by an administrator. Please get
|
||||
in touch with them for more information.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
if (pi.status === 'error') {
|
||||
let errMessage = `This process instance experienced an unexpected error and can not continue. Please get in touch with an administrator for more information and next steps. `;
|
||||
if (myTask && myTask.error_message) {
|
||||
errMessage = errMessage.concat(myTask.error_message);
|
||||
}
|
||||
return <p>{errMessage}</p>;
|
||||
}
|
||||
// Otherwise we are not started, waiting, complete, or user_input_required
|
||||
const defaultMsg =
|
||||
'There are no additional instructions or information for this process.';
|
||||
if (myTask) {
|
||||
return (
|
||||
<InstructionsForEndUser task={myTask} defaultMessage={defaultMsg} />
|
||||
);
|
||||
}
|
||||
return <p>{defaultMsg}</p>;
|
||||
};
|
||||
|
||||
const userMessage = (myTask: ProcessInstanceTask) => {
|
||||
if (!processInstance || processInstance.status === 'completed') {
|
||||
if (!myTask.can_complete && userTasks.includes(myTask.type)) {
|
||||
return (
|
||||
<p>
|
||||
This next task is assigned to a different person or team. There is
|
||||
no action for you to take at this time.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
if (shouldRedirectToTask(myTask)) {
|
||||
return <div>Redirecting you to the next task now ...</div>;
|
||||
}
|
||||
if (myTask && myTask.can_complete && userTasks.includes(myTask.type)) {
|
||||
return `The task ${myTask.title} is ready for you to complete.`;
|
||||
}
|
||||
if (myTask.error_message) {
|
||||
return <div>{myTask.error_message}</div>;
|
||||
}
|
||||
if (processInstance) {
|
||||
return userMessageForProcessInstance(processInstance, myTask);
|
||||
}
|
||||
|
||||
let message =
|
||||
'There are no additional instructions or information for this task.';
|
||||
if (processInstance && processInstance.status !== 'completed') {
|
||||
message = `The tasks cannot be completed on this instance because its status is "${processInstance.status}".`;
|
||||
if (!myTask.can_complete && userTasks.includes(myTask.type)) {
|
||||
return (
|
||||
<p>
|
||||
This next task is assigned to a different person or team. There is no
|
||||
action for you to take at this time.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
if (shouldRedirectToTask(myTask)) {
|
||||
return <div>Redirecting you to the next task now ...</div>;
|
||||
}
|
||||
if (myTask && myTask.can_complete && userTasks.includes(myTask.type)) {
|
||||
return `The task ${myTask.title} is ready for you to complete.`;
|
||||
}
|
||||
if (myTask.error_message) {
|
||||
return <div>{myTask.error_message}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<InstructionsForEndUser task={myTask} defaultMessage={message} />
|
||||
<InstructionsForEndUser
|
||||
task={myTask}
|
||||
defaultMessage="There are no additional instructions or information for this task."
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -171,7 +193,7 @@ export default function ProcessInterstitial({
|
||||
|
||||
if (lastTask) {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
{getLoadingIcon()}
|
||||
{displayableData.map((d, index) => (
|
||||
<div
|
||||
@ -182,8 +204,16 @@ export default function ProcessInterstitial({
|
||||
{userMessage(d)}
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
if (processInstance) {
|
||||
return (
|
||||
<div>
|
||||
{getLoadingIcon()}
|
||||
{userMessageForProcessInstance(processInstance)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <div>{getLoadingIcon()}</div>;
|
||||
}
|
||||
|
@ -1122,6 +1122,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
|
||||
processInstanceId={processInstance.id}
|
||||
processInstanceShowPageUrl={processInstanceShowPageBaseUrl}
|
||||
allowRedirect={false}
|
||||
smallSpinner
|
||||
/>
|
||||
<br />
|
||||
<br />
|
||||
|
Loading…
x
Reference in New Issue
Block a user