Adds workflow spec category dialog
This commit is contained in:
parent
721a73b5ff
commit
7c1dd01a4d
|
@ -11734,9 +11734,9 @@
|
|||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"sartography-workflow-lib": {
|
||||
"version": "0.0.58",
|
||||
"resolved": "https://registry.npmjs.org/sartography-workflow-lib/-/sartography-workflow-lib-0.0.58.tgz",
|
||||
"integrity": "sha512-y0TyRb0LiiLWo3uA9HV91ZIct2tIDoRBAUtwoTi2Kynj4eH0ui2Y4d/pllbC2GFKrfbmmw2NqMP8ZcsCnkuJgA=="
|
||||
"version": "0.0.59",
|
||||
"resolved": "https://registry.npmjs.org/sartography-workflow-lib/-/sartography-workflow-lib-0.0.59.tgz",
|
||||
"integrity": "sha512-Aylrdco6fei7P4VAF+ZG+q3F3TJLkyobPmzGJDe+8vfZ5jlUJ+EffWKFV6LKCdLdkaV3oYRpo4voPnm1OvuRSA=="
|
||||
},
|
||||
"sass": {
|
||||
"version": "1.23.3",
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
"dmn-js-properties-panel": "^0.3.4",
|
||||
"file-saver": "^2.0.2",
|
||||
"rxjs": "~6.5.4",
|
||||
"sartography-workflow-lib": "^0.0.58",
|
||||
"sartography-workflow-lib": "^0.0.59",
|
||||
"tslib": "^1.10.0",
|
||||
"uuid": "^7.0.0",
|
||||
"zone.js": "~0.10.2"
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<div mat-dialog-title>
|
||||
<h1>Workflow Specification</h1>
|
||||
</div>
|
||||
|
||||
<div mat-dialog-content>
|
||||
<form [formGroup]="form">
|
||||
<formly-form [model]="model" [fields]="fields" [options]="options" [form]="form"></formly-form>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div mat-dialog-actions>
|
||||
<button [disabled]="form.invalid" (click)="onSubmit()" color="primary" mat-flat-button>Save</button>
|
||||
<button (click)="onNoClick()" mat-flat-button>Cancel</button>
|
||||
</div>
|
|
@ -0,0 +1,3 @@
|
|||
::ng-deep formly-field mat-form-field {
|
||||
margin-bottom: 40px;
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {MAT_DIALOG_DATA, MatDialogModule, MatDialogRef} from '@angular/material/dialog';
|
||||
import {MatFormFieldModule} from '@angular/material/form-field';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {MatInputModule} from '@angular/material/input';
|
||||
import {BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {FormlyModule} from '@ngx-formly/core';
|
||||
import {FormlyMaterialModule} from '@ngx-formly/material';
|
||||
import {mockWorkflowSpec0} from 'sartography-workflow-lib';
|
||||
import {WorkflowSpecCategoryDialogData} from '../../_interfaces/dialog-data';
|
||||
import {WorkflowSpecCategoryDialogComponent} from './workflow-spec-category-dialog.component';
|
||||
|
||||
describe('WorkflowSpecDialogComponent', () => {
|
||||
let component: WorkflowSpecCategoryDialogComponent;
|
||||
let fixture: ComponentFixture<WorkflowSpecCategoryDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
BrowserAnimationsModule,
|
||||
FormlyModule.forRoot(),
|
||||
FormlyMaterialModule,
|
||||
FormsModule,
|
||||
MatDialogModule,
|
||||
MatFormFieldModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
NoopAnimationsModule,
|
||||
ReactiveFormsModule,
|
||||
],
|
||||
declarations: [WorkflowSpecCategoryDialogComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: MatDialogRef,
|
||||
useValue: {
|
||||
close: (dialogResult: any) => {
|
||||
}
|
||||
}
|
||||
},
|
||||
{provide: MAT_DIALOG_DATA, useValue: []},
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(WorkflowSpecCategoryDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should save data on submit', () => {
|
||||
const closeSpy = spyOn(component.dialogRef, 'close').and.stub();
|
||||
const expectedData: WorkflowSpecCategoryDialogData = mockWorkflowSpec0 as WorkflowSpecCategoryDialogData;
|
||||
component.model = expectedData;
|
||||
component.onSubmit();
|
||||
expect(closeSpy).toHaveBeenCalledWith(expectedData);
|
||||
});
|
||||
|
||||
it('should not change data on cancel', () => {
|
||||
const closeSpy = spyOn(component.dialogRef, 'close').and.stub();
|
||||
component.onNoClick();
|
||||
expect(closeSpy).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,83 @@
|
|||
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 {v4 as uuidv4} from 'uuid';
|
||||
import {WorkflowSpecDialogData} from '../../_interfaces/dialog-data';
|
||||
import {toSnakeCase} from '../../_util/string-clean';
|
||||
|
||||
@Component({
|
||||
selector: 'app-workflow-spec-category-dialog',
|
||||
templateUrl: './workflow-spec-category-dialog.component.html',
|
||||
styleUrls: ['./workflow-spec-category-dialog.component.scss']
|
||||
})
|
||||
export class WorkflowSpecCategoryDialogComponent {
|
||||
form: FormGroup = new FormGroup({});
|
||||
model: any = {};
|
||||
options: FormlyFormOptions = {};
|
||||
fields: FormlyFieldConfig[] = [
|
||||
{
|
||||
key: 'id',
|
||||
type: 'input',
|
||||
defaultValue: this.data.id || uuidv4(),
|
||||
templateOptions: {
|
||||
label: 'ID',
|
||||
placeholder: 'UUID of workflow specification',
|
||||
description: 'This is an autogenerated unique ID and is not editable.',
|
||||
required: true,
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
type: 'input',
|
||||
defaultValue: this.data.name,
|
||||
templateOptions: {
|
||||
label: '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: 'display_name',
|
||||
type: 'input',
|
||||
defaultValue: this.data.display_name,
|
||||
templateOptions: {
|
||||
label: 'Display Name',
|
||||
placeholder: 'Title of the workflow specification',
|
||||
description: 'This is a human readable title for the workflow specification,' +
|
||||
'which should be easy for others to read and remember.',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
type: 'textarea',
|
||||
defaultValue: this.data.description,
|
||||
templateOptions: {
|
||||
label: 'Description',
|
||||
placeholder: 'Description of workflow specification',
|
||||
description: 'Write a few sentences explaining to users why this workflow exists and what it should be used for.',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<WorkflowSpecCategoryDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: WorkflowSpecDialogData,
|
||||
) {
|
||||
}
|
||||
|
||||
onNoClick() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.model.name = toSnakeCase(this.model.name);
|
||||
this.dialogRef.close(this.model);
|
||||
}
|
||||
|
||||
}
|
|
@ -20,6 +20,12 @@ export interface WorkflowSpecDialogData {
|
|||
description: string;
|
||||
}
|
||||
|
||||
export interface WorkflowSpecCategoryDialogData {
|
||||
id: string;
|
||||
name: string;
|
||||
display_name: string;
|
||||
}
|
||||
|
||||
export interface DeleteFileDialogData {
|
||||
confirm: boolean;
|
||||
fileMeta: FileMeta;
|
||||
|
|
|
@ -26,6 +26,7 @@ import {DeleteWorkflowSpecDialogComponent} from './_dialogs/delete-workflow-spec
|
|||
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 {WorkflowSpecCategoryDialogComponent} from './_dialogs/workflow-spec-category-dialog/workflow-spec-category-dialog.component';
|
||||
import {WorkflowSpecDialogComponent} from './_dialogs/workflow-spec-dialog/workflow-spec-dialog.component';
|
||||
import {EmailValidator, EmailValidatorMessage, ShowError} from './_forms/validators/formly.validator';
|
||||
import {GetIconCodePipe} from './_pipes/get-icon-code.pipe';
|
||||
|
@ -81,6 +82,7 @@ export class AppFormlyConfig {
|
|||
OpenFileDialogComponent,
|
||||
SignInComponent,
|
||||
SignOutComponent,
|
||||
WorkflowSpecCategoryDialogComponent,
|
||||
WorkflowSpecDialogComponent,
|
||||
WorkflowSpecListComponent,
|
||||
HomeComponent,
|
||||
|
@ -116,6 +118,7 @@ export class AppFormlyConfig {
|
|||
FileMetaDialogComponent,
|
||||
NewFileDialogComponent,
|
||||
OpenFileDialogComponent,
|
||||
WorkflowSpecCategoryDialogComponent,
|
||||
WorkflowSpecDialogComponent,
|
||||
],
|
||||
providers: [
|
||||
|
|
|
@ -1,23 +1,30 @@
|
|||
|
||||
<div class="container">
|
||||
<h1>Workflow Specifications</h1>
|
||||
<button mat-flat-button color="primary" (click)="editWorkflowSpec()">
|
||||
<mat-icon>library_add</mat-icon>
|
||||
Add new workflow specification
|
||||
</button>
|
||||
<div fxLayout="row" fxLayoutGap="10px">
|
||||
<button mat-flat-button color="primary" (click)="editWorkflowSpec()">
|
||||
<mat-icon>library_add</mat-icon>
|
||||
Add new workflow specification
|
||||
</button>
|
||||
<button mat-flat-button color="primary" (click)="addWorkflowSpecCategory()">
|
||||
<mat-icon>library_add</mat-icon>
|
||||
Add category
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<mat-card *ngFor="let wfs of workflowSpecs" class="mat-elevation-z0">
|
||||
<div class="container" *ngFor="let cat of workflowSpecsByCategory">
|
||||
<h2>{{cat.display_name}}</h2>
|
||||
<mat-card *ngFor="let wfs of cat.workflow_specs" class="mat-elevation-z0">
|
||||
<mat-card-header>
|
||||
<mat-card-title>
|
||||
<h1>
|
||||
<h3>
|
||||
{{wfs.display_name}}
|
||||
<button mat-mini-fab color="primary" (click)="editWorkflowSpec(wfs)"><mat-icon>edit</mat-icon></button>
|
||||
</h1>
|
||||
</h3>
|
||||
</mat-card-title>
|
||||
<mat-card-subtitle>
|
||||
<h2>{{wfs.name}}</h2>
|
||||
<h3>{{wfs.id}}</h3>
|
||||
<h4>{{wfs.name}}</h4>
|
||||
<h5>{{wfs.id}}</h5>
|
||||
</mat-card-subtitle>
|
||||
<span fxFlex></span>
|
||||
<button mat-icon-button title="Delete this workflow specification" color="warn" (click)="confirmDeleteWorkflowSpec(wfs)">
|
||||
|
|
|
@ -1,10 +1,22 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {MatSnackBar} from '@angular/material/snack-bar';
|
||||
import {ApiService, WorkflowSpec} from 'sartography-workflow-lib';
|
||||
import {ApiService, WorkflowSpec, WorkflowSpecCategory} from 'sartography-workflow-lib';
|
||||
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';
|
||||
import {WorkflowSpecDialogComponent} from '../_dialogs/workflow-spec-dialog/workflow-spec-dialog.component';
|
||||
import {DeleteWorkflowSpecDialogData, WorkflowSpecDialogData} from '../_interfaces/dialog-data';
|
||||
import {
|
||||
DeleteWorkflowSpecDialogData,
|
||||
WorkflowSpecCategoryDialogData,
|
||||
WorkflowSpecDialogData
|
||||
} from '../_interfaces/dialog-data';
|
||||
|
||||
interface WorklflowSpecCategoryGroup {
|
||||
id: number;
|
||||
name: string;
|
||||
display_name: string;
|
||||
workflow_specs?: WorkflowSpec[];
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-workflow-spec-list',
|
||||
|
@ -14,6 +26,8 @@ import {DeleteWorkflowSpecDialogData, WorkflowSpecDialogData} from '../_interfac
|
|||
export class WorkflowSpecListComponent implements OnInit {
|
||||
workflowSpecs: WorkflowSpec[] = [];
|
||||
selectedSpec: WorkflowSpec;
|
||||
selectedCat: WorkflowSpecCategory;
|
||||
workflowSpecsByCategory: WorklflowSpecCategoryGroup[] = [];
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
|
@ -48,6 +62,27 @@ export class WorkflowSpecListComponent implements OnInit {
|
|||
});
|
||||
}
|
||||
|
||||
editWorkflowSpecCategory(selectedCat?: WorkflowSpecCategory) {
|
||||
this.selectedCat = selectedCat;
|
||||
|
||||
// Open new filename/workflow spec dialog
|
||||
const dialogRef = this.dialog.open(WorkflowSpecCategoryDialogComponent, {
|
||||
height: '65vh',
|
||||
width: '50vw',
|
||||
data: {
|
||||
id: this.selectedCat ? this.selectedCat.id : '',
|
||||
name: this.selectedCat ? this.selectedCat.name || this.selectedCat.id : '',
|
||||
display_name: this.selectedCat ? this.selectedCat.display_name : '',
|
||||
},
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((data: WorkflowSpecCategoryDialogData) => {
|
||||
if (data && data.id && data.name && data.display_name) {
|
||||
this._upsertWorkflowSpecCategory(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
confirmDeleteWorkflowSpec(wfs: WorkflowSpec) {
|
||||
const dialogRef = this.dialog.open(DeleteWorkflowSpecDialogComponent, {
|
||||
data: {
|
||||
|
@ -66,6 +101,33 @@ export class WorkflowSpecListComponent implements OnInit {
|
|||
private _loadWorkflowSpecs() {
|
||||
this.api.getWorkflowSpecList().subscribe(wfs => {
|
||||
this.workflowSpecs = wfs;
|
||||
this.workflowSpecs.forEach(wf => {
|
||||
if (wf.workflow_spec_category) {
|
||||
const cat = this.workflowSpecsByCategory.find(c => c.id === wf.workflow_spec_category_id);
|
||||
if (cat) {
|
||||
cat.workflow_specs.push(wf);
|
||||
} else {
|
||||
this.workflowSpecsByCategory.push({
|
||||
id: wf.workflow_spec_category_id,
|
||||
name: wf.workflow_spec_category.name,
|
||||
display_name: wf.workflow_spec_category.display_name,
|
||||
workflow_specs: [wf],
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const cat = this.workflowSpecsByCategory.find(c => c.id === -1);
|
||||
if (cat) {
|
||||
cat.workflow_specs.push(wf);
|
||||
} else {
|
||||
this.workflowSpecsByCategory.push({
|
||||
id: -1,
|
||||
name: 'none',
|
||||
display_name: 'No category',
|
||||
workflow_specs: [wf],
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -90,6 +152,26 @@ export class WorkflowSpecListComponent implements OnInit {
|
|||
}
|
||||
}
|
||||
|
||||
private _upsertWorkflowCategorySpecification(data: WorkflowSpecCategoryDialogData) {
|
||||
if (data.id && data.name && data.display_name) {
|
||||
|
||||
// Save old workflow spec id, in case it's changed
|
||||
const specId = this.selectedSpec ? this.selectedSpec.id : undefined;
|
||||
|
||||
const newCat: WorkflowSpecCategory = {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
display_name: data.display_name,
|
||||
};
|
||||
|
||||
if (specId) {
|
||||
this._updateWorkflowSpecCategory(specId, newCat);
|
||||
} else {
|
||||
this._addWorkflowSpecCategory(newCat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _updateWorkflowSpec(specId: string, newSpec: WorkflowSpec) {
|
||||
this.api.updateWorkflowSpecification(specId, newSpec).subscribe(spec => {
|
||||
this._loadWorkflowSpecs();
|
||||
|
@ -104,6 +186,20 @@ export class WorkflowSpecListComponent implements OnInit {
|
|||
});
|
||||
}
|
||||
|
||||
private _updateWorkflowSpecCategory(specId: string, newCat: WorkflowSpecCategory) {
|
||||
this.api.updateWorkflowSpecCategory(specId, newCat).subscribe(spec => {
|
||||
this._loadWorkflowSpecs();
|
||||
this._displayMessage('Saved changes to workflow spec.');
|
||||
});
|
||||
}
|
||||
|
||||
private _addWorkflowSpecCategory(newCat: WorkflowSpecCategory) {
|
||||
this.api.addWorkflowSpecCategory(newCat).subscribe(spec => {
|
||||
this._loadWorkflowSpecs();
|
||||
this._displayMessage('Saved new workflow spec.');
|
||||
});
|
||||
}
|
||||
|
||||
private _deleteWorkflowSpec(workflowSpec: WorkflowSpec) {
|
||||
this.api.deleteWorkflowSpecification(workflowSpec.id).subscribe(() => {
|
||||
this._loadWorkflowSpecs();
|
||||
|
@ -114,5 +210,9 @@ export class WorkflowSpecListComponent implements OnInit {
|
|||
private _displayMessage(message: string) {
|
||||
this.snackBar.open(message, 'Ok', {duration: 3000});
|
||||
}
|
||||
|
||||
addWorkflowSpecCategory() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue