Squashed 'spiffworkflow-frontend/' changes from ab6cf2817..c096ca3f9

c096ca3f9 Report URL fixes (#29)
ca49db4d7 get pagination for groups
2bff2d4b2 Merge branch 'main' into feature/nested-groups-2
8a443ebe2 use error as a status instead of faulted w/ burnettk
2e32ed4d4 favor h1 tags over h2 w/ burnettk
0a4ac2d53 fixed up the process instance show page and moved contents of scss to css file and load that last w/ burnettk
670cbabb0 fix lint
d21a94467 Assure we are using the latest bpmn-js-spiffworkflow library.
a5339a3cb Use the modify method
3983a6eda Minor fixes for the launch buttons.
c63161699 moved usage of ProcessBreadcrumb to use hotCrumbs except for report pages
d06a3b6b7 cleaned up breadcrumbs some more and cleaned up console.log statements
614080e39 support process-groups in breadcrumb component
fe8f79017 add breadcrumb to instances page sometimes
2206972c5 fix recent process models, at least after they visit process model show page
ff0721362 make Files half-width and add link to file
f08538225 lint

git-subtree-dir: spiffworkflow-frontend
git-subtree-split: c096ca3f9c99df2812191b2a02702c1916c239bd
This commit is contained in:
Dan 2022-11-11 11:24:41 -05:00
parent 2b5c7d138e
commit 997146e18f
34 changed files with 610 additions and 472 deletions

9
package-lock.json generated
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",
@ -46545,7 +46545,7 @@
"@csstools/postcss-text-decoration-shorthand": "^1.0.0", "@csstools/postcss-text-decoration-shorthand": "^1.0.0",
"@csstools/postcss-trigonometric-functions": "^1.0.2", "@csstools/postcss-trigonometric-functions": "^1.0.2",
"@csstools/postcss-unset-value": "^1.0.2", "@csstools/postcss-unset-value": "^1.0.2",
"autoprefixer": "10.4.5", "autoprefixer": "10.4.8",
"browserslist": "^4.21.3", "browserslist": "^4.21.3",
"css-blank-pseudo": "^3.0.3", "css-blank-pseudo": "^3.0.3",
"css-has-pseudo": "^3.0.4", "css-has-pseudo": "^3.0.4",
@ -46583,8 +46583,7 @@
}, },
"dependencies": { "dependencies": {
"autoprefixer": { "autoprefixer": {
"version": "10.4.5", "version": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.5.tgz",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.5.tgz",
"integrity": "sha512-Fvd8yCoA7lNX/OUllvS+aS1I7WRBclGXsepbvT8ZaPgrH24rgXpZzF0/6Hh3ZEkwg+0AES/Osd196VZmYoEFtw==", "integrity": "sha512-Fvd8yCoA7lNX/OUllvS+aS1I7WRBclGXsepbvT8ZaPgrH24rgXpZzF0/6Hh3ZEkwg+0AES/Osd196VZmYoEFtw==",
"requires": { "requires": {
"browserslist": "^4.20.2", "browserslist": "^4.20.2",

View File

@ -38,6 +38,10 @@ export default function NavigationBar() {
let newActiveKey = '/admin/process-groups'; let newActiveKey = '/admin/process-groups';
if (location.pathname.match(/^\/admin\/messages\b/)) { if (location.pathname.match(/^\/admin\/messages\b/)) {
newActiveKey = '/admin/messages'; newActiveKey = '/admin/messages';
} else if (
location.pathname.match(/^\/admin\/process-instances\/reports\b/)
) {
newActiveKey = '/admin/process-instances/reports';
} else if (location.pathname.match(/^\/admin\/process-instances\b/)) { } else if (location.pathname.match(/^\/admin\/process-instances\b/)) {
newActiveKey = '/admin/process-instances'; newActiveKey = '/admin/process-instances';
} else if (location.pathname.match(/^\/admin\/secrets\b/)) { } else if (location.pathname.match(/^\/admin\/secrets\b/)) {
@ -119,6 +123,12 @@ export default function NavigationBar() {
> >
Authentications Authentications
</HeaderMenuItem> </HeaderMenuItem>
<HeaderMenuItem
href="/admin/process-instances/reports"
isCurrentPage={isActivePage('/admin/process-instances/reports')}
>
Reports
</HeaderMenuItem>
</> </>
); );
}; };

View File

@ -25,36 +25,3 @@ test('renders hotCrumbs', () => {
const nextElement = screen.getByText(/Process Group: hey/); const nextElement = screen.getByText(/Process Group: hey/);
expect(nextElement).toBeInTheDocument(); expect(nextElement).toBeInTheDocument();
}); });
// FIXME: update to use hotcrumbs
// test('renders process group when given processGroupId', async () => {
// render(
// <BrowserRouter>
// <ProcessBreadcrumb processGroupId="group-a" />
// </BrowserRouter>
// );
// const processGroupElement = screen.getByText(/group-a/);
// expect(processGroupElement).toBeInTheDocument();
// const processGroupBreadcrumbs = await screen.findAllByText(
// /Process Group: group-a/
// );
// expect(processGroupBreadcrumbs[0]).toHaveClass('breadcrumb-item active');
// });
//
// test('renders process model when given processModelId', async () => {
// render(
// <BrowserRouter>
// <ProcessBreadcrumb processGroupId="group-b" processModelId="model-c" />
// </BrowserRouter>
// );
// const processGroupElement = screen.getByText(/group-b/);
// expect(processGroupElement).toBeInTheDocument();
// const processModelBreadcrumbs = await screen.findAllByText(
// /Process Model: model-c/
// );
// expect(processModelBreadcrumbs[0]).toHaveClass('breadcrumb-item active');
// const processGroupBreadcrumbs = await screen.findAllByText(
// /Process Group: group-b/
// );
// expect(processGroupBreadcrumbs[0]).toBeInTheDocument();
// });

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

@ -2,7 +2,7 @@ import { useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
// @ts-ignore // @ts-ignore
import { Button, ButtonSet, Form, Stack, TextInput } from '@carbon/react'; import { Button, ButtonSet, Form, Stack, TextInput } from '@carbon/react';
import {modifyProcessModelPath, slugifyString} from '../helpers'; import { modifyProcessModelPath, slugifyString } from '../helpers';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
import { ProcessGroup } from '../interfaces'; import { ProcessGroup } from '../interfaces';
import ButtonWithConfirmation from './ButtonWithConfirmation'; import ButtonWithConfirmation from './ButtonWithConfirmation';
@ -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

@ -10,6 +10,10 @@ export default function SubNavigation() {
let newActiveKey = '/admin/process-groups'; let newActiveKey = '/admin/process-groups';
if (location.pathname.match(/^\/admin\/messages\b/)) { if (location.pathname.match(/^\/admin\/messages\b/)) {
newActiveKey = '/admin/messages'; newActiveKey = '/admin/messages';
} else if (
location.pathname.match(/^\/admin\/process-instances\/reports\b/)
) {
newActiveKey = '/admin/process-instances/reports';
} else if (location.pathname.match(/^\/admin\/process-instances\b/)) { } else if (location.pathname.match(/^\/admin\/process-instances\b/)) {
newActiveKey = '/admin/process-instances'; newActiveKey = '/admin/process-instances';
} else if (location.pathname.match(/^\/admin\/secrets\b/)) { } else if (location.pathname.match(/^\/admin\/secrets\b/)) {
@ -45,6 +49,9 @@ export default function SubNavigation() {
<Nav.Item> <Nav.Item>
<Nav.Link href="/admin/authentications">Authentications</Nav.Link> <Nav.Link href="/admin/authentications">Authentications</Nav.Link>
</Nav.Item> </Nav.Item>
<Nav.Item>
<Nav.Link href="/admin/process-instances/reports">Reports</Nav.Link>
</Nav.Item>
</Nav> </Nav>
); );
} }

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

@ -6,7 +6,7 @@ export interface Secret {
} }
export interface RecentProcessModel { export interface RecentProcessModel {
processGroupIdentifier: string; processGroupIdentifier?: string;
processModelIdentifier: string; processModelIdentifier: string;
processModelDisplayName: string; processModelDisplayName: string;
} }

View File

@ -81,19 +81,19 @@ export default function AdminRoutes() {
element={<ProcessInstanceShow />} element={<ProcessInstanceShow />}
/> />
<Route <Route
path="process-models/:process_model_id/process-instances/reports" path="process-instances/reports"
element={<ProcessInstanceReportList />} element={<ProcessInstanceReportList />}
/> />
<Route <Route
path="process-models/:process_group_id/:process_model_id/process-instances/reports/:report_identifier" path="process-instances/reports/:report_identifier"
element={<ProcessInstanceReportShow />} element={<ProcessInstanceReportShow />}
/> />
<Route <Route
path="process-models/:process_group_id/:process_model_id/process-instances/reports/new" path="process-instances/reports/new"
element={<ProcessInstanceReportNew />} element={<ProcessInstanceReportNew />}
/> />
<Route <Route
path="process-models/:process_group_id/:process_model_id/process-instances/reports/:report_identifier/edit" path="process-instances/reports/:report_identifier/edit"
element={<ProcessInstanceReportEdit />} element={<ProcessInstanceReportEdit />}
/> />
<Route <Route

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}
@ -158,13 +158,18 @@ export default function HomePage() {
); );
}; };
const tasksWaitingForMe = tasksWaitingForMeComponent();
const relevantProcessModelSection = const relevantProcessModelSection =
recentProcessModels.length > 0 && buildRecentProcessModelSection(); (recentProcessModels.length > 0 && buildRecentProcessModelSection()) ||
null;
if (pagination) { if (pagination) {
if (tasksWaitingForMe === null && relevantProcessModelSection === null) {
return <p>No tasks are waiting for you.</p>;
}
return ( return (
<> <>
{tasksWaitingForMeComponent()} {tasksWaitingForMe}
{relevantProcessModelSection} {relevantProcessModelSection}
</> </>
); );

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

@ -5,8 +5,12 @@ 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';
import {getPageInfoFromSearchParams, modifyProcessModelPath, unModifyProcessModelPath} from '../helpers'; import {
import { ProcessGroup } from '../interfaces'; getPageInfoFromSearchParams,
modifyProcessModelPath,
unModifyProcessModelPath,
} from '../helpers';
import { ProcessGroup, ProcessModel } from '../interfaces';
export default function ProcessGroupShow() { export default function ProcessGroupShow() {
const params = useParams(); const params = useParams();
@ -15,14 +19,19 @@ export default function ProcessGroupShow() {
const [processGroup, setProcessGroup] = useState<ProcessGroup | null>(null); const [processGroup, setProcessGroup] = useState<ProcessGroup | null>(null);
const [processModels, setProcessModels] = useState([]); const [processModels, setProcessModels] = useState([]);
const [processGroups, setProcessGroups] = useState([]); const [processGroups, setProcessGroups] = useState([]);
const [pagination, setPagination] = useState(null); const [modelPagination, setModelPagination] = useState(null);
const [groupPagination, setGroupPagination] = useState(null);
useEffect(() => { useEffect(() => {
const { page, perPage } = getPageInfoFromSearchParams(searchParams); const { page, perPage } = getPageInfoFromSearchParams(searchParams);
const setProcessModelFromResult = (result: any) => { const setProcessModelFromResult = (result: any) => {
setProcessModels(result.results); setProcessModels(result.results);
setPagination(result.pagination); setModelPagination(result.pagination);
};
const setProcessGroupFromResult = (result: any) => {
setProcessGroups(result.results);
setGroupPagination(result.pagination);
}; };
const processResult = (result: any) => { const processResult = (result: any) => {
setProcessGroup(result); setProcessGroup(result);
@ -33,7 +42,10 @@ export default function ProcessGroupShow() {
path: `/process-models?process_group_identifier=${unmodifiedProcessGroupId}&per_page=${perPage}&page=${page}`, path: `/process-models?process_group_identifier=${unmodifiedProcessGroupId}&per_page=${perPage}&page=${page}`,
successCallback: setProcessModelFromResult, successCallback: setProcessModelFromResult,
}); });
setProcessGroups(result.process_groups); HttpService.makeCallToBackend({
path: `/process-groups?process_group_identifier=${unmodifiedProcessGroupId}&per_page=${perPage}&page=${page}`,
successCallback: setProcessGroupFromResult,
});
}; };
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
path: `/process-groups/${params.process_group_id}`, path: `/process-groups/${params.process_group_id}`,
@ -45,19 +57,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 = (row as any).id.replace('/', ':'); 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>
); );
}); });
@ -81,19 +93,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((row as any).id); const modifiedProcessGroupId: String = modifyProcessModelPath(row.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>
); );
}); });
@ -113,15 +125,15 @@ export default function ProcessGroupShow() {
); );
}; };
if (processGroup && pagination) { if (processGroup && groupPagination && modelPagination) {
const { page, perPage } = getPageInfoFromSearchParams(searchParams); const { page, perPage } = getPageInfoFromSearchParams(searchParams);
const modifiedProcessGroupId = modifyProcessModelPath((processGroup as any).id); const modifiedProcessGroupId = modifyProcessModelPath(processGroup.id);
return ( return (
<> <>
<ProcessBreadcrumb <ProcessBreadcrumb
hotCrumbs={[ hotCrumbs={[
['Process Groups', '/admin'], ['Process Groups', '/admin'],
[`Process Group: ${processGroup.display_name}`], ['', `process_group:${processGroup.id}`],
]} ]}
/> />
<ul> <ul>
@ -149,18 +161,18 @@ export default function ProcessGroupShow() {
<PaginationForTable <PaginationForTable
page={page} page={page}
perPage={perPage} perPage={perPage}
pagination={pagination} pagination={modelPagination}
tableToDisplay={buildModelTable()} tableToDisplay={buildModelTable()}
path={`/admin/process-groups/${(processGroup as any).id}`} path={`/admin/process-groups/${processGroup.id}`}
/> />
<br /> <br />
<br /> <br />
<PaginationForTable <PaginationForTable
page={page} page={page}
perPage={perPage} perPage={perPage}
pagination={pagination} pagination={groupPagination}
tableToDisplay={buildGroupTable()} tableToDisplay={buildGroupTable()}
path={`/admin/process-groups/${(processGroup as any).id}`} path={`/admin/process-groups/${processGroup.id}`}
/> />
</ul> </ul>
</> </>

View File

@ -41,6 +41,7 @@ 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 { PaginationObject, ProcessModel } from '../interfaces';
import ProcessModelSearch from '../components/ProcessModelSearch'; import ProcessModelSearch from '../components/ProcessModelSearch';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
export default function ProcessInstanceList() { export default function ProcessInstanceList() {
const params = useParams(); const params = useParams();
@ -471,26 +472,31 @@ export default function ProcessInstanceList() {
); );
}; };
const processInstanceTitleElement = () => { const processInstanceBreadcrumbElement = () => {
const processModelFullIdentifier = const processModelFullIdentifier =
getProcessModelFullIdentifierFromSearchParams(searchParams); getProcessModelFullIdentifierFromSearchParams(searchParams);
if (processModelFullIdentifier === null) { if (processModelFullIdentifier === null) {
return <h2>Process Instances</h2>; return null;
} }
return ( return (
<h2> <ProcessBreadcrumb
Process Instances for:{' '} hotCrumbs={[
<Link ['Process Groups', '/admin'],
to={`/admin/process-models/${modifyProcessModelPath( [
processModelFullIdentifier `Process Model: ${processModelFullIdentifier}`,
)}`} `process_model:${processModelFullIdentifier}:link`,
> ],
{processModelFullIdentifier} ['Process Instances'],
</Link> ]}
</h2> />
); );
}; };
const processInstanceTitleElement = () => {
return <h1>Process Instances</h1>;
};
const toggleShowFilterOptions = () => { const toggleShowFilterOptions = () => {
setShowFilterOptions(!showFilterOptions); setShowFilterOptions(!showFilterOptions);
}; };
@ -499,6 +505,7 @@ export default function ProcessInstanceList() {
const { page, perPage } = getPageInfoFromSearchParams(searchParams); const { page, perPage } = getPageInfoFromSearchParams(searchParams);
return ( return (
<> <>
{processInstanceBreadcrumbElement()}
{processInstanceTitleElement()} {processInstanceTitleElement()}
<Grid fullWidth> <Grid fullWidth>
<Column <Column

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

@ -1,6 +1,5 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
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';
@ -24,15 +23,11 @@ export default function ProcessInstanceReportEdit() {
const [filterBy, setFilterBy] = useState(''); const [filterBy, setFilterBy] = useState('');
const navigateToProcessInstanceReport = (_result: any) => { const navigateToProcessInstanceReport = (_result: any) => {
navigate( navigate(`/admin/process-instances/reports/${params.report_identifier}`);
`/admin/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/reports/${params.report_identifier}`
);
}; };
const navigateToProcessInstanceReports = (_result: any) => { const navigateToProcessInstanceReports = (_result: any) => {
navigate( navigate(`/admin/process-instances/reports`);
`/admin/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/reports`
);
}; };
useEffect(() => { useEffect(() => {
@ -111,8 +106,7 @@ export default function ProcessInstanceReportEdit() {
return ( return (
<> <>
<ProcessBreadcrumb /> <h1>Edit Process Instance Report: {params.report_identifier}</h1>
<h2>Edit Process Instance Report: {params.report_identifier}</h2>
<ButtonWithConfirmation <ButtonWithConfirmation
description={`Delete Report ${params.report_identifier}?`} description={`Delete Report ${params.report_identifier}?`}
onConfirmation={deleteProcessInstanceReport} onConfirmation={deleteProcessInstanceReport}

View File

@ -2,16 +2,11 @@ import { useEffect, useState } from 'react';
// @ts-ignore // @ts-ignore
import { Button, Table } from '@carbon/react'; 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 HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
import { modifyProcessModelPath } from '../helpers';
export default function ProcessInstanceReportList() { export default function ProcessInstanceReportList() {
const params = useParams(); const params = useParams();
const [processInstanceReports, setProcessInstanceReports] = useState([]); const [processInstanceReports, setProcessInstanceReports] = useState([]);
const modifiedProcessModelId = modifyProcessModelPath(
params.process_model_id || ''
);
useEffect(() => { useEffect(() => {
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
@ -27,7 +22,7 @@ export default function ProcessInstanceReportList() {
<tr key={(row as any).id}> <tr key={(row as any).id}>
<td> <td>
<Link <Link
to={`/admin/process-models/${modifiedProcessModelId}/process-instances/reports/${rowToUse.identifier}`} to={`/admin/process-instances/reports/${rowToUse.identifier}`}
> >
{rowToUse.identifier} {rowToUse.identifier}
</Link> </Link>
@ -49,15 +44,8 @@ export default function ProcessInstanceReportList() {
const headerStuff = ( const headerStuff = (
<> <>
<ProcessBreadcrumb <h1>Process Instance Reports</h1>
processGroupId={params.process_group_id} <Button href="/admin/process-instances/reports/new">
processModelId={params.process_model_id}
linkProcessModel
/>
<h2>Reports for Process Model: {params.process_model_id}</h2>
<Button
href={`/admin/process-models/${modifiedProcessModelId}/process-instances/reports/new`}
>
Add a process instance report Add a process instance report
</Button> </Button>
</> </>

View File

@ -1,10 +1,9 @@
import { useState } from 'react'; import { useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
export default function ProcessInstanceReportNew() { export default function ProcessInstanceReportNew() {
const params = useParams();
const navigate = useNavigate(); const navigate = useNavigate();
const [identifier, setIdentifier] = useState(''); const [identifier, setIdentifier] = useState('');
@ -13,9 +12,7 @@ export default function ProcessInstanceReportNew() {
const [filterBy, setFilterBy] = useState(''); const [filterBy, setFilterBy] = useState('');
const navigateToNewProcessInstance = (_result: any) => { const navigateToNewProcessInstance = (_result: any) => {
navigate( navigate(`/admin/process-instances/reports/${identifier}`);
`/admin/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/reports/${identifier}`
);
}; };
const addProcessInstanceReport = (event: any) => { const addProcessInstanceReport = (event: any) => {
@ -59,7 +56,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,9 +80,9 @@ 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-instances/reports/${params.report_identifier}/edit`}
> >
Edit process instance report Edit process instance report
</Button> </Button>

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';
// @ts-ignore import {
import { Button, Modal, Stack } from '@carbon/react'; TrashCan,
StopOutline,
PauseOutline,
PlayOutline,
CaretLeft,
CaretRight,
InProgress,
Checkmark,
Warning,
// @ts-ignore
} 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,23 +145,21 @@ 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" to={`/admin/process-models/${
to={`/admin/process-models/${ params.process_model_id
params.process_model_id }/process-instances/${params.process_instance_id}/${
}/process-instances/${params.process_instance_id}/${ currentSpiffStep(processInstanceToUse) + distance
currentSpiffStep(processInstanceToUse) + distance }`}
}`} >
> {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>
Completed:{' '} <Column sm={1} md={1} lg={1} className="grid-list-title">
{convertSecondsToFormattedDate(processInstanceToUse.end_in_seconds) || Completed:{' '}
'N/A'} </Column>
</li> <Column sm={3} md={3} lg={3} className="grid-date">
{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>
Started:{' '} <Column sm={1} md={1} lg={1} className="grid-list-title">
{convertSecondsToFormattedDate(processInstanceToUse.start_in_seconds)} Started:{' '}
</li> </Column>
<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:{' '}
data-qa="process-instance-log-list-link" </Column>
to={`/admin/process-models/${modifiedProcessModelId}/process-instances/${params.process_instance_id}/logs`} <Column sm={3} md={3} lg={3}>
> <Tag type="gray" size="sm" className="span-tag">
Logs {processInstanceToUse.status} {statusIcon}
</Link> </Tag>
</li> </Column>
<li> </Grid>
<Link <br />
data-qa="process-instance-message-instance-list-link" <Grid condensed fullWidth>
to={`/admin/messages?process_model_id=${params.process_model_id}&process_instance_id=${params.process_instance_id}`} <Column sm={2} md={2} lg={2}>
> <ButtonSet>
Messages <Button
</Link> size="sm"
</li> className="button-white-background"
<li> data-qa="process-instance-log-list-link"
Step {currentSpiffStep(processInstanceToUse)} of{' '} href={`/admin/process-models/${modifiedProcessModelId}/process-instances/${params.process_instance_id}/logs`}
{processInstanceToUse.spiff_step} >
</li> Logs
{previousStepLink(processInstanceToUse)} </Button>
{nextStepLink(processInstanceToUse)} <Button
</ul> size="sm"
className="button-white-background"
data-qa="process-instance-message-instance-list-link"
href={`/admin/messages?process_model_id=${params.process_model_id}&process_instance_id=${params.process_instance_id}`}
>
Messages
</Button>
</ButtonSet>
</Column>
</Grid>
</>
); );
}; };
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(
process_model_id: params.process_model_id, '/admin/process-models/:process_model_id/files/:file_name',
file_name: file.name, {
}); process_model_id: params.process_model_id,
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(
process_model_id: params.process_model_id, '/admin/process-models/:process_model_id/form/:file_name',
file_name: fileName, {
}); process_model_id: params.process_model_id,
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(
process_model_id: params.process_model_id, '/admin/process-models/:process_model_id/files/:file_name',
file_name: file.name, {
}); process_model_id: params.process_model_id,
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

@ -13,6 +13,8 @@ import {
Accordion, Accordion,
AccordionItem, AccordionItem,
Button, Button,
Grid,
Column,
Stack, Stack,
ButtonSet, ButtonSet,
Modal, Modal,
@ -33,15 +35,8 @@ import { ProcessFile, ProcessModel, RecentProcessModel } from '../interfaces';
import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; import ButtonWithConfirmation from '../components/ButtonWithConfirmation';
const storeRecentProcessModelInLocalStorage = ( const storeRecentProcessModelInLocalStorage = (
processModelForStorage: any, processModelForStorage: ProcessModel
params: any
) => { ) => {
if (
params.process_group_id === undefined ||
params.process_model_id === undefined
) {
return;
}
// All values stored in localStorage are strings. // All values stored in localStorage are strings.
// Grab our recentProcessModels string from localStorage. // Grab our recentProcessModels string from localStorage.
const stringFromLocalStorage = window.localStorage.getItem( const stringFromLocalStorage = window.localStorage.getItem(
@ -58,16 +53,16 @@ const storeRecentProcessModelInLocalStorage = (
// Here's the value we want to add // Here's the value we want to add
const value = { const value = {
processGroupIdentifier: processModelForStorage.process_group_id,
processModelIdentifier: processModelForStorage.id, processModelIdentifier: processModelForStorage.id,
processModelDisplayName: processModelForStorage.display_name, processModelDisplayName: processModelForStorage.display_name,
}; };
// anything with a processGroupIdentifier is old and busted. leave it behind.
array = array.filter((item) => item.processGroupIdentifier === undefined);
// If our parsed/empty array doesn't already have this value in it... // If our parsed/empty array doesn't already have this value in it...
const matchingItem = array.find( const matchingItem = array.find(
(item) => (item) => item.processModelIdentifier === value.processModelIdentifier
item.processGroupIdentifier === value.processGroupIdentifier &&
item.processModelIdentifier === value.processModelIdentifier
); );
if (matchingItem === undefined) { if (matchingItem === undefined) {
// add the value to the beginning of the array // add the value to the beginning of the array
@ -77,13 +72,15 @@ const storeRecentProcessModelInLocalStorage = (
if (array.length > 3) { if (array.length > 3) {
array.pop(); array.pop();
} }
// turn the array WITH THE NEW VALUE IN IT into a string to prepare it to be stored in localStorage
const stringRepresentingArray = JSON.stringify(array);
// and store it in localStorage as "recentProcessModels"
window.localStorage.setItem('recentProcessModels', stringRepresentingArray);
} }
// once the old and busted serializations are gone, we can put these two statements inside the above if statement
// turn the array WITH THE NEW VALUE IN IT into a string to prepare it to be stored in localStorage
const stringRepresentingArray = JSON.stringify(array);
// and store it in localStorage as "recentProcessModels"
window.localStorage.setItem('recentProcessModels', stringRepresentingArray);
}; };
export default function ProcessModelShow() { export default function ProcessModelShow() {
@ -106,13 +103,13 @@ export default function ProcessModelShow() {
const processResult = (result: ProcessModel) => { const processResult = (result: ProcessModel) => {
setProcessModel(result); setProcessModel(result);
setReloadModel(false); setReloadModel(false);
storeRecentProcessModelInLocalStorage(result, params); storeRecentProcessModelInLocalStorage(result);
}; };
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
path: `/process-models/${modifiedProcessModelId}`, path: `/process-models/${modifiedProcessModelId}`,
successCallback: processResult, successCallback: processResult,
}); });
}, [params, reloadModel, modifiedProcessModelId]); }, [reloadModel, modifiedProcessModelId]);
const processModelRun = (processInstance: any) => { const processModelRun = (processInstance: any) => {
setErrorMessage(null); setErrorMessage(null);
@ -233,17 +230,22 @@ export default function ProcessModelShow() {
}); });
}; };
const navigateToFileEdit = (processModelFile: ProcessFile) => { const profileModelFileEditUrl = (processModelFile: ProcessFile) => {
if (processModel) { if (processModel) {
if (processModelFile.name.match(/\.(dmn|bpmn)$/)) { if (processModelFile.name.match(/\.(dmn|bpmn)$/)) {
navigate( return `/admin/process-models/${modifiedProcessModelId}/files/${processModelFile.name}`;
`/admin/process-models/${modifiedProcessModelId}/files/${processModelFile.name}`
);
} else if (processModelFile.name.match(/\.(json|md)$/)) {
navigate(
`/admin/process-models/${modifiedProcessModelId}/form/${processModelFile.name}`
);
} }
if (processModelFile.name.match(/\.(json|md)$/)) {
return `/admin/process-models/${modifiedProcessModelId}/form/${processModelFile.name}`;
}
}
return null;
};
const navigateToFileEdit = (processModelFile: ProcessFile) => {
const url = profileModelFileEditUrl(processModelFile);
if (url) {
navigate(url);
} }
}; };
@ -324,10 +326,15 @@ export default function ProcessModelShow() {
if (isPrimaryBpmnFile) { if (isPrimaryBpmnFile) {
primarySuffix = '- Primary File'; primarySuffix = '- Primary File';
} }
let fileLink = null;
const fileUrl = profileModelFileEditUrl(processModelFile);
if (fileUrl) {
fileLink = <Link to={fileUrl}>{processModelFile.name}</Link>;
}
constructedTag = ( constructedTag = (
<TableRow key={processModelFile.name}> <TableRow key={processModelFile.name}>
<TableCell key={`${processModelFile.name}-cell`}> <TableCell key={`${processModelFile.name}-cell`}>
{processModelFile.name} {fileLink}
{primarySuffix} {primarySuffix}
</TableCell> </TableCell>
{actionsTableCell} {actionsTableCell}
@ -371,14 +378,6 @@ export default function ProcessModelShow() {
List List
</Link> </Link>
</li> </li>
<li>
<Link
to={`/admin/process-models/${modifiedProcessModelId}/process-instances/reports`}
data-qa="process-instance-reports-link"
>
Reports
</Link>
</li>
</ul> </ul>
); );
}; };
@ -440,63 +439,67 @@ export default function ProcessModelShow() {
return null; return null;
} }
return ( return (
<Accordion> <Grid fullWidth>
<AccordionItem <Column md={5} lg={9} sm={3}>
data-qa="files-accordion" <Accordion align="end">
title={ <AccordionItem
<Stack orientation="horizontal"> data-qa="files-accordion"
<span> title={
<Button size="sm" kind="ghost"> <Stack orientation="horizontal">
Files <span>
<Button size="sm" kind="ghost">
Files
</Button>
</span>
</Stack>
}
>
<ButtonSet>
<Button
renderIcon={Upload}
data-qa="upload-file-button"
onClick={() => setShowFileUploadModal(true)}
size="sm"
kind=""
className="button-white-background"
>
Upload File
</Button> </Button>
</span> <Button
</Stack> renderIcon={Add}
} href={`/admin/process-models/${modifiedProcessModelId}/files?file_type=bpmn`}
> size="sm"
<ButtonSet> >
<Button New BPMN File
renderIcon={Upload} </Button>
data-qa="upload-file-button" <Button
onClick={() => setShowFileUploadModal(true)} renderIcon={Add}
size="sm" href={`/admin/process-models/${modifiedProcessModelId}/files?file_type=dmn`}
kind="" size="sm"
className="button-white-background" >
> New DMN File
Upload File </Button>
</Button> <Button
<Button renderIcon={Add}
renderIcon={Add} href={`/admin/process-models/${modifiedProcessModelId}/form?file_ext=json`}
href={`/admin/process-models/${modifiedProcessModelId}/files?file_type=bpmn`} size="sm"
size="sm" >
> New JSON File
New BPMN File </Button>
</Button> <Button
<Button renderIcon={Add}
renderIcon={Add} href={`/admin/process-models/${modifiedProcessModelId}/form?file_ext=md`}
href={`/admin/process-models/${modifiedProcessModelId}/files?file_type=dmn`} size="sm"
size="sm" >
> New Markdown File
New DMN File </Button>
</Button> </ButtonSet>
<Button <br />
renderIcon={Add} {processModelFileList()}
href={`/admin/process-models/${modifiedProcessModelId}/form?file_ext=json`} </AccordionItem>
size="sm" </Accordion>
> </Column>
New JSON File </Grid>
</Button>
<Button
renderIcon={Add}
href={`/admin/process-models/${modifiedProcessModelId}/form?file_ext=md`}
size="sm"
>
New Markdown File
</Button>
</ButtonSet>
<br />
{processModelFileList()}
</AccordionItem>
</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?"