mirror of
https://github.com/sartography/spiff-arena.git
synced 2025-02-11 00:46:33 +00:00
Merge pull request #31 from sartography/feature/home_page_redesign
Feature/home page redesign
This commit is contained in:
commit
58514ebefc
15
.github/workflows/backend_tests.yml
vendored
15
.github/workflows/backend_tests.yml
vendored
@ -159,11 +159,12 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload coverage data
|
- name: Upload coverage data
|
||||||
# pin to upload coverage from only one matrix entry, otherwise coverage gets confused later
|
# pin to upload coverage from only one matrix entry, otherwise coverage gets confused later
|
||||||
if: always() && matrix.session == 'tests' && matrix.python == '3.11' && matrix.os == 'ubuntu-latest'
|
if: always() && matrix.session == 'tests' && matrix.python == '3.11' && matrix.os == 'ubuntu-latest' && matrix.database == 'mysql'
|
||||||
uses: "actions/upload-artifact@v3.0.0"
|
uses: "actions/upload-artifact@v3.0.0"
|
||||||
|
# this action doesn't seem to respect working-directory so include working-directory value in path
|
||||||
with:
|
with:
|
||||||
name: coverage-data
|
name: coverage-data
|
||||||
path: ".coverage.*"
|
path: "spiffworkflow-backend/.coverage.*"
|
||||||
|
|
||||||
- name: Upload documentation
|
- name: Upload documentation
|
||||||
if: matrix.session == 'docs-build'
|
if: matrix.session == 'docs-build'
|
||||||
@ -221,7 +222,7 @@ jobs:
|
|||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: tests
|
needs: [tests, run_pre_commit_checks, check_docker_start_script]
|
||||||
steps:
|
steps:
|
||||||
- name: Check out the repository
|
- name: Check out the repository
|
||||||
uses: actions/checkout@v3.0.2
|
uses: actions/checkout@v3.0.2
|
||||||
@ -254,6 +255,8 @@ jobs:
|
|||||||
uses: actions/download-artifact@v3.0.1
|
uses: actions/download-artifact@v3.0.1
|
||||||
with:
|
with:
|
||||||
name: coverage-data
|
name: coverage-data
|
||||||
|
# this action doesn't seem to respect working-directory so include working-directory value in path
|
||||||
|
path: spiffworkflow-backend
|
||||||
|
|
||||||
- name: Combine coverage data and display human readable report
|
- name: Combine coverage data and display human readable report
|
||||||
run: |
|
run: |
|
||||||
@ -294,8 +297,8 @@ jobs:
|
|||||||
path: pr/
|
path: pr/
|
||||||
|
|
||||||
build-and-push-image:
|
build-and-push-image:
|
||||||
if: github.ref_name == 'main' && ${{ github.event_name == 'push' }}
|
needs: coverage
|
||||||
needs: tests
|
if: ${{ github.ref_name == 'main' && github.event_name == 'push' }}
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: ghcr.io
|
||||||
IMAGE_NAME: sartography/spiffworkflow-backend
|
IMAGE_NAME: sartography/spiffworkflow-backend
|
||||||
@ -312,7 +315,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Get current date
|
- name: Get current date
|
||||||
id: date
|
id: date
|
||||||
run: echo "::set-output name=date::$(date +%s)"
|
run: echo "date=$(date +%s)" >> $GITHUB_OUTPUT
|
||||||
- name: Log in to the Container registry
|
- name: Log in to the Container registry
|
||||||
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
|
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
|
||||||
with:
|
with:
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
# spiff-arena
|
# spiff-arena
|
||||||
|
|
||||||
|
This is a monorepo based on git subtrees that pulls together various
|
||||||
|
spiffworkflow-related projects. Here's an example command to push back to one
|
||||||
|
project:
|
||||||
|
|
||||||
git subtree push --prefix=spiffworkflow-frontend git@github.com:sartography/spiffworkflow-frontend.git add_md_file
|
git subtree push --prefix=spiffworkflow-frontend git@github.com:sartography/spiffworkflow-frontend.git add_md_file
|
||||||
|
|
||||||
|
# run all lint checks and tests
|
||||||
|
|
||||||
# run pyl
|
|
||||||
`./bin/run_pyl`
|
`./bin/run_pyl`
|
||||||
|
|
||||||
Requires at root:
|
Requires at root:
|
||||||
|
@ -156,7 +156,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload coverage data
|
- name: Upload coverage data
|
||||||
# pin to upload coverage from only one matrix entry, otherwise coverage gets confused later
|
# pin to upload coverage from only one matrix entry, otherwise coverage gets confused later
|
||||||
if: always() && matrix.session == 'tests' && matrix.python == '3.11' && matrix.os == 'ubuntu-latest'
|
if: always() && matrix.session == 'tests' && matrix.python == '3.11' && matrix.os == 'ubuntu-latest' && matrix.database == 'mysql'
|
||||||
uses: "actions/upload-artifact@v3.0.0"
|
uses: "actions/upload-artifact@v3.0.0"
|
||||||
with:
|
with:
|
||||||
name: coverage-data
|
name: coverage-data
|
||||||
|
@ -39,11 +39,14 @@ class MyJSONEncoder(DefaultJSONProvider):
|
|||||||
return_dict = {}
|
return_dict = {}
|
||||||
for row_key in obj.keys():
|
for row_key in obj.keys():
|
||||||
row_value = obj[row_key]
|
row_value = obj[row_key]
|
||||||
if hasattr(row_value, "__dict__"):
|
if hasattr(row_value, "serialized"):
|
||||||
|
return_dict.update(row_value.serialized)
|
||||||
|
elif hasattr(row_value, "__dict__"):
|
||||||
return_dict.update(row_value.__dict__)
|
return_dict.update(row_value.__dict__)
|
||||||
else:
|
else:
|
||||||
return_dict.update({row_key: row_value})
|
return_dict.update({row_key: row_value})
|
||||||
return_dict.pop("_sa_instance_state")
|
if "_sa_instance_state" in return_dict:
|
||||||
|
return_dict.pop("_sa_instance_state")
|
||||||
return return_dict
|
return return_dict
|
||||||
return super().default(obj)
|
return super().default(obj)
|
||||||
|
|
||||||
|
@ -872,6 +872,64 @@ paths:
|
|||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/Task"
|
$ref: "#/components/schemas/Task"
|
||||||
|
|
||||||
|
/tasks/for-my-open-processes:
|
||||||
|
parameters:
|
||||||
|
- name: page
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: The page number to return. Defaults to page 1.
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
- name: per_page
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: The page number to return. Defaults to page 1.
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- Process Instances
|
||||||
|
operationId: spiffworkflow_backend.routes.process_api_blueprint.task_list_for_my_open_processes
|
||||||
|
summary: returns the list of tasks for given user's open process instances
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: list of tasks
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/Task"
|
||||||
|
|
||||||
|
/tasks/for-processes-started-by-others:
|
||||||
|
parameters:
|
||||||
|
- name: page
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: The page number to return. Defaults to page 1.
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
- name: per_page
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: The page number to return. Defaults to page 1.
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- Process Instances
|
||||||
|
operationId: spiffworkflow_backend.routes.process_api_blueprint.task_list_for_processes_started_by_others
|
||||||
|
summary: returns the list of tasks for given user's open process instances
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: list of tasks
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/Task"
|
||||||
|
|
||||||
/process-instance/{process_instance_id}/tasks:
|
/process-instance/{process_instance_id}/tasks:
|
||||||
parameters:
|
parameters:
|
||||||
- name: process_instance_id
|
- name: process_instance_id
|
||||||
|
@ -100,17 +100,17 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel):
|
|||||||
local_bpmn_xml_file_contents = ""
|
local_bpmn_xml_file_contents = ""
|
||||||
if self.bpmn_xml_file_contents:
|
if self.bpmn_xml_file_contents:
|
||||||
local_bpmn_xml_file_contents = self.bpmn_xml_file_contents.decode("utf-8")
|
local_bpmn_xml_file_contents = self.bpmn_xml_file_contents.decode("utf-8")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"id": self.id,
|
"id": self.id,
|
||||||
"process_model_identifier": self.process_model_identifier,
|
"process_model_identifier": self.process_model_identifier,
|
||||||
"process_group_identifier": self.process_group_identifier,
|
"process_group_identifier": self.process_group_identifier,
|
||||||
"status": self.status,
|
"status": self.status,
|
||||||
"bpmn_json": self.bpmn_json,
|
|
||||||
"start_in_seconds": self.start_in_seconds,
|
"start_in_seconds": self.start_in_seconds,
|
||||||
"end_in_seconds": self.end_in_seconds,
|
"end_in_seconds": self.end_in_seconds,
|
||||||
"process_initiator_id": self.process_initiator_id,
|
"process_initiator_id": self.process_initiator_id,
|
||||||
"bpmn_xml_file_contents": local_bpmn_xml_file_contents,
|
"bpmn_xml_file_contents": local_bpmn_xml_file_contents,
|
||||||
|
"bpmn_version_control_identifier": self.bpmn_version_control_identifier,
|
||||||
|
"bpmn_version_control_type": self.bpmn_version_control_type,
|
||||||
"spiff_step": self.spiff_step,
|
"spiff_step": self.spiff_step,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ from lxml import etree # type: ignore
|
|||||||
from lxml.builder import ElementMaker # type: ignore
|
from lxml.builder import ElementMaker # type: ignore
|
||||||
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
||||||
from SpiffWorkflow.task import TaskState
|
from SpiffWorkflow.task import TaskState
|
||||||
|
from sqlalchemy import and_
|
||||||
from sqlalchemy import asc
|
from sqlalchemy import asc
|
||||||
from sqlalchemy import desc
|
from sqlalchemy import desc
|
||||||
|
|
||||||
@ -37,6 +38,7 @@ from spiffworkflow_backend.exceptions.process_entity_not_found_error import (
|
|||||||
from spiffworkflow_backend.models.active_task import ActiveTaskModel
|
from spiffworkflow_backend.models.active_task import ActiveTaskModel
|
||||||
from spiffworkflow_backend.models.active_task_user import ActiveTaskUserModel
|
from spiffworkflow_backend.models.active_task_user import ActiveTaskUserModel
|
||||||
from spiffworkflow_backend.models.file import FileSchema
|
from spiffworkflow_backend.models.file import FileSchema
|
||||||
|
from spiffworkflow_backend.models.group import GroupModel
|
||||||
from spiffworkflow_backend.models.message_instance import MessageInstanceModel
|
from spiffworkflow_backend.models.message_instance import MessageInstanceModel
|
||||||
from spiffworkflow_backend.models.message_model import MessageModel
|
from spiffworkflow_backend.models.message_model import MessageModel
|
||||||
from spiffworkflow_backend.models.message_triggerable_process_model import (
|
from spiffworkflow_backend.models.message_triggerable_process_model import (
|
||||||
@ -1000,6 +1002,67 @@ def task_list_my_tasks(page: int = 1, per_page: int = 100) -> flask.wrappers.Res
|
|||||||
return make_response(jsonify(response_json), 200)
|
return make_response(jsonify(response_json), 200)
|
||||||
|
|
||||||
|
|
||||||
|
def task_list_for_my_open_processes(
|
||||||
|
page: int = 1, per_page: int = 100
|
||||||
|
) -> flask.wrappers.Response:
|
||||||
|
"""Task_list_for_my_open_processes."""
|
||||||
|
return get_tasks(page=page, per_page=per_page)
|
||||||
|
|
||||||
|
|
||||||
|
def task_list_for_processes_started_by_others(
|
||||||
|
page: int = 1, per_page: int = 100
|
||||||
|
) -> flask.wrappers.Response:
|
||||||
|
"""Task_list_for_processes_started_by_others."""
|
||||||
|
return get_tasks(processes_started_by_user=False, page=page, per_page=per_page)
|
||||||
|
|
||||||
|
|
||||||
|
def get_tasks(
|
||||||
|
processes_started_by_user: bool = True, page: int = 1, per_page: int = 100
|
||||||
|
) -> flask.wrappers.Response:
|
||||||
|
"""Get_tasks."""
|
||||||
|
user_id = g.user.id
|
||||||
|
active_tasks_query = (
|
||||||
|
ActiveTaskModel.query.outerjoin(
|
||||||
|
GroupModel, GroupModel.id == ActiveTaskModel.lane_assignment_id
|
||||||
|
)
|
||||||
|
.join(ProcessInstanceModel)
|
||||||
|
.join(UserModel, UserModel.id == ProcessInstanceModel.process_initiator_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
if processes_started_by_user:
|
||||||
|
active_tasks_query = active_tasks_query.filter(
|
||||||
|
ProcessInstanceModel.process_initiator_id == user_id
|
||||||
|
).outerjoin(ActiveTaskUserModel, and_(ActiveTaskUserModel.user_id == user_id))
|
||||||
|
else:
|
||||||
|
active_tasks_query = active_tasks_query.filter(
|
||||||
|
ProcessInstanceModel.process_initiator_id != user_id
|
||||||
|
).join(ActiveTaskUserModel, and_(ActiveTaskUserModel.user_id == user_id))
|
||||||
|
|
||||||
|
active_tasks = active_tasks_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,
|
||||||
|
GroupModel.identifier.label("group_identifier"),
|
||||||
|
ActiveTaskModel.task_name,
|
||||||
|
ActiveTaskModel.task_title,
|
||||||
|
ActiveTaskModel.process_model_display_name,
|
||||||
|
ActiveTaskModel.process_instance_id,
|
||||||
|
ActiveTaskUserModel.user_id.label("current_user_is_potential_owner"),
|
||||||
|
).paginate(page=page, per_page=per_page, error_out=False)
|
||||||
|
|
||||||
|
response_json = {
|
||||||
|
"results": active_tasks.items,
|
||||||
|
"pagination": {
|
||||||
|
"count": len(active_tasks.items),
|
||||||
|
"total": active_tasks.total,
|
||||||
|
"pages": active_tasks.pages,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return make_response(jsonify(response_json), 200)
|
||||||
|
|
||||||
|
|
||||||
def process_instance_task_list(
|
def process_instance_task_list(
|
||||||
process_instance_id: int, all_tasks: bool = False, spiff_step: int = 0
|
process_instance_id: int, all_tasks: bool = False, spiff_step: int = 0
|
||||||
) -> flask.wrappers.Response:
|
) -> flask.wrappers.Response:
|
||||||
@ -1354,9 +1417,18 @@ def find_process_instance_by_id_or_raise(
|
|||||||
process_instance_id: int,
|
process_instance_id: int,
|
||||||
) -> ProcessInstanceModel:
|
) -> ProcessInstanceModel:
|
||||||
"""Find_process_instance_by_id_or_raise."""
|
"""Find_process_instance_by_id_or_raise."""
|
||||||
process_instance = ProcessInstanceModel.query.filter_by(
|
process_instance_query = ProcessInstanceModel.query.filter_by(
|
||||||
id=process_instance_id
|
id=process_instance_id
|
||||||
).first()
|
)
|
||||||
|
|
||||||
|
# we had a frustrating session trying to do joins and access columns from two tables. here's some notes for our future selves:
|
||||||
|
# this returns an object that allows you to do: process_instance.UserModel.username
|
||||||
|
# process_instance = db.session.query(ProcessInstanceModel, UserModel).filter_by(id=process_instance_id).first()
|
||||||
|
# you can also use splat with add_columns, but it still didn't ultimately give us access to the process instance
|
||||||
|
# attributes or username like we wanted:
|
||||||
|
# process_instance_query.join(UserModel).add_columns(*ProcessInstanceModel.__table__.columns, UserModel.username)
|
||||||
|
|
||||||
|
process_instance = process_instance_query.first()
|
||||||
if process_instance is None:
|
if process_instance is None:
|
||||||
raise (
|
raise (
|
||||||
ApiError(
|
ApiError(
|
||||||
|
@ -1441,7 +1441,7 @@ class TestProcessApi(BaseTest):
|
|||||||
updated_at_in_seconds=round(time.time()),
|
updated_at_in_seconds=round(time.time()),
|
||||||
start_in_seconds=(1000 * i) + 1000,
|
start_in_seconds=(1000 * i) + 1000,
|
||||||
end_in_seconds=(1000 * i) + 2000,
|
end_in_seconds=(1000 * i) + 2000,
|
||||||
bpmn_json=json.dumps({"i": i}),
|
bpmn_version_control_identifier=i,
|
||||||
)
|
)
|
||||||
db.session.add(process_instance)
|
db.session.add(process_instance)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
@ -1487,7 +1487,12 @@ class TestProcessApi(BaseTest):
|
|||||||
results = response.json["results"]
|
results = response.json["results"]
|
||||||
assert len(results) == 4
|
assert len(results) == 4
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
assert json.loads(results[i]["bpmn_json"])["i"] in (1, 2, 3, 4)
|
assert json.loads(results[i]["bpmn_version_control_identifier"]) in (
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
)
|
||||||
|
|
||||||
# start > 2000, end < 5000 - this should eliminate the first 2 and the last
|
# start > 2000, end < 5000 - this should eliminate the first 2 and the last
|
||||||
response = client.get(
|
response = client.get(
|
||||||
@ -1497,8 +1502,8 @@ class TestProcessApi(BaseTest):
|
|||||||
assert response.json is not None
|
assert response.json is not None
|
||||||
results = response.json["results"]
|
results = response.json["results"]
|
||||||
assert len(results) == 2
|
assert len(results) == 2
|
||||||
assert json.loads(results[0]["bpmn_json"])["i"] in (2, 3)
|
assert json.loads(results[0]["bpmn_version_control_identifier"]) in (2, 3)
|
||||||
assert json.loads(results[1]["bpmn_json"])["i"] in (2, 3)
|
assert json.loads(results[1]["bpmn_version_control_identifier"]) in (2, 3)
|
||||||
|
|
||||||
# start > 1000, start < 4000 - this should eliminate the first and the last 2
|
# start > 1000, start < 4000 - this should eliminate the first and the last 2
|
||||||
response = client.get(
|
response = client.get(
|
||||||
@ -1508,8 +1513,8 @@ class TestProcessApi(BaseTest):
|
|||||||
assert response.json is not None
|
assert response.json is not None
|
||||||
results = response.json["results"]
|
results = response.json["results"]
|
||||||
assert len(results) == 2
|
assert len(results) == 2
|
||||||
assert json.loads(results[0]["bpmn_json"])["i"] in (1, 2)
|
assert json.loads(results[0]["bpmn_version_control_identifier"]) in (1, 2)
|
||||||
assert json.loads(results[1]["bpmn_json"])["i"] in (1, 2)
|
assert json.loads(results[1]["bpmn_version_control_identifier"]) in (1, 2)
|
||||||
|
|
||||||
# end > 2000, end < 6000 - this should eliminate the first and the last
|
# end > 2000, end < 6000 - this should eliminate the first and the last
|
||||||
response = client.get(
|
response = client.get(
|
||||||
@ -1520,7 +1525,11 @@ class TestProcessApi(BaseTest):
|
|||||||
results = response.json["results"]
|
results = response.json["results"]
|
||||||
assert len(results) == 3
|
assert len(results) == 3
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
assert json.loads(results[i]["bpmn_json"])["i"] in (1, 2, 3)
|
assert json.loads(results[i]["bpmn_version_control_identifier"]) in (
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
)
|
||||||
|
|
||||||
def test_process_instance_report_list(
|
def test_process_instance_report_list(
|
||||||
self,
|
self,
|
||||||
|
@ -169,14 +169,13 @@ describe('process-instances', () => {
|
|||||||
cy.getBySel('process-instance-list-link').click();
|
cy.getBySel('process-instance-list-link').click();
|
||||||
cy.assertAtLeastOneItemInPaginatedResults();
|
cy.assertAtLeastOneItemInPaginatedResults();
|
||||||
|
|
||||||
|
const statusSelect = '#process-instance-status-select';
|
||||||
PROCESS_STATUSES.forEach((processStatus) => {
|
PROCESS_STATUSES.forEach((processStatus) => {
|
||||||
if (!['all', 'waiting'].includes(processStatus)) {
|
if (!['all', 'waiting'].includes(processStatus)) {
|
||||||
cy.get('#process-instance-status-select').click();
|
cy.get(statusSelect).click();
|
||||||
cy.get('#process-instance-status-select')
|
cy.get(statusSelect).contains(processStatus).click();
|
||||||
.contains(processStatus)
|
|
||||||
.click();
|
|
||||||
// close the dropdown again
|
// close the dropdown again
|
||||||
cy.get('#process-instance-status-select').click();
|
cy.get(statusSelect).click();
|
||||||
cy.getBySel('filter-button').click();
|
cy.getBySel('filter-button').click();
|
||||||
cy.assertAtLeastOneItemInPaginatedResults();
|
cy.assertAtLeastOneItemInPaginatedResults();
|
||||||
cy.getBySel(`process-instance-status-${processStatus}`).contains(
|
cy.getBySel(`process-instance-status-${processStatus}`).contains(
|
||||||
|
@ -144,10 +144,11 @@ describe('process-models', () => {
|
|||||||
|
|
||||||
cy.getBySel('process-instance-list-link').click();
|
cy.getBySel('process-instance-list-link').click();
|
||||||
cy.getBySel('process-instance-show-link').click();
|
cy.getBySel('process-instance-show-link').click();
|
||||||
cy.contains('Delete').click();
|
cy.getBySel('process-instance-delete').click();
|
||||||
cy.contains('Are you sure');
|
cy.contains('Are you sure');
|
||||||
cy.getBySel('modal-confirmation-dialog').find('.cds--btn--danger').click();
|
cy.getBySel('modal-confirmation-dialog').find('.cds--btn--danger').click();
|
||||||
cy.contains(`Process Instances for: ${groupId}/${modelId}`);
|
|
||||||
|
// in breadcrumb
|
||||||
cy.contains(modelId).click();
|
cy.contains(modelId).click();
|
||||||
|
|
||||||
cy.contains('Edit process model').click();
|
cy.contains('Edit process model').click();
|
||||||
|
5
spiffworkflow-frontend/package-lock.json
generated
5
spiffworkflow-frontend/package-lock.json
generated
@ -46545,7 +46545,7 @@
|
|||||||
"@csstools/postcss-text-decoration-shorthand": "^1.0.0",
|
"@csstools/postcss-text-decoration-shorthand": "^1.0.0",
|
||||||
"@csstools/postcss-trigonometric-functions": "^1.0.2",
|
"@csstools/postcss-trigonometric-functions": "^1.0.2",
|
||||||
"@csstools/postcss-unset-value": "^1.0.2",
|
"@csstools/postcss-unset-value": "^1.0.2",
|
||||||
"autoprefixer": "10.4.8",
|
"autoprefixer": "10.4.5",
|
||||||
"browserslist": "^4.21.3",
|
"browserslist": "^4.21.3",
|
||||||
"css-blank-pseudo": "^3.0.3",
|
"css-blank-pseudo": "^3.0.3",
|
||||||
"css-has-pseudo": "^3.0.4",
|
"css-has-pseudo": "^3.0.4",
|
||||||
@ -46583,7 +46583,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"autoprefixer": {
|
"autoprefixer": {
|
||||||
"version": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.5.tgz",
|
"version": "10.4.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.5.tgz",
|
||||||
"integrity": "sha512-Fvd8yCoA7lNX/OUllvS+aS1I7WRBclGXsepbvT8ZaPgrH24rgXpZzF0/6Hh3ZEkwg+0AES/Osd196VZmYoEFtw==",
|
"integrity": "sha512-Fvd8yCoA7lNX/OUllvS+aS1I7WRBclGXsepbvT8ZaPgrH24rgXpZzF0/6Hh3ZEkwg+0AES/Osd196VZmYoEFtw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"browserslist": "^4.20.2",
|
"browserslist": "^4.20.2",
|
||||||
|
@ -6,8 +6,7 @@ import { BrowserRouter, Routes, Route } from 'react-router-dom';
|
|||||||
import ErrorContext from './contexts/ErrorContext';
|
import ErrorContext from './contexts/ErrorContext';
|
||||||
import NavigationBar from './components/NavigationBar';
|
import NavigationBar from './components/NavigationBar';
|
||||||
|
|
||||||
import HomePage from './routes/HomePage';
|
import HomePageRoutes from './routes/HomePageRoutes';
|
||||||
import TaskShow from './routes/TaskShow';
|
|
||||||
import ErrorBoundary from './components/ErrorBoundary';
|
import ErrorBoundary from './components/ErrorBoundary';
|
||||||
import AdminRoutes from './routes/AdminRoutes';
|
import AdminRoutes from './routes/AdminRoutes';
|
||||||
import { ErrorForDisplay } from './interfaces';
|
import { ErrorForDisplay } from './interfaces';
|
||||||
@ -54,17 +53,9 @@ export default function App() {
|
|||||||
{errorTag}
|
{errorTag}
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<HomePage />} />
|
<Route path="/" element={<HomePageRoutes />} />
|
||||||
<Route path="/tasks" element={<HomePage />} />
|
<Route path="/tasks/*" element={<HomePageRoutes />} />
|
||||||
<Route path="/admin/*" element={<AdminRoutes />} />
|
<Route path="/admin/*" element={<AdminRoutes />} />
|
||||||
<Route
|
|
||||||
path="/tasks/:process_instance_id/:task_id"
|
|
||||||
element={<TaskShow />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/tasks/:process_instance_id/:task_id"
|
|
||||||
element={<TaskShow />}
|
|
||||||
/>
|
|
||||||
</Routes>
|
</Routes>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</Content>
|
</Content>
|
||||||
|
@ -3,6 +3,7 @@ import { useState } from 'react';
|
|||||||
import { Button, Modal } from '@carbon/react';
|
import { Button, Modal } from '@carbon/react';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
|
'data-qa'?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
buttonLabel?: string;
|
buttonLabel?: string;
|
||||||
onConfirmation: (..._args: any[]) => any;
|
onConfirmation: (..._args: any[]) => any;
|
||||||
@ -18,6 +19,7 @@ export default function ButtonWithConfirmation({
|
|||||||
description,
|
description,
|
||||||
buttonLabel,
|
buttonLabel,
|
||||||
onConfirmation,
|
onConfirmation,
|
||||||
|
'data-qa': dataQa,
|
||||||
title = 'Are you sure?',
|
title = 'Are you sure?',
|
||||||
confirmButtonLabel = 'OK',
|
confirmButtonLabel = 'OK',
|
||||||
kind = 'danger',
|
kind = 'danger',
|
||||||
@ -58,6 +60,7 @@ export default function ButtonWithConfirmation({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
|
data-qa={dataQa}
|
||||||
onClick={handleShowConfirmationPrompt}
|
onClick={handleShowConfirmationPrompt}
|
||||||
kind={kind}
|
kind={kind}
|
||||||
renderIcon={renderIcon}
|
renderIcon={renderIcon}
|
||||||
|
@ -0,0 +1,139 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
// @ts-ignore
|
||||||
|
import { Button, Table } from '@carbon/react';
|
||||||
|
import { Link, useSearchParams } from 'react-router-dom';
|
||||||
|
import PaginationForTable from './PaginationForTable';
|
||||||
|
import {
|
||||||
|
convertSecondsToFormattedDateTime,
|
||||||
|
getPageInfoFromSearchParams,
|
||||||
|
modifyProcessModelPath,
|
||||||
|
} from '../helpers';
|
||||||
|
import HttpService from '../services/HttpService';
|
||||||
|
import { PaginationObject } from '../interfaces';
|
||||||
|
|
||||||
|
const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5;
|
||||||
|
|
||||||
|
export default function MyTasksForProcessesStartedByOthers() {
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const [tasks, setTasks] = useState([]);
|
||||||
|
const [pagination, setPagination] = useState<PaginationObject | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const { page, perPage } = getPageInfoFromSearchParams(
|
||||||
|
searchParams,
|
||||||
|
PER_PAGE_FOR_TASKS_ON_HOME_PAGE
|
||||||
|
);
|
||||||
|
const setTasksFromResult = (result: any) => {
|
||||||
|
setTasks(result.results);
|
||||||
|
setPagination(result.pagination);
|
||||||
|
};
|
||||||
|
HttpService.makeCallToBackend({
|
||||||
|
path: `/tasks/for-processes-started-by-others?per_page=${perPage}&page=${page}`,
|
||||||
|
successCallback: setTasksFromResult,
|
||||||
|
});
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
const buildTable = () => {
|
||||||
|
const rows = tasks.map((row) => {
|
||||||
|
const rowToUse = row as any;
|
||||||
|
const taskUrl = `/tasks/${rowToUse.process_instance_id}/${rowToUse.task_id}`;
|
||||||
|
const modifiedProcessModelIdentifier = modifyProcessModelPath(
|
||||||
|
rowToUse.process_model_identifier
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<tr key={rowToUse.id}>
|
||||||
|
<td>
|
||||||
|
<Link
|
||||||
|
data-qa="process-model-show-link"
|
||||||
|
to={`/admin/process-models/${modifiedProcessModelIdentifier}`}
|
||||||
|
>
|
||||||
|
{rowToUse.process_model_display_name}
|
||||||
|
</Link>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Link
|
||||||
|
data-qa="process-instance-show-link"
|
||||||
|
to={`/admin/process-models/${modifiedProcessModelIdentifier}/process-instances/${rowToUse.process_instance_id}`}
|
||||||
|
>
|
||||||
|
View {rowToUse.process_instance_id}
|
||||||
|
</Link>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
title={`task id: ${rowToUse.name}, spiffworkflow task guid: ${rowToUse.id}`}
|
||||||
|
>
|
||||||
|
{rowToUse.task_title}
|
||||||
|
</td>
|
||||||
|
<td>{rowToUse.username}</td>
|
||||||
|
<td>{rowToUse.process_instance_status}</td>
|
||||||
|
<td>{rowToUse.group_identifier || '-'}</td>
|
||||||
|
<td>
|
||||||
|
{convertSecondsToFormattedDateTime(
|
||||||
|
rowToUse.created_at_in_seconds
|
||||||
|
) || '-'}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{convertSecondsToFormattedDateTime(
|
||||||
|
rowToUse.updated_at_in_seconds
|
||||||
|
) || '-'}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
href={taskUrl}
|
||||||
|
hidden={rowToUse.process_instance_status === 'suspended'}
|
||||||
|
disabled={!rowToUse.current_user_is_potential_owner}
|
||||||
|
>
|
||||||
|
Go
|
||||||
|
</Button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<Table striped bordered>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Process Model</th>
|
||||||
|
<th>Process Instance</th>
|
||||||
|
<th>Task Name</th>
|
||||||
|
<th>Process Started By</th>
|
||||||
|
<th>Process Instance Status</th>
|
||||||
|
<th>Assigned Group</th>
|
||||||
|
<th>Process Started</th>
|
||||||
|
<th>Process Updated</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>{rows}</tbody>
|
||||||
|
</Table>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const tasksComponent = () => {
|
||||||
|
if (pagination && pagination.total < 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const { page, perPage } = getPageInfoFromSearchParams(
|
||||||
|
searchParams,
|
||||||
|
PER_PAGE_FOR_TASKS_ON_HOME_PAGE
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h1>Tasks waiting for me</h1>
|
||||||
|
<PaginationForTable
|
||||||
|
page={page}
|
||||||
|
perPage={perPage}
|
||||||
|
perPageOptions={[2, PER_PAGE_FOR_TASKS_ON_HOME_PAGE, 25]}
|
||||||
|
pagination={pagination}
|
||||||
|
tableToDisplay={buildTable()}
|
||||||
|
path="/tasks/for-my-open-processes"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (pagination) {
|
||||||
|
return tasksComponent();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
@ -0,0 +1,137 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
// @ts-ignore
|
||||||
|
import { Button, Table } from '@carbon/react';
|
||||||
|
import { Link, useSearchParams } from 'react-router-dom';
|
||||||
|
import PaginationForTable from './PaginationForTable';
|
||||||
|
import {
|
||||||
|
convertSecondsToFormattedDateTime,
|
||||||
|
getPageInfoFromSearchParams,
|
||||||
|
modifyProcessModelPath,
|
||||||
|
} from '../helpers';
|
||||||
|
import HttpService from '../services/HttpService';
|
||||||
|
import { PaginationObject } from '../interfaces';
|
||||||
|
|
||||||
|
const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5;
|
||||||
|
|
||||||
|
export default function MyOpenProcesses() {
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const [tasks, setTasks] = useState([]);
|
||||||
|
const [pagination, setPagination] = useState<PaginationObject | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const { page, perPage } = getPageInfoFromSearchParams(
|
||||||
|
searchParams,
|
||||||
|
PER_PAGE_FOR_TASKS_ON_HOME_PAGE
|
||||||
|
);
|
||||||
|
const setTasksFromResult = (result: any) => {
|
||||||
|
setTasks(result.results);
|
||||||
|
setPagination(result.pagination);
|
||||||
|
};
|
||||||
|
HttpService.makeCallToBackend({
|
||||||
|
path: `/tasks/for-my-open-processes?per_page=${perPage}&page=${page}`,
|
||||||
|
successCallback: setTasksFromResult,
|
||||||
|
});
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
const buildTable = () => {
|
||||||
|
const rows = tasks.map((row) => {
|
||||||
|
const rowToUse = row as any;
|
||||||
|
const taskUrl = `/tasks/${rowToUse.process_instance_id}/${rowToUse.task_id}`;
|
||||||
|
const modifiedProcessModelIdentifier = modifyProcessModelPath(
|
||||||
|
rowToUse.process_model_identifier
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<tr key={rowToUse.id}>
|
||||||
|
<td>
|
||||||
|
<Link
|
||||||
|
data-qa="process-model-show-link"
|
||||||
|
to={`/admin/process-models/${modifiedProcessModelIdentifier}`}
|
||||||
|
>
|
||||||
|
{rowToUse.process_model_display_name}
|
||||||
|
</Link>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Link
|
||||||
|
data-qa="process-instance-show-link"
|
||||||
|
to={`/admin/process-models/${modifiedProcessModelIdentifier}/process-instances/${rowToUse.process_instance_id}`}
|
||||||
|
>
|
||||||
|
View {rowToUse.process_instance_id}
|
||||||
|
</Link>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
title={`task id: ${rowToUse.name}, spiffworkflow task guid: ${rowToUse.id}`}
|
||||||
|
>
|
||||||
|
{rowToUse.task_title}
|
||||||
|
</td>
|
||||||
|
<td>{rowToUse.process_instance_status}</td>
|
||||||
|
<td>{rowToUse.group_identifier || '-'}</td>
|
||||||
|
<td>
|
||||||
|
{convertSecondsToFormattedDateTime(
|
||||||
|
rowToUse.created_at_in_seconds
|
||||||
|
) || '-'}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{convertSecondsToFormattedDateTime(
|
||||||
|
rowToUse.updated_at_in_seconds
|
||||||
|
) || '-'}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
href={taskUrl}
|
||||||
|
hidden={rowToUse.process_instance_status === 'suspended'}
|
||||||
|
disabled={!rowToUse.current_user_is_potential_owner}
|
||||||
|
>
|
||||||
|
Go
|
||||||
|
</Button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<Table striped bordered>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Process Model</th>
|
||||||
|
<th>Process Instance</th>
|
||||||
|
<th>Task Name</th>
|
||||||
|
<th>Process Instance Status</th>
|
||||||
|
<th>Assigned Group</th>
|
||||||
|
<th>Process Started</th>
|
||||||
|
<th>Process Updated</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>{rows}</tbody>
|
||||||
|
</Table>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const tasksComponent = () => {
|
||||||
|
if (pagination && pagination.total < 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const { page, perPage } = getPageInfoFromSearchParams(
|
||||||
|
searchParams,
|
||||||
|
PER_PAGE_FOR_TASKS_ON_HOME_PAGE
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h1>Tasks for my open processes</h1>
|
||||||
|
<PaginationForTable
|
||||||
|
page={page}
|
||||||
|
perPage={perPage}
|
||||||
|
perPageOptions={[2, PER_PAGE_FOR_TASKS_ON_HOME_PAGE, 25]}
|
||||||
|
pagination={pagination}
|
||||||
|
tableToDisplay={buildTable()}
|
||||||
|
path="/tasks/for-my-open-processes"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (pagination) {
|
||||||
|
return tasksComponent();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
12
spiffworkflow-frontend/src/routes/GroupedTasks.tsx
Normal file
12
spiffworkflow-frontend/src/routes/GroupedTasks.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import MyTasksForProcessesStartedByOthers from '../components/MyTasksForProcessesStartedByOthers';
|
||||||
|
import TasksForMyOpenProcesses from '../components/TasksForMyOpenProcesses';
|
||||||
|
|
||||||
|
export default function GroupedTasks() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TasksForMyOpenProcesses />
|
||||||
|
<br />
|
||||||
|
<MyTasksForProcessesStartedByOthers />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
42
spiffworkflow-frontend/src/routes/HomePageRoutes.tsx
Normal file
42
spiffworkflow-frontend/src/routes/HomePageRoutes.tsx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { useContext, useEffect, useState } from 'react';
|
||||||
|
import { Route, Routes, useLocation, useNavigate } from 'react-router-dom';
|
||||||
|
// @ts-ignore
|
||||||
|
import { Tabs, TabList, Tab } from '@carbon/react';
|
||||||
|
import TaskShow from './TaskShow';
|
||||||
|
import ErrorContext from '../contexts/ErrorContext';
|
||||||
|
import MyTasks from './MyTasks';
|
||||||
|
import GroupedTasks from './GroupedTasks';
|
||||||
|
|
||||||
|
export default function HomePageRoutes() {
|
||||||
|
const location = useLocation();
|
||||||
|
const setErrorMessage = (useContext as any)(ErrorContext)[1];
|
||||||
|
const [selectedTabIndex, setSelectedTabIndex] = useState<number>(0);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setErrorMessage(null);
|
||||||
|
let newSelectedTabIndex = 0;
|
||||||
|
if (location.pathname.match(/^\/tasks\/grouped\b/)) {
|
||||||
|
newSelectedTabIndex = 1;
|
||||||
|
}
|
||||||
|
setSelectedTabIndex(newSelectedTabIndex);
|
||||||
|
}, [location, setErrorMessage]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Tabs selectedIndex={selectedTabIndex}>
|
||||||
|
<TabList aria-label="List of tabs">
|
||||||
|
<Tab onClick={() => navigate('/tasks/my-tasks')}>My Tasks</Tab>
|
||||||
|
<Tab onClick={() => navigate('/tasks/grouped')}>Grouped Tasks</Tab>
|
||||||
|
</TabList>
|
||||||
|
</Tabs>
|
||||||
|
<br />
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<MyTasks />} />
|
||||||
|
<Route path="my-tasks" element={<MyTasks />} />
|
||||||
|
<Route path=":process_instance_id/:task_id" element={<TaskShow />} />
|
||||||
|
<Route path="grouped" element={<GroupedTasks />} />
|
||||||
|
</Routes>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -12,7 +12,7 @@ import { PaginationObject, RecentProcessModel } from '../interfaces';
|
|||||||
|
|
||||||
const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5;
|
const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5;
|
||||||
|
|
||||||
export default function HomePage() {
|
export default function MyTasks() {
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const [tasks, setTasks] = useState([]);
|
const [tasks, setTasks] = useState([]);
|
||||||
const [pagination, setPagination] = useState<PaginationObject | null>(null);
|
const [pagination, setPagination] = useState<PaginationObject | null>(null);
|
@ -10,7 +10,7 @@ import {
|
|||||||
modifyProcessModelPath,
|
modifyProcessModelPath,
|
||||||
unModifyProcessModelPath,
|
unModifyProcessModelPath,
|
||||||
} from '../helpers';
|
} from '../helpers';
|
||||||
import { ProcessGroup, ProcessModel } from '../interfaces';
|
import { PaginationObject, ProcessGroup, ProcessModel } from '../interfaces';
|
||||||
|
|
||||||
export default function ProcessGroupShow() {
|
export default function ProcessGroupShow() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@ -19,8 +19,10 @@ export default function ProcessGroupShow() {
|
|||||||
const [processGroup, setProcessGroup] = useState<ProcessGroup | null>(null);
|
const [processGroup, setProcessGroup] = useState<ProcessGroup | null>(null);
|
||||||
const [processModels, setProcessModels] = useState([]);
|
const [processModels, setProcessModels] = useState([]);
|
||||||
const [processGroups, setProcessGroups] = useState([]);
|
const [processGroups, setProcessGroups] = useState([]);
|
||||||
const [modelPagination, setModelPagination] = useState(null);
|
const [modelPagination, setModelPagination] =
|
||||||
const [groupPagination, setGroupPagination] = useState(null);
|
useState<PaginationObject | null>(null);
|
||||||
|
const [groupPagination, setGroupPagination] =
|
||||||
|
useState<PaginationObject | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { page, perPage } = getPageInfoFromSearchParams(searchParams);
|
const { page, perPage } = getPageInfoFromSearchParams(searchParams);
|
||||||
@ -138,6 +140,7 @@ export default function ProcessGroupShow() {
|
|||||||
['', `process_group:${processGroup.id}`],
|
['', `process_group:${processGroup.id}`],
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
<h1>Process Group: {processGroup.display_name}</h1>
|
||||||
<ul>
|
<ul>
|
||||||
<Stack orientation="horizontal" gap={3}>
|
<Stack orientation="horizontal" gap={3}>
|
||||||
<Button
|
<Button
|
||||||
@ -160,22 +163,26 @@ export default function ProcessGroupShow() {
|
|||||||
</Stack>
|
</Stack>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<PaginationForTable
|
{modelPagination && modelPagination.total > 0 && (
|
||||||
page={page}
|
<PaginationForTable
|
||||||
perPage={perPage}
|
page={page}
|
||||||
pagination={modelPagination}
|
perPage={perPage}
|
||||||
tableToDisplay={buildModelTable()}
|
pagination={modelPagination}
|
||||||
path={`/admin/process-groups/${processGroup.id}`}
|
tableToDisplay={buildModelTable()}
|
||||||
/>
|
path={`/admin/process-groups/${processGroup.id}`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<PaginationForTable
|
{groupPagination && groupPagination.total > 0 && (
|
||||||
page={page}
|
<PaginationForTable
|
||||||
perPage={perPage}
|
page={page}
|
||||||
pagination={groupPagination}
|
perPage={perPage}
|
||||||
tableToDisplay={buildGroupTable()}
|
pagination={groupPagination}
|
||||||
path={`/admin/process-groups/${processGroup.id}`}
|
tableToDisplay={buildGroupTable()}
|
||||||
/>
|
path={`/admin/process-groups/${processGroup.id}`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -521,6 +521,7 @@ export default function ProcessInstanceShow() {
|
|||||||
elements.push(resumeButton(processInstanceToUse));
|
elements.push(resumeButton(processInstanceToUse));
|
||||||
elements.push(
|
elements.push(
|
||||||
<ButtonWithConfirmation
|
<ButtonWithConfirmation
|
||||||
|
data-qa="process-instance-delete"
|
||||||
kind="ghost"
|
kind="ghost"
|
||||||
renderIcon={TrashCan}
|
renderIcon={TrashCan}
|
||||||
iconDescription="Delete"
|
iconDescription="Delete"
|
||||||
|
@ -439,10 +439,11 @@ export default function ProcessModelShow() {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Grid fullWidth>
|
<Grid condensed fullWidth>
|
||||||
<Column md={5} lg={9} sm={3}>
|
<Column md={5} lg={9} sm={3}>
|
||||||
<Accordion align="end">
|
<Accordion align="end" open>
|
||||||
<AccordionItem
|
<AccordionItem
|
||||||
|
open
|
||||||
data-qa="files-accordion"
|
data-qa="files-accordion"
|
||||||
title={
|
title={
|
||||||
<Stack orientation="horizontal">
|
<Stack orientation="horizontal">
|
||||||
@ -532,11 +533,11 @@ export default function ProcessModelShow() {
|
|||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
{processInstanceResultTag()}
|
{processInstanceResultTag()}
|
||||||
{processModelButtons()}
|
|
||||||
<br />
|
|
||||||
<br />
|
<br />
|
||||||
<h3>Process Instances</h3>
|
<h3>Process Instances</h3>
|
||||||
{processInstancesUl()}
|
{processInstancesUl()}
|
||||||
|
<br />
|
||||||
|
{processModelButtons()}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user