Merge branch 'main' into feature/remove-loop-reset

This commit is contained in:
Elizabeth Esswein 2023-03-09 09:55:52 -05:00
commit 2ecfecb9b4
10 changed files with 166 additions and 102 deletions

View File

@ -660,6 +660,18 @@ paths:
description: The username of the process initiator description: The username of the process initiator
schema: schema:
type: string type: string
- name: report_columns
in: query
required: false
description: Base64 encoded json of report columns.
schema:
type: string
- name: report_filter_by
in: query
required: false
description: Base64 encoded json of report filter by.
schema:
type: string
get: get:
operationId: spiffworkflow_backend.routes.process_instances_controller.process_instance_list_for_me operationId: spiffworkflow_backend.routes.process_instances_controller.process_instance_list_for_me
summary: Returns a list of process instances that are associated with me. summary: Returns a list of process instances that are associated with me.
@ -779,6 +791,18 @@ paths:
description: The username of the process initiator description: The username of the process initiator
schema: schema:
type: string type: string
- name: report_columns
in: query
required: false
description: Base64 encoded json of report columns.
schema:
type: string
- name: report_filter_by
in: query
required: false
description: Base64 encoded json of report filter by.
schema:
type: string
get: get:
operationId: spiffworkflow_backend.routes.process_instances_controller.process_instance_list operationId: spiffworkflow_backend.routes.process_instances_controller.process_instance_list
summary: Returns a list of process instances. summary: Returns a list of process instances.

View File

@ -55,7 +55,7 @@ class TaskModel(SpiffworkflowBaseDBModel):
json_data_hash: str = db.Column(db.String(255), nullable=False, index=True) json_data_hash: str = db.Column(db.String(255), nullable=False, index=True)
start_in_seconds: float = db.Column(db.DECIMAL(17, 6)) start_in_seconds: float = db.Column(db.DECIMAL(17, 6))
end_in_seconds: float | None = db.Column(db.DECIMAL(17, 6)) end_in_seconds: Union[float, None] = db.Column(db.DECIMAL(17, 6))
class Task: class Task:

View File

@ -1,4 +1,5 @@
"""APIs for dealing with process groups, process models, and process instances.""" """APIs for dealing with process groups, process models, and process instances."""
import base64
import json import json
from uuid import UUID from uuid import UUID
from typing import Any from typing import Any
@ -117,10 +118,10 @@ def process_instance_run(
) )
processor = ProcessInstanceProcessor(process_instance) processor = ProcessInstanceProcessor(process_instance)
processor.lock_process_instance("Web")
if do_engine_steps: if do_engine_steps:
try: try:
processor.lock_process_instance("Web")
processor.do_engine_steps(save=True) processor.do_engine_steps(save=True)
except ApiError as e: except ApiError as e:
ErrorHandlingService().handle_error(processor, e) ErrorHandlingService().handle_error(processor, e)
@ -252,6 +253,8 @@ def process_instance_list_for_me(
report_id: Optional[int] = None, report_id: Optional[int] = None,
user_group_identifier: Optional[str] = None, user_group_identifier: Optional[str] = None,
process_initiator_username: Optional[str] = None, process_initiator_username: Optional[str] = None,
report_columns: Optional[str] = None,
report_filter_by: Optional[str] = None,
) -> flask.wrappers.Response: ) -> flask.wrappers.Response:
"""Process_instance_list_for_me.""" """Process_instance_list_for_me."""
return process_instance_list( return process_instance_list(
@ -268,6 +271,8 @@ def process_instance_list_for_me(
report_id=report_id, report_id=report_id,
user_group_identifier=user_group_identifier, user_group_identifier=user_group_identifier,
with_relation_to_me=True, with_relation_to_me=True,
report_columns=report_columns,
report_filter_by=report_filter_by,
) )
@ -286,12 +291,21 @@ def process_instance_list(
report_id: Optional[int] = None, report_id: Optional[int] = None,
user_group_identifier: Optional[str] = None, user_group_identifier: Optional[str] = None,
process_initiator_username: Optional[str] = None, process_initiator_username: Optional[str] = None,
report_columns: Optional[str] = None,
report_filter_by: Optional[str] = None,
) -> flask.wrappers.Response: ) -> flask.wrappers.Response:
"""Process_instance_list.""" """Process_instance_list."""
process_instance_report = ProcessInstanceReportService.report_with_identifier( process_instance_report = ProcessInstanceReportService.report_with_identifier(
g.user, report_id, report_identifier g.user, report_id, report_identifier
) )
report_column_list = None
if report_columns:
report_column_list = json.loads(base64.b64decode(report_columns))
report_filter_by_list = None
if report_filter_by:
report_filter_by_list = json.loads(base64.b64decode(report_filter_by))
if user_filter: if user_filter:
report_filter = ProcessInstanceReportFilter( report_filter = ProcessInstanceReportFilter(
process_model_identifier=process_model_identifier, process_model_identifier=process_model_identifier,
@ -303,6 +317,8 @@ def process_instance_list(
with_relation_to_me=with_relation_to_me, with_relation_to_me=with_relation_to_me,
process_status=process_status.split(",") if process_status else None, process_status=process_status.split(",") if process_status else None,
process_initiator_username=process_initiator_username, process_initiator_username=process_initiator_username,
report_column_list=report_column_list,
report_filter_by_list=report_filter_by_list,
) )
else: else:
report_filter = ( report_filter = (
@ -317,6 +333,8 @@ def process_instance_list(
process_status=process_status, process_status=process_status,
with_relation_to_me=with_relation_to_me, with_relation_to_me=with_relation_to_me,
process_initiator_username=process_initiator_username, process_initiator_username=process_initiator_username,
report_column_list=report_column_list,
report_filter_by_list=report_filter_by_list,
) )
) )

View File

@ -2,6 +2,7 @@
import json import json
import logging import logging
import re import re
import sys
from typing import Any from typing import Any
from typing import Optional from typing import Optional
@ -171,6 +172,10 @@ def setup_logger(app: Flask) -> None:
the_logger.propagate = False the_logger.propagate = False
the_logger.addHandler(spiff_logger_filehandler) the_logger.addHandler(spiff_logger_filehandler)
else: else:
if len(the_logger.handlers) < 1:
# it's very verbose, so only add handlers for the obscure loggers when log level is DEBUG
if upper_log_level_string == "DEBUG":
the_logger.addHandler(logging.StreamHandler(sys.stdout))
for the_handler in the_logger.handlers: for the_handler in the_logger.handlers:
the_handler.setFormatter(log_formatter) the_handler.setFormatter(log_formatter)
the_handler.setLevel(log_level) the_handler.setLevel(log_level)

View File

@ -50,6 +50,8 @@ class ProcessInstanceReportFilter:
with_tasks_assigned_to_my_group: Optional[bool] = None with_tasks_assigned_to_my_group: Optional[bool] = None
with_relation_to_me: Optional[bool] = None with_relation_to_me: Optional[bool] = None
process_initiator_username: Optional[str] = None process_initiator_username: Optional[str] = None
report_column_list: Optional[list] = None
report_filter_by_list: Optional[list] = None
def to_dict(self) -> dict[str, str]: def to_dict(self) -> dict[str, str]:
"""To_dict.""" """To_dict."""
@ -85,6 +87,10 @@ class ProcessInstanceReportFilter:
d["with_relation_to_me"] = str(self.with_relation_to_me).lower() d["with_relation_to_me"] = str(self.with_relation_to_me).lower()
if self.process_initiator_username is not None: if self.process_initiator_username is not None:
d["process_initiator_username"] = str(self.process_initiator_username) d["process_initiator_username"] = str(self.process_initiator_username)
if self.report_column_list is not None:
d["report_column_list"] = str(self.report_column_list)
if self.report_filter_by_list is not None:
d["report_filter_by_list"] = str(self.report_filter_by_list)
return d return d
@ -229,6 +235,8 @@ class ProcessInstanceReportService:
with_tasks_assigned_to_my_group = bool_value("with_tasks_assigned_to_my_group") with_tasks_assigned_to_my_group = bool_value("with_tasks_assigned_to_my_group")
with_relation_to_me = bool_value("with_relation_to_me") with_relation_to_me = bool_value("with_relation_to_me")
process_initiator_username = filters.get("process_initiator_username") process_initiator_username = filters.get("process_initiator_username")
report_column_list = list_value("report_column_list")
report_filter_by_list = list_value("report_filter_by_list")
report_filter = ProcessInstanceReportFilter( report_filter = ProcessInstanceReportFilter(
process_model_identifier=process_model_identifier, process_model_identifier=process_model_identifier,
@ -244,6 +252,8 @@ class ProcessInstanceReportService:
with_tasks_assigned_to_my_group=with_tasks_assigned_to_my_group, with_tasks_assigned_to_my_group=with_tasks_assigned_to_my_group,
with_relation_to_me=with_relation_to_me, with_relation_to_me=with_relation_to_me,
process_initiator_username=process_initiator_username, process_initiator_username=process_initiator_username,
report_column_list=report_column_list,
report_filter_by_list=report_filter_by_list,
) )
return report_filter return report_filter
@ -265,6 +275,8 @@ class ProcessInstanceReportService:
with_tasks_assigned_to_my_group: Optional[bool] = None, with_tasks_assigned_to_my_group: Optional[bool] = None,
with_relation_to_me: Optional[bool] = None, with_relation_to_me: Optional[bool] = None,
process_initiator_username: Optional[str] = None, process_initiator_username: Optional[str] = None,
report_column_list: Optional[list] = None,
report_filter_by_list: Optional[list] = None,
) -> ProcessInstanceReportFilter: ) -> ProcessInstanceReportFilter:
"""Filter_from_metadata_with_overrides.""" """Filter_from_metadata_with_overrides."""
report_filter = cls.filter_from_metadata(process_instance_report) report_filter = cls.filter_from_metadata(process_instance_report)
@ -291,6 +303,10 @@ class ProcessInstanceReportService:
report_filter.with_tasks_completed_by_me = with_tasks_completed_by_me report_filter.with_tasks_completed_by_me = with_tasks_completed_by_me
if process_initiator_username is not None: if process_initiator_username is not None:
report_filter.process_initiator_username = process_initiator_username report_filter.process_initiator_username = process_initiator_username
if report_column_list is not None:
report_filter.report_column_list = report_column_list
if report_filter_by_list is not None:
report_filter.report_filter_by_list = report_filter_by_list
if with_tasks_assigned_to_my_group is not None: if with_tasks_assigned_to_my_group is not None:
report_filter.with_tasks_assigned_to_my_group = ( report_filter.with_tasks_assigned_to_my_group = (
with_tasks_assigned_to_my_group with_tasks_assigned_to_my_group
@ -483,6 +499,15 @@ class ProcessInstanceReportService:
stock_columns = ProcessInstanceReportService.get_column_names_for_model( stock_columns = ProcessInstanceReportService.get_column_names_for_model(
ProcessInstanceModel ProcessInstanceModel
) )
if report_filter.report_column_list:
process_instance_report.report_metadata["columns"] = (
report_filter.report_column_list
)
if report_filter.report_filter_by_list:
process_instance_report.report_metadata["filter_by"] = (
report_filter.report_filter_by_list
)
for column in process_instance_report.report_metadata["columns"]: for column in process_instance_report.report_metadata["columns"]:
if column["accessor"] in stock_columns: if column["accessor"] in stock_columns:
continue continue

View File

@ -10,7 +10,7 @@ const approveWithUser = (
.contains(/^Submit$/) .contains(/^Submit$/)
.click(); .click();
cy.contains('Tasks I can complete', { timeout: 20000 }); cy.contains('Tasks I can complete', { timeout: 30000 });
cy.get('.cds--btn').contains(/^Go$/).click(); cy.get('.cds--btn').contains(/^Go$/).click();
// approve! // approve!
@ -19,12 +19,12 @@ const approveWithUser = (
.contains(/^Submit$/) .contains(/^Submit$/)
.click(); .click();
if (expectAdditionalApprovalInfoPage) { if (expectAdditionalApprovalInfoPage) {
cy.contains(expectAdditionalApprovalInfoPage, { timeout: 20000 }); cy.contains(expectAdditionalApprovalInfoPage, { timeout: 30000 });
cy.get('button') cy.get('button')
.contains(/^Continue$/) .contains(/^Continue$/)
.click(); .click();
} }
cy.location({ timeout: 20000 }).should((loc) => { cy.location({ timeout: 30000 }).should((loc) => {
expect(loc.pathname).to.eq('/tasks'); expect(loc.pathname).to.eq('/tasks');
}); });
cy.logout(); cy.logout();
@ -37,7 +37,7 @@ describe('pp1', () => {
cy.contains('Start New +').click(); cy.contains('Start New +').click();
cy.contains('Raise New Demand Request'); cy.contains('Raise New Demand Request');
cy.runPrimaryBpmnFile(true); cy.runPrimaryBpmnFile(true);
cy.contains('Please select the type of request to Start the process.'); cy.contains('Please select the type of request to start the process.');
// wait a second to ensure we can click the radio button // wait a second to ensure we can click the radio button
cy.wait(1000); cy.wait(1000);
cy.get('input#root-procurement').click(); cy.get('input#root-procurement').click();
@ -47,7 +47,7 @@ describe('pp1', () => {
.click(); .click();
cy.contains( cy.contains(
'Submit a new demand request for the procurement of needed items', 'Submit a new demand request for the procurement of needed items',
{ timeout: 20000 } { timeout: 30000 }
); );
cy.url().then((currentUrl) => { cy.url().then((currentUrl) => {
@ -68,7 +68,7 @@ describe('pp1', () => {
.contains(/^Submit$/) .contains(/^Submit$/)
.click(); .click();
cy.contains('Task: Enter NDR Items', { timeout: 20000 }); cy.contains('Task: Enter NDR Items', { timeout: 30000 });
cy.get('#root_0_sub_category').select('op_src'); cy.get('#root_0_sub_category').select('op_src');
cy.get('#root_0_item').clear().type('spiffworkflow'); cy.get('#root_0_item').clear().type('spiffworkflow');
cy.get('#root_0_qty').clear().type('1'); cy.get('#root_0_qty').clear().type('1');
@ -81,13 +81,14 @@ describe('pp1', () => {
cy.contains( cy.contains(
'Review and provide any supporting information or files for your request.', 'Review and provide any supporting information or files for your request.',
{ timeout: 20000 } { timeout: 30000 }
); );
cy.contains('Submit the Request').click(); cy.contains('Submit the Request').click();
cy.get('input[value="Submit the Request"]').click(); cy.get('input[value="Submit the Request"]').click();
cy.get('button') cy.get('button')
.contains(/^Submit$/) .contains(/^Submit$/)
.click(); .click();
cy.contains('Tasks for my open instances', { timeout: 30000 });
cy.logout(); cy.logout();
approveWithUser( approveWithUser(

View File

@ -36,6 +36,7 @@ import {
convertSecondsToFormattedDateString, convertSecondsToFormattedDateString,
convertSecondsToFormattedDateTime, convertSecondsToFormattedDateTime,
convertSecondsToFormattedTimeHoursMinutes, convertSecondsToFormattedTimeHoursMinutes,
encodeBase64,
getPageInfoFromSearchParams, getPageInfoFromSearchParams,
getProcessModelFullIdentifierFromSearchParams, getProcessModelFullIdentifierFromSearchParams,
modifyProcessIdentifierForPathParam, modifyProcessIdentifierForPathParam,
@ -268,6 +269,17 @@ export default function ProcessInstanceListTable({
queryParamString += `&report_identifier=${reportIdentifier}`; queryParamString += `&report_identifier=${reportIdentifier}`;
} }
if (searchParams.get('report_columns')) {
queryParamString += `&report_columns=${searchParams.get(
'report_columns'
)}`;
}
if (searchParams.get('report_filter_by')) {
queryParamString += `&report_filter_by=${searchParams.get(
'report_filter_by'
)}`;
}
Object.keys(dateParametersToAlwaysFilterBy).forEach( Object.keys(dateParametersToAlwaysFilterBy).forEach(
(paramName: string) => { (paramName: string) => {
const dateFunctionToCall = const dateFunctionToCall =
@ -529,6 +541,14 @@ export default function ProcessInstanceListTable({
}; };
}; };
const reportColumns = () => {
return (reportMetadata as any).columns;
};
const reportFilterBy = () => {
return (reportMetadata as any).filter_by;
};
const applyFilter = (event: any) => { const applyFilter = (event: any) => {
event.preventDefault(); event.preventDefault();
const { page, perPage } = getPageInfoFromSearchParams( const { page, perPage } = getPageInfoFromSearchParams(
@ -578,6 +598,11 @@ export default function ProcessInstanceListTable({
queryParamString += `&process_initiator_username=${processInitiatorSelection.username}`; queryParamString += `&process_initiator_username=${processInitiatorSelection.username}`;
} }
const reportColumnsBase64 = encodeBase64(JSON.stringify(reportColumns()));
queryParamString += `&report_columns=${reportColumnsBase64}`;
const reportFilterByBase64 = encodeBase64(JSON.stringify(reportFilterBy()));
queryParamString += `&report_filter_by=${reportFilterByBase64}`;
removeError(); removeError();
setProcessInstanceReportJustSaved(null); setProcessInstanceReportJustSaved(null);
setProcessInstanceFilters({}); setProcessInstanceFilters({});
@ -683,10 +708,6 @@ export default function ProcessInstanceListTable({
navigate(`${processInstanceListPathPrefix}${queryParamString}`); navigate(`${processInstanceListPathPrefix}${queryParamString}`);
}; };
const reportColumns = () => {
return (reportMetadata as any).columns;
};
const reportColumnAccessors = () => { const reportColumnAccessors = () => {
return reportColumns().map((reportColumn: ReportColumn) => { return reportColumns().map((reportColumn: ReportColumn) => {
return reportColumn.accessor; return reportColumn.accessor;

View File

@ -1,4 +1,6 @@
import { format } from 'date-fns'; import { format } from 'date-fns';
import { Buffer } from 'buffer';
import { import {
DATE_TIME_FORMAT, DATE_TIME_FORMAT,
DATE_FORMAT, DATE_FORMAT,
@ -260,3 +262,11 @@ export const getBpmnProcessIdentifiers = (rootBpmnElement: any) => {
export const isInteger = (str: string | number) => { export const isInteger = (str: string | number) => {
return /^\d+$/.test(str.toString()); return /^\d+$/.test(str.toString());
}; };
export const encodeBase64 = (data: string) => {
return Buffer.from(data).toString('base64');
};
export const decodeBase64 = (data: string) => {
return Buffer.from(data, 'base64').toString('ascii');
};

View File

@ -80,7 +80,6 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
const [eventPayload, setEventPayload] = useState<string>('{}'); const [eventPayload, setEventPayload] = useState<string>('{}');
const [eventTextEditorEnabled, setEventTextEditorEnabled] = const [eventTextEditorEnabled, setEventTextEditorEnabled] =
useState<boolean>(false); useState<boolean>(false);
const [displayDetails, setDisplayDetails] = useState<boolean>(false);
const [showProcessInstanceMetadata, setShowProcessInstanceMetadata] = const [showProcessInstanceMetadata, setShowProcessInstanceMetadata] =
useState<boolean>(false); useState<boolean>(false);
@ -304,92 +303,26 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
}); });
}; };
const detailedViewElement = () => {
if (!processInstance) {
return null;
}
if (displayDetails) {
return (
<>
<Grid condensed fullWidth>
<Button
kind="ghost"
className="button-link"
onClick={() => setDisplayDetails(false)}
title="Hide Details"
>
&laquo; Hide Details
</Button>
</Grid>
<Grid condensed fullWidth>
<Column sm={1} md={1} lg={2} className="grid-list-title">
Updated At:{' '}
</Column>
<Column sm={3} md={3} lg={3} className="grid-date">
{convertSecondsToFormattedDateTime(
processInstance.updated_at_in_seconds
)}
</Column>
</Grid>
<Grid condensed fullWidth>
<Column sm={1} md={1} lg={2} className="grid-list-title">
Created At:{' '}
</Column>
<Column sm={3} md={3} lg={3} className="grid-date">
{convertSecondsToFormattedDateTime(
processInstance.created_at_in_seconds
)}
</Column>
</Grid>
<Grid condensed fullWidth>
<Column sm={1} md={1} lg={2} className="grid-list-title">
Process model revision:{' '}
</Column>
<Column sm={3} md={3} lg={3} className="grid-date">
{processInstance.bpmn_version_control_identifier} (
{processInstance.bpmn_version_control_type})
</Column>
</Grid>
</>
);
}
return (
<Grid condensed fullWidth>
<Button
kind="ghost"
className="button-link"
onClick={() => setDisplayDetails(true)}
title="Show Details"
>
View Details &raquo;
</Button>
</Grid>
);
};
const getInfoTag = () => { const getInfoTag = () => {
if (!processInstance) { if (!processInstance) {
return null; return null;
} }
const currentEndDate = convertSecondsToFormattedDateTime( let lastUpdatedTimeLabel = 'Updated At';
processInstance.end_in_seconds || 0 let lastUpdatedTime = processInstance.updated_at_in_seconds;
); if (processInstance.end_in_seconds) {
let currentEndDateTag; lastUpdatedTimeLabel = 'Completed';
if (currentEndDate) { lastUpdatedTime = processInstance.end_in_seconds;
currentEndDateTag = (
<Grid condensed fullWidth>
<Column sm={1} md={1} lg={2} className="grid-list-title">
Completed:{' '}
</Column>
<Column sm={3} md={3} lg={3} className="grid-date">
{convertSecondsToFormattedDateTime(
processInstance.end_in_seconds || 0
) || 'N/A'}
</Column>
</Grid>
);
} }
const lastUpdatedTimeTag = (
<Grid condensed fullWidth>
<Column sm={1} md={1} lg={2} className="grid-list-title">
{lastUpdatedTimeLabel}:{' '}
</Column>
<Column sm={3} md={3} lg={3} className="grid-date">
{convertSecondsToFormattedDateTime(lastUpdatedTime || 0) || 'N/A'}
</Column>
</Grid>
);
let statusIcon = <InProgress />; let statusIcon = <InProgress />;
if (processInstance.status === 'suspended') { if (processInstance.status === 'suspended') {
@ -433,13 +366,30 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
<Column sm={1} md={1} lg={2} className="grid-list-title"> <Column sm={1} md={1} lg={2} className="grid-list-title">
Started:{' '} Started:{' '}
</Column> </Column>
<Column sm={3} md={3} lg={3} className="grid-date"> <Column
sm={3}
md={3}
lg={3}
className="grid-date"
title={`Created At: ${convertSecondsToFormattedDateTime(
processInstance.created_at_in_seconds
)}`}
>
{convertSecondsToFormattedDateTime( {convertSecondsToFormattedDateTime(
processInstance.start_in_seconds || 0 processInstance.start_in_seconds || 0
)} )}
</Column> </Column>
</Grid> </Grid>
{currentEndDateTag} {lastUpdatedTimeTag}
<Grid condensed fullWidth>
<Column sm={1} md={1} lg={2} className="grid-list-title">
Process model revision:{' '}
</Column>
<Column sm={3} md={3} lg={3} className="grid-date">
{processInstance.bpmn_version_control_identifier} (
{processInstance.bpmn_version_control_type})
</Column>
</Grid>
<Grid condensed fullWidth> <Grid condensed fullWidth>
<Column sm={1} md={1} lg={2} className="grid-list-title"> <Column sm={1} md={1} lg={2} className="grid-list-title">
Status:{' '} Status:{' '}
@ -450,7 +400,6 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
</Tag> </Tag>
</Column> </Column>
</Grid> </Grid>
{detailedViewElement()}
<br /> <br />
<Grid condensed fullWidth> <Grid condensed fullWidth>
<Column sm={2} md={2} lg={2}> <Column sm={2} md={2} lg={2}>
@ -493,7 +442,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
setShowProcessInstanceMetadata(true); setShowProcessInstanceMetadata(true);
}} }}
> >
Metadata Details
</Button> </Button>
) : null} ) : null}
</ButtonSet> </ButtonSet>
@ -1012,7 +961,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
return ( return (
<Modal <Modal
open={showProcessInstanceMetadata} open={showProcessInstanceMetadata}
modalHeading="Metadata" modalHeading="Details"
passiveModal passiveModal
onRequestClose={() => setShowProcessInstanceMetadata(false)} onRequestClose={() => setShowProcessInstanceMetadata(false)}
> >

View File

@ -19,6 +19,7 @@ import HttpService from '../services/HttpService';
import useAPIError from '../hooks/UseApiError'; import useAPIError from '../hooks/UseApiError';
import { modifyProcessIdentifierForPathParam } from '../helpers'; import { modifyProcessIdentifierForPathParam } from '../helpers';
import { ProcessInstanceTask } from '../interfaces'; import { ProcessInstanceTask } from '../interfaces';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
export default function TaskShow() { export default function TaskShow() {
const [task, setTask] = useState<ProcessInstanceTask | null>(null); const [task, setTask] = useState<ProcessInstanceTask | null>(null);
@ -85,7 +86,6 @@ export default function TaskShow() {
successCallback: processSubmitResult, successCallback: processSubmitResult,
failureCallback: (error: any) => { failureCallback: (error: any) => {
addError(error); addError(error);
setDisabled(false);
}, },
httpMethod: 'PUT', httpMethod: 'PUT',
postBody: dataToSubmit, postBody: dataToSubmit,
@ -288,6 +288,17 @@ export default function TaskShow() {
return ( return (
<main> <main>
<ProcessBreadcrumb
hotCrumbs={[
[
`Process Instance Id: ${params.process_instance_id}`,
`/admin/process-instances/for-me/${modifyProcessIdentifierForPathParam(
task.process_model_identifier
)}/${params.process_instance_id}`,
],
[`Task: ${task.title || task.id}`],
]}
/>
<div>{buildTaskNavigation()}</div> <div>{buildTaskNavigation()}</div>
<h3> <h3>
Task: {task.title} ({task.process_model_display_name}){statusString} Task: {task.title} ({task.process_model_display_name}){statusString}