You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streampipes.apache.org by ri...@apache.org on 2022/07/12 20:52:46 UTC

[incubator-streampipes] 02/02: [STREAMPIPES-537] Change routing of pipeline and pipeline details, make pipeline editor accessible from pipeline view

This is an automated email from the ASF dual-hosted git repository.

riemer pushed a commit to branch STREAMPIPES-558
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git

commit 5e000fd6a9e8112af253a3004b98f94318a4eebc
Author: Dominik Riemer <do...@gmail.com>
AuthorDate: Tue Jul 12 22:52:30 2022 +0200

    [STREAMPIPES-537] Change routing of pipeline and pipeline details, make pipeline editor accessible from pipeline view
---
 ui/deployment/app-routing.module.mst               |   2 -
 ui/deployment/dev/config.yml                       |   1 -
 ui/deployment/modules.yml                          |   2 +-
 .../basic-nav-tabs/basic-nav-tabs.component.html   |  19 +-
 .../basic-nav-tabs/basic-nav-tabs.component.scss   |   4 +
 .../basic-nav-tabs/basic-nav-tabs.component.ts     |  10 +
 .../existing-adapters.component.scss               |   1 -
 ui/src/app/editor/editor.component.ts              |   6 +-
 .../components/edit/quickedit.component.html       |  61 +++---
 .../components/edit/quickedit.component.ts         | 231 ++++++++++-----------
 .../monitoring/pipeline-monitoring.component.html  |  79 +++----
 .../monitoring/pipeline-monitoring.component.ts    |  51 ++---
 .../pipeline-details-overview.component.html       |  46 ++++
 .../pipeline-details-overview.component.scss}      |   1 -
 .../pipeline-details-overview.component.ts         |  54 +++++
 .../components/sp-pipeline-details.directive.ts    |  66 ++++++
 .../app/pipeline-details/pipeline-details-tabs.ts} |  30 +--
 .../pipeline-details.component.html                |  65 ------
 .../pipeline-details/pipeline-details.component.ts |  77 -------
 .../pipeline-details/pipeline-details.module.ts    |  67 +++---
 .../pipeline-overview.component.scss               |   4 +-
 ui/src/app/pipelines/pipelines.component.html      | 118 ++++++-----
 ui/src/app/pipelines/pipelines.component.ts        |   9 +-
 ui/src/app/pipelines/pipelines.module.ts           |  52 +++++
 .../services/pipeline-operations.service.ts        |   8 +-
 25 files changed, 581 insertions(+), 483 deletions(-)

diff --git a/ui/deployment/app-routing.module.mst b/ui/deployment/app-routing.module.mst
index 29e46bcf0..093d7a515 100644
--- a/ui/deployment/app-routing.module.mst
+++ b/ui/deployment/app-routing.module.mst
@@ -22,7 +22,6 @@ import {HomeComponent} from "./home/home.component";
 import {LoginComponent} from "./login/components/login/login.component";
 import {SetupComponent} from "./login/components/setup/setup.component";
 import {StreampipesComponent} from "./core/components/streampipes/streampipes.component";
-import {PipelineDetailsComponent} from "./pipeline-details/pipeline-details.component";
 import {StandaloneDashboardComponent} from "./dashboard/components/standalone/standalone-dashboard.component";
 import {AuthCanActivateChildrenGuard} from "./_guards/auth.can-activate-children.guard";
 import {ConfiguredCanActivateGuard} from "./_guards/configured.can-activate.guard";
@@ -70,7 +69,6 @@ const routes: Routes = [
 
       { path: 'notifications', component: NotificationsComponent },
       { path: 'info', component: InfoComponent },
-      { path: 'pipeline-details', component: PipelineDetailsComponent },
       { path: 'profile', component: ProfileComponent},
     ], canActivateChild: [AuthCanActivateChildrenGuard, PageAuthGuard] }
 ];
diff --git a/ui/deployment/dev/config.yml b/ui/deployment/dev/config.yml
index d134f440f..8a72ac568 100644
--- a/ui/deployment/dev/config.yml
+++ b/ui/deployment/dev/config.yml
@@ -19,7 +19,6 @@ login:
   logo-navigation: 'deployment/dev/img/logo-navigation.png'
   favicon: 'deployment/dev/img/favicon.png'
 modules:
-  - spEditor
   - spPipelines
   - spConnect
   - spDashboard
diff --git a/ui/deployment/modules.yml b/ui/deployment/modules.yml
index f12f05d4f..f71f14a63 100644
--- a/ui/deployment/modules.yml
+++ b/ui/deployment/modules.yml
@@ -42,7 +42,7 @@ spConnect:
   admin: False
   pageNames: 'PageName.CONNECT'
 spPipelines:
-  componentImport: True
+  componentImport: False
   ng5_moduleName: 'PipelinesModule'
   ng5_component: 'PipelinesComponent'
   ng5_componentPath: './pipelines/pipelines.component'
diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.html b/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.html
index 25f3eadd1..12a453415 100644
--- a/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.html
+++ b/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.html
@@ -18,8 +18,20 @@
 
 <div fxLayout="column" class="page-container">
     <div fxLayout="row" class="p-0 sp-bg-lightgray page-container-nav">
-        <div fxLayout="fill">
-            <div fxFlex="100" class="page-container-nav">
+            <div fxFlex="100" class="page-container-nav pr-5" fxLayout="row">
+                <div fxLayout="row"
+                     fxLayoutAlign="start center"
+                     style="border-right: 2px solid var(--color-bg-2)" *ngIf="showBackLink">
+                    <button mat-button
+                            mat-icon-button
+                            color="accent"
+                            matTooltip="Back"
+                            (click)="navigateBack()"
+                            class="edit-menu-btn"
+                            data-cy="save-data-explorer-go-back-to-overview">
+                        <mat-icon>arrow_back</mat-icon>
+                    </button>
+                </div>
                 <nav mat-tab-nav-bar color="accent">
                     <a mat-tab-link *ngFor="let item of spNavigationItems"
                        (click)="navigateTo(item)"
@@ -27,8 +39,9 @@
                         <span class="upper-case">{{item.itemTitle}}</span>
                     </a>
                 </nav>
+                <span fxFlex></span>
+                <ng-content select="[nav]" fxFlex="100" fxLayout="row" fxLayoutAlign="end center"></ng-content>
             </div>
-        </div>
     </div>
 
     <div class="fixed-height page-container-padding-inner" fxLayout="column" fxFlex="100">
diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.scss b/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.scss
index ce626dc9d..19c8ca2a3 100644
--- a/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.scss
+++ b/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.scss
@@ -41,3 +41,7 @@
 .upper-case {
   text-transform: uppercase;
 }
