Modified the home page of the modeler to show a menu on the left hand side that allows navigation between workflow specifications. Back button from the editor takes you back to the home page with the correct spec selected for quicker editing.

This commit is contained in:
Dan Funk 2020-09-02 13:57:18 -04:00
parent 55a3308334
commit 28ddf68f68
9 changed files with 207 additions and 99 deletions

6
package-lock.json generated
View File

@ -12077,9 +12077,9 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sartography-workflow-lib": {
"version": "0.0.386",
"resolved": "https://registry.npmjs.org/sartography-workflow-lib/-/sartography-workflow-lib-0.0.386.tgz",
"integrity": "sha512-4BwaVso9miYZSIxEuWqI8lXBnCz0R42QEvahyNMkNmT7Rn2rrknYchedfipxzOMoJpo3T+5hElbDMlilgwQgWQ=="
"version": "0.0.387",
"resolved": "https://registry.npmjs.org/sartography-workflow-lib/-/sartography-workflow-lib-0.0.387.tgz",
"integrity": "sha512-uVDKA++5JFEdWXaqwlVwxIK51LXpjD7vXpjS+F0SASuEYZNOzfFv8GdRvddu0kGBNaAQ+jqBFxCCbDnQxLbqfA=="
},
"sass": {
"version": "1.26.3",

View File

@ -53,7 +53,7 @@
"ngx-highlightjs": "^4.1.1",
"ngx-markdown": "^9.1.1",
"rxjs": "~6.5.4",
"sartography-workflow-lib": "0.0.386",
"sartography-workflow-lib": "0.0.387",
"tslib": "^1.13.0",
"uuid": "^7.0.2",
"zone.js": "^0.10.3"

View File

@ -1,12 +1,13 @@
import {APP_BASE_HREF} from '@angular/common';
import {Injectable, NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {ExtraOptions, RouterModule, Routes} from '@angular/router';
import {AppEnvironment, SessionRedirectComponent} from 'sartography-workflow-lib';
import {environment} from '../environments/environment.runtime';
import {HomeComponent} from './home/home.component';
import {ModelerComponent} from './modeler/modeler.component';
import {ProtocolBuilderComponent} from './protocol-builder/protocol-builder.component';
import {ReferenceFilesComponent} from './reference-files/reference-files.component';
import {WorkflowSpecListComponent} from './workflow-spec-list/workflow-spec-list.component';
@Injectable()
export class ThisEnvironment implements AppEnvironment {
@ -29,6 +30,10 @@ const routes: Routes = [
path: 'home',
component: HomeComponent
},
{
path: 'home/:spec',
component: WorkflowSpecListComponent
},
{
path: 'pb',
component: ProtocolBuilderComponent

View File

@ -50,6 +50,7 @@ import {ProtocolBuilderComponent} from './protocol-builder/protocol-builder.comp
import {ReferenceFilesComponent} from './reference-files/reference-files.component';
import {WorkflowSpecCardComponent} from './workflow-spec-card/workflow-spec-card.component';
import {WorkflowSpecListComponent} from './workflow-spec-list/workflow-spec-list.component';
import {MatSidenavModule} from '@angular/material/sidenav';
@Injectable()
export class ThisEnvironment implements AppEnvironment {
@ -124,7 +125,9 @@ export function getBaseHref(platformLocation: PlatformLocation): string {
SartographyFormsModule,
SartographyPipesModule,
SartographyWorkflowLibModule,
AppRoutingModule, // <-- This line MUST be last (https://angular.io/guide/router#module-import-order-matters)
AppRoutingModule,
MatSidenavModule,
// <-- This line MUST be last (https://angular.io/guide/router#module-import-order-matters)
],
bootstrap: [AppComponent],
entryComponents: [

View File

@ -1,4 +1,4 @@
import {Component, Input, OnInit} from '@angular/core';
import {Component, Input, OnChanges, OnInit} from '@angular/core';
import {MatDialog} from '@angular/material/dialog';
import {MatSnackBar} from '@angular/material/snack-bar';
import {ActivatedRoute, Router} from '@angular/router';
@ -21,7 +21,7 @@ import * as fileSaver from 'file-saver';
templateUrl: './file-list.component.html',
styleUrls: ['./file-list.component.scss']
})
export class FileListComponent implements OnInit {
export class FileListComponent implements OnInit, OnChanges {
@Input() workflowSpec: WorkflowSpec;
fileMetas: FileMeta[];
fileType = FileType;
@ -39,6 +39,10 @@ export class FileListComponent implements OnInit {
this._loadFileMetas();
}
ngOnChanges() {
this._loadFileMetas();
}
editFile(fileMeta?: FileMeta) {
if (fileMeta && ((fileMeta.type === FileType.BPMN) || (fileMeta.type === FileType.DMN))) {
this.router.navigate([`/modeler/${this.workflowSpec.id}/${fileMeta.id}`]);

View File

@ -1,6 +1,6 @@
<mat-toolbar [ngClass]="{'expanded': expandToolbar}">
<mat-toolbar-row *ngIf="workflowSpec">
<a mat-button [routerLink]="['/']">
<a mat-button [routerLink]="['/home', workflowSpec.name]">
<mat-icon>arrow_back</mat-icon>
Back
</a>

View File

@ -1,4 +1,3 @@
<div class="container mat-typography" fxLayout="column" fxLayoutGap="10px">
<h1>Workflow Specifications</h1>
<div fxLayout="row" fxLayoutGap="10px">
@ -11,86 +10,114 @@
Add category
</button>
</div>
<ng-container *ngFor="let cat of workflowSpecsByCategory">
<ng-container *ngIf="!(cat.id === null && cat.workflow_specs.length === 0)">
<div class="category" fxLayout="row" fxLayoutGap="10px" fxLayoutAlign="start center">
<h2>{{cat.display_name}} ({{cat.name}})</h2>
<div class="category-actions" fxLayout="row" fxLayoutGap="10px" *ngIf="cat.id !== null">
<button mat-mini-fab color="primary" (click)="editWorkflowSpecCategory(cat)">
<mat-icon>edit</mat-icon>
</button>
<button
*ngIf="cat.display_order > 0"
mat-mini-fab
title="Move up"
color="primary"
(click)="editCategoryDisplayOrder(cat.id, -1, workflowSpecsByCategory)"
>
<mat-icon>arrow_upward</mat-icon>
</button>
<button
*ngIf="cat.display_order < workflowSpecsByCategory.length - 2"
mat-mini-fab
title="Move down"
color="primary"
(click)="editCategoryDisplayOrder(cat.id, 1, workflowSpecsByCategory)"
>
<mat-icon>arrow_downward</mat-icon>
</button>
<button mat-icon-button title="Delete this category" color="warn" (click)="confirmDeleteWorkflowSpecCategory(cat)">
<mat-icon>delete</mat-icon>
</button>
<mat-drawer-container class="example-container" autosize>
<mat-drawer #drawer class="example-sidenav" mode="side" opened="true">
<ng-container *ngIf="masterStatusSpec">
<div class="category">
<h4>Master Specification</h4>
<mat-list>
<mat-list-item class="workflow-spec" fxLayout="row">
<a class="spec_menu_item" (click)="selectSpec(masterStatusSpec)">{{masterStatusSpec.display_name}}</a>
</mat-list-item>
</mat-list>
</div>
</div>
<div *ngFor="let wfs of cat.workflow_specs" class="workflow-spec">
<ng-container *ngTemplateOutlet="workflowSpecCard; context: {wfs: wfs, cat: cat}"></ng-container>
</div>
<div *ngIf="cat.workflow_specs.length === 0">No workflow specs in this category</div>
</ng-container>
</ng-container>
<mat-divider></mat-divider>
<ng-container *ngIf="masterStatusSpec">
<h1>Master Status Specification</h1>
<ng-container *ngTemplateOutlet="workflowSpecCard; context: {wfs: masterStatusSpec, cat: null}"></ng-container>
</ng-container>
</div>
</ng-container>
<mat-divider></mat-divider>
<ng-template #workflowSpecCard let-wfs="wfs" let-cat="cat">
<app-workflow-spec-card
[workflowSpec]="wfs"
[actionButtons]="actionButtons"
(workflowUpdated)="onWorkflowUpdated($event)"
></app-workflow-spec-card>
<ng-template #actionButtons>
<div class="workflow-spec-actions">
<button mat-mini-fab title="Check for errors in this workflow specification" color="accent" (click)="validateWorkflowSpec(wfs)">
<mat-icon>verified_user</mat-icon>
</button>
<button
*ngIf="cat && cat.workflow_specs.length > 0 && wfs.display_order !== 0"
mat-mini-fab
title="Move up"
color="primary"
(click)="editSpecDisplayOrder(wfs.id, -1, cat.workflow_specs)"
>
<mat-icon>arrow_upward</mat-icon>
</button>
<button
*ngIf="cat && cat.workflow_specs.length > 0 && (wfs.display_order < cat.workflow_specs.length - 1)"
mat-mini-fab
title="Move down"
color="primary"
(click)="editSpecDisplayOrder(wfs.id, 1, cat.workflow_specs)"
>
<mat-icon>arrow_downward</mat-icon>
</button>
<button mat-mini-fab title="Edit this workflow specification" color="primary" (click)="editWorkflowSpec(wfs)">
<mat-icon>edit</mat-icon>
</button>
<button mat-icon-button title="Delete this workflow specification" color="warn" (click)="confirmDeleteWorkflowSpec(wfs)">
<mat-icon>delete</mat-icon>
</button>
<ng-container *ngFor="let cat of workflowSpecsByCategory">
<ng-container *ngIf="!(cat.id === null && cat.workflow_specs.length === 0)">
<div class="category" fxLayout="row" fxLayoutGap="10px" fxLayoutAlign="start center">
<h4>{{cat.display_name}} ({{cat.name}})</h4>
<div class="category-actions" fxLayout="row" fxLayoutGap="10px" *ngIf="cat.id !== null">
<button mat-mini-fab color="primary" (click)="editWorkflowSpecCategory(cat)">
<mat-icon>edit</mat-icon>
</button>
<button
*ngIf="cat.display_order > 0"
mat-mini-fab
title="Move up"
color="primary"
(click)="editCategoryDisplayOrder(cat.id, -1, workflowSpecsByCategory)"
>
<mat-icon>arrow_upward</mat-icon>
</button>
<button
*ngIf="cat.display_order < workflowSpecsByCategory.length - 2"
mat-mini-fab
title="Move down"
color="primary"
(click)="editCategoryDisplayOrder(cat.id, 1, workflowSpecsByCategory)"
>
<mat-icon>arrow_downward</mat-icon>
</button>
<button mat-icon-button title="Delete this category" color="warn"
(click)="confirmDeleteWorkflowSpecCategory(cat)">
<mat-icon>delete</mat-icon>
</button>
</div>
</div>
<mat-list>
<mat-list-item *ngFor="let wfs of cat.workflow_specs" class="workflow-spec" fxLayout="row" fxLayoutGap="10px" fxLayoutAlign="start center">
<a class="spec_menu_item" (click)="selectSpec(wfs)">{{wfs.display_name}}</a>
<span class="spec-actions" fxLayout="row" fxLayoutGap="10px" *ngIf="cat.id !== null">
<button
*ngIf="cat && cat.workflow_specs.length > 0 && wfs.display_order !== 0"
mat-mini-fab
title="Move up"
color="primary"
(click)="editSpecDisplayOrder(wfs.id, -1, cat.workflow_specs)"
>
<mat-icon>arrow_upward</mat-icon>
</button>
<button
*ngIf="cat && cat.workflow_specs.length > 0 && (wfs.display_order < cat.workflow_specs.length - 1)"
mat-mini-fab
title="Move down"
color="primary"
(click)="editSpecDisplayOrder(wfs.id, 1, cat.workflow_specs)"
>
<mat-icon>arrow_downward</mat-icon>
</button>
</span>
<!--
<ng-container *ngTemplateOutlet="workflowSpecCard; context: {wfs: wfs, cat: cat}"></ng-container>
-->
</mat-list-item>
</mat-list>
<div *ngIf="cat.workflow_specs.length === 0">No workflow specs in this category</div>
</ng-container>
</ng-container>
</mat-drawer>
<div class="content">
<ng-container *ngIf="selectedSpec">
<ng-container *ngTemplateOutlet="workflowSpecCard; context: {wfs: selectedSpec, cat: null}"></ng-container>
</ng-container>
</div>
</ng-template>
</ng-template>
</mat-drawer-container>
<ng-template #workflowSpecCard let-wfs="wfs" let-cat="cat">
<app-workflow-spec-card
[workflowSpec]="wfs"
[actionButtons]="actionButtons"
></app-workflow-spec-card>
<ng-template #actionButtons>
<div class="workflow-spec-actions">
<button mat-mini-fab title="Check for errors in this workflow specification" color="accent"
(click)="validateWorkflowSpec(wfs)">
<mat-icon>verified_user</mat-icon>
</button>
<button mat-mini-fab title="Edit this workflow specification" color="primary" (click)="editWorkflowSpec(wfs)">
<mat-icon>edit</mat-icon>
</button>
<button mat-icon-button title="Delete this workflow specification" color="warn"
(click)="confirmDeleteWorkflowSpec(wfs)">
<mat-icon>delete</mat-icon>
</button>
</div>
</ng-template>
</ng-template>
</div>

View File

@ -1,12 +1,19 @@
@import "../../config";
.container {
padding: 3rem;
}
.category {
padding: 1rem 1rem 1rem 0;
.content {
min-height: 600px;
}
h2 {
margin: 1rem 1rem 1rem 0;
.category {
padding: 1rem 1rem 0 0;
h4 {
margin: 1rem 1rem 0 0;
}
.category-actions {
@ -22,8 +29,37 @@
opacity: 1;
}
}
}
.workflow-spec {
border-left: 4px solid $brand-gray-light;
&:hover {
border-left: 4px solid $brand-primary;
background-color: $brand-primary-tint-4;
cursor: pointer;
}
cursor: pointer;
.spec_menu_item {
flex-flow: row;
display: inline-block;
width: 300px;
margin-left: 20px;
}
.spec-actions {
width: 100px;
opacity: 0;
}
&:hover {
.spec-actions {
opacity: 1;
}
}
}
.workflow-spec-actions {
button {
margin-right: 1em;

View File

@ -22,6 +22,10 @@ import {
WorkflowSpecDialogData
} from '../_interfaces/dialog-data';
import {ApiErrorsComponent} from 'sartography-workflow-lib';
import {ActivatedRoute} from '@angular/router';
import {map} from 'rxjs/operators';
import {Location} from '@angular/common';
import {environment} from '../../environments/environment.runtime';
export interface WorkflowSpecCategoryGroup {
@ -51,12 +55,20 @@ export class WorkflowSpecListComponent implements OnInit {
private api: ApiService,
private snackBar: MatSnackBar,
private bottomSheet: MatBottomSheet,
public dialog: MatDialog
public dialog: MatDialog,
private route: ActivatedRoute,
private location: Location
) {
this._loadWorkflowSpecCategories();
}
ngOnInit() {
this.route.paramMap.subscribe(paramMap => {
if (paramMap.has('spec')) {
this._loadWorkflowSpecCategories(paramMap.get('spec'));
} else {
this._loadWorkflowSpecCategories();
}
});
}
validateWorkflowSpec(wfs: WorkflowSpec) {
@ -69,6 +81,11 @@ export class WorkflowSpecListComponent implements OnInit {
});
}
selectSpec(selectedSpec?: WorkflowSpec) {
this.selectedSpec = selectedSpec;
this.location.replaceState(environment.homeRoute + '/' + selectedSpec.name);
}
editWorkflowSpec(selectedSpec?: WorkflowSpec) {
this.selectedSpec = selectedSpec;
const hasDisplayOrder = this.selectedSpec && isNumberDefined(this.selectedSpec.display_order);
@ -159,7 +176,7 @@ export class WorkflowSpecListComponent implements OnInit {
sortByDisplayOrder = (a, b) => (a.display_order < b.display_order) ? -1 : 1;
private _loadWorkflowSpecCategories() {
private _loadWorkflowSpecCategories(selectedSpecName: string = null) {
this.api.getWorkflowSpecCategoryList().subscribe(cats => {
this.categories = cats.sort(this.sortByDisplayOrder);
@ -177,13 +194,15 @@ export class WorkflowSpecListComponent implements OnInit {
this.workflowSpecsByCategory[i + 1].workflow_specs = [];
});
this._loadWorkflowSpecs();
this._loadWorkflowSpecs(selectedSpecName);
});
}
private _loadWorkflowSpecs() {
private _loadWorkflowSpecs(selectedSpecName: String = null) {
this.api.getWorkflowSpecList().subscribe(wfs => {
this.workflowSpecs = wfs;
this.workflowSpecsByCategory.forEach(cat => {
cat.workflow_specs = this.workflowSpecs
.filter(wf => {
@ -195,6 +214,20 @@ export class WorkflowSpecListComponent implements OnInit {
})
.sort(this.sortByDisplayOrder);
});
// Set the selected workflow to something sensible.
if (!selectedSpecName && this.selectedSpec) {
selectedSpecName = this.selectedSpec.name;
}
if (selectedSpecName) {
this.workflowSpecs.forEach(ws => {
if (selectedSpecName && selectedSpecName === ws.name) {
this.selectedSpec = ws;
}
});
} else {
this.selectedSpec = this.masterStatusSpec;
}
});
}
@ -251,7 +284,7 @@ export class WorkflowSpecListComponent implements OnInit {
private _addWorkflowSpec(newSpec: WorkflowSpec) {
this.api.addWorkflowSpecification(newSpec).subscribe(_ => {
this._loadWorkflowSpecs();
this._loadWorkflowSpecs(newSpec.name);
this._displayMessage('Saved new workflow spec.');
});
}