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 2021/10/13 07:56:47 UTC

[incubator-streampipes] 02/04: [STREAMPIPES-444] Links can be placed on asset dashboards

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

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

commit dc2ede561d9ec74bfbbe23304654b0c58121022f
Author: Dominik Riemer <ri...@fzi.de>
AuthorDate: Wed Oct 13 09:38:50 2021 +0200

    [STREAMPIPES-444] Links can be placed on asset dashboards
---
 .../client/assetdashboard/CanvasAttributes.java    | 28 +++++++++++
 .../app-asset-monitoring.module.ts                 |  4 +-
 .../create-asset/create-asset.component.html       | 36 +++++++++-----
 .../create-asset/create-asset.component.ts         | 30 ++++++++++--
 .../components/view-asset/view-asset.component.ts  | 23 +++++++++
 .../dialog/add-link/add-link-dialog.component.html | 49 +++++++++++++++++++
 .../add-link/add-link-dialog.component.scss}       | 16 ++-----
 .../dialog/add-link/add-link-dialog.component.ts   | 56 ++++++++++++++++++++++
 .../model/selected-visualization-data.model.ts     |  8 +++-
 .../app-asset-monitoring/services/shape.service.ts | 39 +++++++++++++--
 10 files changed, 256 insertions(+), 33 deletions(-)

diff --git a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/assetdashboard/CanvasAttributes.java b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/assetdashboard/CanvasAttributes.java
index 596d5e2..62149be 100644
--- a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/assetdashboard/CanvasAttributes.java
+++ b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/assetdashboard/CanvasAttributes.java
@@ -38,6 +38,10 @@ public class CanvasAttributes {
   private String topic;
   private String name;
 
+  private String hyperlink;
+  private boolean newWindow;
+  private String fontStyle;
+
   public CanvasAttributes() {
   }
 
@@ -168,4 +172,28 @@ public class CanvasAttributes {
   public void setFontSize(String fontSize) {
     this.fontSize = fontSize;
   }
+
+  public String getHyperlink() {
+    return hyperlink;
+  }
+
+  public void setHyperlink(String hyperlink) {
+    this.hyperlink = hyperlink;
+  }
+
+  public boolean isNewWindow() {
+    return newWindow;
+  }
+
+  public void setNewWindow(boolean newWindow) {
+    this.newWindow = newWindow;
+  }
+
+  public String getFontStyle() {
+    return fontStyle;
+  }
+
+  public void setFontStyle(String fontStyle) {
+    this.fontStyle = fontStyle;
+  }
 }
diff --git a/ui/src/app/app-asset-monitoring/app-asset-monitoring.module.ts b/ui/src/app/app-asset-monitoring/app-asset-monitoring.module.ts
index a1260a8..8c4f020 100644
--- a/ui/src/app/app-asset-monitoring/app-asset-monitoring.module.ts
+++ b/ui/src/app/app-asset-monitoring/app-asset-monitoring.module.ts
@@ -40,6 +40,7 @@ import {SaveDashboardDialogComponent} from "./dialog/save-dashboard/save-dashboa
 import {AssetDashboardOverviewComponent} from "./components/dashboard-overview/dashboard-overview.component";
 import {InjectableRxStompConfig, RxStompService, rxStompServiceFactory} from "@stomp/ng2-stompjs";
 import {streamPipesStompConfig} from "../dashboard/services/websocket.config";
+import { AddLinkDialogComponent } from './dialog/add-link/add-link-dialog.component';
 
 @NgModule({
     imports: [
@@ -56,6 +57,7 @@ import {streamPipesStompConfig} from "../dashboard/services/websocket.config";
         AppAssetMonitoringComponent,
         CreateAssetComponent,
         ViewAssetComponent,
+        AddLinkDialogComponent,
         AddPipelineDialogComponent,
         SaveDashboardDialogComponent,
         AssetDashboardOverviewComponent
@@ -85,4 +87,4 @@ import {streamPipesStompConfig} from "../dashboard/services/websocket.config";
     ]
 })
 export class AppAssetMonitoringModule {
-}
\ No newline at end of file
+}
diff --git a/ui/src/app/app-asset-monitoring/components/create-asset/create-asset.component.html b/ui/src/app/app-asset-monitoring/components/create-asset/create-asset.component.html
index 8e81443..1e46811 100644
--- a/ui/src/app/app-asset-monitoring/components/create-asset/create-asset.component.html
+++ b/ui/src/app/app-asset-monitoring/components/create-asset/create-asset.component.html
@@ -16,19 +16,32 @@
   ~
   -->
 
-<div fxLayout="column" fxFlex="100" >
+<div fxLayout="column" fxFlex="100">
     <input type="file" (change)="handleFileInput($event)" accept="image/*" #file style="display:none;">
     <div class="sp-tab-bg sp-blue-bg page-container-nav">
         <div fxLayout="row" fxFlex="100" fxLayoutAlign="start center">
-            <button color="accent" mat-button mat-icon-button (click)="prepareDashboard()" type="submit" [disabled]="!backgroundImagePresent || !measurementPresent">
-                <i class="material-icons">save</i>
-            </button>
-            <button color="accent" mat-button mat-icon-button (click)="file.click()" type="submit" [disabled]="backgroundImagePresent">
-                <i class="material-icons">file_upload</i>
-            </button>
-            <button color="accent" mat-button mat-icon-button (click)="openAddPipelineDialog()" >
-                <i class="material-icons">add</i>
-            </button>
+            <div>
+                <button color="accent" class="mat-basic" mat-button (click)="prepareDashboard()" type="submit"
+                        [disabled]="!backgroundImagePresent || !measurementPresent">
+                    <mat-icon>save</mat-icon>&nbsp;Save
+                </button>
+            </div>
+            <div>
+                <button color="accent" mat-button (click)="file.click()" type="submit"
+                        [disabled]="backgroundImagePresent">
+                    <i class="material-icons">file_upload</i>&nbsp;Add image
+                </button>
+            </div>
+            <div>
+                <button color="accent" mat-button (click)="openAddPipelineDialog()">
+                    <i class="material-icons">add</i>&nbsp;Add value
+                </button>
+            </div>
+            <div>
+                <button color="accent" mat-button (click)="openAddLinkDialog()">
+                    <span fxLayoutAlign="start center"><i class="material-icons">link</i>&nbsp;Add link</span>
+                </button>
+            </div>
             <div fxFlex="100" fxLayout="row" fxLayoutAlign="end center">
                 <button color="accent" mat-button mat-icon-button (click)="clearCanvas()" matTooltip="Clear">
                     <i class="material-icons">
@@ -39,7 +52,8 @@
         </div>
     </div>
     <div id="outerAssemblyArea" class="asset-configuration-board-panel">
-        <div id="asset-configuration-board-canvas" class="asset-configuration-board-canvas" fxLayoutAlign="center center">
+        <div id="asset-configuration-board-canvas" class="asset-configuration-board-canvas"
+             fxLayoutAlign="center center">
 
         </div>
     </div>
diff --git a/ui/src/app/app-asset-monitoring/components/create-asset/create-asset.component.ts b/ui/src/app/app-asset-monitoring/components/create-asset/create-asset.component.ts
index b4dd597..a3e00f4 100644
--- a/ui/src/app/app-asset-monitoring/components/create-asset/create-asset.component.ts
+++ b/ui/src/app/app-asset-monitoring/components/create-asset/create-asset.component.ts
@@ -27,6 +27,7 @@ import { PanelType } from '../../../core-ui/dialog/base-dialog/base-dialog.model
 import { DialogService } from '../../../core-ui/dialog/base-dialog/base-dialog.service';
 import { DashboardConfiguration } from '../../model/dashboard-configuration.model';
 import { RestService } from '../../services/rest.service';