+
+.pr-5 {
+  padding-right: 5px;
+}
diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.ts b/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.ts
index bdace86dd..5ab53a56b 100644
--- a/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.ts
+++ b/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.ts
@@ -33,6 +33,12 @@ export class SpBasicNavTabsComponent {
   @Input()
   activeLink: string;
 
+  @Input()
+  showBackLink = false;
+
+  @Input()
+  backLinkTarget: string[] = [];
+
   constructor(private router: Router) {
 
   }
@@ -40,4 +46,8 @@ export class SpBasicNavTabsComponent {
   navigateTo(spNavigationItem: SpNavigationItem) {
     this.router.navigate(spNavigationItem.itemLink);
   }
+
+  navigateBack() {
+    this.router.navigate(this.backLinkTarget);
+  }
 }
diff --git a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.scss b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.scss
index b0368049f..f38f54d32 100644
--- a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.scss
+++ b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.scss
@@ -48,6 +48,5 @@
 .filter-bar-margin {
   margin-left: 10px;
   padding-left: 10px;
-  border-left: 2px solid var(--color-bg-2)
 }
 
diff --git a/ui/src/app/editor/editor.component.ts b/ui/src/app/editor/editor.component.ts
index 76caa68e6..3ebcb368a 100644
--- a/ui/src/app/editor/editor.component.ts
+++ b/ui/src/app/editor/editor.component.ts
@@ -60,9 +60,9 @@ export class EditorComponent implements OnInit {
   }
 
   ngOnInit() {
-    this.activatedRoute.queryParams.subscribe(params => {
-      if (params['pipeline']) {
-        this.currentModifiedPipelineId = params['pipeline'];
+    this.activatedRoute.params.subscribe(params => {
+      if (params.pipelineId) {
+        this.currentModifiedPipelineId = params.pipelineId;
       }
     });
     zip(this.pipelineElementService.getDataStreams(),
diff --git a/ui/src/app/pipeline-details/components/edit/quickedit.component.html b/ui/src/app/pipeline-details/components/edit/quickedit.component.html
index ba52554b2..2af6ed274 100644
--- a/ui/src/app/pipeline-details/components/edit/quickedit.component.html
+++ b/ui/src/app/pipeline-details/components/edit/quickedit.component.html
@@ -16,28 +16,40 @@
   ~
   -->
 
-<div>
-    <div class="assembly-options-preview sp-blue-bg">
-        <div fxLayout="row" fxLayoutAlign="start center">
-            <h4>Edit configuration</h4>
-            <span fxFlex></span>
-            <button color="accent" mat-button mat-raised-button matTooltip="Save Pipeline" matTooltipPosition="above"
-                    style="display:flex;align-items:center;" class="settings-bar-icon-button"
-                    [disabled]="(!formValid || pipelineUpdating)"
-                    (click)="updatePipeline()">
-                <mat-icon>save</mat-icon>
-                <span>&nbsp;Update pipeline</span>
-            </button>
+<sp-basic-nav-tabs [spNavigationItems]="tabs"
+                   [activeLink]="'quick-edit'"
+                   [showBackLink]="true"
+                   [backLinkTarget]="['pipelines']">
+
+    <div fxLayout="column" class="page-container-padding" *ngIf="pipeline">
+        <pipeline-preview [jspcanvas]="'assembly-preview'"
+                          [pipeline]="pipeline"
+                          (selectedElementEmitter)="selectElement($event)"
+                          style="margin-bottom:15px;"
+                          class="md-padding"
+                          *ngIf="pipelineAvailable"></pipeline-preview>
+        <div class="assembly-options-preview sp-blue-bg">
+            <div fxLayout="row" fxLayoutAlign="start center">
+                <h4>Edit configuration</h4>
+                <span fxFlex></span>
+                <button color="accent" mat-button mat-raised-button matTooltip="Save Pipeline"
+                        matTooltipPosition="above"
+                        style="display:flex;align-items:center;" class="settings-bar-icon-button"
+                        [disabled]="(!formValid || pipelineUpdating)"
+                        (click)="updatePipeline()">
+                    <mat-icon>save</mat-icon>
+                    <span>&nbsp;Update pipeline</span>
+                </button>
+            </div>
         </div>
-    </div>
-    <div class="sp-blue-border">
-        <div *ngIf="selectedElement">
-            <div fxLayout="column" style="padding:5px;padding-left:10px;">
-                <pipeline-elements-row [pipeline]="pipeline"
-                                       [element]="selectedElement"></pipeline-elements-row>
+        <div class="sp-blue-border">
+            <div *ngIf="selectedElement">
+                <div fxLayout="column" style="padding:5px;padding-left:10px;">
+                    <pipeline-elements-row [pipeline]="pipeline"
+                                           [element]="selectedElement"></pipeline-elements-row>
 
-                <div fxFlex="100" fxLayout="column"
-                     *ngIf="isInvocable">
+                    <div fxFlex="100" fxLayout="column"
+                         *ngIf="isInvocable">
                         <form [formGroup]="parentForm" fxFlex="100">
                             <app-static-property *ngFor="let config of _selectedElement.staticProperties"
                                                  [staticProperty]="config"
@@ -48,11 +60,12 @@
                                                  [fieldName]="config.internalName">
                             </app-static-property>
                         </form>
+                    </div>
                 </div>
             </div>
-        </div>
-        <div fxLayout="column" fxLayoutAlign="center center" *ngIf="!selectedElement">
-            (select an element in the preview window to modify it)
+            <div fxLayout="column" fxLayoutAlign="center center" *ngIf="!selectedElement">
+                (select an element in the preview window to modify it)
+            </div>
         </div>
     </div>
-</div>
+</sp-basic-nav-tabs>
diff --git a/ui/src/app/pipeline-details/components/edit/quickedit.component.ts b/ui/src/app/pipeline-details/components/edit/quickedit.component.ts
index 15877ec79..d63894a99 100644
--- a/ui/src/app/pipeline-details/components/edit/quickedit.component.ts
+++ b/ui/src/app/pipeline-details/components/edit/quickedit.component.ts
@@ -16,135 +16,132 @@
  *
  */
 
+import { AfterViewInit, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
 import {
-    AfterViewInit,
-    ChangeDetectorRef,
-    Component,
-    EventEmitter,
-    Input,
-    OnInit,
-    Output
-} from '@angular/core';
-import {
-    PipelineService,
-    DataProcessorInvocation,
-    DataSinkInvocation,
-    EventSchema,
-    Pipeline
+  DataProcessorInvocation,
+  DataSinkInvocation,
+  EventSchema,
+  PipelineService
 } from '@streampipes/platform-services';
 import { PipelineElementUnion } from '../../../editor/model/editor.model';
 import { FormBuilder, FormGroup } from '@angular/forms';
+import { SpPipelineDetailsDirective } from '../sp-pipeline-details.directive';
+import { ActivatedRoute } from '@angular/router';
+import { AuthService } from '../../../services/auth.service';
 
 @Component({
-    selector: 'quick-edit',
-    templateUrl: './quickedit.component.html',
+  selector: 'quick-edit',
+  templateUrl: './quickedit.component.html',
 })
-export class QuickEditComponent implements OnInit, AfterViewInit {
-
-    @Input()
-    pipeline: Pipeline;
-
-    @Output()
-    reloadPipelineEmitter: EventEmitter<void> = new EventEmitter<void>();
-
-    _selectedElement: PipelineElementUnion;
-
-    eventSchemas: EventSchema[];
-
-    parentForm: FormGroup;
-    formValid: boolean;
-    viewInitialized = false;
-
-    isInvocable = false;
-    isDataProcessor = false;
-
-    pipelineUpdating = false;
-
-    constructor(private pipelineService: PipelineService,
-                private fb: FormBuilder,
-                private changeDetectorRef: ChangeDetectorRef) {
-
-    }
-
-    ngOnInit() {
-        this.parentForm = this.fb.group({
-        });
-
-        this.parentForm.statusChanges.subscribe((status) => {
-            this.formValid = this.viewInitialized && this.parentForm.valid;
-        });
-    }
-
-    ngAfterViewInit(): void {
-        this.viewInitialized = true;
-        this.formValid = this.viewInitialized && this.parentForm.valid;
-        this.changeDetectorRef.detectChanges();
-    }
-
-    updatePipeline() {
-        this.pipelineUpdating = true;
-        this.updatePipelineElement();
-        this.pipelineService.updatePipeline(this.pipeline).subscribe(data => {
-            this.reloadPipelineEmitter.emit();
-            this.pipelineUpdating = false;
-        });
-    }
-
-    updatePipelineElement() {
-        if (this._selectedElement instanceof DataProcessorInvocation) {
-            this.updateDataProcessor();
-        } else if (this._selectedElement instanceof DataSinkInvocation) {
-            this.updateDataSink();
-        }
+export class QuickEditComponent extends SpPipelineDetailsDirective implements OnInit, AfterViewInit {
+
+  _selectedElement: PipelineElementUnion;
+
+  eventSchemas: EventSchema[];
+
+  parentForm: FormGroup;
+  formValid: boolean;
+  viewInitialized = false;
+
+  isInvocable = false;
+  isDataProcessor = false;
+
+  pipelineUpdating = false;
+
+  constructor(activatedRoute: ActivatedRoute,
+              pipelineService: PipelineService,
+              authService: AuthService,
+              private fb: FormBuilder,
+              private changeDetectorRef: ChangeDetectorRef) {
+    super(activatedRoute, pipelineService, authService);
+  }
+
+  ngOnInit() {
+    super.onInit();
+    this.parentForm = this.fb.group({});
+
+    this.parentForm.statusChanges.subscribe((status) => {
+      this.formValid = this.viewInitialized && this.parentForm.valid;
+    });
+  }
+
+  ngAfterViewInit(): void {
+    this.viewInitialized = true;
+    this.formValid = this.viewInitialized && this.parentForm.valid;
+    this.changeDetectorRef.detectChanges();
+  }
+
+  updatePipeline() {
+    this.pipelineUpdating = true;
+    this.updatePipelineElement();
+    this.pipelineService.updatePipeline(this.pipeline).subscribe(data => {
+      this.loadPipeline();
+      this.pipelineUpdating = false;
+    });
+  }
+
+  updatePipelineElement() {
+    if (this._selectedElement instanceof DataProcessorInvocation) {
+      this.updateDataProcessor();
+    } else if (this._selectedElement instanceof DataSinkInvocation) {
+      this.updateDataSink();
     }
-
-    updateDataProcessor() {
-        const dataProcessors: DataProcessorInvocation[] = [];
-        this.pipeline.sepas.forEach(p => {
-           if (p.dom === this._selectedElement.dom) {
-               dataProcessors.push(this._selectedElement as DataProcessorInvocation);
-           } else {
-                dataProcessors.push(p);
-           }
-        });
-        this.pipeline.sepas = dataProcessors;
+  }
+
+  updateDataProcessor() {
+    const dataProcessors: DataProcessorInvocation[] = [];
+    this.pipeline.sepas.forEach(p => {
+      if (p.dom === this._selectedElement.dom) {
+        dataProcessors.push(this._selectedElement as DataProcessorInvocation);
+      } else {
+        dataProcessors.push(p);
+      }
+    });
+    this.pipeline.sepas = dataProcessors;
+  }
+
+  updateDataSink() {
+    const dataSinks: DataSinkInvocation[] = [];
+    this.pipeline.actions.forEach(p => {
+      if (p.dom === this._selectedElement.dom) {
+        dataSinks.push(this._selectedElement as DataSinkInvocation);
+      } else {
+        dataSinks.push(p);
+      }
+    });
+    this.pipeline.actions = dataSinks;
+  }
+
+  get selectedElement() {
+    return this._selectedElement;
+  }
+
+  @Input()
+  set selectedElement(selectedElement: PipelineElementUnion) {
+    if (this._selectedElement) {
+      this.updatePipelineElement();
     }
-
-    updateDataSink() {
-        const dataSinks: DataSinkInvocation[] = [];
-        this.pipeline.actions.forEach(p => {
-            if (p.dom === this._selectedElement.dom) {
-                dataSinks.push(this._selectedElement as DataSinkInvocation);
-            } else {
-                dataSinks.push(p);
-            }
-        });
-        this.pipeline.actions = dataSinks;
+    this._selectedElement = selectedElement;
+    this.eventSchemas = [];
+    if (this._selectedElement instanceof DataProcessorInvocation || this._selectedElement instanceof DataSinkInvocation) {
+      (this._selectedElement as any).inputStreams.forEach(is => {
+        this.eventSchemas = this.eventSchemas.concat(is.eventSchema);
+      });
     }
+    this.updateTypeInfo();
+  }
 
-    get selectedElement() {
-        return this._selectedElement;
-    }
+  updateTypeInfo() {
+    this.isDataProcessor = this._selectedElement instanceof DataProcessorInvocation;
+    this.isInvocable = this._selectedElement instanceof DataProcessorInvocation ||
+      this._selectedElement instanceof DataSinkInvocation;
+  }
 
-    @Input()
-    set selectedElement(selectedElement: PipelineElementUnion) {
-        if (this._selectedElement) {
-            this.updatePipelineElement();
-        }
-        this._selectedElement = selectedElement;
-        this.eventSchemas = [];
-        if (this._selectedElement instanceof DataProcessorInvocation || this._selectedElement instanceof DataSinkInvocation) {
-            (this._selectedElement as any).inputStreams.forEach(is => {
-                this.eventSchemas = this.eventSchemas.concat(is.eventSchema);
-            });
-        }
-        this.updateTypeInfo();
-    }
+  selectElement(element: PipelineElementUnion) {
+    this.selectedElement = element;
+  }
 
-    updateTypeInfo() {
-        this.isDataProcessor = this._selectedElement instanceof DataProcessorInvocation;
-        this.isInvocable = this._selectedElement instanceof DataProcessorInvocation ||
-            this._selectedElement instanceof DataSinkInvocation;
-    }
+  onPipelineAvailable(): void {
+  }
 }
 
diff --git a/ui/src/app/pipeline-details/components/monitoring/pipeline-monitoring.component.html b/ui/src/app/pipeline-details/components/monitoring/pipeline-monitoring.component.html
index 4c27a4634..3f4b4c109 100644
--- a/ui/src/app/pipeline-details/components/monitoring/pipeline-monitoring.component.html
+++ b/ui/src/app/pipeline-details/components/monitoring/pipeline-monitoring.component.html
@@ -16,51 +16,52 @@
   ~
   -->
 
+<sp-basic-nav-tabs [spNavigationItems]="tabs"
+                   [activeLink]="'monitoring'"
+                   [showBackLink]="true"
+                   [backLinkTarget]="['pipelines']">
 
-<div fxFlex="100" fxLayout="column">
-    <div fxLayout="column" class="fixed-height add-options">
-        <div class="add-options-item" fxLayoutAlign="start center" fxLayout="row" style="padding-right:10px;">
-            <div fxFlex="100" fxLayout="row" fxLayoutAlign="end center">
-                <mat-slide-toggle [(ngModel)]="autoRefresh" color="accent">Auto refresh</mat-slide-toggle>
-            </div>
-        </div>
+    <div nav fxFlex="100" fxLayout="row" fxLayoutAlign="end center">
+        <mat-slide-toggle [(ngModel)]="autoRefresh" color="accent">Auto refresh</mat-slide-toggle>
     </div>
-    <div fxFlex="100" fxLayout="column" class="page-container-padding-inner">
-        <div fxFlex="100" *ngIf="!pipeline.running" fxLayout="column" fxLayoutAlign="center center">
-            <div class="error-message">(monitoring info is only available for running pipelines)</div>
-            <button mat-button mat-raised-button color="accent"
-                    matTooltip="Start Pipeline" matTooltipPosition="above"
-                    *ngIf="hasPipelineWritePrivileges"
-                    (click)="startPipeline()">
-                <mat-icon>play_arrow</mat-icon>
-                <span>&nbsp;Start pipeline</span>
-            </button>
-        </div>
-        <div fxFlex="100" *ngIf="pipeline.running && pipelineMonitoringInfoAvailable">
-            <div *ngFor="let pipelineElement of allElements" fxLayout="column" class="mb-10">
-                <div class="assembly-options-preview sp-blue-bg" [id]="pipelineElement.elementId">
-                    <div fxLayout="row" fxLayoutAlign="start center">
-                        <h4>{{pipelineElement.name}}</h4>
-                    </div>
-                </div>
-                <div class="sp-blue-border pipeline-element-statistics-panel">
-                    <div fxFlex="100" fxLayout="row">
-                        <div fxFlex="20" fxLayoutAlign="start start">
-                            <pipeline-elements-row style="width: 100%;"
-                                                   [showDescription]="false"
-                                                   [pipeline]="pipeline"
-                                                   [element]="pipelineElement"></pipeline-elements-row>
+    <div fxLayout="column" class="page-container-padding" *ngIf="pipeline">
+        <div fxFlex="100" fxLayout="column" class="page-container-padding-inner">
+            <div fxFlex="100" *ngIf="!pipeline.running" fxLayout="column" fxLayoutAlign="center center">
+                <div class="error-message">(monitoring info is only available for running pipelines)</div>
+                <button mat-button mat-raised-button color="accent"
+                        matTooltip="Start Pipeline" matTooltipPosition="above"
+                        *ngIf="hasPipelineWritePrivileges"
+                        (click)="startPipeline()">
+                    <mat-icon>play_arrow</mat-icon>
+                    <span>&nbsp;Start pipeline</span>
+                </button>
+            </div>
+            <div fxFlex="100" *ngIf="pipeline.running && pipelineMonitoringInfoAvailable">
+                <div *ngFor="let pipelineElement of allElements" fxLayout="column" class="mb-10">
+                    <div class="assembly-options-preview sp-blue-bg" [id]="pipelineElement.elementId">
+                        <div fxLayout="row" fxLayoutAlign="start center">
+                            <h4>{{pipelineElement.name}}</h4>
                         </div>
-                        <div fxFlex="80" fxLayoutAlign="start center">
-                            <pipeline-element-statistics
-                                    [pipeline]="pipeline"
-                                    [pipelineElement]="pipelineElement"
-                                    [pipelineElementMonitoringInfo]="pipelineElementMonitoringInfo.get(pipelineElement.elementId)">
-                            </pipeline-element-statistics>
+                    </div>
+                    <div class="sp-blue-border pipeline-element-statistics-panel">
+                        <div fxFlex="100" fxLayout="row">
+                            <div fxFlex="20" fxLayoutAlign="start start">
+                                <pipeline-elements-row style="width: 100%;"
+                                                       [showDescription]="false"
+                                                       [pipeline]="pipeline"
+                                                       [element]="pipelineElement"></pipeline-elements-row>
+                            </div>
+                            <div fxFlex="80" fxLayoutAlign="start center">
+                                <pipeline-element-statistics
+                                        [pipeline]="pipeline"
+                                        [pipelineElement]="pipelineElement"
+                                        [pipelineElementMonitoringInfo]="pipelineElementMonitoringInfo.get(pipelineElement.elementId)">
+                                </pipeline-element-statistics>
+                            </div>
                         </div>
                     </div>
                 </div>
             </div>
         </div>
     </div>
-</div>
+</sp-basic-nav-tabs>
diff --git a/ui/src/app/pipeline-details/components/monitoring/pipeline-monitoring.component.ts b/ui/src/app/pipeline-details/components/monitoring/pipeline-monitoring.component.ts
index ea83eda39..3cff74f74 100644
--- a/ui/src/app/pipeline-details/components/monitoring/pipeline-monitoring.component.ts
+++ b/ui/src/app/pipeline-details/components/monitoring/pipeline-monitoring.component.ts
@@ -16,29 +16,28 @@
  *
  */
 
-import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
+import { Component, OnDestroy, OnInit } from '@angular/core';
 import {
-  DataProcessorInvocation, DataSinkInvocation,
-  Pipeline, PipelineElementMonitoringInfo,
+  DataProcessorInvocation,
+  DataSinkInvocation,
+  PipelineElementMonitoringInfo,
   PipelineMonitoringInfo,
-  SpDataSet, SpDataStream
+  PipelineMonitoringService,
+  PipelineService,
+  SpDataSet,
+  SpDataStream
 } from '@streampipes/platform-services';
-import { PipelineMonitoringService } from '@streampipes/platform-services';
 import { PipelineOperationsService } from '../../../pipelines/services/pipeline-operations.service';
 import { AuthService } from '../../../services/auth.service';
-import { UserPrivilege } from '../../../_enums/user-privilege.enum';
+import { SpPipelineDetailsDirective } from '../sp-pipeline-details.directive';
+import { ActivatedRoute } from '@angular/router';
 
 @Component({
   selector: 'pipeline-monitoring',
   templateUrl: './pipeline-monitoring.component.html',
   styleUrls: ['./pipeline-monitoring.component.scss']
 })
-export class PipelineMonitoringComponent implements OnInit, OnDestroy {
-
-  _pipeline: Pipeline;
-
-  @Output()
-  reloadPipelineEmitter: EventEmitter<boolean> = new EventEmitter<boolean>();
+export class PipelineMonitoringComponent extends SpPipelineDetailsDirective implements OnInit, OnDestroy {
 
   pipelineMonitoringInfo: PipelineMonitoringInfo;
   pipelineMonitoringInfoAvailable = false;
@@ -49,19 +48,16 @@ export class PipelineMonitoringComponent implements OnInit, OnDestroy {
 
   pipelineElementMonitoringInfo: Map<string, PipelineElementMonitoringInfo>;
 
-  hasPipelineWritePrivileges = false;
-
-  constructor(private pipelineMonitoringService: PipelineMonitoringService,
-              private pipelineOperationsService: PipelineOperationsService,
-              private authService: AuthService) {
+  constructor(activatedRoute: ActivatedRoute,
+              pipelineService: PipelineService,
+              authService: AuthService,
+              private pipelineMonitoringService: PipelineMonitoringService,
+              private pipelineOperationsService: PipelineOperationsService) {
+    super(activatedRoute, pipelineService, authService);
   }
 
   ngOnInit(): void {
-    this.authService.user$.subscribe(user => {
-      this.hasPipelineWritePrivileges = this.authService.hasRole(UserPrivilege.PRIVILEGE_WRITE_PIPELINE);
-    });
-    this.collectAllElements();
-    this.checkMonitoringInfoCollection();
+    super.onInit();
   }
 
   checkMonitoringInfoCollection() {
@@ -104,17 +100,12 @@ export class PipelineMonitoringComponent implements OnInit, OnDestroy {
   }
 
   startPipeline() {
-    this.pipelineOperationsService.startPipeline(this.pipeline._id, this.reloadPipelineEmitter);
+    //this.pipelineOperationsService.startPipeline(this.pipeline._id, () => this.loadPipeline());
   }
 
-  @Input()
-  set pipeline(pipeline: Pipeline) {
-    this._pipeline = pipeline;
+  onPipelineAvailable(): void {
+    this.collectAllElements();
     this.checkMonitoringInfoCollection();
   }
 
-  get pipeline() {
-    return this._pipeline;
-  }
-
 }
diff --git a/ui/src/app/pipeline-details/components/overview/pipeline-details-overview.component.html b/ui/src/app/pipeline-details/components/overview/pipeline-details-overview.component.html
new file mode 100644
index 000000000..eca67efd2
--- /dev/null
+++ b/ui/src/app/pipeline-details/components/overview/pipeline-details-overview.component.html
@@ -0,0 +1,46 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~    http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  ~
+  -->
+
+<sp-basic-nav-tabs [spNavigationItems]="tabs" [activeLink]="'overview'" [showBackLink]="true" [backLinkTarget]="['pipelines']">
+    <div fxLayout="column" class="page-container-padding">
+        <div fxFlex="100" fxLayout="column">
+            <pipeline-preview [jspcanvas]="'assembly-preview'"
+                              [pipeline]="pipeline"
+                              (selectedElementEmitter)="selectElement($event)"
+                              style="margin-bottom:15px;"
+                              class="md-padding"
+                              *ngIf="pipelineAvailable"></pipeline-preview>
+            <div fxFlex fxLayout="row" fxLayoutAlign="start top" *ngIf="pipelineAvailable">
+                <div fxFlex="60" class="md-padding">
+                    <div fxFlex fxLayout="column">
+                        <pipeline-elements [pipeline]="pipeline"
+                                           [selectedElement]="selectedElement"></pipeline-elements>
+                    </div>
+                </div>
+                <div fxFlex="40" class="md-padding">
+                    <div fxFlex fxLayout="column">
+                        <pipeline-actions (reloadPipelineEmitter)="loadPipeline()" [pipeline]="pipeline"
+                                          style="margin-bottom:15px;"
+                                          *ngIf="hasPipelineWritePrivileges"></pipeline-actions>
+                        <pipeline-status [pipeline]="pipeline"></pipeline-status>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</sp-basic-nav-tabs>
diff --git a/ui/src/app/pipeline-details/pipeline-details.component.scss b/ui/src/app/pipeline-details/components/overview/pipeline-details-overview.component.scss
similarity index 99%
rename from ui/src/app/pipeline-details/pipeline-details.component.scss
rename to ui/src/app/pipeline-details/components/overview/pipeline-details-overview.component.scss
index a375af7ff..1100ab49e 100644
--- a/ui/src/app/pipeline-details/pipeline-details.component.scss
+++ b/ui/src/app/pipeline-details/components/overview/pipeline-details-overview.component.scss
@@ -19,4 +19,3 @@
 .md-padding {
   padding: 10px;
 }
-
diff --git a/ui/src/app/pipeline-details/components/overview/pipeline-details-overview.component.ts b/ui/src/app/pipeline-details/components/overview/pipeline-details-overview.component.ts
new file mode 100644
index 000000000..2ace0e015
--- /dev/null
+++ b/ui/src/app/pipeline-details/components/overview/pipeline-details-overview.component.ts
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { SpPipelineDetailsDirective } from '../sp-pipeline-details.directive';
+import { AuthService } from '../../../services/auth.service';
+import { PipelineService } from '@streampipes/platform-services';
+import { PipelineElementUnion } from '../../../editor/model/editor.model';
+
+@Component({
+  selector: 'sp-pipeline-details-overview-component',
+  templateUrl: './pipeline-details-overview.component.html',
+  styleUrls: ['./pipeline-details-overview.component.scss']
+})
+export class SpPipelineDetailsOverviewComponent extends SpPipelineDetailsDirective implements OnInit {
+
+  tabs = [];
+  selectedElement: PipelineElementUnion;
+
+  constructor(activatedRoute: ActivatedRoute,
+              pipelineService: PipelineService,
+              authService: AuthService) {
+    super(activatedRoute, pipelineService, authService);
+  }
+
+  ngOnInit(): void {
+    super.onInit();
+  }
+
+  selectElement(element: PipelineElementUnion) {
+    this.selectedElement = element;
+  }
+
+  onPipelineAvailable(): void {
+  }
+
+
+}
diff --git a/ui/src/app/pipeline-details/components/sp-pipeline-details.directive.ts b/ui/src/app/pipeline-details/components/sp-pipeline-details.directive.ts
new file mode 100644
index 000000000..bf4bb26b0
--- /dev/null
+++ b/ui/src/app/pipeline-details/components/sp-pipeline-details.directive.ts
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { Directive } from '@angular/core';
+import { UserPrivilege } from '../../_enums/user-privilege.enum';
+import { ActivatedRoute } from '@angular/router';
+import { AuthService } from '../../services/auth.service';
+import { Pipeline, PipelineService } from '@streampipes/platform-services';
+import { SpPipelineDetailsTabs } from '../pipeline-details-tabs';
+
+@Directive()
+export abstract class SpPipelineDetailsDirective {
+
+  tabs = [];
+  hasPipelineWritePrivileges = false;
+  hasPipelineDeletePrivileges = false;
+
+  currentPipeline: string;
+  pipeline: Pipeline;
+  pipelineAvailable = false;
+
+  constructor(protected activatedRoute: ActivatedRoute,
+              protected pipelineService: PipelineService,
+              protected authService: AuthService) {
+  }
+
+  onInit(): void {
+    this.authService.user$.subscribe(user => {
+      this.hasPipelineWritePrivileges = this.authService.hasRole(UserPrivilege.PRIVILEGE_WRITE_PIPELINE);
+      this.hasPipelineDeletePrivileges = this.authService.hasRole(UserPrivilege.PRIVILEGE_DELETE_PIPELINE);
+      const pipelineId = this.activatedRoute.snapshot.params.pipelineId;
+      if (pipelineId) {
+        this.tabs = new SpPipelineDetailsTabs().getTabs(pipelineId);
+        this.currentPipeline = pipelineId;
+        this.loadPipeline();
+      }
+    });
+  }
+
+  loadPipeline(): void {
+    this.pipelineService.getPipelineById(this.currentPipeline)
+      .subscribe(pipeline => {
+        this.pipeline = pipeline;
+        this.pipelineAvailable = true;
+        this.onPipelineAvailable();
+      });
+  }
+
+  abstract onPipelineAvailable(): void;
+
+}
diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.ts b/ui/src/app/pipeline-details/pipeline-details-tabs.ts
similarity index 56%
copy from ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.ts
copy to ui/src/app/pipeline-details/pipeline-details-tabs.ts
index bdace86dd..966c4a748 100644
--- a/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.ts
+++ b/ui/src/app/pipeline-details/pipeline-details-tabs.ts
@@ -16,28 +16,16 @@
  *
  */
 
-import { Component, Input } from '@angular/core';
-import { Router } from '@angular/router';
-import { SpNavigationItem } from '../../models/sp-navigation.model';
+import { SpNavigationItem } from '@streampipes/shared-ui';
 
-@Component({
-  selector: 'sp-basic-nav-tabs',
-  templateUrl: './basic-nav-tabs.component.html',
-  styleUrls: ['./basic-nav-tabs.component.scss']
-})
-export class SpBasicNavTabsComponent {
+export class SpPipelineDetailsTabs {
 
-  @Input()
-  spNavigationItems: SpNavigationItem[];
-
-  @Input()
-  activeLink: string;
-
-  constructor(private router: Router) {
-
-  }
-
-  navigateTo(spNavigationItem: SpNavigationItem) {
-    this.router.navigate(spNavigationItem.itemLink);
+  public getTabs(pipelineId: string): SpNavigationItem[] {
+    return [
+      {itemId: 'overview', itemTitle: 'Overview', itemLink: ['pipelines', 'details', pipelineId, 'overview']},
+      {itemId: 'monitoring', itemTitle: 'Monitoring', itemLink: ['pipelines', 'details', pipelineId, 'monitoring']},
+      // {itemId: 'errors', itemTitle: 'Errors', itemLink: ['pipelines', 'details', pipelineId, 'errors']},
+      {itemId: 'quick-edit', itemTitle: 'Quick Edit', itemLink: ['pipelines', 'details', pipelineId, 'quick-edit']}
+    ];
   }
 }
diff --git a/ui/src/app/pipeline-details/pipeline-details.component.html b/ui/src/app/pipeline-details/pipeline-details.component.html
deleted file mode 100644
index 2733ce184..000000000
--- a/ui/src/app/pipeline-details/pipeline-details.component.html
+++ /dev/null
@@ -1,65 +0,0 @@
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You under the Apache License, Version 2.0
-  ~ (the "License"); you may not use this file except in compliance with
-  ~ the License.  You may obtain a copy of the License at
-  ~
-  ~    http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  ~
-  -->
-
-<div fxLayout="column" class="page-container">
-    <div fxLayout="row" class="border sp-tab-bg" style="padding:0px">
-        <div fxFlex="100" class="page-container-nav">
-            <div fxFlex="100" fxLayout="row">
-                <mat-tab-group [selectedIndex]="selectedIndex" (selectedIndexChange)="setSelectedIndex($event)" color="accent">
-                    <mat-tab label="Overview"></mat-tab>
-                    <mat-tab label="Monitoring"></mat-tab>
-                    <mat-tab label="Errors"></mat-tab>
-                    <mat-tab label="Quick Edit" [disabled]="pipelineAvailable && pipeline.running" *ngIf="hasPipelineWritePrivileges"></mat-tab>
-                </mat-tab-group>
-            </div>
-        </div>
-    </div>
-    <div fxFlex="100" fxLayout="column">
-        <pipeline-preview [jspcanvas]="'assembly-preview'"
-                          [pipeline]="pipeline"
-                          (selectedElementEmitter)="selectElement($event)"
-                          style="margin-bottom:15px;"
-                          class="md-padding"
-                          *ngIf="pipelineAvailable"></pipeline-preview>
-    <div fxFlex fxLayout="row" fxLayoutAlign="start top" *ngIf="pipelineAvailable && selectedIndex == 0">
-        <div fxFlex="60" class="md-padding">
-            <div fxFlex fxLayout="column">
-                <pipeline-elements [pipeline]="pipeline"
-                                   [selectedElement]="selectedElement"></pipeline-elements>
-            </div>
-        </div>
-        <div fxFlex="40" class="md-padding">
-            <div fxFlex fxLayout="column">
-                <pipeline-actions (reloadPipelineEmitter)="loadPipeline()" [pipeline]="pipeline"
-                                  style="margin-bottom:15px;" *ngIf="hasPipelineWritePrivileges"></pipeline-actions>
-                <pipeline-status [pipeline]="pipeline"></pipeline-status>
-            </div>
-        </div>
-    </div>
-    <div fxFlex fxLayout="column" fxLayoutAlign="start top" *ngIf="pipelineAvailable && selectedIndex == 1">
-        <pipeline-monitoring [pipeline]="pipeline" (reloadPipelineEmitter)="loadPipeline()"></pipeline-monitoring>
-    </div>
-    <div fxFlex fxLayout="row" fxLayoutAlign="start top" *ngIf="pipelineAvailable && selectedIndex == 3">
-        <div fxFlex="100" class="md-padding">
-            <div fxFlex fxLayout="column">
-                <quick-edit [pipeline]="pipeline" [selectedElement]="selectedElement" (reloadPipelineEmitter)="loadPipeline()"></quick-edit>
-            </div>
-        </div>
-    </div>
-    </div>
-</div>
diff --git a/ui/src/app/pipeline-details/pipeline-details.component.ts b/ui/src/app/pipeline-details/pipeline-details.component.ts
deleted file mode 100644
index 93508741f..000000000
--- a/ui/src/app/pipeline-details/pipeline-details.component.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-import { Component, OnInit } from '@angular/core';
-import { Pipeline, PipelineService } from '@streampipes/platform-services';
-import { PipelineElementUnion } from '../editor/model/editor.model';
-import { ActivatedRoute } from '@angular/router';
-import { AuthService } from '../services/auth.service';
-import { UserPrivilege } from '../_enums/user-privilege.enum';
-
-@Component({
-    selector: 'pipeline-details',
-    templateUrl: './pipeline-details.component.html',
-    styleUrls: ['./pipeline-details.component.scss']
-})
-export class PipelineDetailsComponent implements OnInit {
-
-    currentPipeline: string;
-    pipeline: Pipeline;
-    pipelineAvailable = false;
-
-    selectedIndex = 0;
-    selectedElement: PipelineElementUnion;
-
-    hasPipelineWritePrivileges = false;
-    hasPipelineDeletePrivileges = false;
-
-    constructor(private activatedRoute: ActivatedRoute,
-                private pipelineService: PipelineService,
-                private authService: AuthService) {
-    }
-
-    ngOnInit() {
-        this.authService.user$.subscribe(user => {
-            this.hasPipelineWritePrivileges = this.authService.hasRole(UserPrivilege.PRIVILEGE_WRITE_PIPELINE);
-            this.hasPipelineDeletePrivileges = this.authService.hasRole(UserPrivilege.PRIVILEGE_DELETE_PIPELINE);
-        });
-        this.activatedRoute.queryParams.subscribe(params => {
-            if (params['pipeline']) {
-                this.currentPipeline = params['pipeline'];
-                this.loadPipeline();
-            }
-        });
-    }
-
-    setSelectedIndex(index: number) {
-        this.selectedIndex = index;
-    }
-
-    loadPipeline() {
-        this.pipelineService.getPipelineById(this.currentPipeline)
-            .subscribe(pipeline => {
-                this.pipeline = pipeline;
-                this.pipelineAvailable = true;
-            });
-    }
-
-    selectElement(element: PipelineElementUnion) {
-        this.selectedElement = element;
-    }
-
-}
diff --git a/ui/src/app/pipeline-details/pipeline-details.module.ts b/ui/src/app/pipeline-details/pipeline-details.module.ts
index 8978f7df8..7afc3a8cb 100644
--- a/ui/src/app/pipeline-details/pipeline-details.module.ts
+++ b/ui/src/app/pipeline-details/pipeline-details.module.ts
@@ -24,7 +24,6 @@ import { MatButtonModule } from '@angular/material/button';
 import { CustomMaterialModule } from '../CustomMaterial/custom-material.module';
 import { CommonModule } from '@angular/common';
 import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
-import { PipelineDetailsComponent } from './pipeline-details.component';
 import { PipelinePreviewComponent } from './components/preview/pipeline-preview.component';
 import { EditorModule } from '../editor/editor.module';
 import { PipelineActionsComponent } from './components/actions/pipeline-actions.component';
@@ -39,40 +38,42 @@ import { NgxChartsModule } from '@swimlane/ngx-charts';
 import { BarchartWidgetComponent } from './components/monitoring/widget/barchart/barchart-widget.component';
 import { StatusWidgetComponent } from './components/monitoring/widget/status/status-widget.component';
 import { PlatformServicesModule } from '@streampipes/platform-services';
+import { SpPipelineDetailsOverviewComponent } from './components/overview/pipeline-details-overview.component';
+import { SharedUiModule } from '@streampipes/shared-ui';
 
 @NgModule({
-    imports: [
-        CoreUiModule,
-        FlexLayoutModule,
-        FormsModule,
-        MatTabsModule,
-        MatButtonModule,
-        CustomMaterialModule,
-        CommonModule,
-        MatProgressSpinnerModule,
-        NgxChartsModule,
-        EditorModule,
-        FormsModule,
-        ReactiveFormsModule,
-        PlatformServicesModule
-    ],
-    declarations: [
-        PipelineActionsComponent,
-        PipelineElementsComponent,
-        PipelineElementsRowComponent,
-        PipelineElementStatisticsComponent,
-        PipelineDetailsComponent,
-        PipelineMonitoringComponent,
-        PipelineStatusComponent,
-        PipelinePreviewComponent,
-        QuickEditComponent,
-        StatusWidgetComponent,
-        BarchartWidgetComponent
-    ],
-    providers: [],
-    exports: [
-        PipelineDetailsComponent
-    ]
+  imports: [
+    CoreUiModule,
+    FlexLayoutModule,
+    FormsModule,
+    MatTabsModule,
+    MatButtonModule,
+    CustomMaterialModule,
+    CommonModule,
+    MatProgressSpinnerModule,
+    NgxChartsModule,
+    EditorModule,
+    FormsModule,
+    ReactiveFormsModule,
+    PlatformServicesModule,
+    SharedUiModule
+  ],
+  declarations: [
+    PipelineActionsComponent,
+    PipelineElementsComponent,
+    PipelineElementsRowComponent,
+    PipelineElementStatisticsComponent,
+    PipelineMonitoringComponent,
+    PipelineStatusComponent,
+    PipelinePreviewComponent,
+    QuickEditComponent,
+    StatusWidgetComponent,
+    BarchartWidgetComponent,
+    SpPipelineDetailsOverviewComponent,
+  ],
+  providers: [],
+  exports: [
+  ]
 })
 export class PipelineDetailsModule {
 
diff --git a/ui/src/app/pipelines/components/pipeline-overview/pipeline-overview.component.scss b/ui/src/app/pipelines/components/pipeline-overview/pipeline-overview.component.scss
index c2bef2352..7f1e8435c 100644
--- a/ui/src/app/pipelines/components/pipeline-overview/pipeline-overview.component.scss
+++ b/ui/src/app/pipelines/components/pipeline-overview/pipeline-overview.component.scss
@@ -25,10 +25,10 @@
 }
 
 .mat-row:nth-child(even) {
-  background-color: var(--color-bg-2);
+  background-color: var(--color-bg-1);
 }
 .mat-row:nth-child(odd) {
-  background-color: var(--color-bg-3);
+  background-color: var(--color-bg-2);
 }
 
 .mat-column-status {
diff --git a/ui/src/app/pipelines/pipelines.component.html b/ui/src/app/pipelines/pipelines.component.html
index cb8f87e08..71835b494 100644
--- a/ui/src/app/pipelines/pipelines.component.html
+++ b/ui/src/app/pipelines/pipelines.component.html
@@ -16,65 +16,69 @@
   ~
   -->
 
-<div fxLayout="column" class="page-container">
-    <div fxLayout="row" class="border sp-tab-bg" style="padding:0;">
-        <div fxFlex="100" class="page-container-nav">
-            <div fxFlex="100" fxLayout="row">
-                <button mat-button mat-flat-button color="accent" (click)="showPipelineCategoriesDialog()">Manage Categories</button>
-                <div fxFlex fxLayoutAlign="start center" [attr.id]="'peType'">
-                    <mat-tab-group [selectedIndex]="selectedCategoryIndex" (selectedIndexChange)="setSelectedTab($event)" color="accent">
-                        <mat-tab label="All pipelines"></mat-tab>
-                        <mat-tab *ngFor="let category of pipelineCategories" label="{{category.categoryName}}"></mat-tab>
-                    </mat-tab-group>
-                </div>
-            </div>
-        </div>
+<sp-basic-view [showBackLink]="false" [padding]="true">
+    <div nav fxFlex="100" fxLayoutAlign="start center" fxLayout="row" class="pl-10">
+        <button mat-button mat-raised-button color="accent" (click)="navigateToPipelineEditor()">
+            <i class="material-icons">add</i>&nbsp;New pipeline
+        </button>
+        <button class="mr-10" mat-button color="accent" (click)="startAllPipelines(true)"
+                [disabled]="checkCurrentSelectionStatus(false)" *ngIf="hasPipelineWritePrivileges">
+            <mat-icon>play_arrow</mat-icon><span>Start all pipelines</span>
+        </button>
+        <button mat-button color="accent" (click)="startAllPipelines(false)"
+                [disabled]="checkCurrentSelectionStatus(true)" *ngIf="hasPipelineWritePrivileges">
+            <mat-icon>stop</mat-icon>
+            <span>Stop all pipelines</span>
+        </button>
+        <span fxFlex></span>
+        <button mat-button mat-icon-button color="accent" matTooltip="Refresh pipelines" matTooltipPosition="above"
+                (click)="refreshPipelines()">
+            <i class="material-icons">
+                refresh
+            </i>
+        </button>
+        <button mat-button mat-icon-button color="accent" matTooltip="Export pipelines" matTooltipPosition="above"
+                (click)="exportPipelines()">
+            <i class="material-icons">
+                cloud_download
+            </i>
+        </button>
+        <button mat-button mat-icon-button color="accent" matTooltip="Import pipelines" matTooltipPosition="above"
+                (click)="openImportPipelinesDialog()">
+            <i class="material-icons">
+                cloud_upload
+            </i>
+        </button>
     </div>
-    <div class="fixed-height page-container-padding-inner" fxLayout="column" fxFlex="100">
-        <div fxLayout="row">
-            <button class="mr-10" mat-button mat-raised-button color="accent" (click)="startAllPipelines(true)"
-                       [disabled]="checkCurrentSelectionStatus(false)" *ngIf="hasPipelineWritePrivileges">Start all pipelines
-            </button>
-            <button mat-button mat-raised-button color="accent" (click)="startAllPipelines(false)"
-                       [disabled]="checkCurrentSelectionStatus(true)" *ngIf="hasPipelineWritePrivileges">Stop all pipelines
-            </button>
-        </div>
-        <div class="assemblyOptions sp-blue-bg mt-20" style="padding:5px;">
-            <div fxLayout="row" fxLayoutAlign="start center">
-                <h4>My pipelines</h4>
-                <span fxFlex></span>
-                <button mat-button mat-icon-button matTooltip="Refresh pipelines" matTooltipPosition="above"
-                        (click)="refreshPipelines()">
-                    <i class="material-icons">
-                        refresh
-                    </i>
-                </button>
-                <button mat-button mat-icon-button matTooltip="Export pipelines" matTooltipPosition="above"
-                        (click)="exportPipelines()">
-                    <i class="material-icons">
-                        cloud_download
-                    </i>
-                </button>
-                <button mat-button mat-icon-button matTooltip="Import pipelines" matTooltipPosition="above"
-                        (click)="openImportPipelinesDialog()">
-                    <i class="material-icons">
-                        cloud_upload
-                    </i>
-                </button>
+    <div fxFlex="100" fxLayout="column">
+        <div fxLayout="column">
+            <sp-basic-header-title-component title="My pipelines"></sp-basic-header-title-component>
+            <div fxFlex="100" fxLayout="row" fxLayoutAlign="center start">
+                <sp-basic-inner-panel [showTitle]="false" innerPadding="0" outerMargin="0" fxFlex="90"
+                                      [hideToolbar]="true">
+                    <div fxFlex="100">
+                        <pipeline-overview [activeCategoryId]="activeCategoryId" [pipelines]="pipelines"
+                                           [pipelineToStart]="pipelineToStart"
+                                           (refreshPipelinesEmitter)="refreshPipelines()"
+                                           *ngIf="pipelinesReady"></pipeline-overview>
+                    </div>
+                </sp-basic-inner-panel>
+            </div>
+            <div fxFlex="100" fxLayout="column" style="margin-top: 20px;">
+                <sp-basic-header-title-component title="System-generated pipelines"></sp-basic-header-title-component>
+                <div fxFlex="100" fxLayout="row" fxLayoutAlign="center start">
+                    <sp-basic-inner-panel [showTitle]="false" innerPadding="0" outerMargin="0" fxFlex="90"
+                                          [hideToolbar]="true">
+                        <div fxFlex="100">
+                            <pipeline-overview [activeCategoryId]="activeCategoryId" [pipelines]="systemPipelines"
+                                               [pipelineToStart]="systemPipelineToStart"
+                                               (refreshPipelinesEmitter)="refreshPipelines()"
+                                               *ngIf="pipelinesReady"></pipeline-overview>
+                        </div>
+                    </sp-basic-inner-panel>
+                </div>
             </div>
-        </div>
-        <div class="sp-blue-border">
-            <pipeline-overview [activeCategoryId]="activeCategoryId" [pipelines]="pipelines" [pipelineToStart]="pipelineToStart"
-                              (refreshPipelinesEmitter)="refreshPipelines()" *ngIf="pipelinesReady"></pipeline-overview>
-        </div>
-        <div style="margin-top:20px;margin-bottom:20px;"></div>
-        <div class="assemblyOptions sp-blue-bg">
-            <h4>System-generated pipelines</h4>
-        </div>
-        <div class="sp-blue-border">
-            <pipeline-overview [activeCategoryId]="activeCategoryId" [pipelines]="systemPipelines" [pipelineToStart]="systemPipelineToStart"
-                              (refreshPipelinesEmitter)="refreshPipelines()" *ngIf="pipelinesReady"></pipeline-overview>
         </div>
     </div>
-</div>
+</sp-basic-view>
 
diff --git a/ui/src/app/pipelines/pipelines.component.ts b/ui/src/app/pipelines/pipelines.component.ts
index 316810dbf..c1177fcc6 100644
--- a/ui/src/app/pipelines/pipelines.component.ts
+++ b/ui/src/app/pipelines/pipelines.component.ts
@@ -24,7 +24,7 @@ import { ImportPipelineDialogComponent } from './dialog/import-pipeline/import-p
 import { StartAllPipelinesDialogComponent } from './dialog/start-all-pipelines/start-all-pipelines-dialog.component';
 import { PipelineCategoriesDialogComponent } from './dialog/pipeline-categories/pipeline-categories-dialog.component';
 import { zip } from 'rxjs';
-import { ActivatedRoute } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
 import { AuthService } from '../services/auth.service';
 import { UserPrivilege } from '../_enums/user-privilege.enum';
 
@@ -56,7 +56,8 @@ export class PipelinesComponent implements OnInit {
   constructor(private pipelineService: PipelineService,
               private dialogService: DialogService,
               private activatedRoute: ActivatedRoute,
-              private authService: AuthService) {
+              private authService: AuthService,
+              private router: Router) {
     this.pipelineCategories = [];
     this.starting = false;
     this.stopping = false;
@@ -192,4 +193,8 @@ export class PipelinesComponent implements OnInit {
   showPipeline(pipeline) {
     pipeline.display = !pipeline.display;
   }
+
+  navigateToPipelineEditor() {
+    this.router.navigate(['pipelines', 'create']);
+  }
 }
diff --git a/ui/src/app/pipelines/pipelines.module.ts b/ui/src/app/pipelines/pipelines.module.ts
index 0049362d5..17ca3ecef 100644
--- a/ui/src/app/pipelines/pipelines.module.ts
+++ b/ui/src/app/pipelines/pipelines.module.ts
@@ -39,6 +39,14 @@ import { MatTableModule } from '@angular/material/table';
 import { PipelineNotificationsComponent } from './dialog/pipeline-notifications/pipeline-notifications.component';
 import { CoreUiModule } from '../core-ui/core-ui.module';
 import { PlatformServicesModule } from '@streampipes/platform-services';
+import { SharedUiModule } from '../../../projects/streampipes/shared-ui/src/lib/shared-ui.module';
+import { EditorModule } from '../editor/editor.module';
+import { PipelineDetailsModule } from '../pipeline-details/pipeline-details.module';
+import { RouterModule } from '@angular/router';
+import { EditorComponent } from '../editor/editor.component';
+import { SpPipelineDetailsOverviewComponent } from '../pipeline-details/components/overview/pipeline-details-overview.component';
+import { PipelineMonitoringComponent } from '../pipeline-details/components/monitoring/pipeline-monitoring.component';
+import { QuickEditComponent } from '../pipeline-details/components/edit/quickedit.component';
 
 @NgModule({
   imports: [
@@ -53,6 +61,50 @@ import { PlatformServicesModule } from '@streampipes/platform-services';
     MatTableModule,
     CoreUiModule,
     PlatformServicesModule,
+    EditorModule,
+    PipelineDetailsModule,
+    SharedUiModule,
+    RouterModule.forChild([
+      {
+        path: 'pipelines',
+        children: [
+          {
+            path: '',
+            component: PipelinesComponent
+          },
+          {
+            path: 'details/:pipelineId',
+            children: [
+              {
+                path: '',
+                redirectTo: 'overview',
+                pathMatch: 'full'
+              },
+              {
+                path: 'overview',
+                component: SpPipelineDetailsOverviewComponent,
+              },
+              {
+                path: 'monitoring',
+                component: PipelineMonitoringComponent,
+              },
+              {
+                path: 'quick-edit',
+                component: QuickEditComponent,
+              }
+            ]
+          },
+          {
+            path: 'create',
+            component: EditorComponent
+          },
+          {
+            path: 'modify/:pipelineId',
+            component: EditorComponent
+          }
+        ]
+      }
+    ]),
   ],
   declarations: [
     DeletePipelineDialogComponent,
diff --git a/ui/src/app/pipelines/services/pipeline-operations.service.ts b/ui/src/app/pipelines/services/pipeline-operations.service.ts
index 0135fcab8..997d0f1dd 100644
--- a/ui/src/app/pipelines/services/pipeline-operations.service.ts
+++ b/ui/src/app/pipelines/services/pipeline-operations.service.ts
@@ -141,12 +141,12 @@ export class PipelineOperationsService {
     });
   }
 
-  showPipelineInEditor(id) {
-    this.router.navigate(['editor'], { queryParams: { pipeline: id }});
+  showPipelineInEditor(id: string) {
+    this.router.navigate(['pipelines', 'modify', id]);
   }
 
-  showPipelineDetails(id) {
-    this.router.navigate(['pipeline-details'], { queryParams: { pipeline: id }});
+  showPipelineDetails(id: string) {
+    this.router.navigate(['pipelines', 'details', id]);
   }
 
   modifyPipeline(pipeline) {