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/streampipes.git


The following commit(s) were added to refs/heads/dev by this push:
     new c16cf41d6f Add new traffic light widget to data explorer (#3223)
c16cf41d6f is described below

commit c16cf41d6f1fd1932ead00c70f6ca1453881fd36
Author: Marcelfrueh <[email protected]>
AuthorDate: Thu Sep 12 08:56:34 2024 +0200

    Add new traffic light widget to data explorer (#3223)
---
 .../data-explorer-data-view-toolbar.component.html |   4 +-
 .../traffic-light-widget-config.component.html     | 130 +++++++++++++++
 .../traffic-light-widget-config.component.scss     |  27 +++
 .../traffic-light-widget-config.component.ts       | 183 +++++++++++++++++++++
 .../model/traffic-light-widget.model.ts            |  37 +++++
 .../traffic-light-widget.component.html            |  79 +++++++++
 .../traffic-light-widget.component.scss            |  89 ++++++++++
 .../traffic-light-widget.component.ts              | 180 ++++++++++++++++++++
 ui/src/app/data-explorer/data-explorer.module.ts   |   4 +
 .../registry/data-explorer-widget-registry.ts      |   8 +
 10 files changed, 739 insertions(+), 2 deletions(-)

diff --git 
a/ui/src/app/data-explorer/components/data-view/data-view-toolbar/data-explorer-data-view-toolbar.component.html
 
b/ui/src/app/data-explorer/components/data-view/data-view-toolbar/data-explorer-data-view-toolbar.component.html
index 96aea25dff..de4394dc7e 100644
--- 
a/ui/src/app/data-explorer/components/data-view/data-view-toolbar/data-explorer-data-view-toolbar.component.html
+++ 
b/ui/src/app/data-explorer/components/data-view/data-view-toolbar/data-explorer-data-view-toolbar.component.html
@@ -24,10 +24,10 @@
         fxFlex="100"
     >
         <div
-            fxFlex="15"
+            fxFlex="25"
             fxLayout="row"
             fxLayoutAlign="start center"
-            class="widget-header-text mr-10"
+            class="widget-header-text mr-15"
         >
             <div
                 fxFlex="100"
diff --git 
a/ui/src/app/data-explorer/components/widgets/traffic-light/config/traffic-light-widget-config.component.html
 
b/ui/src/app/data-explorer/components/widgets/traffic-light/config/traffic-light-widget-config.component.html
new file mode 100644
index 0000000000..7c8d7c941c
--- /dev/null
+++ 
b/ui/src/app/data-explorer/components/widgets/traffic-light/config/traffic-light-widget-config.component.html
@@ -0,0 +1,130 @@
+<!--
+  ~ 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-visualization-config-outer
+    [configurationValid]="
+        currentlyConfiguredWidget.visualizationConfig.configurationValid
+    "
+>
+    <sp-configuration-box title="Field">
+        <sp-select-property
+            [availableProperties]="fieldProvider.numericFields"
+            [selectedProperty]="
+                currentlyConfiguredWidget.visualizationConfig
+                    .selectedFieldToObserve
+            "
+            (changeSelectedProperty)="selectFieldToObserve($event)"
+        >
+        </sp-select-property>
+    </sp-configuration-box>
+
+    <sp-configuration-box title="Settings">
+        <div fxLayout="column" fxLayoutGap="10px">
+            <div
+                fxLayout="row"
+                fxLayoutGap="10px"
+                fxLayoutAlign="start center"
+                fxFlex="100"
+            >
+                <small fxFlex="30">Threshold</small>
+                <mat-form-field color="accent" appearance="outline" fxFlex>
+                    <input
+                        type="text"
+                        [(ngModel)]="
+                            currentlyConfiguredWidget.visualizationConfig
+                                .selectedThreshold
+                        "
+                        matInput
+                        (input)="selectThreshold($event.target.value)"
+                        [pattern]="'[0-9.,]*'"
+                        (keydown)="restrictInput($event)"
+                    />
+                </mat-form-field>
+            </div>
+
+            <div
+                fxLayout="row"
+                fxLayoutGap="10px"
+                fxLayoutAlign="start center"
+                fxFlex="100"
+            >
+                <small fxFlex="30">With Threshold being</small>
+                <mat-radio-group
+                    [(ngModel)]="
+                        currentlyConfiguredWidget.visualizationConfig
+                            .selectedUpperLimit
+                    "
+                    (change)="selectUpperLimit($event.source.value)"
+                >
+                    <mat-radio-button [value]="true"
+                        >Upper Limit</mat-radio-button
+                    >
+                    <mat-radio-button [value]="false"
+                        >Lower Limit</mat-radio-button
+                    >
+                </mat-radio-group>
+            </div>
+
+            <div
+                fxLayout="row"
+                fxLayoutGap="10px"
+                fxLayoutAlign="start center"
+                fxFlex="100"
+            >
+                <small fxFlex="30">Warning Range (%)</small>
+                <mat-form-field color="accent" appearance="outline" fxFlex>
+                    <input
+                        [(ngModel)]="
+                            currentlyConfiguredWidget.visualizationConfig
+                                .selectedWarningRange
+                        "
+                        matInput
+                        type="text"
+                        (input)="selectWarningRange($event.target.value)"
+                        [pattern]="'[0-9.,]*'"
+                        (keydown)="restrictInput($event)"
+                    />
+                    <div
+                        *ngIf="warningRangeInterval"
+                        class="warning-range-info"
+                    >
+                        <small>{{ warningRangeInterval }}</small>
+                    </div>
+                </mat-form-field>
+            </div>
+            <div
+                fxLayout="row"
+                fxLayoutGap="10px"
+                fxLayoutAlign="start center"
+                fxFlex="100"
+                class="checkbox-container"
+            >
+                <small>Show Value in Traffic Light</small>
+                <mat-checkbox
+                    color="accent"
+                    [(ngModel)]="
+                        currentlyConfiguredWidget.visualizationConfig
+                            .selectedToShowValue
+                    "
+                    (ngModelChange)="selectToShowValue($event)"
+                >
+                </mat-checkbox>
+            </div>
+        </div>
+    </sp-configuration-box>
+</sp-visualization-config-outer>
diff --git 
a/ui/src/app/data-explorer/components/widgets/traffic-light/config/traffic-light-widget-config.component.scss
 
b/ui/src/app/data-explorer/components/widgets/traffic-light/config/traffic-light-widget-config.component.scss
new file mode 100644
index 0000000000..159b9f5901
--- /dev/null
+++ 
b/ui/src/app/data-explorer/components/widgets/traffic-light/config/traffic-light-widget-config.component.scss
@@ -0,0 +1,27 @@
+/*
+ *   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.
+ */
+
+.warning-range-info {
+    font-size: 0.9rem;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+}
+
+.checkbox-container {
+    margin-top: 20px;
+}
diff --git 
a/ui/src/app/data-explorer/components/widgets/traffic-light/config/traffic-light-widget-config.component.ts
 
b/ui/src/app/data-explorer/components/widgets/traffic-light/config/traffic-light-widget-config.component.ts
new file mode 100644
index 0000000000..7ca8a535c6
--- /dev/null
+++ 
b/ui/src/app/data-explorer/components/widgets/traffic-light/config/traffic-light-widget-config.component.ts
@@ -0,0 +1,183 @@
+/*
+ * 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 } from '@angular/core';
+import { BaseWidgetConfig } from '../../base/base-widget-config';
+import { WidgetConfigurationService } from 
'../../../../services/widget-configuration.service';
+import {
+    TrafficLightVisConfig,
+    TrafficLightWidgetModel,
+} from '../model/traffic-light-widget.model';
+import { DataExplorerFieldProviderService } from 
'../../../../services/data-explorer-field-provider-service';
+import { DataExplorerField } from '@streampipes/platform-services';
+
+@Component({
+    selector: 'sp-data-explorer-traffic-light-widget-config',
+    templateUrl: './traffic-light-widget-config.component.html',
+    styleUrls: ['./traffic-light-widget-config.component.scss'],
+})
+export class TrafficLightWidgetConfigComponent extends BaseWidgetConfig<
+    TrafficLightWidgetModel,
+    TrafficLightVisConfig
+> {
+    constructor(
+        widgetConfigurationService: WidgetConfigurationService,
+        fieldService: DataExplorerFieldProviderService,
+    ) {
+        super(widgetConfigurationService, fieldService);
+    }
+    warningRangeInterval: string;
+
+    selectWarningRange(selectedWarningRange: string): void {
+        const formattedValue = selectedWarningRange?.replace(',', '.');
+        const numericValue = parseFloat(formattedValue);
+        if (!isNaN(numericValue)) {
+            
this.currentlyConfiguredWidget.visualizationConfig.selectedWarningRange =
+                numericValue;
+        } else {
+            
this.currentlyConfiguredWidget.visualizationConfig.selectedWarningRange =
+                null;
+        }
+        this.updateWarningRangeInterval();
+        this.triggerViewRefresh();
+    }
+
+    selectFieldToObserve(selectedFieldToObserve: DataExplorerField): void {
+        
this.currentlyConfiguredWidget.visualizationConfig.selectedFieldToObserve =
+            selectedFieldToObserve;
+        this.triggerViewRefresh();
+    }
+
+    selectUpperLimit(selectedUpperLimit: boolean): void {
+        this.currentlyConfiguredWidget.visualizationConfig.selectedUpperLimit =
+            selectedUpperLimit;
+        this.triggerViewRefresh();
+    }
+
+    selectToShowValue(selectedToShowValue: boolean): void {
+        this.currentlyConfiguredWidget.visualizationConfig.selectedToShowValue 
=
+            selectedToShowValue;
+        this.triggerViewRefresh();
+    }
+
+    selectThreshold(selectedThreshold: string): void {
+        const formattedValue = selectedThreshold?.replace(',', '.');
+        const numericValue = parseFloat(formattedValue);
+        if (!isNaN(numericValue)) {
+            
this.currentlyConfiguredWidget.visualizationConfig.selectedThreshold =
+                numericValue;
+        } else {
+            
this.currentlyConfiguredWidget.visualizationConfig.selectedThreshold =
+                null;
+        }
+        this.updateWarningRangeInterval();
+        this.triggerViewRefresh();
+    }
+
+    protected applyWidgetConfig(config: TrafficLightVisConfig): void {
+        config.selectedFieldToObserve = this.fieldService.getSelectedField(
+            config.selectedFieldToObserve,
+            this.fieldProvider.allFields,
+            () => this.fieldProvider.allFields[0],
+        );
+        this.currentlyConfiguredWidget.visualizationConfig.selectedUpperLimit 
??=
+            true;
+        this.updateWarningRangeInterval();
+    }
+    protected requiredFieldsForChartPresent(): boolean {
+        return true;
+    }
+
+    restrictInput(event: KeyboardEvent): void {
+        const inputValue = (event.target as HTMLInputElement).value;
+        const allowedKeys = [
+            '0',
+            '1',
+            '2',
+            '3',
+            '4',
+            '5',
+            '6',
+            '7',
+            '8',
+            '9',
+            'Backspace',
+            'ArrowLeft',
+            'ArrowRight',
+            'Delete',
+        ];
+
+        const pointOrComma = ['.', ','];
+        if (pointOrComma.includes(event.key)) {
+            if (inputValue.includes('.') || inputValue.includes(',')) {
+                event.preventDefault();
+                return;
+            }
+        }
+
+        if (
+            !allowedKeys.includes(event.key) &&
+            !pointOrComma.includes(event.key) &&
+            !(event.ctrlKey || event.metaKey)
+        ) {
+            event.preventDefault();
+        }
+    }
+
+    updateWarningRangeInterval(): void {
+        if (
+            this.currentlyConfiguredWidget.visualizationConfig
+                .selectedThreshold != null &&
+            this.currentlyConfiguredWidget.visualizationConfig
+                .selectedWarningRange != null
+        ) {
+            const rangeValue =
+                this.currentlyConfiguredWidget.visualizationConfig
+                    .selectedThreshold *
+                (this.currentlyConfiguredWidget.visualizationConfig
+                    .selectedWarningRange /
+                    100);
+
+            if (
+                this.currentlyConfiguredWidget.visualizationConfig
+                    .selectedWarningRange === 0
+            ) {
+                this.warningRangeInterval = 'No Warning Range defined';
+            } else if (
+                this.currentlyConfiguredWidget.visualizationConfig
+                    .selectedUpperLimit
+            ) {
+                const lowerBound =
+                    this.currentlyConfiguredWidget.visualizationConfig
+                        .selectedThreshold - rangeValue;
+                this.warningRangeInterval =
+                    'Current Warning Range: ' +
+                    `${lowerBound} to 
${this.currentlyConfiguredWidget.visualizationConfig.selectedThreshold}`;
+            } else {
+                const upperBound =
+                    this.currentlyConfiguredWidget.visualizationConfig
+                        .selectedThreshold + rangeValue;
+                this.warningRangeInterval =
+                    'Current Warning Range: ' +
+                    
`${this.currentlyConfiguredWidget.visualizationConfig.selectedThreshold} to 
${upperBound}`;
+            }
+        } else {
+            this.warningRangeInterval = '';
+        }
+    }
+}
diff --git 
a/ui/src/app/data-explorer/components/widgets/traffic-light/model/traffic-light-widget.model.ts
 
b/ui/src/app/data-explorer/components/widgets/traffic-light/model/traffic-light-widget.model.ts
new file mode 100644
index 0000000000..43fe5d6beb
--- /dev/null
+++ 
b/ui/src/app/data-explorer/components/widgets/traffic-light/model/traffic-light-widget.model.ts
@@ -0,0 +1,37 @@
+/*
+ * 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 {
+    DataExplorerDataConfig,
+    DataExplorerWidgetModel,
+    DataExplorerField,
+} from '@streampipes/platform-services';
+import { DataExplorerVisConfig } from 
'../../../../models/dataview-dashboard.model';
+
+export interface TrafficLightVisConfig extends DataExplorerVisConfig {
+    selectedWarningRange: number;
+    selectedFieldToObserve: DataExplorerField;
+    selectedUpperLimit: boolean;
+    selectedThreshold: number;
+    selectedToShowValue: boolean;
+}
+
+export interface TrafficLightWidgetModel extends DataExplorerWidgetModel {
+    dataConfig: DataExplorerDataConfig;
+    visualizationConfig: TrafficLightVisConfig;
+}
diff --git 
a/ui/src/app/data-explorer/components/widgets/traffic-light/traffic-light-widget.component.html
 
b/ui/src/app/data-explorer/components/widgets/traffic-light/traffic-light-widget.component.html
new file mode 100644
index 0000000000..72b5a90aa2
--- /dev/null
+++ 
b/ui/src/app/data-explorer/components/widgets/traffic-light/traffic-light-widget.component.html
@@ -0,0 +1,79 @@
+<!--
+  ~   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
+    fxFlex="100"
+    fxLayoutAlign="center center"
+    fxLayout="column"
+    class="main-panel"
+    [ngStyle]="{
+        background: dataExplorerWidget.baseAppearanceConfig.backgroundColor,
+        color: dataExplorerWidget.baseAppearanceConfig.textColor,
+        overflowX: 'auto'
+    }"
+>
+    <sp-no-data-in-date-range
+        *ngIf="showNoDataInDateRange"
+        [viewDateRange]="timeSettings"
+        class="h-100"
+    >
+    </sp-no-data-in-date-range>
+
+    <div fxLayoutAlign="center center">
+        <div
+            class="tl-container"
+            [ngClass]="selectedToShowValue ? 'no-border-container' : ''"
+            [ngStyle]="{
+                width: containerWidth + 'px',
+                height: containerHeight + 'px'
+            }"
+        >
+            <div
+                class="light"
+                [ngClass]="{ 'light-red': activeClass === 'red' }"
+                [ngStyle]="{
+                    width: lightWidth + 'px',
+                    height: lightHeight + 'px'
+                }"
+            ></div>
+            <div
+                class="light"
+                [ngClass]="{ 'light-yellow': activeClass === 'yellow' }"
+                [ngStyle]="{
+                    width: lightWidth + 'px',
+                    height: lightHeight + 'px'
+                }"
+            ></div>
+            <div
+                class="light"
+                [ngClass]="{ 'light-green': activeClass === 'green' }"
+                [ngStyle]="{
+                    width: lightWidth + 'px',
+                    height: lightHeight + 'px'
+                }"
+            ></div>
+        </div>
+    </div>
+    <div class="light-value-container">
+        <div
+            *ngIf="selectedToShowValue"
+            class="light-value"
+            [ngStyle]="{ width: containerWidth + 'px' }"
+        >
+            {{ displayed_value }}
+        </div>
+    </div>
+</div>
diff --git 
a/ui/src/app/data-explorer/components/widgets/traffic-light/traffic-light-widget.component.scss
 
b/ui/src/app/data-explorer/components/widgets/traffic-light/traffic-light-widget.component.scss
new file mode 100644
index 0000000000..cc13ba4625
--- /dev/null
+++ 
b/ui/src/app/data-explorer/components/widgets/traffic-light/traffic-light-widget.component.scss
@@ -0,0 +1,89 @@
+/*
+ *   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.
+ */
+
+.h-100 {
+    height: 100%;
+}
+
+.tl-container {
+    background-color: #222;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    flex-direction: column;
+    padding: 10px;
+    border-radius: 10px;
+}
+
+.no-border-container {
+    border-bottom-left-radius: 0;
+    border-bottom-right-radius: 0;
+    border-top-left-radius: 10px;
+    border-top-right-radius: 10px;
+}
+
+.light {
+    border-radius: 50%;
+    background-color: #3d3535;
+    background: repeating-linear-gradient(#333, #443 5px);
+}
+
+.light-red,
+.light-yellow,
+.light-green {
+    box-shadow: 0 0 40px;
+    z-index: 1;
+}
+
+.light-red {
+    background: repeating-linear-gradient(#f00, #e00 5px);
+    box-shadow: 0 0 40px #f00;
+}
+
+.light-yellow {
+    background: repeating-linear-gradient(#fd0, #ec0 5px);
+    box-shadow: 0 0 40px #fd0;
+}
+
+.light-green {
+    background: repeating-linear-gradient(#0d0, #0c0 5px);
+    box-shadow: 0 0 40px #0d0;
+}
+
+.title-panel {
+    font-size: 20px;
+    height: 30px;
+    margin: 10px 0;
+}
+
+.light-value-container {
+    background-color: #222;
+    border-radius: 10px;
+}
+
+.light-value {
+    color: #fff;
+    font-weight: bold;
+    font-size: 16px;
+    text-align: center;
+    background-color: #222;
+    padding: 10px;
+    border-bottom-left-radius: 10px;
+    border-bottom-right-radius: 10px;
+    border-top-left-radius: 0;
+    border-top-right-radius: 0;
+}
diff --git 
a/ui/src/app/data-explorer/components/widgets/traffic-light/traffic-light-widget.component.ts
 
b/ui/src/app/data-explorer/components/widgets/traffic-light/traffic-light-widget.component.ts
new file mode 100644
index 0000000000..80e3490ddc
--- /dev/null
+++ 
b/ui/src/app/data-explorer/components/widgets/traffic-light/traffic-light-widget.component.ts
@@ -0,0 +1,180 @@
+/*
+ * 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 { BaseDataExplorerWidgetDirective } from 
'../base/base-data-explorer-widget.directive';
+import { TrafficLightWidgetModel } from './model/traffic-light-widget.model';
+import {
+    DataExplorerField,
+    SpQueryResult,
+} from '@streampipes/platform-services';
+
+@Component({
+    selector: 'sp-data-explorer-traffic-light-widget',
+    templateUrl: './traffic-light-widget.component.html',
+    styleUrls: ['./traffic-light-widget.component.scss'],
+})
+export class TrafficLightWidgetComponent
+    extends BaseDataExplorerWidgetDirective<TrafficLightWidgetModel>
+    implements OnInit
+{
+    row: any[][];
+    header: string[];
+    fieldIndex: number;
+
+    width: number;
+    height: number;
+
+    containerWidth: number;
+    containerHeight: number;
+
+    lightWidth: number;
+    lightHeight: number;
+
+    selectedWarningRange: number;
+    selectedFieldToObserve: DataExplorerField;
+    selectedUpperLimit: boolean;
+    selectedThreshold: number;
+    selectedToShowValue: boolean;
+
+    activeClass = 'red';
+    displayed_value: string;
+
+    ngOnInit(): void {
+        super.ngOnInit();
+        this.onResize(
+            this.gridsterItemComponent.width - this.widthOffset,
+            this.gridsterItemComponent.height - this.heightOffset,
+        );
+        this.updateSettings();
+    }
+
+    updateSettings(): void {
+        this.selectedFieldToObserve =
+            this.dataExplorerWidget.visualizationConfig.selectedFieldToObserve;
+        this.selectedWarningRange =
+            this.dataExplorerWidget.visualizationConfig.selectedWarningRange;
+        this.selectedUpperLimit =
+            this.dataExplorerWidget.visualizationConfig.selectedUpperLimit;
+        this.selectedThreshold =
+            this.dataExplorerWidget.visualizationConfig.selectedThreshold;
+        this.selectedToShowValue =
+            this.dataExplorerWidget.visualizationConfig.selectedToShowValue;
+    }
+
+    getTrafficLightColor(): void {
+        const value = this.row[0][this.fieldIndex];
+        this.displayed_value = value.toFixed(2);
+
+        if (this.isInOkRange(value)) {
+            this.activeClass = 'green';
+        } else if (this.isInWarningRange(value)) {
+            this.activeClass = 'yellow';
+        } else {
+            this.activeClass = 'red';
+        }
+    }
+
+    exceedsThreshold(value) {
+        if (this.selectedUpperLimit) {
+            return value >= this.selectedThreshold;
+        } else {
+            return value <= this.selectedThreshold;
+        }
+    }
+
+    isInWarningRange(value) {
+        if (this.exceedsThreshold(value)) {
+            return false;
+        } else {
+            if (this.selectedUpperLimit) {
+                return (
+                    value >=
+                    this.selectedThreshold -
+                        this.selectedThreshold *
+                            (this.selectedWarningRange / 100)
+                );
+            } else {
+                console.log(value);
+
+                return (
+                    value <=
+                    this.selectedThreshold +
+                        this.selectedThreshold *
+                            (this.selectedWarningRange / 100)
+                );
+            }
+        }
+    }
+    isInOkRange(value) {
+        return !this.exceedsThreshold(value) && !this.isInWarningRange(value);
+    }
+
+    public refreshView(): void {
+        this.updateSettings();
+        this.fieldToObserve();
+        this.getTrafficLightColor();
+    }
+
+    public beforeDataFetched(): void {
+        this.setShownComponents(false, false, true, false);
+    }
+
+    fieldToObserve(): void {
+        this.fieldIndex = this.header.indexOf(
+            this.selectedFieldToObserve.runtimeName,
+        );
+    }
+
+    public onDataReceived(spQueryResult: SpQueryResult[]): void {
+        this.header = spQueryResult[0].allDataSeries[0].headers;
+        this.row = spQueryResult[0].allDataSeries[0].rows;
+        this.fieldToObserve();
+        this.getTrafficLightColor();
+        this.setShownComponents(false, true, false, false);
+    }
+
+    onResize(width: number, heigth: number) {
+        this.containerHeight = heigth * 0.8;
+        this.containerWidth = this.containerHeight / 3;
+        this.lightWidth = (this.containerHeight * 0.9) / 3;
+        this.lightHeight = this.lightWidth;
+    }
+
+    handleUpdatedFields(
+        addedFields: DataExplorerField[],
+        removedFields: DataExplorerField[],
+    ) {
+        const updatedFields = this.fieldUpdateService.updateFieldSelection(
+            [
+                this.dataExplorerWidget.visualizationConfig
+                    .selectedFieldToObserve,
+            ],
+            {
+                addedFields,
+                removedFields,
+                fieldProvider: this.fieldProvider,
+            },
+            () => true,
+        );
+
+        this.selectedFieldToObserve = updatedFields[0];
+        this.fieldToObserve();
+        this.refreshView();
+    }
+}
diff --git a/ui/src/app/data-explorer/data-explorer.module.ts 
b/ui/src/app/data-explorer/data-explorer.module.ts
index 81be2dc5fb..09b8f33e75 100644
--- a/ui/src/app/data-explorer/data-explorer.module.ts
+++ b/ui/src/app/data-explorer/data-explorer.module.ts
@@ -46,6 +46,8 @@ import { DataExplorerDashboardPanelComponent } from 
'./components/dashboard/data
 import { TimeRangeSelectorComponent } from 
'./components/time-selector/time-range-selector.component';
 import { DataExplorerDashboardWidgetComponent } from 
'./components/widget/data-explorer-dashboard-widget.component';
 import { ImageWidgetComponent } from 
'./components/widgets/image/image-widget.component';
+import { TrafficLightWidgetComponent } from 
'./components/widgets/traffic-light/traffic-light-widget.component';
+import { TrafficLightWidgetConfigComponent } from 
'./components/widgets/traffic-light/config/traffic-light-widget-config.component';
 import { TableWidgetComponent } from 
'./components/widgets/table/table-widget.component';
 import { AggregateConfigurationComponent } from 
'./components/widgets/utils/aggregate-configuration/aggregate-configuration.component';
 import { LoadDataSpinnerComponent } from 
'./components/widgets/utils/load-data-spinner/load-data-spinner.component';
@@ -247,6 +249,8 @@ import { GaugeWidgetConfigComponent } from 
'./components/widgets/gauge/config/ga
         SelectPropertyComponent,
         TableWidgetComponent,
         TableWidgetConfigComponent,
+        TrafficLightWidgetComponent,
+        TrafficLightWidgetConfigComponent,
         MapWidgetConfigComponent,
         MapWidgetComponent,
         HeatmapWidgetConfigComponent,
diff --git a/ui/src/app/data-explorer/registry/data-explorer-widget-registry.ts 
b/ui/src/app/data-explorer/registry/data-explorer-widget-registry.ts
index 4ef84d7dca..c2d486bfd6 100644
--- a/ui/src/app/data-explorer/registry/data-explorer-widget-registry.ts
+++ b/ui/src/app/data-explorer/registry/data-explorer-widget-registry.ts
@@ -52,6 +52,8 @@ import { SpTimeSeriesAppearanceConfigComponent } from 
'../components/widgets/tim
 import { SpGaugeRendererService } from 
'../components/widgets/gauge/gauge-renderer.service';
 import { GaugeWidgetConfigComponent } from 
'../components/widgets/gauge/config/gauge-widget-config.component';
 import { GaugeWidgetModel } from 
'../components/widgets/gauge/model/gauge-widget.model';
+import { TrafficLightWidgetConfigComponent } from 
'../components/widgets/traffic-light/config/traffic-light-widget-config.component';
+import { TrafficLightWidgetComponent } from 
'../components/widgets/traffic-light/traffic-light-widget.component';
 
 @Injectable({ providedIn: 'root' })
 export class DataExplorerWidgetRegistry {
@@ -84,6 +86,12 @@ export class DataExplorerWidgetRegistry {
                 widgetConfigurationComponent: TableWidgetConfigComponent,
                 widgetComponent: TableWidgetComponent,
             },
+            {
+                id: 'traffic-Light',
+                label: 'Traffic Light',
+                widgetConfigurationComponent: 
TrafficLightWidgetConfigComponent,
+                widgetComponent: TrafficLightWidgetComponent,
+            },
             {
                 id: 'map',
                 label: 'Map',

Reply via email to