Merge branch 'main' into feature/nested-groups-2

# Conflicts:
#	spiffworkflow-frontend/src/routes/ProcessGroupShow.tsx
This commit is contained in:
mike cullerton 2022-11-10 16:50:11 -05:00
commit 4cc5845971
38 changed files with 697 additions and 305 deletions

251
.github/workflows/backend_tests.yml vendored Normal file
View File

@ -0,0 +1,251 @@
name: Tests
on:
- push
- pull_request
jobs:
tests:
name: ${{ matrix.session }} ${{ matrix.python }} / ${{ matrix.os }} ${{ matrix.database }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- { python: "3.11", os: "ubuntu-latest", session: "pre-commit" }
- { python: "3.11", os: "ubuntu-latest", session: "safety" }
- { python: "3.11", os: "ubuntu-latest", session: "mypy" }
- {
python: "3.11",
os: "ubuntu-latest",
session: "tests",
database: "mysql",
}
- {
python: "3.11",
os: "ubuntu-latest",
session: "tests",
database: "postgres",
}
- {
python: "3.11",
os: "ubuntu-latest",
session: "tests",
database: "sqlite",
}
- {
python: "3.11",
os: "macos-latest",
session: "tests",
database: "sqlite",
}
- {
# typeguard 2.13.3 is broken with TypeDict in 3.11.
# probably the next release fixes it.
# https://github.com/agronholm/typeguard/issues/242
python: "3.11",
os: "ubuntu-latest",
session: "typeguard",
database: "sqlite",
}
- { python: "3.11", os: "ubuntu-latest", session: "xdoctest" }
- { python: "3.11", os: "ubuntu-latest", session: "docs-build" }
env:
NOXSESSION: ${{ matrix.session }}
SPIFF_DATABASE_TYPE: ${{ matrix.database }}
FORCE_COLOR: "1"
PRE_COMMIT_COLOR: "always"
DB_PASSWORD: password
FLASK_SESSION_SECRET_KEY: super_secret_key
defaults:
run:
working-directory: spiffworkflow-backend
steps:
- name: Check out the repository
uses: actions/checkout@v3.0.2
- name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v4.2.0
with:
python-version: ${{ matrix.python }}
- name: Upgrade pip
run: |
pip install --constraint=.github/workflows/constraints.txt pip
pip --version
- name: Upgrade pip in virtual environments
shell: python
run: |
import os
import pip
with open(os.environ["GITHUB_ENV"], mode="a") as io:
print(f"VIRTUALENV_PIP={pip.__version__}", file=io)
- name: Install Poetry
run: |
pipx install --pip-args=--constraint=.github/workflows/constraints.txt poetry
poetry --version
- name: Install Nox
run: |
pipx install --pip-args=--constraint=.github/workflows/constraints.txt nox
pipx inject --pip-args=--constraint=.github/workflows/constraints.txt nox nox-poetry
nox --version
- name: Compute pre-commit cache key
if: matrix.session == 'pre-commit'
id: pre-commit-cache
shell: python
run: |
import hashlib
import sys
python = "py{}.{}".format(*sys.version_info[:2])
payload = sys.version.encode() + sys.executable.encode()
digest = hashlib.sha256(payload).hexdigest()
result = "${{ runner.os }}-{}-{}-pre-commit".format(python, digest[:8])
print("::set-output name=result::{}".format(result))
- name: Restore pre-commit cache
uses: actions/cache@v3.0.11
if: matrix.session == 'pre-commit'
with:
path: ~/.cache/pre-commit
key: ${{ steps.pre-commit-cache.outputs.result }}-${{ hashFiles('.pre-commit-config.yaml') }}
restore-keys: |
${{ steps.pre-commit-cache.outputs.result }}-
- name: Setup Mysql
uses: mirromutth/mysql-action@v1.1
with:
host port: 3306
container port: 3306
mysql version: "8.0"
mysql database: "spiffworkflow_backend_testing"
mysql root password: password
if: matrix.database == 'mysql'
- name: Setup Postgres
run: docker run --name postgres-spiff -p 5432:5432 -e POSTGRES_PASSWORD=spiffworkflow_backend -e POSTGRES_USER=spiffworkflow_backend -e POSTGRES_DB=spiffworkflow_backend_testing -d postgres
if: matrix.database == 'postgres'
- name: Run Nox
run: |
nox --force-color --python=${{ matrix.python }}
- name: Upload coverage data
# pin to upload coverage from only one matrix entry, otherwise coverage gets confused later
if: always() && matrix.session == 'tests' && matrix.python == '3.11' && matrix.os == 'ubuntu-latest'
uses: "actions/upload-artifact@v3.0.0"
with:
name: coverage-data
path: ".coverage.*"
- name: Upload documentation
if: matrix.session == 'docs-build'
uses: actions/upload-artifact@v3.0.0
with:
name: docs
path: docs/_build
- name: Upload logs
if: failure() && matrix.session == 'tests'
uses: "actions/upload-artifact@v3.0.0"
with:
name: logs-${{matrix.python}}-${{matrix.os}}-${{matrix.database}}
path: "./log/*.log"
check_docker_start_script:
runs-on: ubuntu-latest
steps:
- name: Check out the repository
uses: actions/checkout@v3.0.2
with:
# Disabling shallow clone is recommended for improving relevancy of reporting in sonarcloud
fetch-depth: 0
- name: start_backend
run: ./bin/build_and_run_with_docker_compose
timeout-minutes: 20
env:
SPIFFWORKFLOW_BACKEND_LOAD_FIXTURE_DATA: "true"
- name: wait_for_backend
run: ./bin/wait_for_server_to_be_up 5
coverage:
runs-on: ubuntu-latest
needs: tests
steps:
- name: Check out the repository
uses: actions/checkout@v3.0.2
with:
# Disabling shallow clone is recommended for improving relevancy of reporting in sonarcloud
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v4.2.0
with:
python-version: "3.11"
- name: Upgrade pip
run: |
pip install --constraint=.github/workflows/constraints.txt pip
pip --version
- name: Install Poetry
run: |
pipx install --pip-args=--constraint=.github/workflows/constraints.txt poetry
poetry --version
- name: Install Nox
run: |
pipx install --pip-args=--constraint=.github/workflows/constraints.txt nox
pipx inject --pip-args=--constraint=.github/workflows/constraints.txt nox nox-poetry
nox --version
- name: Download coverage data
uses: actions/download-artifact@v3.0.1
with:
name: coverage-data
- name: Combine coverage data and display human readable report
run: |
find . -name \*.pyc -delete
nox --force-color --session=coverage
- name: Create coverage report
run: |
nox --force-color --session=coverage -- xml
- name: Upload coverage report
uses: codecov/codecov-action@v3.1.0
- name: SonarCloud Scan
uses: sonarsource/sonarcloud-github-action@master
# thought about just skipping dependabot
# if: ${{ github.actor != 'dependabot[bot]' }}
# but figured all pull requests seems better, since none of them will have access to sonarcloud.
# however, with just skipping pull requests, the build associated with "Triggered via push" is also associated with the pull request and also fails hitting sonarcloud
# if: ${{ github.event_name != 'pull_request' }}
# so just skip everything but main
if: github.ref_name == 'main'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
# part about saving PR number and then using it from auto-merge-dependabot-prs from:
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run
- name: Save PR number
if: ${{ github.event_name == 'pull_request' }}
env:
PR_NUMBER: ${{ github.event.number }}
run: |
mkdir -p ./pr
echo "$PR_NUMBER" > ./pr/pr_number
- uses: actions/upload-artifact@v3
with:
name: pr_number
path: pr/

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
pyrightconfig.json

View File

@ -413,7 +413,7 @@ paths:
- name: process_status - name: process_status
in: query in: query
required: false required: false
description: For filtering - not_started, user_input_required, waiting, complete, faulted, or suspended description: For filtering - not_started, user_input_required, waiting, complete, error, or suspended
schema: schema:
type: string type: string
# process_instance_list # process_instance_list

View File

@ -60,10 +60,9 @@ class ProcessInstanceStatus(SpiffEnum):
user_input_required = "user_input_required" user_input_required = "user_input_required"
waiting = "waiting" waiting = "waiting"
complete = "complete" complete = "complete"
faulted = "faulted" error = "error"
suspended = "suspended" suspended = "suspended"
terminated = "terminated" terminated = "terminated"
erroring = "erroring"
class ProcessInstanceModel(SpiffworkflowBaseDBModel): class ProcessInstanceModel(SpiffworkflowBaseDBModel):

View File

@ -82,10 +82,6 @@ class ProcessInstanceReportModel(SpiffworkflowBaseDBModel):
report_metadata = { report_metadata = {
"columns": [ "columns": [
{"Header": "id", "accessor": "id"}, {"Header": "id", "accessor": "id"},
{
"Header": "process_group_identifier",
"accessor": "process_group_identifier",
},
{ {
"Header": "process_model_identifier", "Header": "process_model_identifier",
"accessor": "process_model_identifier", "accessor": "process_model_identifier",

View File

@ -23,7 +23,7 @@ def load_acceptance_test_fixtures() -> list[ProcessInstanceModel]:
# user_input_required - 2 hours ago # user_input_required - 2 hours ago
# waiting - 3 hourse ago # waiting - 3 hourse ago
# complete - 4 hours ago # complete - 4 hours ago
# faulted - 5 hours ago # error - 5 hours ago
# suspended - 6 hours ago # suspended - 6 hours ago
process_instances = [] process_instances = []
for i in range(len(statuses)): for i in range(len(statuses)):

View File

@ -45,7 +45,7 @@ class ErrorHandlingService:
# fault is the default # fault is the default
self.set_instance_status( self.set_instance_status(
_processor.process_instance_model.id, _processor.process_instance_model.id,
ProcessInstanceStatus.faulted.value, ProcessInstanceStatus.error.value,
) )
if len(process_model.exception_notification_addresses) > 0: if len(process_model.exception_notification_addresses) > 0:

View File

@ -63,7 +63,7 @@ class ProcessInstanceService:
processor.do_engine_steps(save=True) processor.do_engine_steps(save=True)
except Exception as e: except Exception as e:
db.session.rollback() # in case the above left the database with a bad transaction db.session.rollback() # in case the above left the database with a bad transaction
process_instance.status = ProcessInstanceStatus.erroring.value process_instance.status = ProcessInstanceStatus.error.value
db.session.add(process_instance) db.session.add(process_instance)
db.session.commit() db.session.commit()
error_message = ( error_message = (

View File

@ -1715,7 +1715,7 @@ class TestProcessApi(BaseTest):
.first() .first()
) )
assert process is not None assert process is not None
assert process.status == "faulted" assert process.status == "error"
def test_error_handler_suspend( def test_error_handler_suspend(
self, self,
@ -1826,7 +1826,7 @@ class TestProcessApi(BaseTest):
.first() .first()
) )
assert process is not None assert process is not None
assert process.status == "faulted" assert process.status == "error"
def test_process_model_file_create( def test_process_model_file_create(
self, self,

View File

@ -7485,7 +7485,7 @@
}, },
"node_modules/bpmn-js-spiffworkflow": { "node_modules/bpmn-js-spiffworkflow": {
"version": "0.0.8", "version": "0.0.8",
"resolved": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#24c2cc36067adf8fed75990c6bf4a1a67bc9122b", "resolved": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#09fa713bb0bb1b9d4f97684afc46bc3711e11770",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"inherits": "^2.0.4", "inherits": "^2.0.4",
@ -35755,7 +35755,7 @@
} }
}, },
"bpmn-js-spiffworkflow": { "bpmn-js-spiffworkflow": {
"version": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#24c2cc36067adf8fed75990c6bf4a1a67bc9122b", "version": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#09fa713bb0bb1b9d4f97684afc46bc3711e11770",
"from": "bpmn-js-spiffworkflow@sartography/bpmn-js-spiffworkflow#main", "from": "bpmn-js-spiffworkflow@sartography/bpmn-js-spiffworkflow#main",
"requires": { "requires": {
"inherits": "^2.0.4", "inherits": "^2.0.4",

View File

@ -13,7 +13,7 @@ type OwnProps = {
const explodeCrumb = (crumb: HotCrumbItem) => { const explodeCrumb = (crumb: HotCrumbItem) => {
const url: string = crumb[1] || ''; const url: string = crumb[1] || '';
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_unused, processModelId, link] = url.split(':'); const [endingUrlType, processModelId, link] = url.split(':');
const processModelIdSegments = splitProcessModelId(processModelId); const processModelIdSegments = splitProcessModelId(processModelId);
const paths: string[] = []; const paths: string[] = [];
const lastPathItem = processModelIdSegments.pop(); const lastPathItem = processModelIdSegments.pop();
@ -29,7 +29,13 @@ const explodeCrumb = (crumb: HotCrumbItem) => {
} }
); );
if (link === 'link') { if (link === 'link') {
const lastUrl = `/admin/process-models/${paths.join(':')}:${lastPathItem}`; if (lastPathItem !== undefined) {
paths.push(lastPathItem);
}
// process_model to process-models
const lastUrl = `/admin/${endingUrlType
.replace('_', '-')
.replace(/s*$/, 's')}/${paths.join(':')}`;
breadcrumbItems.push( breadcrumbItems.push(
<BreadcrumbItem key={lastPathItem} href={lastUrl}> <BreadcrumbItem key={lastPathItem} href={lastUrl}>
{lastPathItem} {lastPathItem}
@ -64,7 +70,7 @@ export default function ProcessBreadcrumb({
</BreadcrumbItem> </BreadcrumbItem>
); );
} }
if (url && url.startsWith('process_model:')) { if (url && url.match(/^process[_-](model|group)s?:/)) {
return explodeCrumb(crumb); return explodeCrumb(crumb);
} }
return ( return (

View File

@ -79,12 +79,9 @@ export default function ProcessGroupForm({
description: processGroup.description, description: processGroup.description,
}; };
if (mode === 'new') { if (mode === 'new') {
console.log(`parentGroupId: ${parentGroupId}`);
console.log(`processGroup.id: ${processGroup.id}`);
if (parentGroupId) { if (parentGroupId) {
newProcessGroupId = `${parentGroupId}/${processGroup.id}`; newProcessGroupId = `${parentGroupId}/${processGroup.id}`;
} }
console.log(`newProcessGroupId: ${newProcessGroupId}`);
Object.assign(postBody, { Object.assign(postBody, {
id: parentGroupId id: parentGroupId
? `${parentGroupId}/${processGroup.id}` ? `${parentGroupId}/${processGroup.id}`

View File

@ -229,7 +229,7 @@ export default function ReactDiagramEditor({
diagramModeler.on('spiff.script.edit', (event: any) => { diagramModeler.on('spiff.script.edit', (event: any) => {
const { error, element, scriptType, script, eventBus } = event; const { error, element, scriptType, script, eventBus } = event;
if (error) { if (error) {
console.log(error); console.error(error);
} }
handleLaunchScriptEditor(element, script, scriptType, eventBus); handleLaunchScriptEditor(element, script, scriptType, eventBus);
}); });
@ -237,7 +237,7 @@ export default function ReactDiagramEditor({
diagramModeler.on('spiff.markdown.edit', (event: any) => { diagramModeler.on('spiff.markdown.edit', (event: any) => {
const { error, element, value, eventBus } = event; const { error, element, value, eventBus } = event;
if (error) { if (error) {
console.log(error); console.error(error);
} }
handleLaunchMarkdownEditor(element, value, eventBus); handleLaunchMarkdownEditor(element, value, eventBus);
}); });
@ -318,7 +318,7 @@ export default function ReactDiagramEditor({
} }
function handleError(err: any) { function handleError(err: any) {
console.log('ERROR:', err); console.error('ERROR:', err);
} }
function checkTaskCanBeHighlighted(taskBpmnId: string) { function checkTaskCanBeHighlighted(taskBpmnId: string) {
@ -406,7 +406,6 @@ export default function ReactDiagramEditor({
} }
function fetchDiagramFromURL(urlToUse: any) { function fetchDiagramFromURL(urlToUse: any) {
console.log(`urlToUse: ${urlToUse}`);
fetch(urlToUse) fetch(urlToUse)
.then((response) => response.text()) .then((response) => response.text())
.then((text) => { .then((text) => {

View File

@ -12,10 +12,11 @@ export const PROCESS_STATUSES = [
'user_input_required', 'user_input_required',
'waiting', 'waiting',
'complete', 'complete',
'faulted', 'error',
'suspended', 'suspended',
]; ];
// with time: yyyy-MM-dd HH:mm:ss // with time: yyyy-MM-dd HH:mm:ss
export const DATE_TIME_FORMAT = 'yyyy-MM-dd HH:mm:ss';
export const DATE_FORMAT = 'yyyy-MM-dd'; export const DATE_FORMAT = 'yyyy-MM-dd';
export const DATE_FORMAT_CARBON = 'Y-m-d'; export const DATE_FORMAT_CARBON = 'Y-m-d';

View File

@ -1,5 +1,5 @@
import { format } from 'date-fns'; import { format } from 'date-fns';
import { DATE_FORMAT } from './config'; import { DATE_TIME_FORMAT, DATE_FORMAT } from './config';
import { import {
DEFAULT_PER_PAGE, DEFAULT_PER_PAGE,
DEFAULT_PAGE, DEFAULT_PAGE,
@ -51,6 +51,14 @@ export const convertStringToDate = (dateString: string) => {
return null; return null;
}; };
export const convertSecondsToFormattedDateTime = (seconds: number) => {
if (seconds) {
const dateObject = new Date(seconds * 1000);
return format(dateObject, DATE_TIME_FORMAT);
}
return null;
};
export const convertSecondsToFormattedDate = (seconds: number) => { export const convertSecondsToFormattedDate = (seconds: number) => {
if (seconds) { if (seconds) {
const dateObject = new Date(seconds * 1000); const dateObject = new Date(seconds * 1000);

View File

@ -1,24 +1,79 @@
body { /* site is mainly using white theme. */
margin: 0; /* header is mainly using g100 */
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', /* mockup wanted white, not grey, text */
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', .cds--header, a.cds--header__menu-item {
sans-serif; color: white;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
} }
h1{
height: 36px;
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 400;
font-size: 28px;
line-height: 36px;
color: #161616;
flex: none;
order: 0;
align-self: stretch;
flex-grow: 0;
margin-bottom: 1em
}
.span-tag {
color: black;
}
.cds--btn.button-white-background {
color: #393939;
background: #FFFFFF;
background-blend-mode: multiply;
border: 1px solid #393939;
}
.cds--btn.button-white-background:hover {
background: #525252;
}
.cds--breadcrumb-item a.cds--link:hover {
color: #525252;
}
.cds--breadcrumb-item a.cds--link:visited {
color: #525252;
}
.cds--breadcrumb-item a.cds--link:visited:hover {
color: #525252;
}
.cds--breadcrumb-item a.cds--link {
color: #525252;
}
.cds--btn--ghost {
color: black;
}
.cds--btn--ghost:visited {
color: black;
}
.cds--btn--ghost:hover {
color: black;
}
.cds--btn--ghost:visited:hover {
color: black;
}
.cds--header__global .cds--btn--primary {
background-color: #161616
}
.cds--btn--primary {
background-color: #393939;
}
.cds--btn--primary:hover {
background-color: #474747;
}
code { code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace; monospace;
} }
span.bjs-crumb {
color: #0000ff;
}
.bjs-breadcrumbs li:last-of-type span.bjs-crumb a {
color: black;
}
.app-logo { .app-logo {
height: 85%; height: 85%;
width: 85%; width: 85%;
@ -52,6 +107,26 @@ span.bjs-crumb {
margin-bottom: 2em; margin-bottom: 2em;
} }
h1.with-icons {
margin-top: 5px;
}
.grid-list-title {
font-weight: 600;
font-size: 14px;
line-height: 18px;
color: #161616;
}
.grid-date {
font-size: 14px;
line-height: 18px;
letter-spacing: 0.16px;
color: #525252;
}
.smaller-text {
font-size: 14px;
}
.diagram-editor-canvas { .diagram-editor-canvas {
border:1px solid #000000; border:1px solid #000000;
height:70vh; height:70vh;
@ -59,13 +134,6 @@ 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 { .with-bottom-margin {
margin-bottom: 1em; margin-bottom: 1em;
} }

View File

@ -15,85 +15,3 @@
@use '@carbon/colors'; @use '@carbon/colors';
// @use '@carbon/react/scss/colors'; // @use '@carbon/react/scss/colors';
@use '@carbon/react/scss/themes'; @use '@carbon/react/scss/themes';
// var(--cds-link-text-color, var(--cds-link-primary, #0f62fe))
// site is mainly using white theme.
// header is mainly using g100
// mockup wanted white, not grey, text
.cds--header, a.cds--header__menu-item {
// background-color: colors.$gray-100;
color: white;
}
h1{
height: 36px;
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 400;
font-size: 28px;
line-height: 36px;
color: #161616;
flex: none;
order: 0;
align-self: stretch;
flex-grow: 0;
margin-bottom: 1em
}
.cds--breadcrumb-item a.cds--link:hover {
color: #525252;
}
.cds--breadcrumb-item a.cds--link:visited {
color: #525252;
}
.cds--breadcrumb-item a.cds--link:visited:hover {
color: #525252;
}
.cds--breadcrumb-item a.cds--link {
color: #525252;
}
.cds--btn--ghost {
color: black;
}
.cds--btn--ghost:visited {
color: black;
}
.cds--btn--ghost:hover {
color: black;
}
.cds--btn--ghost:visited:hover {
color: black;
}
$slightly-lighter-gray: #474747;
$spiff-header-background-color: #161616;
.cds--header__global .cds--btn--primary {
background-color: $spiff-header-background-color;
}
.cds--btn--primary {
background-color: #393939;
}
.cds--btn--primary:hover {
background-color: $slightly-lighter-gray;
}
// .cds--btn--ghost:visited {
// color: black;
// }
// .cds--btn--ghost:hover {
// color: black;
// }
// .cds--btn--ghost:visited:hover {
// color: black;
// }
// :root {
// --cds-link-primary: #525252;
// }
// .card {
// background: var(--orange);
// --orange: hsl(255, 72%, var(--lightness));
// }

View File

@ -3,8 +3,8 @@ import * as ReactDOMClient from 'react-dom/client';
import App from './App'; import App from './App';
import 'bootstrap/dist/css/bootstrap.css'; import 'bootstrap/dist/css/bootstrap.css';
import './index.css';
import './index.scss'; import './index.scss';
import './index.css';
import reportWebVitals from './reportWebVitals'; import reportWebVitals from './reportWebVitals';
import UserService from './services/UserService'; import UserService from './services/UserService';

View File

@ -122,7 +122,7 @@ export default function HomePage() {
}); });
return ( return (
<> <>
<h2>Processes I can start</h2> <h1>Processes I can start</h1>
<Table striped bordered> <Table striped bordered>
<thead> <thead>
<tr> <tr>
@ -145,7 +145,7 @@ export default function HomePage() {
); );
return ( return (
<> <>
<h2>Tasks waiting for me</h2> <h1>Tasks waiting for me</h1>
<PaginationForTable <PaginationForTable
page={page} page={page}
perPage={perPage} perPage={perPage}

View File

@ -7,6 +7,8 @@ import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import { import {
convertSecondsToFormattedDate, convertSecondsToFormattedDate,
getPageInfoFromSearchParams, getPageInfoFromSearchParams,
modifyProcessModelPath,
unModifyProcessModelPath,
} from '../helpers'; } from '../helpers';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
@ -44,15 +46,9 @@ export default function MessageInstanceList() {
<td> <td>
<Link <Link
data-qa="process-model-show-link" data-qa="process-model-show-link"
to={`/admin/process-groups/${rowToUse.process_group_identifier}`} to={`/admin/process-models/${modifyProcessModelPath(
> rowToUse.process_model_identifier
{rowToUse.process_group_identifier} )}`}
</Link>
</td>
<td>
<Link
data-qa="process-model-show-link"
to={`/admin/process-models/${rowToUse.process_group_identifier}/${rowToUse.process_model_identifier}`}
> >
{rowToUse.process_model_identifier} {rowToUse.process_model_identifier}
</Link> </Link>
@ -60,7 +56,9 @@ export default function MessageInstanceList() {
<td> <td>
<Link <Link
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/${modifyProcessModelPath(
rowToUse.process_model_identifier
)}/process-instances/${rowToUse.process_instance_id}`}
> >
{rowToUse.process_instance_id} {rowToUse.process_instance_id}
</Link> </Link>
@ -80,7 +78,6 @@ export default function MessageInstanceList() {
<thead> <thead>
<tr> <tr>
<th>Instance Id</th> <th>Instance Id</th>
<th>Process Group</th>
<th>Process Model</th> <th>Process Model</th>
<th>Process Instance</th> <th>Process Instance</th>
<th>Message Model</th> <th>Message Model</th>
@ -107,16 +104,29 @@ export default function MessageInstanceList() {
)}&process_instance_id=${searchParams.get('process_instance_id')}`; )}&process_instance_id=${searchParams.get('process_instance_id')}`;
breadcrumbElement = ( breadcrumbElement = (
<ProcessBreadcrumb <ProcessBreadcrumb
processModelId={searchParams.get('process_model_id') as any} hotCrumbs={[
processGroupId={searchParams.get('process_group_id') as any} ['Process Groups', '/admin'],
linkProcessModel [
`Process Model: ${params.process_model_id}`,
`process_model:${unModifyProcessModelPath(
searchParams.get('process_model_id') || ''
)}:link`,
],
[
`Process Instance: ${searchParams.get('process_instance_id')}`,
`/admin/process-models/${searchParams.get(
'process_model_id'
)}/process-instances/${searchParams.get('process_instance_id')}`,
],
['Messages'],
]}
/> />
); );
} }
return ( return (
<> <>
{breadcrumbElement} {breadcrumbElement}
<h2>Messages</h2> <h1>Messages</h1>
<PaginationForTable <PaginationForTable
page={page} page={page}
perPage={perPage} perPage={perPage}

View File

@ -24,8 +24,16 @@ export default function ProcessGroupEdit() {
if (processGroup) { if (processGroup) {
return ( return (
<> <>
<ProcessBreadcrumb processGroupId={(processGroup as any).id} /> <ProcessBreadcrumb
<h2>Edit Process Group: {(processGroup as any).id}</h2> hotCrumbs={[
['Process Groups', '/admin'],
[
`Process Group: ${processGroup.id}:link`,
`process_group:${processGroup.id}:link`,
],
]}
/>
<h1>Edit Process Group: {(processGroup as any).id}</h1>
<ProcessGroupForm <ProcessGroupForm
mode="edit" mode="edit"
processGroup={processGroup} processGroup={processGroup}

View File

@ -1,19 +1,26 @@
import { useState } from 'react'; import { useState } from 'react';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import ProcessGroupForm from '../components/ProcessGroupForm'; import ProcessGroupForm from '../components/ProcessGroupForm';
import { ProcessGroup } from '../interfaces'; import { ProcessGroup, HotCrumbItem } from '../interfaces';
export default function ProcessGroupNew() { export default function ProcessGroupNew() {
const searchParams = new URLSearchParams(document.location.search);
const parentGroupId = searchParams.get('parentGroupId');
const [processGroup, setProcessGroup] = useState<ProcessGroup>({ const [processGroup, setProcessGroup] = useState<ProcessGroup>({
id: '', id: '',
display_name: '', display_name: '',
description: '', description: '',
}); });
const hotCrumbs: HotCrumbItem[] = [['Process Groups', '/admin']];
if (parentGroupId) {
hotCrumbs.push(['', `process_group:${parentGroupId}:link`]);
}
return ( return (
<> <>
<ProcessBreadcrumb /> <ProcessBreadcrumb hotCrumbs={hotCrumbs} />
<h2>Add Process Group</h2> <h1>Add Process Group</h1>
<ProcessGroupForm <ProcessGroupForm
mode="new" mode="new"
processGroup={processGroup} processGroup={processGroup}

View File

@ -10,7 +10,7 @@ import {
modifyProcessModelPath, modifyProcessModelPath,
unModifyProcessModelPath, unModifyProcessModelPath,
} from '../helpers'; } from '../helpers';
import { ProcessGroup } from '../interfaces'; import { ProcessGroup, ProcessModel } from '../interfaces';
export default function ProcessGroupShow() { export default function ProcessGroupShow() {
const params = useParams(); const params = useParams();
@ -49,19 +49,19 @@ export default function ProcessGroupShow() {
if (processGroup === null) { if (processGroup === null) {
return null; return null;
} }
const rows = processModels.map((row) => { const rows = processModels.map((row: ProcessModel) => {
const modifiedProcessModelId: String = modifyProcessModelPath((row as any).id); const modifiedProcessModelId: String = modifyProcessModelPath((row as any).id);
return ( return (
<tr key={(row as any).id}> <tr key={row.id}>
<td> <td>
<Link <Link
to={`/admin/process-models/${modifiedProcessModelId}`} to={`/admin/process-models/${modifiedProcessModelId}`}
data-qa="process-model-show-link" data-qa="process-model-show-link"
> >
{(row as any).id} {row.id}
</Link> </Link>
</td> </td>
<td>{(row as any).display_name}</td> <td>{row.display_name}</td>
</tr> </tr>
); );
}); });
@ -85,21 +85,19 @@ export default function ProcessGroupShow() {
if (processGroup === null) { if (processGroup === null) {
return null; return null;
} }
const rows = processGroups.map((row) => { const rows = processGroups.map((row: ProcessGroup) => {
const modifiedProcessGroupId: String = modifyProcessModelPath( const modifiedProcessGroupId: String = modifyProcessModelPath(row.id);
(row as any).id
);
return ( return (
<tr key={(row as any).id}> <tr key={row.id}>
<td> <td>
<Link <Link
to={`/admin/process-groups/${modifiedProcessGroupId}`} to={`/admin/process-groups/${modifiedProcessGroupId}`}
data-qa="process-model-show-link" data-qa="process-model-show-link"
> >
{(row as any).id} {row.id}
</Link> </Link>
</td> </td>
<td>{(row as any).display_name}</td> <td>{row.display_name}</td>
</tr> </tr>
); );
}); });
@ -121,15 +119,13 @@ export default function ProcessGroupShow() {
if (processGroup && pagination) { if (processGroup && pagination) {
const { page, perPage } = getPageInfoFromSearchParams(searchParams); const { page, perPage } = getPageInfoFromSearchParams(searchParams);
const modifiedProcessGroupId = modifyProcessModelPath( const modifiedProcessGroupId = modifyProcessModelPath(processGroup.id);
(processGroup as any).id
);
return ( return (
<> <>
<ProcessBreadcrumb <ProcessBreadcrumb
hotCrumbs={[ hotCrumbs={[
['Process Groups', '/admin'], ['Process Groups', '/admin'],
[`Process Group: ${processGroup.display_name}`], ['', `process_group:${processGroup.id}`],
]} ]}
/> />
<ul> <ul>
@ -159,7 +155,7 @@ export default function ProcessGroupShow() {
perPage={perPage} perPage={perPage}
pagination={pagination} pagination={pagination}
tableToDisplay={buildModelTable()} tableToDisplay={buildModelTable()}
path={`/admin/process-groups/${(processGroup as any).id}`} path={`/admin/process-groups/${processGroup.id}`}
/> />
<br /> <br />
<br /> <br />
@ -168,7 +164,7 @@ export default function ProcessGroupShow() {
perPage={perPage} perPage={perPage}
pagination={pagination} pagination={pagination}
tableToDisplay={buildGroupTable()} tableToDisplay={buildGroupTable()}
path={`/admin/process-groups/${(processGroup as any).id}`} path={`/admin/process-groups/${processGroup.id}`}
/> />
</ul> </ul>
</> </>

View File

@ -494,7 +494,7 @@ export default function ProcessInstanceList() {
}; };
const processInstanceTitleElement = () => { const processInstanceTitleElement = () => {
return <h2>Process Instances</h2>; return <h1>Process Instances</h1>;
}; };
const toggleShowFilterOptions = () => { const toggleShowFilterOptions = () => {

View File

@ -8,6 +8,7 @@ import {
getPageInfoFromSearchParams, getPageInfoFromSearchParams,
convertSecondsToFormattedDate, convertSecondsToFormattedDate,
modifyProcessModelPath, modifyProcessModelPath,
unModifyProcessModelPath,
} from '../helpers'; } from '../helpers';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
@ -33,10 +34,8 @@ export default function ProcessInstanceLogList() {
}, [searchParams, params]); }, [searchParams, params]);
const buildTable = () => { const buildTable = () => {
// return null;
const rows = processInstanceLogs.map((row) => { const rows = processInstanceLogs.map((row) => {
const rowToUse = row as any; const rowToUse = row as any;
console.log(`rowToUse: ${rowToUse}`);
return ( return (
<tr key={rowToUse.id}> <tr key={rowToUse.id}>
<td>{rowToUse.bpmn_process_identifier}</td> <td>{rowToUse.bpmn_process_identifier}</td>
@ -57,7 +56,7 @@ export default function ProcessInstanceLogList() {
); );
}); });
return ( return (
<Table striped bordered> <Table size="lg">
<thead> <thead>
<tr> <tr>
<th>Bpmn Process Identifier</th> <th>Bpmn Process Identifier</th>
@ -75,13 +74,25 @@ export default function ProcessInstanceLogList() {
}; };
if (pagination) { if (pagination) {
console.log('params.process_model_id', params.process_model_id);
const { page, perPage } = getPageInfoFromSearchParams(searchParams); const { page, perPage } = getPageInfoFromSearchParams(searchParams);
return ( return (
<main> <main>
<ProcessBreadcrumb <ProcessBreadcrumb
processModelId={params.process_model_id} hotCrumbs={[
processGroupId={params.process_group_id} ['Process Groups', '/admin'],
linkProcessModel [
`Process Model: ${params.process_model_id}`,
`process_model:${unModifyProcessModelPath(
params.process_model_id || ''
)}:link`,
],
[
`Process Instance: ${params.process_instance_id}`,
`/admin/process-models/${params.process_model_id}/process-instances/${params.process_instance_id}`,
],
['Logs'],
]}
/> />
<PaginationForTable <PaginationForTable
page={page} page={page}

View File

@ -112,7 +112,7 @@ export default function ProcessInstanceReportEdit() {
return ( return (
<> <>
<ProcessBreadcrumb /> <ProcessBreadcrumb />
<h2>Edit Process Instance Report: {params.report_identifier}</h2> <h1>Edit Process Instance Report: {params.report_identifier}</h1>
<ButtonWithConfirmation <ButtonWithConfirmation
description={`Delete Report ${params.report_identifier}?`} description={`Delete Report ${params.report_identifier}?`}
onConfirmation={deleteProcessInstanceReport} onConfirmation={deleteProcessInstanceReport}

View File

@ -54,7 +54,7 @@ export default function ProcessInstanceReportList() {
processModelId={params.process_model_id} processModelId={params.process_model_id}
linkProcessModel linkProcessModel
/> />
<h2>Reports for Process Model: {params.process_model_id}</h2> <h1>Reports for Process Model: {params.process_model_id}</h1>
<Button <Button
href={`/admin/process-models/${modifiedProcessModelId}/process-instances/reports/new`} href={`/admin/process-models/${modifiedProcessModelId}/process-instances/reports/new`}
> >

View File

@ -59,7 +59,7 @@ export default function ProcessInstanceReportNew() {
return ( return (
<> <>
<ProcessBreadcrumb /> <ProcessBreadcrumb />
<h2>Add Process Model</h2> <h1>Add Process Model</h1>
<form onSubmit={addProcessInstanceReport}> <form onSubmit={addProcessInstanceReport}>
<label htmlFor="identifier"> <label htmlFor="identifier">
identifier: identifier:

View File

@ -80,7 +80,7 @@ export default function ProcessInstanceReport() {
processGroupId={params.process_group_id} processGroupId={params.process_group_id}
linkProcessModel linkProcessModel
/> />
<h2>Process Instance Report: {params.report_identifier}</h2> <h1>Process Instance Report: {params.report_identifier}</h1>
<Button <Button
href={`/admin/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/reports/${params.report_identifier}/edit`} href={`/admin/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/reports/${params.report_identifier}/edit`}
> >

View File

@ -1,13 +1,33 @@
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 {
TrashCan,
StopOutline,
PauseOutline,
PlayOutline,
CaretLeft,
CaretRight,
InProgress,
Checkmark,
Warning,
// @ts-ignore // @ts-ignore
import { Button, Modal, Stack } from '@carbon/react'; } from '@carbon/icons-react';
import {
Grid,
Column,
Button,
ButtonSet,
Tag,
Modal,
Stack,
// @ts-ignore
} 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';
import { import {
convertSecondsToFormattedDate, convertSecondsToFormattedDateTime,
unModifyProcessModelPath, unModifyProcessModelPath,
} from '../helpers'; } from '../helpers';
import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; import ButtonWithConfirmation from '../components/ButtonWithConfirmation';
@ -125,11 +145,10 @@ export default function ProcessInstanceShow() {
const spiffStepLink = ( const spiffStepLink = (
processInstanceToUse: any, processInstanceToUse: any,
label: string, label: any,
distance: number distance: number
) => { ) => {
return ( return (
<li>
<Link <Link
reloadDocument reloadDocument
data-qa="process-instance-step-link" data-qa="process-instance-step-link"
@ -141,7 +160,6 @@ export default function ProcessInstanceShow() {
> >
{label} {label}
</Link> </Link>
</li>
); );
}; };
@ -150,7 +168,7 @@ export default function ProcessInstanceShow() {
return null; return null;
} }
return spiffStepLink(processInstanceToUse, 'Previous Step', -1); return spiffStepLink(processInstanceToUse, <CaretLeft />, -1);
}; };
const nextStepLink = (processInstanceToUse: any) => { const nextStepLink = (processInstanceToUse: any) => {
@ -158,68 +176,106 @@ export default function ProcessInstanceShow() {
return null; return null;
} }
return spiffStepLink(processInstanceToUse, 'Next Step', 1); return spiffStepLink(processInstanceToUse, <CaretRight />, 1);
}; };
const getInfoTag = (processInstanceToUse: any) => { const getInfoTag = (processInstanceToUse: any) => {
const currentEndDate = convertSecondsToFormattedDate( const currentEndDate = convertSecondsToFormattedDateTime(
processInstanceToUse.end_in_seconds processInstanceToUse.end_in_seconds
); );
let currentEndDateTag; let currentEndDateTag;
if (currentEndDate) { if (currentEndDate) {
currentEndDateTag = ( currentEndDateTag = (
<li> <Grid condensed fullWidth>
<Column sm={1} md={1} lg={1} className="grid-list-title">
Completed:{' '} Completed:{' '}
{convertSecondsToFormattedDate(processInstanceToUse.end_in_seconds) || </Column>
'N/A'} <Column sm={3} md={3} lg={3} className="grid-date">
</li> {convertSecondsToFormattedDateTime(
processInstanceToUse.end_in_seconds
) || 'N/A'}
</Column>
</Grid>
); );
} }
let statusIcon = <InProgress />;
if (processInstanceToUse.status === 'suspended') {
statusIcon = <PauseOutline />;
} else if (processInstanceToUse.status === 'complete') {
statusIcon = <Checkmark />;
} else if (processInstanceToUse.status === 'terminated') {
statusIcon = <StopOutline />;
} else if (processInstanceToUse.status === 'error') {
statusIcon = <Warning />;
}
return ( return (
<ul> <>
<li> <Grid condensed fullWidth>
<Column sm={1} md={1} lg={1} className="grid-list-title">
Started:{' '} Started:{' '}
{convertSecondsToFormattedDate(processInstanceToUse.start_in_seconds)} </Column>
</li> <Column sm={3} md={3} lg={3} className="grid-date">
{convertSecondsToFormattedDateTime(
processInstanceToUse.start_in_seconds
)}
</Column>
</Grid>
{currentEndDateTag} {currentEndDateTag}
<li>Status: {processInstanceToUse.status}</li> <Grid condensed fullWidth>
<li> <Column sm={1} md={1} lg={1} className="grid-list-title">
<Link Status:{' '}
</Column>
<Column sm={3} md={3} lg={3}>
<Tag type="gray" size="sm" className="span-tag">
{processInstanceToUse.status} {statusIcon}
</Tag>
</Column>
</Grid>
<br />
<Grid condensed fullWidth>
<Column sm={2} md={2} lg={2}>
<ButtonSet>
<Button
size="sm"
className="button-white-background"
data-qa="process-instance-log-list-link" data-qa="process-instance-log-list-link"
to={`/admin/process-models/${modifiedProcessModelId}/process-instances/${params.process_instance_id}/logs`} href={`/admin/process-models/${modifiedProcessModelId}/process-instances/${params.process_instance_id}/logs`}
> >
Logs Logs
</Link> </Button>
</li> <Button
<li> size="sm"
<Link className="button-white-background"
data-qa="process-instance-message-instance-list-link" data-qa="process-instance-message-instance-list-link"
to={`/admin/messages?process_model_id=${params.process_model_id}&process_instance_id=${params.process_instance_id}`} href={`/admin/messages?process_model_id=${params.process_model_id}&process_instance_id=${params.process_instance_id}`}
> >
Messages Messages
</Link> </Button>
</li> </ButtonSet>
<li> </Column>
Step {currentSpiffStep(processInstanceToUse)} of{' '} </Grid>
{processInstanceToUse.spiff_step} </>
</li>
{previousStepLink(processInstanceToUse)}
{nextStepLink(processInstanceToUse)}
</ul>
); );
}; };
const terminateButton = (processInstanceToUse: any) => { const terminateButton = (processInstanceToUse: any) => {
if ( if (
['complete', 'terminated', 'faulted'].indexOf( ['complete', 'terminated', 'error'].indexOf(
processInstanceToUse.status processInstanceToUse.status
) === -1 ) === -1
) { ) {
return ( return (
<Button onClick={terminateProcessInstance} variant="warning"> <ButtonWithConfirmation
Terminate kind="ghost"
</Button> renderIcon={StopOutline}
iconDescription="Terminate"
hasIconOnly
description={`Terminate Process Instance: ${processInstanceToUse.id}`}
onConfirmation={terminateProcessInstance}
confirmButtonLabel="Terminate"
/>
); );
} }
return <div />; return <div />;
@ -227,14 +283,19 @@ export default function ProcessInstanceShow() {
const suspendButton = (processInstanceToUse: any) => { const suspendButton = (processInstanceToUse: any) => {
if ( if (
['complete', 'terminated', 'faulted', 'suspended'].indexOf( ['complete', 'terminated', 'error', 'suspended'].indexOf(
processInstanceToUse.status processInstanceToUse.status
) === -1 ) === -1
) { ) {
return ( return (
<Button onClick={suspendProcessInstance} variant="warning"> <Button
Suspend onClick={suspendProcessInstance}
</Button> kind="ghost"
renderIcon={PauseOutline}
iconDescription="Suspend"
hasIconOnly
size="lg"
/>
); );
} }
return <div />; return <div />;
@ -243,9 +304,14 @@ export default function ProcessInstanceShow() {
const resumeButton = (processInstanceToUse: any) => { const resumeButton = (processInstanceToUse: any) => {
if (processInstanceToUse.status === 'suspended') { if (processInstanceToUse.status === 'suspended') {
return ( return (
<Button onClick={resumeProcessInstance} variant="warning"> <Button
Resume onClick={resumeProcessInstance}
</Button> kind="ghost"
renderIcon={PlayOutline}
iconDescription="Resume"
hasIconOnly
size="lg"
/>
); );
} }
return <div />; return <div />;
@ -433,6 +499,40 @@ export default function ProcessInstanceShow() {
return null; return null;
}; };
const stepsElement = (processInstanceToUse: any) => {
return (
<Grid condensed fullWidth>
<Column sm={3} md={3} lg={3}>
<Stack orientation="horizontal" gap={3} className="smaller-text">
{previousStepLink(processInstanceToUse)}
Step {currentSpiffStep(processInstanceToUse)} of{' '}
{processInstanceToUse.spiff_step}
{nextStepLink(processInstanceToUse)}
</Stack>
</Column>
</Grid>
);
};
const buttonIcons = (processInstanceToUse: any) => {
const elements = [];
elements.push(terminateButton(processInstanceToUse));
elements.push(suspendButton(processInstanceToUse));
elements.push(resumeButton(processInstanceToUse));
elements.push(
<ButtonWithConfirmation
kind="ghost"
renderIcon={TrashCan}
iconDescription="Delete"
hasIconOnly
description={`Delete Process Instance: ${processInstanceToUse.id}`}
onConfirmation={deleteProcessInstance}
confirmButtonLabel="Delete"
/>
);
return elements;
};
if (processInstance && tasks) { if (processInstance && tasks) {
const processInstanceToUse = processInstance as any; const processInstanceToUse = processInstance as any;
const taskIds = getTaskIds(); const taskIds = getTaskIds();
@ -449,22 +549,22 @@ export default function ProcessInstanceShow() {
`Process Model: ${processModelId}`, `Process Model: ${processModelId}`,
`process_model:${processModelId}:link`, `process_model:${processModelId}:link`,
], ],
[`Process Instance: ${params.process_instance_id}`], [`Process Instance Id: ${processInstanceToUse.id}`],
]} ]}
/> />
<Stack orientation="horizontal" gap={3}> <Stack orientation="horizontal" gap={1}>
<h2>Process Instance Id: {processInstanceToUse.id}</h2> <h1 className="with-icons">
<ButtonWithConfirmation Process Instance Id: {processInstanceToUse.id}
description="Delete Process Instance?" </h1>
onConfirmation={deleteProcessInstance} {buttonIcons(processInstanceToUse)}
buttonLabel="Delete"
/>
{terminateButton(processInstanceToUse)}
{suspendButton(processInstanceToUse)}
{resumeButton(processInstanceToUse)}
</Stack> </Stack>
<br />
<br />
{getInfoTag(processInstanceToUse)} {getInfoTag(processInstanceToUse)}
<br />
{taskDataDisplayArea()} {taskDataDisplayArea()}
{stepsElement(processInstanceToUse)}
<br />
<ReactDiagramEditor <ReactDiagramEditor
processModelId={processModelId || ''} processModelId={processModelId || ''}
diagramXML={processInstanceToUse.bpmn_xml_file_contents || ''} diagramXML={processInstanceToUse.bpmn_xml_file_contents || ''}

View File

@ -4,10 +4,11 @@ import { useParams } from 'react-router-dom';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
import ProcessModelForm from '../components/ProcessModelForm'; import ProcessModelForm from '../components/ProcessModelForm';
import { ProcessModel } from '../interfaces';
export default function ProcessModelEdit() { export default function ProcessModelEdit() {
const params = useParams(); const params = useParams();
const [processModel, setProcessModel] = useState(null); const [processModel, setProcessModel] = useState<ProcessModel | null>(null);
const processModelPath = `process-models/${params.process_model_id}`; const processModelPath = `process-models/${params.process_model_id}`;
useEffect(() => { useEffect(() => {
@ -20,8 +21,16 @@ export default function ProcessModelEdit() {
if (processModel) { if (processModel) {
return ( return (
<> <>
<ProcessBreadcrumb processGroupId={params.process_group_id} /> <ProcessBreadcrumb
<h2>Edit Process Model: {(processModel as any).id}</h2> hotCrumbs={[
['Process Groups', '/admin'],
[
`Process Model: ${processModel.id}`,
`process_model:${processModel.id}:link`,
],
]}
/>
<h1>Edit Process Model: {(processModel as any).id}</h1>
<ProcessModelForm <ProcessModelForm
mode="edit" mode="edit"
processGroupId={params.process_group_id} processGroupId={params.process_group_id}

View File

@ -268,7 +268,7 @@ export default function ProcessModelEditDiagram() {
}); });
event.eventBus.fire('spiff.json_files.returned', { options }); event.eventBus.fire('spiff.json_files.returned', { options });
} else { } else {
console.log('There is no process Model.'); console.error('There is no process Model.');
} }
}; };
@ -281,10 +281,9 @@ export default function ProcessModelEditDiagram() {
options.push({ label: ref.name, value: ref.id }); options.push({ label: ref.name, value: ref.id });
}); });
}); });
console.log('Options', options);
event.eventBus.fire('spiff.dmn_files.returned', { options }); event.eventBus.fire('spiff.dmn_files.returned', { options });
} else { } else {
console.log('There is no process model.'); console.error('There is no process model.');
} }
}; };
@ -641,21 +640,18 @@ export default function ProcessModelEditDiagram() {
const markdownEditor = () => { const markdownEditor = () => {
return ( return (
<Modal <Modal
size="xl" open={showMarkdownEditor}
show={showMarkdownEditor} modalHeading="Edit Markdown"
onHide={handleMarkdownEditorClose} primaryButtonText="Close"
onRequestSubmit={handleMarkdownEditorClose}
size="lg"
> >
<Modal.Header closeButton> <MDEditor
<Modal.Title>Edit Markdown Content</Modal.Title> height={500}
</Modal.Header> highlightEnable={false}
<Modal.Body> value={markdownText}
<MDEditor value={markdownText} onChange={setMarkdownText} /> onChange={setMarkdownText}
</Modal.Body> />
<Modal.Footer>
<Button variant="secondary" onClick={handleMarkdownEditorClose}>
Close
</Button>
</Modal.Footer>
</Modal> </Modal>
); );
}; };
@ -685,33 +681,39 @@ export default function ProcessModelEditDiagram() {
* @param processId * @param processId
*/ */
const fileNameTemplatePath =
'/admin/process-models/:process_model_id/files/:file_name';
const onLaunchBpmnEditor = (processId: string) => { const onLaunchBpmnEditor = (processId: string) => {
const file = findFileNameForReferenceId(processId, 'bpmn'); const file = findFileNameForReferenceId(processId, 'bpmn');
if (file) { if (file) {
const path = generatePath(fileNameTemplatePath, { const path = generatePath(
'/admin/process-models/:process_model_id/files/:file_name',
{
process_model_id: params.process_model_id, process_model_id: params.process_model_id,
file_name: file.name, file_name: file.name,
}); }
);
window.open(path); window.open(path);
} }
}; };
const onLaunchJsonEditor = (fileName: string) => { const onLaunchJsonEditor = (fileName: string) => {
const path = generatePath(fileNameTemplatePath, { const path = generatePath(
'/admin/process-models/:process_model_id/form/:file_name',
{
process_model_id: params.process_model_id, process_model_id: params.process_model_id,
file_name: fileName, file_name: fileName,
}); }
);
window.open(path); window.open(path);
}; };
const onLaunchDmnEditor = (processId: string) => { const onLaunchDmnEditor = (processId: string) => {
const file = findFileNameForReferenceId(processId, 'dmn'); const file = findFileNameForReferenceId(processId, 'dmn');
if (file) { if (file) {
const path = generatePath(fileNameTemplatePath, { const path = generatePath(
'/admin/process-models/:process_model_id/files/:file_name',
{
process_model_id: params.process_model_id, process_model_id: params.process_model_id,
file_name: file.name, file_name: file.name,
}); }
);
window.open(path); window.open(path);
} }
}; };
@ -780,10 +782,10 @@ export default function ProcessModelEditDiagram() {
[processModelFileName], [processModelFileName],
]} ]}
/> />
<h2> <h1>
Process Model File{processModelFile ? ': ' : ''} Process Model File{processModelFile ? ': ' : ''}
{processModelFileName} {processModelFileName}
</h2> </h1>
{appropriateEditor()} {appropriateEditor()}
{newFileNameBox()} {newFileNameBox()}
{scriptEditor()} {scriptEditor()}

View File

@ -16,8 +16,16 @@ export default function ProcessModelNew() {
return ( return (
<> <>
<ProcessBreadcrumb /> <ProcessBreadcrumb
<h2>Add Process Model</h2> hotCrumbs={[
['Process Groups', '/admin'],
[
`Process Group: ${params.process_group_id}`,
`process_group:${params.process_group_id}:link`,
],
]}
/>
<h1>Add Process Model</h1>
<ProcessModelForm <ProcessModelForm
mode="new" mode="new"
processGroupId={params.process_group_id} processGroupId={params.process_group_id}

View File

@ -448,7 +448,7 @@ export default function ProcessModelShow() {
} }
return ( return (
<Grid fullWidth> <Grid fullWidth>
<Column md={4} lg={8}> <Column md={5} lg={9} sm={3}>
<Accordion align="end"> <Accordion align="end">
<AccordionItem <AccordionItem
data-qa="files-accordion" data-qa="files-accordion"

View File

@ -153,9 +153,6 @@ export default function ReactFormEditor() {
return ( return (
<main> <main>
<ProcessBreadcrumb <ProcessBreadcrumb
processGroupId={params.process_group_id}
processModelId={params.process_model_id}
linkProcessModel
hotCrumbs={[ hotCrumbs={[
['Process Groups', '/admin'], ['Process Groups', '/admin'],
[ [
@ -169,10 +166,10 @@ export default function ReactFormEditor() {
[processModelFileName], [processModelFileName],
]} ]}
/> />
<h2> <h1>
Process Model File{processModelFile ? ': ' : ''} Process Model File{processModelFile ? ': ' : ''}
{processModelFileName} {processModelFileName}
</h2> </h1>
{newFileNameBox()} {newFileNameBox()}
<Button onClick={saveFile} variant="danger" data-qa="file-save-button"> <Button onClick={saveFile} variant="danger" data-qa="file-save-button">
Save Save

View File

@ -95,7 +95,7 @@ export default function SecretList() {
if (pagination) { if (pagination) {
return ( return (
<div> <div>
<h2>Secrets</h2> <h1>Secrets</h1>
{SecretsDisplayArea()} {SecretsDisplayArea()}
<Button href="/admin/secrets/new">Add a secret</Button> <Button href="/admin/secrets/new">Add a secret</Button>
</div> </div>

View File

@ -46,7 +46,7 @@ export default function SecretNew() {
return ( return (
<main style={{ padding: '1rem 0' }}> <main style={{ padding: '1rem 0' }}>
<h2>Add Secret</h2> <h1>Add Secret</h1>
<Form onSubmit={addSecret}> <Form onSubmit={addSecret}>
<Form.Group className="mb-3" controlId="formDisplayName"> <Form.Group className="mb-3" controlId="formDisplayName">
<Form.Label> <Form.Label>

View File

@ -65,7 +65,7 @@ export default function SecretShow() {
if (secret) { if (secret) {
return ( return (
<> <>
<h2>Secret Key: {secret.key}</h2> <h1>Secret Key: {secret.key}</h1>
<Stack orientation="horizontal" gap={3}> <Stack orientation="horizontal" gap={3}>
<ButtonWithConfirmation <ButtonWithConfirmation
description="Delete Secret?" description="Delete Secret?"