Merge pull request #263 from sartography/feature/ui-tweaks

Feature/UI tweaks
This commit is contained in:
Kevin Burnett 2023-05-16 19:38:07 +00:00 committed by GitHub
commit df368a7bab
11 changed files with 50 additions and 81 deletions

View File

@ -18,13 +18,13 @@ def setup_database_uri(app: Flask) -> None:
if app.config.get("SPIFFWORKFLOW_BACKEND_DATABASE_URI") is None: if app.config.get("SPIFFWORKFLOW_BACKEND_DATABASE_URI") is None:
database_name = f"spiffworkflow_backend_{app.config['ENV_IDENTIFIER']}" database_name = f"spiffworkflow_backend_{app.config['ENV_IDENTIFIER']}"
if app.config.get("SPIFFWORKFLOW_BACKEND_DATABASE_TYPE") == "sqlite": if app.config.get("SPIFFWORKFLOW_BACKEND_DATABASE_TYPE") == "sqlite":
app.config["SQLALCHEMY_DATABASE_URI"] = ( app.config[
f"sqlite:///{app.instance_path}/db_{app.config['ENV_IDENTIFIER']}.sqlite3" "SQLALCHEMY_DATABASE_URI"
) ] = f"sqlite:///{app.instance_path}/db_{app.config['ENV_IDENTIFIER']}.sqlite3"
elif app.config.get("SPIFFWORKFLOW_BACKEND_DATABASE_TYPE") == "postgres": elif app.config.get("SPIFFWORKFLOW_BACKEND_DATABASE_TYPE") == "postgres":
app.config["SQLALCHEMY_DATABASE_URI"] = ( app.config[
f"postgresql://spiffworkflow_backend:spiffworkflow_backend@localhost:5432/{database_name}" "SQLALCHEMY_DATABASE_URI"
) ] = f"postgresql://spiffworkflow_backend:spiffworkflow_backend@localhost:5432/{database_name}"
else: else:
# use pswd to trick flake8 with hardcoded passwords # use pswd to trick flake8 with hardcoded passwords
db_pswd = app.config.get("SPIFFWORKFLOW_BACKEND_DATABASE_PASSWORD") db_pswd = app.config.get("SPIFFWORKFLOW_BACKEND_DATABASE_PASSWORD")

View File

@ -129,9 +129,9 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel):
def serialized_with_metadata(self) -> dict[str, Any]: def serialized_with_metadata(self) -> dict[str, Any]:
process_instance_attributes = self.serialized process_instance_attributes = self.serialized
process_instance_attributes["process_metadata"] = self.process_metadata process_instance_attributes["process_metadata"] = self.process_metadata
process_instance_attributes["process_model_with_diagram_identifier"] = ( process_instance_attributes[
self.process_model_with_diagram_identifier "process_model_with_diagram_identifier"
) ] = self.process_model_with_diagram_identifier
return process_instance_attributes return process_instance_attributes
@property @property

View File

