Skip to content

[NAE-1684] Frontend component for data field caseRef #112

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: release/6.4.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,10 @@
"rxjs": "~6.6.7",
"showdown": "^2.0.3",
"tslib": "^2.0.0",
"zone.js": "~0.11.4"
"zone.js": "~0.11.4",
"panzoom": "~9.4.3",
"@netgrif/petriflow.svg": "~1.0.0",
"@netgrif/petri.svg": "~1.0.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "~13.3.1",
Expand Down
3 changes: 2 additions & 1 deletion projects/netgrif-components-core/src/assets/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@
"maxFilesSizeExceeded": "Die maximale Größe der hochgeladenen Dateien wird überschritten: ",
"notSelectedUser": "Es wurde kein Benutzer ausgewählt",
"userAssigned": "Benutzer {{userName}} wurde zugewiesen",
"userListAssigned": "Ausgewählte Benutzer {{userNames}} wurden zugewiesen"
"userListAssigned": "Ausgewählte Benutzer {{userNames}} wurden zugewiesen",
"caseNetGetFailed": "Das Abrufen des Fallnetzes ist fehlgeschlagen"
},
"values": {
"boolean": {
Expand Down
3 changes: 2 additions & 1 deletion projects/netgrif-components-core/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@
"maxFilesSizeExceeded": "The maximum size of uploaded files is exceeded: ",
"notSelectedUser": "No user has been selected",
"userAssigned": "User {{userName}} was assigned",
"userListAssigned": "Selected users {{userNames}} were assigned"
"userListAssigned": "Selected users {{userNames}} were assigned",
"caseNetGetFailed": "Getting case net failed"
},
"values": {
"boolean": {
Expand Down
3 changes: 2 additions & 1 deletion projects/netgrif-components-core/src/assets/i18n/sk.json
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@
"maxFilesSizeExceeded": "Je presiahnutá maximálna veľkosť nahrávaných súborov: ",
"notSelectedUser": "Nebol vybraný žiadny používateľ",
"userAssigned": "Používateľ {{userName}} bol pridelený",
"userListAssigned": "Vybraní používatelia {{userNames}} boli pridelení"
"userListAssigned": "Vybraní používatelia {{userNames}} boli pridelení",
"caseNetGetFailed": "Načítanie siete podľa prípadu zlyhalo"
},
"values": {
"boolean": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
import {AfterViewInit, Component, Inject, Input, OnDestroy, Optional} from '@angular/core';
import {
PetriflowArc,
PetriflowCanvasConfigurationService,
PetriflowCanvasFactoryService,
PetriflowCanvasService,
PetriflowInhibitorArc,
PetriflowPlace,
PetriflowPlaceTransitionArc, PetriflowReadArc,
PetriflowResetArc,
PetriflowTransition, PetriflowTransitionPlaceArc
} from '@netgrif/petriflow.svg';
import {
Arc,
InhibitorArc, Place,
ReadArc,
RegularPlaceTransitionArc,
RegularTransitionPlaceArc,
ResetArc
} from '@netgrif/petri.svg';
import {CaseRefField} from './models/case-ref-field';
import {PetriNetResourceService} from '../../resources/engine-endpoint/petri-net-resource.service';
import {CaseResourceService} from '../../resources/engine-endpoint/case-resource.service';
import {TransitionImport} from '../../resources/interface/transition-import';
import {PlaceImport} from '../../resources/interface/place-import';
import {ArcImport} from '../../resources/interface/arc-import';
import {ArcType} from '../../resources/interface/arc-type';
import {PetriNetImport} from '../../resources/interface/petri-net-import';
import {LoggerService} from '../../logger/services/logger.service';
import {SnackBarService} from '../../snack-bar/services/snack-bar.service';
import {TranslateService} from '@ngx-translate/core';
import {AbstractDataFieldComponent} from '../models/abstract-data-field-component';
import {NAE_INFORM_ABOUT_INVALID_DATA} from '../models/invalid-data-policy-token';
import { Subscription } from 'rxjs';

@Component({
selector: 'ncc-abstract-case-ref-field',
template: ''
})
export abstract class AbstractCaseRefFieldComponent extends AbstractDataFieldComponent implements AfterViewInit, OnDestroy {

@Input() public dataField: CaseRefField;
protected sub: Subscription;

constructor(protected _petriflowCanvasService: PetriflowCanvasService, protected _petriflowFactoryService: PetriflowCanvasFactoryService,
protected _petriflowConfigService: PetriflowCanvasConfigurationService, protected _caseResourceService: CaseResourceService,
protected _petriNetResourceService: PetriNetResourceService, protected _log: LoggerService, protected _snackBar: SnackBarService,
protected _translate: TranslateService, @Optional() @Inject(NAE_INFORM_ABOUT_INVALID_DATA) informAboutInvalidData: boolean | null) {
super(informAboutInvalidData);
}

ngAfterViewInit(): void {
this.sub = this.formControl.valueChanges.subscribe(value => {
if (value?.length > 0) {
this._petriNetResourceService.getNetByCaseId(value[0]).subscribe(net => {
this.createNet(net);
}, error => {
this._log.error('Getting net by Case ID failed in field ['+ this.dataField.stringId + ']', error);
this._snackBar.openErrorSnackBar(this._translate.instant('dataField.snackBar.caseNetGetFailed'));
});
}
});
}

protected createNet(net: PetriNetImport) {
const trans: Array<PetriflowTransition> = [];
const places: Array<PetriflowPlace> = [];
const arcs: Array<PetriflowArc<any>> = [];
let minX: number = Number.MAX_SAFE_INTEGER;
let minY: number = Number.MAX_SAFE_INTEGER;
net.transitions.forEach((value) => {
const t = this.createTransitions(value)
trans.push(t);
minX = Math.min(minX, value.position.x);
minY = Math.min(minY, value.position.y);
this.setEmptyEvents(t.canvasElement.element);
})
net.places.forEach((value) => {
const p = this.createPlace(value)
places.push(p);
minX = Math.min(minX, value.position.x);
minY = Math.min(minY, value.position.y);
this.setEmptyEvents(p.canvasElement.element);
p.canvasElement.markingTokens.forEach(markingToken => {
this.setEmptyEvents(markingToken);
});
})
net.arcs.forEach((arc) => {
const a = this.createArcs(trans, places, arc, net)
arcs.push(a);
arc.breakpoints?.forEach(value => {
minX = Math.min(minX, value.x);
minY = Math.min(minY, value.y);
});
this.setEmptyEvents(a.element.arcLine);
});
trans.forEach(value => {
if (net.assignedTasks.includes(value.canvasElement.label.textContent)) {
value.select();
}
if (this.isEnabled(value, places, arcs)) {
value.canvasElement.element.classList.add('svg-transition-enabled');
value.canvasElement.element.setAttributeNS(null, 'fill', 'yellowgreen');
value.canvasElement.element.setAttributeNS(null, 'stroke', 'green');
}
});
this._petriflowCanvasService.panzoom?.moveTo(-minX + 20, -minY + 20);
if (this.dataField.component?.properties?.lock === 'true') {
setTimeout(() => {
this._petriflowCanvasService.panzoom?.pause();
})
}
}

protected createTransitions(value: TransitionImport): PetriflowTransition {
const transition = this._petriflowFactoryService.createTransition(new DOMPoint(value.position.x, value.position.y));
transition.changeId(value.stringId);
this._petriflowConfigService.addTransitionEvents(transition);
return transition;
}

protected createPlace(value: PlaceImport): PetriflowPlace {
const place = this._petriflowFactoryService.createPlace(value.tokens, new DOMPoint(value.position.x, value.position.y));
place.changeId(value.stringId);
this._petriflowConfigService.addPlaceEvents(place);
return place;
}

protected createArcs(trans: Array<PetriflowTransition>, places: Array<PetriflowPlace>, arc: ArcImport, net: PetriNetImport) {
let source: PetriflowPlace | PetriflowTransition = trans.find(value => value.canvasElement.label.textContent === arc.sourceId);
let destination: PetriflowPlace | PetriflowTransition;
let activable: boolean = false;
if (source === undefined) {
source = places.find(value => value.canvasElement.label.textContent === arc.sourceId);
destination = trans.find(value => value.canvasElement.label.textContent === arc.destinationId);
if (net.assignedTasks.includes(destination.canvasElement.label.textContent)) {
source.select();
destination.select();
activable = true;
}
} else {
destination = places.find(value => value.canvasElement.label.textContent === arc.destinationId);
if (net.finishedTasks.includes(source.canvasElement.label.textContent)) {
source.select();
destination.select();
activable = true;
}
}
if (source === undefined || destination === undefined) {
this._log.error("Can't find source or destination for arc [" + arc.importId + "]");
} else {
const newArc: Arc = this.createArc(arc, source, destination);
const petriflowArc: PetriflowArc<Arc> = this.createPetriflowArc(arc, newArc, source);
if (activable) {
petriflowArc.select();
}
this._petriflowCanvasService.canvas.container.appendChild(newArc.container);
this._petriflowCanvasService.petriflowElementsCollection.arcs.push(petriflowArc);
return petriflowArc;
}
return undefined
}

protected createArc(arc: ArcImport, source: PetriflowTransition | PetriflowPlace, destination: PetriflowPlace | PetriflowTransition) {
if (source instanceof PetriflowPlace) {
switch (arc.type) {
case ArcType.ARC: {
return this._petriflowFactoryService.createArc(RegularPlaceTransitionArc, source.canvasElement, destination.canvasElement, arc.breakpoints, arc.multiplicity);
}
case ArcType.RESET: {
return this._petriflowFactoryService.createArc(ResetArc, source.canvasElement, destination.canvasElement, arc.breakpoints, arc.multiplicity);
}
case ArcType.INHIBITOR: {
return this._petriflowFactoryService.createArc(InhibitorArc, source.canvasElement, destination.canvasElement, arc.breakpoints, arc.multiplicity);
}
case ArcType.READ: {
return this._petriflowFactoryService.createArc(ReadArc, source.canvasElement, destination.canvasElement, arc.breakpoints, arc.multiplicity);
}
default: {
return undefined;
}
}
} else if (arc.type === ArcType.ARC) {
return this._petriflowFactoryService.createArc(RegularTransitionPlaceArc, source.canvasElement, destination.canvasElement, arc.breakpoints, arc.multiplicity);
} else {
return undefined;
}
}

protected createPetriflowArc(arc: ArcImport, newArc: Arc, source: PetriflowTransition | PetriflowPlace) {
if (source instanceof PetriflowPlace) {
switch (arc.type) {
case ArcType.ARC: {
return this._petriflowFactoryService.createArc(PetriflowPlaceTransitionArc, newArc);
}
case ArcType.RESET: {
return this._petriflowFactoryService.createArc(PetriflowResetArc, newArc);
}
case ArcType.INHIBITOR: {
return this._petriflowFactoryService.createArc(PetriflowInhibitorArc, newArc);
}
case ArcType.READ: {
return this._petriflowFactoryService.createArc(PetriflowReadArc, newArc);
}
default: {
return undefined;
}
}
} else if (arc.type === ArcType.ARC) {
return this._petriflowFactoryService.createArc(PetriflowTransitionPlaceArc, newArc);
} else {
return undefined;
}
}

protected isEnabled(t: PetriflowTransition, places: Array<PetriflowPlace>, arcs: Array<PetriflowArc<any>>): boolean {
const testMarking: Map<string, number> = new Map();

for (const place of places) {
testMarking.set(place.canvasElement.id, place.canvasElement.tokensCount);
}
for (const arc of arcs) {
if (arc.element.end.id === t.canvasElement.id && (arc instanceof PetriflowInhibitorArc) && testMarking.get((arc.element.start as Place).id) >= parseInt(arc.element.multiplicity.data, 10)) {
return false;
}
}
for (const arc of arcs) {
if (arc.element.end.id === t.canvasElement.id && (arc instanceof PetriflowReadArc) && testMarking.get((arc.element.start as Place).id) < parseInt(arc.element.multiplicity.data, 10)) {
return false;
}
}
for (const arc of arcs) {
if (arc.element.end.id === t.canvasElement.id && arc instanceof PetriflowPlaceTransitionArc) {
const place = testMarking.get((arc.element.start as Place).id)
testMarking.set((arc.element.start as Place).id, place - parseInt(arc.element.multiplicity.data, 10));
}
}
for (const place of testMarking.values()) {
if (place < 0) {
return false;
}
}

return true;
}

public getHeight() {
return (this.dataField.layout && this.dataField.layout.rows && this.dataField.layout.rows) > 1 ?
this.dataField.layout.rows * CaseRefField.FIELD_HEIGHT : CaseRefField.FIELD_HEIGHT;
}

protected setEmptyEvents(svgElement: SVGElement) {
svgElement.onmouseenter = () => {};
svgElement.onmouseleave = () => {};
}

ngOnDestroy() {
super.ngOnDestroy();
this.sub.unsubscribe();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {Behavior} from '../../models/behavior';
import {Layout} from '../../models/layout';
import {Validation} from '../../models/validation';
import {Component} from '../../models/component';
import {DataField} from '../../models/abstract-data-field';

export class CaseRefField extends DataField<Array<string>> {

Check failure on line 7 in projects/netgrif-components-core/src/lib/data-fields/case-ref-field/models/case-ref-field.ts

View workflow job for this annotation

GitHub Actions / Test with SonarCloud

Non-abstract class 'CaseRefField' does not implement inherited abstract member 'getTypedComponentType' from class 'DataField<string[]>'.

public static FIELD_HEIGHT: number = 75;

constructor(stringId: string, title: string, value: Array<string>, behavior: Behavior, placeholder?: string,
description?: string, layout?: Layout, validations?: Array<Validation>, _component?: Component, parentTaskId?: string) {
super(stringId, title, value, behavior, placeholder, description, layout, validations, _component, parentTaskId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {NgxMatDatetimePickerModule} from '@angular-material-components/datetime-
import {NgxMatMomentModule} from '@angular-material-components/moment-adapter';
import {TranslateLibModule} from '../translate/translate-lib.module';
import {DateAdapter} from '@angular/material/core';
import {PetriflowCanvasModule} from '@netgrif/petriflow.svg';

@NgModule({
imports: [
Expand All @@ -23,7 +24,8 @@ import {DateAdapter} from '@angular/material/core';
ReactiveFormsModule,
NgxMatDatetimePickerModule,
NgxMatMomentModule,
TranslateLibModule
TranslateLibModule,
PetriflowCanvasModule
],
providers: [
{provide: DateAdapter, useClass: CustomDateAdapter}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
export * from './i18n-field/i18n-text-field/abstract-i18n-text-field.component';
export * from './i18n-field/i18n-divider-field/abstract-i18n-divider-field.component';
export * from './i18n-field/abstract-i18n-errors.component';
export * from './case-ref-field/abstract-case-ref-field.component';
export * from './user-list-field/abstract-user-list-field.component';
export * from './user-list-field/user-list-default-field/abstract-user-list-default-field.component';
export * from './task-ref-field/abstract-task-ref-field.component';
Expand Down Expand Up @@ -88,9 +89,10 @@
export * from './enumeration-field/models/dynamic-enumeration-field';
export * from './filter-field/models/filter-field';
export * from './i18n-field/models/i18n-field';
export * from './case-ref-field/models/case-ref-field';
export * from './user-list-field/models/user-list-field';
export * from './user-list-field/models/user-list-value';
export * from './case-ref-field/model/case-ref-field';

Check failure on line 95 in projects/netgrif-components-core/src/lib/data-fields/public-api.ts

View workflow job for this annotation

GitHub Actions / Test with SonarCloud

Module './case-ref-field/models/case-ref-field' has already exported a member named 'CaseRefField'. Consider explicitly re-exporting to resolve the ambiguity.
export * from './string-collection-field/models/string-collection-field';

/* Interfaces */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {Page} from '../interface/page';
import {processMessageResponse} from '../../utility/process-message-response';
import {AbstractResourceService} from '../abstract-endpoint/abstract-resource.service';
import RolesAndPermissions from '../../process/rolesAndPermissions';
import {PetriNetImport} from '../interface/petri-net-import';

@Injectable({
providedIn: 'root'
Expand Down Expand Up @@ -181,4 +182,16 @@ export class PetriNetResourceService extends AbstractResourceService {
return this._resourceProvider.delete$<MessageResource>('petrinet/' + netId, this.SERVER_URL)
.pipe(switchMap(processMessageResponse));
}

/**
* get One Net by case ID
*
* **Request Type:** GET
*
* **Request URL:** {{baseUrl}}/api/petrinet/case/{id}
*/
public getNetByCaseId(caseId: string, params?: Params): Observable<PetriNetImport> {
return this._resourceProvider.get$('petrinet/case/' + caseId, this.SERVER_URL, params)
.pipe(map(r => this.changeType(r, undefined)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {Position} from './position';
import {ArcType} from './arc-type';

export interface ArcImport {
destinationId: string;
sourceId: string;
importId: string;
multiplicity: number;
stringId: string;
breakpoints: Array<Position>;
type: ArcType;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export enum ArcType {
ARC = 'arc',
RESET = 'reset',
INHIBITOR = 'inhibitor',
READ = 'read'
}
Loading
Loading