From 020ecfd57060dead30acdbe29c16f1e15f528cbb Mon Sep 17 00:00:00 2001 From: Jozef Daxner Date: Mon, 7 Apr 2025 13:08:04 +0200 Subject: [PATCH 01/13] [NAB-375] Variables with no init values referenced in arcs will cause arcs not to load - arcs which referenced data has non integer value will be displayed/represented with value 0 in edit + simulation mode --- src/app/modeler/services/model/model.service.ts | 6 ++++-- .../modeler/simulation-mode/simulation-mode.service.ts | 8 +++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/app/modeler/services/model/model.service.ts b/src/app/modeler/services/model/model.service.ts index 210ceef..1dcf27e 100644 --- a/src/app/modeler/services/model/model.service.ts +++ b/src/app/modeler/services/model/model.service.ts @@ -483,8 +483,10 @@ export class ModelService { const referencedData = model.getData(id); if (referencedData) { if (referencedData.init.value) { - return Number(referencedData.init.value); - // TODO: NAB-326 check if isFinite and >= 0 + if (/^[1-9]\d*$/.test(referencedData.init.value.trim())) { + return Number(referencedData.init.value); + } + return 0; } return 0; } diff --git a/src/app/modeler/simulation-mode/simulation-mode.service.ts b/src/app/modeler/simulation-mode/simulation-mode.service.ts index bba93bb..b57e6e1 100644 --- a/src/app/modeler/simulation-mode/simulation-mode.service.ts +++ b/src/app/modeler/simulation-mode/simulation-mode.service.ts @@ -87,7 +87,13 @@ export class SimulationModeService extends CanvasModeService { this.originalModel = new BehaviorSubject(this.modelService.model.clone()); this.originalModel.subscribe(model => { this.data = new Map(model.getArcs().filter(a => !!a.reference && !!model.getData(a.reference)) - .map(a => [a.reference, Number.parseInt(model.getData(a.reference).init?.value, 10) || 0])); + .map(a => { + const data = model.getData(a.reference); + if (!!data.init && !!data.init.value && /^[1-9]\d*$/.test(data.init.value)) { + return [a.reference, Number.parseInt(data.init.value, 10)]; + } + return [a.reference, 0]; + })); this.simulation = new BasicSimulation(model, this.data); this.renderModel(model); }); From 77a3d63b635b7eae06babe04ffdafd1b48a73daa Mon Sep 17 00:00:00 2001 From: Milan Mladoniczky <6153201+tuplle@users.noreply.github.com> Date: Wed, 14 May 2025 11:24:13 +0200 Subject: [PATCH 02/13] [NAB-378] Global role support - Add global flag support for roles and improve role ID form - Enhanced role model to include a global flag. - Updated role-detail component to handle global flag changes. - Updated `@netgrif/petriflow` dependency to 2.2.1. --- .gitignore | 1 + package.json | 2 +- .../role-detail/role-detail.component.html | 18 ++++++++++++++---- .../role-detail/role-detail.component.ts | 15 ++++++++++----- .../modeler/services/model/model.service.ts | 1 + 5 files changed, 27 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 3e26ea5..57fc171 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ Thumbs.db .scannerwork/ out/ package-lock.json +pnpm-lock.yaml ### JetBrains template # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 diff --git a/package.json b/package.json index a50c9e0..318d7fa 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "@netgrif/components": "7.0.0-beta.1", "@netgrif/components-core": "7.0.0-beta.1", "@netgrif/petri.svg": "1.1.1", - "@netgrif/petriflow": "2.2.0", + "@netgrif/petriflow": "2.2.1", "@netgrif/petriflow.svg": "1.1.1", "@ngbracket/ngx-layout": "^17.0.1", "@ngx-translate/core": "~15.0.0", diff --git a/src/app/modeler/role-mode/role-detail/role-detail.component.html b/src/app/modeler/role-mode/role-detail/role-detail.component.html index 6d576ba..efe170b 100644 --- a/src/app/modeler/role-mode/role-detail/role-detail.component.html +++ b/src/app/modeler/role-mode/role-detail/role-detail.component.html @@ -4,9 +4,11 @@ Id - - Id is required - Transition with given id already exists + + Id is required + + Transition with given id already exists +
@@ -21,9 +23,17 @@
+
+ + Is Global + +
+ +
+
diff --git a/src/app/modeler/role-mode/role-detail/role-detail.component.ts b/src/app/modeler/role-mode/role-detail/role-detail.component.ts index d4db8ee..4487718 100644 --- a/src/app/modeler/role-mode/role-detail/role-detail.component.ts +++ b/src/app/modeler/role-mode/role-detail/role-detail.component.ts @@ -13,13 +13,13 @@ import {ModelerUtils} from '../../modeler-utils'; @Component({ selector: 'nab-role-detail', templateUrl: './role-detail.component.html', - styleUrl: './role-detail.component.scss' + styleUrl: './role-detail.component.scss', }) export class RoleDetailComponent implements OnDestroy { public role: ChangedRole; public shouldSave: boolean = false; - public form: FormControl; + public roleIdForm: FormControl; public constructor( private _masterService: RoleMasterDetailService, @@ -27,7 +27,7 @@ export class RoleDetailComponent implements OnDestroy { private _router: Router, private _actionMode: ActionsModeService, private _actionsMasterDetail: ActionsMasterDetailService, - protected _historyService: HistoryService + protected _historyService: HistoryService, ) { this._masterService.getSelected$().subscribe(item => { this.saveChange(); @@ -36,9 +36,9 @@ export class RoleDetailComponent implements OnDestroy { } this.role = new ChangedRole(item.clone()); }); - this.form = new FormControl('', [ + this.roleIdForm = new FormControl('', [ Validators.required, - this.validUnique() + this.validUnique(), ]); } @@ -75,6 +75,11 @@ export class RoleDetailComponent implements OnDestroy { this.shouldSave = true; } + changeGlobalFlag($event): void { + this.role.role.global = $event.checked; + this.shouldSave = true; + } + get item(): Role { return this.service.getSelected(); } diff --git a/src/app/modeler/services/model/model.service.ts b/src/app/modeler/services/model/model.service.ts index 210ceef..ad51fa8 100644 --- a/src/app/modeler/services/model/model.service.ts +++ b/src/app/modeler/services/model/model.service.ts @@ -365,6 +365,7 @@ export class ModelService { const role = this.model.getRole(newRole.id); role.id = newRole.role.id; role.title = newRole.role.title; + role.global = newRole.role.global; this.model.removeRole(newRole.id); this.model.addRole(role); From e5daa2f0f41fd4fdbf1765e4106cd504b1b43002 Mon Sep 17 00:00:00 2001 From: Milan Mladoniczky <6153201+tuplle@users.noreply.github.com> Date: Wed, 14 May 2025 11:33:49 +0200 Subject: [PATCH 03/13] [NAB-375] Variables with no init values referenced in arcs will cause arcs not to load - Improved usage of ImportUtils.isInitValueNumber to replace regex checks for initialization values. --- .../modeler/services/model/model.service.ts | 23 ++++++++++--------- .../simulation-mode.service.ts | 10 ++++---- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/app/modeler/services/model/model.service.ts b/src/app/modeler/services/model/model.service.ts index 1dcf27e..6e73b31 100644 --- a/src/app/modeler/services/model/model.service.ts +++ b/src/app/modeler/services/model/model.service.ts @@ -6,12 +6,13 @@ import { DataType, DataVariable, I18nString, + ImportUtils, NodeElement, PetriNet, Place, Role, Transition, - XmlArcType + XmlArcType, } from '@netgrif/petriflow'; import {ModelConfig} from './model-config'; import {CanvasConfiguration} from '@netgrif/petri.svg'; @@ -29,7 +30,7 @@ import {ChangedRole} from '../../role-mode/role-detail/changed-role'; import {ModelerUtils} from '../../modeler-utils'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ModelService { private readonly _model: BehaviorSubject; @@ -59,7 +60,7 @@ export class ModelService { ]); constructor( - private arcFactory: ArcFactory + private arcFactory: ArcFactory, ) { this._model = new BehaviorSubject(undefined); this._placeChange = new Subject(); @@ -113,7 +114,7 @@ export class ModelService { this.alignPositionCoordinate(x, CanvasConfiguration.WIDTH), this.alignPositionCoordinate(y, CanvasConfiguration.HEIGHT), false, - this.nextPlaceId() + this.nextPlaceId(), ); if (this.model.getPlaces().length === 0) { place.marking = 1; @@ -177,7 +178,7 @@ export class ModelService { const transition = new Transition( this.alignPositionCoordinate(x, CanvasConfiguration.WIDTH), this.alignPositionCoordinate(y, CanvasConfiguration.HEIGHT), - this.nextTransitionId() + this.nextTransitionId(), ); this.addTransition(transition); return transition; @@ -272,10 +273,10 @@ export class ModelService { return arc; } - public newArcBreakpoint(arc: Arc, position: DOMPoint, index: number,): void { + public newArcBreakpoint(arc: Arc, position: DOMPoint, index: number): void { const breakPoint = new Breakpoint( this.alignPositionX(position.x), - this.alignPositionY(position.y) + this.alignPositionY(position.y), ); arc.breakpoints.splice(index, 0, breakPoint); this.model.lastChanged = Date.now(); @@ -455,7 +456,7 @@ export class ModelService { public alignPosition(position: DOMPoint): DOMPoint { return new DOMPoint( this.alignPositionX(position.x), - this.alignPositionY(position.y) + this.alignPositionY(position.y), ); } @@ -483,7 +484,7 @@ export class ModelService { const referencedData = model.getData(id); if (referencedData) { if (referencedData.init.value) { - if (/^[1-9]\d*$/.test(referencedData.init.value.trim())) { + if (ImportUtils.isInitValueNumber(referencedData.init)) { return Number(referencedData.init.value); } return 0; @@ -533,8 +534,8 @@ export class ModelService { .map(dg => dg.getDataRefs() .map(ref => - ModelerUtils.numberOfEventActions(ref.getEvents()) - ).reduce((sum, current) => sum + current, 0) + ModelerUtils.numberOfEventActions(ref.getEvents()), + ).reduce((sum, current) => sum + current, 0), ).reduce((sum, current) => sum + current, 0); return eventActions + dataRefActions; } diff --git a/src/app/modeler/simulation-mode/simulation-mode.service.ts b/src/app/modeler/simulation-mode/simulation-mode.service.ts index b57e6e1..9c95d00 100644 --- a/src/app/modeler/simulation-mode/simulation-mode.service.ts +++ b/src/app/modeler/simulation-mode/simulation-mode.service.ts @@ -1,6 +1,6 @@ import {Injectable, Injector} from '@angular/core'; import {BehaviorSubject} from 'rxjs'; -import {Arc, BasicSimulation, PetriNet, Place, Transition} from '@netgrif/petriflow'; +import {Arc, BasicSimulation, ImportUtils, PetriNet, Place, Transition} from '@netgrif/petriflow'; import {TutorialService} from '../../tutorial/tutorial-service'; import {ModelService} from '../services/model/model.service'; import {EventSimulationTool} from './tool/event-simulation.tool'; @@ -24,7 +24,7 @@ import {SimulationMode} from './simulation-mode'; import {CanvasPlace} from '../edit-mode/domain/canvas-place'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class SimulationModeService extends CanvasModeService { @@ -74,7 +74,7 @@ export class SimulationModeService extends CanvasModeService { new ChangeDataTool(modelService, dialog, this, router, transitionService), new ResetPositionAndZoomTool(modelService, dialog, this, router, transitionService), new GridTool(modelService, dialog, this, router, transitionService), - new SwitchLabelTool(modelService, dialog, this, router, transitionService) + new SwitchLabelTool(modelService, dialog, this, router, transitionService), ); this.switchTools.tools.forEach(t => t.bind()); this.tools = [ @@ -82,14 +82,14 @@ export class SimulationModeService extends CanvasModeService { this.defaultTool, new EventSimulationTool(modelService, dialog, this, router, transitionService), ), - this.switchTools + this.switchTools, ]; this.originalModel = new BehaviorSubject(this.modelService.model.clone()); this.originalModel.subscribe(model => { this.data = new Map(model.getArcs().filter(a => !!a.reference && !!model.getData(a.reference)) .map(a => { const data = model.getData(a.reference); - if (!!data.init && !!data.init.value && /^[1-9]\d*$/.test(data.init.value)) { + if (ImportUtils.isInitValueNumber(data.init)) { return [a.reference, Number.parseInt(data.init.value, 10)]; } return [a.reference, 0]; From c173387844ad8a9389b5f9f18a331ac27261c934 Mon Sep 17 00:00:00 2001 From: Milan Mladoniczky <6153201+tuplle@users.noreply.github.com> Date: Wed, 14 May 2025 14:32:23 +0200 Subject: [PATCH 04/13] increase version to 2.1.0-RC.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 318d7fa..fb11046 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@netgrif/application-builder", - "version": "2.1.0-RC.2", + "version": "2.1.0-RC.3", "description": "Netgrif Application Builder for building, configuring and modeling applications for Application Engine.", "homepage": "https://builder.netgrif.com", "license": "SEE LICENSE IN LICENSE FILE", From e6a40a7f59958170a71c5d0f2464ef60d09f496d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Ma=C5=BE=C3=A1ri?= Date: Wed, 28 May 2025 09:54:17 +0200 Subject: [PATCH 05/13] [NAB-382] Application dialog - add default values to Application - add form controls with validations to application edit dialog - add input errors, disable buttons if form has any errors --- .../dialog-application-edit.component.html | 17 ++++++++++------ .../dialog-application-edit.component.ts | 20 ++++++++++++++----- .../dialog-intro/dialog-intro.component.html | 4 ++-- src/app/project-builder/application.ts | 12 +++++------ 4 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.html b/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.html index cae21f2..c3fb718 100644 --- a/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.html +++ b/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.html @@ -4,17 +4,20 @@

Application

Id - + + Id is required
Version - + + Version is required
Name - + + Name is required Description @@ -79,7 +82,7 @@

{{ process[1].title.value }}

@if (!fileInputLoading) { - } @if (!exportLoading) { - @@ -102,7 +106,8 @@

{{ process[1].title.value }}

} - diff --git a/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.ts b/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.ts index 6c8886a..dac881e 100644 --- a/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.ts +++ b/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.ts @@ -25,16 +25,18 @@ import { templateUrl: './dialog-application-edit.component.html', styleUrl: './dialog-application-edit.component.scss', }) -export class DialogApplicationEditComponent implements OnInit { +export class DialogApplicationEditComponent { readonly chipSeparators = [ENTER, COMMA] as const; @ViewChild('appPkgFileInput') fileInput: ElementRef; - public form: FormControl; public fileInputLoading: boolean; public exportLoading: boolean; private packageImporter: ApplicationPackageImport; private packageExporter: ApplicationPackageExport; + public idCtrl: FormControl; + public nameCtrl: FormControl; + public versionCtrl: FormControl; constructor( public applicationService: ApplicationService, @@ -46,7 +48,13 @@ export class DialogApplicationEditComponent implements OnInit { private snackBarService: SnackBarService, private modelService: ModelService ) { - this.form = new FormControl('', [ + this.idCtrl = new FormControl('', [ + Validators.required, + ]); + this.nameCtrl = new FormControl('', [ + Validators.required, + ]); + this.versionCtrl = new FormControl('', [ Validators.required, ]); this.fileInputLoading = false; @@ -55,8 +63,6 @@ export class DialogApplicationEditComponent implements OnInit { this.packageExporter = new ApplicationPackageExport(this.exportUtils, this.exportService); } - ngOnInit(): void { - } exportApplication($event: Event) { $event.stopPropagation(); @@ -136,4 +142,8 @@ export class DialogApplicationEditComponent implements OnInit { }); } + public hasErrors(): boolean { + return this.idCtrl.invalid || this.nameCtrl.invalid || this.versionCtrl.invalid; + } + } diff --git a/src/app/dialogs/dialog-intro/dialog-intro.component.html b/src/app/dialogs/dialog-intro/dialog-intro.component.html index de7aff0..7935689 100644 --- a/src/app/dialogs/dialog-intro/dialog-intro.component.html +++ b/src/app/dialogs/dialog-intro/dialog-intro.component.html @@ -1,13 +1,13 @@

Welcome to Netgrif Application Builder

- Netgrif Application Builder (NAB) is the tool for building process driven applications using Petriflow language. + Netgrif Application Builder (NAB) is a tool for building process driven applications using Petriflow language. NAB is composed of several modules that help you in different stages of application development.

You can start where you left of or start with a fresh new application.

- @if (apps && apps.length != 0) { + @if (apps && apps.length !== 0) {
@for (app of apps; track app.name) { diff --git a/src/app/project-builder/application.ts b/src/app/project-builder/application.ts index 6c0ca1b..03654bb 100644 --- a/src/app/project-builder/application.ts +++ b/src/app/project-builder/application.ts @@ -1,22 +1,22 @@ export default class Application { // TODO validácie public appMeta: string = 'application'; - public id: string; // TODO doplniť o identifikátor a tak aby prešiel cez sanitáciu + public id: string; public name: string; - public description: string = ''; - public version: string = '1.0.0'; + public description: string; + public version: string; public author: string; public tags: Array = []; public processes: Array = []; - constructor(name: string, description: string = '', version: string = '1.0.0') { + constructor(id: string, name: string, description: string, version: string) { + this.id = id; this.name = name; this.description = description; this.version = version; } public static getEmpty(): Application { - return new Application('New Application'); + return new Application('new_application', 'New Application', '', '1.0.0'); } - } From 130ba38c7856e4125ad0134a7938abfe5b1dddcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Ma=C5=BE=C3=A1ri?= Date: Wed, 28 May 2025 12:18:55 +0200 Subject: [PATCH 06/13] [NAB-384] General - add icon to mortgage demo model - remove mortgage service from tutorial service due to cyclic dependency - always add demo mortgage as new process to application - remove unused confirm dialog --- src/app/app.component.ts | 12 +-------- src/app/app.module.ts | 2 -- .../dialog-application-edit.component.html | 2 +- .../dialog-confirm.component.html | 8 ------ .../dialog-confirm.component.scss | 0 .../dialog-confirm.component.spec.ts | 26 ------------------- .../dialog-confirm.component.ts | 9 ------- src/app/modeler/model-import-service.ts | 12 ++++----- src/app/modeler/mortgage.service.ts | 1 - src/app/tutorial/tutorial-service.ts | 15 +---------- src/assets/mortgage_simple.xml | 1 + 11 files changed, 9 insertions(+), 79 deletions(-) delete mode 100644 src/app/dialogs/dialog-confirm/dialog-confirm.component.html delete mode 100644 src/app/dialogs/dialog-confirm/dialog-confirm.component.scss delete mode 100644 src/app/dialogs/dialog-confirm/dialog-confirm.component.spec.ts delete mode 100644 src/app/dialogs/dialog-confirm/dialog-confirm.component.ts diff --git a/src/app/app.component.ts b/src/app/app.component.ts index b0a9340..d69e36e 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -5,7 +5,6 @@ import {NetgrifApplicationEngine} from '@netgrif/components-core/'; import {JoyrideService} from 'ngx-joyride'; import {AppBuilderConfigurationService} from './app-builder-configuration.service'; import {DialogApplicationEditComponent} from './dialogs/dialog-application-edit/dialog-application-edit.component'; -import {DialogConfirmComponent} from './dialogs/dialog-confirm/dialog-confirm.component'; import {DialogIntroComponent} from './dialogs/dialog-intro/dialog-intro.component'; import {ModelImportService} from './modeler/model-import-service'; import {MortgageService} from './modeler/mortgage.service'; @@ -30,12 +29,10 @@ export class AppComponent implements AfterViewInit { constructor( config: AppBuilderConfigurationService, - private router: Router, private matDialog: MatDialog, private readonly joyrideService: JoyrideService, private _mortgageService: MortgageService, private tutorialService: TutorialService, - private importService: ModelImportService, private db: DatabaseStorageService, public modelService: ModelService, public applicationService: ApplicationService, @@ -80,14 +77,7 @@ export class AppComponent implements AfterViewInit { } addMortgage() { - const dialogRef = this.matDialog.open(DialogConfirmComponent); - - dialogRef.afterClosed().subscribe(result => { - if (result === true) { - this._mortgageService.loadModel(); - this.router.navigate(['/modeler']); - } - }); + this._mortgageService.loadModel(); } help() { diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 62f36d0..be15cbb 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -16,7 +16,6 @@ import {RoleModeComponent} from './modeler/role-mode/role-mode.component'; import {ActionsModeComponent} from './modeler/actions-mode/actions-mode.component'; import {MatIconRegistry} from '@angular/material/icon'; import {environment} from '../environments/environment'; -import {DialogConfirmComponent} from './dialogs/dialog-confirm/dialog-confirm.component'; import {DialogRefactorComponent} from './dialogs/dialog-refactor/dialog-refactor.component'; import {DialogErrorsComponent} from './dialogs/dialog-errors/dialog-errors.component'; import {ExportService, ExportUtils, ImportService} from '@netgrif/petriflow'; @@ -67,7 +66,6 @@ const appRoutes: Routes = [ @NgModule({ declarations: [ AppComponent, - DialogConfirmComponent, DialogRefactorComponent, DialogErrorsComponent, DialogDeadNetComponent, diff --git a/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.html b/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.html index cae21f2..7219182 100644 --- a/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.html +++ b/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.html @@ -21,7 +21,7 @@

Application

- + Author diff --git a/src/app/dialogs/dialog-confirm/dialog-confirm.component.html b/src/app/dialogs/dialog-confirm/dialog-confirm.component.html deleted file mode 100644 index 9b7f496..0000000 --- a/src/app/dialogs/dialog-confirm/dialog-confirm.component.html +++ /dev/null @@ -1,8 +0,0 @@ -

New Model Confirmation

- - Are you sure you want to open example model ? You loose all data from actual model. - - - - - diff --git a/src/app/dialogs/dialog-confirm/dialog-confirm.component.scss b/src/app/dialogs/dialog-confirm/dialog-confirm.component.scss deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/dialogs/dialog-confirm/dialog-confirm.component.spec.ts b/src/app/dialogs/dialog-confirm/dialog-confirm.component.spec.ts deleted file mode 100644 index 7b84374..0000000 --- a/src/app/dialogs/dialog-confirm/dialog-confirm.component.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; -import {MaterialImportModule} from '../../material-import/material-import.module'; - -import {DialogConfirmComponent} from './dialog-confirm.component'; - -describe('DialogConfirmComponent', () => { - let component: DialogConfirmComponent; - let fixture: ComponentFixture; - - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [DialogConfirmComponent], - imports: [MaterialImportModule], - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(DialogConfirmComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/dialogs/dialog-confirm/dialog-confirm.component.ts b/src/app/dialogs/dialog-confirm/dialog-confirm.component.ts deleted file mode 100644 index 18c26d3..0000000 --- a/src/app/dialogs/dialog-confirm/dialog-confirm.component.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {Component} from '@angular/core'; - -@Component({ - selector: 'nab-dialog-confirm', - templateUrl: './dialog-confirm.component.html', - styleUrls: ['./dialog-confirm.component.scss'], -}) -export class DialogConfirmComponent { -} diff --git a/src/app/modeler/model-import-service.ts b/src/app/modeler/model-import-service.ts index faeed78..9816cac 100644 --- a/src/app/modeler/model-import-service.ts +++ b/src/app/modeler/model-import-service.ts @@ -4,9 +4,8 @@ import {ImportSuccessfulComponent} from './control-panel/import-successful/impor import {ImportService} from '@netgrif/petriflow'; import {MatDialog} from '@angular/material/dialog'; import {MatSnackBar} from '@angular/material/snack-bar'; +import {ApplicationService} from '../project-builder/application.service'; import {Router} from '@angular/router'; -import {ModelService} from './services/model/model.service'; -import {HistoryService} from './services/history/history.service'; @Injectable({ providedIn: 'root' @@ -15,11 +14,10 @@ export class ModelImportService { constructor( private importService: ImportService, - private modelService: ModelService, private snackBar: MatSnackBar, private dialog: MatDialog, + private _appService: ApplicationService, private router: Router, - private historyService: HistoryService ) { } @@ -42,9 +40,9 @@ export class ModelImportService { } if (petriNetResult.model !== undefined) { - this.modelService.model = petriNetResult.model; - this.historyService.save(`Model ${this.modelService.model.id} has been imported.`) + this._appService.addModel(petriNetResult.model); + this._appService.switchActiveModel(petriNetResult.model.id); + this.router.navigate(['/modeler']); } - this.router.navigate(['/modeler']); } } diff --git a/src/app/modeler/mortgage.service.ts b/src/app/modeler/mortgage.service.ts index 07696aa..2f20c7b 100644 --- a/src/app/modeler/mortgage.service.ts +++ b/src/app/modeler/mortgage.service.ts @@ -1,6 +1,5 @@ import {Injectable} from '@angular/core'; import {HttpClient} from '@angular/common/http'; -import {ModelerTabsService} from './services/modeler-tabs.service'; import {ModelImportService} from './model-import-service'; @Injectable({ diff --git a/src/app/tutorial/tutorial-service.ts b/src/app/tutorial/tutorial-service.ts index 6515f1d..f0df6fc 100644 --- a/src/app/tutorial/tutorial-service.ts +++ b/src/app/tutorial/tutorial-service.ts @@ -1,8 +1,6 @@ import {Injectable} from '@angular/core'; -import {MortgageService} from '../modeler/mortgage.service'; import {Router} from '@angular/router'; import {TutorialStep} from './tutorial-step'; -import {ModelService} from '../modeler/services/model/model.service'; @Injectable({ providedIn: 'root' @@ -27,24 +25,16 @@ export class TutorialService { bug: TutorialStep; steps: Array; onClose: () => void; - mortgageLoaded: boolean; constructor( - private mortgageService: MortgageService, private router: Router, - private modelService: ModelService ) { this.welcome = TutorialStep.of( 'welcome', 'Welcome to the Netgrif Application Builder', 'Netgrif Application Builder (NAB) is the tool for building process driven applications using Petriflow language. NAB is composed of several modules that help you in different stages of application development.', () => { - this.mortgageLoaded = false; - if (modelService.model.getTransitions().length === 0 && modelService.model.getPlaces().length === 0 && modelService.model.getArcs().length === 0 && - modelService.model.getDataSet().length === 0 && modelService.model.getTransactions().length === 0 && modelService.model.getRoles().length === 0) { - this.mortgageService.loadModel(); - this.mortgageLoaded = true; - } + // TODO: release/3.0 - load mortgage - cyclic dependency through MortgageService this.router.navigate(['/modeler']); }, () => { @@ -235,9 +225,6 @@ export class TutorialService { ]; this.onClose = () => { this.router.navigate(['/modeler']); - if (this.mortgageLoaded) { - this.modelService.model = this.modelService.newModel(); // TODO toto vytvorí nový model ktorý asi nie je treba - } }; } } diff --git a/src/assets/mortgage_simple.xml b/src/assets/mortgage_simple.xml index 1a4a2fb..224f6d6 100644 --- a/src/assets/mortgage_simple.xml +++ b/src/assets/mortgage_simple.xml @@ -3,6 +3,7 @@ 1.0.0 MTG Mortgage + house true false false From e5dda5ac7120188aa5381fc382f5a3a2b94bbe48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Ma=C5=BE=C3=A1ri?= Date: Wed, 28 May 2025 12:26:58 +0200 Subject: [PATCH 07/13] [NAB-384] General - add unique process id validation to edit process dialog --- .../dialog-model-edit.component.html | 3 ++- .../dialog-model-edit.component.ts | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/app/dialogs/dialog-model-edit/dialog-model-edit.component.html b/src/app/dialogs/dialog-model-edit/dialog-model-edit.component.html index 4611105..176f4a4 100644 --- a/src/app/dialogs/dialog-model-edit/dialog-model-edit.component.html +++ b/src/app/dialogs/dialog-model-edit/dialog-model-edit.component.html @@ -1,9 +1,10 @@ -

Edit model

+

Edit process

Id Id is required + Process with given id already exists in application Version diff --git a/src/app/dialogs/dialog-model-edit/dialog-model-edit.component.ts b/src/app/dialogs/dialog-model-edit/dialog-model-edit.component.ts index 7c13d1f..868db47 100644 --- a/src/app/dialogs/dialog-model-edit/dialog-model-edit.component.ts +++ b/src/app/dialogs/dialog-model-edit/dialog-model-edit.component.ts @@ -32,9 +32,11 @@ export class DialogModelEditComponent { private _processTool: ProcessActionsTool ) { this.model = data; - this.idCtrl = new FormControl('', [Validators.required]); + this.idCtrl = new FormControl('', [ + Validators.required, + this.validUnique() + ]); this.versionCtrl = new FormControl('', [ - // Validators.required, this.validVersion() ]); this.titleCtrl = new FormControl('', [Validators.required]); @@ -73,6 +75,16 @@ export class DialogModelEditComponent { }; } + private validUnique(): ValidatorFn { + return (fc: FormControl): { [key: string]: any } | null => { + if (this.modelService.appService.getModel(fc.value) !== undefined && fc.value !== this.model.originalElement.id) { + return ({validUnique: true}); + } else { + return null; + } + }; + } + getTags() { return Array.from(this.model.model.tags, ([key, value]) => ({ key, value })); } From 0eef67851bf97a58ef71d67e47e4cd8ca8e46a73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Ma=C5=BE=C3=A1ri?= Date: Wed, 4 Jun 2025 07:47:43 +0200 Subject: [PATCH 08/13] [NAB-381] History view - refactor history service - use SequenceGenerator for model ids --- src/app/app.component.html | 2 +- src/app/app.component.ts | 9 +++- .../dialog-application-edit.component.ts | 2 +- .../dialog-intro/dialog-intro.component.ts | 1 - .../modeler/control-panel/modes/redo-tool.ts | 2 +- .../modeler/control-panel/modes/undo-tool.ts | 2 +- .../trigger-tree/trigger-tree.component.ts | 9 +++- .../history-master-detail.service.ts | 2 +- .../services/history/history.service.ts | 54 ++++++++++++------- src/app/modeler/services/history/history.ts | 10 +++- .../modeler/services/model/model.service.ts | 2 +- .../project-builder/application.service.ts | 32 +++++------ 12 files changed, 82 insertions(+), 45 deletions(-) diff --git a/src/app/app.component.html b/src/app/app.component.html index 586c862..d7a60b3 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -19,8 +19,8 @@ @for (process of applicationService.models; track process[0]) { + diff --git a/src/app/app.component.ts b/src/app/app.component.ts index d69e36e..97573e4 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,4 @@ -import {AfterViewInit, Component, HostListener} from '@angular/core'; +import {AfterViewInit, Component, HostListener, OnInit} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; import {Router} from '@angular/router'; import {NetgrifApplicationEngine} from '@netgrif/components-core/'; @@ -18,7 +18,7 @@ import {TutorialService} from './tutorial/tutorial-service'; templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], }) -export class AppComponent implements AfterViewInit { +export class AppComponent implements OnInit, AfterViewInit { title = 'Netgrif Application Builder'; config: NetgrifApplicationEngine; @@ -40,6 +40,11 @@ export class AppComponent implements AfterViewInit { this.config = config.get(); } + ngOnInit():void { + this.applicationService.createApplication(); + this.applicationService.switchToFirst(); + } + ngAfterViewInit(): void { // TODO: NAB-326 https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API this.matDialog.open(DialogIntroComponent, { diff --git a/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.ts b/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.ts index 6c8886a..a806947 100644 --- a/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.ts +++ b/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.ts @@ -130,7 +130,7 @@ export class DialogApplicationEditComponent implements OnInit { } this.applicationService.models.set(changedModel.model.id, changedModel.model); if (changedModel) { - this.historyService.save(`Model has been changed.`); // TODO sprav historiu pre všetky procesy + this.historyService.save(`Model has been changed.`, changedModel.model); // TODO sprav historiu pre všetky procesy } } }); diff --git a/src/app/dialogs/dialog-intro/dialog-intro.component.ts b/src/app/dialogs/dialog-intro/dialog-intro.component.ts index 1635306..0436b00 100644 --- a/src/app/dialogs/dialog-intro/dialog-intro.component.ts +++ b/src/app/dialogs/dialog-intro/dialog-intro.component.ts @@ -38,7 +38,6 @@ export class DialogIntroComponent { } createNewApplication() { - this.applicationService.createApplication(); this.dialog.open(DialogApplicationEditComponent, { width: '50%', panelClass: 'dialog-width-50', diff --git a/src/app/modeler/control-panel/modes/redo-tool.ts b/src/app/modeler/control-panel/modes/redo-tool.ts index b0177ca..8565cf4 100644 --- a/src/app/modeler/control-panel/modes/redo-tool.ts +++ b/src/app/modeler/control-panel/modes/redo-tool.ts @@ -22,7 +22,7 @@ export class RedoTool extends Tool { ) ); this.disabled.next(true); - history.historyChange.subscribe(change => { + history.historyChange().subscribe(change => { this.disabled.next(change.size === 0 || change.head === change.size - 1); }); } diff --git a/src/app/modeler/control-panel/modes/undo-tool.ts b/src/app/modeler/control-panel/modes/undo-tool.ts index 5b0c7ba..9b0fc9c 100644 --- a/src/app/modeler/control-panel/modes/undo-tool.ts +++ b/src/app/modeler/control-panel/modes/undo-tool.ts @@ -22,7 +22,7 @@ export class UndoTool extends Tool { ) ); this.disabled.next(true); - history.historyChange.subscribe(change => { + history.historyChange().subscribe(change => { this.disabled.next(change.size === 0 || change.head === 0); }); } diff --git a/src/app/modeler/control-panel/trees/trigger-tree/trigger-tree.component.ts b/src/app/modeler/control-panel/trees/trigger-tree/trigger-tree.component.ts index 1b10df7..bf20bc5 100644 --- a/src/app/modeler/control-panel/trees/trigger-tree/trigger-tree.component.ts +++ b/src/app/modeler/control-panel/trees/trigger-tree/trigger-tree.component.ts @@ -22,7 +22,7 @@ interface TriggerNode { {provide: NGX_MAT_DATE_FORMATS, useValue: DATE_TIME_FORMAT} ] }) -export class TriggerTreeComponent { +export class TriggerTreeComponent implements OnInit { @Input() triggers: Array; typeOptions = [{key: 'auto', value: 'Auto'}, {key: 'user', value: 'User'}, {key: 'time', value: 'Time'}]; @@ -41,6 +41,13 @@ export class TriggerTreeComponent { this.counter = 0; } + ngOnInit() { + if (this.triggers && this.triggers.length > 0) { + this.import(); + this.import(); + } + } + import(): void { this.counter = 0; const tree = [{ diff --git a/src/app/modeler/history-mode/history-master-detail.service.ts b/src/app/modeler/history-mode/history-master-detail.service.ts index f5896ab..8591015 100644 --- a/src/app/modeler/history-mode/history-master-detail.service.ts +++ b/src/app/modeler/history-mode/history-master-detail.service.ts @@ -16,7 +16,7 @@ export class HistoryMasterDetailService extends AbstractMasterDetailService> { - return this._historyService.history.memory; + return this._historyService.history().memory; } public create(): HistoryChange { diff --git a/src/app/modeler/services/history/history.service.ts b/src/app/modeler/services/history/history.service.ts index b77fbfc..b959d16 100644 --- a/src/app/modeler/services/history/history.service.ts +++ b/src/app/modeler/services/history/history.service.ts @@ -13,15 +13,13 @@ import {RedoTool} from '../../control-panel/modes/redo-tool'; }) export class HistoryService { - private readonly _history: History; - private readonly _historyChange: Subject>; + private readonly _histories: Map>; constructor( private modelService: ModelService, private exportService: ExportService, ) { - this._history = new History(); - this._historyChange = new Subject(); + this._histories = new Map>(); } public save(message: string, model?: PetriNet): void { @@ -30,39 +28,57 @@ export class HistoryService { this.push(model.clone(), message); } - public undo(): void { - this.reloadModel(this._history.undo(), UndoTool.ID); + public undo(id= this.getId()): void { + const history = this.findById(id); + this.reloadModel(history.undo(), UndoTool.ID); } - public redo(): void { - this.reloadModel(this._history.redo(), RedoTool.ID); + public redo(id= this.getId()): void { + const history = this.findById(id); + this.reloadModel(history.redo(), RedoTool.ID); } public reload(model: PetriNet): void { - this._history.head = this._history.memory.findIndex(value => value.record === model); + this.history(model.id).head = this.history(model.id).memory.findIndex(value => value.record === model); this.reloadModel(model, ''); } + public changeId(oldId: string, newId: string): void { + this._histories.set(newId, this._histories.get(oldId)); + this._histories.delete(oldId); + } + + private getId(): string { + return this.modelService.model.id; + } + private reloadModel(model: PetriNet, message: string): PetriNet { if (model === undefined) { return undefined; } - this.historyChange.next(HistoryChange.of(this._history, message)); + this.historyChange(model.id).next(HistoryChange.of(this.history(model.id), message)); this.modelService.model = model.clone(); return model; } private push(model: PetriNet, message: string): void { - if (!model || model.lastChanged === this.currentModel?.lastChanged || this.history.memory.find(change => + if (!model || model.lastChanged === this.currentModel(model.id)?.lastChanged || this.history(model.id).memory.find(change => change.record.lastChanged === model.lastChanged) ) { return; } - const update = this._history.push(model, message); - this.historyChange.next(update); + const update = this.findById(model.id).push(model, message); + this.historyChange(model.id).next(update); this.saveToLocalStorage(model).then(); } + private findById(id: string): History { + if (!this._histories.has(id)) { + this._histories.set(id, new History()) + } + return this._histories.get(id); + } + async saveToLocalStorage(model: PetriNet): Promise { localStorage.setItem(ModelerConfig.LOCALSTORAGE.DRAFT_MODEL.KEY, this.exportService.exportXml(model)); localStorage.setItem(ModelerConfig.LOCALSTORAGE.DRAFT_MODEL.TIMESTAMP, new Date().toLocaleString()); @@ -70,15 +86,15 @@ export class HistoryService { localStorage.setItem(ModelerConfig.LOCALSTORAGE.DRAFT_MODEL.TITLE, `${model.title.value}`); } - get historyChange(): Subject> { - return this._historyChange; + public historyChange(id= this.getId()): Subject> { + return this.findById(id).change; } - get currentModel(): PetriNet { - return this._history.record; + public currentModel(id= this.getId()): PetriNet { + return this.findById(id).record; } - get history(): History { - return this._history; + public history(id= this.getId()): History { + return this.findById(id); } } diff --git a/src/app/modeler/services/history/history.ts b/src/app/modeler/services/history/history.ts index ba756a0..aa16ff5 100644 --- a/src/app/modeler/services/history/history.ts +++ b/src/app/modeler/services/history/history.ts @@ -1,14 +1,18 @@ import {HistoryChange} from './history-change'; +import {Subject} from 'rxjs'; export class History { + private _memory: Array>; private _head: number; private readonly limit: number; + private readonly _change: Subject>; constructor(limit: number = 100) { this._memory = new Array>(); - this.limit = limit; this._head = -1; + this.limit = limit; + this._change = new Subject(); } public get record(): T { @@ -68,4 +72,8 @@ export class History { get memory(): Array> { return this._memory; } + + get change(): Subject> { + return this._change; + } } diff --git a/src/app/modeler/services/model/model.service.ts b/src/app/modeler/services/model/model.service.ts index 0a5f19d..1f3965f 100644 --- a/src/app/modeler/services/model/model.service.ts +++ b/src/app/modeler/services/model/model.service.ts @@ -98,7 +98,7 @@ export class ModelService { public newModel(): PetriNet { const model = new PetriNet(); - model.id = ModelConfig.IDENTIFIER + '-' + this.appService.getAndIncrementModelSequence(); + model.id = this.appService.nextModelId(); model.version = ModelConfig.VERSION; model.title = new I18nString(ModelConfig.TITLE); model.initials = ModelConfig.INITIALS; diff --git a/src/app/project-builder/application.service.ts b/src/app/project-builder/application.service.ts index b287e20..51502dc 100644 --- a/src/app/project-builder/application.service.ts +++ b/src/app/project-builder/application.service.ts @@ -8,6 +8,8 @@ import {HistoryService} from '../modeler/services/history/history.service'; import {ModelService} from '../modeler/services/model/model.service'; import Application from './application'; import {SimulationModeService} from "../modeler/simulation-mode/simulation-mode.service"; +import {SequenceGenerator} from '../modeler/services/model/sequence-generator'; +import { ModelConfig } from '../modeler/services/model/model-config'; @Injectable({ providedIn: 'root', @@ -16,8 +18,7 @@ export class ApplicationService implements OnDestroy { private readonly _models: Map; private _application: Application; - private _modelIdSequence = 0; - + private _modelIdSequence = new SequenceGenerator(`${ModelConfig.IDENTIFIER}_`); private _modelSubscription: Subscription; constructor( @@ -58,8 +59,12 @@ export class ApplicationService implements OnDestroy { return this._models; } - getAndIncrementModelSequence(): number { - return this._modelIdSequence++; + public nextModelId(): string { + const id = this._modelIdSequence.next(); + if (this.models.has(id)) { + return this.nextModelId(); + } + return id; } getModel(id: string): PetriNet { @@ -74,17 +79,16 @@ export class ApplicationService implements OnDestroy { this._application = Application.getEmpty(); this.addNewEmptyModel(); console.log('New application created', this._application); + this._modelIdSequence.reset([]); return this._application; } private deleteModel(processId: string) { if (this.modelService.model.id === processId) { - if (this._models.size > 1) { - this.switchActiveModel(this._models.keys().next().value); - } else { + if (this._models.size <= 1) { this.addNewEmptyModel(); // nemôže byť aplikácie bez procesu - this.switchActiveModel(this._models.keys().next().value); } + this.switchActiveModel(this._models.keys().next().value); } this._models.delete(processId); this.updateProcesses(); @@ -114,19 +118,17 @@ export class ApplicationService implements OnDestroy { } addNewEmptyModel() { - const newModel = this.modelService.newModel(); - this._models.set(newModel.id, newModel); - this.updateProcesses(); - // this.modelService.model = this.modelService.newModel(); - this.historyService.save(`New model has been created.`, newModel); - console.log('New process added', newModel.id); + this.addModel(this.modelService.newModel()); } updateModelId(oldId: string, newId: string) { - if (!this._models.get(oldId)) return; + if (!this._models.get(oldId)) { + return; + } this._models.set(newId, this._models.get(oldId)); this._models.delete(oldId); this.updateProcesses(); + this.historyService.changeId(oldId, newId); console.log('Process id updated', oldId, '->', newId); } From 45d697f7217a774c89418629dfca85c464b98c1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Ma=C5=BE=C3=A1ri?= Date: Wed, 4 Jun 2025 08:51:23 +0200 Subject: [PATCH 09/13] [NAB-380] Release 3.0.0 - fix removing wrong process --- .../dialog-application-edit.component.html | 23 +++++++++--------- .../dialog-application-edit.component.ts | 24 ++++++++++++------- .../project-builder/application.service.ts | 24 +++++++------------ 3 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.html b/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.html index 1eafa6e..55414ce 100644 --- a/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.html +++ b/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.html @@ -10,7 +10,8 @@

Application

Version - + Version is required
@@ -24,7 +25,7 @@

Application

- + Author @@ -51,20 +52,20 @@

Application

Processes

- @for (process of applicationService.models; track process[0]) { - - {{ process[1].icon }} -

{{ process[1].title.value }}

+ @for (process of applicationService.modelList; track process) { + + {{ process.icon }} +

{{ process.title.value }}

- Id: {{ process[0] }},   - Initials: {{ process[1].initials }},   - Version: {{ process[1].version }} + Id: {{ process.id }},   + Initials: {{ process.initials }},   + Version: {{ process.version }}

- -
diff --git a/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.ts b/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.ts index 53aca26..eb13b11 100644 --- a/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.ts +++ b/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.ts @@ -1,5 +1,5 @@ import {COMMA, ENTER} from '@angular/cdk/keycodes'; -import {Component, ElementRef, OnInit, ViewChild} from '@angular/core'; +import {Component, ElementRef, ViewChild} from '@angular/core'; import {FormControl, Validators} from '@angular/forms'; import {MatChipInputEvent} from '@angular/material/chips'; import {MatDialog} from '@angular/material/dialog'; @@ -14,11 +14,8 @@ import {ApplicationPackageExport} from "../../project-builder/application-packag import {ModelExportService} from "../../modeler/services/model/model-export.service"; import {DialogErrorsComponent} from "../dialog-errors/dialog-errors.component"; import {ModelService} from "../../modeler/services/model/model.service"; -import { - SnackBarHorizontalPosition, - SnackBarService, - SnackBarVerticalPosition -} from "@netgrif/components-core"; +import {SnackBarHorizontalPosition, SnackBarService, SnackBarVerticalPosition} from "@netgrif/components-core"; +import {DialogDeleteModelComponent} from '../dialog-delete-model/dialog-delete-model.component'; @Component({ selector: 'nab-dialog-application-edit', @@ -71,8 +68,8 @@ export class DialogApplicationEditComponent { .catch(err => { console.error(err); }).finally(() => { - this.exportLoading = false; - }); + this.exportLoading = false; + }); } importApplication($event: Event) { @@ -136,12 +133,21 @@ export class DialogApplicationEditComponent { } this.applicationService.models.set(changedModel.model.id, changedModel.model); if (changedModel) { - this.historyService.save(`Model has been changed.`, changedModel.model); // TODO sprav historiu pre všetky procesy + this.historyService.save(`Model has been changed.`, changedModel.model); } } }); } + removeModel(processId: string) { + const dialogRef = this.dialog.open(DialogDeleteModelComponent); + dialogRef.afterClosed().subscribe(result => { + if (result === true) { + this.applicationService.removeModel(processId); + } + }); + } + public hasErrors(): boolean { return this.idCtrl.invalid || this.nameCtrl.invalid || this.versionCtrl.invalid; } diff --git a/src/app/project-builder/application.service.ts b/src/app/project-builder/application.service.ts index 51502dc..f4bf62e 100644 --- a/src/app/project-builder/application.service.ts +++ b/src/app/project-builder/application.service.ts @@ -3,13 +3,12 @@ import {MatDialog} from '@angular/material/dialog'; import {PetriNet} from '@netgrif/petriflow'; import {Subscription} from 'rxjs'; import {filter} from 'rxjs/operators'; -import {DialogDeleteModelComponent} from '../dialogs/dialog-delete-model/dialog-delete-model.component'; import {HistoryService} from '../modeler/services/history/history.service'; import {ModelService} from '../modeler/services/model/model.service'; import Application from './application'; import {SimulationModeService} from "../modeler/simulation-mode/simulation-mode.service"; import {SequenceGenerator} from '../modeler/services/model/sequence-generator'; -import { ModelConfig } from '../modeler/services/model/model-config'; +import {ModelConfig} from '../modeler/services/model/model-config'; @Injectable({ providedIn: 'root', @@ -59,6 +58,10 @@ export class ApplicationService implements OnDestroy { return this._models; } + get modelList(): Array { + return Array.from(this._models.values()); + } + public nextModelId(): string { const id = this._modelIdSequence.next(); if (this.models.has(id)) { @@ -95,19 +98,10 @@ export class ApplicationService implements OnDestroy { console.log('Process removed', processId); } - removeModel(processId: string, confirmationDialog = true) { // TODO remove cez app edit dialog nefunguje, vymaze iný prvok - if (!confirmationDialog) { - this.deleteModel(processId); - } else { - const dialogRef = this.dialog.open(DialogDeleteModelComponent); - dialogRef.afterClosed().subscribe(result => { - if (result === true) { - const oldId = this.modelService.model.id; - this.deleteModel(oldId); - this.historyService.save(`Model ${oldId} has been deleted.`, this.modelService.model); - } - }); - } + removeModel(processId: string) { + const model = this.getModel(processId); + this.deleteModel(processId); + this.historyService.save(`Model ${processId} has been deleted.`, model); } addModel(net: PetriNet): void { From e59855ab7937f51d03299616c754545b1885b39d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Ma=C5=BE=C3=A1ri?= Date: Wed, 4 Jun 2025 09:51:26 +0200 Subject: [PATCH 10/13] [NAB-380] Release 3.0.0 - fix selected process css class --- src/app/app.component.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/app.component.html b/src/app/app.component.html index d7a60b3..3d2097b 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -18,9 +18,8 @@
@for (process of applicationService.models; track process[0]) { - - From 7a029345f20dc165cc68da250689bf5158f38473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Ma=C5=BE=C3=A1ri?= Date: Fri, 6 Jun 2025 12:00:16 +0200 Subject: [PATCH 11/13] [NAB-380] Release 3.0.0 - remove unused dialog - route to editor canvas on model switch to refresh views --- src/app/app.component.html | 2 +- src/app/app.component.ts | 26 +++++-------------- src/app/app.module.ts | 4 --- .../dialog-local-storage-model.component.html | 14 ---------- .../dialog-local-storage-model.component.scss | 0 ...alog-local-storage-model.component.spec.ts | 25 ------------------ .../dialog-local-storage-model.component.ts | 22 ---------------- .../project-builder/application.service.ts | 23 ++++++++++------ 8 files changed, 23 insertions(+), 93 deletions(-) delete mode 100644 src/app/dialogs/dialog-local-storage-model/dialog-local-storage-model.component.html delete mode 100644 src/app/dialogs/dialog-local-storage-model/dialog-local-storage-model.component.scss delete mode 100644 src/app/dialogs/dialog-local-storage-model/dialog-local-storage-model.component.spec.ts delete mode 100644 src/app/dialogs/dialog-local-storage-model/dialog-local-storage-model.component.ts diff --git a/src/app/app.component.html b/src/app/app.component.html index 3d2097b..ff532c4 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -27,7 +27,7 @@ } add diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 97573e4..ee312e8 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,12 +1,10 @@ import {AfterViewInit, Component, HostListener, OnInit} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; -import {Router} from '@angular/router'; import {NetgrifApplicationEngine} from '@netgrif/components-core/'; import {JoyrideService} from 'ngx-joyride'; import {AppBuilderConfigurationService} from './app-builder-configuration.service'; import {DialogApplicationEditComponent} from './dialogs/dialog-application-edit/dialog-application-edit.component'; import {DialogIntroComponent} from './dialogs/dialog-intro/dialog-intro.component'; -import {ModelImportService} from './modeler/model-import-service'; import {MortgageService} from './modeler/mortgage.service'; import {ModelService} from './modeler/services/model/model.service'; import {ApplicationService} from './project-builder/application.service'; @@ -40,7 +38,7 @@ export class AppComponent implements OnInit, AfterViewInit { this.config = config.get(); } - ngOnInit():void { + ngOnInit(): void { this.applicationService.createApplication(); this.applicationService.switchToFirst(); } @@ -57,21 +55,7 @@ export class AppComponent implements OnInit, AfterViewInit { /*const oldModel = localStorage.getItem(ModelerConfig.LOCALSTORAGE.DRAFT_MODEL.KEY); if (!oldModel) { return; - } - const dialogRef = this.matDialog.open(DialogLocalStorageModelComponent, { - data: { - id: localStorage.getItem(ModelerConfig.LOCALSTORAGE.DRAFT_MODEL.ID), - timestamp: localStorage.getItem(ModelerConfig.LOCALSTORAGE.DRAFT_MODEL.TIMESTAMP), - title: localStorage.getItem(ModelerConfig.LOCALSTORAGE.DRAFT_MODEL.TITLE), - }, - }); - dialogRef.afterClosed().subscribe(result => { - if (result === true) { - this.importService.importFromXml(oldModel); - } else if (result === false) { - localStorage.clear(); - } - });*/ + }*/ } openApplicationDialog() { @@ -100,8 +84,12 @@ export class AppComponent implements OnInit, AfterViewInit { window.open(url, '_blank'); } - switchToProcess(processId: string) { + switchToProcess(processId: string): void { this.applicationService.switchActiveModel(processId); } + addNewEmptyModel(): void { + const model = this.applicationService.addNewEmptyModel(); + this.applicationService.switchActiveModel(model.id); + } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index be15cbb..a64ef76 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -29,9 +29,6 @@ import {DialogDeleteModelComponent} from './dialogs/dialog-delete-model/dialog-d import {DialogArcEditComponent} from './dialogs/dialog-arc-edit/dialog-arc-edit.component'; import {DialogTransitionEditComponent} from './dialogs/dialog-transition-edit/dialog-transition-edit.component'; import {SimulationModeComponent} from './modeler/simulation-mode/simulation-mode.component'; -import { - DialogLocalStorageModelComponent -} from './dialogs/dialog-local-storage-model/dialog-local-storage-model.component'; import {HistoryModeComponent} from './modeler/history-mode/history-mode.component'; import {DialogChangeDataComponent} from './dialogs/dialog-change-data/dialog-change-data.component'; import {DialogModelEditComponent} from './dialogs/dialog-model-edit/dialog-model-edit.component'; @@ -77,7 +74,6 @@ const appRoutes: Routes = [ DialogChangeDataComponent, DialogModelEditComponent, MaterialIconPickerComponent, - DialogLocalStorageModelComponent, DialogMarkingChangeComponent, DialogApplicationEditComponent, DialogIntroComponent, diff --git a/src/app/dialogs/dialog-local-storage-model/dialog-local-storage-model.component.html b/src/app/dialogs/dialog-local-storage-model/dialog-local-storage-model.component.html deleted file mode 100644 index 21e2693..0000000 --- a/src/app/dialogs/dialog-local-storage-model/dialog-local-storage-model.component.html +++ /dev/null @@ -1,14 +0,0 @@ -

Continue previous work

- - Do you wish to continue work on "{{data.title}}" [{{data.id}}] from {{data.timestamp}} - - - - - diff --git a/src/app/dialogs/dialog-local-storage-model/dialog-local-storage-model.component.scss b/src/app/dialogs/dialog-local-storage-model/dialog-local-storage-model.component.scss deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/dialogs/dialog-local-storage-model/dialog-local-storage-model.component.spec.ts b/src/app/dialogs/dialog-local-storage-model/dialog-local-storage-model.component.spec.ts deleted file mode 100644 index c5dc7bd..0000000 --- a/src/app/dialogs/dialog-local-storage-model/dialog-local-storage-model.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { DialogLocalStorageModelComponent } from './dialog-local-storage-model.component'; - -describe('DialogLocalStorageModelComponent', () => { - let component: DialogLocalStorageModelComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ DialogLocalStorageModelComponent ] - }) - .compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(DialogLocalStorageModelComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/dialogs/dialog-local-storage-model/dialog-local-storage-model.component.ts b/src/app/dialogs/dialog-local-storage-model/dialog-local-storage-model.component.ts deleted file mode 100644 index 093bf7f..0000000 --- a/src/app/dialogs/dialog-local-storage-model/dialog-local-storage-model.component.ts +++ /dev/null @@ -1,22 +0,0 @@ -import {Component, Inject, OnInit} from '@angular/core'; -import {MAT_DIALOG_DATA} from '@angular/material/dialog'; - -export interface DialogData { - id: string; - timestamp: string; - title: string; -} - -@Component({ - selector: 'nab-dialog-local-storage-model', - templateUrl: './dialog-local-storage-model.component.html', - styleUrls: ['./dialog-local-storage-model.component.scss'] -}) -export class DialogLocalStorageModelComponent { - - constructor( - @Inject(MAT_DIALOG_DATA) public data: DialogData - ) { - } - -} diff --git a/src/app/project-builder/application.service.ts b/src/app/project-builder/application.service.ts index f4bf62e..33daaf0 100644 --- a/src/app/project-builder/application.service.ts +++ b/src/app/project-builder/application.service.ts @@ -1,5 +1,4 @@ import {Injectable, OnDestroy} from '@angular/core'; -import {MatDialog} from '@angular/material/dialog'; import {PetriNet} from '@netgrif/petriflow'; import {Subscription} from 'rxjs'; import {filter} from 'rxjs/operators'; @@ -9,6 +8,8 @@ import Application from './application'; import {SimulationModeService} from "../modeler/simulation-mode/simulation-mode.service"; import {SequenceGenerator} from '../modeler/services/model/sequence-generator'; import {ModelConfig} from '../modeler/services/model/model-config'; +import {Router} from '@angular/router'; +import {EditModeService} from '../modeler/edit-mode/edit-mode.service'; @Injectable({ providedIn: 'root', @@ -23,8 +24,9 @@ export class ApplicationService implements OnDestroy { constructor( private modelService: ModelService, private historyService: HistoryService, - private dialog: MatDialog, - private simulationModeService: SimulationModeService + private simulationModeService: SimulationModeService, + private editModeService: EditModeService, + private router: Router, ) { this._models = new Map(); this._modelSubscription = modelService.modelSubject.pipe( @@ -111,8 +113,10 @@ export class ApplicationService implements OnDestroy { console.log('New process added', net.id); } - addNewEmptyModel() { - this.addModel(this.modelService.newModel()); + addNewEmptyModel(): PetriNet { + const newModel = this.modelService.newModel() + this.addModel(newModel); + return newModel; } updateModelId(oldId: string, newId: string) { @@ -127,16 +131,19 @@ export class ApplicationService implements OnDestroy { } switchActiveModel(processId: string) { - if (!this._models.get(processId)) return; + if (!this._models.get(processId)) { + return; + } this.modelService.model = this._models.get(processId); this.simulationModeService.originalModel.next(this._models.get(processId)); - this.historyService.save(`Model ${this.modelService.model.id} has been changed.`, this._models.get(processId)); + this.router.navigate(['/modeler']); + this.editModeService.renderModel(); console.log('Current process switched', processId); } switchToFirst() { if (this._application.processes.length > 0) { - this.modelService.model = this._models.get(this._application.processes[0]); + this.switchActiveModel(this._application.processes[0]); } } From 4ad51079029c8c802d28c3bd0cbb09ff843111ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Ma=C5=BE=C3=A1ri?= Date: Thu, 19 Jun 2025 12:39:46 +0200 Subject: [PATCH 12/13] [NAB-380] Release 3.0.0 - refactor app view and process list to tabs --- src/app/app.component.html | 186 +++++++++--------- src/app/app.component.scss | 10 +- src/app/app.component.ts | 29 +-- .../dialog-application-edit.component.ts | 1 + .../data-detail/data-detail.component.ts | 9 + .../data-mode/data-master-detail.service.ts | 3 +- src/styles.scss | 34 +++- 7 files changed, 153 insertions(+), 119 deletions(-) diff --git a/src/app/app.component.html b/src/app/app.component.html index ff532c4..8618d58 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,104 +1,98 @@
- - -
- -
- - - app_registration - - -
- - @for (process of applicationService.models; track process[0]) { - - {{ process[1].icon }} - - } - - add - - -
-
- - - - play_arrow - - - - - - device_hub - - - device_hub - - - - - - bug_report - - - help - - -
-
-
+ + + + + + play_arrow + + + + + + device_hub + + + device_hub + + + + + bug_report + + + help + + + + + + + + + @for (process of applicationService.models; track process[0]) { + + + @if (process[1].icon) { + {{ process[1].icon }} + } + {{ process[1].title.value }} [{{ process[1].id }}] + + + } + + + + + +
diff --git a/src/app/app.component.scss b/src/app/app.component.scss index 715971a..56f2083 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -16,7 +16,7 @@ .logo { display: flex; justify-content: center; - padding-top: 0; + padding-top: 1px; } mat-action-list { @@ -86,12 +86,12 @@ padding-top: 0; .mdc-list-item { - //padding-left: 9px; - //padding-right: 9px; + padding-left: 9px; + padding-right: 9px; height: 42px; } } -.active-process { - background-color: map-get(netgif-theme.$netgrif-blue, 900); +.tab-icon { + margin-right: 8px; } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index ee312e8..e20679f 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -10,6 +10,7 @@ import {ModelService} from './modeler/services/model/model.service'; import {ApplicationService} from './project-builder/application.service'; import {DatabaseStorageService} from './project-builder/database-storage.service'; import {TutorialService} from './tutorial/tutorial-service'; +import {MatTabChangeEvent} from '@angular/material/tabs'; @Component({ selector: 'nab-root', @@ -45,17 +46,15 @@ export class AppComponent implements OnInit, AfterViewInit { ngAfterViewInit(): void { // TODO: NAB-326 https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API - this.matDialog.open(DialogIntroComponent, { - width: '40%', - panelClass: 'dialog-width-40', - disableClose: true, - data: this.db.getAllApplications(), - }); - - /*const oldModel = localStorage.getItem(ModelerConfig.LOCALSTORAGE.DRAFT_MODEL.KEY); - if (!oldModel) { - return; - }*/ + const apps = this.db.getAllApplications(); + if (apps && apps.length > 0) { + this.matDialog.open(DialogIntroComponent, { + width: '40%', + panelClass: 'dialog-width-40', + disableClose: true, + data: apps, + }); + } } openApplicationDialog() { @@ -88,8 +87,16 @@ export class AppComponent implements OnInit, AfterViewInit { this.applicationService.switchActiveModel(processId); } + changeTab(tabEvent: MatTabChangeEvent): void { + this.switchToProcess(tabEvent.tab.textLabel); + } + addNewEmptyModel(): void { const model = this.applicationService.addNewEmptyModel(); this.applicationService.switchActiveModel(model.id); } + + activeProcessIndex(): number { + return this.applicationService.modelList.indexOf(this.applicationService.getActiveModel()) + 1; + } } diff --git a/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.ts b/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.ts index eb13b11..9279065 100644 --- a/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.ts +++ b/src/app/dialogs/dialog-application-edit/dialog-application-edit.component.ts @@ -64,6 +64,7 @@ export class DialogApplicationEditComponent { exportApplication($event: Event) { $event.stopPropagation(); this.exportLoading = true; + // TODO: NAB-380 - export tags? this.packageExporter.generatePackageFile(this.applicationService.application, this.applicationService.models) .catch(err => { console.error(err); diff --git a/src/app/modeler/data-mode/data-detail/data-detail.component.ts b/src/app/modeler/data-mode/data-detail/data-detail.component.ts index 4c3f60e..7127c40 100644 --- a/src/app/modeler/data-mode/data-detail/data-detail.component.ts +++ b/src/app/modeler/data-mode/data-detail/data-detail.component.ts @@ -187,6 +187,15 @@ export class DataDetailComponent implements OnDestroy { this.item.placeholder.value = $event.target.value; break; } + case 'init': { + const value = $event.target.value; + if (this.item.init === undefined) { + this.item.init = new I18nWithDynamic(value); + } else { + this.item.init.value = value; + } + break; + } case 'dynamic-init': { const value = $event.source.checked; if (this.item.init === undefined) { diff --git a/src/app/modeler/data-mode/data-master-detail.service.ts b/src/app/modeler/data-mode/data-master-detail.service.ts index 2d684c0..828dec7 100644 --- a/src/app/modeler/data-mode/data-master-detail.service.ts +++ b/src/app/modeler/data-mode/data-master-detail.service.ts @@ -1,5 +1,5 @@ import {Injectable} from '@angular/core'; -import {DataType, DataVariable} from '@netgrif/petriflow'; +import {DataType, DataVariable, I18nWithDynamic} from '@netgrif/petriflow'; import {ModelService} from '../services/model/model.service'; import {AbstractMasterDetailService} from '../components/master-detail/abstract-master-detail.service'; import {Sort} from '@angular/material/sort'; @@ -22,6 +22,7 @@ export class DataMasterDetailService extends AbstractMasterDetailService Date: Tue, 24 Jun 2025 11:39:33 +0200 Subject: [PATCH 13/13] [NAB-380] Release 3.0.0 - partial fix of history --- src/app/modeler/control-panel/modes/redo-tool.ts | 1 + .../modeler/services/history/history.service.ts | 14 ++++++++------ src/app/project-builder/application.service.ts | 15 +++++++++++++++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/app/modeler/control-panel/modes/redo-tool.ts b/src/app/modeler/control-panel/modes/redo-tool.ts index 8565cf4..c230fd9 100644 --- a/src/app/modeler/control-panel/modes/redo-tool.ts +++ b/src/app/modeler/control-panel/modes/redo-tool.ts @@ -22,6 +22,7 @@ export class RedoTool extends Tool { ) ); this.disabled.next(true); + // TODO: NAB-380 history.historyChange().subscribe(change => { this.disabled.next(change.size === 0 || change.head === change.size - 1); }); diff --git a/src/app/modeler/services/history/history.service.ts b/src/app/modeler/services/history/history.service.ts index b959d16..41cfda8 100644 --- a/src/app/modeler/services/history/history.service.ts +++ b/src/app/modeler/services/history/history.service.ts @@ -28,12 +28,12 @@ export class HistoryService { this.push(model.clone(), message); } - public undo(id= this.getId()): void { + public undo(id = this.getId()): void { const history = this.findById(id); this.reloadModel(history.undo(), UndoTool.ID); } - public redo(id= this.getId()): void { + public redo(id = this.getId()): void { const history = this.findById(id); this.reloadModel(history.redo(), RedoTool.ID); } @@ -57,7 +57,9 @@ export class HistoryService { return undefined; } this.historyChange(model.id).next(HistoryChange.of(this.history(model.id), message)); - this.modelService.model = model.clone(); + const newModel = model.clone(); + this.modelService.appService.updateModel(this.modelService.model.id, newModel); + this.modelService.model = newModel; return model; } @@ -86,15 +88,15 @@ export class HistoryService { localStorage.setItem(ModelerConfig.LOCALSTORAGE.DRAFT_MODEL.TITLE, `${model.title.value}`); } - public historyChange(id= this.getId()): Subject> { + public historyChange(id = this.getId()): Subject> { return this.findById(id).change; } - public currentModel(id= this.getId()): PetriNet { + public currentModel(id = this.getId()): PetriNet { return this.findById(id).record; } - public history(id= this.getId()): History { + public history(id = this.getId()): History { return this.findById(id); } } diff --git a/src/app/project-builder/application.service.ts b/src/app/project-builder/application.service.ts index 33daaf0..9f302df 100644 --- a/src/app/project-builder/application.service.ts +++ b/src/app/project-builder/application.service.ts @@ -113,6 +113,21 @@ export class ApplicationService implements OnDestroy { console.log('New process added', net.id); } + updateModel(oldId: string, model: PetriNet): void { + // TODO: NAB-380 reload model undo/redo + const oldModel = this._models.get(oldId); + if (!oldModel) { + return + } + if (oldId === model.id) { + this._models.set(oldId, model); + } else { + this._models.delete(oldId); + this._models.set(oldId, model); + } + this.updateProcesses(); + } + addNewEmptyModel(): PetriNet { const newModel = this.modelService.newModel() this.addModel(newModel);