-
-
0">
-
- {{ 'processManagement.readOnly' | translate }}
-
-
- {{ 'processManagement.systemProcess' | translate }}
-
-
-
+
+
+
+
+
+
+
+
+
-
-
+
+
diff --git a/projects/valtimo/process-management/src/lib/components/process-management-builder/process-management-builder.component.scss b/projects/valtimo/process-management/src/lib/components/process-management-builder/process-management-builder.component.scss
index 9b69e6608..22530d361 100644
--- a/projects/valtimo/process-management/src/lib/components/process-management-builder/process-management-builder.component.scss
+++ b/projects/valtimo/process-management/src/lib/components/process-management-builder/process-management-builder.component.scss
@@ -14,41 +14,87 @@
* limitations under the License.
*/
-$border: 1px solid #dee2e6;
+.bpmn {
+ &__container {
+ position: relative;
+ background-color: var(--cds-layer);
+ width: 100%;
+ }
-.versions {
- border: $border;
-}
+ &__modeler {
+ width: 100%;
+ height: 100%;
+ flex-direction: row;
+ padding: 1px;
+ box-sizing: border-box;
+ outline: 1px solid var(--cds-border-subtle);
+ outline-offset: -1px;
-.diagram {
- border: $border;
-}
+ // hide blue focus border bpmn
+ ::ng-deep .djs-container > svg {
+ outline: none !important;
+ }
+ }
-.process-title {
- color: #6b6b6b;
- font-size: 1.5rem;
-}
+ &__modeler-canvas {
+ width: 100%;
+ }
-.fullscreen-toggle {
- font-size: 2rem;
- > i {
- cursor: pointer;
+ &__modeler-panel {
+ width: 481px;
+ padding-left: 1px;
+ box-shadow: inset 1px 0 0 0 var(--cds-border-subtle);
+ box-sizing: border-box;
}
-}
-.modeler {
- height: 90vh;
+ &__loading {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background-color: var(--cds-background-selected);
+ }
}
-.select-version-control {
- width: 90px;
-}
+.page-header-actions {
+ display: flex;
+ width: 100%;
+ justify-content: space-between;
-#properties {
- border-left: $border;
- padding-left: 0;
+ &__left-container {
+ display: flex;
+ gap: 24px;
+ align-items: flex-end;
+ }
+
+ &__tags {
+ display: flex;
+ gap: 8px;
+ align-items: flex-end;
+ }
+
+ &__version-dropdown {
+ width: 150px;
+ }
+
+ &__buttons {
+ display: flex;
+ gap: 16px;
+ }
}
-#readOnlyCanvas {
- height: 90vh;
+::ng-deep .process-link-properties-panel {
+ padding: 8px 12px 16px 12px;
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ gap: 16px;
+
+ .cds--btn {
+ flex-shrink: 1;
+ width: 100%;
+ display: flex;
+ align-items: center;
+ }
}
diff --git a/projects/valtimo/process-management/src/lib/components/process-management-builder/process-management-builder.component.ts b/projects/valtimo/process-management/src/lib/components/process-management-builder/process-management-builder.component.ts
index 0d3d4d385..773bbde21 100644
--- a/projects/valtimo/process-management/src/lib/components/process-management-builder/process-management-builder.component.ts
+++ b/projects/valtimo/process-management/src/lib/components/process-management-builder/process-management-builder.component.ts
@@ -14,201 +14,469 @@
* limitations under the License.
*/
-import {Component, OnDestroy, OnInit, ViewEncapsulation} from '@angular/core';
-import {HttpClient} from '@angular/common/http';
-import {ProcessDefinition, ProcessService} from '@valtimo/process';
-import {AlertService, ModalService, PageTitleService} from '@valtimo/components';
+import {AfterViewInit, Component, ElementRef, OnDestroy, ViewChild} from '@angular/core';
+import {CommonModule} from '@angular/common';
+import {
+ CARBON_CONSTANTS,
+ FitPageDirectiveModule,
+ ModalService,
+ PageHeaderService,
+ PageTitleService,
+ RenderInPageHeaderDirectiveModule,
+} from '@valtimo/components';
import {ActivatedRoute, Router} from '@angular/router';
-import {BehaviorSubject, forkJoin, Observable} from 'rxjs';
-import {LayoutService} from '@valtimo/layout';
+import {
+ BehaviorSubject,
+ combineLatest,
+ filter,
+ from,
+ map,
+ Observable,
+ startWith,
+ Subject,
+ Subscription,
+ switchMap,
+ take,
+ tap,
+} from 'rxjs';
+import {ProcessDefinition, ProcessService} from '@valtimo/process';
+import {
+ ButtonModule,
+ DialogModule,
+ DropdownModule,
+ IconModule,
+ IconService,
+ ListItem,
+ LoadingModule,
+ NotificationModule,
+ NotificationService,
+ SelectModule,
+ TagModule,
+} from 'carbon-components-angular';
import Modeler from 'bpmn-js/lib/Modeler';
-import BpmnViewer from 'bpmn-js';
+import NavigatedViewer from 'bpmn-js/lib/NavigatedViewer';
+import {ReactiveFormsModule} from '@angular/forms';
+import {TranslateModule, TranslateService} from '@ngx-translate/core';
+import {Deploy16, Download16} from '@carbon/icons';
import {
BpmnPropertiesPanelModule,
BpmnPropertiesProviderModule,
CamundaPlatformPropertiesProviderModule,
} from 'bpmn-js-properties-panel';
-import CamundaBpmnModdle from 'camunda-bpmn-moddle/resources/camunda.json';
import camundaPlatformBehaviors from 'camunda-bpmn-js-behaviors/lib/camunda-platform';
-import magicPropertiesProviderModule from './customizer';
-import {ProcessLinkService, ProcessLinkStateService} from '@valtimo/process-link';
-import {ProcessManagementService} from '../../process-management.service';
+import CamundaBpmnModdle from 'camunda-bpmn-moddle/resources/camunda.json';
+import {ValtimoPropertiesProviderModule} from './panel';
+import {distinctUntilChanged} from 'rxjs/operators';
+import {isEqual} from 'lodash';
+import {ProcessManagementEditorService} from '../../services';
+import {OpenProcessLinkModalEvent, ProcessManagementWindow} from '../../models';
+import {
+ ProcessLinkButtonService,
+ ProcessLinkCreateEvent,
+ ProcessLinkEditMode,
+ ProcessLinkModule,
+ ProcessLinkService,
+ ProcessLinkStateService,
+ ProcessLinkStepService,
+} from '@valtimo/process-link';
+import {EMPTY_BPMN} from '../../constants';
@Component({
selector: 'valtimo-process-management-builder',
templateUrl: './process-management-builder.component.html',
styleUrls: ['./process-management-builder.component.scss'],
- encapsulation: ViewEncapsulation.None,
+ standalone: true,
+ imports: [
+ CommonModule,
+ FitPageDirectiveModule,
+ LoadingModule,
+ RenderInPageHeaderDirectiveModule,
+ DropdownModule,
+ ReactiveFormsModule,
+ SelectModule,
+ ButtonModule,
+ IconModule,
+ TranslateModule,
+ TagModule,
+ ProcessLinkModule,
+ ProcessLinkModule,
+ DialogModule,
+ NotificationModule,
+ ],
+ providers: [
+ ProcessManagementEditorService,
+ ProcessLinkStateService,
+ ProcessLinkStepService,
+ ProcessLinkButtonService,
+ NotificationService,
+ ],
})
-export class ProcessManagementBuilderComponent implements OnInit, OnDestroy {
- public bpmnModeler;
- public bpmnViewer;
- public processDefinitionVersions: ProcessDefinition[] | null = null;
- public selectedVersion: ProcessDefinition | null = null;
- public processKey: string | null = null;
+export class ProcessManagementBuilderComponent implements AfterViewInit, OnDestroy {
+ @ViewChild('modeler', {static: false}) modelerElementRef!: ElementRef;
+ @ViewChild('modelerPanel', {static: false}) modelerPanelElementRef!: ElementRef;
+ @ViewChild('viewer', {static: false}) viewerElementRef!: ElementRef;
+ @ViewChild('viewerPanel', {static: false}) viewerPanelElementRef!: ElementRef;
+
+ public readonly loading$ = new BehaviorSubject
(true);
+
+ private _bpmnModeler!: Modeler;
+ private _bpmnViewer!: NavigatedViewer;
+
public isReadOnlyProcess$ = new BehaviorSubject(false);
public isSystemProcess$ = new BehaviorSubject(false);
- private elementTemplateFiles: string[] = ['mailSendTask'];
+
+ public readonly selectedProcessDefinitionXml$ =
+ this.processManagementEditorService.selectionProcessDefinition$.pipe(
+ filter(selectedProcessDefinition => !!selectedProcessDefinition?.id),
+ distinctUntilChanged((previous, current) => isEqual(previous, current)),
+ tap(selectedProcessDefinition => {
+ this.loading$.next(true);
+ this.pageTitleService.setCustomPageTitle(selectedProcessDefinition.name);
+ }),
+ switchMap(selectedProcessDefinition =>
+ this.processService.getProcessDefinitionXml(selectedProcessDefinition.id)
+ ),
+ tap(result => {
+ this._bpmnModeler?.importXML(result.bpmn20Xml);
+ this._bpmnViewer?.importXML(result.bpmn20Xml);
+ this.isReadOnlyProcess$.next(result.readOnly);
+ this.isSystemProcess$.next(result.systemProcess);
+ this.loading$.next(false);
+ })
+ );
+
+ private readonly _processDefinitionKey$ = this.route.params.pipe(
+ map(params => params.key),
+ filter(key => !!key)
+ );
+
+ private readonly _reload$ = new Subject();
+
+ public readonly changesPending$ = new BehaviorSubject(false);
+
+ public readonly processDefinitionVersions$ = combineLatest([
+ this._processDefinitionKey$,
+ this._reload$.pipe(startWith(null)),
+ ]).pipe(
+ switchMap(([processDefinitionKey]) =>
+ this.processService.getProcessDefinitionVersions(processDefinitionKey)
+ ),
+ tap(processDefinitions => {
+ this.changesPending$.next(false);
+ this.setSelectedProcessDefinitionToLatest(processDefinitions);
+ })
+ );
+
+ public readonly processDefinitionVersionsListItems$: Observable = combineLatest([
+ this.processDefinitionVersions$,
+ this.processManagementEditorService.selectionProcessDefinition$,
+ this.translateService.stream('key'),
+ ]).pipe(
+ map(([processDefinitionVersions, selectionProcessDefinition]) =>
+ processDefinitionVersions
+ .map(processDefinitionVersion => ({
+ id: processDefinitionVersion.version,
+ content: `${this.translateService.instant('processManagement.version')}${processDefinitionVersion.version}`,
+ selected: selectionProcessDefinition.version === processDefinitionVersion.version,
+ processDefinitionVersion,
+ }))
+ .sort((a, b) => b.id - a.id)
+ )
+ );
+
+ public readonly compactMode$ = this.pageHeaderService.compactMode$;
+
+ public readonly creatingNewProcess$ = new BehaviorSubject(false);
+
+ private readonly _subscriptions = new Subscription();
constructor(
- private readonly http: HttpClient,
- private readonly processService: ProcessService,
- public readonly layoutService: LayoutService,
- private readonly alertService: AlertService,
private readonly route: ActivatedRoute,
- private readonly router: Router,
+ private readonly processService: ProcessService,
private readonly pageTitleService: PageTitleService,
-
+ private readonly translateService: TranslateService,
+ private readonly iconService: IconService,
+ private readonly pageHeaderService: PageHeaderService,
+ private readonly processManagementEditorService: ProcessManagementEditorService,
private readonly modalService: ModalService,
- private readonly stateService: ProcessLinkStateService,
private readonly processLinkService: ProcessLinkService,
- private readonly processManagementService: ProcessManagementService
+ private readonly processLinkStateService: ProcessLinkStateService,
+ private readonly router: Router,
+ private readonly notificationService: NotificationService
) {
- (window as any).modalService = modalService;
- (window as any).stateService = stateService;
- (window as any).processLinkService = processLinkService;
+ this.iconService.registerAll([Deploy16, Download16]);
+ (window as any as ProcessManagementWindow).processManagementEditorService =
+ processManagementEditorService;
+ (window as any as ProcessManagementWindow).translateService = translateService;
}
- ngOnInit() {
- this.init();
+ public ngAfterViewInit(): void {
+ this.initModeler();
+ this.initViewer();
+ this.subscribeToOpenProcessLinkModalEvents();
+ this.subscribeToProcessLinkUpdateEvents();
+ this.subscribeToProcessLinkCreateEvents();
+ this.subscribeToProcessLinkDeleteEvents();
+ this.processLinkStateService.setEditMode(ProcessLinkEditMode.EMIT_EVENTS);
+ this.initIfCreate();
}
- init() {
- this.processKey = this.route.snapshot.paramMap.get('key');
- forkJoin(this.getElementTemplates()).subscribe((elementTemplates: any[]) => {
- this.bpmnModeler = new Modeler({
- container: '#canvas',
- height: '90vh',
- valtimoRenderer: {
- test: 'test',
- },
- additionalModules: [
- BpmnPropertiesPanelModule,
- BpmnPropertiesProviderModule,
- CamundaPlatformPropertiesProviderModule,
- camundaPlatformBehaviors,
- magicPropertiesProviderModule,
- ],
- propertiesPanel: {
- parent: '#properties',
- },
- moddleExtensions: {
- camunda: CamundaBpmnModdle,
- },
- elementTemplates,
+ public ngOnDestroy(): void {
+ this._bpmnModeler?.destroy();
+ this._bpmnViewer?.destroy();
+ this._subscriptions.unsubscribe();
+ }
+
+ public deployChanges(isReadOnlyProcess: boolean): void {
+ combineLatest([
+ isReadOnlyProcess ? from(this._bpmnViewer.saveXML()) : from(this._bpmnModeler.saveXML()),
+ this.processManagementEditorService.processLinksForSelectedDefinition$,
+ this.processManagementEditorService.selectionProcessDefinition$,
+ ])
+ .pipe(
+ take(1),
+ switchMap(([result, processLinks, selectedProcessDefinition]) =>
+ this.processLinkService.deployProcessWithProcessLinks(
+ processLinks as ProcessLinkCreateEvent[],
+ selectedProcessDefinition.id,
+ !isReadOnlyProcess ? result.xml : null
+ )
+ )
+ )
+ .subscribe(() => {
+ this.reload();
});
- this.bpmnViewer = new BpmnViewer();
- this.bpmnViewer.attachTo('#readOnlyCanvas');
- if (this.processKey) {
- this.loadProcessVersions(this.processKey);
- this.selectedVersion = null;
- } else {
- this.loadEmptyBpmn();
- }
- });
}
- deploy(): void {
- this.bpmnModeler.saveXML((err: any, xml: any) => {
- this.processService.deployProcess(xml).subscribe(asd => {
- if (this.processKey) {
- this.loadProcessVersions(this.processKey);
- } else {
- this.router.navigate(['/processes']);
+ public deployNewProcessDefinition(): void {
+ combineLatest([
+ from(this._bpmnModeler.saveXML()),
+ this.processManagementEditorService.processLinksForSelectedDefinition$,
+ ])
+ .pipe(
+ take(1),
+ switchMap(([result, processLinks]) =>
+ this.processLinkService.deployProcessWithProcessLinks(
+ processLinks.map(link => ({
+ ...link,
+ processDefinitionId: '-',
+ })) as ProcessLinkCreateEvent[],
+ null,
+ result.xml
+ )
+ )
+ )
+ .subscribe(() => {
+ this.router.navigate(['/processes']);
+ this.notificationService.showToast({
+ caption: this.translateService.instant('formFlow.savedSuccessTitleMessage'),
+ type: 'success',
+ duration: CARBON_CONSTANTS.notificationDuration,
+ showClose: true,
+ title: this.translateService.instant('formFlow.savedSuccessTitle'),
+ });
+ });
+ }
+
+ public export(isReadOnlyProcess: boolean): void {
+ (isReadOnlyProcess ? from(this._bpmnViewer.saveXML()) : from(this._bpmnModeler.saveXML()))
+ .pipe(take(1))
+ .subscribe(result => {
+ const file = new Blob([result.xml], {type: 'text/xml'});
+ const link = document.createElement('a');
+ link.download = 'diagram.bpmn';
+ link.href = window.URL.createObjectURL(file);
+ link.click();
+ window.URL.revokeObjectURL(link.href);
+ link.remove();
+ });
+ }
+
+ public selectedVersionChange(event: {item: {processDefinitionVersion: ProcessDefinition}}): void {
+ this.processManagementEditorService.selectionProcessDefinition$
+ .pipe(take(1))
+ .subscribe(selectedVersion => {
+ if (selectedVersion.id !== event.item.processDefinitionVersion.id) {
+ this.processManagementEditorService.setSelectedProcessDefinition(
+ event?.item?.processDefinitionVersion
+ );
+ this.changesPending$.next(false);
}
- this.alertService.success('Deployment successful');
- this.selectedVersion = null;
});
- });
}
- reset() {
- this.bpmnModeler.destroy();
- this.bpmnViewer.destroy();
- this.init();
+ private setSelectedProcessDefinitionToLatest(processDefinitions: ProcessDefinition[]): void {
+ this.processManagementEditorService.setSelectedProcessDefinition(
+ processDefinitions.reduce((acc, version) => (version.version > acc.version ? version : acc))
+ );
}
- download() {
- this.bpmnModeler.saveXML((err: any, xml: any) => {
- const file = new Blob([xml], {type: 'text/xml'});
- const link = document.createElement('a');
- link.download = 'diagram.bpmn';
- link.href = window.URL.createObjectURL(file);
- link.click();
- window.URL.revokeObjectURL(link.href);
- link.remove();
+ private initModeler(): void {
+ this._bpmnModeler = new Modeler({
+ additionalModules: [
+ BpmnPropertiesPanelModule,
+ BpmnPropertiesProviderModule,
+ CamundaPlatformPropertiesProviderModule,
+ camundaPlatformBehaviors,
+ ValtimoPropertiesProviderModule,
+ ],
+ moddleExtensions: {
+ camunda: CamundaBpmnModdle,
+ },
+ propertiesPanel: {
+ parent: this.modelerPanelElementRef.nativeElement,
+ },
+ });
+
+ this._bpmnModeler?.attachTo(this.modelerElementRef.nativeElement);
+
+ this._bpmnModeler.on('commandStack.changed', () => {
+ this.changesPending$.next(true);
});
}
- loadEmptyBpmn() {
- const url = '/assets/bpmn/initial.bpmn';
- this.http
- .get(url, {
- headers: {observe: 'response'},
- responseType: 'text',
- })
- .subscribe((xml: any) => {
- this.bpmnModeler.importXML(xml);
- this.bpmnViewer.importXML(xml);
- });
+ private initViewer(): void {
+ const disableCommands = () => {
+ const commandStack = this._bpmnViewer.get('commandStack') as any;
+ const originalExecute = commandStack?.execute?.bind(commandStack);
+
+ if (commandStack?.execute) {
+ commandStack.execute = (command: string, context: any) => {
+ if (
+ command === 'elements.delete' ||
+ command === 'elements.copy' ||
+ command === 'elements.paste' ||
+ command === 'elements.create'
+ ) {
+ return;
+ }
+ originalExecute(command, context);
+ };
+ }
+ };
+
+ const DisableBpmnWriteModule = {
+ paletteProvider: ['value', {}],
+ contextPadProvider: ['value', {}],
+ directEditing: [
+ 'value',
+ {
+ registerProvider: () => {},
+ activate: () => {},
+ deactivate: () => {},
+ isActive: () => false,
+ },
+ ],
+ move: ['value', null],
+ resizeHandles: [
+ 'value',
+ {
+ addResizer: () => {},
+ removeResizers: () => {},
+ },
+ ],
+ };
+
+ this._bpmnViewer = new Modeler({
+ additionalModules: [
+ DisableBpmnWriteModule,
+ BpmnPropertiesPanelModule,
+ ValtimoPropertiesProviderModule,
+ ],
+ moddleExtensions: {
+ camunda: CamundaBpmnModdle,
+ },
+ propertiesPanel: {
+ parent: this.viewerPanelElementRef.nativeElement,
+ },
+ });
+
+ this._bpmnViewer?.attachTo(this.viewerElementRef.nativeElement);
+
+ this._bpmnViewer.on('import.done', () => {
+ disableCommands();
+ });
+
+ this._bpmnViewer.on('commandStack.changed', () => {
+ this.changesPending$.next(true);
+ });
}
- getElementTemplates(): Observable[] {
- const templateObs = [];
- for (const file of this.elementTemplateFiles) {
- templateObs.push(
- this.http.get(`/assets/bpmn/element-templates/${file}.json`, {
- headers: {observe: 'response'},
- responseType: 'json',
- })
- );
- }
- return templateObs;
+ private reload(): void {
+ this._reload$.next(null);
}
- private setLatestVersion() {
- this.selectedVersion = this.processDefinitionVersions.reduce((acc, version) =>
- version.version > acc.version ? version : acc
- );
- this.loadProcessBpmn();
- (window as any).processLinks = this.processManagementService.getProcessLinks(
- this.selectedVersion.id
- );
+ private handleUpdateEvent(event: OpenProcessLinkModalEvent): void {
+ this.modalService.setModalData(event?.modalParams);
+ this.processLinkStateService.setModalParams(event?.modalParams);
+ this.processLinkStateService.setElementName(event?.modalParams?.element?.name);
+ this.processLinkStateService.selectProcessLink(event.processLink);
+ this.processLinkStateService.showModal();
}
- loadProcessVersions(processDefinitionKey: string) {
- this.processService
- .getProcessDefinitionVersions(processDefinitionKey)
- .subscribe((processDefinitionVersions: ProcessDefinition[]) => {
- this.processDefinitionVersions = processDefinitionVersions;
- this.pageTitleService.setCustomPageTitle(
- processDefinitionVersions[processDefinitionVersions.length - 1].name
- );
- this.setLatestVersion();
+ private handleCreateEvent(event: OpenProcessLinkModalEvent): void {
+ this.processLinkService
+ .getProcessLinkCandidates(event.modalParams.element.activityListenerType)
+ .subscribe(candidates => {
+ this.modalService.setModalData(event?.modalParams);
+ this.processLinkStateService.setModalParams(event?.modalParams);
+ this.processLinkStateService.setElementName(event?.modalParams?.element?.name);
+ this.processLinkStateService.setAvailableProcessLinkTypes(candidates);
+ this.processLinkStateService.showModal();
});
}
- compareProcessDefinitions(pd1: ProcessDefinition, pd2: ProcessDefinition) {
- if (pd1 === null && pd2 === null) {
- return true;
- }
- if (pd1 === null || pd2 === null) {
- return false;
- }
- return pd1.id === pd2.id;
+ private subscribeToOpenProcessLinkModalEvents(): void {
+ this._subscriptions.add(
+ this.processManagementEditorService.openProcessLinkModalEvents$.subscribe(event => {
+ if (event.processLink) {
+ this.handleUpdateEvent(event);
+ } else {
+ this.handleCreateEvent(event);
+ }
+ })
+ );
}
- loadProcessBpmn() {
- this.processService.getProcessDefinitionXml(this.selectedVersion.id).subscribe(result => {
- this.bpmnModeler.importXML(result['bpmn20Xml']);
- this.bpmnViewer.importXML(result['bpmn20Xml']);
- this.isReadOnlyProcess$.next(result.readOnly);
- this.isSystemProcess$.next(result.systemProcess);
- });
+ private subscribeToProcessLinkUpdateEvents(): void {
+ this._subscriptions.add(
+ this.processLinkStateService.processLinkUpdateEvents$.subscribe(event => {
+ this.processManagementEditorService.updateProcessLink(event);
+ this.processLinkStateService.stopSaving();
+ this.processLinkStateService.closeModal();
+ })
+ );
}
- ngOnDestroy() {
- this.bpmnModeler.destroy();
- this.bpmnViewer.destroy();
+ private subscribeToProcessLinkCreateEvents(): void {
+ this._subscriptions.add(
+ this.processLinkStateService.processLinkCreateEvents$.subscribe(event => {
+ this.processManagementEditorService.createProcessLink(event);
+ this.processLinkStateService.stopSaving();
+ this.processLinkStateService.closeModal();
+ })
+ );
+ }
+
+ private subscribeToProcessLinkDeleteEvents(): void {
+ this._subscriptions.add(
+ this.processLinkStateService.processLinkDeleteEvents$.subscribe(event => {
+ this.processManagementEditorService.deleteProcessLink(event);
+ this.processLinkStateService.stopSaving();
+ this.processLinkStateService.closeModal();
+ })
+ );
+ }
+
+ private initIfCreate(): void {
+ const currentUrl = this.route.snapshot.url.toString();
+
+ if (!currentUrl.includes('create')) return;
+
+ this.creatingNewProcess$.next(true);
+ this._bpmnModeler?.importXML(EMPTY_BPMN);
+ this.isReadOnlyProcess$.next(false);
+ this.isSystemProcess$.next(false);
+ this.loading$.next(false);
}
}
diff --git a/projects/valtimo/process-management/src/lib/components/process-management-editor/index.ts b/projects/valtimo/process-management/src/lib/components/process-management-editor/index.ts
deleted file mode 100644
index ca8a39b00..000000000
--- a/projects/valtimo/process-management/src/lib/components/process-management-editor/index.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright 2015-2024 Ritense BV, the Netherlands.
- *
- * Licensed under EUPL, Version 1.2 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-export * from './process-management-editor.component';
diff --git a/projects/valtimo/process-management/src/lib/components/process-management-editor/process-management-editor.component.html b/projects/valtimo/process-management/src/lib/components/process-management-editor/process-management-editor.component.html
deleted file mode 100644
index eb3f819ba..000000000
--- a/projects/valtimo/process-management/src/lib/components/process-management-editor/process-management-editor.component.html
+++ /dev/null
@@ -1,116 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/projects/valtimo/process-management/src/lib/components/process-management-editor/process-management-editor.component.scss b/projects/valtimo/process-management/src/lib/components/process-management-editor/process-management-editor.component.scss
deleted file mode 100644
index 22530d361..000000000
--- a/projects/valtimo/process-management/src/lib/components/process-management-editor/process-management-editor.component.scss
+++ /dev/null
@@ -1,100 +0,0 @@
-/*!
- * Copyright 2015-2024 Ritense BV, the Netherlands.
- *
- * Licensed under EUPL, Version 1.2 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-.bpmn {
- &__container {
- position: relative;
- background-color: var(--cds-layer);
- width: 100%;
- }
-
- &__modeler {
- width: 100%;
- height: 100%;
- flex-direction: row;
- padding: 1px;
- box-sizing: border-box;
- outline: 1px solid var(--cds-border-subtle);
- outline-offset: -1px;
-
- // hide blue focus border bpmn
- ::ng-deep .djs-container > svg {
- outline: none !important;
- }
- }
-
- &__modeler-canvas {
- width: 100%;
- }
-
- &__modeler-panel {
- width: 481px;
- padding-left: 1px;
- box-shadow: inset 1px 0 0 0 var(--cds-border-subtle);
- box-sizing: border-box;
- }
-
- &__loading {
- width: 100%;
- height: 100%;
- display: flex;
- justify-content: center;
- align-items: center;
- background-color: var(--cds-background-selected);
- }
-}
-
-.page-header-actions {
- display: flex;
- width: 100%;
- justify-content: space-between;
-
- &__left-container {
- display: flex;
- gap: 24px;
- align-items: flex-end;
- }
-
- &__tags {
- display: flex;
- gap: 8px;
- align-items: flex-end;
- }
-
- &__version-dropdown {
- width: 150px;
- }
-
- &__buttons {
- display: flex;
- gap: 16px;
- }
-}
-
-::ng-deep .process-link-properties-panel {
- padding: 8px 12px 16px 12px;
- width: 100%;
- display: flex;
- flex-direction: row;
- gap: 16px;
-
- .cds--btn {
- flex-shrink: 1;
- width: 100%;
- display: flex;
- align-items: center;
- }
-}
diff --git a/projects/valtimo/process-management/src/lib/components/process-management-editor/process-management-editor.component.ts b/projects/valtimo/process-management/src/lib/components/process-management-editor/process-management-editor.component.ts
deleted file mode 100644
index 6a0f81b71..000000000
--- a/projects/valtimo/process-management/src/lib/components/process-management-editor/process-management-editor.component.ts
+++ /dev/null
@@ -1,482 +0,0 @@
-/*
- * Copyright 2015-2024 Ritense BV, the Netherlands.
- *
- * Licensed under EUPL, Version 1.2 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import {AfterViewInit, Component, ElementRef, OnDestroy, ViewChild} from '@angular/core';
-import {CommonModule} from '@angular/common';
-import {
- CARBON_CONSTANTS,
- FitPageDirectiveModule,
- ModalService,
- PageHeaderService,
- PageTitleService,
- RenderInPageHeaderDirectiveModule,
-} from '@valtimo/components';
-import {ActivatedRoute, Router} from '@angular/router';
-import {
- BehaviorSubject,
- combineLatest,
- filter,
- from,
- map,
- Observable,
- startWith,
- Subject,
- Subscription,
- switchMap,
- take,
- tap,
-} from 'rxjs';
-import {ProcessDefinition, ProcessService} from '@valtimo/process';
-import {
- ButtonModule,
- DialogModule,
- DropdownModule,
- IconModule,
- IconService,
- ListItem,
- LoadingModule,
- NotificationModule,
- NotificationService,
- SelectModule,
- TagModule,
-} from 'carbon-components-angular';
-import Modeler from 'bpmn-js/lib/Modeler';
-import NavigatedViewer from 'bpmn-js/lib/NavigatedViewer';
-import {ReactiveFormsModule} from '@angular/forms';
-import {TranslateModule, TranslateService} from '@ngx-translate/core';
-import {Deploy16, Download16} from '@carbon/icons';
-import {
- BpmnPropertiesPanelModule,
- BpmnPropertiesProviderModule,
- CamundaPlatformPropertiesProviderModule,
-} from 'bpmn-js-properties-panel';
-import camundaPlatformBehaviors from 'camunda-bpmn-js-behaviors/lib/camunda-platform';
-import CamundaBpmnModdle from 'camunda-bpmn-moddle/resources/camunda.json';
-import {ValtimoPropertiesProviderModule} from './panel';
-import {distinctUntilChanged} from 'rxjs/operators';
-import {isEqual} from 'lodash';
-import {ProcessManagementEditorService} from '../../services';
-import {OpenProcessLinkModalEvent, ProcessManagementWindow} from '../../models';
-import {
- ProcessLinkButtonService,
- ProcessLinkCreateEvent,
- ProcessLinkEditMode,
- ProcessLinkModule,
- ProcessLinkService,
- ProcessLinkStateService,
- ProcessLinkStepService,
-} from '@valtimo/process-link';
-import {EMPTY_BPMN} from '../../constants';
-
-@Component({
- selector: 'valtimo-process-management-editor',
- templateUrl: './process-management-editor.component.html',
- styleUrls: ['./process-management-editor.component.scss'],
- standalone: true,
- imports: [
- CommonModule,
- FitPageDirectiveModule,
- LoadingModule,
- RenderInPageHeaderDirectiveModule,
- DropdownModule,
- ReactiveFormsModule,
- SelectModule,
- ButtonModule,
- IconModule,
- TranslateModule,
- TagModule,
- ProcessLinkModule,
- ProcessLinkModule,
- DialogModule,
- NotificationModule,
- ],
- providers: [
- ProcessManagementEditorService,
- ProcessLinkStateService,
- ProcessLinkStepService,
- ProcessLinkButtonService,
- NotificationService,
- ],
-})
-export class ProcessManagementEditorComponent implements AfterViewInit, OnDestroy {
- @ViewChild('modeler', {static: false}) modelerElementRef!: ElementRef;
- @ViewChild('modelerPanel', {static: false}) modelerPanelElementRef!: ElementRef;
- @ViewChild('viewer', {static: false}) viewerElementRef!: ElementRef;
- @ViewChild('viewerPanel', {static: false}) viewerPanelElementRef!: ElementRef;
-
- public readonly loading$ = new BehaviorSubject(true);
-
- private _bpmnModeler!: Modeler;
- private _bpmnViewer!: NavigatedViewer;
-
- public isReadOnlyProcess$ = new BehaviorSubject(false);
- public isSystemProcess$ = new BehaviorSubject(false);
-
- public readonly selectedProcessDefinitionXml$ =
- this.processManagementEditorService.selectionProcessDefinition$.pipe(
- filter(selectedProcessDefinition => !!selectedProcessDefinition?.id),
- distinctUntilChanged((previous, current) => isEqual(previous, current)),
- tap(selectedProcessDefinition => {
- this.loading$.next(true);
- this.pageTitleService.setCustomPageTitle(selectedProcessDefinition.name);
- }),
- switchMap(selectedProcessDefinition =>
- this.processService.getProcessDefinitionXml(selectedProcessDefinition.id)
- ),
- tap(result => {
- this._bpmnModeler?.importXML(result.bpmn20Xml);
- this._bpmnViewer?.importXML(result.bpmn20Xml);
- this.isReadOnlyProcess$.next(result.readOnly);
- this.isSystemProcess$.next(result.systemProcess);
- this.loading$.next(false);
- })
- );
-
- private readonly _processDefinitionKey$ = this.route.params.pipe(
- map(params => params.key),
- filter(key => !!key)
- );
-
- private readonly _reload$ = new Subject();
-
- public readonly changesPending$ = new BehaviorSubject(false);
-
- public readonly processDefinitionVersions$ = combineLatest([
- this._processDefinitionKey$,
- this._reload$.pipe(startWith(null)),
- ]).pipe(
- switchMap(([processDefinitionKey]) =>
- this.processService.getProcessDefinitionVersions(processDefinitionKey)
- ),
- tap(processDefinitions => {
- this.changesPending$.next(false);
- this.setSelectedProcessDefinitionToLatest(processDefinitions);
- })
- );
-
- public readonly processDefinitionVersionsListItems$: Observable = combineLatest([
- this.processDefinitionVersions$,
- this.processManagementEditorService.selectionProcessDefinition$,
- this.translateService.stream('key'),
- ]).pipe(
- map(([processDefinitionVersions, selectionProcessDefinition]) =>
- processDefinitionVersions
- .map(processDefinitionVersion => ({
- id: processDefinitionVersion.version,
- content: `${this.translateService.instant('processManagement.version')}${processDefinitionVersion.version}`,
- selected: selectionProcessDefinition.version === processDefinitionVersion.version,
- processDefinitionVersion,
- }))
- .sort((a, b) => b.id - a.id)
- )
- );
-
- public readonly compactMode$ = this.pageHeaderService.compactMode$;
-
- public readonly creatingNewProcess$ = new BehaviorSubject(false);
-
- private readonly _subscriptions = new Subscription();
-
- constructor(
- private readonly route: ActivatedRoute,
- private readonly processService: ProcessService,
- private readonly pageTitleService: PageTitleService,
- private readonly translateService: TranslateService,
- private readonly iconService: IconService,
- private readonly pageHeaderService: PageHeaderService,
- private readonly processManagementEditorService: ProcessManagementEditorService,
- private readonly modalService: ModalService,
- private readonly processLinkService: ProcessLinkService,
- private readonly processLinkStateService: ProcessLinkStateService,
- private readonly router: Router,
- private readonly notificationService: NotificationService
- ) {
- this.iconService.registerAll([Deploy16, Download16]);
- (window as any as ProcessManagementWindow).processManagementEditorService =
- processManagementEditorService;
- (window as any as ProcessManagementWindow).translateService = translateService;
- }
-
- public ngAfterViewInit(): void {
- this.initModeler();
- this.initViewer();
- this.subscribeToOpenProcessLinkModalEvents();
- this.subscribeToProcessLinkUpdateEvents();
- this.subscribeToProcessLinkCreateEvents();
- this.subscribeToProcessLinkDeleteEvents();
- this.processLinkStateService.setEditMode(ProcessLinkEditMode.EMIT_EVENTS);
- this.initIfCreate();
- }
-
- public ngOnDestroy(): void {
- this._bpmnModeler?.destroy();
- this._bpmnViewer?.destroy();
- this._subscriptions.unsubscribe();
- }
-
- public deployChanges(isReadOnlyProcess: boolean): void {
- combineLatest([
- isReadOnlyProcess ? from(this._bpmnViewer.saveXML()) : from(this._bpmnModeler.saveXML()),
- this.processManagementEditorService.processLinksForSelectedDefinition$,
- this.processManagementEditorService.selectionProcessDefinition$,
- ])
- .pipe(
- take(1),
- switchMap(([result, processLinks, selectedProcessDefinition]) =>
- this.processLinkService.deployProcessWithProcessLinks(
- processLinks as ProcessLinkCreateEvent[],
- selectedProcessDefinition.id,
- !isReadOnlyProcess ? result.xml : null
- )
- )
- )
- .subscribe(() => {
- this.reload();
- });
- }
-
- public deployNewProcessDefinition(): void {
- combineLatest([
- from(this._bpmnModeler.saveXML()),
- this.processManagementEditorService.processLinksForSelectedDefinition$,
- ])
- .pipe(
- take(1),
- switchMap(([result, processLinks]) =>
- this.processLinkService.deployProcessWithProcessLinks(
- processLinks.map(link => ({
- ...link,
- processDefinitionId: '-',
- })) as ProcessLinkCreateEvent[],
- null,
- result.xml
- )
- )
- )
- .subscribe(() => {
- this.router.navigate(['/processes']);
- this.notificationService.showToast({
- caption: this.translateService.instant('formFlow.savedSuccessTitleMessage'),
- type: 'success',
- duration: CARBON_CONSTANTS.notificationDuration,
- showClose: true,
- title: this.translateService.instant('formFlow.savedSuccessTitle'),
- });
- });
- }
-
- public export(isReadOnlyProcess: boolean): void {
- (isReadOnlyProcess ? from(this._bpmnViewer.saveXML()) : from(this._bpmnModeler.saveXML()))
- .pipe(take(1))
- .subscribe(result => {
- const file = new Blob([result.xml], {type: 'text/xml'});
- const link = document.createElement('a');
- link.download = 'diagram.bpmn';
- link.href = window.URL.createObjectURL(file);
- link.click();
- window.URL.revokeObjectURL(link.href);
- link.remove();
- });
- }
-
- public selectedVersionChange(event: {item: {processDefinitionVersion: ProcessDefinition}}): void {
- this.processManagementEditorService.selectionProcessDefinition$
- .pipe(take(1))
- .subscribe(selectedVersion => {
- if (selectedVersion.id !== event.item.processDefinitionVersion.id) {
- this.processManagementEditorService.setSelectedProcessDefinition(
- event?.item?.processDefinitionVersion
- );
- this.changesPending$.next(false);
- }
- });
- }
-
- private setSelectedProcessDefinitionToLatest(processDefinitions: ProcessDefinition[]): void {
- this.processManagementEditorService.setSelectedProcessDefinition(
- processDefinitions.reduce((acc, version) => (version.version > acc.version ? version : acc))
- );
- }
-
- private initModeler(): void {
- this._bpmnModeler = new Modeler({
- additionalModules: [
- BpmnPropertiesPanelModule,
- BpmnPropertiesProviderModule,
- CamundaPlatformPropertiesProviderModule,
- camundaPlatformBehaviors,
- ValtimoPropertiesProviderModule,
- ],
- moddleExtensions: {
- camunda: CamundaBpmnModdle,
- },
- propertiesPanel: {
- parent: this.modelerPanelElementRef.nativeElement,
- },
- });
-
- this._bpmnModeler?.attachTo(this.modelerElementRef.nativeElement);
-
- this._bpmnModeler.on('commandStack.changed', () => {
- this.changesPending$.next(true);
- });
- }
-
- private initViewer(): void {
- const disableCommands = () => {
- const commandStack = this._bpmnViewer.get('commandStack') as any;
- const originalExecute = commandStack?.execute?.bind(commandStack);
-
- if (commandStack?.execute) {
- commandStack.execute = (command: string, context: any) => {
- if (
- command === 'elements.delete' ||
- command === 'elements.copy' ||
- command === 'elements.paste' ||
- command === 'elements.create'
- ) {
- return;
- }
- originalExecute(command, context);
- };
- }
- };
-
- const DisableBpmnWriteModule = {
- paletteProvider: ['value', {}],
- contextPadProvider: ['value', {}],
- directEditing: [
- 'value',
- {
- registerProvider: () => {},
- activate: () => {},
- deactivate: () => {},
- isActive: () => false,
- },
- ],
- move: ['value', null],
- resizeHandles: [
- 'value',
- {
- addResizer: () => {},
- removeResizers: () => {},
- },
- ],
- };
-
- this._bpmnViewer = new Modeler({
- additionalModules: [
- DisableBpmnWriteModule,
- BpmnPropertiesPanelModule,
- ValtimoPropertiesProviderModule,
- ],
- moddleExtensions: {
- camunda: CamundaBpmnModdle,
- },
- propertiesPanel: {
- parent: this.viewerPanelElementRef.nativeElement,
- },
- });
-
- this._bpmnViewer?.attachTo(this.viewerElementRef.nativeElement);
-
- this._bpmnViewer.on('import.done', () => {
- disableCommands();
- });
-
- this._bpmnViewer.on('commandStack.changed', () => {
- this.changesPending$.next(true);
- });
- }
-
- private reload(): void {
- this._reload$.next(null);
- }
-
- private handleUpdateEvent(event: OpenProcessLinkModalEvent): void {
- this.modalService.setModalData(event?.modalParams);
- this.processLinkStateService.setModalParams(event?.modalParams);
- this.processLinkStateService.setElementName(event?.modalParams?.element?.name);
- this.processLinkStateService.selectProcessLink(event.processLink);
- this.processLinkStateService.showModal();
- }
-
- private handleCreateEvent(event: OpenProcessLinkModalEvent): void {
- this.processLinkService
- .getProcessLinkCandidates(event.modalParams.element.activityListenerType)
- .subscribe(candidates => {
- this.modalService.setModalData(event?.modalParams);
- this.processLinkStateService.setModalParams(event?.modalParams);
- this.processLinkStateService.setElementName(event?.modalParams?.element?.name);
- this.processLinkStateService.setAvailableProcessLinkTypes(candidates);
- this.processLinkStateService.showModal();
- });
- }
-
- private subscribeToOpenProcessLinkModalEvents(): void {
- this._subscriptions.add(
- this.processManagementEditorService.openProcessLinkModalEvents$.subscribe(event => {
- if (event.processLink) {
- this.handleUpdateEvent(event);
- } else {
- this.handleCreateEvent(event);
- }
- })
- );
- }
-
- private subscribeToProcessLinkUpdateEvents(): void {
- this._subscriptions.add(
- this.processLinkStateService.processLinkUpdateEvents$.subscribe(event => {
- this.processManagementEditorService.updateProcessLink(event);
- this.processLinkStateService.stopSaving();
- this.processLinkStateService.closeModal();
- })
- );
- }
-
- private subscribeToProcessLinkCreateEvents(): void {
- this._subscriptions.add(
- this.processLinkStateService.processLinkCreateEvents$.subscribe(event => {
- this.processManagementEditorService.createProcessLink(event);
- this.processLinkStateService.stopSaving();
- this.processLinkStateService.closeModal();
- })
- );
- }
-
- private subscribeToProcessLinkDeleteEvents(): void {
- this._subscriptions.add(
- this.processLinkStateService.processLinkDeleteEvents$.subscribe(event => {
- this.processManagementEditorService.deleteProcessLink(event);
- this.processLinkStateService.stopSaving();
- this.processLinkStateService.closeModal();
- })
- );
- }
-
- private initIfCreate(): void {
- const currentUrl = this.route.snapshot.url.toString();
-
- if (!currentUrl.includes('create')) return;
-
- this.creatingNewProcess$.next(true);
- this._bpmnModeler?.importXML(EMPTY_BPMN);
- this.isReadOnlyProcess$.next(false);
- this.isSystemProcess$.next(false);
- this.loading$.next(false);
- }
-}
diff --git a/projects/valtimo/process-management/src/lib/process-management-routing.ts b/projects/valtimo/process-management/src/lib/process-management-routing.ts
index 343855b1e..284e0e413 100644
--- a/projects/valtimo/process-management/src/lib/process-management-routing.ts
+++ b/projects/valtimo/process-management/src/lib/process-management-routing.ts
@@ -18,9 +18,8 @@ import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {CommonModule} from '@angular/common';
import {AuthGuardService} from '@valtimo/security';
-import {ProcessManagementComponent} from './components/process-management/process-management.component';
import {ROLE_ADMIN} from '@valtimo/config';
-import {ProcessManagementEditorComponent} from './components/process-management-editor/process-management-editor.component';
+import {ProcessManagementBuilderComponent, ProcessManagementComponent} from './components';
const routes: Routes = [
{
@@ -31,13 +30,13 @@ const routes: Routes = [
},
{
path: 'processes/create',
- component: ProcessManagementEditorComponent,
+ component: ProcessManagementBuilderComponent,
canActivate: [AuthGuardService],
data: {title: 'Create new Process', roles: [ROLE_ADMIN]},
},
{
path: 'processes/process/:key',
- component: ProcessManagementEditorComponent,
+ component: ProcessManagementBuilderComponent,
canActivate: [AuthGuardService],
data: {title: 'Process details', roles: [ROLE_ADMIN], customPageTitle: true},
},
diff --git a/projects/valtimo/process-management/src/lib/process-management.module.ts b/projects/valtimo/process-management/src/lib/process-management.module.ts
index c817e9e8c..2c62b7b13 100644
--- a/projects/valtimo/process-management/src/lib/process-management.module.ts
+++ b/projects/valtimo/process-management/src/lib/process-management.module.ts
@@ -24,7 +24,6 @@ import {ProcessLinkModule} from '@valtimo/process-link';
import {
ProcessManagementBuilderComponent,
ProcessManagementComponent,
- ProcessManagementEditorComponent,
ProcessManagementListComponent,
ProcessManagementUploadComponent,
} from './components';
@@ -40,7 +39,6 @@ import {
@NgModule({
declarations: [
ProcessManagementComponent,
- ProcessManagementBuilderComponent,
ProcessManagementListComponent,
ProcessManagementUploadComponent,
],
@@ -52,7 +50,7 @@ import {
FormsModule,
TranslateModule,
ProcessLinkModule,
- ProcessManagementEditorComponent,
+ ProcessManagementBuilderComponent,
CarbonListModule,
ButtonModule,
IconModule,