added tasks table to process instance show page w/ burnettk
This commit is contained in:
parent
96cc8b02d0
commit
14397099f4
|
@ -1333,6 +1333,12 @@ paths:
|
|||
|
||||
/tasks:
|
||||
parameters:
|
||||
- name: process_instance_id
|
||||
in: query
|
||||
required: false
|
||||
description: The process instance id to search by.
|
||||
schema:
|
||||
type: integer
|
||||
- name: page
|
||||
in: query
|
||||
required: false
|
||||
|
|
|
@ -106,6 +106,11 @@ permissions:
|
|||
users: []
|
||||
allowed_permissions: [create, read, update, delete]
|
||||
uri: /process-instances/reports/*
|
||||
read-process-instances-find-by-id:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /process-instances/find-by-id/*
|
||||
processes-read:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
|
|
|
@ -67,35 +67,48 @@ class ReactJsonSchemaSelectOption(TypedDict):
|
|||
|
||||
# TODO: see comment for before_request
|
||||
# @process_api_blueprint.route("/v1.0/tasks", methods=["GET"])
|
||||
def task_list_my_tasks(page: int = 1, per_page: int = 100) -> flask.wrappers.Response:
|
||||
def task_list_my_tasks(
|
||||
process_instance_id: Optional[int] = None, page: int = 1, per_page: int = 100
|
||||
) -> flask.wrappers.Response:
|
||||
"""Task_list_my_tasks."""
|
||||
principal = _find_principal_or_raise()
|
||||
human_tasks = (
|
||||
human_task_query = (
|
||||
HumanTaskModel.query.order_by(desc(HumanTaskModel.id)) # type: ignore
|
||||
.join(ProcessInstanceModel)
|
||||
.join(HumanTaskUserModel)
|
||||
.filter_by(user_id=principal.user_id)
|
||||
.filter(HumanTaskModel.completed == False) # noqa: E712
|
||||
# just need this add_columns to add the process_model_identifier. Then add everything back that was removed.
|
||||
.add_columns(
|
||||
ProcessInstanceModel.process_model_identifier,
|
||||
ProcessInstanceModel.process_model_display_name,
|
||||
ProcessInstanceModel.status,
|
||||
HumanTaskModel.task_name,
|
||||
HumanTaskModel.task_title,
|
||||
HumanTaskModel.task_type,
|
||||
HumanTaskModel.task_status,
|
||||
HumanTaskModel.task_id,
|
||||
HumanTaskModel.id,
|
||||
HumanTaskModel.process_model_display_name,
|
||||
HumanTaskModel.process_instance_id,
|
||||
.group_by(HumanTaskModel.id)
|
||||
.join(
|
||||
ProcessInstanceModel,
|
||||
ProcessInstanceModel.id == HumanTaskModel.process_instance_id,
|
||||
)
|
||||
.paginate(page=page, per_page=per_page, error_out=False)
|
||||
.join(HumanTaskUserModel, HumanTaskUserModel.human_task_id == HumanTaskModel.id)
|
||||
.filter(HumanTaskUserModel.user_id == principal.user_id)
|
||||
.join(UserModel, UserModel.id == HumanTaskUserModel.user_id)
|
||||
.filter(HumanTaskModel.completed == False) # noqa: E712
|
||||
.outerjoin(GroupModel, GroupModel.id == HumanTaskModel.lane_assignment_id)
|
||||
)
|
||||
tasks = [HumanTaskModel.to_task(human_task) for human_task in human_tasks.items]
|
||||
|
||||
if process_instance_id is not None:
|
||||
human_task_query = human_task_query.filter(
|
||||
ProcessInstanceModel.id == process_instance_id
|
||||
)
|
||||
|
||||
human_tasks = human_task_query.add_columns(
|
||||
ProcessInstanceModel.process_model_identifier,
|
||||
ProcessInstanceModel.status.label("process_instance_status"), # type: ignore
|
||||
ProcessInstanceModel.updated_at_in_seconds,
|
||||
ProcessInstanceModel.created_at_in_seconds,
|
||||
UserModel.username.label("process_initiator_username"), # type: ignore
|
||||
GroupModel.identifier.label("assigned_user_group_identifier"),
|
||||
HumanTaskModel.task_name,
|
||||
HumanTaskModel.task_title,
|
||||
HumanTaskModel.process_model_display_name,
|
||||
HumanTaskModel.process_instance_id,
|
||||
func.group_concat(UserModel.username.distinct()).label( # type: ignore
|
||||
"potential_owner_usernames"
|
||||
),
|
||||
).paginate(page=page, per_page=per_page, error_out=False)
|
||||
|
||||
response_json = {
|
||||
"results": tasks,
|
||||
"results": human_tasks.items,
|
||||
"pagination": {
|
||||
"count": len(human_tasks.items),
|
||||
"total": human_tasks.total,
|
||||
|
@ -416,6 +429,7 @@ def _get_tasks(
|
|||
HumanTaskModel.id == HumanTaskUserModel.human_task_id,
|
||||
),
|
||||
)
|
||||
|
||||
if has_lane_assignment_id:
|
||||
if user_group_identifier:
|
||||
human_tasks_query = human_tasks_query.filter(
|
||||
|
|
|
@ -29,6 +29,13 @@ type OwnProps = {
|
|||
showStartedBy?: boolean;
|
||||
showWaitingOn?: boolean;
|
||||
textToShowIfEmpty?: string;
|
||||
shouldPaginateTable?: boolean;
|
||||
showProcessId?: boolean;
|
||||
showProcessModelIdentifier?: boolean;
|
||||
showTableDescriptionAsTooltip?: boolean;
|
||||
showDateStarted?: boolean;
|
||||
showLastUpdated?: boolean;
|
||||
hideIfNoTasks?: boolean;
|
||||
};
|
||||
|
||||
export default function TaskListTable({
|
||||
|
@ -42,6 +49,13 @@ export default function TaskListTable({
|
|||
autoReload = false,
|
||||
showStartedBy = true,
|
||||
showWaitingOn = true,
|
||||
shouldPaginateTable = true,
|
||||
showProcessId = true,
|
||||
showProcessModelIdentifier = true,
|
||||
showTableDescriptionAsTooltip = false,
|
||||
showDateStarted = true,
|
||||
showLastUpdated = true,
|
||||
hideIfNoTasks = false,
|
||||
}: OwnProps) {
|
||||
const [searchParams] = useSearchParams();
|
||||
const [tasks, setTasks] = useState<ProcessInstanceTask[] | null>(null);
|
||||
|
@ -89,10 +103,6 @@ export default function TaskListTable({
|
|||
) => {
|
||||
let fullUsernameString = '';
|
||||
let shortUsernameString = '';
|
||||
if (processInstanceTask.assigned_user_group_identifier) {
|
||||
fullUsernameString = processInstanceTask.assigned_user_group_identifier;
|
||||
shortUsernameString = processInstanceTask.assigned_user_group_identifier;
|
||||
}
|
||||
if (processInstanceTask.potential_owner_usernames) {
|
||||
fullUsernameString = processInstanceTask.potential_owner_usernames;
|
||||
const usernames =
|
||||
|
@ -103,82 +113,133 @@ export default function TaskListTable({
|
|||
}
|
||||
shortUsernameString = firstTwoUsernames.join(',');
|
||||
}
|
||||
if (processInstanceTask.assigned_user_group_identifier) {
|
||||
fullUsernameString = processInstanceTask.assigned_user_group_identifier;
|
||||
shortUsernameString = processInstanceTask.assigned_user_group_identifier;
|
||||
}
|
||||
return <span title={fullUsernameString}>{shortUsernameString}</span>;
|
||||
};
|
||||
|
||||
const buildTable = () => {
|
||||
if (!tasks) {
|
||||
return null;
|
||||
}
|
||||
const rows = tasks.map((row: ProcessInstanceTask) => {
|
||||
const taskUrl = `/tasks/${row.process_instance_id}/${row.task_id}`;
|
||||
const modifiedProcessModelIdentifier =
|
||||
modifyProcessIdentifierForPathParam(row.process_model_identifier);
|
||||
const getTableRow = (processInstanceTask: ProcessInstanceTask) => {
|
||||
const taskUrl = `/tasks/${processInstanceTask.process_instance_id}/${processInstanceTask.task_id}`;
|
||||
const modifiedProcessModelIdentifier = modifyProcessIdentifierForPathParam(
|
||||
processInstanceTask.process_model_identifier
|
||||
);
|
||||
|
||||
const regex = new RegExp(`\\b(${preferredUsername}|${userEmail})\\b`);
|
||||
let hasAccessToCompleteTask = false;
|
||||
if (row.potential_owner_usernames.match(regex)) {
|
||||
hasAccessToCompleteTask = true;
|
||||
}
|
||||
return (
|
||||
<tr key={row.id}>
|
||||
<td>
|
||||
<Link
|
||||
data-qa="process-instance-show-link"
|
||||
to={`/admin/process-instances/for-me/${modifiedProcessModelIdentifier}/${row.process_instance_id}`}
|
||||
title={`View process instance ${row.process_instance_id}`}
|
||||
>
|
||||
{row.process_instance_id}
|
||||
</Link>
|
||||
</td>
|
||||
<td>
|
||||
<Link
|
||||
data-qa="process-model-show-link"
|
||||
to={`/admin/process-models/${modifiedProcessModelIdentifier}`}
|
||||
title={row.process_model_identifier}
|
||||
>
|
||||
{row.process_model_display_name}
|
||||
</Link>
|
||||
</td>
|
||||
<td
|
||||
title={`task id: ${row.name}, spiffworkflow task guid: ${row.id}`}
|
||||
const regex = new RegExp(`\\b(${preferredUsername}|${userEmail})\\b`);
|
||||
let hasAccessToCompleteTask = false;
|
||||
if ((processInstanceTask.potential_owner_usernames || '').match(regex)) {
|
||||
hasAccessToCompleteTask = true;
|
||||
}
|
||||
const rowElements = [];
|
||||
if (showProcessId) {
|
||||
rowElements.push(
|
||||
<td>
|
||||
<Link
|
||||
data-qa="process-instance-show-link"
|
||||
to={`/admin/process-instances/for-me/${modifiedProcessModelIdentifier}/${processInstanceTask.process_instance_id}`}
|
||||
title={`View process instance ${processInstanceTask.process_instance_id}`}
|
||||
>
|
||||
{row.task_title}
|
||||
</td>
|
||||
{showStartedBy ? <td>{row.process_initiator_username}</td> : ''}
|
||||
{showWaitingOn ? <td>{getWaitingForTableCellComponent(row)}</td> : ''}
|
||||
<td>
|
||||
{convertSecondsToFormattedDateTime(row.created_at_in_seconds) ||
|
||||
'-'}
|
||||
</td>
|
||||
<TableCellWithTimeAgoInWords
|
||||
timeInSeconds={row.updated_at_in_seconds}
|
||||
/>
|
||||
<td>
|
||||
<Button
|
||||
variant="primary"
|
||||
href={taskUrl}
|
||||
hidden={row.process_instance_status === 'suspended'}
|
||||
disabled={!hasAccessToCompleteTask}
|
||||
>
|
||||
Go
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
{processInstanceTask.process_instance_id}
|
||||
</Link>
|
||||
</td>
|
||||
);
|
||||
});
|
||||
let tableHeaders = ['Id', 'Process', 'Task'];
|
||||
}
|
||||
if (showProcessModelIdentifier) {
|
||||
rowElements.push(
|
||||
<td>
|
||||
<Link
|
||||
data-qa="process-model-show-link"
|
||||
to={`/admin/process-models/${modifiedProcessModelIdentifier}`}
|
||||
title={processInstanceTask.process_model_identifier}
|
||||
>
|
||||
{processInstanceTask.process_model_display_name}
|
||||
</Link>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
rowElements.push(
|
||||
<td
|
||||
title={`task id: ${processInstanceTask.name}, spiffworkflow task guid: ${processInstanceTask.id}`}
|
||||
>
|
||||
{processInstanceTask.task_title}
|
||||
</td>
|
||||
);
|
||||
if (showStartedBy) {
|
||||
rowElements.push(
|
||||
<td>{processInstanceTask.process_initiator_username}</td>
|
||||
);
|
||||
}
|
||||
if (showWaitingOn) {
|
||||
rowElements.push(
|
||||
<td>{getWaitingForTableCellComponent(processInstanceTask)}</td>
|
||||
);
|
||||
}
|
||||
if (showDateStarted) {
|
||||
rowElements.push(
|
||||
<td>
|
||||
{convertSecondsToFormattedDateTime(
|
||||
processInstanceTask.created_at_in_seconds
|
||||
) || '-'}
|
||||
</td>
|
||||
);
|
||||
}
|
||||
if (showLastUpdated) {
|
||||
rowElements.push(
|
||||
<TableCellWithTimeAgoInWords
|
||||
timeInSeconds={processInstanceTask.updated_at_in_seconds}
|
||||
/>
|
||||
);
|
||||
}
|
||||
rowElements.push(
|
||||
<td>
|
||||
<Button
|
||||
variant="primary"
|
||||
href={taskUrl}
|
||||
hidden={processInstanceTask.process_instance_status === 'suspended'}
|
||||
disabled={!hasAccessToCompleteTask}
|
||||
>
|
||||
Go
|
||||
</Button>
|
||||
</td>
|
||||
);
|
||||
return <tr key={processInstanceTask.id}>{rowElements}</tr>;
|
||||
};
|
||||
|
||||
const getTableHeaders = () => {
|
||||
let tableHeaders = [];
|
||||
if (showProcessId) {
|
||||
tableHeaders.push('Id');
|
||||
}
|
||||
if (showProcessModelIdentifier) {
|
||||
tableHeaders.push('Process');
|
||||
}
|
||||
tableHeaders.push('Task');
|
||||
if (showStartedBy) {
|
||||
tableHeaders.push('Started By');
|
||||
}
|
||||
if (showWaitingOn) {
|
||||
tableHeaders.push('Waiting For');
|
||||
}
|
||||
tableHeaders = tableHeaders.concat([
|
||||
'Date Started',
|
||||
'Last Updated',
|
||||
'Actions',
|
||||
]);
|
||||
if (showDateStarted) {
|
||||
tableHeaders.push('Date Started');
|
||||
}
|
||||
if (showLastUpdated) {
|
||||
tableHeaders.push('Last Updated');
|
||||
}
|
||||
tableHeaders = tableHeaders.concat(['Actions']);
|
||||
return tableHeaders;
|
||||
};
|
||||
|
||||
const buildTable = () => {
|
||||
if (!tasks) {
|
||||
return null;
|
||||
}
|
||||
const tableHeaders = getTableHeaders();
|
||||
const rows = tasks.map((processInstanceTask: ProcessInstanceTask) => {
|
||||
return getTableRow(processInstanceTask);
|
||||
});
|
||||
return (
|
||||
<Table striped bordered>
|
||||
<thead>
|
||||
|
@ -207,24 +268,41 @@ export default function TaskListTable({
|
|||
undefined,
|
||||
paginationQueryParamPrefix
|
||||
);
|
||||
return (
|
||||
<PaginationForTable
|
||||
page={page}
|
||||
perPage={perPage}
|
||||
perPageOptions={[2, PER_PAGE_FOR_TASKS_ON_HOME_PAGE, 25]}
|
||||
pagination={pagination}
|
||||
tableToDisplay={buildTable()}
|
||||
paginationQueryParamPrefix={paginationQueryParamPrefix}
|
||||
paginationClassName={paginationClassName}
|
||||
/>
|
||||
let tableElement = (
|
||||
<div className={paginationClassName}>{buildTable()}</div>
|
||||
);
|
||||
if (shouldPaginateTable) {
|
||||
tableElement = (
|
||||
<PaginationForTable
|
||||
page={page}
|
||||
perPage={perPage}
|
||||
perPageOptions={[2, PER_PAGE_FOR_TASKS_ON_HOME_PAGE, 25]}
|
||||
pagination={pagination}
|
||||
tableToDisplay={buildTable()}
|
||||
paginationQueryParamPrefix={paginationQueryParamPrefix}
|
||||
paginationClassName={paginationClassName}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return tableElement;
|
||||
};
|
||||
|
||||
if (tasks) {
|
||||
const tableAndDescriptionElement = () => {
|
||||
if (showTableDescriptionAsTooltip) {
|
||||
return <h2 title={tableDescription}>{tableTitle}</h2>;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<h2>{tableTitle}</h2>
|
||||
<p className="data-table-description">{tableDescription}</p>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
if (tasks && (tasks.length > 0 || hideIfNoTasks === false)) {
|
||||
return (
|
||||
<>
|
||||
{tableAndDescriptionElement()}
|
||||
{tasksComponent()}
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -34,12 +34,12 @@ export interface ProcessInstanceTask {
|
|||
process_identifier: string;
|
||||
name: string;
|
||||
process_initiator_username: string;
|
||||
assigned_user_group_identifier: string;
|
||||
created_at_in_seconds: number;
|
||||
updated_at_in_seconds: number;
|
||||
current_user_is_potential_owner: number;
|
||||
potential_owner_usernames: string;
|
||||
calling_subprocess_task_id: string;
|
||||
potential_owner_usernames?: string;
|
||||
assigned_user_group_identifier?: string;
|
||||
}
|
||||
|
||||
export interface ProcessReference {
|
||||
|
|
|
@ -48,6 +48,7 @@ import {
|
|||
} from '../interfaces';
|
||||
import { usePermissionFetcher } from '../hooks/PermissionService';
|
||||
import ProcessInstanceClass from '../classes/ProcessInstanceClass';
|
||||
import TaskListTable from '../components/TaskListTable';
|
||||
|
||||
type OwnProps = {
|
||||
variant: string;
|
||||
|
@ -1009,6 +1010,26 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
|
|||
</Stack>
|
||||
<br />
|
||||
<br />
|
||||
<Grid condensed fullWidth>
|
||||
<Column md={6} lg={8} sm={4}>
|
||||
<TaskListTable
|
||||
apiPath="/tasks"
|
||||
additionalParams={`process_instance_id=${processInstance.id}`}
|
||||
tableTitle="Tasks I can complete"
|
||||
tableDescription="These are tasks that can be completed by you, either because they were assigned to a group you are in, or because they were assigned directly to you."
|
||||
paginationClassName="with-large-bottom-margin"
|
||||
textToShowIfEmpty="There are no tasks you can complete for this process instance."
|
||||
shouldPaginateTable={false}
|
||||
showProcessModelIdentifier={false}
|
||||
showProcessId={false}
|
||||
showStartedBy={false}
|
||||
showTableDescriptionAsTooltip
|
||||
showDateStarted={false}
|
||||
showLastUpdated={false}
|
||||
hideIfNoTasks
|
||||
/>
|
||||
</Column>
|
||||
</Grid>
|
||||
{getInfoTag()}
|
||||
<br />
|
||||
{taskUpdateDisplayArea()}
|
||||
|
|
Loading…
Reference in New Issue