WIP: Moves util methods to sartography-workflow-lib. Attempts to get file dialog working.
This commit is contained in:
parent
ced9a852e8
commit
afca7cece8
|
@ -12171,9 +12171,9 @@
|
|||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"sartography-workflow-lib": {
|
||||
"version": "0.0.68",
|
||||
"resolved": "https://registry.npmjs.org/sartography-workflow-lib/-/sartography-workflow-lib-0.0.68.tgz",
|
||||
"integrity": "sha512-W5wJ7YIqDu/0ul1AZNazCUNzmxGNTbLd2i/nArYNgf0WwJO2j+zQsxd41TJVjHLp4dHpR/UsnCXGH/cC0Pz23w=="
|
||||
"version": "0.0.72",
|
||||
"resolved": "https://registry.npmjs.org/sartography-workflow-lib/-/sartography-workflow-lib-0.0.72.tgz",
|
||||
"integrity": "sha512-l6153jMOkKPgi2dOODOdTSHdPyyZpYjuylkPt38ct4XYaaF+kiT3GadhZli3sxg8KRkYfiaJ5krZRgl3adNu7A=="
|
||||
},
|
||||
"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.68",
|
||||
"sartography-workflow-lib": "^0.0.72",
|
||||
"tslib": "^1.11.1",
|
||||
"uuid": "^7.0.2",
|
||||
"zone.js": "^0.10.3"
|
||||
|
|
|
@ -2,9 +2,8 @@ import {Component, Inject} from '@angular/core';
|
|||
import {FormGroup} from '@angular/forms';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
|
||||
import {FormlyFieldConfig, FormlyFormOptions} from '@ngx-formly/core';
|
||||
import {FileType} from 'sartography-workflow-lib';
|
||||
import {cleanUpFilename, FileType} from 'sartography-workflow-lib';
|
||||
import {FileMetaDialogData} from '../../_interfaces/dialog-data';
|
||||
import {cleanUpFilename} from '../../_util/string-clean';
|
||||
|
||||
@Component({
|
||||
selector: 'app-new-file-dialog',
|
||||
|
@ -21,55 +20,47 @@ export class FileMetaDialogComponent {
|
|||
public dialogRef: MatDialogRef<FileMetaDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: FileMetaDialogData
|
||||
) {
|
||||
const fileTypeOptions = Object.entries(FileType).map(ft => {
|
||||
return {
|
||||
label: ft[0],
|
||||
value: ft[1]
|
||||
};
|
||||
});
|
||||
const fileTypeOptions = Object.entries(FileType).map(ft => {
|
||||
return {
|
||||
label: ft[0],
|
||||
value: ft[1]
|
||||
};
|
||||
});
|
||||
|
||||
this.fields = [
|
||||
{
|
||||
key: 'id',
|
||||
type: 'input',
|
||||
defaultValue: this.data.id,
|
||||
templateOptions: {
|
||||
hide: true,
|
||||
},
|
||||
this.fields = [
|
||||
{
|
||||
key: 'fileName',
|
||||
type: 'input',
|
||||
defaultValue: this.data.fileName,
|
||||
templateOptions: {
|
||||
label: 'File Name',
|
||||
placeholder: 'Name of file',
|
||||
description: 'Enter a name, in lowercase letters, separated by underscores, that is easy for you to remember.' +
|
||||
'It will be converted to all_lowercase_with_underscores when you save.',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'fileName',
|
||||
type: 'input',
|
||||
defaultValue: this.data.fileName,
|
||||
templateOptions: {
|
||||
label: 'File Name',
|
||||
placeholder: 'Name of workflow specification',
|
||||
description: 'Enter a name, in lowercase letters, separated by underscores, that is easy for you to remember.' +
|
||||
'It will be converted to all_lowercase_with_underscores when you save.',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'fileType',
|
||||
type: 'select',
|
||||
defaultValue: this.data.fileType,
|
||||
templateOptions: {
|
||||
label: 'File Type',
|
||||
placeholder: 'Extension of file',
|
||||
required: true,
|
||||
options: fileTypeOptions,
|
||||
},
|
||||
{
|
||||
key: 'fileType',
|
||||
type: 'select',
|
||||
defaultValue: this.data.fileType,
|
||||
templateOptions: {
|
||||
label: 'File Type',
|
||||
placeholder: 'Extension of file',
|
||||
required: true,
|
||||
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,
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
onNoClick() {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import {Component} from '@angular/core';
|
||||
import {MatDialogRef} from '@angular/material/dialog';
|
||||
import {ApiService} from 'sartography-workflow-lib';
|
||||
import {getDiagramTypeFromXml} from '../../_util/diagram-type';
|
||||
import {cleanUpFilename} from '../../_util/string-clean';
|
||||
import {ApiService, cleanUpFilename, getDiagramTypeFromXml} from 'sartography-workflow-lib';
|
||||
|
||||
@Component({
|
||||
selector: 'app-open-file-dialog',
|
||||
|
|
|
@ -2,8 +2,8 @@ import {Component, Inject} from '@angular/core';
|
|||
import {FormGroup} from '@angular/forms';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
|
||||
import {FormlyFieldConfig, FormlyFormOptions} from '@ngx-formly/core';
|
||||
import {toSnakeCase} from 'sartography-workflow-lib';
|
||||
import {WorkflowSpecDialogData} from '../../_interfaces/dialog-data';
|
||||
import {toSnakeCase} from '../../_util/string-clean';
|
||||
|
||||
@Component({
|
||||
selector: 'app-workflow-spec-category-dialog',
|
||||
|
|
|
@ -2,10 +2,9 @@ import {Component, Inject} from '@angular/core';
|
|||
import {FormGroup} from '@angular/forms';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
|
||||
import {FormlyFieldConfig, FormlyFormOptions, FormlyTemplateOptions} from '@ngx-formly/core';
|
||||
import {ApiService} from 'sartography-workflow-lib';
|
||||
import {ApiService, toSnakeCase} from 'sartography-workflow-lib';
|
||||
import {v4 as uuidv4} from 'uuid';
|
||||
import {WorkflowSpecDialogData} from '../../_interfaces/dialog-data';
|
||||
import {toSnakeCase} from '../../_util/string-clean';
|
||||
|
||||
@Component({
|
||||
selector: 'app-workflow-spec-dialog',
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
import {FileType} from 'sartography-workflow-lib';
|
||||
|
||||
export const getDiagramTypeFromXml = (xml: string): FileType => {
|
||||
return (xml && xml.includes('dmn.xsd') ? FileType.DMN : FileType.BPMN);
|
||||
};
|
|
@ -1,3 +0,0 @@
|
|||
export const isNumberDefined = (n: number): boolean => {
|
||||
return (typeof n === 'number') && isFinite(n) && !isNaN(n);
|
||||
};
|
|
@ -1,20 +0,0 @@
|
|||
import {cleanUpFilename, toSnakeCase, trimString} from './string-clean';
|
||||
|
||||
describe('String Cleaning Utilities', () => {
|
||||
const afterTrimming = `I'm tired of wasting letters when punctuation will do, period. -Steve Martin`;
|
||||
const beforeTrimming = ` 📌📍🏁 <>?:"{}[] ${afterTrimming} !@#$%^& ✌️👍👆 `;
|
||||
|
||||
it('converts a string to snake case', () => {
|
||||
expect(toSnakeCase(beforeTrimming)).toEqual('i_m_tired_of_wasting_letters_when_punctuation_will_do_period_steve_martin');
|
||||
});
|
||||
|
||||
it('cleans up a file name and replaces or adds the extension', () => {
|
||||
expect(cleanUpFilename(beforeTrimming, 'bpmn')).toEqual(`I'm tired of wasting letters when punctuation will do, period.bpmn`);
|
||||
expect(cleanUpFilename(' no extension ', 'bpmn')).toEqual('no extension.bpmn');
|
||||
});
|
||||
|
||||
it('trims non-word characters from a string', () => {
|
||||
expect(trimString(beforeTrimming)).toEqual(afterTrimming);
|
||||
});
|
||||
|
||||
});
|
|
@ -1,25 +0,0 @@
|
|||
import {FileType} from 'sartography-workflow-lib';
|
||||
|
||||
export const trimString = (str: string): string => {
|
||||
return !str ? '' : String(str).replace(/^\W+|\W+$/gi, '');
|
||||
};
|
||||
|
||||
export const toSnakeCase = (str: string): string => {
|
||||
str = trimString(str);
|
||||
return !str ? '' : String(str)
|
||||
.replace(/\W+/gi, '_')
|
||||
.toLowerCase();
|
||||
};
|
||||
|
||||
export const cleanUpFilename = (str: string, extension: FileType|string): string => {
|
||||
const arr = trimString(str).split('.');
|
||||
|
||||
// Add file extension, if necessary
|
||||
if (arr.length < 2) {
|
||||
arr.push(extension);
|
||||
} else {
|
||||
(arr[arr.length - 1]) = extension;
|
||||
}
|
||||
|
||||
return arr.join('.');
|
||||
};
|
|
@ -5,11 +5,16 @@ import {ControlValueAccessor} from '@angular/forms';
|
|||
import BpmnModeler from 'bpmn-js/lib/Modeler';
|
||||
import DmnModeler from 'dmn-js/lib/Modeler';
|
||||
import * as fileSaver from 'file-saver';
|
||||
import {ApiService, BPMN_DIAGRAM_DEFAULT, DMN_DIAGRAM_DEFAULT, FileType} from 'sartography-workflow-lib';
|
||||
import {
|
||||
ApiService,
|
||||
BPMN_DIAGRAM_DEFAULT,
|
||||
DMN_DIAGRAM_DEFAULT,
|
||||
FileType,
|
||||
getDiagramTypeFromXml
|
||||
} from 'sartography-workflow-lib';
|
||||
import {v4 as uuidv4} from 'uuid';
|
||||
import {BpmnWarning} from '../_interfaces/bpmn-warning';
|
||||
import {ImportEvent} from '../_interfaces/import-event';
|
||||
import {getDiagramTypeFromXml} from '../_util/diagram-type';
|
||||
import {bpmnModelerConfig} from './bpmn-modeler-config';
|
||||
import {dmnModelerConfig} from './dmn-modeler-config';
|
||||
|
||||
|
|
|
@ -27,3 +27,17 @@
|
|||
</mat-list-item>
|
||||
</mat-list>
|
||||
</div>
|
||||
<div class="file-list-actions">
|
||||
<button [queryParams]="{action: 'newFile'}" [routerLink]="['/modeler/' + workflowSpec.id]" mat-button>
|
||||
<mat-icon>note_add</mat-icon>
|
||||
Add new BPMN or DMN file
|
||||
</button>
|
||||
<button [queryParams]="{action: 'openFile'}" [routerLink]="['/modeler/' + workflowSpec.id]" mat-button>
|
||||
<mat-icon>cloud_upload</mat-icon>
|
||||
Upload BPMN or DMN file
|
||||
</button>
|
||||
<button (click)="editFile()" mat-button>
|
||||
<mat-icon>cloud_upload</mat-icon>
|
||||
Upload Template file
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {MatSnackBar} from '@angular/material/snack-bar';
|
||||
import {Router} from '@angular/router';
|
||||
import {ApiService, FileMeta, FileType, WorkflowSpec} from 'sartography-workflow-lib';
|
||||
import {ActivatedRoute, Params, Router} from '@angular/router';
|
||||
import {ApiService, FileMeta, FileType, isNumberDefined, WorkflowSpec} from 'sartography-workflow-lib';
|
||||
import {DeleteFileDialogComponent} from '../_dialogs/delete-file-dialog/delete-file-dialog.component';
|
||||
import {FileMetaDialogComponent} from '../_dialogs/file-meta-dialog/file-meta-dialog.component';
|
||||
import {DeleteFileDialogData, FileMetaDialogData} from '../_interfaces/dialog-data';
|
||||
|
@ -20,6 +20,7 @@ export class FileListComponent implements OnInit {
|
|||
constructor(
|
||||
private api: ApiService,
|
||||
public dialog: MatDialog,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private snackBar: MatSnackBar,
|
||||
) {
|
||||
|
@ -29,8 +30,8 @@ export class FileListComponent implements OnInit {
|
|||
this._loadFileMetas();
|
||||
}
|
||||
|
||||
editFile(fileMeta: FileMeta) {
|
||||
if (fileMeta.type === FileType.BPMN ||fileMeta.type === FileType.DMN) {
|
||||
editFile(fileMeta?: FileMeta) {
|
||||
if (fileMeta && ((fileMeta.type === FileType.BPMN) || (fileMeta.type === FileType.DMN))) {
|
||||
this.router.navigate([`/modeler/${this.workflowSpec.id}/${fileMeta.id}`]);
|
||||
} else {
|
||||
// Show edit file meta dialog
|
||||
|
@ -39,11 +40,30 @@ export class FileListComponent implements OnInit {
|
|||
}
|
||||
|
||||
editFileMeta(fm: FileMeta) {
|
||||
// Set route query string
|
||||
this.router.navigate([], {
|
||||
relativeTo: this.route,
|
||||
fragment: this.workflowSpec.id,
|
||||
queryParams: {
|
||||
workflow_spec_id: this.workflowSpec.id
|
||||
},
|
||||
queryParamsHandling: 'merge'
|
||||
}).finally(() => {
|
||||
// Get file data
|
||||
if (fm && isNumberDefined(fm.id)) {
|
||||
this.api.getFileData(fm.id).subscribe(fileData => this._openFileDialog(fm, fileData));
|
||||
} else {
|
||||
this._openFileDialog();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _openFileDialog(fm?: FileMeta, fileData?: Blob) {
|
||||
const dialogRef = this.dialog.open(FileMetaDialogComponent, {
|
||||
data: {
|
||||
fileName: fm.name,
|
||||
fileType: fm.type,
|
||||
file: fm.file,
|
||||
fileName: fm ? fm.name : undefined,
|
||||
fileType: fm ? fm.type : undefined,
|
||||
file: fileData ? fileData : undefined,
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -61,6 +81,7 @@ export class FileListComponent implements OnInit {
|
|||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
confirmDelete(fm: FileMeta) {
|
||||
|
@ -114,4 +135,5 @@ export class FileListComponent implements OnInit {
|
|||
this.api.getFileData(fm.id).subscribe((fd: File) => fm.file = fd);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,16 +3,21 @@ import {AfterViewInit, Component, ViewChild} from '@angular/core';
|
|||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {MatSnackBar} from '@angular/material/snack-bar';
|
||||
import {ActivatedRoute, Params, Router} from '@angular/router';
|
||||
import {ApiService, FileMeta, FileType, WorkflowSpec} from 'sartography-workflow-lib';
|
||||
import {BpmnWarning} from '../_interfaces/bpmn-warning';
|
||||
import {FileMetaDialogData, NewFileDialogData, OpenFileDialogData} from '../_interfaces/dialog-data';
|
||||
import {ImportEvent} from '../_interfaces/import-event';
|
||||
import {getDiagramTypeFromXml} from '../_util/diagram-type';
|
||||
import {isNumberDefined} from '../_util/is-number-defined';
|
||||
import {DiagramComponent} from '../diagram/diagram.component';
|
||||
import {
|
||||
ApiService,
|
||||
FileMeta,
|
||||
FileType,
|
||||
getDiagramTypeFromXml,
|
||||
isNumberDefined,
|
||||
WorkflowSpec
|
||||
} from 'sartography-workflow-lib';
|
||||
import {FileMetaDialogComponent} from '../_dialogs/file-meta-dialog/file-meta-dialog.component';
|
||||
import {NewFileDialogComponent} from '../_dialogs/new-file-dialog/new-file-dialog.component';
|
||||
import {OpenFileDialogComponent} from '../_dialogs/open-file-dialog/open-file-dialog.component';
|
||||
import {BpmnWarning} from '../_interfaces/bpmn-warning';
|
||||
import {FileMetaDialogData, NewFileDialogData, OpenFileDialogData} from '../_interfaces/dialog-data';
|
||||
import {ImportEvent} from '../_interfaces/import-event';
|
||||
import {DiagramComponent} from '../diagram/diagram.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-modeler',
|
||||
|
@ -115,7 +120,7 @@ export class ModelerComponent implements AfterViewInit {
|
|||
this.xml = (event.target as FileReader).result.toString();
|
||||
const diagramType = getDiagramTypeFromXml(this.xml);
|
||||
this.diagramComponent.openDiagram(this.xml, diagramType);
|
||||
}
|
||||
};
|
||||
|
||||
readFile(file: File) {
|
||||
// FileReader must be instantiated this way so unit test can spy on it.
|
||||
|
@ -157,8 +162,7 @@ export class ModelerComponent implements AfterViewInit {
|
|||
}
|
||||
|
||||
openFileDialog() {
|
||||
const dialogRef = this.dialog.open(OpenFileDialogComponent, {
|
||||
});
|
||||
const dialogRef = this.dialog.open(OpenFileDialogComponent, {});
|
||||
|
||||
dialogRef.afterClosed().subscribe((data: OpenFileDialogData) => {
|
||||
if (data && data.file) {
|
||||
|
@ -169,8 +173,7 @@ export class ModelerComponent implements AfterViewInit {
|
|||
}
|
||||
|
||||
newFileDialog() {
|
||||
const dialogRef = this.dialog.open(NewFileDialogComponent, {
|
||||
});
|
||||
const dialogRef = this.dialog.open(NewFileDialogComponent, {});
|
||||
|
||||
dialogRef.afterClosed().subscribe((data: NewFileDialogData) => {
|
||||
if (data && data.fileType) {
|
||||
|
@ -185,6 +188,7 @@ export class ModelerComponent implements AfterViewInit {
|
|||
data: {
|
||||
fileName: this.diagramFile ? this.diagramFile.name : this.fileName || '',
|
||||
fileType: this.diagramType || getDiagramTypeFromXml(this.xml),
|
||||
file: this.diagramFile || undefined,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<mat-card class="mat-elevation-z0">
|
||||
<mat-card class="mat-elevation-z0" [id]="workflowSpec.id">
|
||||
<mat-card-header>
|
||||
<mat-card-title fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="10px">
|
||||
<h3>{{workflowSpec.display_name}}</h3>
|
||||
|
@ -18,13 +18,5 @@
|
|||
<app-file-list [workflowSpec]="workflowSpec"></app-file-list>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button [queryParams]="{action: 'newFile'}" [routerLink]="['/modeler/' + workflowSpec.id]" mat-button>
|
||||
<mat-icon>note_add</mat-icon>
|
||||
Add new BPMN or DMN file
|
||||
</button>
|
||||
<button [queryParams]="{action: 'openFile'}" [routerLink]="['/modeler/' + workflowSpec.id]" mat-button>
|
||||
<mat-icon>cloud_upload</mat-icon>
|
||||
Upload BPMN or DMN file
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import {Component, Input, OnInit, TemplateRef} from '@angular/core';
|
||||
import {WorkflowSpec} from 'sartography-workflow-lib';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {MatSnackBar} from '@angular/material/snack-bar';
|
||||
import {ApiService, WorkflowSpec} from 'sartography-workflow-lib';
|
||||
|
||||
@Component({
|
||||
selector: 'app-workflow-spec-card',
|
||||
|
@ -16,4 +18,8 @@ export class WorkflowSpecCardComponent implements OnInit {
|
|||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
openFileDialog() {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {MatSnackBar} from '@angular/material/snack-bar';
|
||||
import {ApiService, WorkflowSpec, WorkflowSpecCategory} from 'sartography-workflow-lib';
|
||||
import {ApiService, isNumberDefined, WorkflowSpec, WorkflowSpecCategory} from 'sartography-workflow-lib';
|
||||
import {DeleteWorkflowSpecCategoryDialogComponent} from '../_dialogs/delete-workflow-spec-category-dialog/delete-workflow-spec-category-dialog.component';
|
||||
import {DeleteWorkflowSpecDialogComponent} from '../_dialogs/delete-workflow-spec-dialog/delete-workflow-spec-dialog.component';
|
||||
import {WorkflowSpecCategoryDialogComponent} from '../_dialogs/workflow-spec-category-dialog/workflow-spec-category-dialog.component';
|
||||
|
@ -12,7 +12,6 @@ import {
|
|||
WorkflowSpecCategoryDialogData,
|
||||
WorkflowSpecDialogData
|
||||
} from '../_interfaces/dialog-data';
|
||||
import {isNumberDefined} from '../_util/is-number-defined';
|
||||
|
||||
interface WorklflowSpecCategoryGroup {
|
||||
id: number;
|
||||
|
@ -138,13 +137,13 @@ export class WorkflowSpecListComponent implements OnInit {
|
|||
}
|
||||
|
||||
private _loadWorkflowSpecs() {
|
||||
this.api.getWorkflowSpecList().subscribe(wfs => {
|
||||
this.api.getWorkflowSpecList().subscribe(wfs => {
|
||||
this.workflowSpecs = wfs;
|
||||
this.workflowSpecsByCategory.forEach(cat => {
|
||||
cat.workflow_specs = this.workflowSpecs.filter(wf => wf.workflow_spec_category_id === cat.id);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _upsertWorkflowSpecification(data: WorkflowSpecDialogData) {
|
||||
if (data.id && data.name && data.display_name && data.description) {
|
||||
|
|
Loading…
Reference in New Issue