spiff-arena/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx

952 lines
28 KiB
TypeScript
Raw Normal View History

import { useContext, useEffect, useRef, useState } from 'react';
import {
generatePath,
useNavigate,
useParams,
useSearchParams,
} from 'react-router-dom';
// @ts-ignore
import { Button, Modal, Content, Tabs, TabList, Tab, TabPanels, TabPanel } from '@carbon/react';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import Editor, { DiffEditor } from '@monaco-editor/react';
import MDEditor from '@uiw/react-md-editor';
import ReactDiagramEditor from '../components/ReactDiagramEditor';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import HttpService from '../services/HttpService';
import ErrorContext from '../contexts/ErrorContext';
import { makeid, modifyProcessIdentifierForPathParam } from '../helpers';
import {
CarbonComboBoxProcessSelection,
ProcessFile,
ProcessModel,
ProcessReference,
} from '../interfaces';
import ProcessSearch from '../components/ProcessSearch';
2022-12-13 14:20:53 -05:00
import { Notification } from '../components/Notification';
export default function ProcessModelEditDiagram() {
const [showFileNameEditor, setShowFileNameEditor] = useState(false);
const handleShowFileNameEditor = () => setShowFileNameEditor(true);
const [processModel, setProcessModel] = useState<ProcessModel | null>(null);
const [scriptText, setScriptText] = useState<string>('');
const [scriptType, setScriptType] = useState<string>('');
const [scriptEventBus, setScriptEventBus] = useState<any>(null);
const [scriptModeling, setScriptModeling] = useState(null);
const [scriptElement, setScriptElement] = useState(null);
const [showScriptEditor, setShowScriptEditor] = useState(false);
const handleShowScriptEditor = () => setShowScriptEditor(true);
const [markdownText, setMarkdownText] = useState<string | undefined>('');
const [markdownEventBus, setMarkdownEventBus] = useState<any>(null);
const [showMarkdownEditor, setShowMarkdownEditor] = useState(false);
const [showProcessSearch, setShowProcessSearch] = useState(false);
const [processSearchEventBus, setProcessSearchEventBus] = useState<any>(null);
const [processSearchElement, setProcessSearchElement] = useState<any>(null);
const [processes, setProcesses] = useState<ProcessReference[]>([]);
const handleShowMarkdownEditor = () => setShowMarkdownEditor(true);
const editorRef = useRef(null);
const monacoRef = useRef(null);
const failingScriptLineClassNamePrefix = 'failingScriptLineError';
function handleEditorDidMount(editor: any, monaco: any) {
// here is the editor instance
// you can store it in `useRef` for further usage
editorRef.current = editor;
monacoRef.current = monaco;
}
interface ScriptUnitTest {
id: string;
inputJson: any;
expectedOutputJson: any;
}
interface ScriptUnitTestResult {
result: boolean;
context?: object;
error?: string;
line_number?: number;
offset?: number;
}
const [currentScriptUnitTest, setCurrentScriptUnitTest] =
useState<ScriptUnitTest | null>(null);
const [currentScriptUnitTestIndex, setCurrentScriptUnitTestIndex] =
useState<number>(-1);
const [scriptUnitTestResult, setScriptUnitTestResult] =
useState<ScriptUnitTestResult | null>(null);
const params = useParams();
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const setErrorMessage = (useContext as any)(ErrorContext)[1];
const [processModelFile, setProcessModelFile] = useState<ProcessFile | null>(
null
);
const [newFileName, setNewFileName] = useState('');
const [bpmnXmlForDiagramRendering, setBpmnXmlForDiagramRendering] =
useState(null);
const modifiedProcessModelId = modifyProcessIdentifierForPathParam(
2022-11-07 17:22:46 -05:00
(params as any).process_model_id
);
const processModelPath = `process-models/${modifiedProcessModelId}`;
useEffect(() => {
// Grab all available process models in case we need to search for them.
// Taken from the Process Group List
const processResults = (result: any) => {
const selectionArray = result.map((item: any) => {
const label = `${item.display_name} (${item.identifier})`;
Object.assign(item, { label });
return item;
});
setProcesses(selectionArray);
};
HttpService.makeCallToBackend({
path: `/processes`,
successCallback: processResults,
});
}, [processModel]);
useEffect(() => {
const processResult = (result: ProcessModel) => {
setProcessModel(result);
};
HttpService.makeCallToBackend({
path: `/${processModelPath}`,
successCallback: processResult,
});
}, [processModelPath]);
useEffect(() => {
const fileResult = (result: any) => {
setProcessModelFile(result);
setBpmnXmlForDiagramRendering(result.file_contents);
};
if (params.file_name) {
HttpService.makeCallToBackend({
path: `/${processModelPath}/files/${params.file_name}`,
successCallback: fileResult,
});
}
}, [processModelPath, params]);
const handleFileNameCancel = () => {
setShowFileNameEditor(false);
setNewFileName('');
};
const navigateToProcessModelFile = (_result: any) => {
if (!params.file_name) {
const fileNameWithExtension = `${newFileName}.${searchParams.get(
'file_type'
)}`;
navigate(
2022-11-07 17:22:46 -05:00
`/admin/process-models/${modifiedProcessModelId}/files/${fileNameWithExtension}`
);
}
};
2022-12-13 14:20:53 -05:00
const [displaySaveFileMessage, setDisplaySaveFileMessage] =
useState<boolean>(false);
const saveDiagram = (bpmnXML: any, fileName = params.file_name) => {
setErrorMessage(null);
setBpmnXmlForDiagramRendering(bpmnXML);
2022-11-07 17:22:46 -05:00
let url = `/process-models/${modifiedProcessModelId}/files`;
let httpMethod = 'PUT';
let fileNameWithExtension = fileName;
if (newFileName) {
fileNameWithExtension = `${newFileName}.${searchParams.get('file_type')}`;
httpMethod = 'POST';
} else {
url += `/${fileNameWithExtension}`;
}
if (!fileNameWithExtension) {
handleShowFileNameEditor();
return;
}
const bpmnFile = new File([bpmnXML], fileNameWithExtension);
const formData = new FormData();
formData.append('file', bpmnFile);
formData.append('fileName', bpmnFile.name);
HttpService.makeCallToBackend({
path: url,
successCallback: navigateToProcessModelFile,
failureCallback: setErrorMessage,
httpMethod,
postBody: formData,
});
// after saving the file, make sure we null out newFileName
// so it does not get used over the params
setNewFileName('');
2022-12-13 14:20:53 -05:00
setDisplaySaveFileMessage(true);
};
const onDeleteFile = (fileName = params.file_name) => {
2022-11-07 17:22:46 -05:00
const url = `/process-models/${modifiedProcessModelId}/files/${fileName}`;
const httpMethod = 'DELETE';
const navigateToProcessModelShow = (_httpResult: any) => {
navigate(`/admin/process-models/${modifiedProcessModelId}`);
};
HttpService.makeCallToBackend({
path: url,
successCallback: navigateToProcessModelShow,
httpMethod,
});
};
const onSetPrimaryFile = (fileName = params.file_name) => {
2022-11-07 17:22:46 -05:00
const url = `/process-models/${modifiedProcessModelId}`;
const httpMethod = 'PUT';
const navigateToProcessModelShow = (_httpResult: any) => {
navigate(`/admin${url}`);
};
const processModelToPass = {
primary_file_name: fileName,
};
HttpService.makeCallToBackend({
path: url,
successCallback: navigateToProcessModelShow,
httpMethod,
postBody: processModelToPass,
});
};
const handleFileNameSave = (event: any) => {
event.preventDefault();
setShowFileNameEditor(false);
saveDiagram(bpmnXmlForDiagramRendering);
};
const newFileNameBox = () => {
const fileExtension = `.${searchParams.get('file_type')}`;
return (
<Modal
open={showFileNameEditor}
modalHeading="Processs Model File Name"
primaryButtonText="Save Changes"
secondaryButtonText="Cancel"
onSecondarySubmit={handleFileNameCancel}
onRequestSubmit={handleFileNameSave}
onRequestClose={handleFileNameCancel}
>
<label>File Name:</label>
<span>
<input
name="file_name"
type="text"
value={newFileName}
onChange={(e) => setNewFileName(e.target.value)}
autoFocus
/>
{fileExtension}
</span>
</Modal>
);
};
const resetUnitTextResult = () => {
setScriptUnitTestResult(null);
const styleSheet = document.styleSheets[0];
const ruleList = styleSheet.cssRules;
for (let ii = ruleList.length - 1; ii >= 0; ii -= 1) {
const regexp = new RegExp(
`^.${failingScriptLineClassNamePrefix}_.*::after `
);
if (ruleList[ii].cssText.match(regexp)) {
styleSheet.deleteRule(ii);
}
}
};
const makeApiHandler = (event: any) => {
return function fireEvent(results: any) {
event.eventBus.fire('spiff.service_tasks.returned', {
serviceTaskOperators: results,
});
};
};
const onServiceTasksRequested = (event: any) => {
HttpService.makeCallToBackend({
2022-12-05 16:35:20 -05:00
path: `/service-tasks`,
successCallback: makeApiHandler(event),
});
};
const onJsonFilesRequested = (event: any) => {
if (processModel) {
const jsonFiles = processModel.files.filter((f) => f.type === 'json');
const options = jsonFiles.map((f) => {
return { label: f.name, value: f.name };
});
event.eventBus.fire('spiff.json_files.returned', { options });
} else {
console.error('There is no process Model.');
}
};
const onDmnFilesRequested = (event: any) => {
if (processModel) {
const dmnFiles = processModel.files.filter((f) => f.type === 'dmn');
const options: any[] = [];
dmnFiles.forEach((file) => {
file.references.forEach((ref) => {
options.push({ label: ref.display_name, value: ref.identifier });
});
});
event.eventBus.fire('spiff.dmn_files.returned', { options });
} else {
console.error('There is no process model.');
}
};
const getScriptUnitTestElements = (element: any) => {
const { extensionElements } = element.businessObject;
if (extensionElements && extensionElements.values.length > 0) {
const unitTestModdleElements = extensionElements
.get('values')
.filter(function getInstanceOfType(e: any) {
return e.$instanceOf('spiffworkflow:unitTests');
})[0];
if (unitTestModdleElements) {
return unitTestModdleElements.unitTests;
}
}
return [];
};
const setScriptUnitTestElementWithIndex = (
scriptIndex: number,
element: any = scriptElement
) => {
const unitTestsModdleElements = getScriptUnitTestElements(element);
if (unitTestsModdleElements.length > 0) {
setCurrentScriptUnitTest(unitTestsModdleElements[scriptIndex]);
setCurrentScriptUnitTestIndex(scriptIndex);
}
};
const onLaunchScriptEditor = (
element: any,
script: string,
scriptTypeString: string,
eventBus: any,
modeling: any
) => {
// TODO: modeling is only needed for script unit tests.
// we should update this to act like updating scripts
// where we pass an event to bpmn-js
setScriptModeling(modeling);
setScriptText(script || '');
setScriptType(scriptTypeString);
setScriptEventBus(eventBus);
setScriptElement(element);
setScriptUnitTestElementWithIndex(0, element);
handleShowScriptEditor();
};
const handleScriptEditorClose = () => {
scriptEventBus.fire('spiff.script.update', {
scriptType,
script: scriptText,
element: scriptElement,
});
resetUnitTextResult();
setShowScriptEditor(false);
};
const handleEditorScriptChange = (value: any) => {
setScriptText(value);
};
const handleEditorScriptTestUnitInputChange = (value: any) => {
if (currentScriptUnitTest) {
currentScriptUnitTest.inputJson.value = value;
(scriptModeling as any).updateProperties(scriptElement, {});
}
};
const handleEditorScriptTestUnitOutputChange = (value: any) => {
if (currentScriptUnitTest) {
currentScriptUnitTest.expectedOutputJson.value = value;
(scriptModeling as any).updateProperties(scriptElement, {});
}
};
const generalEditorOptions = () => {
return {
glyphMargin: false,
folding: false,
lineNumbersMinChars: 0,
};
};
const jsonEditorOptions = () => {
return Object.assign(generalEditorOptions(), {
minimap: { enabled: false },
folding: true
});
}
const setPreviousScriptUnitTest = () => {
resetUnitTextResult();
const newScriptIndex = currentScriptUnitTestIndex - 1;
if (newScriptIndex >= 0) {
setScriptUnitTestElementWithIndex(newScriptIndex);
}
};
const setNextScriptUnitTest = () => {
resetUnitTextResult();
const newScriptIndex = currentScriptUnitTestIndex + 1;
const unitTestsModdleElements = getScriptUnitTestElements(scriptElement);
if (newScriptIndex < unitTestsModdleElements.length) {
setScriptUnitTestElementWithIndex(newScriptIndex);
}
};
const processScriptUnitTestRunResult = (result: any) => {
if ('result' in result) {
setScriptUnitTestResult(result);
if (
result.line_number &&
result.error &&
editorRef.current &&
monacoRef.current
) {
const currentClassName = `${failingScriptLineClassNamePrefix}_${makeid(
7
)}`;
// document.documentElement.style.setProperty causes the content property to go away
// so add the rule dynamically instead of changing a property variable
document.styleSheets[0].addRule(
`.${currentClassName}::after`,
`content: " # ${result.error.replaceAll('"', '')}"; color: red`
);
const lineLength =
scriptText.split('\n')[result.line_number - 1].length + 1;
const editorRefToUse = editorRef.current as any;
editorRefToUse.deltaDecorations(
[],
[
{
// Range(lineStart, column, lineEnd, column)
range: new (monacoRef.current as any).Range(
result.line_number,
lineLength
),
options: { afterContentClassName: currentClassName },
},
]
);
}
}
};
const runCurrentUnitTest = () => {
if (currentScriptUnitTest && scriptElement) {
let inputJson = '';
let expectedJson = '';
try {
inputJson = JSON.parse(currentScriptUnitTest.inputJson.value);
expectedJson = JSON.parse(currentScriptUnitTest.expectedOutputJson.value);
} catch (e) {
setScriptUnitTestResult({ result:false, error:"The JSON provided contains a formatting error."})
return;
}
resetUnitTextResult();
HttpService.makeCallToBackend({
2022-11-07 17:22:46 -05:00
path: `/process-models/${modifiedProcessModelId}/script-unit-tests/run`,
httpMethod: 'POST',
successCallback: processScriptUnitTestRunResult,
postBody: {
bpmn_task_identifier: (scriptElement as any).id,
python_script: scriptText,
input_json: inputJson,
expected_output_json: expectedJson
},
});
}
};
const unitTestFailureElement = () => {
if (
scriptUnitTestResult &&
scriptUnitTestResult.result === false
) {
let errorMessage = '';
if (scriptUnitTestResult.context) {
errorMessage = 'Unexpected result. Please see the comparison below.';
} else if (scriptUnitTestResult.line_number) {
errorMessage = `Error encountered running the script. Please check the code around line ${ scriptUnitTestResult.line_number}`
} else {
errorMessage = `Error encountered running the script. ${JSON.stringify(scriptUnitTestResult.error)}`
}
let errorStringElement = <span>{ errorMessage }</span>;
let errorContextElement = null;
if (scriptUnitTestResult.context) {
errorStringElement = (
<span>Unexpected result. Please see the comparison below.</span>
);
let outputJson = '{}';
if (currentScriptUnitTest) {
outputJson = JSON.stringify(
JSON.parse(currentScriptUnitTest.expectedOutputJson.value),
null,
' '
);
}
const contextJson = JSON.stringify(
scriptUnitTestResult.context,
null,
' '
);
errorContextElement = (
<DiffEditor
height={200}
width="auto"
originalLanguage="json"
modifiedLanguage="json"
options={Object.assign(jsonEditorOptions(), {})}
original={outputJson}
modified={contextJson}
/>
);
}
return (
<span style={{ color: 'red', fontSize: '1em' }}>
{errorStringElement}
{errorContextElement}
</span>
);
}
return null;
};
const scriptUnitTestEditorElement = () => {
if (currentScriptUnitTest) {
let previousButtonDisable = true;
if (currentScriptUnitTestIndex > 0) {
previousButtonDisable = false;
}
let nextButtonDisable = true;
const unitTestsModdleElements = getScriptUnitTestElements(scriptElement);
if (currentScriptUnitTestIndex < unitTestsModdleElements.length - 1) {
nextButtonDisable = false;
}
// unset current unit test if all tests were deleted
if (unitTestsModdleElements.length < 1) {
setCurrentScriptUnitTest(null);
setCurrentScriptUnitTestIndex(-1);
}
let scriptUnitTestResultBoolElement = null;
if (scriptUnitTestResult) {
scriptUnitTestResultBoolElement = (
<Col xs={1}>
{scriptUnitTestResult.result === true && (
<span style={{ color: 'green', fontSize: '3em' }}></span>
)}
{scriptUnitTestResult.result === false && (
<span style={{ color: 'red', fontSize: '3em' }}></span>
)}
</Col>
);
}
let inputJson = currentScriptUnitTest.inputJson.value;
let outputJson = currentScriptUnitTest.expectedOutputJson.value;
try {
inputJson = JSON.stringify(
JSON.parse(currentScriptUnitTest.inputJson.value),
null,
' '
);
outputJson = JSON.stringify(
JSON.parse(currentScriptUnitTest.expectedOutputJson.value),
null,
' '
);
} catch (e) {
// Attemping to format the json failed -- it's invalid.
}
return (
<main>
<Content>
<Row>
<Col xs={8}>
<Button variant="link" disabled>
Unit Test: {currentScriptUnitTest.id}
</Button>
</Col>
<Col xs={1}>
<Button
data-qa="unit-test-previous-button"
onClick={setPreviousScriptUnitTest}
variant="link"
disabled={previousButtonDisable}
>
&laquo;
</Button>
</Col>
<Col xs={1}>
<Button
data-qa="unit-test-next-button"
style={{ fontSize: '1.5em' }}
onClick={setNextScriptUnitTest}
variant="link"
disabled={nextButtonDisable}
>
&raquo;
</Button>
</Col>
<Col xs={1}>
<Button
className="justify-content-end"
data-qa="unit-test-run"
style={{ fontSize: '1.5em' }}
onClick={runCurrentUnitTest}
>
Run
</Button>
</Col>
<Col xs={1}>{scriptUnitTestResultBoolElement}</Col>
</Row>
<Row>
<Col>{unitTestFailureElement()}</Col>
</Row>
<Row>
<Col>
<div>Input Json:</div>
<div>
<Editor
height={500}
width="auto"
defaultLanguage="json"
options={Object.assign(jsonEditorOptions(), {})}
value={inputJson}
onChange={handleEditorScriptTestUnitInputChange}
/>
</div>
</Col>
<Col>
<div>Expected Output Json:</div>
<div>
<Editor
height={500}
width="auto"
defaultLanguage="json"
options={Object.assign(jsonEditorOptions(), {})}
value={outputJson}
onChange={handleEditorScriptTestUnitOutputChange}
/>
</div>
</Col>
</Row>
</Content>
</main>
);
}
return null;
};
const scriptEditor = () => {
return (
<Editor
height={500}
width="auto"
options={generalEditorOptions()}
defaultLanguage="python"
value={scriptText}
onChange={handleEditorScriptChange}
onMount={handleEditorDidMount}
/>
);
};
const scriptEditorAndTests = () => {
let scriptName = '';
if (scriptElement) {
scriptName = (scriptElement as any).di.bpmnElement.name;
}
return (
<Modal
open={showScriptEditor}
modalHeading={`Editing Script: ${scriptName}`}
primaryButtonText="Close"
onRequestSubmit={handleScriptEditorClose}
size="lg"
onRequestClose={handleScriptEditorClose}
>
<Tabs>
<TabList aria-label="List of tabs" activation="manual">
<Tab>Script Editor</Tab>
<Tab>Unit Tests</Tab>
</TabList>
<TabPanels>
<TabPanel>{scriptEditor()}</TabPanel>
<TabPanel>{scriptUnitTestEditorElement()}</TabPanel>
</TabPanels>
</Tabs>
</Modal>
);
};
const onLaunchMarkdownEditor = (
element: any,
markdown: string,
eventBus: any
) => {
setMarkdownText(markdown || '');
setMarkdownEventBus(eventBus);
handleShowMarkdownEditor();
};
const handleMarkdownEditorClose = () => {
markdownEventBus.fire('spiff.markdown.update', {
value: markdownText,
});
setShowMarkdownEditor(false);
};
const markdownEditor = () => {
return (
<Modal
2022-11-10 12:31:26 -05:00
open={showMarkdownEditor}
2022-11-10 14:32:39 -05:00
modalHeading="Edit Markdown"
2022-11-10 12:31:26 -05:00
primaryButtonText="Close"
onRequestSubmit={handleMarkdownEditorClose}
onRequestClose={handleMarkdownEditorClose}
2022-11-10 12:31:26 -05:00
size="lg"
>
2022-11-10 14:32:39 -05:00
<MDEditor
height={500}
highlightEnable={false}
value={markdownText}
onChange={setMarkdownText}
/>
</Modal>
);
};
const onSearchProcessModels = (
processId: string,
eventBus: any,
element: any
) => {
setProcessSearchEventBus(eventBus);
setProcessSearchElement(element);
setShowProcessSearch(true);
};
const processSearchOnClose = (selection: CarbonComboBoxProcessSelection) => {
const selectedProcessModel = selection.selectedItem;
if (selectedProcessModel) {
processSearchEventBus.fire('spiff.callactivity.update', {
element: processSearchElement,
value: selectedProcessModel.identifier,
});
}
setShowProcessSearch(false);
};
const processModelSelector = () => {
return (
<Modal
open={showProcessSearch}
modalHeading="Select Process Model"
primaryButtonText="Close"
onRequestSubmit={processSearchOnClose}
size="lg"
>
<ProcessSearch
height="500px"
onChange={processSearchOnClose}
processes={processes}
titleText="Process model search"
/>
</Modal>
);
};
const findFileNameForReferenceId = (
id: string,
type: string
): ProcessFile | null => {
// Given a reference id (like a process_id, or decision_id) finds the file
// that contains that reference and returns it.
let matchFile = null;
if (processModel) {
const files = processModel.files.filter((f) => f.type === type);
files.some((file) => {
if (file.references.some((ref) => ref.identifier === id)) {
matchFile = file;
return true;
}
return false;
});
}
return matchFile;
};
const onLaunchBpmnEditor = (processId: string) => {
const processRef = processes.find((p) => {
return p.identifier === processId;
});
if (processRef) {
2022-11-10 12:31:26 -05:00
const path = generatePath(
'/admin/process-models/:process_model_path/files/:file_name',
2022-11-10 12:31:26 -05:00
{
process_model_path: modifyProcessIdentifierForPathParam(
processRef.process_model_id
),
file_name: processRef.file_name,
2022-11-10 12:31:26 -05:00
}
);
window.open(path);
}
};
const onLaunchJsonEditor = (fileName: string) => {
2022-11-10 12:31:26 -05:00
const path = generatePath(
'/admin/process-models/:process_model_id/form/:file_name',
{
process_model_id: params.process_model_id,
file_name: fileName,
}
);
window.open(path);
};
const onLaunchDmnEditor = (processId: string) => {
const file = findFileNameForReferenceId(processId, 'dmn');
if (file) {
2022-11-10 12:31:26 -05:00
const path = generatePath(
'/admin/process-models/:process_model_id/files/:file_name',
{
process_model_id: params.process_model_id,
file_name: file.name,
}
);
window.open(path);
}
};
const isDmn = () => {
const fileName = params.file_name || '';
return searchParams.get('file_type') === 'dmn' || fileName.endsWith('.dmn');
};
const appropriateEditor = () => {
if (isDmn()) {
return (
<ReactDiagramEditor
processModelId={params.process_model_id || ''}
saveDiagram={saveDiagram}
onDeleteFile={onDeleteFile}
diagramXML={bpmnXmlForDiagramRendering}
fileName={params.file_name}
diagramType="dmn"
/>
);
}
// let this be undefined (so we won't display the button) unless the
// current primary_file_name is different from the one we're looking at.
let onSetPrimaryFileCallback;
if (
processModel &&
params.file_name &&
params.file_name !== processModel.primary_file_name
) {
onSetPrimaryFileCallback = onSetPrimaryFile;
}
return (
<ReactDiagramEditor
processModelId={params.process_model_id || ''}
saveDiagram={saveDiagram}
onDeleteFile={onDeleteFile}
isPrimaryFile={params.file_name === processModel?.primary_file_name}
onSetPrimaryFile={onSetPrimaryFileCallback}
diagramXML={bpmnXmlForDiagramRendering}
fileName={params.file_name}
diagramType="bpmn"
onLaunchScriptEditor={onLaunchScriptEditor}
onServiceTasksRequested={onServiceTasksRequested}
onLaunchMarkdownEditor={onLaunchMarkdownEditor}
onLaunchBpmnEditor={onLaunchBpmnEditor}
onLaunchJsonEditor={onLaunchJsonEditor}
onJsonFilesRequested={onJsonFilesRequested}
onLaunchDmnEditor={onLaunchDmnEditor}
onDmnFilesRequested={onDmnFilesRequested}
onSearchProcessModels={onSearchProcessModels}
/>
);
};
2022-12-13 14:20:53 -05:00
const saveFileMessage = () => {
if (displaySaveFileMessage) {
return (
<Notification
title="File Saved: "
onClose={() => setDisplaySaveFileMessage(false)}
>
Changes to the file were saved.
</Notification>
);
}
return null;
};
// if a file name is not given then this is a new model and the ReactDiagramEditor component will handle it
2022-11-18 15:06:02 -05:00
if ((bpmnXmlForDiagramRendering || !params.file_name) && processModel) {
const processModelFileName = processModelFile ? processModelFile.name : '';
return (
<>
<ProcessBreadcrumb
hotCrumbs={[
['Process Groups', '/admin'],
{
entityToExplode: processModel,
entityType: 'process-model',
linkLastItem: true,
},
[processModelFileName],
]}
/>
2022-11-10 15:46:57 -05:00
<h1>
Process Model File{processModelFile ? ': ' : ''}
{processModelFileName}
2022-11-10 15:46:57 -05:00
</h1>
2022-12-13 14:20:53 -05:00
{saveFileMessage()}
{appropriateEditor()}
{newFileNameBox()}
{scriptEditorAndTests()}
{markdownEditor()}
{processModelSelector()}
<div id="diagram-container" />
</>
);
}
return null;
}