@ -415,9 +415,9 @@ class ProcessInstanceProcessor:
tld.process_instance_id = process_instance_model.id tld.process_instance_id = process_instance_model.id
# we want this to be the fully qualified path to the process model including all group subcomponents # we want this to be the fully qualified path to the process model including all group subcomponents
current_app.config["THREAD_LOCAL_DATA"].process_model_identifier = ( current_app.config[
f"{process_instance_model.process_model_identifier}" "THREAD_LOCAL_DATA"
) ].process_model_identifier = f"{process_instance_model.process_model_identifier}"
self.process_instance_model = process_instance_model self.process_instance_model = process_instance_model
self.process_model_service = ProcessModelService() self.process_model_service = ProcessModelService()
@ -577,9 +577,9 @@ class ProcessInstanceProcessor:
bpmn_subprocess_definition.bpmn_identifier bpmn_subprocess_definition.bpmn_identifier
] = bpmn_process_definition_dict ] = bpmn_process_definition_dict
spiff_bpmn_process_dict["subprocess_specs"][bpmn_subprocess_definition.bpmn_identifier]["task_specs"] = {} spiff_bpmn_process_dict["subprocess_specs"][bpmn_subprocess_definition.bpmn_identifier]["task_specs"] = {}
bpmn_subprocess_definition_bpmn_identifiers[bpmn_subprocess_definition.id] = ( bpmn_subprocess_definition_bpmn_identifiers[
bpmn_subprocess_definition.bpmn_identifier bpmn_subprocess_definition.id
) ] = bpmn_subprocess_definition.bpmn_identifier
task_definitions = TaskDefinitionModel.query.filter( task_definitions = TaskDefinitionModel.query.filter(
TaskDefinitionModel.bpmn_process_definition_id.in_( # type: ignore TaskDefinitionModel.bpmn_process_definition_id.in_( # type: ignore

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1601,14 +1601,17 @@ export default function ProcessInstanceListTable({
) { ) {
hasAccessToCompleteTask = true; hasAccessToCompleteTask = true;
} }
let buttonText = 'View'; let buttonText = 'View';
if (hasAccessToCompleteTask && processInstance.task_id) { if (hasAccessToCompleteTask && processInstance.task_id) {
buttonText = 'Go'; buttonText = 'Go';
} }
buttonElement = ( buttonElement = (
<Button kind="secondary" href={interstitialUrl}> <Button
kind="secondary"
href={interstitialUrl}
style={{ width: '60px' }}
>
{buttonText} {buttonText}
</Button> </Button>
); );

View File

@ -478,22 +478,28 @@ svg.notification-icon {
.user_instructions_0 { .user_instructions_0 {
filter: opacity(1); filter: opacity(1);
font-size: 1.2em;
margin-bottom: 30px;
} }
.user_instructions_1 { .user_instructions_1 {
filter: opacity(60%); filter: opacity(60%);
font-size: 1.1em;
} }
.user_instructions_2 { .user_instructions_2 {
filter: opacity(40%); filter: opacity(40%);
font-size: 1em;
} }
.user_instructions_3 { .user_instructions_3 {
filter: opacity(20%); filter: opacity(20%);
font-size: 9em;
} }
.user_instructions_4 { .user_instructions_4 {
filter: opacity(10%); filter: opacity(10%);
font-size: 8em;
} }
.float-right { .float-right {

View File

@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import { fetchEventSource } from '@microsoft/fetch-event-source'; import { fetchEventSource } from '@microsoft/fetch-event-source';
// @ts-ignore // @ts-ignore
import { Loading, Grid, Column, Button } from '@carbon/react'; import { Loading, Button } from '@carbon/react';
import { BACKEND_BASE_URL } from '../config'; import { BACKEND_BASE_URL } from '../config';
import { getBasicHeaders } from '../services/HttpService'; import { getBasicHeaders } from '../services/HttpService';
@ -89,26 +89,17 @@ export default function ProcessInterstitial() {
return state; return state;
}; };
const getStatusImage = () => { const getLoadingIcon = () => {
switch (getStatus()) { if (getStatus() === 'RUNNING') {
case 'RUNNING':
return ( return (
<Loading description="Active loading indicator" withOverlay={false} /> <Loading
description="Active loading indicator"
withOverlay={false}
style={{ margin: 'auto' }}
/>
); );
case 'LOCKED':
return <img src="/interstitial/locked.png" alt="Locked" />;
case 'READY':
case 'REDIRECTING':
return <img src="/interstitial/redirect.png" alt="Redirecting ...." />;
case 'WAITING':
return <img src="/interstitial/waiting.png" alt="Waiting ...." />;
case 'COMPLETED':
return <img src="/interstitial/completed.png" alt="Completed" />;
case 'ERROR':
return <img src="/interstitial/errored.png" alt="Errored" />;
default:
return getStatus();
} }
return null;
}; };
const getReturnHomeButton = (index: number) => { const getReturnHomeButton = (index: number) => {
@ -123,6 +114,7 @@ export default function ProcessInterstitial() {
kind="secondary" kind="secondary"
data-qa="return-to-home-button" data-qa="return-to-home-button"
onClick={() => navigate(`/tasks`)} onClick={() => navigate(`/tasks`)}
style={{ marginBottom: 30 }}
> >
Return to Home Return to Home
</Button> </Button>
@ -132,35 +124,14 @@ export default function ProcessInterstitial() {
return ''; return '';
}; };
const getHr = (index: number) => {
if (index === 0) {
return (
<div style={{ padding: '10px 0 50px 0' }}>
<hr />
</div>
);
}
return '';
};
function capitalize(str: string): string {
if (str && str.length > 0) {
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
}
return '';
}
const userMessage = (myTask: ProcessInstanceTask) => { const userMessage = (myTask: ProcessInstanceTask) => {
if (!processInstance || processInstance.status === 'completed') { if (!processInstance || processInstance.status === 'completed') {
if (!myTask.can_complete && userTasks.includes(myTask.type)) { if (!myTask.can_complete && userTasks.includes(myTask.type)) {
return ( return (
<>
<h4 className="heading-compact-01">Waiting on Someone Else</h4>
<p> <p>
This next task is assigned to a different person or team. There is This next task is assigned to a different person or team. There is
no action for you to take at this time. no action for you to take at this time.
</p> </p>
</>
); );
} }
if (shouldRedirect(myTask)) { if (shouldRedirect(myTask)) {
@ -189,6 +160,7 @@ export default function ProcessInterstitial() {
if (state === 'CLOSED' && lastTask === null) { if (state === 'CLOSED' && lastTask === null) {
navigate(`/tasks`); navigate(`/tasks`);
} }
if (lastTask) { if (lastTask) {
return ( return (
<> <>
@ -206,21 +178,10 @@ export default function ProcessInterstitial() {
], ],
]} ]}
/> />
<div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> {getLoadingIcon()}
{getStatusImage()} <div style={{ maxWidth: 800, margin: 'auto', padding: 50 }}>
<div>
<h1 style={{ marginBottom: '0em' }}>
{lastTask.process_model_display_name}:{' '}
{lastTask.process_instance_id}
</h1>
<div>Status: {capitalize(getStatus())}</div>
</div>
</div>
<br />
<br />
{data.map((d, index) => ( {data.map((d, index) => (
<Grid fullWidth style={{ marginBottom: '1em' }}> <>
<Column md={6} lg={8} sm={4}>
<div <div
className={ className={
index < 4 index < 4
@ -231,10 +192,9 @@ export default function ProcessInterstitial() {
{userMessage(d)} {userMessage(d)}
</div> </div>
{getReturnHomeButton(index)} {getReturnHomeButton(index)}
{getHr(index)} </>
</Column>
</Grid>
))} ))}
</div>
</> </>
); );
} }