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 291dc235dc feat(#3725): Add dashboard kiosk mode (#3729)
291dc235dc is described below
commit 291dc235dc946da1b5c48f1fe6cb7debdcb3e716
Author: Dominik Riemer <[email protected]>
AuthorDate: Mon Aug 18 20:39:32 2025 +0200
feat(#3725): Add dashboard kiosk mode (#3729)
* feat(#3725): Add dashboard kiosk mode to UI
* Add auto-refresh for kiosk mode
* Fix dashboard widget deletion behaviour
* Remove loading bar
* Add missing license header
---
.../model/dashboard/CompositeDashboardModel.java | 10 ++
.../impl/dashboard/DataLakeDashboardResource.java | 24 ++-
ui/deployment/app-routing.module.mst | 1 +
.../src/lib/apis/dashboard.service.ts | 14 +-
.../src/lib/model/dashboard/dashboard.model.ts | 1 +
.../kiosk/dashboard-kiosk.component.html | 73 ++++++++
.../kiosk/dashboard-kiosk.component.scss | 33 +++-
.../components/kiosk/dashboard-kiosk.component.ts | 133 ++++++++++++++
.../app/dashboard-kiosk/dashboard-kiosk.module.ts | 63 +++++++
.../chart-view/abstract-chart-view.directive.ts | 2 +-
.../grid-view/dashboard-grid-view.component.html | 61 +++++++
.../grid-view/dashboard-grid-view.component.scss | 11 +-
.../grid-view/dashboard-grid-view.component.ts | 16 +-
.../slide-view/dashboard-slide-view.component.html | 4 +-
.../slide-view/dashboard-slide-view.component.scss | 0
.../slide-view/dashboard-slide-view.component.ts | 0
.../dashboard-shared.module.ts} | 48 +----
.../services/dashboard.service.ts | 6 +-
.../grid-view/dashboard-grid-view.component.html | 49 ------
.../dashboard-overview-table.component.html | 20 ++-
.../dashboard-overview-table.component.ts | 36 ++--
.../overview/dashboard-overview.component.ts | 2 +-
.../components/panel/dashboard-panel.component.ts | 53 +++---
ui/src/app/dashboard/dashboard.module.ts | 6 +-
.../data-explorer-chart-container.component.html | 194 +++++++++++----------
.../data-explorer-chart-container.component.ts | 6 +-
.../base/base-data-explorer-widget.directive.ts | 1 +
.../charts/base/echarts-widget.component.ts | 18 ++
.../models/dataview-dashboard.model.ts | 1 +
.../data-explorer-chart-view.component.html | 4 +-
.../data-explorer-overview-table.component.ts | 21 +--
.../overview/data-explorer-overview.component.ts | 12 +-
.../overview/data-explorer-overview.directive.ts | 12 +-
33 files changed, 633 insertions(+), 302 deletions(-)
diff --git
a/streampipes-model/src/main/java/org/apache/streampipes/model/dashboard/CompositeDashboardModel.java
b/streampipes-model/src/main/java/org/apache/streampipes/model/dashboard/CompositeDashboardModel.java
index cb06527e42..cd14784aaa 100644
---
a/streampipes-model/src/main/java/org/apache/streampipes/model/dashboard/CompositeDashboardModel.java
+++
b/streampipes-model/src/main/java/org/apache/streampipes/model/dashboard/CompositeDashboardModel.java
@@ -22,8 +22,18 @@ import
org.apache.streampipes.model.datalake.DataExplorerWidgetModel;
import org.apache.streampipes.model.datalake.DataLakeMeasure;
import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
public record CompositeDashboardModel(DashboardModel dashboard,
List<DataExplorerWidgetModel> widgets,
List<DataLakeMeasure> dataLakeMeasures) {
+
+ public String getRevisionHash() {
+
+ return Stream.concat(
+ Stream.of(dashboard.getRev()),
+ widgets.stream().map(DashboardEntity::getRev)
+ ).collect(Collectors.joining("|"));
+ }
}
diff --git
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/dashboard/DataLakeDashboardResource.java
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/dashboard/DataLakeDashboardResource.java
index f97b7083a1..0fcb6e2162 100644
---
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/dashboard/DataLakeDashboardResource.java
+++
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/dashboard/DataLakeDashboardResource.java
@@ -20,11 +20,12 @@ package org.apache.streampipes.rest.impl.dashboard;
import org.apache.streampipes.model.client.user.DefaultPrivilege;
-import org.apache.streampipes.model.dashboard.CompositeDashboardModel;
import org.apache.streampipes.model.dashboard.DashboardModel;
import org.apache.streampipes.resource.management.DataExplorerResourceManager;
import
org.apache.streampipes.rest.core.base.impl.AbstractAuthGuardedRestResource;
+import org.springframework.http.CacheControl;
+import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PostFilter;
@@ -35,10 +36,12 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
+import java.util.Objects;
@RestController
@RequestMapping("/api/v3/datalake/dashboard")
@@ -59,8 +62,23 @@ public class DataLakeDashboardResource extends
AbstractAuthGuardedRestResource {
@GetMapping(path = "/{dashboardId}/composite", produces =
MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("this.hasReadAuthority() and hasPermission(#dashboardId,
'READ')")
- public CompositeDashboardModel
getCompositeDashboardModel(@PathVariable("dashboardId") String dashboardId) {
- return getResourceManager().getCompositeDashboard(dashboardId);
+ public ResponseEntity<?>
getCompositeDashboardModel(@PathVariable("dashboardId") String dashboardId,
+ @RequestHeader(value =
"If-None-Match", required = false) String ifNoneMatch) {
+ var dashboard = getResourceManager().getCompositeDashboard(dashboardId);
+ var currentEtag = "\"" + dashboard.getRevisionHash() + "\"";
+ if (Objects.nonNull(ifNoneMatch)) {
+ if (currentEtag.equals(ifNoneMatch)) {
+ return ResponseEntity
+ .status(HttpStatus.NOT_MODIFIED)
+ .eTag(currentEtag)
+ .build();
+ }
+ }
+ return ResponseEntity
+ .ok()
+ .eTag(currentEtag)
+ .cacheControl(CacheControl.noCache())
+ .body(dashboard);
}
@PutMapping(path = "/{dashboardId}", produces =
MediaType.APPLICATION_JSON_VALUE)
diff --git a/ui/deployment/app-routing.module.mst
b/ui/deployment/app-routing.module.mst
index e0e69d4639..e3e44722fa 100644
--- a/ui/deployment/app-routing.module.mst
+++ b/ui/deployment/app-routing.module.mst
@@ -50,6 +50,7 @@ const routes: Routes = [
{ path: 'apidocs', component: ApidocsComponent, canActivate:
[ConfiguredCanActivateGuard]},
{ path: 'login', component: LoginComponent, canActivate:
[ConfiguredCanActivateGuard],
data: {animation: 'LoginPage'}},
+ { path: 'dashboard-kiosk', loadChildren: () =>
import('./dashboard-kiosk/dashboard-kiosk.module').then(m =>
m.DashboardKioskModule), canActivate: [ConfiguredCanActivateGuard]},
{ path: 'register', component: RegisterComponent, canActivate:
[RegistrationAllowedCanActivateGuard] },
{ path: 'activate-account', component: ActivateAccountComponent,
canActivate: [RegistrationAllowedCanActivateGuard] },
{ path: 'restore-password', component: RestorePasswordComponent,
canActivate: [RestorePasswordAllowedCanActivateGuard] },
diff --git
a/ui/projects/streampipes/platform-services/src/lib/apis/dashboard.service.ts
b/ui/projects/streampipes/platform-services/src/lib/apis/dashboard.service.ts
index 4a40b03218..cd0618bd8b 100644
---
a/ui/projects/streampipes/platform-services/src/lib/apis/dashboard.service.ts
+++
b/ui/projects/streampipes/platform-services/src/lib/apis/dashboard.service.ts
@@ -16,7 +16,7 @@
*
*/
-import { HttpClient } from '@angular/common/http';
+import { HttpClient, HttpContext, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { SharedDatalakeRestService } from './shared-dashboard.service';
@@ -24,6 +24,7 @@ import {
CompositeDashboard,
Dashboard,
} from '../model/dashboard/dashboard.model';
+import { NGX_LOADING_BAR_IGNORED } from '@ngx-loading-bar/http-client';
@Injectable({
providedIn: 'root',
@@ -42,9 +43,18 @@ export class DashboardService {
return this.http.get<Dashboard>(`${this.dashboardUrl}/${dashboardId}`);
}
- getCompositeDashboard(dashboardId: string): Observable<CompositeDashboard>
{
+ getCompositeDashboard(
+ dashboardId: string,
+ eTag = undefined,
+ ): Observable<HttpResponse<any>> {
+ const headers = eTag ? { 'If-None-Match': eTag } : {};
return this.http.get<CompositeDashboard>(
`${this.dashboardUrl}/${dashboardId}/composite`,
+ {
+ headers,
+ observe: 'response',
+ context: new HttpContext().set(NGX_LOADING_BAR_IGNORED, true),
+ },
);
}
diff --git
a/ui/projects/streampipes/platform-services/src/lib/model/dashboard/dashboard.model.ts
b/ui/projects/streampipes/platform-services/src/lib/model/dashboard/dashboard.model.ts
index 927d1a1946..79bd32d7b6 100644
---
a/ui/projects/streampipes/platform-services/src/lib/model/dashboard/dashboard.model.ts
+++
b/ui/projects/streampipes/platform-services/src/lib/model/dashboard/dashboard.model.ts
@@ -59,4 +59,5 @@ export interface CompositeDashboard {
dashboard: Dashboard;
dataLakeMeasures: DataLakeMeasure[];
widgets: DataExplorerWidgetModel[];
+ revisionHash: string;
}
diff --git
a/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.html
b/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.html
new file mode 100644
index 0000000000..c08275e9c7
--- /dev/null
+++ b/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.html
@@ -0,0 +1,73 @@
+<!--
+ ~ 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"
+ style="height: 100vh"
+ class="light-mode w-100 standalone-outer"
+>
+ <mat-toolbar class="standalone-toolbar">
+ <div class="md-toolbar-tools sp-toolbar">
+ <div fxFlex="100" fxLayout fxLayoutAlign="start center">
+ <div
+ class="md-toolbar-tools"
+ style="height: 60px; max-height: 60px"
+ fxLayout="row"
+ fxLayoutAlign="start center"
+ >
+ <div
+ style="
+ padding: 5px;
+ border-radius: 0px;
+ margin-right: 15px;
+ position: relative;
+ left: 20px;
+ "
+ >
+ <img
+ alt="icon"
+ src="../../../../assets/img/sp/logo.png"
+ style="max-height: 45px; max-width: 250px"
+ />
+ </div>
+ <span class="dashboard-title">{{ dashboard?.name }}</span>
+ @if (dashboard?.description) {
+ <span class="dashboard-description"
+ > | {{ dashboard?.description }}</span
+ >
+ }
+ </div>
+ </div>
+ </div>
+ </mat-toolbar>
+ <div fxLayout="column" style="height: calc(100vh - 40px)">
+ @if (dashboard) {
+ <sp-dashboard-grid-view
+ #dashboardGrid
+ *ngIf="dashboard?.widgets.length > 0"
+ [editMode]="false"
+ [kioskMode]="true"
+ [dashboard]="dashboard"
+ [widgets]="widgets"
+ [timeSettings]="dashboard.dashboardTimeSettings"
+ class="dashboard-grid"
+ >
+ </sp-dashboard-grid-view>
+ }
+ </div>
+</div>
diff --git
a/streampipes-model/src/main/java/org/apache/streampipes/model/dashboard/CompositeDashboardModel.java
b/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.scss
similarity index 65%
copy from
streampipes-model/src/main/java/org/apache/streampipes/model/dashboard/CompositeDashboardModel.java
copy to
ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.scss
index cb06527e42..b8d6d190bd 100644
---
a/streampipes-model/src/main/java/org/apache/streampipes/model/dashboard/CompositeDashboardModel.java
+++ b/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.scss
@@ -16,14 +16,33 @@
*
*/
-package org.apache.streampipes.model.dashboard;
+@use '../../../../scss/_variables';
-import org.apache.streampipes.model.datalake.DataExplorerWidgetModel;
-import org.apache.streampipes.model.datalake.DataLakeMeasure;
+.h-100 {
+ height: 100%;
+}
+
+.standalone-outer {
+ background: var(--color-bg-1);
+}
+
+.dashboard-grid {
+ display: flex;
+ flex-direction: column;
+ flex: 1 1 100%;
+}
+
+.standalone-toolbar {
+ height: 60px;
+ background: var(--color-bg-1);
+}
-import java.util.List;
+.dashboard-title {
+ margin-left: 2rem;
+ font-size: 1.25rem;
+}
-public record CompositeDashboardModel(DashboardModel dashboard,
- List<DataExplorerWidgetModel> widgets,
- List<DataLakeMeasure> dataLakeMeasures) {
+.dashboard-description {
+ font-size: 1.25rem;
+ font-weight: normal;
}
diff --git
a/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.ts
b/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.ts
new file mode 100644
index 0000000000..871967aeb2
--- /dev/null
+++ b/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.ts
@@ -0,0 +1,133 @@
+/*
+ * 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, inject, OnDestroy, OnInit } from '@angular/core';
+import {
+ CompositeDashboard,
+ Dashboard,
+ DashboardService,
+ DataExplorerWidgetModel,
+ TimeSettings,
+} from '@streampipes/platform-services';
+import { ActivatedRoute } from '@angular/router';
+import { of, Subscription, timer } from 'rxjs';
+import { switchMap } from 'rxjs/operators';
+import { TimeSelectionService } from '@streampipes/shared-ui';
+import { DataExplorerDashboardService } from
'../../../dashboard-shared/services/dashboard.service';
+
+@Component({
+ selector: 'sp-dashboard-kiosk',
+ standalone: false,
+ templateUrl: './dashboard-kiosk.component.html',
+ styleUrl: './dashboard-kiosk.component.scss',
+})
+export class DashboardKioskComponent implements OnInit, OnDestroy {
+ private route = inject(ActivatedRoute);
+ private dashboardService = inject(DashboardService);
+ private timeSelectionService = inject(TimeSelectionService);
+ private dataExplorerDashboardService =
inject(DataExplorerDashboardService);
+
+ dashboard: Dashboard;
+ widgets: DataExplorerWidgetModel[] = [];
+ refresh$: Subscription;
+ eTag: string;
+
+ ngOnInit() {
+ const dashboardId = this.route.snapshot.params.dashboardId;
+ this.dashboardService
+ .getCompositeDashboard(dashboardId)
+ .subscribe(res => {
+ if (res.ok) {
+ const cd = res.body;
+ cd.dashboard.widgets.forEach(w => {
+ w.widgetId ??=
+
this.dataExplorerDashboardService.makeUniqueWidgetId();
+ });
+ const eTag = res.headers.get('ETag');
+ this.initDashboard(cd, eTag);
+ }
+ });
+ }
+
+ initDashboard(cd: CompositeDashboard, eTag: string): void {
+ this.dashboard = cd.dashboard;
+ this.widgets = cd.widgets;
+ this.eTag = eTag;
+ if (this.dashboard.dashboardLiveSettings.refreshModeActive) {
+ this.createQuerySubscription();
+ this.createRefreshListener();
+ }
+ }
+
+ createQuerySubscription() {
+ this.refresh$ = timer(
+ 0,
+ this.dashboard.dashboardLiveSettings.refreshIntervalInSeconds *
+ 1000,
+ )
+ .pipe(
+ switchMap(() => {
+ this.timeSelectionService.updateTimeSettings(
+ this.timeSelectionService.defaultQuickTimeSelections,
+ this.dashboard.dashboardTimeSettings,
+ new Date(),
+ );
+ this.updateDateRange(this.dashboard.dashboardTimeSettings);
+ return of(null);
+ }),
+ )
+ .subscribe();
+ }
+
+ createRefreshListener(): void {
+ this.dashboardService
+ .getCompositeDashboard(this.dashboard.elementId, this.eTag) //
this should send If-None-Match
+ .subscribe({
+ next: res => {
+ if (res.status === 200) {
+ const newEtag = res.headers.get('ETag');
+ if (newEtag) {
+ this.eTag = newEtag;
+ }
+ this.dashboard = undefined;
+ this.refresh$?.unsubscribe();
+ setTimeout(() => {
+ this.initDashboard(res.body, newEtag);
+ });
+ }
+ setTimeout(() => this.createRefreshListener(), 5000);
+ },
+ error: err => {
+ setTimeout(() => this.createRefreshListener(), 5000);
+ },
+ });
+ }
+
+ updateDateRange(timeSettings: TimeSettings) {
+ let ts = undefined;
+ if (this.dashboard.dashboardGeneralSettings.globalTimeEnabled) {
+ this.dashboard.dashboardTimeSettings = timeSettings;
+ ts = timeSettings;
+ }
+ this.timeSelectionService.notify(ts);
+ }
+
+ ngOnDestroy() {
+ this.refresh$?.unsubscribe();
+ }
+}
diff --git a/ui/src/app/dashboard-kiosk/dashboard-kiosk.module.ts
b/ui/src/app/dashboard-kiosk/dashboard-kiosk.module.ts
new file mode 100644
index 0000000000..d9b1ade07e
--- /dev/null
+++ b/ui/src/app/dashboard-kiosk/dashboard-kiosk.module.ts
@@ -0,0 +1,63 @@
+/*
+ * 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 { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { RouterModule } from '@angular/router';
+import { SharedUiModule } from '@streampipes/shared-ui';
+import { DataExplorerSharedModule } from
'../data-explorer-shared/data-explorer-shared.module';
+import { TranslateModule } from '@ngx-translate/core';
+import { DashboardSharedModule } from
'../dashboard-shared/dashboard-shared.module';
+import { MatToolbarModule } from '@angular/material/toolbar';
+import {
+ DefaultFlexDirective,
+ DefaultLayoutAlignDirective,
+ DefaultLayoutDirective,
+} from '@ngbracket/ngx-layout';
+import { DashboardKioskComponent } from
'./components/kiosk/dashboard-kiosk.component';
+
+@NgModule({
+ imports: [
+ CommonModule,
+ MatToolbarModule,
+ SharedUiModule,
+ DataExplorerSharedModule,
+ DashboardSharedModule,
+ TranslateModule.forChild(),
+ RouterModule.forChild([
+ {
+ path: '',
+ children: [
+ {
+ path: ':dashboardId',
+ component: DashboardKioskComponent,
+ },
+ ],
+ },
+ ]),
+ DefaultFlexDirective,
+ DefaultLayoutDirective,
+ DefaultLayoutAlignDirective,
+ ],
+ declarations: [DashboardKioskComponent],
+ providers: [],
+ exports: [],
+})
+export class DashboardKioskModule {
+ constructor() {}
+}
diff --git
a/ui/src/app/dashboard/components/chart-view/abstract-chart-view.directive.ts
b/ui/src/app/dashboard-shared/components/chart-view/abstract-chart-view.directive.ts
similarity index 99%
rename from
ui/src/app/dashboard/components/chart-view/abstract-chart-view.directive.ts
rename to
ui/src/app/dashboard-shared/components/chart-view/abstract-chart-view.directive.ts
index bf3adc6ad4..8a22e809f6 100644
---
a/ui/src/app/dashboard/components/chart-view/abstract-chart-view.directive.ts
+++
b/ui/src/app/dashboard-shared/components/chart-view/abstract-chart-view.directive.ts
@@ -85,7 +85,7 @@ export abstract class AbstractChartViewDirective {
loadWidgetConfig(widgetId: string, setCurrentlyConfigured?: boolean) {
if (!this.isGridView()) {
- this.widgetsVisible = false;
+ this.widgetsAvailable = false;
}
this.dataViewDataExplorerService
.getChart(widgetId)
diff --git
a/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.html
b/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.html
new file mode 100644
index 0000000000..43f50a8dda
--- /dev/null
+++
b/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.html
@@ -0,0 +1,61 @@
+<!--
+ ~ 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.
+ ~
+ -->
+
+@if (dashboard.displayHeader) {
+ <div class="text-center">
+ <h2>{{ dashboard.name }}</h2>
+ <h3>{{ dashboard.description }}</h3>
+ </div>
+}
+<gridster
+ [options]="options"
+ [ngClass]="editMode ? 'edit' : ''"
+ class="custom-gridster-style"
+>
+ @for (item of dashboard.widgets; let i = $index; track item.widgetId) {
+ <ng-container>
+ <gridster-item
+ [item]="item"
+ #gridsterItemComponent
+ class="widget-outer"
+ >
+ @if (widgetsAvailable && configuredWidgets.has(item.id)) {
+ <sp-data-explorer-chart-container
+ [ngStyle]="{
+ height: gridsterItemComponent.height - 13 + 'px'
+ }"
+ [timeSettings]="timeSettings"
+ [globalTimeEnabled]="
+
dashboard.dashboardGeneralSettings.globalTimeEnabled
+ "
+ (deleteCallback)="propagateItemRemoval($event)"
+ (startEditModeEmitter)="startEditMode($event)"
+ [dashboardItem]="item"
+ [configuredWidget]="configuredWidgets.get(item.id)"
+ [dataLakeMeasure]="dataLakeMeasures.get(item.id)"
+ [editMode]="editMode"
+ [kioskMode]="kioskMode"
+ [gridMode]="true"
+ [widgetIndex]="i"
+ [gridsterItemComponent]="gridsterItemComponent"
+ ></sp-data-explorer-chart-container>
+ }
+ </gridster-item>
+ </ng-container>
+ }
+</gridster>
diff --git
a/ui/src/app/dashboard/components/chart-view/grid-view/dashboard-grid-view.component.scss
b/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.scss
similarity index 86%
rename from
ui/src/app/dashboard/components/chart-view/grid-view/dashboard-grid-view.component.scss
rename to
ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.scss
index cc372aac45..4c2294b9ad 100644
---
a/ui/src/app/dashboard/components/chart-view/grid-view/dashboard-grid-view.component.scss
+++
b/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.scss
@@ -17,7 +17,7 @@
*/
gridster.custom-gridster-style ::ng-deep {
- background: var(--color-bg-0);
+ background: var(--color-bg-1);
}
gridster.custom-gridster-style.edit ::ng-deep {
@@ -40,8 +40,9 @@ gridster.scrollVertical ::ng-deep {
border-right: 1px solid var(--color-bg-1);
}
-.shadow {
- box-shadow:
- rgba(50, 50, 93, 0.25) 0px 2px 5px -1px,
- rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;
+.widget-outer {
+ //box-shadow:
+ // rgba(50, 50, 93, 0.25) 0px 2px 5px -1px,
+ // rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;
+ border: 1px solid var(--color-bg-2);
}
diff --git
a/ui/src/app/dashboard/components/chart-view/grid-view/dashboard-grid-view.component.ts
b/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.ts
similarity index 91%
rename from
ui/src/app/dashboard/components/chart-view/grid-view/dashboard-grid-view.component.ts
rename to
ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.ts
index 481454a769..d11558fe8e 100644
---
a/ui/src/app/dashboard/components/chart-view/grid-view/dashboard-grid-view.component.ts
+++
b/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.ts
@@ -18,13 +18,18 @@
import {
Component,
+ Input,
OnChanges,
OnInit,
QueryList,
SimpleChanges,
ViewChildren,
} from '@angular/core';
-import { GridsterItemComponent, GridType } from 'angular-gridster2';
+import {
+ DisplayGrid,
+ GridsterItemComponent,
+ GridType,
+} from 'angular-gridster2';
import { GridsterInfo } from
'../../../../data-explorer-shared/models/gridster-info.model';
import { IDataViewDashboardConfig } from
'../../../../data-explorer-shared/models/dataview-dashboard.model';
import { AbstractChartViewDirective } from '../abstract-chart-view.directive';
@@ -39,6 +44,9 @@ export class DashboardGridViewComponent
extends AbstractChartViewDirective
implements OnInit, OnChanges
{
+ @Input()
+ kioskMode = false;
+
options: IDataViewDashboardConfig;
loaded = false;
@@ -56,8 +64,10 @@ export class DashboardGridViewComponent
minRows: 4,
fixedRowHeight: 100,
fixedColWidth: 100,
- margin: 5,
- displayGrid: this.editMode ? 'always' : 'none',
+ margin: 3,
+ displayGrid: this.editMode
+ ? DisplayGrid.OnDragAndResize
+ : DisplayGrid.None,
resizable: { enabled: this.editMode },
itemResizeCallback: (item, itemComponent) => {
this.resizeService.notify({
diff --git
a/ui/src/app/dashboard/components/chart-view/slide-view/dashboard-slide-view.component.html
b/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.html
similarity index 96%
rename from
ui/src/app/dashboard/components/chart-view/slide-view/dashboard-slide-view.component.html
rename to
ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.html
index 4c73a94118..1640260e3a 100644
---
a/ui/src/app/dashboard/components/chart-view/slide-view/dashboard-slide-view.component.html
+++
b/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.html
@@ -48,7 +48,7 @@
</div>
<div fxFlex="100">
<div class="h-100 w-100 mw-100" id="slideViewOuter" fxFlex="100">
- <sp-data-explorer-dashboard-widget
+ <sp-data-explorer-chart-container
[ngStyle]="{
height: gridsterItemComponent.height - 15 + 'px'
}"
@@ -71,7 +71,7 @@
currentWidget &&
widgetsVisible
"
- ></sp-data-explorer-dashboard-widget>
+ ></sp-data-explorer-chart-container>
</div>
</div>
</div>
diff --git
a/ui/src/app/dashboard/components/chart-view/slide-view/dashboard-slide-view.component.scss
b/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.scss
similarity index 100%
rename from
ui/src/app/dashboard/components/chart-view/slide-view/dashboard-slide-view.component.scss
rename to
ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.scss
diff --git
a/ui/src/app/dashboard/components/chart-view/slide-view/dashboard-slide-view.component.ts
b/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.ts
similarity index 100%
rename from
ui/src/app/dashboard/components/chart-view/slide-view/dashboard-slide-view.component.ts
rename to
ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.ts
diff --git a/ui/src/app/dashboard/dashboard.module.ts
b/ui/src/app/dashboard-shared/dashboard-shared.module.ts
similarity index 67%
copy from ui/src/app/dashboard/dashboard.module.ts
copy to ui/src/app/dashboard-shared/dashboard-shared.module.ts
index adc7211f38..0a4e008572 100644
--- a/ui/src/app/dashboard/dashboard.module.ts
+++ b/ui/src/app/dashboard-shared/dashboard-shared.module.ts
@@ -24,13 +24,11 @@ import { MatTabsModule } from '@angular/material/tabs';
import { FormsModule } from '@angular/forms';
import { ColorPickerComponent } from 'ngx-color-picker';
import { MatGridListModule } from '@angular/material/grid-list';
-import { DashboardOverviewComponent } from
'./components/overview/dashboard-overview.component';
import { CdkTableModule } from '@angular/cdk/table';
import { LeafletModule } from '@bluehalo/ngx-leaflet';
import { CoreUiModule } from '../core-ui/core-ui.module';
import { PlatformServicesModule } from '@streampipes/platform-services';
import { ServicesModule } from '../services/services.module';
-import { RouterModule } from '@angular/router';
import { SharedUiModule } from '@streampipes/shared-ui';
import { MatDividerModule } from '@angular/material/divider';
import { MatFormFieldModule } from '@angular/material/form-field';
@@ -59,16 +57,8 @@ import { MatButtonToggleModule } from
'@angular/material/button-toggle';
import { MatChipsModule } from '@angular/material/chips';
import { MatSliderModule } from '@angular/material/slider';
import { DataExplorerSharedModule } from
'../data-explorer-shared/data-explorer-shared.module';
-import { DashboardPanelComponent } from
'./components/panel/dashboard-panel.component';
-import { DataExplorerPanelCanDeactivateGuard } from
'../data-explorer-shared/services/data-explorer-panel.can-deactivate.guard';
import { DashboardGridViewComponent } from
'./components/chart-view/grid-view/dashboard-grid-view.component';
import { DashboardSlideViewComponent } from
'./components/chart-view/slide-view/dashboard-slide-view.component';
-import { DashboardToolbarComponent } from
'./components/panel/dashboard-toolbar/dashboard-toolbar.component';
-import { ChartSelectionPanelComponent } from
'./components/panel/chart-selection-panel/chart-selection-panel.component';
-import { ChartPreviewComponent } from
'./components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component';
-import { ChartSelectionComponent } from
'./components/panel/chart-selection-panel/chart-selection/chart-selection.component';
-import { EditDashboardDialogComponent } from
'./dialogs/edit-dashboard/edit-dashboard-dialog.component';
-import { DashboardOverviewTableComponent } from
'./components/overview/dashboard-overview-table/dashboard-overview-table.component';
import { TranslateModule } from '@ngx-translate/core';
@NgModule({
@@ -114,43 +104,11 @@ import { TranslateModule } from '@ngx-translate/core';
SharedUiModule,
DataExplorerSharedModule,
TranslateModule.forChild(),
- RouterModule.forChild([
- {
- path: '',
- children: [
- {
- path: '',
- component: DashboardOverviewComponent,
- },
- {
- path: ':id',
- component: DashboardPanelComponent,
- canDeactivate: [DataExplorerPanelCanDeactivateGuard],
- },
- {
- path: ':id/:startTime/:endTime',
- component: DashboardPanelComponent,
- canDeactivate: [DataExplorerPanelCanDeactivateGuard],
- },
- ],
- },
- ]),
- ],
- declarations: [
- DashboardOverviewComponent,
- DashboardGridViewComponent,
- DashboardPanelComponent,
- DashboardSlideViewComponent,
- DashboardToolbarComponent,
- ChartSelectionPanelComponent,
- ChartPreviewComponent,
- ChartSelectionComponent,
- EditDashboardDialogComponent,
- DashboardOverviewTableComponent,
],
+ declarations: [DashboardGridViewComponent, DashboardSlideViewComponent],
providers: [],
- exports: [],
+ exports: [DashboardGridViewComponent, DashboardSlideViewComponent],
})
-export class DashboardModule {
+export class DashboardSharedModule {
constructor() {}
}
diff --git a/ui/src/app/dashboard/services/dashboard.service.ts
b/ui/src/app/dashboard-shared/services/dashboard.service.ts
similarity index 89%
rename from ui/src/app/dashboard/services/dashboard.service.ts
rename to ui/src/app/dashboard-shared/services/dashboard.service.ts
index e2f5b569fe..efa9ba8ab1 100644
--- a/ui/src/app/dashboard/services/dashboard.service.ts
+++ b/ui/src/app/dashboard-shared/services/dashboard.service.ts
@@ -17,7 +17,7 @@
*/
import { Dashboard } from '@streampipes/platform-services';
-import { EditDashboardDialogComponent } from
'../dialogs/edit-dashboard/edit-dashboard-dialog.component';
+import { EditDashboardDialogComponent } from
'../../dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component';
import { DialogService, PanelType } from '@streampipes/shared-ui';
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
@@ -42,4 +42,8 @@ export class DataExplorerDashboardService {
},
});
}
+
+ makeUniqueWidgetId(): string {
+ return Math.random().toString(36).slice(2, 12);
+ }
}
diff --git
a/ui/src/app/dashboard/components/chart-view/grid-view/dashboard-grid-view.component.html
b/ui/src/app/dashboard/components/chart-view/grid-view/dashboard-grid-view.component.html
deleted file mode 100644
index 46abe817da..0000000000
---
a/ui/src/app/dashboard/components/chart-view/grid-view/dashboard-grid-view.component.html
+++ /dev/null
@@ -1,49 +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 *ngIf="dashboard.displayHeader" class="text-center">
- <h2>{{ dashboard.name }}</h2>
- <h3>{{ dashboard.description }}</h3>
-</div>
-<gridster
- [options]="options"
- [ngClass]="editMode ? 'edit' : ''"
- class="custom-gridster-style"
->
- <ng-container *ngFor="let item of dashboard.widgets; let i = index">
- <gridster-item [item]="item" #gridsterItemComponent class="shadow">
- <sp-data-explorer-dashboard-widget
- [ngStyle]="{ height: gridsterItemComponent.height - 13 + 'px'
}"
- [timeSettings]="timeSettings"
- [globalTimeEnabled]="
- dashboard.dashboardGeneralSettings.globalTimeEnabled
- "
- (deleteCallback)="propagateItemRemoval($event)"
- (startEditModeEmitter)="startEditMode($event)"
- [dashboardItem]="item"
- [configuredWidget]="configuredWidgets.get(item.id)"
- [dataLakeMeasure]="dataLakeMeasures.get(item.id)"
- [editMode]="editMode"
- [gridMode]="true"
- [widgetIndex]="i"
- [gridsterItemComponent]="gridsterItemComponent"
- *ngIf="widgetsAvailable && configuredWidgets.has(item.id)"
- ></sp-data-explorer-dashboard-widget>
- </gridster-item>
- </ng-container>
-</gridster>
diff --git
a/ui/src/app/dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component.html
b/ui/src/app/dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component.html
index ff0e0525df..112ba222b1 100644
---
a/ui/src/app/dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component.html
+++
b/ui/src/app/dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component.html
@@ -28,7 +28,7 @@
>
<ng-container matColumnDef="name">
<th
- fxFlex="60"
+ fxFlex="40"
fxLayoutAlign="start center"
mat-header-cell
*matHeaderCellDef
@@ -36,7 +36,7 @@
{{ 'Dashboards' | translate }}
</th>
<td
- fxFlex="60"
+ fxFlex="40"
fxLayoutAlign="center start"
mat-cell
data-cy="dashboard-table-overview"
@@ -54,7 +54,6 @@
<ng-container matColumnDef="lastModified">
<th
- fxFlex="60"
fxLayoutAlign="start center"
mat-header-cell
*matHeaderCellDef
@@ -62,7 +61,6 @@
{{ 'Last modified' | translate }}
</th>
<td
- fxFlex="60"
fxLayoutAlign="center start"
mat-cell
*matCellDef="let element"
@@ -81,7 +79,6 @@
<ng-container matColumnDef="createdAt">
<th
- fxFlex="60"
fxLayoutAlign="start center"
mat-header-cell
*matHeaderCellDef
@@ -89,7 +86,6 @@
{{ 'Created' | translate }}
</th>
<td
- fxFlex="60"
fxLayoutAlign="center start"
mat-cell
*matCellDef="let element"
@@ -104,13 +100,13 @@
<ng-container matColumnDef="actions">
<th
- fxFlex="40"
+ fxFlex
fxLayoutAlign="center center"
mat-header-cell
*matHeaderCellDef
></th>
<td
- fxFlex="40"
+ fxFlex
fxLayoutAlign="start center"
mat-cell
*matCellDef="let element"
@@ -134,6 +130,14 @@
>
<i class="material-icons">edit</i>
</button>
+ <button
+ mat-icon-button
+ color="accent"
+ [matTooltip]="'Kiosk mode' | translate"
+ (click)="openDashboardInKioskMode(element)"
+ >
+ <i class="material-icons">open_in_new</i>
+ </button>
<button
mat-icon-button
color="accent"
diff --git
a/ui/src/app/dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component.ts
b/ui/src/app/dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component.ts
index ddb415b76e..b2c53014aa 100644
---
a/ui/src/app/dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component.ts
+++
b/ui/src/app/dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component.ts
@@ -16,22 +16,19 @@
*
*/
-import { Component, EventEmitter, Output } from '@angular/core';
+import { Component, EventEmitter, inject, Output } from '@angular/core';
import { MatTableDataSource } from '@angular/material/table';
import { Dashboard, DashboardService } from '@streampipes/platform-services';
import {
ConfirmDialogComponent,
- CurrentUserService,
- DialogService,
+ DateFormatService,
} from '@streampipes/shared-ui';
-import { AuthService } from '../../../../services/auth.service';
import { SpDataExplorerOverviewDirective } from
'../../../../data-explorer/components/overview/data-explorer-overview.directive';
-import { DataExplorerRoutingService } from
'../../../../data-explorer-shared/services/data-explorer-routing.service';
import { MatDialog } from '@angular/material/dialog';
-import { DataExplorerDashboardService } from
'../../../services/dashboard.service';
+import { DataExplorerDashboardService } from
'../../../../dashboard-shared/services/dashboard.service';
import { DataExplorerSharedService } from
'../../../../data-explorer-shared/services/data-explorer-shared.service';
import { TranslateService } from '@ngx-translate/core';
-import { DateFormatService } from '@streampipes/shared-ui';
+import { Router } from '@angular/router';
@Component({
selector: 'sp-dashboard-overview-table',
@@ -50,20 +47,13 @@ export class DashboardOverviewTableComponent extends
SpDataExplorerOverviewDirec
@Output()
resourceCountEmitter: EventEmitter<number> = new EventEmitter();
- constructor(
- private dashboardService: DashboardService,
- private dataExplorerDashboardService: DataExplorerDashboardService,
- private dataExplorerSharedService: DataExplorerSharedService,
- public dialogService: DialogService,
- routingService: DataExplorerRoutingService,
- authService: AuthService,
- currentUserService: CurrentUserService,
- private dialog: MatDialog,
- protected translateService: TranslateService,
- protected dateFormatService: DateFormatService,
- ) {
- super(dialogService, authService, currentUserService, routingService);
- }
+ private dashboardService = inject(DashboardService);
+ private dataExplorerDashboardService =
inject(DataExplorerDashboardService);
+ private dataExplorerSharedService = inject(DataExplorerSharedService);
+ private dialog = inject(MatDialog);
+ protected translateService = inject(TranslateService);
+ protected dateFormatService = inject(DateFormatService);
+ private router = inject(Router);
afterInit(): void {
this.displayedColumns = [
@@ -158,4 +148,8 @@ export class DashboardOverviewTableComponent extends
SpDataExplorerOverviewDirec
formatDate(timestamp?: number): string {
return this.dateFormatService.formatDate(timestamp);
}
+
+ openDashboardInKioskMode(dashboard: Dashboard) {
+ this.router.navigate(['dashboard-kiosk', dashboard.elementId]);
+ }
}
diff --git
a/ui/src/app/dashboard/components/overview/dashboard-overview.component.ts
b/ui/src/app/dashboard/components/overview/dashboard-overview.component.ts
index d40ea058e4..7fb66fbdec 100644
--- a/ui/src/app/dashboard/components/overview/dashboard-overview.component.ts
+++ b/ui/src/app/dashboard/components/overview/dashboard-overview.component.ts
@@ -27,7 +27,7 @@ import { AuthService } from '../../../services/auth.service';
import { UserPrivilege } from '../../../_enums/user-privilege.enum';
import { SpDashboardRoutes } from '../../dashboard.routes';
import { Dashboard } from '@streampipes/platform-services';
-import { DataExplorerDashboardService } from
'../../services/dashboard.service';
+import { DataExplorerDashboardService } from
'../../../dashboard-shared/services/dashboard.service';
import { DashboardOverviewTableComponent } from
'./dashboard-overview-table/dashboard-overview-table.component';
import { TranslateService } from '@ngx-translate/core';
diff --git a/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
b/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
index 4931bc6417..57aa4e6c35 100644
--- a/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
+++ b/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
@@ -16,9 +16,9 @@
*
*/
-import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
+import { Component, inject, OnDestroy, OnInit, ViewChild } from
'@angular/core';
import { Observable, of, Subscription, timer } from 'rxjs';
-import { DashboardGridViewComponent } from
'../chart-view/grid-view/dashboard-grid-view.component';
+import { DashboardGridViewComponent } from
'../../../dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component';
import {
ClientDashboardItem,
Dashboard,
@@ -36,7 +36,7 @@ import {
ActivatedRouteSnapshot,
RouterStateSnapshot,
} from '@angular/router';
-import { DashboardSlideViewComponent } from
'../chart-view/slide-view/dashboard-slide-view.component';
+import { DashboardSlideViewComponent } from
'../../../dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component';
import {
ConfirmDialogComponent,
CurrentUserService,
@@ -50,6 +50,7 @@ import { DataExplorerRoutingService } from
'../../../data-explorer-shared/servic
import { DataExplorerDetectChangesService } from
'../../../data-explorer/services/data-explorer-detect-changes.service';
import { SupportsUnsavedChangeDialog } from
'../../../data-explorer-shared/models/dataview-dashboard.model';
import { TranslateService } from '@ngx-translate/core';
+import { DataExplorerDashboardService } from
'../../../dashboard-shared/services/dashboard.service';
@Component({
selector: 'sp-dashboard-panel',
@@ -85,18 +86,17 @@ export class DashboardPanelComponent
authSubscription: Subscription;
refreshSubscription: Subscription;
- constructor(
- private detectChangesService: DataExplorerDetectChangesService,
- private dialog: MatDialog,
- private timeSelectionService: TimeSelectionService,
- private authService: AuthService,
- private currentUserService: CurrentUserService,
- private dashboardService: DashboardService,
- private route: ActivatedRoute,
- private routingService: DataExplorerRoutingService,
- private breadcrumbService: SpBreadcrumbService,
- private translateService: TranslateService,
- ) {}
+ private detectChangesService = inject(DataExplorerDetectChangesService);
+ private dialog = inject(MatDialog);
+ private timeSelectionService = inject(TimeSelectionService);
+ private authService = inject(AuthService);
+ private currentUserService = inject(CurrentUserService);
+ private dashboardService = inject(DashboardService);
+ private route = inject(ActivatedRoute);
+ private routingService = inject(DataExplorerRoutingService);
+ private breadcrumbService = inject(SpBreadcrumbService);
+ private translateService = inject(TranslateService);
+ private dataExplorerDashboardService =
inject(DataExplorerDashboardService);
public ngOnInit() {
const params = this.route.snapshot.params;
@@ -130,10 +130,11 @@ export class DashboardPanelComponent
dashboardItem.rows = 4;
dashboardItem.x = 0;
dashboardItem.y = 0;
+ dashboardItem.widgetId =
+ this.dataExplorerDashboardService.makeUniqueWidgetId();
this.dashboard.widgets.push(dashboardItem);
setTimeout(() => {
if (this.viewMode === 'grid') {
- console.log(this.dashboardGrid);
this.dashboardGrid.loadWidgetConfig(dataViewElementId, true);
} else {
this.dashboardSlide.loadWidgetConfig(dataViewElementId, true);
@@ -181,6 +182,7 @@ export class DashboardPanelComponent
removeChartFromDashboard(widgetIndex: number) {
this.dashboard.widgets.splice(widgetIndex, 1);
+ this.widgets.splice(widgetIndex, 1);
}
updateDateRange(timeSettings: TimeSettings) {
@@ -210,12 +212,19 @@ export class DashboardPanelComponent
getDashboard(dashboardId: string, startTime: number, endTime: number) {
this.dashboardService
.getCompositeDashboard(dashboardId)
- .subscribe(compositeDashboard => {
- this.dashboard = compositeDashboard.dashboard;
- this.widgets = compositeDashboard.widgets;
- this.originalDashboard = JSON.parse(
- JSON.stringify(compositeDashboard.dashboard),
- );
+ .subscribe(resp => {
+ if (resp.ok) {
+ const compositeDashboard = resp.body;
+ compositeDashboard.dashboard.widgets.forEach(w => {
+ w.widgetId ??=
+
this.dataExplorerDashboardService.makeUniqueWidgetId();
+ });
+ this.dashboard = compositeDashboard.dashboard;
+ this.widgets = compositeDashboard.widgets;
+ this.originalDashboard = JSON.parse(
+ JSON.stringify(compositeDashboard.dashboard),
+ );
+ }
this.breadcrumbService.updateBreadcrumb(
this.breadcrumbService.makeRoute(
[SpDashboardRoutes.BASE],
diff --git a/ui/src/app/dashboard/dashboard.module.ts
b/ui/src/app/dashboard/dashboard.module.ts
index adc7211f38..e5a299417e 100644
--- a/ui/src/app/dashboard/dashboard.module.ts
+++ b/ui/src/app/dashboard/dashboard.module.ts
@@ -61,8 +61,6 @@ import { MatSliderModule } from '@angular/material/slider';
import { DataExplorerSharedModule } from
'../data-explorer-shared/data-explorer-shared.module';
import { DashboardPanelComponent } from
'./components/panel/dashboard-panel.component';
import { DataExplorerPanelCanDeactivateGuard } from
'../data-explorer-shared/services/data-explorer-panel.can-deactivate.guard';
-import { DashboardGridViewComponent } from
'./components/chart-view/grid-view/dashboard-grid-view.component';
-import { DashboardSlideViewComponent } from
'./components/chart-view/slide-view/dashboard-slide-view.component';
import { DashboardToolbarComponent } from
'./components/panel/dashboard-toolbar/dashboard-toolbar.component';
import { ChartSelectionPanelComponent } from
'./components/panel/chart-selection-panel/chart-selection-panel.component';
import { ChartPreviewComponent } from
'./components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component';
@@ -70,6 +68,7 @@ import { ChartSelectionComponent } from
'./components/panel/chart-selection-pane
import { EditDashboardDialogComponent } from
'./dialogs/edit-dashboard/edit-dashboard-dialog.component';
import { DashboardOverviewTableComponent } from
'./components/overview/dashboard-overview-table/dashboard-overview-table.component';
import { TranslateModule } from '@ngx-translate/core';
+import { DashboardSharedModule } from
'../dashboard-shared/dashboard-shared.module';
@NgModule({
imports: [
@@ -113,6 +112,7 @@ import { TranslateModule } from '@ngx-translate/core';
ServicesModule,
SharedUiModule,
DataExplorerSharedModule,
+ DashboardSharedModule,
TranslateModule.forChild(),
RouterModule.forChild([
{
@@ -138,9 +138,7 @@ import { TranslateModule } from '@ngx-translate/core';
],
declarations: [
DashboardOverviewComponent,
- DashboardGridViewComponent,
DashboardPanelComponent,
- DashboardSlideViewComponent,
DashboardToolbarComponent,
ChartSelectionPanelComponent,
ChartPreviewComponent,
diff --git
a/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.html
b/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.html
index 21edefca05..1dc6c0a031 100644
---
a/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.html
+++
b/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.html
@@ -40,112 +40,118 @@
>
{{ configuredWidget.baseAppearanceConfig.widgetTitle }}
</div>
- <div fxFlex="100" fxLayout="row" fxLayoutAlign="end center">
- <mat-spinner
- [diameter]="20"
- color="primary"
- class="mr-10"
- *ngIf="timerActive"
- >
- </mat-spinner>
- <div
- class="time-counter"
- *ngIf="editMode"
- [ngStyle]="{
- background:
- configuredWidget.baseAppearanceConfig.textColor,
- color: configuredWidget.baseAppearanceConfig
- .backgroundColor
- }"
- >
- {{ loadingTime }}s
- </div>
- <button
- mat-icon-button
- [matMenuTriggerFor]="menu"
- [aria-label]="'More options' | translate"
- [matTooltip]="'More options' | translate"
- *ngIf="!dataViewMode"
- [attr.data-cy]="
- 'more-options-' +
-
configuredWidget.baseAppearanceConfig.widgetTitle.replaceAll(
- ' ',
- ''
- )
- "
- >
- <mat-icon>more_vert</mat-icon>
- </button>
- <mat-menu #menu="matMenu">
- <button mat-menu-item (click)="downloadDataAsFile()">
- <mat-icon>get_app</mat-icon>
- <span>{{ 'Download data' | translate }}</span>
- </button>
+ @if (!kioskMode) {
+ <div fxFlex="100" fxLayout="row" fxLayoutAlign="end center">
+ <mat-spinner
+ [diameter]="20"
+ color="primary"
+ class="mr-10"
+ *ngIf="timerActive"
+ >
+ </mat-spinner>
+ <div
+ class="time-counter"
+ *ngIf="editMode"
+ [ngStyle]="{
+ background:
+
configuredWidget.baseAppearanceConfig.textColor,
+ color: configuredWidget.baseAppearanceConfig
+ .backgroundColor
+ }"
+ >
+ {{ loadingTime }}s
+ </div>
<button
- mat-menu-item
- (click)="startEditMode()"
- *ngIf="hasDataExplorerWritePrivileges"
+ mat-icon-button
+ [matMenuTriggerFor]="menu"
+ [aria-label]="'More options' | translate"
+ [matTooltip]="'More options' | translate"
+ *ngIf="!dataViewMode"
[attr.data-cy]="
- 'start-edit-' +
+ 'more-options-' +
configuredWidget.baseAppearanceConfig.widgetTitle.replaceAll(
' ',
''
)
"
>
- <mat-icon>edit</mat-icon>
- <span>{{ 'Edit Chart' | translate }}</span>
+ <mat-icon>more_vert</mat-icon>
</button>
- </mat-menu>
- <button
- mat-icon-button
- [matMenuTriggerFor]="optMenu"
- *ngIf="!globalTimeEnabled"
- [aria-label]="'Options' | translate"
- data-cy="options-data-explorer"
- #menuTrigger="matMenuTrigger"
- [matTooltip]="tooltipText"
- matTooltipClass="no-wrap-tooltip"
- >
- <mat-icon
- [color]="timeSettingsModified ? 'primary' : 'default'"
- >alarm_clock</mat-icon
- >
- </button>
- <mat-menu #optMenu="matMenu" class="large-menu">
- <sp-time-selector-menu
- #timeSelectorMenu
- *ngIf="quickSelections"
- [timeSettings]="clonedTimeSettings"
- [quickSelections]="quickSelections"
- [enableTimePicker]="enableTimePicker"
- [maxDayRange]="maxDayRange"
- [labels]="labels"
-
(timeSettingsEmitter)="modifyWidgetTimeSettings($event)"
- class="w-100"
- >
+ <mat-menu #menu="matMenu">
+ <button mat-menu-item (click)="downloadDataAsFile()">
+ <mat-icon>get_app</mat-icon>
+ <span>{{ 'Download data' | translate }}</span>
+ </button>
<button
- mat-raised-button
- class="mat-basic"
- (click)="resetWidgetTimeSettings()"
+ mat-menu-item
+ (click)="startEditMode()"
+ *ngIf="hasDataExplorerWritePrivileges"
+ [attr.data-cy]="
+ 'start-edit-' +
+
configuredWidget.baseAppearanceConfig.widgetTitle.replaceAll(
+ ' ',
+ ''
+ )
+ "
>
- {{ 'Reset' | translate }}
+ <mat-icon>edit</mat-icon>
+ <span>{{ 'Edit Chart' | translate }}</span>
</button>
- </sp-time-selector-menu>
- </mat-menu>
- <button
- mat-icon-button
- (click)="removeWidget()"
- [matTooltip]="'Delete Chart' | translate"
- *ngIf="editMode && hasDataExplorerWritePrivileges"
- [attr.data-cy]="
- 'remove-' +
- configuredWidget.baseAppearanceConfig.widgetTitle
- "
- >
- <mat-icon>clear</mat-icon>
- </button>
- </div>
+ </mat-menu>
+ <button
+ mat-icon-button
+ [matMenuTriggerFor]="optMenu"
+ *ngIf="!globalTimeEnabled"
+ [aria-label]="'Options' | translate"
+ data-cy="options-data-explorer"
+ #menuTrigger="matMenuTrigger"
+ [matTooltip]="tooltipText"
+ matTooltipClass="no-wrap-tooltip"
+ >
+ <mat-icon
+ [color]="
+ timeSettingsModified ? 'primary' : 'default'
+ "
+ >alarm_clock</mat-icon
+ >
+ </button>
+ <mat-menu #optMenu="matMenu" class="large-menu">
+ <sp-time-selector-menu
+ #timeSelectorMenu
+ *ngIf="quickSelections"
+ [timeSettings]="clonedTimeSettings"
+ [quickSelections]="quickSelections"
+ [enableTimePicker]="enableTimePicker"
+ [maxDayRange]="maxDayRange"
+ [labels]="labels"
+ (timeSettingsEmitter)="
+ modifyWidgetTimeSettings($event)
+ "
+ class="w-100"
+ >
+ <button
+ mat-raised-button
+ class="mat-basic"
+ (click)="resetWidgetTimeSettings()"
+ >
+ {{ 'Reset' | translate }}
+ </button>
+ </sp-time-selector-menu>
+ </mat-menu>
+ <button
+ mat-icon-button
+ (click)="removeWidget()"
+ [matTooltip]="'Delete Chart' | translate"
+ *ngIf="editMode && hasDataExplorerWritePrivileges"
+ [attr.data-cy]="
+ 'remove-' +
+ configuredWidget.baseAppearanceConfig.widgetTitle
+ "
+ >
+ <mat-icon>clear</mat-icon>
+ </button>
+ </div>
+ }
</div>
<div
fxLayout="column"
diff --git
a/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.ts
b/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.ts
index fb13faf5bb..91f35ac40c 100644
---
a/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.ts
+++
b/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.ts
@@ -58,7 +58,7 @@ import { DataExplorerSharedService } from
'../../services/data-explorer-shared.s
import { MatMenuTrigger } from '@angular/material/menu';
@Component({
- selector: 'sp-data-explorer-dashboard-widget',
+ selector: 'sp-data-explorer-chart-container',
templateUrl: './data-explorer-chart-container.component.html',
styleUrls: ['./data-explorer-chart-container.component.scss'],
standalone: false,
@@ -93,6 +93,9 @@ export class DataExplorerChartContainerComponent
@Input()
gridMode = true;
+ @Input()
+ kioskMode = false;
+
@Input()
widgetIndex: number;
@@ -252,6 +255,7 @@ export class DataExplorerChartContainerComponent
this.componentRef.instance.gridsterItemComponent =
this.gridsterItemComponent;
this.componentRef.instance.editMode = this.editMode;
+ this.componentRef.instance.kioskMode = this.kioskMode;
this.componentRef.instance.dataViewDashboardItem = this.dashboardItem;
this.componentRef.instance.dataExplorerWidget = this.configuredWidget;
this.componentRef.instance.previewMode = this.previewMode;
diff --git
a/ui/src/app/data-explorer-shared/components/charts/base/base-data-explorer-widget.directive.ts
b/ui/src/app/data-explorer-shared/components/charts/base/base-data-explorer-widget.directive.ts
index 508dc62ff5..2eebe05707 100644
---
a/ui/src/app/data-explorer-shared/components/charts/base/base-data-explorer-widget.directive.ts
+++
b/ui/src/app/data-explorer-shared/components/charts/base/base-data-explorer-widget.directive.ts
@@ -70,6 +70,7 @@ export abstract class BaseDataExplorerWidgetDirective<
@Input() gridsterItem: GridsterItem;
@Input() gridsterItemComponent: GridsterItemComponent;
@Input() editMode: boolean;
+ @Input() kioskMode: boolean;
@Input() timeSettings: TimeSettings;
diff --git
a/ui/src/app/data-explorer-shared/components/charts/base/echarts-widget.component.ts
b/ui/src/app/data-explorer-shared/components/charts/base/echarts-widget.component.ts
index 0ca75865a8..375499beda 100644
---
a/ui/src/app/data-explorer-shared/components/charts/base/echarts-widget.component.ts
+++
b/ui/src/app/data-explorer-shared/components/charts/base/echarts-widget.component.ts
@@ -124,6 +124,24 @@ export class SpEchartsWidgetComponent<T extends
DataExplorerWidgetModel>
},
),
};
+ if (this.kioskMode) {
+ ['toolbox', 'visualMap'].forEach(key => {
+ const item = this.option[key];
+ if (item) {
+ (Array.isArray(item) ? item : [item]).forEach(
+ obj => (obj.show = false),
+ );
+ }
+ });
+ Object.assign(this.option, {
+ grid: {
+ left: 60,
+ right: 60,
+ top: 60,
+ bottom: 60,
+ },
+ });
+ }
} else {
this.showInvalidConfiguration = true;
}
diff --git a/ui/src/app/data-explorer-shared/models/dataview-dashboard.model.ts
b/ui/src/app/data-explorer-shared/models/dataview-dashboard.model.ts
index 57f4089b1b..56fd261462 100644
--- a/ui/src/app/data-explorer-shared/models/dataview-dashboard.model.ts
+++ b/ui/src/app/data-explorer-shared/models/dataview-dashboard.model.ts
@@ -47,6 +47,7 @@ export interface BaseWidgetData<T extends
DataExplorerWidgetModel> {
gridsterItem: GridsterItem;
gridsterItemComponent: GridsterItemComponent;
editMode: boolean;
+ kioskMode: boolean;
timeSettings: TimeSettings;
diff --git
a/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.html
b/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.html
index ac7c19b8c4..9bb81bf9b0 100644
---
a/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.html
+++
b/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.html
@@ -68,7 +68,7 @@
</mat-drawer>
<mat-drawer-content class="h-100 dashboard-grid">
<div #panel fxFlex="100" fxLayout="column">
- <sp-data-explorer-dashboard-widget
+ <sp-data-explorer-chart-container
*ngIf="
dataView &&
gridsterItemComponent &&
@@ -83,7 +83,7 @@
"
(startEditModeEmitter)="editDataView()"
>
- </sp-data-explorer-dashboard-widget>
+ </sp-data-explorer-chart-container>
</div>
</mat-drawer-content>
</mat-drawer-container>
diff --git
a/ui/src/app/data-explorer/components/overview/data-explorer-overview-table/data-explorer-overview-table.component.ts
b/ui/src/app/data-explorer/components/overview/data-explorer-overview-table/data-explorer-overview-table.component.ts
index 5d8770f0a7..65e04ab49d 100644
---
a/ui/src/app/data-explorer/components/overview/data-explorer-overview-table/data-explorer-overview-table.component.ts
+++
b/ui/src/app/data-explorer/components/overview/data-explorer-overview-table/data-explorer-overview-table.component.ts
@@ -16,7 +16,7 @@
*
*/
-import { Component, EventEmitter, Output } from '@angular/core';
+import { Component, EventEmitter, inject, Output } from '@angular/core';
import { SpDataExplorerOverviewDirective } from
'../data-explorer-overview.directive';
import { MatTableDataSource } from '@angular/material/table';
import {
@@ -34,6 +34,7 @@ import { DataExplorerRoutingService } from
'../../../../data-explorer-shared/ser
import { DataExplorerSharedService } from
'../../../../data-explorer-shared/services/data-explorer-shared.service';
import { MatDialog } from '@angular/material/dialog';
import { TranslateService } from '@ngx-translate/core';
+import { DataExplorerDashboardService } from
'../../../../dashboard-shared/services/dashboard.service';
@Component({
selector: 'sp-data-explorer-overview-table',
@@ -50,19 +51,11 @@ export class SpDataExplorerDataViewOverviewComponent
extends SpDataExplorerOverv
@Output()
resourceCountEmitter: EventEmitter<number> = new EventEmitter();
- constructor(
- private dataViewService: ChartService,
- private dataExplorerDashboardService: DataExplorerSharedService,
- public dialogService: DialogService,
- authService: AuthService,
- currentUserService: CurrentUserService,
- routingService: DataExplorerRoutingService,
- private dialog: MatDialog,
- private translateService: TranslateService,
- protected dateFormatService: DateFormatService,
- ) {
- super(dialogService, authService, currentUserService, routingService);
- }
+ private dataViewService = inject(ChartService);
+ private dataExplorerDashboardService = inject(DataExplorerSharedService);
+ private dialog = inject(MatDialog);
+ private translateService = inject(TranslateService);
+ private dateFormatService = inject(DateFormatService);
afterInit(): void {
this.displayedColumns = [
diff --git
a/ui/src/app/data-explorer/components/overview/data-explorer-overview.component.ts
b/ui/src/app/data-explorer/components/overview/data-explorer-overview.component.ts
index 6ed6fa791c..8d9f50bcbf 100644
---
a/ui/src/app/data-explorer/components/overview/data-explorer-overview.component.ts
+++
b/ui/src/app/data-explorer/components/overview/data-explorer-overview.component.ts
@@ -16,7 +16,7 @@
*
*/
-import { Component, ViewChild } from '@angular/core';
+import { Component, inject, ViewChild } from '@angular/core';
import {
CurrentUserService,
DialogService,
@@ -41,15 +41,7 @@ export class DataExplorerOverviewComponent extends
SpDataExplorerOverviewDirecti
@ViewChild(SpDataExplorerDataViewOverviewComponent)
chartsOverview: SpDataExplorerDataViewOverviewComponent;
- constructor(
- public dialogService: DialogService,
- private breadcrumbService: SpBreadcrumbService,
- authService: AuthService,
- currentUserService: CurrentUserService,
- routingService: DataExplorerRoutingService,
- ) {
- super(dialogService, authService, currentUserService, routingService);
- }
+ private breadcrumbService = inject(SpBreadcrumbService);
afterInit(): void {
this.breadcrumbService.updateBreadcrumb(
diff --git
a/ui/src/app/data-explorer/components/overview/data-explorer-overview.directive.ts
b/ui/src/app/data-explorer/components/overview/data-explorer-overview.directive.ts
index 2e80ab99d1..7a9a887c53 100644
---
a/ui/src/app/data-explorer/components/overview/data-explorer-overview.directive.ts
+++
b/ui/src/app/data-explorer/components/overview/data-explorer-overview.directive.ts
@@ -16,7 +16,7 @@
*
*/
-import { Directive, OnDestroy, OnInit } from '@angular/core';
+import { Directive, inject, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { UserPrivilege } from '../../../_enums/user-privilege.enum';
import { UserRole } from '../../../_enums/user-role.enum';
@@ -34,12 +34,10 @@ export abstract class SpDataExplorerOverviewDirective
authSubscription: Subscription;
- protected constructor(
- public dialogService: DialogService,
- protected authService: AuthService,
- protected currentUserService: CurrentUserService,
- protected routingService: DataExplorerRoutingService,
- ) {}
+ public dialogService = inject(DialogService);
+ protected authService = inject(AuthService);
+ protected currentUserService = inject(CurrentUserService);
+ protected routingService = inject(DataExplorerRoutingService);
ngOnInit() {
this.authSubscription = this.currentUserService.user$.subscribe(