Squashed 'spiffworkflow-frontend/' changes from 55607af9..16dc9a7c
16dc9a7c Merge pull request #75 from sartography/bug/replace-file-warning c9d376a3 Merge pull request #74 from sartography/bug/delete-primary-warning dcf5b5f2 Merge pull request #73 from sartography/bug/save-file-message d4ae393b added new api endpoint to get task-info so users with access to process instances can see the tasks but not the data 3d6c0acb get all of the process identifiers that the diagram knows about so we can display the correct task info 818fdbbd Allow viewing/editing xml of bpmn and dmn files (#76) e8a86a5a words 04ee4be6 process model cypress tests are passing w/ burnettk 23754677 some fixes for ci w/ burnettk 4ceb9364 throw error if not logged in w/ burnettk 7272eec0 force login if not logged when navigating to frontend w/ burnettk e285d9a4 Merge pull request #72 from sartography/feature/view_call_activity_diagram db8c9ec8 pyl and fix test w/ burnettk 7f0416cb use forEach 54881ae8 gitignore things f09d3aab Add a message when file is saved. 3bb56589 some fixes to ensure we display the correct task data for the diagram elements w/ burnettk 29533216 Don't show delete button for primary file 4fbeba49 allow viewing the diagram for a specific process identifier 43d7bfed Confirm before overwriting file when uploading file with same name git-subtree-dir: spiffworkflow-frontend git-subtree-split: 16dc9a7c4535eea9f15374fad8ecff77d6027a66
This commit is contained in:
parent
e4e0056581
commit
6b38b62ccf
|
@ -8,6 +8,9 @@
|
|||
# testing
|
||||
/coverage
|
||||
|
||||
# in case we accidentally run backend tests in frontend. :D
|
||||
/.coverage.*
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { modifyProcessIdentifierForPathParam } from '../../src/helpers';
|
||||
import { miscDisplayName } from '../support/helpers';
|
||||
|
||||
describe('process-models', () => {
|
||||
beforeEach(() => {
|
||||
|
@ -16,7 +17,7 @@ describe('process-models', () => {
|
|||
const modelDisplayName = `Test Model 2 ${id}`;
|
||||
const modelId = `test-model-2-${id}`;
|
||||
const newModelDisplayName = `${modelDisplayName} edited`;
|
||||
cy.contains('99-Shared Resources').click();
|
||||
cy.contains(miscDisplayName).click();
|
||||
cy.wait(500);
|
||||
cy.contains(groupDisplayName).click();
|
||||
cy.createModel(groupId, modelId, modelDisplayName);
|
||||
|
@ -34,7 +35,7 @@ describe('process-models', () => {
|
|||
cy.contains(`Process Model: ${newModelDisplayName}`);
|
||||
|
||||
// go back to process model show by clicking on the breadcrumb
|
||||
cy.contains(modelId).click();
|
||||
cy.contains(modelDisplayName).click();
|
||||
|
||||
cy.getBySel('delete-process-model-button').click();
|
||||
cy.contains('Are you sure');
|
||||
|
@ -46,6 +47,7 @@ describe('process-models', () => {
|
|||
`process-groups/${modifyProcessIdentifierForPathParam(groupId)}`
|
||||
);
|
||||
cy.contains(modelId).should('not.exist');
|
||||
cy.contains(modelDisplayName).should('not.exist');
|
||||
});
|
||||
|
||||
it('can create new bpmn, dmn, and json files', () => {
|
||||
|
@ -61,11 +63,11 @@ describe('process-models', () => {
|
|||
const dmnFileName = `dmn_test_file_${id}`;
|
||||
const jsonFileName = `json_test_file_${id}`;
|
||||
|
||||
cy.contains('99-Shared Resources').click();
|
||||
cy.contains(miscDisplayName).click();
|
||||
cy.wait(500);
|
||||
cy.contains(groupDisplayName).click();
|
||||
cy.createModel(groupId, modelId, modelDisplayName);
|
||||
cy.contains(directParentGroupId).click();
|
||||
cy.contains(groupDisplayName).click();
|
||||
cy.contains(modelDisplayName).click();
|
||||
cy.url().should(
|
||||
'include',
|
||||
|
@ -90,7 +92,7 @@ describe('process-models', () => {
|
|||
cy.get('input[name=file_name]').type(bpmnFileName);
|
||||
cy.contains('Save Changes').click();
|
||||
cy.contains(`Process Model File: ${bpmnFileName}`);
|
||||
cy.contains(modelId).click();
|
||||
cy.contains(modelDisplayName).click();
|
||||
cy.contains(`Process Model: ${modelDisplayName}`);
|
||||
// cy.getBySel('files-accordion').click();
|
||||
cy.contains(`${bpmnFileName}.bpmn`).should('exist');
|
||||
|
@ -108,7 +110,7 @@ describe('process-models', () => {
|
|||
cy.get('input[name=file_name]').type(dmnFileName);
|
||||
cy.contains('Save Changes').click();
|
||||
cy.contains(`Process Model File: ${dmnFileName}`);
|
||||
cy.contains(modelId).click();
|
||||
cy.contains(modelDisplayName).click();
|
||||
cy.contains(`Process Model: ${modelDisplayName}`);
|
||||
// cy.getBySel('files-accordion').click();
|
||||
cy.contains(`${dmnFileName}.dmn`).should('exist');
|
||||
|
@ -124,7 +126,7 @@ describe('process-models', () => {
|
|||
cy.contains(`Process Model File: ${jsonFileName}`);
|
||||
// wait for json to load before clicking away to avoid network errors
|
||||
cy.wait(500);
|
||||
cy.contains(modelId).click();
|
||||
cy.contains(modelDisplayName).click();
|
||||
cy.contains(`Process Model: ${modelDisplayName}`);
|
||||
// cy.getBySel('files-accordion').click();
|
||||
cy.contains(`${jsonFileName}.json`).should('exist');
|
||||
|
@ -151,12 +153,12 @@ describe('process-models', () => {
|
|||
const modelDisplayName = `Test Model 2 ${id}`;
|
||||
const modelId = `test-model-2-${id}`;
|
||||
cy.contains('Add a process group');
|
||||
cy.contains('99-Shared Resources').click();
|
||||
cy.contains(miscDisplayName).click();
|
||||
cy.wait(500);
|
||||
cy.contains(groupDisplayName).click();
|
||||
cy.createModel(groupId, modelId, modelDisplayName);
|
||||
|
||||
cy.contains(`${directParentGroupId}`).click();
|
||||
cy.contains(`${groupDisplayName}`).click();
|
||||
cy.contains('Add a process model');
|
||||
cy.contains(modelDisplayName).click();
|
||||
cy.url().should(
|
||||
|
@ -186,7 +188,7 @@ describe('process-models', () => {
|
|||
.click();
|
||||
|
||||
// in breadcrumb
|
||||
cy.contains(modelId).click();
|
||||
cy.contains(modelDisplayName).click();
|
||||
|
||||
cy.getBySel('delete-process-model-button').click();
|
||||
cy.contains('Are you sure');
|
||||
|
@ -203,7 +205,7 @@ describe('process-models', () => {
|
|||
|
||||
// process models no longer has pagination post-tiles
|
||||
// it.only('can paginate items', () => {
|
||||
// cy.contains('99-Shared Resources').click();
|
||||
// cy.contains(miscDisplayName).click();
|
||||
// cy.wait(500);
|
||||
// cy.contains('Acceptance Tests Group One').click();
|
||||
// cy.basicPaginationTest();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { string } from 'prop-types';
|
||||
import { modifyProcessIdentifierForPathParam } from '../../src/helpers';
|
||||
import { miscDisplayName } from './helpers';
|
||||
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
|
@ -86,15 +87,15 @@ Cypress.Commands.add('createModel', (groupId, modelId, modelDisplayName) => {
|
|||
Cypress.Commands.add(
|
||||
'runPrimaryBpmnFile',
|
||||
(expectAutoRedirectToHumanTask = false) => {
|
||||
cy.contains('Run').click();
|
||||
cy.contains('Start').click();
|
||||
if (expectAutoRedirectToHumanTask) {
|
||||
// the url changes immediately, so also make sure we get some content from the next page, "Task:", or else when we try to interact with the page, it'll re-render and we'll get an error with cypress.
|
||||
cy.url().should('include', `/tasks/`);
|
||||
cy.contains('Task: ');
|
||||
} else {
|
||||
cy.contains(/Process Instance.*kicked off/);
|
||||
cy.contains(/Process Instance.*[kK]icked [oO]ff/);
|
||||
cy.reload(true);
|
||||
cy.contains(/Process Instance.*kicked off/).should('not.exist');
|
||||
cy.contains(/Process Instance.*[kK]icked [oO]ff/).should('not.exist');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -103,8 +104,8 @@ Cypress.Commands.add(
|
|||
'navigateToProcessModel',
|
||||
(groupDisplayName, modelDisplayName, modelIdentifier) => {
|
||||
cy.navigateToAdmin();
|
||||
cy.contains('99-Shared Resources').click();
|
||||
cy.contains(`Process Group: 99-Shared Resources`, { timeout: 10000 });
|
||||
cy.contains(miscDisplayName).click();
|
||||
cy.contains(`Process Group: ${miscDisplayName}`, { timeout: 10000 });
|
||||
cy.contains(groupDisplayName).click();
|
||||
cy.contains(`Process Group: ${groupDisplayName}`);
|
||||
// https://stackoverflow.com/q/51254946/6090676
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export const miscDisplayName = 'Shared Resources';
|
|
@ -68,7 +68,7 @@
|
|||
"@cypress/grep": "^3.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.30.5",
|
||||
"@typescript-eslint/parser": "^5.30.6",
|
||||
"cypress": "^10.8.0",
|
||||
"cypress": "^12",
|
||||
"eslint": "^8.19.0",
|
||||
"eslint_d": "^12.2.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
|
@ -9850,9 +9850,9 @@
|
|||
"integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A=="
|
||||
},
|
||||
"node_modules/cypress": {
|
||||
"version": "10.11.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-10.11.0.tgz",
|
||||
"integrity": "sha512-lsaE7dprw5DoXM00skni6W5ElVVLGAdRUUdZjX2dYsGjbY/QnpzWZ95Zom1mkGg0hAaO/QVTZoFVS7Jgr/GUPA==",
|
||||
"version": "12.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-12.1.0.tgz",
|
||||
"integrity": "sha512-7fz8N84uhN1+ePNDsfQvoWEl4P3/VGKKmAg+bJQFY4onhA37Ys+6oBkGbNdwGeC7n2QqibNVPhk8x3YuQLwzfw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
|
@ -9903,7 +9903,7 @@
|
|||
"cypress": "bin/cypress"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
"node": "^14.0.0 || ^16.0.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cypress/node_modules/@types/node": {
|
||||
|
@ -38586,9 +38586,9 @@
|
|||
"integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A=="
|
||||
},
|
||||
"cypress": {
|
||||
"version": "10.11.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-10.11.0.tgz",
|
||||
"integrity": "sha512-lsaE7dprw5DoXM00skni6W5ElVVLGAdRUUdZjX2dYsGjbY/QnpzWZ95Zom1mkGg0hAaO/QVTZoFVS7Jgr/GUPA==",
|
||||
"version": "12.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-12.1.0.tgz",
|
||||
"integrity": "sha512-7fz8N84uhN1+ePNDsfQvoWEl4P3/VGKKmAg+bJQFY4onhA37Ys+6oBkGbNdwGeC7n2QqibNVPhk8x3YuQLwzfw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@cypress/request": "^2.88.10",
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
"@cypress/grep": "^3.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.30.5",
|
||||
"@typescript-eslint/parser": "^5.30.6",
|
||||
"cypress": "^10.8.0",
|
||||
"cypress": "^12",
|
||||
"eslint": "^8.19.0",
|
||||
"eslint_d": "^12.2.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
|
|
|
@ -13,6 +13,7 @@ import AdminRoutes from './routes/AdminRoutes';
|
|||
import { ErrorForDisplay } from './interfaces';
|
||||
|
||||
import { AbilityContext } from './contexts/Can';
|
||||
import UserService from './services/UserService';
|
||||
|
||||
export default function App() {
|
||||
const [errorMessage, setErrorMessage] = useState<ErrorForDisplay | null>(
|
||||
|
@ -24,6 +25,11 @@ export default function App() {
|
|||
[errorMessage]
|
||||
);
|
||||
|
||||
if (!UserService.isLoggedIn()) {
|
||||
UserService.doLogin();
|
||||
return null;
|
||||
}
|
||||
|
||||
const ability = defineAbility(() => {});
|
||||
|
||||
let errorTag = null;
|
||||
|
|
|
@ -24,6 +24,7 @@ import UserService from '../services/UserService';
|
|||
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
||||
import { PermissionsToCheck } from '../interfaces';
|
||||
import { usePermissionFetcher } from '../hooks/PermissionService';
|
||||
import { UnauthenticatedError } from '../services/HttpService';
|
||||
|
||||
// for ref: https://react-bootstrap.github.io/components/navbar/
|
||||
export default function NavigationBar() {
|
||||
|
@ -39,6 +40,11 @@ export default function NavigationBar() {
|
|||
const [activeKey, setActiveKey] = useState<string>('');
|
||||
|
||||
const { targetUris } = useUriListForPermissions();
|
||||
|
||||
// App.jsx forces login (which redirects to keycloak) so we should never get here if we're not logged in.
|
||||
if (!UserService.isLoggedIn()) {
|
||||
throw new UnauthenticatedError('You must be authenticated to do this.');
|
||||
}
|
||||
const permissionRequestData: PermissionsToCheck = {
|
||||
[targetUris.authenticationListPath]: ['GET'],
|
||||
[targetUris.messageInstanceListPath]: ['GET'],
|
||||
|
@ -135,6 +141,9 @@ export default function NavigationBar() {
|
|||
};
|
||||
|
||||
const headerMenuItems = () => {
|
||||
if (!UserService.isLoggedIn()) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<HeaderMenuItem href="/" isCurrentPage={isActivePage('/')}>
|
||||
|
|
|
@ -300,8 +300,13 @@ export default function ProcessInstanceListTable({
|
|||
|
||||
checkFiltersAndRun();
|
||||
if (autoReload) {
|
||||
refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, checkFiltersAndRun);
|
||||
return refreshAtInterval(
|
||||
REFRESH_INTERVAL,
|
||||
REFRESH_TIMEOUT,
|
||||
checkFiltersAndRun
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
}, [
|
||||
autoReload,
|
||||
searchParams,
|
||||
|
@ -838,8 +843,8 @@ export default function ProcessInstanceListTable({
|
|||
return null;
|
||||
}}
|
||||
shouldFilterItem={shouldFilterReportColumn}
|
||||
placeholder="Choose a report column"
|
||||
titleText="Report Column"
|
||||
placeholder="Choose a column to show"
|
||||
titleText="Column"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -888,7 +893,7 @@ export default function ProcessInstanceListTable({
|
|||
kind="ghost"
|
||||
size="sm"
|
||||
className={`button-tag-icon ${tagTypeClass}`}
|
||||
title={`Edit ${reportColumnForEditing.accessor}`}
|
||||
title={`Edit ${reportColumnForEditing.accessor} column`}
|
||||
onClick={() => {
|
||||
setReportColumnToOperateOn(reportColumnForEditing);
|
||||
setShowReportColumnForm(true);
|
||||
|
@ -916,7 +921,7 @@ export default function ProcessInstanceListTable({
|
|||
<Button
|
||||
data-qa="add-column-button"
|
||||
renderIcon={AddAlt}
|
||||
iconDescription="Filter Options"
|
||||
iconDescription="Column options"
|
||||
className="with-tiny-top-margin"
|
||||
kind="ghost"
|
||||
hasIconOnly
|
||||
|
|
|
@ -52,22 +52,25 @@ import TouchModule from 'diagram-js/lib/navigation/touch';
|
|||
// @ts-expect-error TS(7016) FIXME
|
||||
import ZoomScrollModule from 'diagram-js/lib/navigation/zoomscroll';
|
||||
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { Can } from '@casl/react';
|
||||
import HttpService from '../services/HttpService';
|
||||
|
||||
import ButtonWithConfirmation from './ButtonWithConfirmation';
|
||||
import { makeid } from '../helpers';
|
||||
import { getBpmnProcessIdentifiers, makeid } from '../helpers';
|
||||
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
||||
import { PermissionsToCheck } from '../interfaces';
|
||||
import { PermissionsToCheck, ProcessInstanceTask } from '../interfaces';
|
||||
import { usePermissionFetcher } from '../hooks/PermissionService';
|
||||
|
||||
type OwnProps = {
|
||||
processModelId: string;
|
||||
diagramType: string;
|
||||
readyOrWaitingBpmnTaskIds?: string[] | null;
|
||||
completedTasksBpmnIds?: string[] | null;
|
||||
readyOrWaitingProcessInstanceTasks?: ProcessInstanceTask[] | null;
|
||||
completedProcessInstanceTasks?: ProcessInstanceTask[] | null;
|
||||
saveDiagram?: (..._args: any[]) => any;
|
||||
onDeleteFile?: (..._args: any[]) => any;
|
||||
isPrimaryFile?: boolean;
|
||||
onSetPrimaryFile?: (..._args: any[]) => any;
|
||||
diagramXML?: string | null;
|
||||
fileName?: string;
|
||||
|
@ -88,10 +91,11 @@ type OwnProps = {
|
|||
export default function ReactDiagramEditor({
|
||||
processModelId,
|
||||
diagramType,
|
||||
readyOrWaitingBpmnTaskIds,
|
||||
completedTasksBpmnIds,
|
||||
readyOrWaitingProcessInstanceTasks,
|
||||
completedProcessInstanceTasks,
|
||||
saveDiagram,
|
||||
onDeleteFile,
|
||||
isPrimaryFile,
|
||||
onSetPrimaryFile,
|
||||
diagramXML,
|
||||
fileName,
|
||||
|
@ -119,6 +123,7 @@ export default function ReactDiagramEditor({
|
|||
[targetUris.processModelFileShowPath]: ['POST', 'GET', 'PUT', 'DELETE'],
|
||||
};
|
||||
const { ability } = usePermissionFetcher(permissionRequestData);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (diagramModelerState) {
|
||||
|
@ -227,7 +232,11 @@ export default function ReactDiagramEditor({
|
|||
|
||||
function handleElementClick(event: any) {
|
||||
if (onElementClick) {
|
||||
onElementClick(event.element);
|
||||
const canvas = diagramModeler.get('canvas');
|
||||
const bpmnProcessIdentifiers = getBpmnProcessIdentifiers(
|
||||
canvas.getRootElement()
|
||||
);
|
||||
onElementClick(event.element, bpmnProcessIdentifiers);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -350,12 +359,19 @@ export default function ReactDiagramEditor({
|
|||
|
||||
function highlightBpmnIoElement(
|
||||
canvas: any,
|
||||
taskBpmnId: string,
|
||||
bpmnIoClassName: string
|
||||
processInstanceTask: ProcessInstanceTask,
|
||||
bpmnIoClassName: string,
|
||||
bpmnProcessIdentifiers: string[]
|
||||
) {
|
||||
if (checkTaskCanBeHighlighted(taskBpmnId)) {
|
||||
if (checkTaskCanBeHighlighted(processInstanceTask.name)) {
|
||||
try {
|
||||
canvas.addMarker(taskBpmnId, bpmnIoClassName);
|
||||
if (
|
||||
bpmnProcessIdentifiers.includes(
|
||||
processInstanceTask.process_identifier
|
||||
)
|
||||
) {
|
||||
canvas.addMarker(processInstanceTask.name, bpmnIoClassName);
|
||||
}
|
||||
} catch (bpmnIoError: any) {
|
||||
// the task list also contains task for processes called from call activities which will
|
||||
// not exist in this diagram so just ignore them for now.
|
||||
|
@ -394,21 +410,29 @@ export default function ReactDiagramEditor({
|
|||
// highlighting a field
|
||||
// Option 3 at:
|
||||
// https://github.com/bpmn-io/bpmn-js-examples/tree/master/colors
|
||||
if (readyOrWaitingBpmnTaskIds) {
|
||||
readyOrWaitingBpmnTaskIds.forEach((readyOrWaitingBpmnTaskId) => {
|
||||
if (readyOrWaitingProcessInstanceTasks) {
|
||||
const bpmnProcessIdentifiers = getBpmnProcessIdentifiers(
|
||||
canvas.getRootElement()
|
||||
);
|
||||
readyOrWaitingProcessInstanceTasks.forEach((readyOrWaitingBpmnTask) => {
|
||||
highlightBpmnIoElement(
|
||||
canvas,
|
||||
readyOrWaitingBpmnTaskId,
|
||||
'active-task-highlight'
|
||||
readyOrWaitingBpmnTask,
|
||||
'active-task-highlight',
|
||||
bpmnProcessIdentifiers
|
||||
);
|
||||
});
|
||||
}
|
||||
if (completedTasksBpmnIds) {
|
||||
completedTasksBpmnIds.forEach((completedTaskBpmnId) => {
|
||||
if (completedProcessInstanceTasks) {
|
||||
const bpmnProcessIdentifiers = getBpmnProcessIdentifiers(
|
||||
canvas.getRootElement()
|
||||
);
|
||||
completedProcessInstanceTasks.forEach((completedTask) => {
|
||||
highlightBpmnIoElement(
|
||||
canvas,
|
||||
completedTaskBpmnId,
|
||||
'completed-task-highlight'
|
||||
completedTask,
|
||||
'completed-task-highlight',
|
||||
bpmnProcessIdentifiers
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -484,8 +508,8 @@ export default function ReactDiagramEditor({
|
|||
diagramType,
|
||||
diagramXML,
|
||||
diagramXMLString,
|
||||
readyOrWaitingBpmnTaskIds,
|
||||
completedTasksBpmnIds,
|
||||
readyOrWaitingProcessInstanceTasks,
|
||||
completedProcessInstanceTasks,
|
||||
fileName,
|
||||
performingXmlUpdates,
|
||||
processModelId,
|
||||
|
@ -533,6 +557,8 @@ export default function ReactDiagramEditor({
|
|||
});
|
||||
};
|
||||
|
||||
const canViewXml = fileName !== undefined;
|
||||
|
||||
const userActionOptions = () => {
|
||||
if (diagramType !== 'readonly') {
|
||||
return (
|
||||
|
@ -549,7 +575,7 @@ export default function ReactDiagramEditor({
|
|||
a={targetUris.processModelFileShowPath}
|
||||
ability={ability}
|
||||
>
|
||||
{fileName && (
|
||||
{fileName && !isPrimaryFile && (
|
||||
<ButtonWithConfirmation
|
||||
description={`Delete file ${fileName}?`}
|
||||
onConfirmation={handleDelete}
|
||||
|
@ -571,6 +597,23 @@ export default function ReactDiagramEditor({
|
|||
>
|
||||
<Button onClick={downloadXmlFile}>Download</Button>
|
||||
</Can>
|
||||
<Can
|
||||
I="GET"
|
||||
a={targetUris.processModelFileShowPath}
|
||||
ability={ability}
|
||||
>
|
||||
{canViewXml && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
navigate(
|
||||
`/admin/process-models/${processModelId}/form/${fileName}`
|
||||
);
|
||||
}}
|
||||
>
|
||||
View XML
|
||||
</Button>
|
||||
)}
|
||||
</Can>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ export default function MyOpenProcesses() {
|
|||
});
|
||||
};
|
||||
getTasks();
|
||||
refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks);
|
||||
return refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks);
|
||||
}, [searchParams]);
|
||||
|
||||
const buildTable = () => {
|
||||
|
|
|
@ -41,7 +41,7 @@ export default function TasksWaitingForMyGroups() {
|
|||
});
|
||||
};
|
||||
getTasks();
|
||||
refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks);
|
||||
return refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks);
|
||||
}, [searchParams]);
|
||||
|
||||
const buildTable = () => {
|
||||
|
|
|
@ -208,5 +208,29 @@ export const refreshAtInterval = (
|
|||
() => clearInterval(intervalRef),
|
||||
timeout * 1000
|
||||
);
|
||||
return [intervalRef, timeoutRef];
|
||||
return () => {
|
||||
clearInterval(intervalRef);
|
||||
clearTimeout(timeoutRef);
|
||||
};
|
||||
};
|
||||
|
||||
const getChildProcesses = (bpmnElement: any) => {
|
||||
let elements: string[] = [];
|
||||
bpmnElement.children.forEach((c: any) => {
|
||||
if (c.type === 'bpmn:Participant') {
|
||||
if (c.businessObject.processRef) {
|
||||
elements.push(c.businessObject.processRef.id);
|
||||
}
|
||||
elements = [...elements, ...getChildProcesses(c)];
|
||||
} else if (c.type === 'bpmn:SubProcess') {
|
||||
elements.push(c.id);
|
||||
}
|
||||
});
|
||||
return elements;
|
||||
};
|
||||
|
||||
export const getBpmnProcessIdentifiers = (rootBpmnElement: any) => {
|
||||
const childProcesses = getChildProcesses(rootBpmnElement);
|
||||
childProcesses.push(rootBpmnElement.businessObject.id);
|
||||
return childProcesses;
|
||||
};
|
||||
|
|
|
@ -14,7 +14,8 @@ export const useUriListForPermissions = () => {
|
|||
processInstanceListPath: '/v1.0/process-instances',
|
||||
processInstanceLogListPath: `/v1.0/logs/${params.process_model_id}/${params.process_instance_id}`,
|
||||
processInstanceReportListPath: '/v1.0/process-instances/reports',
|
||||
processInstanceTaskListPath: `/v1.0/task-data/${params.process_model_id}/${params.process_instance_id}`,
|
||||
processInstanceTaskListPath: `/v1.0/process-instances/${params.process_model_id}/${params.process_instance_id}/task-info`,
|
||||
processInstanceTaskListDataPath: `/v1.0/task-data/${params.process_model_id}/${params.process_instance_id}`,
|
||||
processModelCreatePath: `/v1.0/process-models/${params.process_group_id}`,
|
||||
processModelFileCreatePath: `/v1.0/process-models/${params.process_model_id}/files`,
|
||||
processModelFileShowPath: `/v1.0/process-models/${params.process_model_id}/files/${params.file_name}`,
|
||||
|
|
|
@ -11,6 +11,13 @@ export interface RecentProcessModel {
|
|||
processModelDisplayName: string;
|
||||
}
|
||||
|
||||
export interface ProcessInstanceTask {
|
||||
id: string;
|
||||
state: string;
|
||||
process_identifier: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface ProcessReference {
|
||||
name: string; // The process or decision Display name.
|
||||
identifier: string; // The unique id of the process
|
||||
|
@ -39,6 +46,7 @@ export interface ProcessInstance {
|
|||
id: number;
|
||||
process_model_identifier: string;
|
||||
process_model_display_name: string;
|
||||
spiff_step?: number;
|
||||
}
|
||||
|
||||
export interface MessageCorrelationProperties {
|
||||
|
|
|
@ -21,10 +21,11 @@ export default function ProcessInstanceList() {
|
|||
<ProcessBreadcrumb
|
||||
hotCrumbs={[
|
||||
['Process Groups', '/admin'],
|
||||
[
|
||||
`Process Model: ${processModelFullIdentifier}`,
|
||||
`process_model:${processModelFullIdentifier}:link`,
|
||||
],
|
||||
{
|
||||
entityToExplode: processModelFullIdentifier,
|
||||
entityType: 'process-model-id',
|
||||
linkLastItem: true,
|
||||
},
|
||||
['Process Instances'],
|
||||
]}
|
||||
/>
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import { useContext, useEffect, useState } from 'react';
|
||||
import Editor from '@monaco-editor/react';
|
||||
import { useParams, useNavigate, Link } from 'react-router-dom';
|
||||
import {
|
||||
useParams,
|
||||
useNavigate,
|
||||
Link,
|
||||
useSearchParams,
|
||||
} from 'react-router-dom';
|
||||
import {
|
||||
TrashCan,
|
||||
StopOutline,
|
||||
|
@ -34,15 +39,21 @@ import {
|
|||
import ButtonWithConfirmation from '../components/ButtonWithConfirmation';
|
||||
import ErrorContext from '../contexts/ErrorContext';
|
||||
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
||||
import { PermissionsToCheck } from '../interfaces';
|
||||
import {
|
||||
PermissionsToCheck,
|
||||
ProcessInstance,
|
||||
ProcessInstanceTask,
|
||||
} from '../interfaces';
|
||||
import { usePermissionFetcher } from '../hooks/PermissionService';
|
||||
|
||||
export default function ProcessInstanceShow() {
|
||||
const navigate = useNavigate();
|
||||
const params = useParams();
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
const [processInstance, setProcessInstance] = useState(null);
|
||||
const [tasks, setTasks] = useState<Array<object> | null>(null);
|
||||
const [processInstance, setProcessInstance] =
|
||||
useState<ProcessInstance | null>(null);
|
||||
const [tasks, setTasks] = useState<ProcessInstanceTask[] | null>(null);
|
||||
const [tasksCallHadError, setTasksCallHadError] = useState<boolean>(false);
|
||||
const [taskToDisplay, setTaskToDisplay] = useState<object | null>(null);
|
||||
const [taskDataToDisplay, setTaskDataToDisplay] = useState<string>('');
|
||||
|
@ -59,8 +70,10 @@ export default function ProcessInstanceShow() {
|
|||
const permissionRequestData: PermissionsToCheck = {
|
||||
[targetUris.messageInstanceListPath]: ['GET'],
|
||||
[targetUris.processInstanceTaskListPath]: ['GET'],
|
||||
[targetUris.processInstanceTaskListDataPath]: ['GET', 'PUT'],
|
||||
[targetUris.processInstanceActionPath]: ['DELETE'],
|
||||
[targetUris.processInstanceLogListPath]: ['GET'],
|
||||
[targetUris.processModelShowPath]: ['PUT'],
|
||||
[`${targetUris.processInstanceActionPath}/suspend`]: ['PUT'],
|
||||
[`${targetUris.processInstanceActionPath}/terminate`]: ['PUT'],
|
||||
[`${targetUris.processInstanceActionPath}/resume`]: ['PUT'],
|
||||
|
@ -80,17 +93,28 @@ export default function ProcessInstanceShow() {
|
|||
const processTaskFailure = () => {
|
||||
setTasksCallHadError(true);
|
||||
};
|
||||
let queryParams = '';
|
||||
const processIdentifier = searchParams.get('process_identifier');
|
||||
if (processIdentifier) {
|
||||
queryParams = `?process_identifier=${processIdentifier}`;
|
||||
}
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/process-instances/${modifiedProcessModelId}/${params.process_instance_id}`,
|
||||
path: `/process-instances/${modifiedProcessModelId}/${params.process_instance_id}${queryParams}`,
|
||||
successCallback: setProcessInstance,
|
||||
});
|
||||
let taskParams = '?all_tasks=true';
|
||||
if (typeof params.spiff_step !== 'undefined') {
|
||||
taskParams = `${taskParams}&spiff_step=${params.spiff_step}`;
|
||||
}
|
||||
if (ability.can('GET', targetUris.processInstanceTaskListPath)) {
|
||||
let taskPath = '';
|
||||
if (ability.can('GET', targetUris.processInstanceTaskListDataPath)) {
|
||||
taskPath = `${targetUris.processInstanceTaskListDataPath}${taskParams}`;
|
||||
} else if (ability.can('GET', targetUris.processInstanceTaskListPath)) {
|
||||
taskPath = `${targetUris.processInstanceTaskListPath}${taskParams}`;
|
||||
}
|
||||
if (taskPath) {
|
||||
HttpService.makeCallToBackend({
|
||||
path: `${targetUris.processInstanceTaskListPath}${taskParams}`,
|
||||
path: taskPath,
|
||||
successCallback: setTasks,
|
||||
failureCallback: processTaskFailure,
|
||||
});
|
||||
|
@ -98,7 +122,14 @@ export default function ProcessInstanceShow() {
|
|||
setTasksCallHadError(true);
|
||||
}
|
||||
}
|
||||
}, [params, modifiedProcessModelId, permissionsLoaded, ability, targetUris]);
|
||||
}, [
|
||||
params,
|
||||
modifiedProcessModelId,
|
||||
permissionsLoaded,
|
||||
ability,
|
||||
targetUris,
|
||||
searchParams,
|
||||
]);
|
||||
|
||||
const deleteProcessInstance = () => {
|
||||
HttpService.makeCallToBackend({
|
||||
|
@ -140,12 +171,12 @@ export default function ProcessInstanceShow() {
|
|||
const getTaskIds = () => {
|
||||
const taskIds = { completed: [], readyOrWaiting: [] };
|
||||
if (tasks) {
|
||||
tasks.forEach(function getUserTasksElement(task: any) {
|
||||
tasks.forEach(function getUserTasksElement(task: ProcessInstanceTask) {
|
||||
if (task.state === 'COMPLETED') {
|
||||
(taskIds.completed as any).push(task.name);
|
||||
(taskIds.completed as any).push(task);
|
||||
}
|
||||
if (task.state === 'READY' || task.state === 'WAITING') {
|
||||
(taskIds.readyOrWaiting as any).push(task.name);
|
||||
(taskIds.readyOrWaiting as any).push(task);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -175,15 +206,18 @@ export default function ProcessInstanceShow() {
|
|||
label: any,
|
||||
distance: number
|
||||
) => {
|
||||
const processIdentifier = searchParams.get('process_identifier');
|
||||
let queryParams = '';
|
||||
if (processIdentifier) {
|
||||
queryParams = `?process_identifier=${processIdentifier}`;
|
||||
}
|
||||
return (
|
||||
<Link
|
||||
reloadDocument
|
||||
data-qa="process-instance-step-link"
|
||||
to={`/admin/process-instances/${
|
||||
params.process_model_id
|
||||
}/process-instances/${params.process_instance_id}/${
|
||||
currentSpiffStep(processInstanceToUse) + distance
|
||||
}`}
|
||||
to={`/admin/process-instances/${params.process_model_id}/${
|
||||
params.process_instance_id
|
||||
}/${currentSpiffStep(processInstanceToUse) + distance}${queryParams}`}
|
||||
>
|
||||
{label}
|
||||
</Link>
|
||||
|
@ -364,10 +398,15 @@ export default function ProcessInstanceShow() {
|
|||
}
|
||||
};
|
||||
|
||||
const handleClickedDiagramTask = (shapeElement: any) => {
|
||||
const handleClickedDiagramTask = (
|
||||
shapeElement: any,
|
||||
bpmnProcessIdentifiers: any
|
||||
) => {
|
||||
if (tasks) {
|
||||
const matchingTask: any = tasks.find(
|
||||
(task: any) => task.name === shapeElement.id
|
||||
(task: any) =>
|
||||
task.name === shapeElement.id &&
|
||||
bpmnProcessIdentifiers.includes(task.process_identifier)
|
||||
);
|
||||
if (matchingTask) {
|
||||
setTaskToDisplay(matchingTask);
|
||||
|
@ -411,7 +450,9 @@ export default function ProcessInstanceShow() {
|
|||
|
||||
const canEditTaskData = (task: any) => {
|
||||
return (
|
||||
task.state === 'READY' && showingLastSpiffStep(processInstance as any)
|
||||
ability.can('PUT', targetUris.processInstanceTaskListDataPath) &&
|
||||
task.state === 'READY' &&
|
||||
showingLastSpiffStep(processInstance as any)
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -460,7 +501,10 @@ export default function ProcessInstanceShow() {
|
|||
const taskDataButtons = (task: any) => {
|
||||
const buttons = [];
|
||||
|
||||
if (task.type === 'Script Task') {
|
||||
if (
|
||||
task.type === 'Script Task' &&
|
||||
ability.can('PUT', targetUris.processModelShowPath)
|
||||
) {
|
||||
buttons.push(
|
||||
<Button
|
||||
data-qa="create-script-unit-test-button"
|
||||
|
@ -471,19 +515,28 @@ export default function ProcessInstanceShow() {
|
|||
);
|
||||
}
|
||||
|
||||
if (task.type === 'Call Activity') {
|
||||
buttons.push(
|
||||
<Link
|
||||
data-qa="go-to-call-activity-result"
|
||||
to={`${window.location.pathname}?process_identifier=${task.call_activity_process_identifier}`}
|
||||
target="_blank"
|
||||
>
|
||||
View Call Activity Diagram
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
if (canEditTaskData(task)) {
|
||||
if (editingTaskData) {
|
||||
buttons.push(
|
||||
<Button
|
||||
data-qa="create-script-unit-test-button"
|
||||
onClick={saveTaskData}
|
||||
>
|
||||
<Button data-qa="save-task-data-button" onClick={saveTaskData}>
|
||||
Save
|
||||
</Button>
|
||||
);
|
||||
buttons.push(
|
||||
<Button
|
||||
data-qa="create-script-unit-test-button"
|
||||
data-qa="cancel-task-data-edit-button"
|
||||
onClick={cancelEditingTaskData}
|
||||
>
|
||||
Cancel
|
||||
|
@ -492,7 +545,7 @@ export default function ProcessInstanceShow() {
|
|||
} else {
|
||||
buttons.push(
|
||||
<Button
|
||||
data-qa="create-script-unit-test-button"
|
||||
data-qa="edit-task-data-button"
|
||||
onClick={() => setEditingTaskData(true)}
|
||||
>
|
||||
Edit
|
||||
|
@ -622,8 +675,8 @@ export default function ProcessInstanceShow() {
|
|||
processModelId={processModelId || ''}
|
||||
diagramXML={processInstanceToUse.bpmn_xml_file_contents || ''}
|
||||
fileName={processInstanceToUse.bpmn_xml_file_contents || ''}
|
||||
readyOrWaitingBpmnTaskIds={taskIds.readyOrWaiting}
|
||||
completedTasksBpmnIds={taskIds.completed}
|
||||
readyOrWaitingProcessInstanceTasks={taskIds.readyOrWaiting}
|
||||
completedProcessInstanceTasks={taskIds.completed}
|
||||
diagramType="readonly"
|
||||
onElementClick={handleClickedDiagramTask}
|
||||
/>
|
||||
|
|
|
@ -25,6 +25,7 @@ import {
|
|||
ProcessReference,
|
||||
} from '../interfaces';
|
||||
import ProcessSearch from '../components/ProcessSearch';
|
||||
import { Notification } from '../components/Notification';
|
||||
|
||||
export default function ProcessModelEditDiagram() {
|
||||
const [showFileNameEditor, setShowFileNameEditor] = useState(false);
|
||||
|
@ -157,6 +158,8 @@ export default function ProcessModelEditDiagram() {
|
|||
}
|
||||
};
|
||||
|
||||
const [displaySaveFileMessage, setDisplaySaveFileMessage] =
|
||||
useState<boolean>(false);
|
||||
const saveDiagram = (bpmnXML: any, fileName = params.file_name) => {
|
||||
setErrorMessage(null);
|
||||
setBpmnXmlForDiagramRendering(bpmnXML);
|
||||
|
@ -192,6 +195,7 @@ export default function ProcessModelEditDiagram() {
|
|||
// after saving the file, make sure we null out newFileName
|
||||
// so it does not get used over the params
|
||||
setNewFileName('');
|
||||
setDisplaySaveFileMessage(true);
|
||||
};
|
||||
|
||||
const onDeleteFile = (fileName = params.file_name) => {
|
||||
|
@ -819,6 +823,7 @@ export default function ProcessModelEditDiagram() {
|
|||
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}
|
||||
|
@ -836,6 +841,20 @@ export default function ProcessModelEditDiagram() {
|
|||
);
|
||||
};
|
||||
|
||||
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
|
||||
if ((bpmnXmlForDiagramRendering || !params.file_name) && processModel) {
|
||||
const processModelFileName = processModelFile ? processModelFile.name : '';
|
||||
|
@ -856,6 +875,7 @@ export default function ProcessModelEditDiagram() {
|
|||
Process Model File{processModelFile ? ': ' : ''}
|
||||
{processModelFileName}
|
||||
</h1>
|
||||
{saveFileMessage()}
|
||||
{appropriateEditor()}
|
||||
{newFileNameBox()}
|
||||
{scriptEditor()}
|
||||
|
|
|
@ -264,6 +264,7 @@ export default function ProcessModelShow() {
|
|||
</Can>
|
||||
);
|
||||
|
||||
if (!isPrimaryBpmnFile) {
|
||||
elements.push(
|
||||
<Can
|
||||
I="DELETE"
|
||||
|
@ -283,6 +284,7 @@ export default function ProcessModelShow() {
|
|||
/>
|
||||
</Can>
|
||||
);
|
||||
}
|
||||
if (processModelFile.name.match(/\.bpmn$/) && !isPrimaryBpmnFile) {
|
||||
elements.push(
|
||||
<Can I="PUT" a={targetUris.processModelShowPath} ability={ability}>
|
||||
|
@ -360,13 +362,12 @@ export default function ProcessModelShow() {
|
|||
);
|
||||
};
|
||||
|
||||
const handleFileUploadCancel = () => {
|
||||
setShowFileUploadModal(false);
|
||||
setFilesToUpload(null);
|
||||
};
|
||||
const [fileUploadEvent, setFileUploadEvent] = useState(null);
|
||||
const [duplicateFilename, setDuplicateFilename] = useState<String>('');
|
||||
const [showOverwriteConfirmationPrompt, setShowOverwriteConfirmationPrompt] =
|
||||
useState(false);
|
||||
|
||||
const handleFileUpload = (event: any) => {
|
||||
if (processModel) {
|
||||
const doFileUpload = (event: any) => {
|
||||
event.preventDefault();
|
||||
const url = `/process-models/${modifiedProcessModelId}/files`;
|
||||
const formData = new FormData();
|
||||
|
@ -378,10 +379,60 @@ export default function ProcessModelShow() {
|
|||
httpMethod: 'POST',
|
||||
postBody: formData,
|
||||
});
|
||||
}
|
||||
setFilesToUpload(null);
|
||||
};
|
||||
|
||||
const handleFileUploadCancel = () => {
|
||||
setShowFileUploadModal(false);
|
||||
setFilesToUpload(null);
|
||||
};
|
||||
const handleOverwriteFileConfirm = () => {
|
||||
setShowOverwriteConfirmationPrompt(false);
|
||||
doFileUpload(fileUploadEvent);
|
||||
};
|
||||
const handleOverwriteFileCancel = () => {
|
||||
setShowOverwriteConfirmationPrompt(false);
|
||||
setFilesToUpload(null);
|
||||
};
|
||||
|
||||
const confirmOverwriteFileDialog = () => {
|
||||
return (
|
||||
<Modal
|
||||
danger
|
||||
open={showOverwriteConfirmationPrompt}
|
||||
data-qa="file-overwrite-modal-confirmation-dialog"
|
||||
modalHeading={`Overwrite the file: ${duplicateFilename}`}
|
||||
modalLabel="Overwrite file?"
|
||||
primaryButtonText="Yes"
|
||||
secondaryButtonText="Cancel"
|
||||
onSecondarySubmit={handleOverwriteFileCancel}
|
||||
onRequestSubmit={handleOverwriteFileConfirm}
|
||||
onRequestClose={handleOverwriteFileCancel}
|
||||
/>
|
||||
);
|
||||
};
|
||||
const displayOverwriteConfirmation = (filename: String) => {
|
||||
setDuplicateFilename(filename);
|
||||
setShowOverwriteConfirmationPrompt(true);
|
||||
};
|
||||
|
||||
const checkDuplicateFile = (event: any) => {
|
||||
if (processModel && processModel.files.length > 0) {
|
||||
processModel.files.forEach((file) => {
|
||||
if (file.name === filesToUpload[0].name) {
|
||||
displayOverwriteConfirmation(file.name);
|
||||
setFileUploadEvent(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileUpload = (event: any) => {
|
||||
if (processModel) {
|
||||
checkDuplicateFile(event);
|
||||
}
|
||||
setShowFileUploadModal(false);
|
||||
};
|
||||
|
||||
const fileUploadModal = () => {
|
||||
return (
|
||||
|
@ -548,6 +599,7 @@ export default function ProcessModelShow() {
|
|||
return (
|
||||
<>
|
||||
{fileUploadModal()}
|
||||
{confirmOverwriteFileDialog()}
|
||||
<ProcessBreadcrumb
|
||||
hotCrumbs={[
|
||||
['Process Groups', '/admin'],
|
||||
|
|
|
@ -36,7 +36,20 @@ export default function ReactFormEditor() {
|
|||
return searchParams.get('file_ext') ?? 'json';
|
||||
})();
|
||||
|
||||
const editorDefaultLanguage = fileExtension === 'md' ? 'markdown' : 'json';
|
||||
const hasDiagram = fileExtension === 'bpmn' || fileExtension === 'dmn';
|
||||
|
||||
const editorDefaultLanguage = (() => {
|
||||
if (fileExtension === 'json') {
|
||||
return 'json';
|
||||
}
|
||||
if (hasDiagram) {
|
||||
return 'xml';
|
||||
}
|
||||
if (fileExtension === 'md') {
|
||||
return 'markdown';
|
||||
}
|
||||
return 'text';
|
||||
})();
|
||||
|
||||
const modifiedProcessModelId = modifyProcessIdentifierForPathParam(
|
||||
`${params.process_model_id}`
|
||||
|
@ -193,6 +206,19 @@ export default function ReactFormEditor() {
|
|||
buttonLabel="Delete"
|
||||
/>
|
||||
) : null}
|
||||
{hasDiagram ? (
|
||||
<Button
|
||||
onClick={() =>
|
||||
navigate(
|
||||
`/admin/process-models/${modifiedProcessModelId}/files/${params.file_name}`
|
||||
)
|
||||
}
|
||||
variant="danger"
|
||||
data-qa="view-diagram-button"
|
||||
>
|
||||
View Diagram
|
||||
</Button>
|
||||
) : null}
|
||||
<Editor
|
||||
height={600}
|
||||
width="auto"
|
||||
|
|
|
@ -40,7 +40,7 @@ export default function TaskShow() {
|
|||
|
||||
const { targetUris } = useUriListForPermissions();
|
||||
const permissionRequestData: PermissionsToCheck = {
|
||||
[targetUris.processInstanceTaskListPath]: ['GET'],
|
||||
[targetUris.processInstanceTaskListDataPath]: ['GET'],
|
||||
};
|
||||
const { ability, permissionsLoaded } = usePermissionFetcher(
|
||||
permissionRequestData
|
||||
|
@ -50,7 +50,7 @@ export default function TaskShow() {
|
|||
if (permissionsLoaded) {
|
||||
const processResult = (result: any) => {
|
||||
setTask(result);
|
||||
if (ability.can('GET', targetUris.processInstanceTaskListPath)) {
|
||||
if (ability.can('GET', targetUris.processInstanceTaskListDataPath)) {
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/task-data/${modifyProcessIdentifierForPathParam(
|
||||
result.process_model_identifier
|
||||
|
|
|
@ -26,7 +26,7 @@ type backendCallProps = {
|
|||
postBody?: any;
|
||||
};
|
||||
|
||||
class UnauthenticatedError extends Error {
|
||||
export class UnauthenticatedError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = 'UnauthenticatedError';
|
||||
|
|
|
@ -27,8 +27,8 @@ const doLogout = () => {
|
|||
const idToken = getIdToken();
|
||||
localStorage.removeItem('jwtAccessToken');
|
||||
localStorage.removeItem('jwtIdToken');
|
||||
const redirctUrl = `${window.location.origin}/`;
|
||||
const url = `${BACKEND_BASE_URL}/logout?redirect_url=${redirctUrl}&id_token=${idToken}`;
|
||||
const redirectUrl = `${window.location.origin}`;
|
||||
const url = `${BACKEND_BASE_URL}/logout?redirect_url=${redirectUrl}&id_token=${idToken}`;
|
||||
window.location.href = url;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue