Skip to content

Commit

Permalink
feat: add resource allocation interaction (1st step) (#10)
Browse files Browse the repository at this point in the history
When clicking on the "Allocate more resource" button, the sub-process
diagram is displayed.
- It's running task is highlighted and the path leading to it is
modified as well. This task proposed a popover which opens on hover.
- In the future, the style of some elements of the diagram will be
modified when hovering on the row of the popover.
When clicking on the "Contact-Client" button, a window alert is
displayed. In the future, it will display the 2nd process of the diagram
and it will let interact with it.


### Implementation notes

This PR introduces refactoring to better encapsulate the logic.
In particular, it provides classes that may be reused outside this demo
and served as starting point for experimental features
- BpmnElementsSearcher
- PathResolver

The case monitoring data are fully managed by dedicated classes both for
the main process and the sub-process.
Other parts of the code are still leaking the use cases at several
places. This will be refactored later to limit the size of this PR.

---------

Co-authored-by: Thomas Bouffard <[email protected]>
  • Loading branch information
assynour and tbouffard authored Mar 14, 2023
1 parent de2b7b6 commit bcf46a9
Show file tree
Hide file tree
Showing 8 changed files with 476 additions and 185 deletions.
16 changes: 0 additions & 16 deletions src/bpmn-elements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,3 @@ export function isGateway(elementId: string): boolean {
export function isEvent(elementId: string): boolean {
return events.has(elementId);
}

export function getElementIdByName(elementName: string): string | undefined {
for (const [key, value] of activities.entries()) {
if (value === elementName) {
return key;
}
}

for (const [key, value] of events.entries()) {
if (value === elementName) {
return key;
}
}

return undefined;
}
134 changes: 134 additions & 0 deletions src/case-monitoring-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import {type BpmnVisualization} from 'bpmn-visualization';
import {BpmnElementsSearcher} from './utils/bpmn-elements.js';
import {PathResolver} from './utils/paths.js';

export type CaseMonitoringData = {
executedShapes: string[];
runningActivities: string[];
enabledShapes: string[];
pendingShapes: string[];
visitedEdges: string[];
};

/**
* Simulate fetching data from an execution system.
*/
abstract class AbstractCaseMonitoringDataProvider {
protected readonly bpmnElementsSearcher: BpmnElementsSearcher;
private readonly pathResolver: PathResolver;

protected constructor(protected readonly bpmnVisualization: BpmnVisualization) {
this.bpmnElementsSearcher = new BpmnElementsSearcher(bpmnVisualization);
this.pathResolver = new PathResolver(bpmnVisualization);
}

/**
* In real life, parameters would be passed to this method, at least the `case id`.
*/
fetch(): CaseMonitoringData {
const executedShapes = this.getExecutedShapes();
const runningActivities = this.getRunningActivities();
const enabledShapes = this.getEnabledShapes();
const pendingShapes = this.getPendingShapes();

const visitedEdges = this.pathResolver.getVisitedEdges([...executedShapes, ...runningActivities, ...enabledShapes, ...pendingShapes]);
return {
executedShapes,
runningActivities,
enabledShapes,
pendingShapes,
visitedEdges,
};
}

abstract getExecutedShapes(): string[];

abstract getRunningActivities(): string[];

abstract getEnabledShapes(): string[];

abstract getPendingShapes(): string[];
}

class MainProcessCaseMonitoringDataProvider extends AbstractCaseMonitoringDataProvider {
constructor(protected readonly bpmnVisualization: BpmnVisualization) {
super(bpmnVisualization);
}

getEnabledShapes(): string[] {
return [];
}

getExecutedShapes(): string[] {
const shapes: string[] = [];
addNonNullElement(shapes, this.bpmnElementsSearcher.getElementIdByName('New POI needed')); // Start event
addNonNullElement(shapes, 'Gateway_0xh0plz'); // Parallel gateway after start event
addNonNullElement(shapes, this.bpmnElementsSearcher.getElementIdByName('Vendor creates invoice'));
addNonNullElement(shapes, this.bpmnElementsSearcher.getElementIdByName('Create Purchase Order Item'));
return shapes;
}

getPendingShapes(): string[] {
const pendingShapes: string[] = [];
pendingShapes.push('Gateway_0domayw');
return pendingShapes;
}

getRunningActivities(): string[] {
const activities: string[] = [];
addNonNullElement(activities, this.bpmnElementsSearcher.getElementIdByName('SRM subprocess'));
return activities;
}
}

class SecondaryProcessCaseMonitoringDataProvider extends AbstractCaseMonitoringDataProvider {
constructor(protected readonly bpmnVisualization: BpmnVisualization) {
super(bpmnVisualization);
}

getEnabledShapes(): string[] {
const shapes: string[] = [];
addNonNullElement(shapes, this.bpmnElementsSearcher.getElementIdByName('SRM: Awaiting Approval'));
return shapes;
}

getExecutedShapes(): string[] {
const shapes: string[] = [];
addNonNullElement(shapes, 'Event_1dnxra5'); // Start event
addNonNullElement(shapes, this.bpmnElementsSearcher.getElementIdByName('SRM: Created'));
addNonNullElement(shapes, this.bpmnElementsSearcher.getElementIdByName('SRM: Complete'));
return shapes;
}

getPendingShapes(): string[] {
return [];
}

getRunningActivities(): string[] {
return [];
}
}

export function getCaseMonitoringData(processId: string, bpmnVisualization: BpmnVisualization): CaseMonitoringData {
const caseMonitoringDataProvider = processId === 'main' ? new MainProcessCaseMonitoringDataProvider(bpmnVisualization) : new SecondaryProcessCaseMonitoringDataProvider(bpmnVisualization);

const executedShapes = caseMonitoringDataProvider.getExecutedShapes();
const runningActivities = caseMonitoringDataProvider.getRunningActivities();
const enabledShapes = caseMonitoringDataProvider.getEnabledShapes();
const pendingShapes = caseMonitoringDataProvider.getPendingShapes();

const visitedEdges = new PathResolver(bpmnVisualization).getVisitedEdges([...executedShapes, ...runningActivities, ...enabledShapes, ...pendingShapes]);
return {
executedShapes,
runningActivities,
enabledShapes,
pendingShapes,
visitedEdges,
};
}

function addNonNullElement(elements: string[], elt: string | undefined) {
if (elt) {
elements.push(elt);
}
}
Loading

0 comments on commit bcf46a9

Please sign in to comment.