Merge pull request #22 from sartography/feature/carbon_ui

Feature/carbon UI
This commit is contained in:
Kevin Burnett 2022-11-04 22:33:38 +00:00 committed by GitHub
commit 2db70303c2
47 changed files with 1539 additions and 762 deletions

View File

@ -153,7 +153,6 @@ paths:
description: The number of groups to show per page. Defaults to page 10. description: The number of groups to show per page. Defaults to page 10.
schema: schema:
type: integer type: integer
# process_groups_list
get: get:
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_groups_list operationId: spiffworkflow_backend.routes.process_api_blueprint.process_groups_list
summary: get list summary: get list
@ -168,7 +167,6 @@ paths:
type: array type: array
items: items:
$ref: "#/components/schemas/ProcessModelCategory" $ref: "#/components/schemas/ProcessModelCategory"
# process_group_add
post: post:
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_group_add operationId: spiffworkflow_backend.routes.process_api_blueprint.process_group_add
summary: Add process group summary: Add process group
@ -429,7 +427,7 @@ paths:
description: For filtering - beginning of start window - in seconds since epoch description: For filtering - beginning of start window - in seconds since epoch
schema: schema:
type: integer type: integer
- name: start_till - name: start_to
in: query in: query
required: false required: false
description: For filtering - end of start window - in seconds since epoch description: For filtering - end of start window - in seconds since epoch
@ -441,7 +439,7 @@ paths:
description: For filtering - beginning of end window - in seconds since epoch description: For filtering - beginning of end window - in seconds since epoch
schema: schema:
type: integer type: integer
- name: end_till - name: end_to
in: query in: query
required: false required: false
description: For filtering - end of end window - in seconds since epoch description: For filtering - end of end window - in seconds since epoch

View File

@ -1,6 +1,7 @@
"""Process_group.""" """Process_group."""
from __future__ import annotations from __future__ import annotations
import dataclasses
from dataclasses import dataclass from dataclasses import dataclass
from dataclasses import field from dataclasses import field
from typing import Any from typing import Any
@ -20,6 +21,7 @@ class ProcessGroup:
id: str # A unique string name, lower case, under scores (ie, 'my_group') id: str # A unique string name, lower case, under scores (ie, 'my_group')
display_name: str display_name: str
description: str | None = None
display_order: int | None = 0 display_order: int | None = 0
admin: bool | None = False admin: bool | None = False
process_models: list[ProcessModelInfo] = field( process_models: list[ProcessModelInfo] = field(
@ -38,6 +40,12 @@ class ProcessGroup:
return True return True
return False return False
@property
def serialized(self) -> dict:
"""Serialized."""
original_dict = dataclasses.asdict(self)
return {x: original_dict[x] for x in original_dict if x not in ["sort_index"]}
class ProcessGroupSchema(Schema): class ProcessGroupSchema(Schema):
"""ProcessGroupSchema.""" """ProcessGroupSchema."""

View File

@ -30,7 +30,6 @@ class ProcessModelInfo:
display_name: str display_name: str
description: str description: str
process_group_id: str = "" process_group_id: str = ""
process_group: Any | None = None
primary_file_name: str | None = None primary_file_name: str | None = None
primary_process_id: str | None = None primary_process_id: str | None = None
display_order: int | None = 0 display_order: int | None = 0

View File

@ -43,6 +43,7 @@ from spiffworkflow_backend.models.message_triggerable_process_model import (
MessageTriggerableProcessModel, MessageTriggerableProcessModel,
) )
from spiffworkflow_backend.models.principal import PrincipalModel from spiffworkflow_backend.models.principal import PrincipalModel
from spiffworkflow_backend.models.process_group import ProcessGroup
from spiffworkflow_backend.models.process_group import ProcessGroupSchema from spiffworkflow_backend.models.process_group import ProcessGroupSchema
from spiffworkflow_backend.models.process_instance import ProcessInstanceApiSchema from spiffworkflow_backend.models.process_instance import ProcessInstanceApiSchema
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
@ -135,18 +136,12 @@ def permissions_check(body: Dict[str, Dict[str, list[str]]]) -> flask.wrappers.R
return make_response(jsonify({"results": response_dict}), 200) return make_response(jsonify({"results": response_dict}), 200)
def process_group_add( def process_group_add(body: dict) -> flask.wrappers.Response:
body: Dict[str, Union[str, bool, int]]
) -> flask.wrappers.Response:
"""Add_process_group.""" """Add_process_group."""
process_model_service = ProcessModelService() process_model_service = ProcessModelService()
process_group = ProcessGroupSchema().load(body) process_group = ProcessGroup(**body)
process_model_service.add_process_group(process_group) process_model_service.add_process_group(process_group)
return Response( return make_response(jsonify(process_group), 201)
json.dumps(ProcessGroupSchema().dump(process_group)),
status=201,
mimetype="application/json",
)
def process_group_delete(process_group_id: str) -> flask.wrappers.Response: def process_group_delete(process_group_id: str) -> flask.wrappers.Response:
@ -155,13 +150,18 @@ def process_group_delete(process_group_id: str) -> flask.wrappers.Response:
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json") return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
def process_group_update( def process_group_update(process_group_id: str, body: dict) -> flask.wrappers.Response:
process_group_id: str, body: Dict[str, Union[str, bool, int]]
) -> Dict[str, Union[str, bool, int]]:
"""Process Group Update.""" """Process Group Update."""
process_group = ProcessGroupSchema().load(body) body_include_list = ["display_name", "description"]
body_filtered = {
include_item: body[include_item]
for include_item in body_include_list
if include_item in body
}
process_group = ProcessGroup(id=process_group_id, **body_filtered)
ProcessModelService().update_process_group(process_group) ProcessModelService().update_process_group(process_group)
return ProcessGroupSchema().dump(process_group) # type: ignore return make_response(jsonify(process_group), 200)
def process_groups_list(page: int = 1, per_page: int = 100) -> flask.wrappers.Response: def process_groups_list(page: int = 1, per_page: int = 100) -> flask.wrappers.Response:
@ -174,6 +174,7 @@ def process_groups_list(page: int = 1, per_page: int = 100) -> flask.wrappers.Re
remainder = len(process_groups) % per_page remainder = len(process_groups) % per_page
if remainder > 0: if remainder > 0:
pages += 1 pages += 1
response_json = { response_json = {
"results": ProcessGroupSchema(many=True).dump(batch), "results": ProcessGroupSchema(many=True).dump(batch),
"pagination": { "pagination": {
@ -199,7 +200,7 @@ def process_group_show(
status_code=400, status_code=400,
) )
) from exception ) from exception
return ProcessGroupSchema().dump(process_group) return make_response(jsonify(process_group), 200)
def process_model_add( def process_model_add(
@ -225,7 +226,6 @@ def process_model_add(
status_code=400, status_code=400,
) )
process_model_info.process_group = process_group
process_model_service.add_spec(process_model_info) process_model_service.add_spec(process_model_info)
return Response( return Response(
json.dumps(ProcessModelInfoSchema().dump(process_model_info)), json.dumps(ProcessModelInfoSchema().dump(process_model_info)),
@ -651,9 +651,9 @@ def process_instance_list(
page: int = 1, page: int = 1,
per_page: int = 100, per_page: int = 100,
start_from: Optional[int] = None, start_from: Optional[int] = None,
start_till: Optional[int] = None, start_to: Optional[int] = None,
end_from: Optional[int] = None, end_from: Optional[int] = None,
end_till: Optional[int] = None, end_to: Optional[int] = None,
process_status: Optional[str] = None, process_status: Optional[str] = None,
) -> flask.wrappers.Response: ) -> flask.wrappers.Response:
"""Process_instance_list.""" """Process_instance_list."""
@ -684,17 +684,17 @@ def process_instance_list(
process_instance_query = process_instance_query.filter( process_instance_query = process_instance_query.filter(
ProcessInstanceModel.start_in_seconds >= start_from ProcessInstanceModel.start_in_seconds >= start_from
) )
if start_till is not None: if start_to is not None:
process_instance_query = process_instance_query.filter( process_instance_query = process_instance_query.filter(
ProcessInstanceModel.start_in_seconds <= start_till ProcessInstanceModel.start_in_seconds <= start_to
) )
if end_from is not None: if end_from is not None:
process_instance_query = process_instance_query.filter( process_instance_query = process_instance_query.filter(
ProcessInstanceModel.end_in_seconds >= end_from ProcessInstanceModel.end_in_seconds >= end_from
) )
if end_till is not None: if end_to is not None:
process_instance_query = process_instance_query.filter( process_instance_query = process_instance_query.filter(
ProcessInstanceModel.end_in_seconds <= end_till ProcessInstanceModel.end_in_seconds <= end_to
) )
if process_status is not None: if process_status is not None:
process_status_array = process_status.split(",") process_status_array = process_status.split(",")

View File

@ -170,7 +170,7 @@ class ProcessModelService(FileSystemService):
json_path = os.path.join(cat_path, self.CAT_JSON_FILE) json_path = os.path.join(cat_path, self.CAT_JSON_FILE)
with open(json_path, "w") as cat_json: with open(json_path, "w") as cat_json:
json.dump( json.dump(
self.GROUP_SCHEMA.dump(process_group), process_group.serialized,
cat_json, cat_json,
indent=4, indent=4,
sort_keys=True, sort_keys=True,
@ -274,6 +274,5 @@ class ProcessModelService(FileSystemService):
with open(spec_path, "w") as wf_json: with open(spec_path, "w") as wf_json:
json.dump(self.WF_SCHEMA.dump(spec), wf_json, indent=4) json.dump(self.WF_SCHEMA.dump(spec), wf_json, indent=4)
if process_group: if process_group:
spec.process_group = process_group
spec.process_group_id = process_group.id spec.process_group_id = process_group.id
return spec return spec

View File

@ -17,7 +17,6 @@ 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.group import GroupModel from spiffworkflow_backend.models.group import GroupModel
from spiffworkflow_backend.models.process_group import ProcessGroup from spiffworkflow_backend.models.process_group import ProcessGroup
from spiffworkflow_backend.models.process_group import ProcessGroupSchema
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
from spiffworkflow_backend.models.process_instance_report import ( from spiffworkflow_backend.models.process_instance_report import (
@ -388,25 +387,29 @@ class TestProcessApi(BaseTest):
display_name="Another Test Category", display_name="Another Test Category",
display_order=0, display_order=0,
admin=False, admin=False,
description="Test Description",
) )
response = client.post( response = client.post(
"/v1.0/process-groups", "/v1.0/process-groups",
headers=self.logged_in_headers(with_super_admin_user), headers=self.logged_in_headers(with_super_admin_user),
content_type="application/json", content_type="application/json",
data=json.dumps(ProcessGroupSchema().dump(process_group)), data=json.dumps(process_group.serialized),
) )
assert response.status_code == 201 assert response.status_code == 201
assert response.json
# Check what is returned # Check what is returned
result = ProcessGroupSchema().loads(response.get_data(as_text=True)) result = ProcessGroup(**response.json)
assert result is not None assert result is not None
assert result.display_name == "Another Test Category" assert result.display_name == "Another Test Category"
assert result.id == "test" assert result.id == "test"
assert result.description == "Test Description"
# Check what is persisted # Check what is persisted
persisted = ProcessModelService().get_process_group("test") persisted = ProcessModelService().get_process_group("test")
assert persisted.display_name == "Another Test Category" assert persisted.display_name == "Another Test Category"
assert persisted.id == "test" assert persisted.id == "test"
assert persisted.description == "Test Description"
def test_process_group_delete( def test_process_group_delete(
self, self,
@ -461,7 +464,7 @@ class TestProcessApi(BaseTest):
f"/v1.0/process-groups/{group_id}", f"/v1.0/process-groups/{group_id}",
headers=self.logged_in_headers(with_super_admin_user), headers=self.logged_in_headers(with_super_admin_user),
content_type="application/json", content_type="application/json",
data=json.dumps(ProcessGroupSchema().dump(process_group)), data=json.dumps(process_group.serialized),
) )
assert response.status_code == 200 assert response.status_code == 200
@ -788,6 +791,7 @@ class TestProcessApi(BaseTest):
f"/v1.0/process-groups/{test_process_group_id}", f"/v1.0/process-groups/{test_process_group_id}",
headers=self.logged_in_headers(with_super_admin_user), headers=self.logged_in_headers(with_super_admin_user),
) )
assert response.status_code == 200 assert response.status_code == 200
assert response.json is not None assert response.json is not None
assert response.json["id"] == test_process_group_id assert response.json["id"] == test_process_group_id
@ -1299,7 +1303,7 @@ class TestProcessApi(BaseTest):
# 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(
"/v1.0/process-instances?start_from=2001&end_till=5999", "/v1.0/process-instances?start_from=2001&end_to=5999",
headers=self.logged_in_headers(with_super_admin_user), headers=self.logged_in_headers(with_super_admin_user),
) )
assert response.json is not None assert response.json is not None
@ -1310,7 +1314,7 @@ class TestProcessApi(BaseTest):
# 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(
"/v1.0/process-instances?start_from=1001&start_till=3999", "/v1.0/process-instances?start_from=1001&start_to=3999",
headers=self.logged_in_headers(with_super_admin_user), headers=self.logged_in_headers(with_super_admin_user),
) )
assert response.json is not None assert response.json is not None
@ -1321,7 +1325,7 @@ class TestProcessApi(BaseTest):
# 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(
"/v1.0/process-instances?end_from=2001&end_till=5999", "/v1.0/process-instances?end_from=2001&end_to=5999",
headers=self.logged_in_headers(with_super_admin_user), headers=self.logged_in_headers(with_super_admin_user),
) )
assert response.json is not None assert response.json is not None

View File

@ -10,4 +10,9 @@ module.exports = defineConfig({
// implement node event listeners here // implement node event listeners here
}, },
}, },
// this scrolls away from the elements for some reason with carbon when set to top
// https://github.com/cypress-io/cypress/issues/2353
// https://docs.cypress.io/guides/core-concepts/interacting-with-elements#Scrolling
scrollBehavior: "center",
}); });

View File

@ -32,7 +32,7 @@ describe('process-groups', () => {
cy.contains('Delete').click(); cy.contains('Delete').click();
cy.contains('Are you sure'); cy.contains('Are you sure');
cy.contains('OK').click(); cy.getBySel('modal-confirmation-dialog').find('.cds--btn--danger').click();
cy.url().should('include', `process-groups`); cy.url().should('include', `process-groups`);
cy.contains(groupId).should('not.exist'); cy.contains(groupId).should('not.exist');
}); });

View File

@ -43,12 +43,19 @@ const updateBpmnPythonScriptWithMonaco = (
cy.contains('Launch Editor').click(); cy.contains('Launch Editor').click();
// sometimes, Loading... appears for more than 4 seconds. not sure why. // sometimes, Loading... appears for more than 4 seconds. not sure why.
cy.contains('Loading...').should('not.exist'); cy.contains('Loading...').should('not.exist');
// the delay 30 is because, at some point, monaco started automatically
// adding a second double quote when we type a double quote. when it does
// that, there is a race condition where it sometimes gets in more text
// before the second double quote appears because the robot is typing faster
// than a human being could, so we artificially slow it down to make it more
// human.
cy.get('.monaco-editor textarea:first') cy.get('.monaco-editor textarea:first')
.click() .click()
.focused() // change subject to currently focused element .focused() // change subject to currently focused element
// .type('{ctrl}a') // had been doing it this way, but it turns out to be flaky relative to clear() // .type('{ctrl}a') // had been doing it this way, but it turns out to be flaky relative to clear()
.clear() .clear()
.type(pythonScript); .type(pythonScript, { delay: 30 });
cy.contains('Close').click(); cy.contains('Close').click();
// wait for a little bit for the xml to get set before saving // wait for a little bit for the xml to get set before saving
@ -111,8 +118,10 @@ describe('process-instances', () => {
}); });
it('can create a new instance and can modify with monaco text editor', () => { it('can create a new instance and can modify with monaco text editor', () => {
const originalPythonScript = 'person = "Kevin"'; // leave off the ending double quote since manco adds it
const newPythonScript = 'person = "Mike"'; const originalPythonScript = 'person = "Kevin';
const newPythonScript = 'person = "Mike';
const bpmnFile = 'process_model_one.bpmn'; const bpmnFile = 'process_model_one.bpmn';
// Change bpmn // Change bpmn

View File

@ -99,7 +99,7 @@ describe('process-models', () => {
cy.contains(/^Process Model File$/); cy.contains(/^Process Model File$/);
// Some reason, cypress evals json strings so we have to escape it it with '{{}' // Some reason, cypress evals json strings so we have to escape it it with '{{}'
cy.get('.view-line').type('{{} "test_key": "test_value" }'); cy.get('.view-line').type('{{} "test_key": "test_value" }');
cy.contains('Save').click(); cy.getBySel('file-save-button').click();
cy.get('input[name=file_name]').type(jsonFileName); cy.get('input[name=file_name]').type(jsonFileName);
cy.contains('Save Changes').click(); cy.contains('Save Changes').click();
cy.contains(`Process Model File: ${jsonFileName}`); cy.contains(`Process Model File: ${jsonFileName}`);
@ -168,12 +168,8 @@ describe('process-models', () => {
}); });
it('can allow searching for model', () => { it('can allow searching for model', () => {
cy.get('[name=process-model-selection]').click(); cy.getBySel('process-model-selection').click().type('model-3');
cy.get('[name=process-model-selection]').type('model-3'); cy.contains('acceptance-tests-group-one/acceptance-tests-model-3').click();
cy.get( cy.contains('List').click();
`[aria-label="acceptance-tests-group-one/acceptance-tests-model-3"]`
).click();
cy.contains('Process Instances').click();
}); });
}); });

View File

@ -21,6 +21,7 @@ describe('tasks', () => {
cy.logout(); cy.logout();
}); });
// TODO: need to fix the next_task thing to make this pass
it('can complete and navigate a form', () => { it('can complete and navigate a form', () => {
const groupDisplayName = 'Acceptance Tests Group One'; const groupDisplayName = 'Acceptance Tests Group One';
const modelId = `acceptance-tests-model-2`; const modelId = `acceptance-tests-model-2`;

View File

@ -31,11 +31,13 @@ Cypress.Commands.add('getBySel', (selector, ...args) => {
}); });
Cypress.Commands.add('navigateToHome', () => { Cypress.Commands.add('navigateToHome', () => {
cy.getBySel('nav-home').click(); cy.get('button[aria-label="Open menu"]').click();
cy.getBySel('side-nav-items').contains('Home').click();
// cy.getBySel('nav-home').click();
}); });
Cypress.Commands.add('navigateToAdmin', () => { Cypress.Commands.add('navigateToAdmin', () => {
cy.getBySel('spiffworkflow-logo').click(); cy.visit('/admin');
}); });
Cypress.Commands.add('login', (selector, ...args) => { Cypress.Commands.add('login', (selector, ...args) => {
@ -101,18 +103,15 @@ Cypress.Commands.add(
); );
Cypress.Commands.add('basicPaginationTest', () => { Cypress.Commands.add('basicPaginationTest', () => {
cy.get('#pagination-page-dropdown') cy.getBySel('pagination-options').scrollIntoView();
.type('typing_to_open_dropdown_box....FIXME') cy.get('.cds--select__item-count').find('.cds--select-input').select('2');
.find('.dropdown-item')
.contains(/^2$/)
.click();
cy.contains(/^1-2 of \d+$/); // NOTE: this is a em dash instead of en dash
cy.getBySel('pagination-previous-button-inactive'); cy.contains(/\b12 of \d+/);
cy.getBySel('pagination-next-button').click(); cy.get('.cds--pagination__button--forward').click();
cy.contains(/^3-4 of \d+$/); cy.contains(/\b34 of \d+/);
cy.getBySel('pagination-previous-button').click(); cy.get('.cds--pagination__button--backward').click();
cy.contains(/^1-2 of \d+$/); cy.contains(/\b12 of \d+/);
}); });
Cypress.Commands.add('assertAtLeastOneItemInPaginatedResults', () => { Cypress.Commands.add('assertAtLeastOneItemInPaginatedResults', () => {

View File

@ -11,6 +11,9 @@
"@babel/core": "^7.18.10", "@babel/core": "^7.18.10",
"@babel/plugin-transform-react-jsx": "^7.18.6", "@babel/plugin-transform-react-jsx": "^7.18.6",
"@babel/preset-react": "^7.18.6", "@babel/preset-react": "^7.18.6",
"@carbon/icons-react": "^11.10.0",
"@carbon/react": "^1.16.0",
"@carbon/styles": "^1.16.0",
"@ginkgo-bioworks/react-json-schema-form-builder": "^2.9.0", "@ginkgo-bioworks/react-json-schema-form-builder": "^2.9.0",
"@monaco-editor/react": "^4.4.5", "@monaco-editor/react": "^4.4.5",
"@rjsf/core": "^4.2.0", "@rjsf/core": "^4.2.0",
@ -2140,6 +2143,138 @@
"resolved": "https://registry.npmjs.org/@camunda/zeebe-element-templates-json-schema/-/zeebe-element-templates-json-schema-0.6.0.tgz", "resolved": "https://registry.npmjs.org/@camunda/zeebe-element-templates-json-schema/-/zeebe-element-templates-json-schema-0.6.0.tgz",
"integrity": "sha512-qawIFM52lp1hW2vWrHaX8ywguZsp2olE0DRTHUY+KWH5GwszZwGWECP3tji1KVih2TasQyf28kcQVh8TeQ6dAg==" "integrity": "sha512-qawIFM52lp1hW2vWrHaX8ywguZsp2olE0DRTHUY+KWH5GwszZwGWECP3tji1KVih2TasQyf28kcQVh8TeQ6dAg=="
}, },
"node_modules/@carbon/colors": {
"version": "11.7.0",
"resolved": "https://registry.npmjs.org/@carbon/colors/-/colors-11.7.0.tgz",
"integrity": "sha512-2B57vtirYTpxGg7p9yIO+s4cwL5QB8ogGQB5Pz+zdaHoVxrLsGlxAg28CQgk7yxw7doqST79bRVYS6FaJq+qJw=="
},
"node_modules/@carbon/feature-flags": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/@carbon/feature-flags/-/feature-flags-0.9.0.tgz",
"integrity": "sha512-uNCRkxsNwLdJVNwQ5S5vrLAm4yawWzhFBwyP9EgXVmRzJH85Aim+miC+QNjRXntJDYhZYDuLABTeb3VD692ypA=="
},
"node_modules/@carbon/grid": {
"version": "11.7.0",
"resolved": "https://registry.npmjs.org/@carbon/grid/-/grid-11.7.0.tgz",
"integrity": "sha512-OCDXQNSPVOoBQN08Qn5wo2WpKRGRm/kEBo6ZTl2NoDCl21mcYJZ0IDbKPTukixJB1sUNDUlnrpwMoaznPj48dw==",
"dependencies": {
"@carbon/layout": "^11.7.0"
}
},
"node_modules/@carbon/icon-helpers": {
"version": "10.34.0",
"resolved": "https://registry.npmjs.org/@carbon/icon-helpers/-/icon-helpers-10.34.0.tgz",
"integrity": "sha512-Ov9EBc1tR/DDrMI0pN1drj2jb27ISmYFBLdDji+aivVJkLPy8R/jikJOsOBgIq2kUjQJYNN199k2acHKjZdYIg=="
},
"node_modules/@carbon/icons-react": {
"version": "11.10.0",
"resolved": "https://registry.npmjs.org/@carbon/icons-react/-/icons-react-11.10.0.tgz",
"integrity": "sha512-807RWTfbvVzmsDg2DJ4FjwYNbJSgkrEd1Ui8I07YheJVb3sbYGGZMG7aCS0qXVlrQOhB2hggtxSW1w9NksUXNA==",
"hasInstallScript": true,
"dependencies": {
"@carbon/icon-helpers": "^10.34.0",
"@carbon/telemetry": "0.1.0",
"prop-types": "^15.7.2"
},
"peerDependencies": {
"react": ">=16"
}
},
"node_modules/@carbon/layout": {
"version": "11.7.0",
"resolved": "https://registry.npmjs.org/@carbon/layout/-/layout-11.7.0.tgz",
"integrity": "sha512-p4YQvW8U5Go0Tz1PZZgllGSPmoq8xBB5PHByuHiAjzwGclxPsBmY6Ea7tftINFW8VlcjDcampyT8VfZXhP2lFg=="
},
"node_modules/@carbon/motion": {
"version": "11.5.0",
"resolved": "https://registry.npmjs.org/@carbon/motion/-/motion-11.5.0.tgz",
"integrity": "sha512-5QEALh+xZzICdgVLanSpiDfBTErzVgEze/xUKs7ZdSbd6p1FaDKDGvCmj9RCsaz/CMVHIWo65MshIglSWX5Xvw=="
},
"node_modules/@carbon/react": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/@carbon/react/-/react-1.16.0.tgz",
"integrity": "sha512-kVeL/iyVqN2tfcoE1xliIm4w2ex9L4m/b8KuGo7ZuqBmRzANQdfSYGfj11KU0TXT1CbOVFrqsT/aBxApsP5IDg==",
"hasInstallScript": true,
"dependencies": {
"@babel/runtime": "^7.18.3",
"@carbon/feature-flags": "^0.9.0",
"@carbon/icons-react": "^11.10.0",
"@carbon/layout": "^11.7.0",
"@carbon/styles": "^1.16.0",
"@carbon/telemetry": "0.1.0",
"classnames": "2.3.2",
"copy-to-clipboard": "^3.3.1",
"downshift": "5.2.1",
"flatpickr": "4.6.9",
"invariant": "^2.2.3",
"lodash.debounce": "^4.0.8",
"lodash.findlast": "^4.5.0",
"lodash.isequal": "^4.5.0",
"lodash.omit": "^4.5.0",
"lodash.throttle": "^4.1.1",
"prop-types": "^15.7.2",
"react-is": "^17.0.2",
"use-resize-observer": "^6.0.0",
"wicg-inert": "^3.1.1",
"window-or-global": "^1.0.1"
},
"peerDependencies": {
"react": "^16.8.6 || ^17.0.1",
"react-dom": "^16.8.6 || ^17.0.1",
"sass": "^1.33.0"
}
},
"node_modules/@carbon/react/node_modules/react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
},
"node_modules/@carbon/styles": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/@carbon/styles/-/styles-1.16.0.tgz",
"integrity": "sha512-LSjRw2Ws8rWI1a96KYUuX10jG+rpSn68dHlZhhDq+RJWsMGpjFhKUxPoTiJli2hHXyxey6rXV0hfr7oBJ0ud7w==",
"dependencies": {
"@carbon/colors": "^11.7.0",
"@carbon/feature-flags": "^0.9.0",
"@carbon/grid": "^11.7.0",
"@carbon/layout": "^11.7.0",
"@carbon/motion": "^11.5.0",
"@carbon/themes": "^11.11.0",
"@carbon/type": "^11.11.0",
"@ibm/plex": "6.0.0-next.6"
},
"peerDependencies": {
"sass": "^1.33.0"
}
},
"node_modules/@carbon/telemetry": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@carbon/telemetry/-/telemetry-0.1.0.tgz",
"integrity": "sha512-kNWt0bkgPwGW0i5h7HFuljbKRXPvIhsKbB+1tEURAYLXoJg9iJLF1eGvWN5iVoFCS2zje4GR3OGOsvvKVe7Hlg==",
"bin": {
"carbon-telemetry": "bin/carbon-telemetry.js"
}
},
"node_modules/@carbon/themes": {
"version": "11.11.0",
"resolved": "https://registry.npmjs.org/@carbon/themes/-/themes-11.11.0.tgz",
"integrity": "sha512-EhLizr2oMqJXNubI2zWw09qcSPLZdWoBOQ6uNcjHzpXgoGNMwbVJE/qGMy/ivr+EOs6Fe0z5T0u97v+ZhSmRhg==",
"dependencies": {
"@carbon/colors": "^11.7.0",
"@carbon/layout": "^11.7.0",
"@carbon/type": "^11.11.0",
"color": "^4.0.0"
}
},
"node_modules/@carbon/type": {
"version": "11.11.0",
"resolved": "https://registry.npmjs.org/@carbon/type/-/type-11.11.0.tgz",
"integrity": "sha512-eX6z8BibP1En1xBm2wUd01Nzk0Tm1jftR2QSD4JBn4xhnkGR824gpcbLTAIMGx9/Mr3R65Enbam3uFO0OOaH8g==",
"dependencies": {
"@carbon/grid": "^11.7.0",
"@carbon/layout": "^11.7.0"
}
},
"node_modules/@codemirror/autocomplete": { "node_modules/@codemirror/autocomplete": {
"version": "6.3.0", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.3.0.tgz", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.3.0.tgz",
@ -2899,6 +3034,14 @@
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
}, },
"node_modules/@ibm/plex": {
"version": "6.0.0-next.6",
"resolved": "https://registry.npmjs.org/@ibm/plex/-/plex-6.0.0-next.6.tgz",
"integrity": "sha512-B3uGruTn2rS5gweynLmfSe7yCawSRsJguJJQHVQiqf4rh2RNgJFu8YLE2Zd/JHV0ZXoVMOslcXP2k3hMkxKEyA==",
"engines": {
"node": ">=14"
}
},
"node_modules/@istanbuljs/load-nyc-config": { "node_modules/@istanbuljs/load-nyc-config": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@ -8009,9 +8152,9 @@
} }
}, },
"node_modules/classnames": { "node_modules/classnames": {
"version": "2.3.1", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
}, },
"node_modules/clean-css": { "node_modules/clean-css": {
"version": "5.3.1", "version": "5.3.1",
@ -8207,6 +8350,18 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/color": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
"integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
"dependencies": {
"color-convert": "^2.0.1",
"color-string": "^1.9.0"
},
"engines": {
"node": ">=12.5.0"
}
},
"node_modules/color-convert": { "node_modules/color-convert": {
"version": "1.9.3", "version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@ -8220,6 +8375,31 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
}, },
"node_modules/color-string": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
"dependencies": {
"color-name": "^1.0.0",
"simple-swizzle": "^0.2.2"
}
},
"node_modules/color/node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/colord": { "node_modules/colord": {
"version": "2.9.3", "version": "2.9.3",
"resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
@ -8484,6 +8664,14 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/copy-to-clipboard": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.2.tgz",
"integrity": "sha512-Vme1Z6RUDzrb6xAI7EZlVZ5uvOk2F//GaxKUxajDqm9LhOVM1inxNAD2vy+UZDYsd0uyA9s7b3/FVZPSxqrCfg==",
"dependencies": {
"toggle-selection": "^1.0.6"
}
},
"node_modules/core_d": { "node_modules/core_d": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/core_d/-/core_d-5.0.1.tgz", "resolved": "https://registry.npmjs.org/core_d/-/core_d-5.0.1.tgz",
@ -9005,9 +9193,9 @@
"integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==" "integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A=="
}, },
"node_modules/cypress": { "node_modules/cypress": {
"version": "10.8.0", "version": "10.11.0",
"resolved": "https://registry.npmjs.org/cypress/-/cypress-10.8.0.tgz", "resolved": "https://registry.npmjs.org/cypress/-/cypress-10.11.0.tgz",
"integrity": "sha512-QVse0dnLm018hgti2enKMVZR9qbIO488YGX06nH5j3Dg1isL38DwrBtyrax02CANU6y8F4EJUuyW6HJKw1jsFA==", "integrity": "sha512-lsaE7dprw5DoXM00skni6W5ElVVLGAdRUUdZjX2dYsGjbY/QnpzWZ95Zom1mkGg0hAaO/QVTZoFVS7Jgr/GUPA==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
@ -9872,6 +10060,25 @@
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
"integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA=="
}, },
"node_modules/downshift": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/downshift/-/downshift-5.2.1.tgz",
"integrity": "sha512-uHX2OLbWthLR8QbR8NCI8OmjvvJxq8+PrA95KblFd9JyB1zVZh1HnszzsWMMCnMuH6IvsUtVfF5HY7XfijJ2dw==",
"dependencies": {
"@babel/runtime": "^7.9.1",
"compute-scroll-into-view": "^1.0.13",
"prop-types": "^15.7.2",
"react-is": "^16.13.1"
},
"peerDependencies": {
"react": ">=0.14.9"
}
},
"node_modules/downshift/node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/duplexer": { "node_modules/duplexer": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
@ -11596,6 +11803,11 @@
"node": "^10.12.0 || >=12.0.0" "node": "^10.12.0 || >=12.0.0"
} }
}, },
"node_modules/flatpickr": {
"version": "4.6.9",
"resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.9.tgz",
"integrity": "sha512-F0azNNi8foVWKSF+8X+ZJzz8r9sE1G4hl06RyceIaLvyltKvDl6vqk9Lm/6AUUCi5HWaIjiUbk7UpeE/fOXOpw=="
},
"node_modules/flatted": { "node_modules/flatted": {
"version": "3.2.7", "version": "3.2.7",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
@ -12794,6 +13006,12 @@
"url": "https://opencollective.com/immer" "url": "https://opencollective.com/immer"
} }
}, },
"node_modules/immutable": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz",
"integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==",
"peer": true
},
"node_modules/import-fresh": { "node_modules/import-fresh": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@ -18240,6 +18458,16 @@
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
}, },
"node_modules/lodash.findlast": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.findlast/-/lodash.findlast-4.6.0.tgz",
"integrity": "sha512-+OGwb1FVKjhc2aIEQ9vKqNDW1a0/HaCLr0iCIK10jfVif3dBE0nhQD0jOZNZLh7zOlmFUTrk+vt85eXoH4vKuA=="
},
"node_modules/lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
},
"node_modules/lodash.memoize": { "node_modules/lodash.memoize": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@ -18250,6 +18478,11 @@
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
}, },
"node_modules/lodash.omit": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz",
"integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg=="
},
"node_modules/lodash.once": { "node_modules/lodash.once": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
@ -18261,6 +18494,11 @@
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
"integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA=="
}, },
"node_modules/lodash.throttle": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
"integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="
},
"node_modules/lodash.truncate": { "node_modules/lodash.truncate": {
"version": "4.4.2", "version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
@ -23919,6 +24157,11 @@
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
}, },
"node_modules/resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
},
"node_modules/resolve": { "node_modules/resolve": {
"version": "1.22.1", "version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
@ -24247,6 +24490,23 @@
"resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz",
"integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA=="
}, },
"node_modules/sass": {
"version": "1.55.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.55.0.tgz",
"integrity": "sha512-Pk+PMy7OGLs9WaxZGJMn7S96dvlyVBwwtToX895WmCpAOr5YiJYEUJfiJidMuKb613z2xNWcXCHEuOvjZbqC6A==",
"peer": true,
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",
"source-map-js": ">=0.6.2 <2.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/sax": { "node_modules/sax": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
@ -24717,6 +24977,19 @@
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
}, },
"node_modules/simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
"dependencies": {
"is-arrayish": "^0.3.1"
}
},
"node_modules/simple-swizzle/node_modules/is-arrayish": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
},
"node_modules/sisteransi": { "node_modules/sisteransi": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
@ -26202,6 +26475,11 @@
"ret": "~0.1.10" "ret": "~0.1.10"
} }
}, },
"node_modules/toggle-selection": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
"integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ=="
},
"node_modules/toidentifier": { "node_modules/toidentifier": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@ -27747,6 +28025,18 @@
"react": "^16.8.0 || ^17.0.0" "react": "^16.8.0 || ^17.0.0"
} }
}, },
"node_modules/use-resize-observer": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-6.1.0.tgz",
"integrity": "sha512-SiPcWHiIQ1CnHmb6PxbYtygqiZXR0U9dNkkbpX9VYnlstUwF8+QqpUTrzh13pjPwcjMVGR+QIC+nvF5ujfFNng==",
"dependencies": {
"resize-observer-polyfill": "^1.5.1"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/util": { "node_modules/util": {
"version": "0.11.1", "version": "0.11.1",
"resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
@ -28868,6 +29158,11 @@
"integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==",
"dev": true "dev": true
}, },
"node_modules/wicg-inert": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/wicg-inert/-/wicg-inert-3.1.2.tgz",
"integrity": "sha512-Ba9tGNYxXwaqKEi9sJJvPMKuo063umUPsHN0JJsjrs2j8KDSzkWLMZGZ+MH1Jf1Fq4OWZ5HsESJID6nRza2ang=="
},
"node_modules/widest-line": { "node_modules/widest-line": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz",
@ -28923,6 +29218,11 @@
"url": "https://github.com/chalk/strip-ansi?sponsor=1" "url": "https://github.com/chalk/strip-ansi?sponsor=1"
} }
}, },
"node_modules/window-or-global": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/window-or-global/-/window-or-global-1.0.1.tgz",
"integrity": "sha512-tE12J/NenOv4xdVobD+AD3fT06T4KNqnzRhkv5nBIu7K+pvOH2oLCEgYP+i+5mF2jtI6FEADheOdZkA8YWET9w=="
},
"node_modules/word-wrap": { "node_modules/word-wrap": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
@ -30836,6 +31136,124 @@
"resolved": "https://registry.npmjs.org/@camunda/zeebe-element-templates-json-schema/-/zeebe-element-templates-json-schema-0.6.0.tgz", "resolved": "https://registry.npmjs.org/@camunda/zeebe-element-templates-json-schema/-/zeebe-element-templates-json-schema-0.6.0.tgz",
"integrity": "sha512-qawIFM52lp1hW2vWrHaX8ywguZsp2olE0DRTHUY+KWH5GwszZwGWECP3tji1KVih2TasQyf28kcQVh8TeQ6dAg==" "integrity": "sha512-qawIFM52lp1hW2vWrHaX8ywguZsp2olE0DRTHUY+KWH5GwszZwGWECP3tji1KVih2TasQyf28kcQVh8TeQ6dAg=="
}, },
"@carbon/colors": {
"version": "11.7.0",
"resolved": "https://registry.npmjs.org/@carbon/colors/-/colors-11.7.0.tgz",
"integrity": "sha512-2B57vtirYTpxGg7p9yIO+s4cwL5QB8ogGQB5Pz+zdaHoVxrLsGlxAg28CQgk7yxw7doqST79bRVYS6FaJq+qJw=="
},
"@carbon/feature-flags": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/@carbon/feature-flags/-/feature-flags-0.9.0.tgz",
"integrity": "sha512-uNCRkxsNwLdJVNwQ5S5vrLAm4yawWzhFBwyP9EgXVmRzJH85Aim+miC+QNjRXntJDYhZYDuLABTeb3VD692ypA=="
},
"@carbon/grid": {
"version": "11.7.0",
"resolved": "https://registry.npmjs.org/@carbon/grid/-/grid-11.7.0.tgz",
"integrity": "sha512-OCDXQNSPVOoBQN08Qn5wo2WpKRGRm/kEBo6ZTl2NoDCl21mcYJZ0IDbKPTukixJB1sUNDUlnrpwMoaznPj48dw==",
"requires": {
"@carbon/layout": "^11.7.0"
}
},
"@carbon/icon-helpers": {
"version": "10.34.0",
"resolved": "https://registry.npmjs.org/@carbon/icon-helpers/-/icon-helpers-10.34.0.tgz",
"integrity": "sha512-Ov9EBc1tR/DDrMI0pN1drj2jb27ISmYFBLdDji+aivVJkLPy8R/jikJOsOBgIq2kUjQJYNN199k2acHKjZdYIg=="
},
"@carbon/icons-react": {
"version": "11.10.0",
"resolved": "https://registry.npmjs.org/@carbon/icons-react/-/icons-react-11.10.0.tgz",
"integrity": "sha512-807RWTfbvVzmsDg2DJ4FjwYNbJSgkrEd1Ui8I07YheJVb3sbYGGZMG7aCS0qXVlrQOhB2hggtxSW1w9NksUXNA==",
"requires": {
"@carbon/icon-helpers": "^10.34.0",
"@carbon/telemetry": "0.1.0",
"prop-types": "^15.7.2"
}
},
"@carbon/layout": {
"version": "11.7.0",
"resolved": "https://registry.npmjs.org/@carbon/layout/-/layout-11.7.0.tgz",
"integrity": "sha512-p4YQvW8U5Go0Tz1PZZgllGSPmoq8xBB5PHByuHiAjzwGclxPsBmY6Ea7tftINFW8VlcjDcampyT8VfZXhP2lFg=="
},
"@carbon/motion": {
"version": "11.5.0",
"resolved": "https://registry.npmjs.org/@carbon/motion/-/motion-11.5.0.tgz",
"integrity": "sha512-5QEALh+xZzICdgVLanSpiDfBTErzVgEze/xUKs7ZdSbd6p1FaDKDGvCmj9RCsaz/CMVHIWo65MshIglSWX5Xvw=="
},
"@carbon/react": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/@carbon/react/-/react-1.16.0.tgz",
"integrity": "sha512-kVeL/iyVqN2tfcoE1xliIm4w2ex9L4m/b8KuGo7ZuqBmRzANQdfSYGfj11KU0TXT1CbOVFrqsT/aBxApsP5IDg==",
"requires": {
"@babel/runtime": "^7.18.3",
"@carbon/feature-flags": "^0.9.0",
"@carbon/icons-react": "^11.10.0",
"@carbon/layout": "^11.7.0",
"@carbon/styles": "^1.16.0",
"@carbon/telemetry": "0.1.0",
"classnames": "2.3.2",
"copy-to-clipboard": "^3.3.1",
"downshift": "5.2.1",
"flatpickr": "4.6.9",
"invariant": "^2.2.3",
"lodash.debounce": "^4.0.8",
"lodash.findlast": "^4.5.0",
"lodash.isequal": "^4.5.0",
"lodash.omit": "^4.5.0",
"lodash.throttle": "^4.1.1",
"prop-types": "^15.7.2",
"react-is": "^17.0.2",
"use-resize-observer": "^6.0.0",
"wicg-inert": "^3.1.1",
"window-or-global": "^1.0.1"
},
"dependencies": {
"react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
}
}
},
"@carbon/styles": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/@carbon/styles/-/styles-1.16.0.tgz",
"integrity": "sha512-LSjRw2Ws8rWI1a96KYUuX10jG+rpSn68dHlZhhDq+RJWsMGpjFhKUxPoTiJli2hHXyxey6rXV0hfr7oBJ0ud7w==",
"requires": {
"@carbon/colors": "^11.7.0",
"@carbon/feature-flags": "^0.9.0",
"@carbon/grid": "^11.7.0",
"@carbon/layout": "^11.7.0",
"@carbon/motion": "^11.5.0",
"@carbon/themes": "^11.11.0",
"@carbon/type": "^11.11.0",
"@ibm/plex": "6.0.0-next.6"
}
},
"@carbon/telemetry": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@carbon/telemetry/-/telemetry-0.1.0.tgz",
"integrity": "sha512-kNWt0bkgPwGW0i5h7HFuljbKRXPvIhsKbB+1tEURAYLXoJg9iJLF1eGvWN5iVoFCS2zje4GR3OGOsvvKVe7Hlg=="
},
"@carbon/themes": {
"version": "11.11.0",
"resolved": "https://registry.npmjs.org/@carbon/themes/-/themes-11.11.0.tgz",
"integrity": "sha512-EhLizr2oMqJXNubI2zWw09qcSPLZdWoBOQ6uNcjHzpXgoGNMwbVJE/qGMy/ivr+EOs6Fe0z5T0u97v+ZhSmRhg==",
"requires": {
"@carbon/colors": "^11.7.0",
"@carbon/layout": "^11.7.0",
"@carbon/type": "^11.11.0",
"color": "^4.0.0"
}
},
"@carbon/type": {
"version": "11.11.0",
"resolved": "https://registry.npmjs.org/@carbon/type/-/type-11.11.0.tgz",
"integrity": "sha512-eX6z8BibP1En1xBm2wUd01Nzk0Tm1jftR2QSD4JBn4xhnkGR824gpcbLTAIMGx9/Mr3R65Enbam3uFO0OOaH8g==",
"requires": {
"@carbon/grid": "^11.7.0",
"@carbon/layout": "^11.7.0"
}
},
"@codemirror/autocomplete": { "@codemirror/autocomplete": {
"version": "6.3.0", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.3.0.tgz", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.3.0.tgz",
@ -31364,6 +31782,11 @@
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
}, },
"@ibm/plex": {
"version": "6.0.0-next.6",
"resolved": "https://registry.npmjs.org/@ibm/plex/-/plex-6.0.0-next.6.tgz",
"integrity": "sha512-B3uGruTn2rS5gweynLmfSe7yCawSRsJguJJQHVQiqf4rh2RNgJFu8YLE2Zd/JHV0ZXoVMOslcXP2k3hMkxKEyA=="
},
"@istanbuljs/load-nyc-config": { "@istanbuljs/load-nyc-config": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@ -35236,9 +35659,9 @@
} }
}, },
"classnames": { "classnames": {
"version": "2.3.1", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
}, },
"clean-css": { "clean-css": {
"version": "5.3.1", "version": "5.3.1",
@ -35376,6 +35799,30 @@
"object-visit": "^1.0.0" "object-visit": "^1.0.0"
} }
}, },
"color": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
"integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
"requires": {
"color-convert": "^2.0.1",
"color-string": "^1.9.0"
},
"dependencies": {
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
}
}
},
"color-convert": { "color-convert": {
"version": "1.9.3", "version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@ -35389,6 +35836,15 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
}, },
"color-string": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
"requires": {
"color-name": "^1.0.0",
"simple-swizzle": "^0.2.2"
}
},
"colord": { "colord": {
"version": "2.9.3", "version": "2.9.3",
"resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
@ -35614,6 +36070,14 @@
"resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
"integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==" "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw=="
}, },
"copy-to-clipboard": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.2.tgz",
"integrity": "sha512-Vme1Z6RUDzrb6xAI7EZlVZ5uvOk2F//GaxKUxajDqm9LhOVM1inxNAD2vy+UZDYsd0uyA9s7b3/FVZPSxqrCfg==",
"requires": {
"toggle-selection": "^1.0.6"
}
},
"core_d": { "core_d": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/core_d/-/core_d-5.0.1.tgz", "resolved": "https://registry.npmjs.org/core_d/-/core_d-5.0.1.tgz",
@ -36020,9 +36484,9 @@
"integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==" "integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A=="
}, },
"cypress": { "cypress": {
"version": "10.8.0", "version": "10.11.0",
"resolved": "https://registry.npmjs.org/cypress/-/cypress-10.8.0.tgz", "resolved": "https://registry.npmjs.org/cypress/-/cypress-10.11.0.tgz",
"integrity": "sha512-QVse0dnLm018hgti2enKMVZR9qbIO488YGX06nH5j3Dg1isL38DwrBtyrax02CANU6y8F4EJUuyW6HJKw1jsFA==", "integrity": "sha512-lsaE7dprw5DoXM00skni6W5ElVVLGAdRUUdZjX2dYsGjbY/QnpzWZ95Zom1mkGg0hAaO/QVTZoFVS7Jgr/GUPA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@cypress/request": "^2.88.10", "@cypress/request": "^2.88.10",
@ -36715,6 +37179,24 @@
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
"integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA=="
}, },
"downshift": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/downshift/-/downshift-5.2.1.tgz",
"integrity": "sha512-uHX2OLbWthLR8QbR8NCI8OmjvvJxq8+PrA95KblFd9JyB1zVZh1HnszzsWMMCnMuH6IvsUtVfF5HY7XfijJ2dw==",
"requires": {
"@babel/runtime": "^7.9.1",
"compute-scroll-into-view": "^1.0.13",
"prop-types": "^15.7.2",
"react-is": "^16.13.1"
},
"dependencies": {
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
}
}
},
"duplexer": { "duplexer": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
@ -38020,6 +38502,11 @@
"rimraf": "^3.0.2" "rimraf": "^3.0.2"
} }
}, },
"flatpickr": {
"version": "4.6.9",
"resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.9.tgz",
"integrity": "sha512-F0azNNi8foVWKSF+8X+ZJzz8r9sE1G4hl06RyceIaLvyltKvDl6vqk9Lm/6AUUCi5HWaIjiUbk7UpeE/fOXOpw=="
},
"flatted": { "flatted": {
"version": "3.2.7", "version": "3.2.7",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
@ -38876,6 +39363,12 @@
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz", "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz",
"integrity": "sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==" "integrity": "sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ=="
}, },
"immutable": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz",
"integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==",
"peer": true
},
"import-fresh": { "import-fresh": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@ -43000,6 +43493,16 @@
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
}, },
"lodash.findlast": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.findlast/-/lodash.findlast-4.6.0.tgz",
"integrity": "sha512-+OGwb1FVKjhc2aIEQ9vKqNDW1a0/HaCLr0iCIK10jfVif3dBE0nhQD0jOZNZLh7zOlmFUTrk+vt85eXoH4vKuA=="
},
"lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
},
"lodash.memoize": { "lodash.memoize": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@ -43010,6 +43513,11 @@
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
}, },
"lodash.omit": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz",
"integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg=="
},
"lodash.once": { "lodash.once": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
@ -43021,6 +43529,11 @@
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
"integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA=="
}, },
"lodash.throttle": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
"integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="
},
"lodash.truncate": { "lodash.truncate": {
"version": "4.4.2", "version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
@ -46934,6 +47447,11 @@
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
}, },
"resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
},
"resolve": { "resolve": {
"version": "1.22.1", "version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
@ -47167,6 +47685,17 @@
"resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz",
"integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA=="
}, },
"sass": {
"version": "1.55.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.55.0.tgz",
"integrity": "sha512-Pk+PMy7OGLs9WaxZGJMn7S96dvlyVBwwtToX895WmCpAOr5YiJYEUJfiJidMuKb613z2xNWcXCHEuOvjZbqC6A==",
"peer": true,
"requires": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",
"source-map-js": ">=0.6.2 <2.0.0"
}
},
"sax": { "sax": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
@ -47565,6 +48094,21 @@
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
}, },
"simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
"requires": {
"is-arrayish": "^0.3.1"
},
"dependencies": {
"is-arrayish": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
}
}
},
"sisteransi": { "sisteransi": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
@ -48736,6 +49280,11 @@
"is-number": "^7.0.0" "is-number": "^7.0.0"
} }
}, },
"toggle-selection": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
"integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ=="
},
"toidentifier": { "toidentifier": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@ -49905,6 +50454,14 @@
"integrity": "sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==", "integrity": "sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==",
"requires": {} "requires": {}
}, },
"use-resize-observer": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-6.1.0.tgz",
"integrity": "sha512-SiPcWHiIQ1CnHmb6PxbYtygqiZXR0U9dNkkbpX9VYnlstUwF8+QqpUTrzh13pjPwcjMVGR+QIC+nvF5ujfFNng==",
"requires": {
"resize-observer-polyfill": "^1.5.1"
}
},
"util": { "util": {
"version": "0.11.1", "version": "0.11.1",
"resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
@ -50782,6 +51339,11 @@
"integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==",
"dev": true "dev": true
}, },
"wicg-inert": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/wicg-inert/-/wicg-inert-3.1.2.tgz",
"integrity": "sha512-Ba9tGNYxXwaqKEi9sJJvPMKuo063umUPsHN0JJsjrs2j8KDSzkWLMZGZ+MH1Jf1Fq4OWZ5HsESJID6nRza2ang=="
},
"widest-line": { "widest-line": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz",
@ -50815,6 +51377,11 @@
} }
} }
}, },
"window-or-global": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/window-or-global/-/window-or-global-1.0.1.tgz",
"integrity": "sha512-tE12J/NenOv4xdVobD+AD3fT06T4KNqnzRhkv5nBIu7K+pvOH2oLCEgYP+i+5mF2jtI6FEADheOdZkA8YWET9w=="
},
"word-wrap": { "word-wrap": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",

View File

@ -6,6 +6,9 @@
"@babel/core": "^7.18.10", "@babel/core": "^7.18.10",
"@babel/plugin-transform-react-jsx": "^7.18.6", "@babel/plugin-transform-react-jsx": "^7.18.6",
"@babel/preset-react": "^7.18.6", "@babel/preset-react": "^7.18.6",
"@carbon/icons-react": "^11.10.0",
"@carbon/react": "^1.16.0",
"@carbon/styles": "^1.16.0",
"@ginkgo-bioworks/react-json-schema-form-builder": "^2.9.0", "@ginkgo-bioworks/react-json-schema-form-builder": "^2.9.0",
"@monaco-editor/react": "^4.4.5", "@monaco-editor/react": "^4.4.5",
"@rjsf/core": "^4.2.0", "@rjsf/core": "^4.2.0",
@ -55,6 +58,10 @@
"@ginkgo-bioworks/react-json-schema-form-builder": { "@ginkgo-bioworks/react-json-schema-form-builder": {
"react": "^18.2.0", "react": "^18.2.0",
"bootstrap": "^5.2.0-beta1" "bootstrap": "^5.2.0-beta1"
},
"@carbon/react": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
} }
}, },
"scripts": { "scripts": {

View File

@ -1,5 +1,6 @@
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import { Container } from 'react-bootstrap'; // @ts-ignore
import { Content } from '@carbon/react';
import { BrowserRouter, Routes, Route } from 'react-router-dom'; import { BrowserRouter, Routes, Route } from 'react-router-dom';
import ErrorContext from './contexts/ErrorContext'; import ErrorContext from './contexts/ErrorContext';
@ -9,7 +10,6 @@ import HomePage from './routes/HomePage';
import TaskShow from './routes/TaskShow'; 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 SubNavigation from './components/SubNavigation';
import { ErrorForDisplay } from './interfaces'; import { ErrorForDisplay } from './interfaces';
export default function App() { export default function App() {
@ -47,30 +47,27 @@ export default function App() {
return ( return (
<ErrorContext.Provider value={errorContextValueArray}> <ErrorContext.Provider value={errorContextValueArray}>
<NavigationBar /> <BrowserRouter>
<Container> <NavigationBar />
{errorTag} <Content>
<ErrorBoundary> {errorTag}
<BrowserRouter> <ErrorBoundary>
<SubNavigation /> <Routes>
<main style={{ padding: '1rem 0' }}> <Route path="/" element={<HomePage />} />
<Routes> <Route path="/tasks" element={<HomePage />} />
<Route path="/" element={<HomePage />} /> <Route path="/admin/*" element={<AdminRoutes />} />
<Route path="/tasks" element={<HomePage />} /> <Route
<Route path="/admin/*" element={<AdminRoutes />} /> path="/tasks/:process_instance_id/:task_id"
<Route element={<TaskShow />}
path="/tasks/:process_instance_id/:task_id" />
element={<TaskShow />} <Route
/> path="/tasks/:process_instance_id/:task_id"
<Route element={<TaskShow />}
path="/tasks/:process_instance_id/:task_id" />
element={<TaskShow />} </Routes>
/> </ErrorBoundary>
</Routes> </Content>
</main> </BrowserRouter>
</BrowserRouter>
</ErrorBoundary>
</Container>
</ErrorContext.Provider> </ErrorContext.Provider>
); );
} }

View File

@ -1,5 +1,6 @@
import { useState } from 'react'; import { useState } from 'react';
import { Button, Modal } from 'react-bootstrap'; // @ts-ignore
import { Button, Modal } from '@carbon/react';
type OwnProps = { type OwnProps = {
description?: string; description?: string;
@ -25,13 +26,6 @@ export default function ButtonWithConfirmation({
setShowConfirmationPrompt(false); setShowConfirmationPrompt(false);
}; };
const modalBodyElement = () => {
if (description) {
return <Modal.Body>{description}</Modal.Body>;
}
return null;
};
const handleConfirmation = () => { const handleConfirmation = () => {
onConfirmation(); onConfirmation();
setShowConfirmationPrompt(false); setShowConfirmationPrompt(false);
@ -40,28 +34,22 @@ export default function ButtonWithConfirmation({
const confirmationDialog = () => { const confirmationDialog = () => {
return ( return (
<Modal <Modal
show={showConfirmationPrompt} open={showConfirmationPrompt}
onHide={handleConfirmationPromptCancel} danger
> data-qa="modal-confirmation-dialog"
<Modal.Header closeButton> modalHeading={description}
<Modal.Title>{title}</Modal.Title> modalLabel={title}
</Modal.Header> primaryButtonText={confirmButtonLabel}
{modalBodyElement()} secondaryButtonText="Cancel"
<Modal.Footer> onSecondarySubmit={handleConfirmationPromptCancel}
<Button variant="secondary" onClick={handleConfirmationPromptCancel}> onRequestSubmit={handleConfirmation}
Cancel />
</Button>
<Button variant="primary" onClick={handleConfirmation}>
{confirmButtonLabel}
</Button>
</Modal.Footer>
</Modal>
); );
}; };
return ( return (
<> <>
<Button onClick={handleShowConfirmationPrompt} variant="danger"> <Button onClick={handleShowConfirmationPrompt} kind="danger">
{buttonLabel} {buttonLabel}
</Button> </Button>
{confirmationDialog()} {confirmationDialog()}

View File

@ -1,12 +1,28 @@
import { Button, Navbar, Nav, Container } from 'react-bootstrap'; import {
Header,
HeaderContainer,
HeaderMenuButton,
SkipToContent,
SideNav,
SideNavItems,
HeaderSideNavItems,
HeaderName,
HeaderNavigation,
HeaderMenuItem,
HeaderGlobalAction,
HeaderGlobalBar,
// @ts-ignore
} from '@carbon/react';
// @ts-ignore
import { Logout, Login } from '@carbon/icons-react';
import { useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom';
// @ts-expect-error TS(2307) FIXME: Cannot find module '../logo.svg' or its correspond... Remove this comment to see the full error message // @ts-expect-error TS(2307) FIXME: Cannot find module '../logo.svg' or its correspond... Remove this comment to see the full error message
import logo from '../logo.svg'; import logo from '../logo.svg';
import UserService from '../services/UserService'; import UserService from '../services/UserService';
// for ref: https://react-bootstrap.github.io/components/navbar/ // for ref: https://react-bootstrap.github.io/components/navbar/
export default function NavigationBar() { export default function NavigationBar() {
const navElements = null;
const handleLogout = () => { const handleLogout = () => {
UserService.doLogout(); UserService.doLogout();
}; };
@ -15,56 +31,116 @@ export default function NavigationBar() {
UserService.doLogin(); UserService.doLogin();
}; };
const loginLink = () => { const location = useLocation();
if (!UserService.isLoggedIn()) { const [activeKey, setActiveKey] = useState<string>('');
return (
<Navbar.Collapse className="justify-content-end"> useEffect(() => {
<Navbar.Text> let newActiveKey = '/admin/process-groups';
<Button variant="link" onClick={handleLogin}> if (location.pathname.match(/^\/admin\/messages\b/)) {
Login newActiveKey = '/admin/messages';
</Button> } else if (location.pathname.match(/^\/admin\/process-instances\b/)) {
</Navbar.Text> newActiveKey = '/admin/process-instances';
</Navbar.Collapse> } else if (location.pathname.match(/^\/admin\/secrets\b/)) {
); newActiveKey = '/admin/secrets';
} else if (location.pathname.match(/^\/admin\/authentications\b/)) {
newActiveKey = '/admin/authentications';
} else if (location.pathname === '/') {
newActiveKey = '/';
} else if (location.pathname.match(/^\/tasks\b/)) {
newActiveKey = '/';
} }
return null; setActiveKey(newActiveKey);
}, [location]);
const isActivePage = (menuItemPath: string) => {
return activeKey === menuItemPath;
}; };
const logoutLink = () => { const loginAndLogoutAction = () => {
if (UserService.isLoggedIn()) { if (UserService.isLoggedIn()) {
return ( return (
<Navbar.Collapse className="justify-content-end"> <>
<Navbar.Text> <HeaderGlobalAction>{UserService.getUsername()}</HeaderGlobalAction>
Signed in as: <strong>{UserService.getUsername()}</strong> <HeaderGlobalAction
</Navbar.Text> aria-label="Logout"
<Navbar.Text> onClick={handleLogout}
<Button data-qa="logout-button"
variant="link" >
onClick={handleLogout} <Logout />
data-qa="logout-button" </HeaderGlobalAction>
> </>
Logout
</Button>
</Navbar.Text>
</Navbar.Collapse>
); );
} }
return null; return (
<HeaderGlobalAction
data-qa="login-button"
aria-label="Login"
onClick={handleLogin}
>
<Login />
</HeaderGlobalAction>
);
}; };
return ( const headerMenuItems = () => {
<Navbar bg="dark" expand="lg" variant="dark"> return (
<Container> <>
<Navbar.Brand data-qa="spiffworkflow-logo" href="/admin"> <HeaderMenuItem href="/" isCurrentPage={isActivePage('/')}>
<img src={logo} className="app-logo" alt="logo" /> Home
</Navbar.Brand> </HeaderMenuItem>
<Navbar.Toggle aria-controls="basic-navbar-nav" /> <HeaderMenuItem
<Navbar.Collapse id="basic-navbar-nav"> href="/admin/process-groups"
<Nav className="me-auto">{navElements}</Nav> isCurrentPage={isActivePage('/admin/process-groups')}
</Navbar.Collapse> data-qa="header-nav-processes"
{loginLink()} >
{logoutLink()} Processes
</Container> </HeaderMenuItem>
</Navbar> <HeaderMenuItem
); href="/admin/process-instances"
isCurrentPage={isActivePage('/admin/process-instances')}
>
Process Instances
</HeaderMenuItem>
</>
);
};
if (activeKey) {
// TODO: apply theme g100 to the header
return (
<HeaderContainer
render={({ isSideNavExpanded, onClickSideNavExpand }: any) => (
<Header aria-label="IBM Platform Name">
<SkipToContent />
<HeaderMenuButton
aria-label="Open menu"
onClick={onClickSideNavExpand}
isActive={isSideNavExpanded}
/>
<HeaderName href="/" prefix="" data-qa="spiffworkflow-logo">
<img src={logo} className="app-logo" alt="logo" />
</HeaderName>
<HeaderNavigation
data-qa="main-nav-header"
aria-label="Spiffworkflow"
>
{headerMenuItems()}
</HeaderNavigation>
<SideNav
data-qa="side-nav-items"
aria-label="Side navigation"
expanded={isSideNavExpanded}
isPersistent={false}
>
<SideNavItems>
<HeaderSideNavItems>{headerMenuItems()}</HeaderSideNavItems>
</SideNavItems>
</SideNav>
<HeaderGlobalBar>{loginAndLogoutAction()}</HeaderGlobalBar>
</Header>
)}
/>
);
}
return null;
} }

View File

@ -1,7 +1,8 @@
import React from 'react'; import { useNavigate } from 'react-router-dom';
import { Link } from 'react-router-dom';
import { Dropdown, Stack } from 'react-bootstrap'; // @ts-ignore
import { Pagination } from '@carbon/react';
import { PaginationObject } from '../interfaces';
export const DEFAULT_PER_PAGE = 50; export const DEFAULT_PER_PAGE = 50;
export const DEFAULT_PAGE = 1; export const DEFAULT_PAGE = 1;
@ -10,9 +11,7 @@ type OwnProps = {
page: number; page: number;
perPage: number; perPage: number;
perPageOptions?: number[]; perPageOptions?: number[];
pagination: { pagination: PaginationObject | null;
[key: string]: number;
};
tableToDisplay: any; tableToDisplay: any;
queryParamString?: string; queryParamString?: string;
path: string; path: string;
@ -28,140 +27,32 @@ export default function PaginationForTable({
path, path,
}: OwnProps) { }: OwnProps) {
const PER_PAGE_OPTIONS = [2, 10, 50, 100]; const PER_PAGE_OPTIONS = [2, 10, 50, 100];
const navigate = useNavigate();
const buildPerPageDropdown = () => { const updateRows = (args: any) => {
const perPageDropdownRows = (perPageOptions || PER_PAGE_OPTIONS).map( const newPage = args.page;
(perPageOption) => { const { pageSize } = args;
if (perPageOption === perPage) { navigate(`${path}?page=${newPage}&per_page=${pageSize}${queryParamString}`);
return (
<Dropdown.Item
key={perPageOption}
href={`${path}?page=1&per_page=${perPageOption}`}
active
>
{perPageOption}
</Dropdown.Item>
);
}
return (
<Dropdown.Item
key={perPageOption}
href={`${path}?page=1&per_page=${perPageOption}`}
>
{perPageOption}
</Dropdown.Item>
);
}
);
return (
<Stack direction="horizontal" gap={3}>
<Dropdown className="ms-auto" id="pagination-page-dropdown">
<Dropdown.Toggle
id="process-instances-per-page"
variant="light border"
>
Number to show: {perPage}
</Dropdown.Toggle>
<Dropdown.Menu variant="light">{perPageDropdownRows}</Dropdown.Menu>
</Dropdown>
</Stack>
);
}; };
const buildPaginationNav = () => { if (pagination) {
let previousPageTag = null;
if (page === 1) {
previousPageTag = (
<li
data-qa="pagination-previous-button-inactive"
className="page-item disabled"
key="previous"
>
<span style={{ fontSize: '1.5em' }} className="page-link">
&laquo;
</span>
</li>
);
} else {
previousPageTag = (
<li className="page-item" key="previous">
<Link
data-qa="pagination-previous-button"
className="page-link"
style={{ fontSize: '1.5em' }}
to={`${path}?page=${
page - 1
}&per_page=${perPage}${queryParamString}`}
>
&laquo;
</Link>
</li>
);
}
let nextPageTag = null;
if (page >= pagination.pages) {
nextPageTag = (
<li
data-qa="pagination-next-button-inactive"
className="page-item disabled"
key="next"
>
<span style={{ fontSize: '1.5em' }} className="page-link">
&raquo;
</span>
</li>
);
} else {
nextPageTag = (
<li className="page-item" key="next">
<Link
data-qa="pagination-next-button"
className="page-link"
style={{ fontSize: '1.5em' }}
to={`${path}?page=${
page + 1
}&per_page=${perPage}${queryParamString}`}
>
&raquo;
</Link>
</li>
);
}
let startingNumber = (page - 1) * perPage + 1;
let endingNumber = page * perPage;
if (endingNumber > pagination.total) {
endingNumber = pagination.total;
}
if (startingNumber > pagination.total) {
startingNumber = pagination.total;
}
return ( return (
<Stack direction="horizontal" gap={3}> <>
<p className="ms-auto"> {tableToDisplay}
{startingNumber}-{endingNumber} of{' '} <Pagination
<span data-qa="total-paginated-items">{pagination.total}</span> data-qa="pagination-options"
</p> backwardText="Previous page"
<nav aria-label="Page navigation"> forwardText="Next page"
<div> itemsPerPageText="Items per page:"
<ul className="pagination"> page={page}
{previousPageTag} pageNumberText="Page Number"
{nextPageTag} pageSize={perPage}
</ul> pageSizes={perPageOptions || PER_PAGE_OPTIONS}
</div> totalItems={pagination.total}
</nav> onChange={updateRows}
</Stack> />
</>
); );
}; }
return null;
return (
<main>
{buildPaginationNav()}
{tableToDisplay}
{buildPerPageDropdown()}
</main>
);
} }

View File

@ -0,0 +1,181 @@
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
// @ts-ignore
import { Button, ButtonSet, Form, Stack, TextInput } from '@carbon/react';
import { slugifyString } from '../helpers';
import HttpService from '../services/HttpService';
import { ProcessGroup } from '../interfaces';
import ButtonWithConfirmation from './ButtonWithConfirmation';
type OwnProps = {
mode: string;
processGroup: ProcessGroup;
setProcessGroup: (..._args: any[]) => any;
};
export default function ProcessGroupForm({
mode,
processGroup,
setProcessGroup,
}: OwnProps) {
const [identifierInvalid, setIdentifierInvalid] = useState<boolean>(false);
const [idHasBeenUpdatedByUser, setIdHasBeenUpdatedByUser] =
useState<boolean>(false);
const [displayNameInvalid, setDisplayNameInvalid] = useState<boolean>(false);
const navigate = useNavigate();
const navigateToProcessGroup = (_result: any) => {
if (processGroup) {
navigate(`/admin/process-groups/${processGroup.id}`);
}
};
const navigateToProcessGroups = (_result: any) => {
navigate(`/admin/process-groups`);
};
const hasValidIdentifier = (identifierToCheck: string) => {
return identifierToCheck.match(/^[a-z0-9][0-9a-z-]+[a-z0-9]$/);
};
const deleteProcessGroup = () => {
HttpService.makeCallToBackend({
path: `/process-groups/${processGroup.id}`,
successCallback: navigateToProcessGroups,
httpMethod: 'DELETE',
});
};
const handleFormSubmission = (event: any) => {
event.preventDefault();
let hasErrors = false;
if (!hasValidIdentifier(processGroup.id)) {
setIdentifierInvalid(true);
hasErrors = true;
}
if (processGroup.display_name === '') {
setDisplayNameInvalid(true);
hasErrors = true;
}
if (hasErrors) {
return;
}
let path = '/process-groups';
if (mode === 'edit') {
path = `/process-groups/${processGroup.id}`;
}
let httpMethod = 'POST';
if (mode === 'edit') {
httpMethod = 'PUT';
}
const postBody = {
display_name: processGroup.display_name,
description: processGroup.description,
};
if (mode === 'new') {
Object.assign(postBody, { id: processGroup.id });
}
HttpService.makeCallToBackend({
path,
successCallback: navigateToProcessGroup,
httpMethod,
postBody,
});
};
const updateProcessGroup = (newValues: any) => {
const processGroupToCopy = {
...processGroup,
};
Object.assign(processGroupToCopy, newValues);
setProcessGroup(processGroupToCopy);
};
const onDisplayNameChanged = (newDisplayName: any) => {
setDisplayNameInvalid(false);
const updateDict = { display_name: newDisplayName };
if (!idHasBeenUpdatedByUser) {
Object.assign(updateDict, { id: slugifyString(newDisplayName) });
}
updateProcessGroup(updateDict);
};
const formElements = () => {
const textInputs = [
<TextInput
id="process-group-display-name"
name="display_name"
invalidText="Display Name is required."
invalid={displayNameInvalid}
labelText="Display Name*"
value={processGroup.display_name}
onChange={(event: any) => onDisplayNameChanged(event.target.value)}
onBlur={(event: any) => console.log('event', event)}
/>,
];
if (mode === 'new') {
textInputs.push(
<TextInput
id="process-group-identifier"
name="id"
invalidText="Identifier is required and must be all lowercase characters and hyphens."
invalid={identifierInvalid}
labelText="Identifier*"
value={processGroup.id}
onChange={(event: any) => {
updateProcessGroup({ id: event.target.value });
// was invalid, and now valid
if (identifierInvalid && hasValidIdentifier(event.target.value)) {
setIdentifierInvalid(false);
}
setIdHasBeenUpdatedByUser(true);
}}
/>
);
}
textInputs.push(
<TextInput
id="process-group-description"
name="description"
labelText="Description"
value={processGroup.description}
onChange={(event: any) =>
updateProcessGroup({ description: event.target.value })
}
/>
);
return textInputs;
};
const formButtons = () => {
const buttons = [
<Button kind="secondary" type="submit">
Submit
</Button>,
];
if (mode === 'edit') {
buttons.push(
<ButtonWithConfirmation
description={`Delete Process Group ${processGroup.id}?`}
onConfirmation={deleteProcessGroup}
buttonLabel="Delete"
confirmButtonLabel="Delete"
/>
);
}
return <ButtonSet>{buttons}</ButtonSet>;
};
return (
<Form onSubmit={handleFormSubmission}>
<Stack gap={5}>
{formElements()}
{formButtons()}
</Stack>
</Form>
);
}

View File

@ -0,0 +1,48 @@
import {
ComboBox,
// @ts-ignore
} from '@carbon/react';
import { truncateString } from '../helpers';
import { ProcessModel } from '../interfaces';
type OwnProps = {
onChange: (..._args: any[]) => any;
processModels: ProcessModel[];
selectedItem?: ProcessModel | null;
titleText?: string;
};
export default function ProcessModelSearch({
processModels,
selectedItem,
onChange,
titleText = 'Process model',
}: OwnProps) {
const shouldFilterProcessModel = (options: any) => {
const processModel: ProcessModel = options.item;
const { inputValue } = options;
return `${processModel.process_group_id}/${processModel.id} (${processModel.display_name})`.includes(
inputValue
);
};
return (
<ComboBox
onChange={onChange}
id="process-model-select"
data-qa="process-model-selection"
items={processModels}
itemToString={(processModel: ProcessModel) => {
if (processModel) {
return `${processModel.process_group_id}/${
processModel.id
} (${truncateString(processModel.display_name, 20)})`;
}
return null;
}}
shouldFilterItem={shouldFilterProcessModel}
placeholder="Choose a process model"
titleText={titleText}
selectedItem={selectedItem}
/>
);
}

View File

@ -18,7 +18,8 @@ import {
} from 'dmn-js-properties-panel'; } from 'dmn-js-properties-panel';
import React, { useRef, useEffect, useState } from 'react'; import React, { useRef, useEffect, useState } from 'react';
import Button from 'react-bootstrap/Button'; // @ts-ignore
import { Button } from '@carbon/react';
import 'bpmn-js/dist/assets/diagram-js.css'; import 'bpmn-js/dist/assets/diagram-js.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css'; import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css';

View File

@ -16,4 +16,6 @@ export const PROCESS_STATUSES = [
'suspended', 'suspended',
]; ];
export const DATE_FORMAT = 'yyyy-MM-dd HH:mm:ss'; // with time: yyyy-MM-dd HH:mm:ss
export const DATE_FORMAT = 'yyyy-MM-dd';
export const DATE_FORMAT_CARBON = 'Y-m-d';

View File

@ -20,7 +20,10 @@ export const capitalizeFirstLetter = (string: any) => {
return string.charAt(0).toUpperCase() + string.slice(1); return string.charAt(0).toUpperCase() + string.slice(1);
}; };
export const convertDateToSeconds = (date: any, onChangeFunction: any) => { export const convertDateToSeconds = (
date: any,
onChangeFunction: any = null
) => {
let dateInSeconds = date; let dateInSeconds = date;
if (date !== null) { if (date !== null) {
let dateInMilliseconds = date; let dateInMilliseconds = date;
@ -39,14 +42,26 @@ export const convertDateToSeconds = (date: any, onChangeFunction: any) => {
return null; return null;
}; };
export const convertSecondsToFormattedDate = (seconds: number) => { export const convertStringToDate = (dateString: string) => {
if (seconds) { if (dateString) {
const startDate = new Date(seconds * 1000); return new Date(dateString);
return format(startDate, DATE_FORMAT);
} }
return null; return null;
}; };
export const convertSecondsToFormattedDate = (seconds: number) => {
if (seconds) {
const dateObject = new Date(seconds * 1000);
return format(dateObject, DATE_FORMAT);
}
return null;
};
export const convertDateStringToSeconds = (dateString: string) => {
const dateObject = convertStringToDate(dateString);
return convertDateToSeconds(dateObject);
};
export const objectIsEmpty = (obj: object) => { export const objectIsEmpty = (obj: object) => {
return Object.keys(obj).length === 0; return Object.keys(obj).length === 0;
}; };
@ -90,3 +105,11 @@ export const getProcessModelFullIdentifierFromSearchParams = (
} }
return processModelFullIdentifier; return processModelFullIdentifier;
}; };
// https://stackoverflow.com/a/71352046/6090676
export const truncateString = (text: string, len: number) => {
if (text.length > len && text.length > 0) {
return `${text.split(' ').slice(0, len).join(' ')} ...`;
}
return text;
};

View File

@ -20,12 +20,15 @@ span.bjs-crumb {
} }
.app-logo { .app-logo {
height: 35%; height: 85%;
width: 35%; width: 85%;
margin-top: 1em; margin-top: 1em;
margin-bottom: 1em; margin-bottom: 1em;
} }
.spiffworkflow-header-container {
margin-bottom: 2em;
}
.active-task-highlight:not(.djs-connection) .djs-visual > :nth-child(1) { .active-task-highlight:not(.djs-connection) .djs-visual > :nth-child(1) {
fill: yellow !important; fill: yellow !important;
@ -44,6 +47,17 @@ span.bjs-crumb {
margin:auto; margin:auto;
} }
.cds--btn.button-white-background {
color: #393939;
background: #FFFFFF;
background-blend-mode: multiply;
border: 1px solid #393939;
}
.with-bottom-margin {
margin-bottom: 1em;
}
.diagram-viewer-canvas { .diagram-viewer-canvas {
border:1px solid #000000; border:1px solid #000000;
height:70vh; height:70vh;

View File

@ -0,0 +1,5 @@
// @use '@carbon/react/scss/themes';
// @use '@carbon/react/scss/theme' with ($theme: themes.$g100);
@use '@carbon/react';
@use '@carbon/styles';
// @include grid.flex-grid();

View File

@ -4,6 +4,7 @@ import App from './App';
import 'bootstrap/dist/css/bootstrap.css'; import 'bootstrap/dist/css/bootstrap.css';
import './index.css'; import './index.css';
import './index.scss';
import reportWebVitals from './reportWebVitals'; import reportWebVitals from './reportWebVitals';
import UserService from './services/UserService'; import UserService from './services/UserService';

View File

@ -14,6 +14,7 @@ export interface RecentProcessModel {
export interface ProcessGroup { export interface ProcessGroup {
id: string; id: string;
display_name: string; display_name: string;
description?: string | null;
} }
export interface ProcessModel { export interface ProcessModel {
@ -41,3 +42,13 @@ export interface AuthenticationItem {
id: string; id: string;
parameters: AuthenticationParam[]; parameters: AuthenticationParam[];
} }
export interface PaginationObject {
count: number;
total: number;
pages: number;
}
export interface CarbonComboBoxSelection {
selectedItem: ProcessModel;
}

View File

@ -1,5 +1,6 @@
import { useContext, useEffect, useState } from 'react'; import { useContext, useEffect, useState } from 'react';
import { Table } from 'react-bootstrap'; // @ts-ignore
import { Table } from '@carbon/react';
import ErrorContext from '../contexts/ErrorContext'; import ErrorContext from '../contexts/ErrorContext';
import { AuthenticationItem } from '../interfaces'; import { AuthenticationItem } from '../interfaces';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';

View File

@ -1,17 +1,18 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Button, Table } from 'react-bootstrap'; // @ts-ignore
import { Button, Table } from '@carbon/react';
import { Link, useSearchParams } from 'react-router-dom'; import { Link, useSearchParams } from 'react-router-dom';
import PaginationForTable from '../components/PaginationForTable'; import PaginationForTable from '../components/PaginationForTable';
import { getPageInfoFromSearchParams } from '../helpers'; import { getPageInfoFromSearchParams } from '../helpers';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
import { RecentProcessModel } from '../interfaces'; 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 HomePage() {
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const [tasks, setTasks] = useState([]); const [tasks, setTasks] = useState([]);
const [pagination, setPagination] = useState(null); const [pagination, setPagination] = useState<PaginationObject | null>(null);
useEffect(() => { useEffect(() => {
const { page, perPage } = getPageInfoFromSearchParams( const { page, perPage } = getPageInfoFromSearchParams(
@ -53,7 +54,7 @@ export default function HomePage() {
data-qa="process-instance-show-link" data-qa="process-instance-show-link"
to={`/admin/process-models/${rowToUse.process_group_identifier}/${rowToUse.process_model_identifier}/process-instances/${rowToUse.process_instance_id}`} to={`/admin/process-models/${rowToUse.process_group_identifier}/${rowToUse.process_model_identifier}/process-instances/${rowToUse.process_instance_id}`}
> >
View View {rowToUse.process_instance_id}
</Link> </Link>
</td> </td>
<td <td
@ -125,10 +126,10 @@ export default function HomePage() {
); );
}; };
const relevantProcessModelSection = const tasksWaitingForMeComponent = () => {
recentProcessModels.length > 0 && buildRecentProcessModelSection(); if (pagination && pagination.total < 1) {
return null;
if (pagination) { }
const { page, perPage } = getPageInfoFromSearchParams( const { page, perPage } = getPageInfoFromSearchParams(
searchParams, searchParams,
PER_PAGE_FOR_TASKS_ON_HOME_PAGE PER_PAGE_FOR_TASKS_ON_HOME_PAGE
@ -144,6 +145,17 @@ export default function HomePage() {
tableToDisplay={buildTable()} tableToDisplay={buildTable()}
path="/tasks" path="/tasks"
/> />
</>
);
};
const relevantProcessModelSection =
recentProcessModels.length > 0 && buildRecentProcessModelSection();
if (pagination) {
return (
<>
{tasksWaitingForMeComponent()}
{relevantProcessModelSection} {relevantProcessModelSection}
</> </>
); );

View File

@ -1,5 +1,6 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Table } from 'react-bootstrap'; // @ts-ignore
import { Table } from '@carbon/react';
import { Link, useParams, useSearchParams } from 'react-router-dom'; import { Link, useParams, useSearchParams } from 'react-router-dom';
import PaginationForTable from '../components/PaginationForTable'; import PaginationForTable from '../components/PaginationForTable';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb';

View File

@ -1,20 +1,18 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { Button, Stack } from 'react-bootstrap'; // @ts-ignore
import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; import ProcessGroupForm from '../components/ProcessGroupForm';
import { ProcessGroup } from '../interfaces';
export default function ProcessGroupEdit() { export default function ProcessGroupEdit() {
const [displayName, setDisplayName] = useState('');
const params = useParams(); const params = useParams();
const navigate = useNavigate(); const [processGroup, setProcessGroup] = useState<ProcessGroup | null>(null);
const [processGroup, setProcessGroup] = useState(null);
useEffect(() => { useEffect(() => {
const setProcessGroupsFromResult = (result: any) => { const setProcessGroupsFromResult = (result: any) => {
setProcessGroup(result); setProcessGroup(result);
setDisplayName(result.display_name);
}; };
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
@ -23,69 +21,16 @@ export default function ProcessGroupEdit() {
}); });
}, [params]); }, [params]);
const navigateToProcessGroup = (_result: any) => {
navigate(`/admin/process-groups/${(processGroup as any).id}`);
};
const navigateToProcessGroups = (_result: any) => {
navigate(`/admin/process-groups`);
};
const updateProcessGroup = (event: any) => {
event.preventDefault();
HttpService.makeCallToBackend({
path: `/process-groups/${(processGroup as any).id}`,
successCallback: navigateToProcessGroup,
httpMethod: 'PUT',
postBody: {
display_name: displayName,
id: (processGroup as any).id,
},
});
};
const deleteProcessGroup = () => {
HttpService.makeCallToBackend({
path: `/process-groups/${(processGroup as any).id}`,
successCallback: navigateToProcessGroups,
httpMethod: 'DELETE',
});
};
const onDisplayNameChanged = (newDisplayName: any) => {
setDisplayName(newDisplayName);
};
if (processGroup) { if (processGroup) {
return ( return (
<> <>
<ProcessBreadcrumb processGroupId={(processGroup as any).id} /> <ProcessBreadcrumb processGroupId={(processGroup as any).id} />
<h2>Edit Process Group: {(processGroup as any).id}</h2> <h2>Edit Process Group: {(processGroup as any).id}</h2>
<form onSubmit={updateProcessGroup}> <ProcessGroupForm
<label>Display Name:</label> mode="edit"
<input processGroup={processGroup}
name="display_name" setProcessGroup={setProcessGroup}
type="text" />
value={displayName}
onChange={(e) => onDisplayNameChanged(e.target.value)}
/>
<br />
<br />
<Stack direction="horizontal" gap={3}>
<Button type="submit">Submit</Button>
<Button
variant="secondary"
href={`/admin/process-groups/${(processGroup as any).id}`}
>
Cancel
</Button>
<ButtonWithConfirmation
description={`Delete Process Group ${(processGroup as any).id}?`}
onConfirmation={deleteProcessGroup}
buttonLabel="Delete"
/>
</Stack>
</form>
</> </>
); );
} }

View File

@ -1,13 +1,21 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Link, useNavigate, useSearchParams } from 'react-router-dom'; import { Link, useNavigate, useSearchParams } from 'react-router-dom';
import { Button, Form, InputGroup, Table } from 'react-bootstrap'; import {
import { Typeahead } from 'react-bootstrap-typeahead'; Button,
import { Option } from 'react-bootstrap-typeahead/types/types'; Table,
// ExpandableTile,
// TileAboveTheFoldContent,
// TileBelowTheFoldContent,
// TextInput,
// ClickableTile,
// @ts-ignore
} from '@carbon/react';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import PaginationForTable from '../components/PaginationForTable'; import PaginationForTable from '../components/PaginationForTable';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
import { getPageInfoFromSearchParams } from '../helpers'; import { getPageInfoFromSearchParams } from '../helpers';
import { ProcessModel } from '../interfaces'; import { CarbonComboBoxSelection, ProcessGroup } from '../interfaces';
import ProcessModelSearch from '../components/ProcessModelSearch';
// Example process group json // Example process group json
// {'process_group_id': 'sure', 'display_name': 'Test Workflows', 'id': 'test_process_group'} // {'process_group_id': 'sure', 'display_name': 'Test Workflows', 'id': 'test_process_group'}
@ -17,8 +25,9 @@ export default function ProcessGroupList() {
const [processGroups, setProcessGroups] = useState([]); const [processGroups, setProcessGroups] = useState([]);
const [pagination, setPagination] = useState(null); const [pagination, setPagination] = useState(null);
const [processModeleSelectionOptions, setProcessModelSelectionOptions] = const [processModelAvailableItems, setProcessModelAvailableItems] = useState(
useState([]); []
);
useEffect(() => { useEffect(() => {
const setProcessGroupsFromResult = (result: any) => { const setProcessGroupsFromResult = (result: any) => {
@ -31,7 +40,7 @@ export default function ProcessGroupList() {
Object.assign(item, { label }); Object.assign(item, { label });
return item; return item;
}); });
setProcessModelSelectionOptions(selectionArray); setProcessModelAvailableItems(selectionArray);
}; };
const { page, perPage } = getPageInfoFromSearchParams(searchParams); const { page, perPage } = getPageInfoFromSearchParams(searchParams);
@ -48,7 +57,7 @@ export default function ProcessGroupList() {
}, [searchParams]); }, [searchParams]);
const buildTable = () => { const buildTable = () => {
const rows = processGroups.map((row) => { const rows = processGroups.map((row: ProcessGroup) => {
return ( return (
<tr key={(row as any).id}> <tr key={(row as any).id}>
<td> <td>
@ -72,6 +81,17 @@ export default function ProcessGroupList() {
<tbody>{rows}</tbody> <tbody>{rows}</tbody>
</Table> </Table>
); );
// const rows = processGroups.map((row: ProcessGroup) => {
// return (
// <span>
// <ClickableTile href={`/admin/process-groups/${row.id}`}>
// {row.display_name}
// </ClickableTile>
// </span>
// );
// });
//
// return <div style={{ width: '400px' }}>{rows}</div>;
}; };
const processGroupsDisplayArea = () => { const processGroupsDisplayArea = () => {
@ -97,35 +117,18 @@ export default function ProcessGroupList() {
}; };
const processModelSearchArea = () => { const processModelSearchArea = () => {
const processModelSearchOnChange = (selected: Option[]) => { const processModelSearchOnChange = (selection: CarbonComboBoxSelection) => {
const processModel = selected[0] as ProcessModel; const processModel = selection.selectedItem;
navigate( navigate(
`/admin/process-models/${processModel.process_group_id}/${processModel.id}` `/admin/process-models/${processModel.process_group_id}/${processModel.id}`
); );
}; };
return ( return (
<form onSubmit={function hey() {}}> <ProcessModelSearch
<h3>Search</h3> onChange={processModelSearchOnChange}
<Form.Group> processModels={processModelAvailableItems}
<InputGroup> titleText="Process model search"
<InputGroup.Text className="text-nowrap"> />
Process Model:{' '}
</InputGroup.Text>
<Typeahead
style={{ width: 500 }}
id="process-model-selection"
labelKey="label"
onChange={processModelSearchOnChange}
// for cypress tests since data-qa does not work
inputProps={{
name: 'process-model-selection',
}}
options={processModeleSelectionOptions}
placeholder="Choose a process model..."
/>
</InputGroup>
</Form.Group>
</form>
); );
}; };
@ -133,7 +136,9 @@ export default function ProcessGroupList() {
return ( return (
<> <>
<ProcessBreadcrumb hotCrumbs={[['Process Groups']]} /> <ProcessBreadcrumb hotCrumbs={[['Process Groups']]} />
<Button href="/admin/process-groups/new">Add a process group</Button> <Button kind="secondary" href="/admin/process-groups/new">
Add a process group
</Button>
<br /> <br />
<br /> <br />
{processModelSearchArea()} {processModelSearchArea()}

View File

@ -1,71 +1,24 @@
import { useState } from 'react'; import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import Button from 'react-bootstrap/Button';
import Form from 'react-bootstrap/Form';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import { slugifyString } from '../helpers'; import ProcessGroupForm from '../components/ProcessGroupForm';
import HttpService from '../services/HttpService'; import { ProcessGroup } from '../interfaces';
export default function ProcessGroupNew() { export default function ProcessGroupNew() {
const [identifier, setIdentifier] = useState(''); const [processGroup, setProcessGroup] = useState<ProcessGroup>({
const [idHasBeenUpdatedByUser, setIdHasBeenUpdatedByUser] = useState(false); id: '',
const [displayName, setDisplayName] = useState(''); display_name: '',
const navigate = useNavigate(); description: '',
});
const navigateToProcessGroup = (_result: any) => {
navigate(`/admin/process-groups/${identifier}`);
};
const addProcessGroup = (event: any) => {
event.preventDefault();
HttpService.makeCallToBackend({
path: `/process-groups`,
successCallback: navigateToProcessGroup,
httpMethod: 'POST',
postBody: {
id: identifier,
display_name: displayName,
},
});
};
const onDisplayNameChanged = (newDisplayName: any) => {
setDisplayName(newDisplayName);
if (!idHasBeenUpdatedByUser) {
setIdentifier(slugifyString(newDisplayName));
}
};
return ( return (
<> <>
<ProcessBreadcrumb /> <ProcessBreadcrumb />
<h2>Add Process Group</h2> <h2>Add Process Group</h2>
<Form onSubmit={addProcessGroup}> <ProcessGroupForm
<Form.Group className="mb-3" controlId="display_name"> mode="new"
<Form.Label>Display Name:</Form.Label> processGroup={processGroup}
<Form.Control setProcessGroup={setProcessGroup}
type="text" />
name="display_name"
value={displayName}
onChange={(e) => onDisplayNameChanged(e.target.value)}
/>
</Form.Group>
<Form.Group className="mb-3" controlId="identifier">
<Form.Label>ID:</Form.Label>
<Form.Control
type="text"
name="id"
value={identifier}
onChange={(e) => {
setIdentifier(e.target.value);
setIdHasBeenUpdatedByUser(true);
}}
/>
</Form.Group>
<Button variant="primary" type="submit">
Submit
</Button>
</Form>
</> </>
); );
} }

View File

@ -1,6 +1,7 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Link, useSearchParams, useParams } from 'react-router-dom'; import { Link, useSearchParams, useParams } from 'react-router-dom';
import { Button, Table, Stack } from 'react-bootstrap'; // @ts-ignore
import { Button, Table, Stack } from '@carbon/react';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import PaginationForTable from '../components/PaginationForTable'; import PaginationForTable from '../components/PaginationForTable';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
@ -81,7 +82,7 @@ export default function ProcessGroupShow() {
]} ]}
/> />
<ul> <ul>
<Stack direction="horizontal" gap={3}> <Stack orientation="horizontal" gap={3}>
<Button <Button
href={`/admin/process-models/${(processGroup as any).id}/new`} href={`/admin/process-models/${(processGroup as any).id}/new`}
> >

View File

@ -6,14 +6,27 @@ import {
useSearchParams, useSearchParams,
} from 'react-router-dom'; } from 'react-router-dom';
import { Button, Table, Stack, Form, InputGroup } from 'react-bootstrap'; // @ts-ignore
// @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'reac... Remove this comment to see the full error message import { Filter } from '@carbon/icons-react';
import DatePicker from 'react-datepicker';
import { Typeahead } from 'react-bootstrap-typeahead';
import { Option } from 'react-bootstrap-typeahead/types/types';
import { PROCESS_STATUSES, DATE_FORMAT } from '../config';
import { import {
convertDateToSeconds, Button,
ButtonSet,
DatePicker,
DatePickerInput,
Table,
Grid,
Column,
MultiSelect,
// TableHeader,
// TableHead,
// TableRow,
// TableBody,
// TableCell,
// @ts-ignore
} from '@carbon/react';
import { PROCESS_STATUSES, DATE_FORMAT, DATE_FORMAT_CARBON } from '../config';
import {
convertDateStringToSeconds,
convertSecondsToFormattedDate, convertSecondsToFormattedDate,
getPageInfoFromSearchParams, getPageInfoFromSearchParams,
getProcessModelFullIdentifierFromSearchParams, getProcessModelFullIdentifierFromSearchParams,
@ -27,6 +40,8 @@ import HttpService from '../services/HttpService';
import 'react-bootstrap-typeahead/css/Typeahead.css'; import 'react-bootstrap-typeahead/css/Typeahead.css';
import 'react-bootstrap-typeahead/css/Typeahead.bs5.css'; import 'react-bootstrap-typeahead/css/Typeahead.bs5.css';
import { PaginationObject, ProcessModel } from '../interfaces';
import ProcessModelSearch from '../components/ProcessModelSearch';
export default function ProcessInstanceList() { export default function ProcessInstanceList() {
const params = useParams(); const params = useParams();
@ -34,36 +49,38 @@ export default function ProcessInstanceList() {
const navigate = useNavigate(); const navigate = useNavigate();
const [processInstances, setProcessInstances] = useState([]); const [processInstances, setProcessInstances] = useState([]);
const [pagination, setPagination] = useState(null); const [pagination, setPagination] = useState<PaginationObject | null>(null);
const oneHourInSeconds = 3600; const oneHourInSeconds = 3600;
const oneMonthInSeconds = oneHourInSeconds * 24 * 30; const oneMonthInSeconds = oneHourInSeconds * 24 * 30;
const [startFrom, setStartFrom] = useState(null); const [startFrom, setStartFrom] = useState<string>('');
const [startTill, setStartTill] = useState(null); const [startTo, setStartTo] = useState<string>('');
const [endFrom, setEndFrom] = useState(null); const [endFrom, setEndFrom] = useState<string>('');
const [endTill, setEndTill] = useState(null); const [endTo, setEndTo] = useState<string>('');
const [showFilterOptions, setShowFilterOptions] = useState<boolean>(false);
const setErrorMessage = (useContext as any)(ErrorContext)[1]; const setErrorMessage = (useContext as any)(ErrorContext)[1];
const [processStatuseSelectionOptions, setProcessStatusSelectionOptions] = const [processStatusAllOptions, setProcessStatusAllOptions] = useState<any[]>(
useState<any[]>([]);
const [processStatusSelection, setProcessStatusSelection] = useState<
Option[]
>([]);
const [processModeleSelectionOptions, setProcessModelSelectionOptions] =
useState([]);
const [processModelSelection, setProcessModelSelection] = useState<Option[]>(
[] []
); );
const [processStatusSelection, setProcessStatusSelection] = useState<
string[]
>([]);
const [processModelAvailableItems, setProcessModelAvailableItems] = useState<
ProcessModel[]
>([]);
const [processModelSelection, setProcessModelSelection] =
useState<ProcessModel | null>(null);
const parametersToAlwaysFilterBy = useMemo(() => { const parametersToAlwaysFilterBy = useMemo(() => {
return { return {
start_from: setStartFrom, start_from: setStartFrom,
start_till: setStartTill, start_to: setStartTo,
end_from: setEndFrom, end_from: setEndFrom,
end_till: setEndTill, end_to: setEndTo,
}; };
}, [setStartFrom, setStartTill, setEndFrom, setEndTill]); }, [setStartFrom, setStartTo, setEndFrom, setEndTo]);
const parametersToGetFromSearchParams = useMemo(() => { const parametersToGetFromSearchParams = useMemo(() => {
return { return {
@ -90,7 +107,10 @@ export default function ProcessInstanceList() {
const searchParamValue = searchParams.get(paramName); const searchParamValue = searchParams.get(paramName);
if (searchParamValue) { if (searchParamValue) {
queryParamString += `&${paramName}=${searchParamValue}`; queryParamString += `&${paramName}=${searchParamValue}`;
functionToCall(searchParamValue); const dateString = convertSecondsToFormattedDate(
searchParamValue as any
);
functionToCall(dateString);
} }
}); });
@ -118,24 +138,24 @@ export default function ProcessInstanceList() {
const label = `${item.process_group_id}/${item.id}`; const label = `${item.process_group_id}/${item.id}`;
Object.assign(item, { label }); Object.assign(item, { label });
if (label === processModelFullIdentifier) { if (label === processModelFullIdentifier) {
setProcessModelSelection([item]); setProcessModelSelection(item);
} }
return item; return item;
}); });
setProcessModelSelectionOptions(selectionArray); setProcessModelAvailableItems(selectionArray);
const processStatusSelectedArray: Option[] = []; const processStatusSelectedArray: string[] = [];
const processStatusSelectionArray = PROCESS_STATUSES.map( const processStatusAllOptionsArray = PROCESS_STATUSES.map(
(processStatusOption: any) => { (processStatusOption: any) => {
const regex = new RegExp(`\\b${processStatusOption}\\b`); const regex = new RegExp(`\\b${processStatusOption}\\b`);
if ((searchParams.get('process_status') || '').match(regex)) { if ((searchParams.get('process_status') || '').match(regex)) {
processStatusSelectedArray.push({ label: processStatusOption }); processStatusSelectedArray.push(processStatusOption);
} }
return { label: processStatusOption }; return processStatusOption;
} }
); );
setProcessStatusSelection(processStatusSelectedArray); setProcessStatusSelection(processStatusSelectedArray);
setProcessStatusSelectionOptions(processStatusSelectionArray); setProcessStatusAllOptions(processStatusAllOptionsArray);
getProcessInstances(); getProcessInstances();
} }
@ -170,51 +190,58 @@ export default function ProcessInstanceList() {
} }
}; };
const handleFilter = (event: any) => { const applyFilter = (event: any) => {
event.preventDefault(); event.preventDefault();
const { page, perPage } = getPageInfoFromSearchParams(searchParams); const { page, perPage } = getPageInfoFromSearchParams(searchParams);
let queryParamString = `per_page=${perPage}&page=${page}`; let queryParamString = `per_page=${perPage}&page=${page}`;
if (isTrueComparison(startFrom, '>', startTill)) { const startFromSeconds = convertDateStringToSeconds(startFrom);
setErrorMessage({ message: 'startFrom cannot be after startTill' }); const endFromSeconds = convertDateStringToSeconds(endFrom);
const startToSeconds = convertDateStringToSeconds(startTo);
const endToSeconds = convertDateStringToSeconds(endTo);
if (isTrueComparison(startFromSeconds, '>', startToSeconds)) {
setErrorMessage({
message: '"Start date from" cannot be after "start date to"',
});
return; return;
} }
if (isTrueComparison(endFrom, '>', endTill)) { if (isTrueComparison(endFromSeconds, '>', endToSeconds)) {
setErrorMessage({ message: 'endFrom cannot be after endTill' }); setErrorMessage({
message: '"End date from" cannot be after "end date to"',
});
return; return;
} }
if (isTrueComparison(startFrom, '>', endFrom)) { if (isTrueComparison(startFromSeconds, '>', endFromSeconds)) {
setErrorMessage({ message: 'startFrom cannot be after endFrom' }); setErrorMessage({
message: '"Start date from" cannot be after "end date from"',
});
return; return;
} }
if (isTrueComparison(startTill, '>', endTill)) { if (isTrueComparison(startToSeconds, '>', endToSeconds)) {
setErrorMessage({ message: 'startTill cannot be after endTill' }); setErrorMessage({
message: '"Start date to" cannot be after "end date to"',
});
return; return;
} }
if (startFrom) { if (startFromSeconds) {
queryParamString += `&start_from=${startFrom}`; queryParamString += `&start_from=${startFromSeconds}`;
} }
if (startTill) { if (startToSeconds) {
queryParamString += `&start_till=${startTill}`; queryParamString += `&start_to=${startToSeconds}`;
} }
if (endFrom) { if (endFromSeconds) {
queryParamString += `&end_from=${endFrom}`; queryParamString += `&end_from=${endFromSeconds}`;
} }
if (endTill) { if (endToSeconds) {
queryParamString += `&end_till=${endTill}`; queryParamString += `&end_to=${endToSeconds}`;
} }
if (processStatusSelection.length > 0) { if (processStatusSelection.length > 0) {
const processStatusSelectionString = processStatusSelection.map( queryParamString += `&process_status=${processStatusSelection}`;
(pss: any) => {
return pss.label;
}
);
queryParamString += `&process_status=${processStatusSelectionString}`;
} }
if (processModelSelection.length > 0) {
const currentProcessModel: any = processModelSelection[0]; if (processModelSelection) {
queryParamString += `&process_group_identifier=${currentProcessModel.process_group_id}&process_model_identifier=${currentProcessModel.id}`; queryParamString += `&process_group_identifier=${processModelSelection.process_group_id}&process_model_identifier=${processModelSelection.id}`;
} }
setErrorMessage(null); setErrorMessage(null);
@ -227,30 +254,22 @@ export default function ProcessInstanceList() {
initialDate: any, initialDate: any,
onChangeFunction: any onChangeFunction: any
) => { ) => {
let selectedDate = null;
if (initialDate) {
selectedDate = new Date(initialDate * 1000);
}
return ( return (
<Form.Group> <DatePicker dateFormat={DATE_FORMAT_CARBON} datePickerType="single">
<InputGroup> <DatePickerInput
<Stack className="ms-auto" direction="horizontal" gap={3}> id={`date-picker-${name}`}
<InputGroup.Text className="text-nowrap"> placeholder={DATE_FORMAT}
{labelString} labelText={labelString}
{'\u00A0'} type="text"
</InputGroup.Text> size="md"
<DatePicker autocomplete="off"
id={`date-picker-${name}`} allowInput={false}
selected={selectedDate} onChange={(dateChangeEvent: any) => {
onChange={(date: any) => onChangeFunction(dateChangeEvent.srcElement.value);
convertDateToSeconds(date, onChangeFunction) }}
} value={initialDate}
showTimeSelect />
dateFormat={DATE_FORMAT} </DatePicker>
/>
</Stack>
</InputGroup>
</Form.Group>
); );
}; };
@ -273,96 +292,88 @@ export default function ProcessInstanceList() {
return queryParamString; return queryParamString;
}; };
const processModelSearch = () => { const processStatusSearch = () => {
return ( return (
<Form.Group> <MultiSelect
<InputGroup> label="Choose Status"
<InputGroup.Text className="text-nowrap"> id="process-instance-status-select"
Process Model:{' '} titleText="Status"
</InputGroup.Text> items={processStatusAllOptions}
<Typeahead onChange={(selection: any) => {
style={{ width: 500 }} setProcessStatusSelection(selection.selectedItems);
id="process-model-selection" }}
labelKey="label" itemToString={(item: any) => {
onChange={setProcessModelSelection} return item || '';
options={processModeleSelectionOptions} }}
placeholder="Choose a process model..." selectionFeedback="top-after-reopen"
selected={processModelSelection} selectedItems={processStatusSelection}
/> />
</InputGroup>
</Form.Group>
); );
}; };
const processStatusSearch = () => { const clearFilters = () => {
return ( setProcessModelSelection(null);
<Form.Group> setProcessStatusSelection([]);
<InputGroup> setStartFrom('');
<InputGroup.Text className="text-nowrap"> setStartTo('');
Process Status:{' '} setEndFrom('');
</InputGroup.Text> setEndTo('');
<Typeahead
multiple
style={{ width: 500 }}
id="process-status-selection"
// for cypress tests since data-qa does not work
inputProps={{
name: 'process-status-selection',
}}
labelKey="label"
onChange={setProcessStatusSelection}
options={processStatuseSelectionOptions}
placeholder="Choose process statuses..."
selected={processStatusSelection}
/>
</InputGroup>
</Form.Group>
);
}; };
const filterOptions = () => { const filterOptions = () => {
if (!showFilterOptions) {
return null;
}
return ( return (
<div className="container"> <>
<div className="row"> <Grid fullWidth className="with-bottom-margin">
<div className="col"> <Column md={8}>
<form onSubmit={handleFilter}> <ProcessModelSearch
<Stack direction="horizontal" gap={3}> onChange={(selection: any) =>
{processModelSearch()} setProcessModelSelection(selection.selectedItem)
</Stack> }
<br /> processModels={processModelAvailableItems}
<Stack direction="horizontal" gap={3}> selectedItem={processModelSelection}
{dateComponent( />
'Start Range: ', </Column>
'start-from', <Column md={8}>{processStatusSearch()}</Column>
startFrom, </Grid>
setStartFrom <Grid fullWidth className="with-bottom-margin">
)} <Column md={4}>
{dateComponent('-', 'start-till', startTill, setStartTill)} {dateComponent(
</Stack> 'Start date from',
<br /> 'start-from',
<Stack direction="horizontal" gap={3}> startFrom,
{dateComponent( setStartFrom
'End Range: \u00A0\u00A0', )}
'end-from', </Column>
endFrom, <Column md={4}>
setEndFrom {dateComponent('Start date to', 'start-to', startTo, setStartTo)}
)} </Column>
{dateComponent('-', 'end-till', endTill, setEndTill)} <Column md={4}>
</Stack> {dateComponent('End date from', 'end-from', endFrom, setEndFrom)}
<br /> </Column>
<Stack direction="horizontal" gap={3}> <Column md={4}>
{processStatusSearch()} {dateComponent('End date to', 'end-to', endTo, setEndTo)}
</Stack> </Column>
<Stack direction="horizontal" gap={3}> </Grid>
<Button className="ms-auto" variant="secondary" type="submit"> <Grid fullWidth className="with-bottom-margin">
Filter <Column md={4}>
</Button> <ButtonSet>
</Stack> <Button
</form> kind=""
</div> className="button-white-background"
<div className="col" /> onClick={clearFilters}
</div> >
</div> Clear
</Button>
<Button kind="secondary" onClick={applyFilter}>
Filter
</Button>
</ButtonSet>
</Column>
</Grid>
</>
); );
}; };
@ -404,7 +415,7 @@ export default function ProcessInstanceList() {
); );
}); });
return ( return (
<Table striped bordered> <Table size="lg">
<thead> <thead>
<tr> <tr>
<th>Process Instance Id</th> <th>Process Instance Id</th>
@ -436,20 +447,42 @@ export default function ProcessInstanceList() {
); );
}; };
const toggleShowFilterOptions = () => {
setShowFilterOptions(!showFilterOptions);
};
if (pagination) { if (pagination) {
const { page, perPage } = getPageInfoFromSearchParams(searchParams); const { page, perPage } = getPageInfoFromSearchParams(searchParams);
return ( return (
<> <>
{processInstanceTitleElement()} {processInstanceTitleElement()}
<Grid fullWidth>
<Column lg={15} />
<Column lg={1}>
<Button
kind="ghost"
renderIcon={Filter}
iconDescription="Filter Options"
hasIconOnly
size="lg"
onClick={toggleShowFilterOptions}
/>
</Column>
</Grid>
{filterOptions()} {filterOptions()}
<PaginationForTable <br />
page={page} <Grid fullWidth>
perPage={perPage} <Column lg={16}>
pagination={pagination} <PaginationForTable
tableToDisplay={buildTable()} page={page}
queryParamString={getSearchParamsAsQueryString()} perPage={perPage}
path="/admin/process-instances" pagination={pagination}
/> tableToDisplay={buildTable()}
queryParamString={getSearchParamsAsQueryString()}
path="/admin/process-instances"
/>
</Column>
</Grid>
</> </>
); );
} }

View File

@ -1,5 +1,6 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Table } from 'react-bootstrap'; // @ts-ignore
import { Table } from '@carbon/react';
import { useParams, useSearchParams, Link } from 'react-router-dom'; import { useParams, useSearchParams, Link } from 'react-router-dom';
import PaginationForTable from '../components/PaginationForTable'; import PaginationForTable from '../components/PaginationForTable';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb';

View File

@ -1,5 +1,6 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Button, Table } from 'react-bootstrap'; // @ts-ignore
import { Button, Table } from '@carbon/react';
import { useParams, Link } from 'react-router-dom'; import { useParams, Link } from 'react-router-dom';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';

View File

@ -1,7 +1,8 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useParams, useSearchParams } from 'react-router-dom'; import { useParams, useSearchParams } from 'react-router-dom';
import { Button, Table } from 'react-bootstrap'; // @ts-ignore
import { Button, Table } from '@carbon/react';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import PaginationForTable from '../components/PaginationForTable'; import PaginationForTable from '../components/PaginationForTable';

View File

@ -1,7 +1,8 @@
import { useContext, useEffect, useState } from 'react'; import { useContext, useEffect, useState } from 'react';
import Editor from '@monaco-editor/react'; import Editor from '@monaco-editor/react';
import { useParams, useNavigate, Link } from 'react-router-dom'; import { useParams, useNavigate, Link } from 'react-router-dom';
import { Button, Modal, Stack } from 'react-bootstrap'; // @ts-ignore
import { Button, Modal, Stack } from '@carbon/react';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
import ReactDiagramEditor from '../components/ReactDiagramEditor'; import ReactDiagramEditor from '../components/ReactDiagramEditor';
@ -408,15 +409,15 @@ export default function ProcessInstanceShow() {
const taskToUse: any = { ...taskToDisplay, data: taskDataToDisplay }; const taskToUse: any = { ...taskToDisplay, data: taskDataToDisplay };
if (taskToDisplay) { if (taskToDisplay) {
return ( return (
<Modal show={!!taskToUse} onHide={handleTaskDataDisplayClose}> <Modal
<Modal.Header closeButton> open={!!taskToUse}
<Modal.Title> passiveModal
<Stack direction="horizontal" gap={2}> onRequestClose={handleTaskDataDisplayClose}
{taskToUse.name} ({taskToUse.type}): {taskToUse.state} >
{taskDataButtons(taskToUse)} <Stack orientation="horizontal" gap={2}>
</Stack> {taskToUse.name} ({taskToUse.type}): {taskToUse.state}
</Modal.Title> {taskDataButtons(taskToUse)}
</Modal.Header> </Stack>
{taskDataContainer()} {taskDataContainer()}
</Modal> </Modal>
); );
@ -435,7 +436,7 @@ export default function ProcessInstanceShow() {
processGroupId={params.process_group_id} processGroupId={params.process_group_id}
linkProcessModel linkProcessModel
/> />
<Stack direction="horizontal" gap={3}> <Stack orientation="horizontal" gap={3}>
<h2>Process Instance Id: {processInstanceToUse.id}</h2> <h2>Process Instance Id: {processInstanceToUse.id}</h2>
<ButtonWithConfirmation <ButtonWithConfirmation
description="Delete Process Instance?" description="Delete Process Instance?"

View File

@ -1,6 +1,7 @@
import { useState, useEffect, useContext } from 'react'; import { useState, useEffect, useContext } from 'react';
import { useParams, useNavigate } from 'react-router-dom'; import { useParams, useNavigate } from 'react-router-dom';
import { Button, Stack } from 'react-bootstrap'; // @ts-ignore
import { Button, Stack } from '@carbon/react';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; import ButtonWithConfirmation from '../components/ButtonWithConfirmation';
@ -79,7 +80,7 @@ export default function ProcessModelEdit() {
/> />
<br /> <br />
<br /> <br />
<Stack direction="horizontal" gap={3}> <Stack orientation="horizontal" gap={3}>
<Button type="submit">Submit</Button> <Button type="submit">Submit</Button>
<Button variant="secondary" href={`/admin/${processModelPath}`}> <Button variant="secondary" href={`/admin/${processModelPath}`}>
Cancel Cancel

View File

@ -1,7 +1,8 @@
import { useContext, useEffect, useRef, useState } from 'react'; import { useContext, useEffect, useRef, useState } from 'react';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import { Button, Modal, Stack } from 'react-bootstrap'; // @ts-ignore
import Container from 'react-bootstrap/Container'; import { Button, Modal, Stack, Content } from '@carbon/react';
// import Container from 'react-bootstrap/Container';
import Row from 'react-bootstrap/Row'; import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col'; import Col from 'react-bootstrap/Col';
@ -193,31 +194,25 @@ export default function ProcessModelEditDiagram() {
const newFileNameBox = () => { const newFileNameBox = () => {
const fileExtension = `.${searchParams.get('file_type')}`; const fileExtension = `.${searchParams.get('file_type')}`;
return ( return (
<Modal show={showFileNameEditor} onHide={handleFileNameCancel}> <Modal
<Modal.Header closeButton> open={showFileNameEditor}
<Modal.Title>Process Model File Name</Modal.Title> modalHeading="Processs Model File Name"
</Modal.Header> primaryButtonText="Save Changes"
<form onSubmit={handleFileNameSave}> secondaryButtonText="Cancel"
<label>File Name:</label> onSecondarySubmit={handleFileNameCancel}
<span> onRequestSubmit={handleFileNameSave}
<input >
name="file_name" <label>File Name:</label>
type="text" <span>
value={newFileName} <input
onChange={(e) => setNewFileName(e.target.value)} name="file_name"
autoFocus type="text"
/> value={newFileName}
{fileExtension} onChange={(e) => setNewFileName(e.target.value)}
</span> autoFocus
<Modal.Footer> />
<Button variant="secondary" onClick={handleFileNameCancel}> {fileExtension}
Cancel </span>
</Button>
<Button variant="primary" type="submit">
Save Changes
</Button>
</Modal.Footer>
</form>
</Modal> </Modal>
); );
}; };
@ -288,7 +283,6 @@ export default function ProcessModelEditDiagram() {
// we should update this to act like updating scripts // we should update this to act like updating scripts
// where we pass an event to bpmn-js // where we pass an event to bpmn-js
setScriptModeling(modeling); setScriptModeling(modeling);
setScriptText(script || ''); setScriptText(script || '');
setScriptType(scriptTypeString); setScriptType(scriptTypeString);
setScriptEventBus(eventBus); setScriptEventBus(eventBus);
@ -478,7 +472,7 @@ export default function ProcessModelEditDiagram() {
} }
return ( return (
<main> <main>
<Container> <Content>
<Row> <Row>
<Col xs={8}> <Col xs={8}>
<Button variant="link" disabled style={{ fontSize: '1.5em' }}> <Button variant="link" disabled style={{ fontSize: '1.5em' }}>
@ -519,11 +513,11 @@ export default function ProcessModelEditDiagram() {
</Col> </Col>
<Col xs={1}>{scriptUnitTestResultBoolElement}</Col> <Col xs={1}>{scriptUnitTestResultBoolElement}</Col>
</Row> </Row>
</Container> </Content>
<Stack direction="horizontal" gap={3}> <Stack orientation="horizontal" gap={3}>
{unitTestFailureElement()} {unitTestFailureElement()}
</Stack> </Stack>
<Stack direction="horizontal" gap={3}> <Stack orientation="horizontal" gap={3}>
<Stack> <Stack>
<div>Input Json:</div> <div>Input Json:</div>
<div> <div>
@ -564,28 +558,25 @@ export default function ProcessModelEditDiagram() {
if (scriptElement) { if (scriptElement) {
scriptName = (scriptElement as any).di.bpmnElement.name; scriptName = (scriptElement as any).di.bpmnElement.name;
} }
return ( return (
<Modal size="xl" show={showScriptEditor} onHide={handleScriptEditorClose}> <Modal
<Modal.Header closeButton> open={showScriptEditor}
<Modal.Title>Editing Script: {scriptName}</Modal.Title> modalHeading={`Editing Script: ${scriptName}`}
</Modal.Header> primaryButtonText="Close"
<Modal.Body> onRequestSubmit={handleScriptEditorClose}
<Editor size="lg"
height={500} >
width="auto" <Editor
options={generalEditorOptions()} height={500}
defaultLanguage="python" width="auto"
defaultValue={scriptText} options={generalEditorOptions()}
onChange={handleEditorScriptChange} defaultLanguage="python"
onMount={handleEditorDidMount} value={scriptText}
/> onChange={handleEditorScriptChange}
{scriptUnitTestEditorElement()} onMount={handleEditorDidMount}
</Modal.Body> />
<Modal.Footer> {scriptUnitTestEditorElement()}
<Button variant="secondary" onClick={handleScriptEditorClose}>
Close
</Button>
</Modal.Footer>
</Modal> </Modal>
); );
}; };

View File

@ -1,6 +1,7 @@
import { useContext, useEffect, useState } from 'react'; import { useContext, useEffect, useState } from 'react';
import { Link, useParams } from 'react-router-dom'; import { Link, useParams } from 'react-router-dom';
import { Button, Stack } from 'react-bootstrap'; // @ts-ignore
import { Button, Stack } from '@carbon/react';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import FileInput from '../components/FileInput'; import FileInput from '../components/FileInput';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
@ -210,7 +211,7 @@ export default function ProcessModelShow() {
const processModelButtons = () => { const processModelButtons = () => {
return ( return (
<Stack direction="horizontal" gap={3}> <Stack orientation="horizontal" gap={3}>
<Button onClick={processInstanceCreateAndRun} variant="primary"> <Button onClick={processInstanceCreateAndRun} variant="primary">
Run Run
</Button> </Button>

View File

@ -1,7 +1,8 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import Editor from '@monaco-editor/react'; import Editor from '@monaco-editor/react';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import { Button, Modal } from 'react-bootstrap'; // @ts-ignore
import { Button, Modal } from '@carbon/react';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; import ButtonWithConfirmation from '../components/ButtonWithConfirmation';
@ -118,31 +119,25 @@ export default function ReactFormEditor() {
const newFileNameBox = () => { const newFileNameBox = () => {
return ( return (
<Modal show={showFileNameEditor} onHide={handleFileNameCancel}> <Modal
<Modal.Header closeButton> open={showFileNameEditor}
<Modal.Title>Process Model File Name</Modal.Title> modalHeading="Processs Model File Name"
</Modal.Header> primaryButtonText="Save Changes"
<form onSubmit={handleFileNameSave}> secondaryButtonText="Cancel"
<label>File Name:</label> onSecondarySubmit={handleFileNameCancel}
<span> onRequestSubmit={handleFileNameSave}
<input >
name="file_name" <label>File Name:</label>
type="text" <span>
value={newFileName} <input
onChange={(e) => setNewFileName(e.target.value)} name="file_name"
autoFocus type="text"
/> value={newFileName}
.{fileExtension} onChange={(e) => setNewFileName(e.target.value)}
</span> autoFocus
<Modal.Footer> />
<Button variant="secondary" onClick={handleFileNameCancel}> {fileExtension}
Cancel </span>
</Button>
<Button variant="primary" type="submit">
Save Changes
</Button>
</Modal.Footer>
</form>
</Modal> </Modal>
); );
}; };
@ -160,7 +155,7 @@ export default function ReactFormEditor() {
{processModelFile ? `: ${(processModelFile as any).name}` : ''} {processModelFile ? `: ${(processModelFile as any).name}` : ''}
</h2> </h2>
{newFileNameBox()} {newFileNameBox()}
<Button onClick={saveFile} variant="danger"> <Button onClick={saveFile} variant="danger" data-qa="file-save-button">
Save Save
</Button> </Button>
{params.file_name ? ( {params.file_name ? (

View File

@ -1,6 +1,7 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Link, useSearchParams } from 'react-router-dom'; import { Link, useSearchParams } from 'react-router-dom';
import { Button, Table } from 'react-bootstrap'; // @ts-ignore
import { Button, Table } from '@carbon/react';
import { MdDelete } from 'react-icons/md'; import { MdDelete } from 'react-icons/md';
import PaginationForTable from '../components/PaginationForTable'; import PaginationForTable from '../components/PaginationForTable';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';

View File

@ -1,6 +1,7 @@
import { useState } from 'react'; import { useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { Stack } from 'react-bootstrap'; // @ts-ignore
import { Stack } from '@carbon/react';
import Button from 'react-bootstrap/Button'; import Button from 'react-bootstrap/Button';
import Form from 'react-bootstrap/Form'; import Form from 'react-bootstrap/Form';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
@ -67,7 +68,7 @@ export default function SecretNew() {
}} }}
/> />
</Form.Group> </Form.Group>
<Stack direction="horizontal" gap={3}> <Stack orientation="horizontal" gap={3}>
<Button variant="primary" type="submit"> <Button variant="primary" type="submit">
Submit Submit
</Button> </Button>

View File

@ -1,6 +1,7 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useParams, useNavigate } from 'react-router-dom'; import { useParams, useNavigate } from 'react-router-dom';
import { Stack, Table, Button } from 'react-bootstrap'; // @ts-ignore
import { Stack, Table, Button } from '@carbon/react';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
import { Secret } from '../interfaces'; import { Secret } from '../interfaces';
import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; import ButtonWithConfirmation from '../components/ButtonWithConfirmation';
@ -64,8 +65,8 @@ export default function SecretShow() {
if (secret) { if (secret) {
return ( return (
<> <>
<Stack direction="horizontal" gap={3}> <h2>Secret Key: {secret.key}</h2>
<h2>Secret Key: {secret.key}</h2> <Stack orientation="horizontal" gap={3}>
<ButtonWithConfirmation <ButtonWithConfirmation
description="Delete Secret?" description="Delete Secret?"
onConfirmation={deleteSecret} onConfirmation={deleteSecret}

View File

@ -1,7 +1,8 @@
import { useContext, useEffect, useState } from 'react'; import { useContext, useEffect, useState } from 'react';
import { Link, useNavigate, useParams } from 'react-router-dom'; import { Link, useNavigate, useParams } from 'react-router-dom';
import Form from '@rjsf/core'; import Form from '@rjsf/core';
import { Button, Stack } from 'react-bootstrap'; // @ts-ignore
import { Button, Stack } from '@carbon/react';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm'; import remarkGfm from 'remark-gfm';
@ -84,7 +85,7 @@ export default function TaskShow() {
}); });
} }
return ( return (
<Stack direction="horizontal" gap={3}> <Stack orientation="horizontal" gap={3}>
<Button href="/tasks">Go Back To List</Button> <Button href="/tasks">Go Back To List</Button>
{userTasksElement} {userTasksElement}
</Stack> </Stack>