diff --git a/streampipes-model/src/main/java/org/apache/streampipes/model/datalake/DataExplorerWidgetModel.java b/streampipes-model/src/main/java/org/apache/streampipes/model/datalake/DataExplorerWidgetModel.java index 2371363acc..e9ac5bc159 100644 --- a/streampipes-model/src/main/java/org/apache/streampipes/model/datalake/DataExplorerWidgetModel.java +++ b/streampipes-model/src/main/java/org/apache/streampipes/model/datalake/DataExplorerWidgetModel.java @@ -40,6 +40,9 @@ public class DataExplorerWidgetModel extends DashboardEntity { @JsonSerialize(using = CustomMapSerializer.class, as = Map.class) private Map dataConfig; + @JsonSerialize(using = CustomMapSerializer.class, as = Map.class) + private Map timeSettings; + private String pipelineId; private String measureName; @@ -49,6 +52,7 @@ public DataExplorerWidgetModel() { this.baseAppearanceConfig = new HashMap<>(); this.visualizationConfig = new HashMap<>(); this.dataConfig = new HashMap<>(); + this.timeSettings = new HashMap<>(); } public String getWidgetId() { @@ -107,4 +111,8 @@ public void setMeasureName(String measureName) { this.measureName = measureName; } + public Map getTimeSettings() { + return this.timeSettings; + } + } diff --git a/ui/projects/streampipes/platform-services/src/lib/apis/data-view-data-explorer.service.ts b/ui/projects/streampipes/platform-services/src/lib/apis/data-view-data-explorer.service.ts index a9315adca6..68c1988cb4 100644 --- a/ui/projects/streampipes/platform-services/src/lib/apis/data-view-data-explorer.service.ts +++ b/ui/projects/streampipes/platform-services/src/lib/apis/data-view-data-explorer.service.ts @@ -21,7 +21,6 @@ import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { Dashboard } from '../model/dashboard/dashboard.model'; import { Injectable } from '@angular/core'; -import { DatalakeRestService } from './datalake-rest.service'; import { DataExplorerWidgetModel, DataLakeMeasure, @@ -34,32 +33,13 @@ import { SharedDatalakeRestService } from './shared-dashboard.service'; export class DataViewDataExplorerService { constructor( private http: HttpClient, - private dataLakeRestService: DatalakeRestService, private sharedDatalakeRestService: SharedDatalakeRestService, ) {} - getVisualizableData(): Observable { - return this.dataLakeRestService.getAllMeasurementSeries().pipe( - map(data => { - return (data as any[]).map(d => - DataLakeMeasure.fromData(d as DataLakeMeasure), - ); - }), - ); - } - getDataViews(): Observable { return this.sharedDatalakeRestService.getDashboards(this.dashboardUrl); } - getDataView(dataViewId: string): Observable { - return this.http.get(this.dashboardUrl + '/' + dataViewId).pipe( - map(data => { - return data as Dashboard; - }), - ); - } - updateDashboard(dashboard: Dashboard): Observable { return this.sharedDatalakeRestService.updateDashboard( this.dashboardUrl, @@ -97,6 +77,12 @@ export class DataViewDataExplorerService { return `${this.baseUrl}/api/v3/datalake/dashboard/widgets`; } + getAllWidgets(): Observable { + return this.http + .get(this.dashboardWidgetUrl) + .pipe(map(res => res as DataExplorerWidgetModel[])); + } + getWidget(widgetId: string): Observable { return this.http.get(this.dashboardWidgetUrl + '/' + widgetId).pipe( map(response => { diff --git a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts index cf79d51045..1679c7e64e 100644 --- a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts +++ b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts @@ -20,7 +20,7 @@ /* tslint:disable */ /* eslint-disable */ // @ts-nocheck -// Generated using typescript-generator version 3.2.1263 on 2024-06-30 09:10:19. +// Generated using typescript-generator version 3.2.1263 on 2024-07-13 11:00:10. export class NamedStreamPipesEntity implements Storable { '@class': @@ -1097,6 +1097,7 @@ export class DataExplorerWidgetModel extends DashboardEntity { dataConfig: { [index: string]: any }; measureName: string; pipelineId: string; + timeSettings: { [index: string]: any }; visualizationConfig: { [index: string]: any }; widgetId: string; widgetType: string; @@ -1118,6 +1119,9 @@ export class DataExplorerWidgetModel extends DashboardEntity { ); instance.measureName = data.measureName; instance.pipelineId = data.pipelineId; + instance.timeSettings = __getCopyObjectFn(__identity())( + data.timeSettings, + ); instance.visualizationConfig = __getCopyObjectFn(__identity())( data.visualizationConfig, ); diff --git a/ui/src/app/data-explorer/components/data-view/data-explorer-data-view.component.html b/ui/src/app/data-explorer/components/data-view/data-explorer-data-view.component.html new file mode 100644 index 0000000000..4cd25092a3 --- /dev/null +++ b/ui/src/app/data-explorer/components/data-view/data-explorer-data-view.component.html @@ -0,0 +1,74 @@ + + + +
+ + +
+
+ + +
+ + +
+
+ +
+ + +
+
+
+
+
diff --git a/ui/src/app/data-explorer/components/data-view/data-explorer-data-view.component.scss b/ui/src/app/data-explorer/components/data-view/data-explorer-data-view.component.scss new file mode 100644 index 0000000000..fa736754ff --- /dev/null +++ b/ui/src/app/data-explorer/components/data-view/data-explorer-data-view.component.scss @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +.fixed-height { + height: 50px; +} + +.data-explorer-options { + padding: 0px; +} + +.data-explorer-options-item { + display: inline; + margin-right: 10px; +} + +.m-20 { + margin: 20px; +} + +.h-100 { + height: 100%; +} + +.dashboard-grid { + display: flex; + flex-direction: column; + flex: 1 1 100%; +} + +.designer-panel-container { + width: 100%; + height: 100%; +} + +.designer-panel { + width: 450px; + border: 1px solid var(--color-tab-border); +} diff --git a/ui/src/app/data-explorer/components/data-view/data-explorer-data-view.component.ts b/ui/src/app/data-explorer/components/data-view/data-explorer-data-view.component.ts new file mode 100644 index 0000000000..478328c207 --- /dev/null +++ b/ui/src/app/data-explorer/components/data-view/data-explorer-data-view.component.ts @@ -0,0 +1,120 @@ +/* + * 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, ElementRef, OnInit, ViewChild } from '@angular/core'; +import { + DataExplorerWidgetModel, + DataLakeMeasure, + DataViewDataExplorerService, + TimeSettings, +} from '@streampipes/platform-services'; +import { ActivatedRoute, Router } from '@angular/router'; +import { TimeSelectionService } from '../../services/time-selection.service'; + +@Component({ + selector: 'sp-data-explorer-data-view', + templateUrl: './data-explorer-data-view.component.html', + styleUrls: ['./data-explorer-data-view.component.scss'], +}) +export class DataExplorerDataViewComponent implements OnInit { + dataViewLoaded = false; + timeSettings: TimeSettings; + + editMode = true; + dataView: DataExplorerWidgetModel; + dataLakeMeasure: DataLakeMeasure; + gridsterItemComponent: any; + + @ViewChild('panel', { static: false }) outerPanel: ElementRef; + + constructor( + private route: ActivatedRoute, + private router: Router, + private dataViewService: DataViewDataExplorerService, + private timeSelectionService: TimeSelectionService, + ) {} + + ngOnInit() { + const dataViewId = this.route.snapshot.params.id; + this.editMode = this.route.snapshot.queryParams.editMode; + + if (dataViewId) { + this.dataViewService.getWidget(dataViewId).subscribe(res => { + this.dataView = res; + if (!this.dataView.timeSettings?.startTime) { + this.timeSettings = this.makeDefaultTimeSettings(); + } else { + this.timeSettings = this.dataView + .timeSettings as TimeSettings; + } + this.afterDataViewLoaded(); + }); + } else { + this.createWidget(); + this.timeSettings = this.makeDefaultTimeSettings(); + this.afterDataViewLoaded(); + } + } + + afterDataViewLoaded(): void { + this.dataViewLoaded = true; + setTimeout(() => { + const width = this.outerPanel.nativeElement.offsetWidth; + const height = this.outerPanel.nativeElement.offsetHeight; + this.gridsterItemComponent = { width, height }; + this.timeSelectionService.notify(this.timeSettings); + }); + } + + makeDefaultTimeSettings(): TimeSettings { + return { + dynamicSelection: 1440, + endTime: new Date().getTime(), + startTime: new Date().getTime() - 1000 * 24 * 60 * 60, + }; + } + + createWidget() { + this.dataLakeMeasure = new DataLakeMeasure(); + this.dataView = new DataExplorerWidgetModel(); + this.dataView['@class'] = + 'org.apache.streampipes.model.datalake.DataExplorerWidgetModel'; + this.dataView.baseAppearanceConfig = {}; + this.dataView.baseAppearanceConfig.widgetTitle = 'New Widget'; + this.dataView.dataConfig = {}; + this.dataView.dataConfig.ignoreMissingValues = false; + this.dataView.baseAppearanceConfig.backgroundColor = '#FFFFFF'; + this.dataView.baseAppearanceConfig.textColor = '#3e3e3e'; + this.dataView = { ...this.dataView }; + } + + saveDataView(): void { + this.dataView.timeSettings = this.timeSettings; + const observable = this.editMode + ? this.dataViewService.updateWidget(this.dataView) + : this.dataViewService.saveWidget(this.dataView); + observable.subscribe(() => { + this.router.navigate(['dataexplorer']); + }); + } + + updateDateRange(timeSettings: TimeSettings) { + this.timeSettings = timeSettings; + this.timeSelectionService.notify(timeSettings); + } +} 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 new file mode 100644 index 0000000000..4be9360243 --- /dev/null +++ b/ui/src/app/data-explorer/components/data-view/data-view-toolbar/data-explorer-data-view-toolbar.component.html @@ -0,0 +1,54 @@ + + +
+
+ + +
+ + +
+
+
diff --git a/ui/src/app/data-explorer/components/data-view/data-view-toolbar/data-explorer-data-view-toolbar.component.ts b/ui/src/app/data-explorer/components/data-view/data-view-toolbar/data-explorer-data-view-toolbar.component.ts new file mode 100644 index 0000000000..37295d4769 --- /dev/null +++ b/ui/src/app/data-explorer/components/data-view/data-view-toolbar/data-explorer-data-view-toolbar.component.ts @@ -0,0 +1,40 @@ +/* + * 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, EventEmitter, Input, Output } from '@angular/core'; +import { TimeSettings } from '@streampipes/platform-services'; + +@Component({ + selector: 'sp-data-explorer-data-view-toolbar', + templateUrl: './data-explorer-data-view-toolbar.component.html', +}) +export class DataExplorerDataViewToolbarComponent { + @Input() + editMode = true; + + @Input() + timeSettings: TimeSettings; + + timeRangeVisible = true; + + @Output() + saveDataViewEmitter: EventEmitter = new EventEmitter(); + + @Output() + updateDateRangeEmitter: EventEmitter = new EventEmitter(); +} diff --git a/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview.component.html b/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview.component.html deleted file mode 100644 index 0aadcd6c57..0000000000 --- a/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview.component.html +++ /dev/null @@ -1,144 +0,0 @@ - - - -
- -
-
- -
- - - - Data View - - - {{ element.name }}
- {{ element.description }} - -
- - - - -
- - - - - -
- -
-
-
-
-
diff --git a/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview.component.ts b/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview.component.ts deleted file mode 100644 index 6a5e38a4b5..0000000000 --- a/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview.component.ts +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { MatTableDataSource } from '@angular/material/table'; -import { DataExplorerEditDataViewDialogComponent } from '../../dialogs/edit-dashboard/data-explorer-edit-data-view-dialog.component'; -import { - Dashboard, - DataViewDataExplorerService, -} from '@streampipes/platform-services'; -import { - CurrentUserService, - DialogService, - PanelType, - SpBreadcrumbService, -} from '@streampipes/shared-ui'; -import { ObjectPermissionDialogComponent } from '../../../core-ui/object-permission-dialog/object-permission-dialog.component'; -import { UserRole } from '../../../_enums/user-role.enum'; -import { AuthService } from '../../../services/auth.service'; -import { UserPrivilege } from '../../../_enums/user-privilege.enum'; -import { Router } from '@angular/router'; -import { SpDataExplorerRoutes } from '../../data-explorer.routes'; -import { Subscription } from 'rxjs'; - -@Component({ - selector: 'sp-data-explorer-dashboard-overview', - templateUrl: './data-explorer-dashboard-overview.component.html', - styleUrls: ['./data-explorer-dashboard-overview.component.scss'], -}) -export class DataExplorerDashboardOverviewComponent - implements OnInit, OnDestroy -{ - dataSource = new MatTableDataSource(); - displayedColumns: string[] = []; - dashboards: Dashboard[] = []; - - isAdmin = false; - - hasDataExplorerWritePrivileges = false; - hasDataExplorerDeletePrivileges = false; - - authSubscription: Subscription; - - constructor( - private dataViewService: DataViewDataExplorerService, - private dashboardService: DataViewDataExplorerService, - public dialogService: DialogService, - private authService: AuthService, - private currentUserService: CurrentUserService, - private router: Router, - private breadcrumbService: SpBreadcrumbService, - ) {} - - ngOnInit(): void { - this.breadcrumbService.updateBreadcrumb( - this.breadcrumbService.getRootLink(SpDataExplorerRoutes.BASE), - ); - this.authSubscription = this.currentUserService.user$.subscribe( - user => { - this.hasDataExplorerWritePrivileges = this.authService.hasRole( - UserPrivilege.PRIVILEGE_WRITE_DATA_EXPLORER_VIEW, - ); - this.hasDataExplorerDeletePrivileges = this.authService.hasRole( - UserPrivilege.PRIVILEGE_DELETE_DATA_EXPLORER_VIEW, - ); - this.isAdmin = user.roles.indexOf(UserRole.ROLE_ADMIN) > -1; - this.displayedColumns = ['name', 'actions']; - }, - ); - - this.getDashboards(); - } - - ngOnDestroy() { - if (this.authSubscription) { - this.authSubscription.unsubscribe(); - } - } - - getDashboards() { - this.dataViewService.getDataViews().subscribe(data => { - this.dashboards = data.sort((a, b) => a.name.localeCompare(b.name)); - this.dataSource.data = this.dashboards; - }); - } - - openNewDataViewDialog() { - const dataViewDashboard: Dashboard = {}; - dataViewDashboard.dashboardGeneralSettings = {}; - dataViewDashboard.widgets = []; - dataViewDashboard.name = ''; - - this.openDataViewModificationDialog(true, dataViewDashboard); - } - - openDataViewModificationDialog(createMode: boolean, dashboard: Dashboard) { - const dialogRef = this.dialogService.open( - DataExplorerEditDataViewDialogComponent, - { - panelType: PanelType.STANDARD_PANEL, - title: createMode ? 'New Data View' : 'Edit Data View', - width: '70vw', - data: { - createMode: createMode, - dashboard: dashboard, - }, - }, - ); - - dialogRef.afterClosed().subscribe(result => { - this.getDashboards(); - }); - } - - showPermissionsDialog(dashboard: Dashboard) { - const dialogRef = this.dialogService.open( - ObjectPermissionDialogComponent, - { - panelType: PanelType.SLIDE_IN_PANEL, - title: 'Manage permissions', - width: '50vw', - data: { - objectInstanceId: dashboard.elementId, - headerTitle: - 'Manage permissions for dashboard ' + dashboard.name, - }, - }, - ); - - dialogRef.afterClosed().subscribe(refresh => { - if (refresh) { - this.getDashboards(); - } - }); - } - - openEditDataViewDialog(dashboard: Dashboard) { - this.openDataViewModificationDialog(false, dashboard); - } - - openDeleteDashboardDialog(dashboard: Dashboard) { - this.dashboardService.deleteDashboard(dashboard).subscribe(() => { - this.getDashboards(); - }); - } - - showDashboard(dashboard: Dashboard) { - this.router.navigate(['dataexplorer/', dashboard.elementId]); - } - - editDashboard(dashboard: Dashboard) { - this.router.navigate(['dataexplorer/', dashboard.elementId], { - queryParams: { action: 'edit' }, - }); - } -} diff --git a/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview/data-explorer-dashboard-overview.component.html b/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview/data-explorer-dashboard-overview.component.html new file mode 100644 index 0000000000..a2eae808fa --- /dev/null +++ b/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview/data-explorer-dashboard-overview.component.html @@ -0,0 +1,114 @@ + + +
+ +
+ + + + Data View + + + {{ element.name }}
+ {{ element.description }} + +
+ + + + +
+ + + + + +
+ +
+
+
+
diff --git a/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview/data-explorer-dashboard-overview.component.ts b/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview/data-explorer-dashboard-overview.component.ts new file mode 100644 index 0000000000..a883f94f62 --- /dev/null +++ b/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview/data-explorer-dashboard-overview.component.ts @@ -0,0 +1,126 @@ +/* + * 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 { MatTableDataSource } from '@angular/material/table'; +import { + Dashboard, + DataViewDataExplorerService, +} from '@streampipes/platform-services'; +import { ObjectPermissionDialogComponent } from '../../../../core-ui/object-permission-dialog/object-permission-dialog.component'; +import { + CurrentUserService, + DialogService, + PanelType, +} from '@streampipes/shared-ui'; +import { AuthService } from '../../../../services/auth.service'; +import { Router } from '@angular/router'; +import { SpDataExplorerOverviewDirective } from '../data-explorer-overview.directive'; +import { DataExplorerDashboardService } from '../../../services/data-explorer-dashboard.service'; + +@Component({ + selector: 'sp-data-explorer-dashboard-overview', + templateUrl: './data-explorer-dashboard-overview.component.html', + styleUrls: ['../data-explorer-overview.component.scss'], +}) +export class SpDataExplorerDashboardOverviewComponent extends SpDataExplorerOverviewDirective { + dataSource = new MatTableDataSource(); + displayedColumns: string[] = []; + dashboards: Dashboard[] = []; + + constructor( + private dataViewService: DataViewDataExplorerService, + private dashboardService: DataViewDataExplorerService, + private dataExplorerDashboardService: DataExplorerDashboardService, + public dialogService: DialogService, + router: Router, + authService: AuthService, + currentUserService: CurrentUserService, + ) { + super(dialogService, authService, currentUserService, router); + } + + afterInit(): void { + this.displayedColumns = ['name', 'actions']; + this.getDashboards(); + } + + showPermissionsDialog(dashboard: Dashboard) { + const dialogRef = this.dialogService.open( + ObjectPermissionDialogComponent, + { + panelType: PanelType.SLIDE_IN_PANEL, + title: 'Manage permissions', + width: '50vw', + data: { + objectInstanceId: dashboard.elementId, + headerTitle: + 'Manage permissions for dashboard ' + dashboard.name, + }, + }, + ); + + dialogRef.afterClosed().subscribe(refresh => { + if (refresh) { + this.getDashboards(); + } + }); + } + + openEditDataViewDialog(dashboard: Dashboard) { + const dialogRef = + this.dataExplorerDashboardService.openDataViewModificationDialog( + false, + dashboard, + ); + + dialogRef.afterClosed().subscribe(() => { + this.getDashboards(); + }); + } + + openDeleteDashboardDialog(dashboard: Dashboard) { + this.dashboardService.deleteDashboard(dashboard).subscribe(() => { + this.getDashboards(); + }); + } + + showDashboard(dashboard: Dashboard) { + this.router.navigate([ + 'dataexplorer', + 'dashboard', + dashboard.elementId, + ]); + } + + editDashboard(dashboard: Dashboard) { + this.router.navigate( + ['dataexplorer', 'dashboard', dashboard.elementId], + { + queryParams: { action: 'edit' }, + }, + ); + } + + getDashboards() { + this.dataViewService.getDataViews().subscribe(data => { + this.dashboards = data.sort((a, b) => a.name.localeCompare(b.name)); + this.dataSource.data = this.dashboards; + }); + } +} diff --git a/ui/src/app/data-explorer/components/overview/data-explorer-data-view-overview/data-explorer-data-view-overview.component.html b/ui/src/app/data-explorer/components/overview/data-explorer-data-view-overview/data-explorer-data-view-overview.component.html new file mode 100644 index 0000000000..7d09daa464 --- /dev/null +++ b/ui/src/app/data-explorer/components/overview/data-explorer-data-view-overview/data-explorer-data-view-overview.component.html @@ -0,0 +1,95 @@ + + +
+ +
+ + + + Data View + + + {{ element.baseAppearanceConfig.widgetTitle }}
+ +
+ + + + +
+ + + +
+ +
+
+
+
diff --git a/ui/src/app/data-explorer/components/overview/data-explorer-data-view-overview/data-explorer-data-view-overview.component.ts b/ui/src/app/data-explorer/components/overview/data-explorer-data-view-overview/data-explorer-data-view-overview.component.ts new file mode 100644 index 0000000000..277ef2b5cf --- /dev/null +++ b/ui/src/app/data-explorer/components/overview/data-explorer-data-view-overview/data-explorer-data-view-overview.component.ts @@ -0,0 +1,77 @@ +/* + * 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 { SpDataExplorerOverviewDirective } from '../data-explorer-overview.directive'; +import { MatTableDataSource } from '@angular/material/table'; +import { + Dashboard, + DataExplorerWidgetModel, + DataViewDataExplorerService, +} from '@streampipes/platform-services'; +import { CurrentUserService, DialogService } from '@streampipes/shared-ui'; +import { Router } from '@angular/router'; +import { AuthService } from '../../../../services/auth.service'; + +@Component({ + selector: 'sp-data-explorer-data-view-overview', + templateUrl: './data-explorer-data-view-overview.component.html', + styleUrls: ['../data-explorer-overview.component.scss'], +}) +export class SpDataExplorerDataViewOverviewComponent extends SpDataExplorerOverviewDirective { + dataSource = new MatTableDataSource(); + displayedColumns: string[] = []; + dashboards: Dashboard[] = []; + + constructor( + private dataViewService: DataViewDataExplorerService, + public dialogService: DialogService, + authService: AuthService, + currentUserService: CurrentUserService, + router: Router, + ) { + super(dialogService, authService, currentUserService, router); + } + + afterInit(): void { + this.displayedColumns = ['name', 'actions']; + this.getCharts(); + } + + getCharts(): void { + this.dataViewService.getAllWidgets().subscribe(widgets => { + widgets = widgets.sort((a, b) => + a.baseAppearanceConfig.widgetTitle.localeCompare( + b.baseAppearanceConfig.widgetTitle, + ), + ); + this.dataSource.data = widgets; + }); + } + + openDataView(dataView: DataExplorerWidgetModel, editMode: boolean): void { + this.router.navigate( + ['dataexplorer', 'data-view', dataView.elementId], + { queryParams: { editMode } }, + ); + } + + deleteDataView(dataView: DataExplorerWidgetModel) { + this.dataViewService.deleteWidget(dataView.elementId); + } +} diff --git a/ui/src/app/data-explorer/components/overview/data-explorer-overview.component.html b/ui/src/app/data-explorer/components/overview/data-explorer-overview.component.html new file mode 100644 index 0000000000..f2fb845c8a --- /dev/null +++ b/ui/src/app/data-explorer/components/overview/data-explorer-overview.component.html @@ -0,0 +1,55 @@ + + + +
+ + +
+
+ + +
+
diff --git a/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview.component.scss b/ui/src/app/data-explorer/components/overview/data-explorer-overview.component.scss similarity index 100% rename from ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview.component.scss rename to ui/src/app/data-explorer/components/overview/data-explorer-overview.component.scss 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 new file mode 100644 index 0000000000..151daee106 --- /dev/null +++ b/ui/src/app/data-explorer/components/overview/data-explorer-overview.component.ts @@ -0,0 +1,85 @@ +/* + * 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, ViewChild } from '@angular/core'; +import { Dashboard } from '@streampipes/platform-services'; +import { + CurrentUserService, + DialogService, + SpBreadcrumbService, +} from '@streampipes/shared-ui'; +import { AuthService } from '../../../services/auth.service'; +import { SpDataExplorerRoutes } from '../../data-explorer.routes'; +import { DataExplorerDashboardService } from '../../services/data-explorer-dashboard.service'; +import { SpDataExplorerDashboardOverviewComponent } from './data-explorer-dashboard-overview/data-explorer-dashboard-overview.component'; +import { SpDataExplorerOverviewDirective } from './data-explorer-overview.directive'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'sp-data-explorer-overview', + templateUrl: './data-explorer-overview.component.html', + styleUrls: ['./data-explorer-overview.component.scss'], +}) +export class DataExplorerOverviewComponent extends SpDataExplorerOverviewDirective { + @ViewChild(SpDataExplorerDashboardOverviewComponent) + dashboardOverview: SpDataExplorerDashboardOverviewComponent; + + constructor( + public dialogService: DialogService, + private breadcrumbService: SpBreadcrumbService, + private dataExplorerDashboardService: DataExplorerDashboardService, + authService: AuthService, + currentUserService: CurrentUserService, + router: Router, + ) { + super(dialogService, authService, currentUserService, router); + } + + afterInit(): void { + this.breadcrumbService.updateBreadcrumb( + this.breadcrumbService.getRootLink(SpDataExplorerRoutes.BASE), + ); + } + + openNewDashboardDialog() { + const dataViewDashboard: Dashboard = {}; + dataViewDashboard.dashboardGeneralSettings = {}; + dataViewDashboard.widgets = []; + dataViewDashboard.name = ''; + + this.openDataViewModificationDialog(true, dataViewDashboard); + } + + createNewDataView(): void { + this.router.navigate(['dataexplorer', 'data-view'], { + queryParams: { editMode: true }, + }); + } + + openDataViewModificationDialog(createMode: boolean, dashboard: Dashboard) { + const dialogRef = + this.dataExplorerDashboardService.openDataViewModificationDialog( + createMode, + dashboard, + ); + + dialogRef.afterClosed().subscribe(() => { + this.dashboardOverview.getDashboards(); + }); + } +} 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 new file mode 100644 index 0000000000..909bf51903 --- /dev/null +++ b/ui/src/app/data-explorer/components/overview/data-explorer-overview.directive.ts @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { Directive, OnDestroy, OnInit } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { UserPrivilege } from '../../../_enums/user-privilege.enum'; +import { UserRole } from '../../../_enums/user-role.enum'; +import { CurrentUserService, DialogService } from '@streampipes/shared-ui'; +import { AuthService } from '../../../services/auth.service'; +import { Router } from '@angular/router'; + +@Directive() +export abstract class SpDataExplorerOverviewDirective + implements OnInit, OnDestroy +{ + isAdmin = false; + + public hasDataExplorerWritePrivileges = false; + public hasDataExplorerDeletePrivileges = false; + + authSubscription: Subscription; + + protected constructor( + public dialogService: DialogService, + protected authService: AuthService, + protected currentUserService: CurrentUserService, + protected router: Router, + ) {} + + ngOnInit() { + this.authSubscription = this.currentUserService.user$.subscribe( + user => { + this.hasDataExplorerWritePrivileges = this.authService.hasRole( + UserPrivilege.PRIVILEGE_WRITE_DATA_EXPLORER_VIEW, + ); + this.hasDataExplorerDeletePrivileges = this.authService.hasRole( + UserPrivilege.PRIVILEGE_DELETE_DATA_EXPLORER_VIEW, + ); + this.isAdmin = user.roles.indexOf(UserRole.ROLE_ADMIN) > -1; + this.afterInit(); + }, + ); + } + + abstract afterInit(): void; + + ngOnDestroy() { + this.authSubscription?.unsubscribe(); + } +} diff --git a/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.ts b/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.ts index f1a937ffa6..468c266931 100644 --- a/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.ts +++ b/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.ts @@ -149,13 +149,9 @@ export class DataExplorerDashboardWidgetComponent implements OnInit, OnDestroy { } ngOnDestroy() { - this.componentRef.destroy(); - if (this.authSubscription) { - this.authSubscription.unsubscribe(); - } - if (this.widgetTypeChangedSubscription) { - this.widgetTypeChangedSubscription.unsubscribe(); - } + this.componentRef?.destroy(); + this.authSubscription?.unsubscribe(); + this.widgetTypeChangedSubscription?.unsubscribe(); } chooseWidget(widgetTypeId: string) { @@ -200,9 +196,9 @@ export class DataExplorerDashboardWidgetComponent implements OnInit, OnDestroy { this.componentRef.onDestroy(destroy => { this.componentRef.instance.cleanupSubscriptions(); - removeSub.unsubscribe(); - timerSub.unsubscribe(); - errorSub.unsubscribe(); + removeSub?.unsubscribe(); + timerSub?.unsubscribe(); + errorSub?.unsubscribe(); }); } diff --git a/ui/src/app/data-explorer/data-explorer.module.ts b/ui/src/app/data-explorer/data-explorer.module.ts index 6d31754b25..44b8ab9baf 100644 --- a/ui/src/app/data-explorer/data-explorer.module.ts +++ b/ui/src/app/data-explorer/data-explorer.module.ts @@ -37,7 +37,7 @@ import { ColorPickerModule } from 'ngx-color-picker'; import { PlatformServicesModule } from '@streampipes/platform-services'; import { CoreUiModule } from '../core-ui/core-ui.module'; import { DataExplorerDashboardGridComponent } from './components/widget-view/grid-view/data-explorer-dashboard-grid.component'; -import { DataExplorerDashboardOverviewComponent } from './components/overview/data-explorer-dashboard-overview.component'; +import { DataExplorerOverviewComponent } from './components/overview/data-explorer-overview.component'; import { DataExplorerDashboardPanelComponent } from './components/panel/data-explorer-dashboard-panel.component'; import { TimeRangeSelectorComponent } from './components/time-selector/timeRangeSelector.component'; import { DataExplorerDashboardWidgetComponent } from './components/widget/data-explorer-dashboard-widget.component'; @@ -113,6 +113,10 @@ import { MatTooltipModule } from '@angular/material/tooltip'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { SpImageContainerComponent } from './components/widgets/image/image-container/image-container.component'; +import { SpDataExplorerDataViewOverviewComponent } from './components/overview/data-explorer-data-view-overview/data-explorer-data-view-overview.component'; +import { SpDataExplorerDashboardOverviewComponent } from './components/overview/data-explorer-dashboard-overview/data-explorer-dashboard-overview.component'; +import { DataExplorerDataViewComponent } from './components/data-view/data-explorer-data-view.component'; +import { DataExplorerDataViewToolbarComponent } from './components/data-view/data-view-toolbar/data-explorer-data-view-toolbar.component'; @NgModule({ imports: [ @@ -167,15 +171,23 @@ import { SpImageContainerComponent } from './components/widgets/image/image-cont children: [ { path: '', - component: DataExplorerDashboardOverviewComponent, + component: DataExplorerOverviewComponent, }, { - path: ':id', + path: 'data-view', + component: DataExplorerDataViewComponent, + }, + { + path: 'data-view/:id', + component: DataExplorerDataViewComponent, + }, + { + path: 'dashboard/:id', component: DataExplorerDashboardPanelComponent, canDeactivate: [DataExplorerPanelCanDeactivateGuard], }, { - path: ':id/:startTime/:endTime', + path: 'dashboard/:id/:startTime/:endTime', component: DataExplorerDashboardPanelComponent, canDeactivate: [DataExplorerPanelCanDeactivateGuard], }, @@ -186,7 +198,7 @@ import { SpImageContainerComponent } from './components/widgets/image/image-cont declarations: [ AggregateConfigurationComponent, DataExplorerDashboardGridComponent, - DataExplorerDashboardOverviewComponent, + DataExplorerOverviewComponent, DataExplorerDashboardPanelComponent, DataExplorerDashboardSlideViewComponent, DataExplorerDashboardWidgetComponent, @@ -194,6 +206,8 @@ import { SpImageContainerComponent } from './components/widgets/image/image-cont DataExplorerEditDataViewDialogComponent, DataExplorerWidgetAppearanceSettingsComponent, DataExplorerWidgetDataSettingsComponent, + DataExplorerDataViewComponent, + DataExplorerDataViewToolbarComponent, CorrelationWidgetConfigComponent, FieldSelectionPanelComponent, FieldSelectionComponent, @@ -222,6 +236,8 @@ import { SpImageContainerComponent } from './components/widgets/image/image-cont DataExplorerVisualisationSettingsComponent, WidgetDirective, TooMuchDataComponent, + SpDataExplorerDataViewOverviewComponent, + SpDataExplorerDashboardOverviewComponent, SpEchartsWidgetComponent, SpValueHeatmapWidgetConfigComponent, SpHistogramChartWidgetConfigComponent, diff --git a/ui/src/app/data-explorer/services/data-explorer-dashboard.service.ts b/ui/src/app/data-explorer/services/data-explorer-dashboard.service.ts new file mode 100644 index 0000000000..aefed314e1 --- /dev/null +++ b/ui/src/app/data-explorer/services/data-explorer-dashboard.service.ts @@ -0,0 +1,42 @@ +/* + * 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 { Injectable } from '@angular/core'; +import { Dashboard } from '@streampipes/platform-services'; +import { DataExplorerEditDataViewDialogComponent } from '../dialogs/edit-dashboard/data-explorer-edit-data-view-dialog.component'; +import { DialogService, PanelType } from '@streampipes/shared-ui'; + +@Injectable({ providedIn: 'root' }) +export class DataExplorerDashboardService { + constructor(private dialogService: DialogService) {} + + openDataViewModificationDialog(createMode: boolean, dashboard: Dashboard) { + return this.dialogService.open( + DataExplorerEditDataViewDialogComponent, + { + panelType: PanelType.STANDARD_PANEL, + title: createMode ? 'New Data View' : 'Edit Data View', + width: '70vw', + data: { + createMode: createMode, + dashboard: dashboard, + }, + }, + ); + } +}