mirror of
https://github.com/sartography/cr-connect-bpmn.git
synced 2025-01-11 17:44:32 +00:00
Merge pull request #9 from sartography/feature/activate-workflow-specs
Feature/activate workflow specs
This commit is contained in:
commit
c0c7b48c57
6
package-lock.json
generated
6
package-lock.json
generated
@ -12171,9 +12171,9 @@
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"sartography-workflow-lib": {
|
||||
"version": "0.0.72",
|
||||
"resolved": "https://registry.npmjs.org/sartography-workflow-lib/-/sartography-workflow-lib-0.0.72.tgz",
|
||||
"integrity": "sha512-l6153jMOkKPgi2dOODOdTSHdPyyZpYjuylkPt38ct4XYaaF+kiT3GadhZli3sxg8KRkYfiaJ5krZRgl3adNu7A=="
|
||||
"version": "0.0.74",
|
||||
"resolved": "https://registry.npmjs.org/sartography-workflow-lib/-/sartography-workflow-lib-0.0.74.tgz",
|
||||
"integrity": "sha512-v8Kz89ta85O1nO7ICtJnok0LbxwEofwcqG2eivmo78xR4qYGisUl68GUTYOHdctAQwNdNbyUW7DxkqpkaPb8ew=="
|
||||
},
|
||||
"sass": {
|
||||
"version": "1.23.3",
|
||||
|
@ -49,7 +49,7 @@
|
||||
"ngx-file-drop": "^8.0.8",
|
||||
"ngx-markdown": "^9.0.0",
|
||||
"rxjs": "~6.5.4",
|
||||
"sartography-workflow-lib": "^0.0.72",
|
||||
"sartography-workflow-lib": "^0.0.74",
|
||||
"tslib": "^1.11.1",
|
||||
"uuid": "^7.0.2",
|
||||
"zone.js": "^0.10.3"
|
||||
|
@ -1,6 +1,12 @@
|
||||
<div mat-dialog-content>
|
||||
<form [formGroup]="form">
|
||||
<formly-form [model]="model" [fields]="fields" [options]="options" [form]="form"></formly-form>
|
||||
<formly-form
|
||||
[model]="model"
|
||||
[fields]="fields"
|
||||
[options]="options"
|
||||
[form]="form"
|
||||
(modelChange)="onModelChange($event)"
|
||||
></formly-form>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {Component, Inject} from '@angular/core';
|
||||
import {FormGroup} from '@angular/forms';
|
||||
import {FormControl, FormGroup} from '@angular/forms';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
|
||||
import {FormlyFieldConfig, FormlyFormOptions} from '@ngx-formly/core';
|
||||
import {cleanUpFilename, FileType} from 'sartography-workflow-lib';
|
||||
@ -51,15 +51,18 @@ export class FileMetaDialogComponent {
|
||||
options: fileTypeOptions,
|
||||
},
|
||||
},
|
||||
// {
|
||||
// key: 'file',
|
||||
// type: 'file',
|
||||
// defaultValue: this.data.file,
|
||||
// templateOptions: {
|
||||
// label: 'File',
|
||||
// required: true,
|
||||
// },
|
||||
// }
|
||||
{
|
||||
key: 'file',
|
||||
type: 'file',
|
||||
defaultValue: this.data.file,
|
||||
templateOptions: {
|
||||
label: 'File',
|
||||
required: true,
|
||||
},
|
||||
modelOptions: {
|
||||
updateOn: 'change'
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
@ -72,4 +75,16 @@ export class FileMetaDialogComponent {
|
||||
this.dialogRef.close(this.model);
|
||||
}
|
||||
|
||||
onModelChange(model: any) {
|
||||
console.log('model', model);
|
||||
if (model.file && typeof model.file === 'object' && model.file instanceof Blob) {
|
||||
// Upload file
|
||||
const fileReader = new (window as any).FileReader();
|
||||
fileReader.onload = (event: ProgressEvent) => {
|
||||
const stringContent = (event.target as FileReader).result.toString();
|
||||
console.log(stringContent);
|
||||
};
|
||||
fileReader.readAsText(model.file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,11 +202,15 @@ export class DiagramComponent implements ControlValueAccessor, AfterViewInit {
|
||||
|
||||
this.modeler.on('commandStack.changed', () => this.saveDiagram());
|
||||
|
||||
this.modeler.on('views.changed', event => this.saveDiagram());
|
||||
this.modeler.on('views.changed', () => {
|
||||
this.modeler.getActiveViewer().get('eventBus').on('commandStack.changed', () => this.saveDiagram());
|
||||
this.saveDiagram();
|
||||
});
|
||||
|
||||
this.modeler.on('import.done', ({error}) => {
|
||||
if (!error) {
|
||||
const activeView = this.modeler.getActiveView();
|
||||
|
||||
if (activeView.type === 'drd') {
|
||||
this.modeler.getActiveViewer().get('canvas').zoom('fit-viewport');
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import {of} from 'rxjs';
|
||||
import {
|
||||
ApiService,
|
||||
FileMeta,
|
||||
FileType,
|
||||
MockEnvironment,
|
||||
mockFileMeta0,
|
||||
mockFileMetas,
|
||||
@ -20,6 +21,8 @@ import {DeleteFileDialogComponent} from '../_dialogs/delete-file-dialog/delete-f
|
||||
import {DeleteFileDialogData} from '../_interfaces/dialog-data';
|
||||
import {GetIconCodePipe} from '../_pipes/get-icon-code.pipe';
|
||||
import {FileListComponent} from './file-list.component';
|
||||
import createClone from 'rfdc';
|
||||
|
||||
|
||||
describe('FileListComponent', () => {
|
||||
let httpMock: HttpTestingController;
|
||||
@ -146,11 +149,58 @@ describe('FileListComponent', () => {
|
||||
expect(loadFileMetasSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should navigate to modeler to edit a file', () => {
|
||||
it('should navigate to modeler to edit a BPMN or DMN file', () => {
|
||||
const routerNavigateSpy = spyOn((component as any).router, 'navigate');
|
||||
component.workflowSpec = mockWorkflowSpec0;
|
||||
component.editFile(mockFileMeta0);
|
||||
expect(routerNavigateSpy).toHaveBeenCalledWith([`/modeler/${mockWorkflowSpec0.id}/${mockFileMeta0.id}`]);
|
||||
|
||||
routerNavigateSpy.calls.reset();
|
||||
const mockDmnMeta = createClone()(mockFileMeta0);
|
||||
mockDmnMeta.type = FileType.DMN;
|
||||
component.editFile(mockDmnMeta);
|
||||
expect(routerNavigateSpy).toHaveBeenCalledWith([`/modeler/${mockWorkflowSpec0.id}/${mockDmnMeta.id}`]);
|
||||
});
|
||||
|
||||
it('should open file metadata dialog for non-BPMN files', () => {
|
||||
const routerNavigateSpy = spyOn((component as any).router, 'navigate');
|
||||
const editFileMetaSpy = spyOn(component, 'editFileMeta');
|
||||
component.workflowSpec = mockWorkflowSpec0;
|
||||
const mockDocMeta = createClone()(mockFileMeta0);
|
||||
mockDocMeta.type = FileType.DOCX;
|
||||
component.editFile(mockDocMeta);
|
||||
expect(routerNavigateSpy).not.toHaveBeenCalled();
|
||||
expect(editFileMetaSpy).toHaveBeenCalledWith(mockDocMeta);
|
||||
|
||||
routerNavigateSpy.calls.reset();
|
||||
editFileMetaSpy.calls.reset();
|
||||
component.editFile(null);
|
||||
expect(routerNavigateSpy).not.toHaveBeenCalled();
|
||||
expect(editFileMetaSpy).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
it('should change route and then open file metadata dialog', () => {
|
||||
const routerNavigateSpy = spyOn((component as any).router, 'navigate')
|
||||
.and.returnValue({finally: finallyCallback => finallyCallback()});
|
||||
const _openFileDialogSpy = spyOn((component as any), '_openFileDialog').and.stub();
|
||||
component.workflowSpec = mockWorkflowSpec0;
|
||||
const mockDocMeta = createClone()(mockFileMeta0);
|
||||
mockDocMeta.type = FileType.DOCX;
|
||||
component.editFileMeta(mockDocMeta);
|
||||
expect(routerNavigateSpy).toHaveBeenCalled();
|
||||
|
||||
const fakeBlob = new Blob(['I am a fake blob. A real blob says "blorp blorp blorp."']);
|
||||
const fReq = httpMock.expectOne(`apiRoot/file/${mockDocMeta.id}/data`);
|
||||
expect(fReq.request.method).toEqual('GET');
|
||||
fReq.flush(fakeBlob);
|
||||
expect(_openFileDialogSpy).toHaveBeenCalledWith(mockDocMeta, fakeBlob);
|
||||
|
||||
routerNavigateSpy.calls.reset();
|
||||
_openFileDialogSpy.calls.reset();
|
||||
|
||||
component.editFileMeta(null);
|
||||
expect(routerNavigateSpy).toHaveBeenCalled();
|
||||
expect(_openFileDialogSpy).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it('should flag a file as primary', () => {
|
||||
|
@ -283,7 +283,6 @@ describe('ModelerComponent', () => {
|
||||
});
|
||||
|
||||
it('should save file changes', () => {
|
||||
const updateFileMetaSpy = spyOn(component.api, 'updateFileMeta').and.returnValue(of(mockFileMeta0));
|
||||
const updateFileDataSpy = spyOn(component.api, 'updateFileData').and.returnValue(of(mockFileMeta0));
|
||||
const snackBarOpenSpy = spyOn(component.snackBar, 'open').and.stub();
|
||||
|
||||
@ -292,7 +291,6 @@ describe('ModelerComponent', () => {
|
||||
component.diagramComponent.writeValue(BPMN_DIAGRAM_EMPTY.replace(/REPLACE_ME/g, 'cream_colored_ponies'));
|
||||
component.saveFileChanges();
|
||||
|
||||
expect(updateFileMetaSpy).toHaveBeenCalledWith(mockFileMeta0);
|
||||
expect(updateFileDataSpy).toHaveBeenCalledWith(mockFileMeta0);
|
||||
expect(snackBarOpenSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
@ -295,10 +295,9 @@ export class ModelerComponent implements AfterViewInit {
|
||||
this.xml = this.draftXml;
|
||||
this.diagramFileMeta.file = new File([this.xml], this.diagramFileMeta.name, {type: 'text/xml'});
|
||||
|
||||
this.api.updateFileData(this.diagramFileMeta).subscribe(() => {
|
||||
this.api.updateFileMeta(this.diagramFileMeta).subscribe(() => {
|
||||
this.snackBar.open(`Saved changes to file metadata ${this.diagramFileMeta.name}.`, 'Ok', {duration: 5000});
|
||||
});
|
||||
this.api.updateFileData(this.diagramFileMeta).subscribe(newFileMeta => {
|
||||
this.diagramFileMeta = newFileMeta;
|
||||
this.snackBar.open(`Saved changes to file metadata ${this.diagramFileMeta.name}.`, 'Ok', {duration: 5000});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,21 @@
|
||||
<mat-card class="mat-elevation-z0" [id]="workflowSpec.id">
|
||||
<mat-card class="mat-elevation-z0 {{workflowSpec.is_status ? 'master-status' : ''}}" [id]="workflowSpec.id">
|
||||
<mat-card-header>
|
||||
<mat-card-title fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="10px">
|
||||
<h3>{{workflowSpec.display_name}}</h3>
|
||||
<div class="action-buttons">
|
||||
<ng-container *ngTemplateOutlet="actionButtons"></ng-container>
|
||||
</div>
|
||||
<span fxFlex></span>
|
||||
<ng-container>
|
||||
<button (click)="makeMasterStatus()" *ngIf="!workflowSpec.is_status" mat-button color="accent">
|
||||
<mat-icon>radio_button_unchecked</mat-icon>
|
||||
Master status spec
|
||||
</button>
|
||||
<button *ngIf="workflowSpec.is_status" (click)="makeMasterStatus()" mat-button color="accent">
|
||||
<mat-icon>radio_button_checked</mat-icon>
|
||||
Master status spec
|
||||
</button>
|
||||
</ng-container>
|
||||
</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
|
@ -1,6 +1,12 @@
|
||||
@import "../../config";
|
||||
|
||||
mat-card {
|
||||
margin-bottom: 1em;
|
||||
border: 1px solid #CCCCCC;
|
||||
border: 1px solid $brand-gray;
|
||||
|
||||
&.master-status {
|
||||
border: 2px dashed $brand-accent;
|
||||
}
|
||||
|
||||
mat-card-title {
|
||||
h3 {
|
||||
|
@ -1,6 +1,4 @@
|
||||
import {Component, Input, OnInit, TemplateRef} from '@angular/core';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {MatSnackBar} from '@angular/material/snack-bar';
|
||||
import {Component, EventEmitter, Input, OnInit, Output, TemplateRef} from '@angular/core';
|
||||
import {ApiService, WorkflowSpec} from 'sartography-workflow-lib';
|
||||
|
||||
@Component({
|
||||
@ -11,15 +9,20 @@ import {ApiService, WorkflowSpec} from 'sartography-workflow-lib';
|
||||
export class WorkflowSpecCardComponent implements OnInit {
|
||||
@Input() workflowSpec: WorkflowSpec;
|
||||
@Input() actionButtons: TemplateRef<any>;
|
||||
@Output() workflowUpdated: EventEmitter<WorkflowSpec> = new EventEmitter();
|
||||
|
||||
constructor() {
|
||||
constructor(
|
||||
private api: ApiService
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
openFileDialog() {
|
||||
|
||||
|
||||
makeMasterStatus() {
|
||||
this.workflowSpec.is_status = true;
|
||||
this.api.updateWorkflowSpecification(this.workflowSpec.id, this.workflowSpec).subscribe(spec => {
|
||||
this.workflowUpdated.emit(spec);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div *ngFor="let wfs of cat.workflow_specs" class="workflow-spec">
|
||||
<app-workflow-spec-card [workflowSpec]="wfs" [actionButtons]="actionButtons">
|
||||
<app-workflow-spec-card
|
||||
[workflowSpec]="wfs"
|
||||
[actionButtons]="actionButtons"
|
||||
(workflowUpdated)="onWorkflowUpdated($event)">
|
||||
</app-workflow-spec-card>
|
||||
<ng-template #actionButtons>
|
||||
<div class="workflow-spec-actions">
|
||||
|
@ -233,5 +233,23 @@ export class WorkflowSpecListComponent implements OnInit {
|
||||
this.snackBar.open(message, 'Ok', {duration: 3000});
|
||||
}
|
||||
|
||||
onWorkflowUpdated(spec: WorkflowSpec) {
|
||||
if (spec.is_status) {
|
||||
// Mark all other specs as not is_status
|
||||
let numUpdated = this.workflowSpecs.length - 1;
|
||||
this.workflowSpecs.forEach(wfs => {
|
||||
if (wfs.id !== spec.id) {
|
||||
wfs.is_status = false;
|
||||
this.api.updateWorkflowSpecification(wfs.id, wfs).subscribe(() => {
|
||||
numUpdated--;
|
||||
if (numUpdated === 0) {
|
||||
this._loadWorkflowSpecCategories();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
this._loadWorkflowSpecCategories();
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user