+import { AddLinkDialogComponent } from '../../dialog/add-link/add-link-dialog.component';
 
 interface Window {
     Image: any;
@@ -214,7 +215,8 @@ export class CreateAssetComponent implements AfterViewInit {
 
         dialogRef.afterClosed().subscribe(result => {
             if (result) {
-                this.addNewVisulizationItem(result);
+                const visGroup = this.shapeService.makeNewMeasurementShape(result);
+                this.addNewVisulizationItem(visGroup);
                 this.measurementPresent = true;
                 this.mainLayer.draw();
             }
@@ -222,6 +224,25 @@ export class CreateAssetComponent implements AfterViewInit {
         });
     }
 
+    openAddLinkDialog() {
+        this.keyboardListenerActive = false;
+        const dialogRef = this.dialogService.open(AddLinkDialogComponent, {
+            panelType: PanelType.SLIDE_IN_PANEL,
+            title: 'Add link',
+            width: '50vw',
+            data: {
+            }
+        });
+
+        dialogRef.afterClosed().subscribe(result => {
+            if (result) {
+                console.log(result);
+                this.addNewVisulizationItem(result);
+            }
+            this.keyboardListenerActive = true;
+        });
+    }
+
     @HostListener('document:keydown', ['$event'])
     handleKeyboardEvent(event: KeyboardEvent) {
         if (this.keyboardListenerActive) {
@@ -252,8 +273,7 @@ export class CreateAssetComponent implements AfterViewInit {
         }
     }
 
-    addNewVisulizationItem(visualizationConfig) {
-        const visGroup = this.shapeService.makeNewMeasurementShape(visualizationConfig);
+    addNewVisulizationItem(visGroup) {
         const id = this.makeId();
         visGroup.id(id);
         this.mainLayer.add(visGroup);
@@ -264,8 +284,8 @@ export class CreateAssetComponent implements AfterViewInit {
         const tr = this.getNewTransformer(id);
         this.mainLayer.add(tr);
         tr.attachTo(visGroup);
-        //this.mainLayer.draw();
-        //this.currentlySelectedShape = visGroup;
+        this.mainLayer.draw();
+        this.currentlySelectedShape = visGroup;
     }
 
     getNewTransformer(id: string): Konva.Transformer {
diff --git a/ui/src/app/app-asset-monitoring/components/view-asset/view-asset.component.ts b/ui/src/app/app-asset-monitoring/components/view-asset/view-asset.component.ts
index 674deff..d1c08e6 100644
--- a/ui/src/app/app-asset-monitoring/components/view-asset/view-asset.component.ts
+++ b/ui/src/app/app-asset-monitoring/components/view-asset/view-asset.component.ts
@@ -56,11 +56,34 @@ export class ViewAssetComponent {
         this.backgroundImageLayer = new Konva.Layer();
         this.showImage();
         this.mainCanvasStage.add(this.backgroundImageLayer);
+        const labels = this.mainCanvasStage.find('Label');
+        labels.each(label => {
+           label.on('mouseenter', () => this.onMouseEnter(label));
+            label.on('mouseleave', () => this.onMouseLeave(label));
+           label.on('click', () => this.onLinkClicked(label));
+        });
+
         this.backgroundImageLayer.moveToBottom();
         this.mainCanvasStage.draw();
         this.updateMeasurements();
     }
 
+    onMouseEnter(label) {
+        label.children[0].attrs.fontStyle = 'bold';
+        this.mainCanvasStage.draw();
+    }
+
+    onMouseLeave(label) {
+        label.children[0].attrs.fontStyle = 'normal';
+        this.mainCanvasStage.draw();
+    }
+
+    onLinkClicked(label) {
+        const href = label.children[0].attrs.hyperlink;
+        const newWindow = label.children[0].attrs.newWindow;
+        newWindow ? (window as any).open(href) : (window as any).location.href = href;
+    }
+
     updateMeasurements() {
         const dynamicShapes = this.mainCanvasStage.find('.dynamic-text');
         dynamicShapes.forEach(ds => {
diff --git a/ui/src/app/app-asset-monitoring/dialog/add-link/add-link-dialog.component.html b/ui/src/app/app-asset-monitoring/dialog/add-link/add-link-dialog.component.html
new file mode 100644
index 0000000..3a2b81f
--- /dev/null
+++ b/ui/src/app/app-asset-monitoring/dialog/add-link/add-link-dialog.component.html
@@ -0,0 +1,49 @@
+<!--
+  ~ 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 class="sp-dialog-container">
+    <div class="sp-dialog-content p-15">
+        <div fxFlex="100" fxLayout="column">
+            <h4>New link</h4>
+            <div class="dialog-margin-bottom" fxLayout="column">
+                <mat-form-field class="dialog-margin-bottom" color="accent">
+                    <mat-label>Label</mat-label>
+                    <input matInput placeholder="Label" [(ngModel)]="linkLabel">
+                    <mat-hint>The label that should be displayed as link</mat-hint>
+                </mat-form-field>
+                <mat-form-field color="accent">
+                    <mat-label>Target</mat-label>
+                    <input matInput placeholder="Label" [(ngModel)]="linkHref">
+                    <mat-hint>The target URL of the link</mat-hint>
+                </mat-form-field>
+                <mat-checkbox [(ngModel)]="newWindow">Open in new window</mat-checkbox>
+            </div>
+
+        </div>
+    </div>
+    <mat-divider></mat-divider>
+    <div class="sp-dialog-actions">
+        <button mat-button mat-raised-button class="mat-basic mr-10" (click)="cancel()">
+            Cancel
+        </button>
+        <button mat-button mat-raised-button color="accent" (click)="add()">
+            Add
+        </button>
+    </div>
+</div>
+
diff --git a/ui/src/app/app-asset-monitoring/model/selected-visualization-data.model.ts b/ui/src/app/app-asset-monitoring/dialog/add-link/add-link-dialog.component.scss
similarity index 73%
copy from ui/src/app/app-asset-monitoring/model/selected-visualization-data.model.ts
copy to ui/src/app/app-asset-monitoring/dialog/add-link/add-link-dialog.component.scss
index 2bcbe28..974d8b1 100644
--- a/ui/src/app/app-asset-monitoring/model/selected-visualization-data.model.ts
+++ b/ui/src/app/app-asset-monitoring/dialog/add-link/add-link-dialog.component.scss
@@ -16,14 +16,8 @@
  *
  */
 
-export interface SelectedVisualizationData {
-    labelBackgroundColor: string;
-    labelTextColor: string;
-    measurementBackgroundColor: string;
-    measurementTextColor: string;
-    visualizationId: string;
-    measurement: string;
-    label: string;
-    brokerUrl: string;
-    topic: string;
-}
\ No newline at end of file
+@import '../../../../scss/sp/sp-dialog.scss';
+
+.mr-10 {
+  margin-right: 10px;
+}
diff --git a/ui/src/app/app-asset-monitoring/dialog/add-link/add-link-dialog.component.ts b/ui/src/app/app-asset-monitoring/dialog/add-link/add-link-dialog.component.ts
new file mode 100644
index 0000000..3e45a79
--- /dev/null
+++ b/ui/src/app/app-asset-monitoring/dialog/add-link/add-link-dialog.component.ts
@@ -0,0 +1,56 @@
+/*
+ * 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 { DialogRef } from '../../../core-ui/dialog/base-dialog/dialog-ref';
+import { ShapeService } from '../../services/shape.service';
+import { HyperlinkConfig } from '../../model/selected-visualization-data.model';
+
+@Component({
+  selector: 'sp-add-link-dialog-component',
+  templateUrl: 'add-link-dialog.component.html',
+  styleUrls: ['./add-link-dialog.component.scss'],
+})
+export class AddLinkDialogComponent implements OnInit {
+
+  linkLabel: string;
+  linkHref: string;
+  newWindow = false;
+
+  constructor(private dialogRef: DialogRef<AddLinkDialogComponent>,
+              private shapeService: ShapeService) {}
+
+  ngOnInit(): void {
+  }
+
+  cancel() {
+    this.dialogRef.close();
+  }
+
+  add() {
+    const hyperlinkConfig: HyperlinkConfig = {
+      linkLabel: this.linkLabel,
+      linkHref: this.linkHref,
+      newWindow: this.newWindow
+    };
+
+    const group = this.shapeService.makeNewHyperlinkGroup(hyperlinkConfig);
+    this.dialogRef.close(group);
+  }
+
+}
diff --git a/ui/src/app/app-asset-monitoring/model/selected-visualization-data.model.ts b/ui/src/app/app-asset-monitoring/model/selected-visualization-data.model.ts
index 2bcbe28..027482c 100644
--- a/ui/src/app/app-asset-monitoring/model/selected-visualization-data.model.ts
+++ b/ui/src/app/app-asset-monitoring/model/selected-visualization-data.model.ts
@@ -26,4 +26,10 @@ export interface SelectedVisualizationData {
     label: string;
     brokerUrl: string;
     topic: string;
-}
\ No newline at end of file
+}
+
+export interface HyperlinkConfig {
+    linkLabel: string;
+    linkHref: string;
+    newWindow: boolean;
+}
diff --git a/ui/src/app/app-asset-monitoring/services/shape.service.ts b/ui/src/app/app-asset-monitoring/services/shape.service.ts
index 6f0c56d..6e4a698 100644
--- a/ui/src/app/app-asset-monitoring/services/shape.service.ts
+++ b/ui/src/app/app-asset-monitoring/services/shape.service.ts
@@ -20,7 +20,10 @@ import {Injectable} from "@angular/core";
 
 
 import Konva from "konva";
-import {SelectedVisualizationData} from "../model/selected-visualization-data.model";
+import {
+    HyperlinkConfig,
+    SelectedVisualizationData
+} from "../model/selected-visualization-data.model";
 
 @Injectable()
 export class ShapeService {
@@ -29,22 +32,50 @@ export class ShapeService {
 
     }
 
+    makeNewHyperlinkGroup(hyperlinkConfig: HyperlinkConfig) {
+        const group = this.makeGroup(true);
+        group.add(this.makeHyperlinkLabel(hyperlinkConfig));
+        return group;
+    }
+
+    makeHyperlinkLabel(hyperlinkConfig: HyperlinkConfig): Konva.Label {
+        const label = new Konva.Label({
+            x: 200,
+            y: 40,
+        });
+        label.add(this.makeHyperlinkText(hyperlinkConfig));
+        return label;
+    }
+
+    makeHyperlinkText(hyperlinkConfig: HyperlinkConfig): Konva.Text {
+        const settings: any = {
+            text: hyperlinkConfig.linkLabel,
+            width: 200,
+            height: 20,
+            hyperlink: hyperlinkConfig.linkHref,
+            newWindow: hyperlinkConfig.newWindow,
+            textDecoration: 'underline',
+            fill: '#1b1464',
+        };
+        return new Konva.Text(settings);
+    }
+
     makeNewMeasurementShape(visualizationConfig: SelectedVisualizationData): Konva.Group {
-        let visualizationGroup = this.makeGroup(true);
+        const visualizationGroup = this.makeGroup(true);
         visualizationGroup.add(this.makeLabelGroup(visualizationConfig));
         visualizationGroup.add(this.makeMeasurementGroup(visualizationConfig));
         return visualizationGroup;
     }
 
     makeLabelGroup(config: SelectedVisualizationData): Konva.Group {
-        let labelGroup = this.makeGroup(false);
+        const labelGroup = this.makeGroup(false);
         labelGroup.add(this.makeRect(config.labelBackgroundColor, 120, 40, 120, 20));
         labelGroup.add(this.makeText(config, config.label, config.labelTextColor, 120, 45, 120, 20, false))
         return labelGroup;
     }
 
     makeMeasurementGroup(config: SelectedVisualizationData): Konva.Group {
-        let measurementGroup = this.makeGroup(false);
+        const measurementGroup = this.makeGroup(false);
         measurementGroup.add(this.makeRect(config.measurementBackgroundColor, 120, 60, 120, 40));
         measurementGroup.add(this.makeText(config, config.measurement, config.measurementTextColor, 120, 65, 120, 40, true))
         return measurementGroup;