This is an automated email from the ASF dual-hosted git repository. riemer pushed a commit to branch minor-layout-harmonization in repository https://gitbox.apache.org/repos/asf/streampipes.git
commit f5bf14ce0b6255829c315e87dccc4af0453ee8a4 Author: Dominik Riemer <[email protected]> AuthorDate: Mon Nov 3 11:14:24 2025 +0100 Add global asset filtering --- ui/deployment/base-navigation.component.mst | 30 ++-- ui/deployment/categories.yml | 2 +- ui/deployment/modules.yml | 14 +- ...asset-browser-filter-asset-model.component.html | 45 ++++++ ...sset-browser-filter-asset-model.component.scss} | 0 .../asset-browser-filter-asset-model.component.ts | 47 +++++++ .../asset-browser-filter-labels.component.html | 81 ++++------- .../asset-browser-filter-labels.component.ts | 8 ++ .../asset-browser-filter.component.html | 5 + .../asset-browser-filter.component.ts | 7 +- .../asset-browser-toolbar.component.html | 17 ++- .../asset-browser-toolbar.component.scss} | 28 ++-- .../asset-browser-toolbar.component.ts | 70 +++------ .../asset-browser/asset-browser.model.ts | 11 ++ .../asset-browser/asset-browser.service.ts | 156 ++++++++++++++++----- .../basic-nav-tabs/basic-nav-tabs.component.html | 21 ++- .../basic-nav-tabs/basic-nav-tabs.component.scss | 8 +- .../shared-ui/src/lib/shared-ui.module.ts | 2 + .../existing-adapters.component.html | 8 +- .../existing-adapters.component.ts | 58 ++++---- .../app/core/collapse.service.ts} | 32 ++--- .../components/breadcrumb/breadcrumb.component.ts | 28 ++-- .../core/components/iconbar/iconbar.component.html | 83 +++-------- .../core/components/iconbar/iconbar.component.scss | 1 - .../streampipes/streampipes.component.html | 27 +--- .../streampipes/streampipes.component.scss | 1 - .../streampipes/streampipes.component.ts | 25 +++- .../core/components/toolbar/toolbar.component.html | 1 + .../core/components/toolbar/toolbar.component.ts | 3 + ui/src/app/core/core.module.ts | 2 + ui/src/app/core/navigation.backup | 96 ++++++------- .../dashboard-overview-table.component.html | 6 +- .../dashboard-overview-table.component.ts | 56 +++++--- .../overview/dashboard-overview.component.html | 3 +- .../overview/dashboard-overview.component.ts | 17 +-- .../data-explorer-overview-table.component.ts | 52 ++++--- .../overview/data-explorer-overview.component.html | 3 +- .../overview/data-explorer-overview.component.ts | 32 +++-- .../overview/data-explorer-overview.directive.ts | 59 -------- .../components/auth-box/auth-box.component.scss | 4 +- ui/src/app/pipelines/pipelines.component.ts | 69 ++++----- ui/src/scss/sp/buttons-mat3.scss | 14 +- 42 files changed, 659 insertions(+), 573 deletions(-) diff --git a/ui/deployment/base-navigation.component.mst b/ui/deployment/base-navigation.component.mst index d7d25a1ab6..5f9acee025 100644 --- a/ui/deployment/base-navigation.component.mst +++ b/ui/deployment/base-navigation.component.mst @@ -23,7 +23,8 @@ import { AuthService } from '../../services/auth.service'; import { CurrentUserService } from '@streampipes/shared-ui'; import { AppConstants } from '../../services/app.constants'; import { UserPrivilege } from '../../_enums/user-privilege.enum'; -import { inject, signal } from '@angular/core'; +import { inject } from '@angular/core'; +import { CollapseService } from '../collapse.service'; export interface MenuItem { link: string; @@ -55,7 +56,6 @@ export abstract class BaseNavigationComponent { topItems: MenuItem[] = []; menuGroups: MenuGroup[] = []; notificationsVisible = false; - isCollapsed = signal<boolean>(this.loadCollapsed()); public categories: MenuCategory[] = [ {{#categoriesActive}} @@ -65,7 +65,7 @@ export abstract class BaseNavigationComponent { collapsed: {{{collapsed}}}, }, {{/categoriesActive}} - ] + ]; public menu = [ {{#modulesActive}} @@ -85,6 +85,9 @@ export abstract class BaseNavigationComponent { protected currentUserService = inject(CurrentUserService); protected router = inject(Router); protected appConstants = inject(AppConstants); + protected collapseService = inject(CollapseService); + + collapsed = this.collapseService.isCollapsed; onInit() { this.currentUserService.user$.subscribe(user => { @@ -142,23 +145,12 @@ export abstract class BaseNavigationComponent { return this.authService.isAnyAccessGranted(privileges); } - toggleMenubar(): void { - const next = !this.isCollapsed(); - this.isCollapsed.set(next); - localStorage.setItem('sp-menubar-collapsed', JSON.stringify(next)); - } - - toggleGroup(group: MenuGroup): void { - group.collapsed = !group.collapsed; - } + toggleGroup(group: MenuGroup): void { + group.collapsed = !group.collapsed; + } - private loadCollapsed(): boolean { - try { - const v = localStorage.getItem('sp-menubar-collapsed'); - return v ? JSON.parse(v) : false; - } catch { - return false; + toggleMenubar(): void { + this.collapseService.toggleMenubar(); } - } } diff --git a/ui/deployment/categories.yml b/ui/deployment/categories.yml index d6c12ef162..2653a3ac13 100644 --- a/ui/deployment/categories.yml +++ b/ui/deployment/categories.yml @@ -9,4 +9,4 @@ collapsed: false - id: management title: Management - collapsed: true + collapsed: false diff --git a/ui/deployment/modules.yml b/ui/deployment/modules.yml index f11fd16949..8c30a2c1d3 100644 --- a/ui/deployment/modules.yml +++ b/ui/deployment/modules.yml @@ -21,7 +21,7 @@ spAssets: path: './assets/assets.module' link: 'assets' url: '/editor/:pipeline' - title: 'Asset Management' + title: 'Assets' description: 'Manage assets which help assigning pipelines, dashboards and adapters to real-world objects such as machines and plants.' icon: 'precision_manufacturing' privileges: '[UserPrivilege.PRIVILEGE_READ_ASSETS, UserPrivilege.PRIVILEGE_WRITE_ASSETS]' @@ -50,7 +50,7 @@ spConnect: path: './connect/connect.module' link: 'connect' url: '/connect' - title: 'Connect' + title: 'Connectors' description: 'StreamPipes Connect lets you easily connect new data sources for a variety of data formats and protocols.' icon: 'power' privileges: '[UserPrivilege.PRIVILEGE_READ_ADAPTER, UserPrivilege.PRIVILEGE_WRITE_ADAPTER]' @@ -96,8 +96,8 @@ spConfiguration: path: './configuration/configuration.module' link: 'configuration' url: '/configuration' - title: 'Configuration' - description: 'In the configuration module, basic StreamPipes settings and services can be configured.' + title: 'Settings' + description: 'In the settings module, basic StreamPipes settings and services can be configured.' icon: 'settings' pageNames: 'PageName.SETTINGS' privileges: '[UserPrivilege.PRIVILEGE_WRITE_ASSETS, UserPrivilege.PRIVILEGE_WRITE_LABELS, UserPrivilege.PRIVILEGE_WRITE_FILES]' @@ -111,7 +111,7 @@ spDashboard: path: './dashboard/dashboard.module' link: 'dashboard' url: '/dashboard' - title: 'Dashboard' + title: 'Dashboards' description: 'The live dashboard visualizes data in real-time.' icon: 'dashboard' pageNames: 'PageName.DASHBOARD' @@ -134,8 +134,8 @@ spDataExplorer: path: './data-explorer/data-explorer.module' link: 'dataexplorer' url: '/dataexplorer' - title: 'Data Explorer' - description: 'The data explorer lets you visually inspect historical data from your connected sources.' + title: 'Charts' + description: 'The chart view lets you visually inspect historical data from your connected sources.' icon: 'query_stats' pageNames: 'PageName.DATA_EXPLORER' privileges: '[UserPrivilege.PRIVILEGE_READ_DATA_EXPLORER_VIEW, UserPrivilege.PRIVILEGE_WRITE_DATA_EXPLORER_VIEW]' diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-asset-model/asset-browser-filter-asset-model.component.html b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-asset-model/asset-browser-filter-asset-model.component.html index e69de29bb2..e3e48e17cb 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-asset-model/asset-browser-filter-asset-model.component.html +++ b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-asset-model/asset-browser-filter-asset-model.component.html @@ -0,0 +1,45 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one or more + ~ contributor license agreements. See the NOTICE file distributed with + ~ this work for additional information regarding copyright ownership. + ~ The ASF licenses this file to You under the Apache License, Version 2.0 + ~ (the "License"); you may not use this file except in compliance with + ~ the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ + --> + +<sp-asset-browser-filter-outer + [selectedItems]="activeFilters.selectedAssetModels" + [allItems]="assets" + [title]="'Assets' | translate" + (selectAllEmitter)="selectAll()" + (deselectAllEmitter)="selectNone()" +> + <mat-form-field + appearance="outline" + class="form-field-small filter-selection" + color="accent" + > + <mat-select + multiple + [compareWith]="compare" + [(ngModel)]="activeFilters.selectedAssetModels" + > + <mat-option + *ngFor="let asset of assets; let i = index" + [value]="asset" + class="smaller-font-size" + > + {{ asset.assetName }} + </mat-option> + </mat-select> + </mat-form-field> +</sp-asset-browser-filter-outer> diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-asset-model/asset-browser-filter-asset-model.component.html b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-asset-model/asset-browser-filter-asset-model.component.scss similarity index 100% copy from ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-asset-model/asset-browser-filter-asset-model.component.html copy to ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-asset-model/asset-browser-filter-asset-model.component.scss diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-asset-model/asset-browser-filter-asset-model.component.ts b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-asset-model/asset-browser-filter-asset-model.component.ts index e69de29bb2..b9f6406122 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-asset-model/asset-browser-filter-asset-model.component.ts +++ b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-asset-model/asset-browser-filter-asset-model.component.ts @@ -0,0 +1,47 @@ +/* + * 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, Input } from '@angular/core'; +import { Isa95TypeDesc, SpAsset } from '@streampipes/platform-services'; +import { AssetFilter } from '../../../asset-browser.model'; + +@Component({ + selector: 'sp-asset-browser-filter-asset-model', + templateUrl: 'asset-browser-filter-asset-model.component.html', + styleUrls: ['../asset-browser-filter.component.scss'], + standalone: false, +}) +export class AssetBrowserFilterAssetModelComponent { + @Input() + assets: SpAsset[]; + + @Input() + activeFilters: AssetFilter; + + compare(o1: Isa95TypeDesc, o2: Isa95TypeDesc): boolean { + return o1.type === o2.type; + } + + selectAll(): void { + this.activeFilters.selectedAssetModels = [...this.assets]; + } + + selectNone(): void { + this.activeFilters.selectedAssetModels = []; + } +} diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-labels/asset-browser-filter-labels.component.html b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-labels/asset-browser-filter-labels.component.html index 71e18135d7..d28b3b60e1 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-labels/asset-browser-filter-labels.component.html +++ b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-labels/asset-browser-filter-labels.component.html @@ -16,63 +16,34 @@ ~ --> -<div - fxLayout="row" - fxFlex="100" - fxLayoutAlign="start center" - class="filter-section" +<sp-asset-browser-filter-outer + [selectedItems]="activeFilters.selectedLabels" + [allItems]="labels" + [title]="'Labels' | translate" + (selectAllEmitter)="selectAll()" + (deselectAllEmitter)="selectNone()" > - <div - fxFlex="50" - fxLayout="row" - fxLayoutAlign="start center" - fxLayoutGap="10px" + <mat-form-field + appearance="outline" + class="form-field-small filter-selection" + color="accent" > - <div - class="filter-header" - fxLayout="row" - fxLayoutAlign="start center" - fxFlex="100" + <mat-select + multiple + [compareWith]="compare" + [(ngModel)]="activeFilters.selectedLabels" > - <div - fxLayoutAlign="center center" - class="active-filters mr-5" - *ngIf="activeFilters.selectedLabels.length > 0" + <mat-option + *ngFor="let label of labels; let i = index" + [value]="label" > - <i class="material-icons fs-icon">filter_alt</i> - </div> - <span>{{ 'Labels' | translate }}</span> - </div> - </div> - <div - fxFlex="50" - class="filter-selection" - fxLayout="column" - (click)="$event.stopPropagation()" - fxLayoutAlign="end center" - > - <mat-form-field - appearance="outline" - class="form-field-small filter-selection" - color="accent" - > - <mat-select - multiple - [compareWith]="compare" - [(ngModel)]="activeFilters.selectedLabels" - > - <mat-option - *ngFor="let label of labels; let i = index" - [value]="label" + <sp-label + [labelText]="label.label" + size="small" + [labelBackground]="label.color" > - <sp-label - [labelText]="label.label" - size="small" - [labelBackground]="label.color" - > - </sp-label> - </mat-option> - </mat-select> - </mat-form-field> - </div> -</div> + </sp-label> + </mat-option> + </mat-select> + </mat-form-field> +</sp-asset-browser-filter-outer> diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-labels/asset-browser-filter-labels.component.ts b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-labels/asset-browser-filter-labels.component.ts index efab492b15..e5fb6ff484 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-labels/asset-browser-filter-labels.component.ts +++ b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-labels/asset-browser-filter-labels.component.ts @@ -33,6 +33,14 @@ export class AssetBrowserFilterLabelsComponent { @Input() activeFilters: AssetFilter; + selectAll(): void { + this.activeFilters.selectedLabels = [...this.labels]; + } + + selectNone(): void { + this.activeFilters.selectedLabels = []; + } + compare(o1: SpLabel, o2: SpLabel): boolean { return o1._id === o2._id; } diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter.component.html b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter.component.html index fbe541a7b7..dcbaa8d4dd 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter.component.html +++ b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter.component.html @@ -22,6 +22,11 @@ *ngIf="activeFilters" > <div fxLayout="column" fxLayoutGap="10px"> + <sp-asset-browser-filter-asset-model + [assets]="assetBrowserData.assets" + [activeFilters]="activeFilters" + > + </sp-asset-browser-filter-asset-model> <sp-asset-browser-filter-type [activeFilters]="activeFilters"> </sp-asset-browser-filter-type> <sp-asset-browser-filter-sites diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter.component.ts b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter.component.ts index e20f5b7fdd..2178dba3e4 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter.component.ts +++ b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter.component.ts @@ -37,9 +37,10 @@ import { SpAssetBrowserService } from '../../asset-browser.service'; export class AssetBrowserFilterComponent implements OnInit, OnDestroy { @Input() assetBrowserData: AssetBrowserData; + activeFilters: AssetFilter; - filterSub: Subscription; + filter$: Subscription; @Output() closeMenu: EventEmitter<void> = new EventEmitter(); @@ -47,7 +48,7 @@ export class AssetBrowserFilterComponent implements OnInit, OnDestroy { constructor(private assetBrowserService: SpAssetBrowserService) {} ngOnInit() { - this.filterSub = this.assetBrowserService.filter$.subscribe( + this.filter$ = this.assetBrowserService.filter$.subscribe( activeFilters => { this.activeFilters = activeFilters; }, @@ -65,6 +66,6 @@ export class AssetBrowserFilterComponent implements OnInit, OnDestroy { } ngOnDestroy() { - this.filterSub?.unsubscribe(); + this.filter$?.unsubscribe(); } } diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-toolbar.component.html b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-toolbar.component.html index cbf4d703c8..d56c601f8f 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-toolbar.component.html +++ b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-toolbar.component.html @@ -19,14 +19,25 @@ @if (showAssetBrowser) { <div fxLayoutAlign="end center"> <button - mat-icon-button - color="accent" + mat-flat-button + class="btn-secondary-outline btn-filter" + color="secondary" [matMenuTriggerFor]="menu" #menuTrigger="matMenuTrigger" [matTooltip]="'Filter assets' | translate" + [disabled]="filterDisabled" (menuClosed)="menuTrigger.closeMenu()" > - <mat-icon>filter_alt</mat-icon> + <mat-icon [class.active-filters]="filterActive" + >filter_alt</mat-icon + > + @if (filterActive) { + <span>{{ + selectedAssetCount + ' of ' + allAssetCount + ' ' + 'assets' + }}</span> + } @else { + <span>{{ 'All assets' | translate }}</span> + } </button> <mat-menu #menu="matMenu" fxFlex="100" class="large-menu"> <sp-asset-browser-filter diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser.model.ts b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-toolbar.component.scss similarity index 66% copy from ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser.model.ts copy to ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-toolbar.component.scss index 4523873e0e..a829ff7584 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser.model.ts +++ b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-toolbar.component.scss @@ -1,4 +1,4 @@ -/* +/*! * 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. @@ -16,23 +16,15 @@ * */ -import { - AssetLink, - AssetSiteDesc, - Isa95TypeDesc, - SpAsset, - SpLabel, -} from '@streampipes/platform-services'; - -export interface AssetBrowserData { - assets: SpAsset[]; - assetLinks: AssetLink[]; - sites: AssetSiteDesc[]; - labels: SpLabel[]; +.active-filters { + border-radius: 50%; + background: var(--color-secondary); + color: var(--color-bg-0); + padding: 2px; + line-height: 20px; + font-weight: bold; } -export interface AssetFilter { - selectedSites: AssetSiteDesc[]; - selectedTypes: Isa95TypeDesc[]; - selectedLabels: SpLabel[]; +.btn-filter { + font-weight: 700; } diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-toolbar.component.ts b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-toolbar.component.ts index 9390c2ade1..6c318c82e7 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-toolbar.component.ts +++ b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-toolbar.component.ts @@ -16,60 +16,31 @@ * */ -import { - Component, - EventEmitter, - inject, - Input, - OnInit, - Output, - ViewChild, -} from '@angular/core'; +import { Component, inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { AssetBrowserData } from '../asset-browser.model'; import { MatMenuTrigger } from '@angular/material/menu'; import { Subscription } from 'rxjs'; -import { TranslateService } from '@ngx-translate/core'; import { CurrentUserService } from '../../../services/current-user.service'; import { SpAssetBrowserService } from '../asset-browser.service'; -import { SpAsset } from '@streampipes/platform-services'; @Component({ selector: 'sp-asset-browser-toolbar', templateUrl: 'asset-browser-toolbar.component.html', + styleUrls: ['asset-browser-toolbar.component.scss'], standalone: false, }) -export class AssetBrowserToolbarComponent implements OnInit { +export class AssetBrowserToolbarComponent implements OnInit, OnDestroy { private currentUserService = inject(CurrentUserService); private assetBrowserService = inject(SpAssetBrowserService); - private translateService = inject(TranslateService); - - @Input() - allResourcesAlias = this.translateService.instant('Resources'); - - @Input() - browserWidth = 20; - - @Input() - filteredAssetLinkType: string; - - @Input() - resourceCount = 0; - - @Input() - assetSelectionMode = false; - - @Output() - filterIdsEmitter: EventEmitter<Set<string>> = new EventEmitter< - Set<string> - >(); - - @Output() - selectedAssetIdEmitter: EventEmitter<string> = new EventEmitter<string>(); assetBrowserData: AssetBrowserData; showAssetBrowser = false; - assetBrowserDataSub: Subscription; + assetBrowserData$: Subscription; + filterActive = false; + filterDisabled = false; + selectedAssetCount = 0; + allAssetCount = 0; @ViewChild('menuTrigger') menu: MatMenuTrigger; @@ -79,26 +50,23 @@ export class AssetBrowserToolbarComponent implements OnInit { 'PRIVILEGE_WRITE_ASSETS', ]); if (this.showAssetBrowser) { - this.assetBrowserDataSub = + this.assetBrowserData$ = this.assetBrowserService.assetData$.subscribe(assetData => { this.assetBrowserData = assetData; - console.log(assetData); }); + this.assetBrowserService.currentAssetFilter$.subscribe( + assetFilter => { + this.filterActive = assetFilter.filterActive; + this.filterDisabled = assetFilter.filterDisabled; + this.allAssetCount = assetFilter.allAssetCount; + this.selectedAssetCount = assetFilter.selectedAssetCount; + }, + ); } } - applyAssetFilter(asset: SpAsset) { - const elementIds = new Set<string>(); - if (asset.assetId !== '_root') { - this.assetBrowserService.collectElementIds( - asset, - this.filteredAssetLinkType, - elementIds, - ); - this.filterIdsEmitter.emit(elementIds); - } - this.filterIdsEmitter.emit(elementIds); - this.selectedAssetIdEmitter.emit(asset.assetId); + ngOnDestroy() { + this.assetBrowserData$?.unsubscribe(); } closeMenu(): void { diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser.model.ts b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser.model.ts index 4523873e0e..18005f0c7a 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser.model.ts +++ b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser.model.ts @@ -35,4 +35,15 @@ export interface AssetFilter { selectedSites: AssetSiteDesc[]; selectedTypes: Isa95TypeDesc[]; selectedLabels: SpLabel[]; + selectedAssetModels: SpAsset[]; +} + +export interface FilterResult { + filterActive: boolean; + filterDisabled: boolean; + activeElementIds?: Set<string>; + currentAssetLink?: string; + selectedAssets?: SpAsset[]; + allAssetCount?: number; + selectedAssetCount?: number; } diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser.service.ts b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser.service.ts index 3b58e3d3e4..8732e87579 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser.service.ts +++ b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser.service.ts @@ -27,15 +27,24 @@ import { SpAsset, SpLabel, } from '@streampipes/platform-services'; -import { AssetBrowserData, AssetFilter } from './asset-browser.model'; +import { + AssetBrowserData, + AssetFilter, + FilterResult, +} from './asset-browser.model'; @Injectable({ providedIn: 'root' }) export class SpAssetBrowserService { assetData$ = new BehaviorSubject<AssetBrowserData>(undefined); expanded$ = new BehaviorSubject<boolean>(true); filter$ = new BehaviorSubject<AssetFilter>(undefined); + currentAssetFilter$ = new BehaviorSubject<FilterResult>({ + filterActive: false, + filterDisabled: true, + }); loadedAssetData: AssetBrowserData; + activeAssetLink = undefined; constructor( private genericStorageService: GenericStorageService, @@ -44,20 +53,26 @@ export class SpAssetBrowserService { this.loadAssetData(); } + applyAssetLinkType(assetLink: string) { + this.activeAssetLink = assetLink; + if (this.filter$.getValue() !== undefined) { + this.applyFilters(this.filter$.getValue()); + } + } + loadAssetData(): void { - const assetsReq = this.genericStorageService.getAllDocuments( + const assets$ = this.genericStorageService.getAllDocuments( AssetConstants.ASSET_APP_DOC_NAME, ); - const assetLinksReq = this.genericStorageService.getAllDocuments( + const assetLinks$ = this.genericStorageService.getAllDocuments( AssetConstants.ASSET_LINK_TYPES_DOC_NAME, ); - const sitesReq = this.genericStorageService.getAllDocuments( + const sites$ = this.genericStorageService.getAllDocuments( AssetConstants.ASSET_SITES_APP_DOC_NAME, ); - const labelsReq = - this.genericStorageService.getAllDocuments('sp-labels'); + const labels$ = this.genericStorageService.getAllDocuments('sp-labels'); - zip([assetsReq, assetLinksReq, sitesReq, labelsReq]).subscribe(res => { + zip([assets$, assetLinks$, sites$, labels$]).subscribe(res => { this.loadedAssetData = { assets: res[0].sort((a, b) => a.assetName.localeCompare(b.assetName), @@ -72,15 +87,22 @@ export class SpAssetBrowserService { } private reloadFilters(): void { - const data = this.assetData$.getValue(); - const filters: AssetFilter = { - selectedSites: [...data.sites].sort((a, b) => - a.label.localeCompare(b.label), - ), - selectedLabels: [], - selectedTypes: [...this.typeService.getTypeDescriptions()], - }; - this.filter$.next(filters); + if ( + this.activeAssetLink !== undefined && + this.assetData$.getValue() !== undefined + ) { + const data = this.assetData$.getValue(); + const filters: AssetFilter = { + selectedSites: [...data.sites].sort((a, b) => + a.label.localeCompare(b.label), + ), + selectedLabels: [...data.labels], + selectedTypes: [...this.typeService.getTypeDescriptions()], + selectedAssetModels: [...data.assets], + }; + this.filter$.next(filters); + this.applyFilters(filters); + } } resetFilters(): void { @@ -92,29 +114,78 @@ export class SpAssetBrowserService { const clonedLoadedAssetData = JSON.parse( JSON.stringify(this.loadedAssetData), ) as AssetBrowserData; - const filteredAssets = clonedLoadedAssetData.assets - .filter( - a => - this.allSelected( - this.typeService.getTypeDescriptions(), - filter.selectedTypes, - ) || this.filterType(a, filter.selectedTypes), - ) - .filter( - a => - this.allSelected( - clonedLoadedAssetData.sites, - filter.selectedSites, - ) || this.filterSites(a, filter.selectedSites), - ) - .filter(a => this.filterLabels(a, filter.selectedLabels)); - - this.assetData$.next({ - assets: filteredAssets, - assetLinks: clonedLoadedAssetData.assetLinks, - sites: clonedLoadedAssetData.sites, - labels: clonedLoadedAssetData.labels, + const allAssetsSelected = this.allSelected( + clonedLoadedAssetData.assets, + filter.selectedAssetModels, + ); + const allTypesSelected = this.allSelected( + this.typeService.getTypeDescriptions(), + filter.selectedTypes, + ); + const allSitesSelected = this.allSelected( + clonedLoadedAssetData.sites, + filter.selectedSites, + ); + const allLabelsSelected = this.allSelected( + clonedLoadedAssetData.labels, + filter.selectedLabels, + ); + + if ( + allAssetsSelected && + allTypesSelected && + allSitesSelected && + allLabelsSelected + ) { + this.currentAssetFilter$.next({ + filterActive: false, + filterDisabled: false, + activeElementIds: new Set<string>(), + selectedAssets: clonedLoadedAssetData.assets, + currentAssetLink: this.activeAssetLink, + }); + } else { + const filteredAssets = clonedLoadedAssetData.assets + .filter( + a => + allAssetsSelected || + this.filterAssetModel(a, filter.selectedAssetModels), + ) + .filter( + a => + allTypesSelected || + this.filterType(a, filter.selectedTypes), + ) + .filter( + a => + allSitesSelected || + this.filterSites(a, filter.selectedSites), + ) + .filter( + a => + allLabelsSelected || + this.filterLabels(a, filter.selectedLabels), + ); + + this.applyAssetFilter(filteredAssets); + } + } + + applyAssetFilter(filteredAssets: SpAsset[]) { + const elementIds = new Set<string>(); + filteredAssets.forEach(asset => { + this.collectElementIds(asset, this.activeAssetLink, elementIds); }); + const currentFilter = { + filterActive: true, + activeElementIds: elementIds, + selectedAssets: filteredAssets, + currentAssetLink: this.activeAssetLink, + allAssetCount: this.assetData$.getValue().assets.length, + selectedAssetCount: filteredAssets.length, + filterDisabled: false, + }; + this.currentAssetFilter$.next(currentFilter); } private allSelected(items: any[], selected: any[]) { @@ -139,6 +210,15 @@ export class SpAssetBrowserService { return matchesSelf; } + private filterAssetModel( + asset: SpAsset, + selectedAssets: SpAsset[], + ): boolean { + return ( + selectedAssets.find(a => a.assetId === asset.assetId) !== undefined + ); + } + private filterSites( asset: SpAsset, selectedSites: AssetSiteDesc[], diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.html b/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.html index cfd55f4941..89a0bce84c 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.html +++ b/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.html @@ -37,7 +37,13 @@ <mat-icon>arrow_back</mat-icon> </button> </div> - <nav mat-tab-nav-bar mat-stretch-tabs="false" color="accent"> + <nav + mat-tab-nav-bar + [mat-stretch-tabs]="true" + color="accent" + [tabPanel]="tabPanel" + class="w-100" + > <a mat-tab-link *ngFor="let item of spNavigationItems" @@ -49,12 +55,13 @@ </a> </nav> <span fxFlex></span> - <ng-content - select="[nav]" - fxFlex="100" - fxLayout="row" - fxLayoutAlign="end center" - ></ng-content> + <mat-tab-nav-panel #tabPanel></mat-tab-nav-panel> + <!-- <ng-content--> + <!-- select="[nav]"--> + <!-- fxFlex="100"--> + <!-- fxLayout="row"--> + <!-- fxLayoutAlign="end center"--> + <!-- ></ng-content>--> </div> </div> diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.scss b/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.scss index 004d4e0ae0..3dadfe1a3b 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.scss +++ b/ui/projects/streampipes/shared-ui/src/lib/components/basic-nav-tabs/basic-nav-tabs.component.scss @@ -17,11 +17,11 @@ */ .sp-bg-lightgray { - background-color: var(--color-bg-1); + background-color: var(--color-bg-0); } .sp-tab-bg { - background-color: var(--color-bg-1); + background-color: var(--color-bg-0); } .page-container { @@ -47,3 +47,7 @@ .pr-5 { padding-right: 5px; } + +.mat-mdc-tab-header { + flex-shrink: 1; +} diff --git a/ui/projects/streampipes/shared-ui/src/lib/shared-ui.module.ts b/ui/projects/streampipes/shared-ui/src/lib/shared-ui.module.ts index 61f40fc93c..e82ad6620b 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/shared-ui.module.ts +++ b/ui/projects/streampipes/shared-ui/src/lib/shared-ui.module.ts @@ -100,6 +100,7 @@ import { SortByRuntimeNamePipe } from './pipes/sort-by-runtime-name.pipe'; import { DragDropModule } from '@angular/cdk/drag-drop'; import { SpTableActionsDirective } from './components/sp-table/sp-table-actions.directive'; import { AssetLinkConfigurationComponent } from './components/asset-link-configuration/asset-link-configuration.component'; +import { AssetBrowserFilterAssetModelComponent } from './components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-asset-model/asset-browser-filter-asset-model.component'; @NgModule({ declarations: [ @@ -153,6 +154,7 @@ import { AssetLinkConfigurationComponent } from './components/asset-link-configu SortByRuntimeNamePipe, SpTableActionsDirective, AssetLinkConfigurationComponent, + AssetBrowserFilterAssetModelComponent, ], imports: [ CommonModule, diff --git a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html index a4b1d5caec..9ca03d6a26 100644 --- a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html +++ b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html @@ -66,13 +66,6 @@ fxLayoutAlign="end center" class="page-container-nav" > - <sp-asset-browser-toolbar - filteredAssetLinkType="adapter" - [allResourcesAlias]="'Adapters' | translate" - [resourceCount]="existingAdapters.length" - (filterIdsEmitter)="applyAdapterFilters($event)" - > - </sp-asset-browser-toolbar> <button mat-icon-button color="accent" @@ -123,6 +116,7 @@ <ng-container matColumnDef="start"> <th mat-header-cell *matHeaderCellDef>Start</th> <td + (click)="$event.stopPropagation()" mat-cell *matCellDef="let adapter" data-cy="adapters-table" diff --git a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.ts b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.ts index 8758e518fd..6005432dfe 100644 --- a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.ts +++ b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.ts @@ -16,7 +16,7 @@ * */ -import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { Component, inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { AdapterDescription, AdapterMonitoringService, @@ -31,6 +31,7 @@ import { DialogRef, DialogService, PanelType, + SpAssetBrowserService, SpBreadcrumbService, SpExceptionDetailsDialogComponent, } from '@streampipes/shared-ui'; @@ -81,40 +82,46 @@ export class ExistingAdaptersComponent implements OnInit, OnDestroy { adapterMetrics: Record<string, SpMetricsEntry> = {}; tutorialActive = false; - userSubscription: Subscription; - tutorialActiveSubscription: Subscription; + assetFilter$: Subscription; + user$: Subscription; + tutorial$: Subscription; currentFilterIds: Set<string> = new Set<string>(); startAdapterErrorText = 'Could not start adapter'; stopAdapterErrorText = 'Could not stop adapter'; - constructor( - private adapterService: AdapterService, - private dialogService: DialogService, - private currentUserService: CurrentUserService, - private router: Router, - private pipelineElementAssetService: PipelineElementAssetService, - private adapterFilter: AdapterFilterPipe, - private breadcrumbService: SpBreadcrumbService, - private adapterMonitoringService: AdapterMonitoringService, - private shepherdService: ShepherdService, - private translate: TranslateService, - ) {} + private adapterService = inject(AdapterService); + private dialogService = inject(DialogService); + private currentUserService = inject(CurrentUserService); + private router = inject(Router); + private pipelineElementAssetService = inject(PipelineElementAssetService); + private adapterFilter = inject(AdapterFilterPipe); + private breadcrumbService = inject(SpBreadcrumbService); + private shepherdService = inject(ShepherdService); + private translate = inject(TranslateService); + private adapterMonitoringService = inject(AdapterMonitoringService); + private assetFilterService = inject(SpAssetBrowserService); ngOnInit(): void { + this.assetFilterService.applyAssetLinkType('adapter'); + this.assetFilter$ = + this.assetFilterService.currentAssetFilter$.subscribe(filter => { + this.currentFilterIds = + filter?.activeElementIds || new Set<string>(); + this.applyAdapterFilters(this.currentFilterIds); + }); this.breadcrumbService.updateBreadcrumb( this.breadcrumbService.getRootLink(SpConnectRoutes.BASE), ); - this.userSubscription = this.currentUserService.user$.subscribe( - user => { - this.isAdmin = user.roles.indexOf(UserRole.ROLE_ADMIN) > -1; - this.getAdaptersRunning(); + this.user$ = this.currentUserService.user$.subscribe(user => { + this.isAdmin = user.roles.indexOf(UserRole.ROLE_ADMIN) > -1; + this.getAdaptersRunning(); + }); + this.tutorial$ = this.shepherdService.tutorialActive$.subscribe( + tutorialActive => { + this.tutorialActive = tutorialActive; }, ); - this.tutorialActiveSubscription = - this.shepherdService.tutorialActive$.subscribe(tutorialActive => { - this.tutorialActive = tutorialActive; - }); } startAdapter(adapter: AdapterDescription) { @@ -323,7 +330,8 @@ export class ExistingAdaptersComponent implements OnInit, OnDestroy { } ngOnDestroy() { - this.userSubscription?.unsubscribe(); - this.tutorialActiveSubscription?.unsubscribe(); + this.user$?.unsubscribe(); + this.tutorial$?.unsubscribe(); + this.assetFilter$?.unsubscribe(); } } diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-labels/asset-browser-filter-labels.component.ts b/ui/src/app/core/collapse.service.ts similarity index 57% copy from ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-labels/asset-browser-filter-labels.component.ts copy to ui/src/app/core/collapse.service.ts index efab492b15..ce6674e77e 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-toolbar/asset-browser-filter/asset-browser-filter-labels/asset-browser-filter-labels.component.ts +++ b/ui/src/app/core/collapse.service.ts @@ -16,24 +16,24 @@ * */ -import { Component, Input } from '@angular/core'; -import { SpLabel } from '@streampipes/platform-services'; -import { AssetFilter } from '../../../asset-browser.model'; +import { Injectable, signal } from '@angular/core'; -@Component({ - selector: 'sp-asset-browser-filter-labels', - templateUrl: 'asset-browser-filter-labels.component.html', - styleUrls: ['../asset-browser-filter.component.scss'], - standalone: false, -}) -export class AssetBrowserFilterLabelsComponent { - @Input() - labels: SpLabel[] = []; +@Injectable({ providedIn: 'root' }) +export class CollapseService { + isCollapsed = signal<boolean>(this.loadCollapsed()); - @Input() - activeFilters: AssetFilter; + toggleMenubar(): void { + const next = !this.isCollapsed(); + this.isCollapsed.set(next); + localStorage.setItem('sp-menubar-collapsed', JSON.stringify(next)); + } - compare(o1: SpLabel, o2: SpLabel): boolean { - return o1._id === o2._id; + private loadCollapsed(): boolean { + try { + const v = localStorage.getItem('sp-menubar-collapsed'); + return v ? JSON.parse(v) : false; + } catch { + return false; + } } } diff --git a/ui/src/app/core/components/breadcrumb/breadcrumb.component.ts b/ui/src/app/core/components/breadcrumb/breadcrumb.component.ts index f2edeb801f..75c5751e61 100644 --- a/ui/src/app/core/components/breadcrumb/breadcrumb.component.ts +++ b/ui/src/app/core/components/breadcrumb/breadcrumb.component.ts @@ -16,9 +16,10 @@ * */ -import { Component, OnInit } from '@angular/core'; +import { Component, inject, OnDestroy, OnInit } from '@angular/core'; import { SpBreadcrumbItem, SpBreadcrumbService } from '@streampipes/shared-ui'; import { Router } from '@angular/router'; +import { Subscription } from 'rxjs'; @Component({ selector: 'sp-breadcrumb', @@ -26,23 +27,28 @@ import { Router } from '@angular/router'; styleUrls: ['./breadcrumb.component.scss'], standalone: false, }) -export class SpBreadcrumbComponent implements OnInit { +export class SpBreadcrumbComponent implements OnInit, OnDestroy { currentNavItems: SpBreadcrumbItem[] = []; - constructor( - private breadcrumbService: SpBreadcrumbService, - private router: Router, - ) {} + private breadcrumbService = inject(SpBreadcrumbService); + private router = inject(Router); + + private breadcrumb$: Subscription; ngOnInit(): void { - this.breadcrumbService.currentNavHierarchy$.subscribe( - currentNavItems => { - this.currentNavItems = currentNavItems; - }, - ); + this.breadcrumb$ = + this.breadcrumbService.currentNavHierarchy$.subscribe( + currentNavItems => { + this.currentNavItems = currentNavItems; + }, + ); } navigateTo(target: string[]) { this.router.navigate(target); } + + ngOnDestroy() { + this.breadcrumb$?.unsubscribe(); + } } diff --git a/ui/src/app/core/components/iconbar/iconbar.component.html b/ui/src/app/core/components/iconbar/iconbar.component.html index f7a3d2b104..cf3a10ceaa 100644 --- a/ui/src/app/core/components/iconbar/iconbar.component.html +++ b/ui/src/app/core/components/iconbar/iconbar.component.html @@ -16,14 +16,14 @@ ~ --> -<div class="menubar-root" [class.collapsed]="isCollapsed()" fxLayout="column"> +<div class="menubar-root" [class.collapsed]="collapsed()" fxLayout="column"> <!-- Header / brand --> <div class="menubar-header" fxLayout="row" fxLayoutAlign="space-between center" > - <div class="brand" [class.hidden]="isCollapsed()"> + <div class="brand" [class.hidden]="collapsed()"> <img alt="logo" src="assets/img/sp/logo-navigation.png" /> </div> @@ -31,10 +31,10 @@ type="button" class="collapse-btn" (click)="toggleMenubar()" - [attr.aria-label]="isCollapsed() ? 'Expand menu' : 'Collapse menu'" - [title]="isCollapsed() ? 'Expand' : 'Collapse'" + [attr.aria-label]="collapsed() ? 'Expand menu' : 'Collapse menu'" + [title]="collapsed() ? 'Expand' : 'Collapse'" > - <mat-icon class="collapse-icon" [class.rotated]="isCollapsed()" + <mat-icon class="collapse-icon" [class.rotated]="collapsed()" >chevron_left</mat-icon > </button> @@ -52,7 +52,7 @@ class="menu-item" [class.menu-item-selected]="item.link === activePage" (click)="go(item.link)" - [title]="isCollapsed() ? item.title : null" + [title]="collapsed() ? item.title : null" [attr.aria-current]=" item.link === activePage ? 'page' : null " @@ -60,7 +60,7 @@ <mat-icon class="item-icon">{{ item.icon }}</mat-icon> <span class="item-title text-ellipsis" - [class.hidden]="isCollapsed()" + [class.hidden]="collapsed()" >{{ item.title }}</span > </button> @@ -75,21 +75,21 @@ <!-- Group header --> <button type="button" - [class.hidden]="isCollapsed()" + [class.hidden]="collapsed()" class="menu-group-title" (click)="toggleGroup(group)" [attr.aria-expanded]="!group.collapsed" - [title]="isCollapsed() ? group.title : null" + [title]="collapsed() ? group.title : null" > <span class="group-title-text text-ellipsis" - [class.hidden]="isCollapsed()" + [class.hidden]="collapsed()" >{{ group.title }}</span > <mat-icon class="chevron" [class.rotate]="!group.collapsed" - [class.hidden]="isCollapsed()" + [class.hidden]="collapsed()" >expand_more</mat-icon > </button> @@ -98,9 +98,9 @@ <div class="menu-group-items" [style.maxHeight]=" - group.collapsed && !isCollapsed() ? '0px' : '500px' + group.collapsed && !collapsed() ? '0px' : '500px' " - [class.instant]="isCollapsed()" + [class.instant]="collapsed()" role="list" > @for (item of group.items; track item.title) { @@ -113,7 +113,7 @@ item.link === activePage " (click)="go(item.link)" - [title]="isCollapsed() ? item.title : null" + [title]="collapsed() ? item.title : null" [attr.aria-current]=" item.link === activePage ? 'page' : '' " @@ -123,7 +123,7 @@ }}</mat-icon> <span class="item-title text-ellipsis" - [class.hidden]="isCollapsed()" + [class.hidden]="collapsed()" >{{ item.title }}</span > </button> @@ -131,61 +131,10 @@ } </div> </section> - @if (isCollapsed()) { + @if (collapsed()) { <mat-divider></mat-divider> } } </div> </div> </div> - -<!-- <div--> -<!-- class="md-toolbar-tools"--> -<!-- style="height: 40px; max-height: 40px"--> -<!-- fxFlex--> -<!-- fxLayout="row"--> -<!-- fxLayoutAlign="start center"--> -<!-- >--> -<!-- <div--> -<!-- style="--> - -<!-- border-radius: 0px;--> -<!-- margin-right: 15px;--> -<!-- position: relative;--> -<!-- left: 20px;--> -<!-- "--> -<!-- >--> -<!-- <img--> -<!-- alt="icon"--> -<!-- src="assets/img/sp/logo.png"--> -<!-- style="max-height: 35px; max-width: 250px"--> -<!-- />--> -<!-- </div>--> -<!-- </div>--> - -<!-- <div fxFlex="100" fxLayout="column" class="menu-bar">--> -<!-- @for (item of menu; track item.title) {--> -<!-- @if (item.visible) {--> -<!-- <div--> -<!-- style="min-width: 0; padding: 5px 10px"--> -<!-- fxLayoutAlign="start center"--> -<!-- fxLayoutGap="10px"--> -<!-- (click)="go(item.link)"--> -<!-- [ngClass]="--> -<!-- item.link === activePage--> -<!-- ? 'iconbar-item iconbar-item-selected'--> -<!-- : 'iconbar-item'--> -<!-- "--> -<!-- >--> - -<!-- <mat-icon--> - -<!-- [attr.data-cy]="'navigation-icon-' + item.link"--> -<!-- >--> -<!-- {{ item.icon }}--> -<!-- </mat-icon>--> -<!-- <span>{{ item.title }}</span>--> -<!-- </div>--> -<!-- }--> -<!-- }--> -<!-- </div>--> diff --git a/ui/src/app/core/components/iconbar/iconbar.component.scss b/ui/src/app/core/components/iconbar/iconbar.component.scss index 9483c23acd..ae54e27743 100644 --- a/ui/src/app/core/components/iconbar/iconbar.component.scss +++ b/ui/src/app/core/components/iconbar/iconbar.component.scss @@ -37,7 +37,6 @@ } .iconbar-item:hover { - //background: var(--color-bg-1); cursor: pointer; } diff --git a/ui/src/app/core/components/streampipes/streampipes.component.html b/ui/src/app/core/components/streampipes/streampipes.component.html index b9465e5f01..7a7cc9799d 100644 --- a/ui/src/app/core/components/streampipes/streampipes.component.html +++ b/ui/src/app/core/components/streampipes/streampipes.component.html @@ -21,13 +21,16 @@ @fadeSlideInOut > <div class="layout-container"> - <!-- Left Menubar --> <div class="menubar"> <sp-iconbar></sp-iconbar> </div> - <!-- Right Section (Toolbar + Main Content) --> - <div class="main-section"> + <div + class="main-section" + [style.width]=" + collapsed() ? 'calc(100vw - 62px)' : 'calc(100vw - 260px)' + " + > <div class="sp-toolbar-outer"> <sp-toolbar></sp-toolbar> </div> @@ -38,21 +41,3 @@ </div> </div> </div> - -<!--<div--> -<!-- [ngClass]="darkMode ? 'dark-mode base-style' : 'light-mode base-style'"--> -<!-- @fadeSlideInOut--> -<!-->--> -<!-- <div class="sp-toolbar-outer">--> -<!-- <sp-toolbar></sp-toolbar>--> -<!-- </div>--> -<!-- <div style="display: flex; flex-direction: row; height: calc(100vh - 40px)">--> -<!-- <div style="color: white" class="iconbar">--> -<!-- <sp-iconbar></sp-iconbar>--> -<!-- </div>--> -<!-- <div style="width: 100%; height: 100%; overflow-y: auto" class="base">--> -<!-- <sp-breadcrumb></sp-breadcrumb>--> -<!-- <router-outlet></router-outlet>--> -<!-- </div>--> -<!-- </div>--> -<!--</div>--> diff --git a/ui/src/app/core/components/streampipes/streampipes.component.scss b/ui/src/app/core/components/streampipes/streampipes.component.scss index 4f4b41c6f0..736499d7cf 100644 --- a/ui/src/app/core/components/streampipes/streampipes.component.scss +++ b/ui/src/app/core/components/streampipes/streampipes.component.scss @@ -60,7 +60,6 @@ .main-section { display: flex; flex-direction: column; - width: 100%; height: 100%; background: var(--color-bg-1); } diff --git a/ui/src/app/core/components/streampipes/streampipes.component.ts b/ui/src/app/core/components/streampipes/streampipes.component.ts index 99b5d1cd79..1521acf52e 100644 --- a/ui/src/app/core/components/streampipes/streampipes.component.ts +++ b/ui/src/app/core/components/streampipes/streampipes.component.ts @@ -16,11 +16,12 @@ * */ -import { Component, inject, OnInit } from '@angular/core'; -import { AuthService } from '../../../services/auth.service'; +import { Component, inject, OnDestroy, OnInit } from '@angular/core'; import { animate, style, transition, trigger } from '@angular/animations'; import { CurrentUserService } from '@streampipes/shared-ui'; import { TranslateService } from '@ngx-translate/core'; +import { CollapseService } from '../../collapse.service'; +import { Subscription } from 'rxjs'; @Component({ selector: 'sp-streampipes', @@ -37,19 +38,31 @@ import { TranslateService } from '@ngx-translate/core'; ], standalone: false, }) -export class StreampipesComponent implements OnInit { +export class StreampipesComponent implements OnInit, OnDestroy { darkMode: boolean; private translate = inject(TranslateService); + private collapseService = inject(CollapseService); + private currentUserService = inject(CurrentUserService); - constructor(public currentUserService: CurrentUserService) {} + darkMode$: Subscription; + user$: Subscription; + + collapsed = this.collapseService.isCollapsed; ngOnInit(): void { - this.currentUserService.darkMode$.subscribe(dm => (this.darkMode = dm)); - this.currentUserService.user$.subscribe(user => { + this.darkMode$ = this.currentUserService.darkMode$.subscribe( + dm => (this.darkMode = dm), + ); + this.user$ = this.currentUserService.user$.subscribe(user => { if (user.language !== null && user.language !== 'browser') { this.translate.use(user.language); } }); } + + ngOnDestroy() { + this.darkMode$?.unsubscribe(); + this.user$?.unsubscribe(); + } } diff --git a/ui/src/app/core/components/toolbar/toolbar.component.html b/ui/src/app/core/components/toolbar/toolbar.component.html index 960d5f75b6..dd5f8ff0fe 100644 --- a/ui/src/app/core/components/toolbar/toolbar.component.html +++ b/ui/src/app/core/components/toolbar/toolbar.component.html @@ -36,6 +36,7 @@ <!-- >Dark Mode</mat-slide-toggle--> <!-- >--> <!-- </div>--> + <sp-asset-browser-toolbar> </sp-asset-browser-toolbar> <div [ngClass]=" 'notifications' === activePage diff --git a/ui/src/app/core/components/toolbar/toolbar.component.ts b/ui/src/app/core/components/toolbar/toolbar.component.ts index 249fc23871..045c572795 100644 --- a/ui/src/app/core/components/toolbar/toolbar.component.ts +++ b/ui/src/app/core/components/toolbar/toolbar.component.ts @@ -27,6 +27,7 @@ import { Subscription, timer } from 'rxjs'; import { exhaustMap } from 'rxjs/operators'; import { NotificationCountService } from '../../../services/notification-count-service'; import { LoginService } from '../../../login/services/login.service'; +import { SpAssetBrowserService } from '@streampipes/shared-ui'; @Component({ selector: 'sp-toolbar', @@ -57,8 +58,10 @@ export class ToolbarComponent private restApi = inject(RestApi); private overlay = inject(OverlayContainer); public notificationCountService = inject(NotificationCountService); + private assetFilterService = inject(SpAssetBrowserService); ngOnInit(): void { + this.assetFilterService.applyAssetLinkType(''); this.unreadNotificationsSubscription = timer(0, 10000) .pipe(exhaustMap(() => this.restApi.getUnreadNotificationsCount())) .subscribe(response => { diff --git a/ui/src/app/core/core.module.ts b/ui/src/app/core/core.module.ts index c66b60c18a..1026d8f711 100644 --- a/ui/src/app/core/core.module.ts +++ b/ui/src/app/core/core.module.ts @@ -39,6 +39,7 @@ import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { SpBreadcrumbComponent } from './components/breadcrumb/breadcrumb.component'; import { SharedUiModule } from '@streampipes/shared-ui'; import { ShortenPipe } from './pipes/shorten.pipe'; +import { TranslatePipe } from '@ngx-translate/core'; @NgModule({ imports: [ @@ -60,6 +61,7 @@ import { ShortenPipe } from './pipes/shorten.pipe'; MatSlideToggleModule, ReactiveFormsModule, SharedUiModule, + TranslatePipe, ], declarations: [ SpBreadcrumbComponent, diff --git a/ui/src/app/core/navigation.backup b/ui/src/app/core/navigation.backup index 0888e86b31..d199118b7a 100644 --- a/ui/src/app/core/navigation.backup +++ b/ui/src/app/core/navigation.backup @@ -23,7 +23,8 @@ import { AuthService } from '../../services/auth.service'; import { CurrentUserService } from '@streampipes/shared-ui'; import { AppConstants } from '../../services/app.constants'; import { UserPrivilege } from '../../_enums/user-privilege.enum'; -import { inject, signal } from '@angular/core'; +import { inject } from '@angular/core'; +import { CollapseService } from '../collapse.service'; export interface MenuItem { link: string; @@ -32,6 +33,7 @@ export interface MenuItem { pageNames: any[]; privileges: any[]; visible?: boolean; + category: string; } export interface MenuGroup { @@ -49,33 +51,34 @@ export interface MenuCategory { export abstract class BaseNavigationComponent { activePageName: string; + activePage = 'home'; authenticated = true; topItems: MenuItem[] = []; menuGroups: MenuGroup[] = []; notificationsVisible = false; public categories: MenuCategory[] = [ - { - id: 'top', - title: 'top', - collapsed: false - }, - { - id: 'analytics', - title: 'Analytics', - collapsed: false - }, - { - id: 'visualization', - title: 'Visualization', - collapsed: false - }, - { - id: 'management', - title: 'Management', - collapsed: true - }, - ] + { + id: 'top', + title: 'top', + collapsed: false, + }, + { + id: 'analytics', + title: 'Analytics', + collapsed: false, + }, + { + id: 'visualization', + title: 'Visualization', + collapsed: false, + }, + { + id: 'management', + title: 'Management', + collapsed: false, + }, + ]; public menu = [ { @@ -98,7 +101,7 @@ export abstract class BaseNavigationComponent { }, { link: 'connect', - title: 'Connect', + title: 'Connectors', icon: 'power', pageNames: [PageName.CONNECT], privileges: [UserPrivilege.PRIVILEGE_READ_ADAPTER, UserPrivilege.PRIVILEGE_WRITE_ADAPTER], @@ -107,7 +110,7 @@ export abstract class BaseNavigationComponent { }, { link: 'dashboard', - title: 'Dashboard', + title: 'Dashboards', icon: 'dashboard', pageNames: [PageName.DASHBOARD], privileges: [UserPrivilege.PRIVILEGE_READ_DASHBOARD, UserPrivilege.PRIVILEGE_WRITE_DASHBOARD], @@ -116,7 +119,7 @@ export abstract class BaseNavigationComponent { }, { link: 'dataexplorer', - title: 'Data Explorer', + title: 'Charts', icon: 'query_stats', pageNames: [PageName.DATA_EXPLORER], privileges: [UserPrivilege.PRIVILEGE_READ_DATA_EXPLORER_VIEW, UserPrivilege.PRIVILEGE_WRITE_DATA_EXPLORER_VIEW], @@ -125,7 +128,7 @@ export abstract class BaseNavigationComponent { }, { link: 'assets', - title: 'Asset Management', + title: 'Assets', icon: 'precision_manufacturing', pageNames: [PageName.ASSETS], privileges: [UserPrivilege.PRIVILEGE_READ_ASSETS, UserPrivilege.PRIVILEGE_WRITE_ASSETS], @@ -134,7 +137,7 @@ export abstract class BaseNavigationComponent { }, { link: 'configuration', - title: 'Configuration', + title: 'Settings', icon: 'settings', pageNames: [PageName.SETTINGS], privileges: [UserPrivilege.PRIVILEGE_WRITE_ASSETS, UserPrivilege.PRIVILEGE_WRITE_LABELS, UserPrivilege.PRIVILEGE_WRITE_FILES], @@ -143,13 +146,13 @@ export abstract class BaseNavigationComponent { }, ]; - - - protected authService = inject(AuthService); protected currentUserService = inject(CurrentUserService); protected router = inject(Router); protected appConstants = inject(AppConstants); + protected collapseService = inject(CollapseService); + + collapsed = this.collapseService.isCollapsed; onInit() { this.currentUserService.user$.subscribe(user => { @@ -179,10 +182,6 @@ export abstract class BaseNavigationComponent { }); } - getActivePage() { - return this.activePage; - } - getPageTitle(path) { let currentTitle = this.appConstants.APP_NAME; this.menu.forEach(m => { @@ -211,28 +210,11 @@ export abstract class BaseNavigationComponent { return this.authService.isAnyAccessGranted(privileges); } - activePage: string = 'home'; - isCollapsed = signal<boolean>(this.loadCollapsed()); - - // Collapse/expand entire menubar - toggleMenubar(): void { - const next = !this.isCollapsed(); - this.isCollapsed.set(next); - localStorage.setItem('sp-menubar-collapsed', JSON.stringify(next)); - } - - // Collapse/expand a single group - toggleGroup(group: MenuGroup): void { - group.collapsed = !group.collapsed; - } - - private loadCollapsed(): boolean { - try { - const v = localStorage.getItem('sp-menubar-collapsed'); - return v ? JSON.parse(v) : false; - } catch { - return false; - } - } + toggleGroup(group: MenuGroup): void { + group.collapsed = !group.collapsed; + } + toggleMenubar(): void { + this.collapseService.toggleMenubar(); + } } 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 2814907bd6..0d339c3c21 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 @@ -83,7 +83,7 @@ <mat-icon>visibility</mat-icon> <span>{{ 'Show' | translate }}</span> </button> - @if (hasDataExplorerWritePrivileges) { + @if (hasDashboardWritePrivileges) { <button mat-menu-item [attr.data-cy]="'edit-dashboard-' + element.name" @@ -109,7 +109,7 @@ <mat-icon>open_in_new</mat-icon> <span>{{ 'Kiosk mode' | translate }}</span> </button> - @if (hasDataExplorerWritePrivileges) { + @if (hasDashboardWritePrivileges) { <button mat-menu-item [attr.data-cy]=" @@ -131,7 +131,7 @@ <mat-icon>share</mat-icon> <span>{{ 'Manage permissions' | translate }}</span> </button> - @if (hasDataExplorerWritePrivileges) { + @if (hasDashboardWritePrivileges) { <button mat-menu-item [attr.data-cy]="'delete-dashboard-' + element.name" 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 dd65b255eb..5b7b7b231e 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,24 @@ * */ -import { Component, EventEmitter, inject, Output } from '@angular/core'; +import { Component, inject, Input, OnDestroy, OnInit } from '@angular/core'; import { MatTableDataSource } from '@angular/material/table'; import { Dashboard, DashboardService } from '@streampipes/platform-services'; import { ConfirmDialogComponent, DateFormatService, + DialogService, PanelType, + SpAssetBrowserService, } from '@streampipes/shared-ui'; -import { SpDataExplorerOverviewDirective } from '../../../../data-explorer/components/overview/data-explorer-overview.directive'; import { MatDialog } from '@angular/material/dialog'; 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 { Router } from '@angular/router'; import { CloneDashboardDialogComponent } from '../../../dialogs/clone-dashboard/clone-dashboard-dialog.component'; -import { EditDashboardDialogComponent } from '../../../dialogs/edit-dashboard/edit-dashboard-dialog.component'; +import { Subscription } from 'rxjs'; +import { DataExplorerRoutingService } from '../../../../data-explorer-shared/services/data-explorer-routing.service'; @Component({ selector: 'sp-dashboard-overview-table', @@ -41,15 +43,23 @@ import { EditDashboardDialogComponent } from '../../../dialogs/edit-dashboard/ed ], standalone: false, }) -export class DashboardOverviewTableComponent extends SpDataExplorerOverviewDirective { +export class DashboardOverviewTableComponent implements OnInit, OnDestroy { + @Input() + hasDashboardWritePrivileges: boolean; + + @Input() + admin: boolean; + dataSource = new MatTableDataSource<Dashboard>(); - displayedColumns: string[] = []; + displayedColumns: string[] = [ + 'name', + 'lastModified', + 'createdAt', + 'actions', + ]; dashboards: Dashboard[] = []; filteredDashboards: Dashboard[] = []; - @Output() - resourceCountEmitter: EventEmitter<number> = new EventEmitter(); - private dashboardService = inject(DashboardService); private dataExplorerDashboardService = inject(DataExplorerDashboardService); private dataExplorerSharedService = inject(DataExplorerSharedService); @@ -57,14 +67,21 @@ export class DashboardOverviewTableComponent extends SpDataExplorerOverviewDirec protected translateService = inject(TranslateService); protected dateFormatService = inject(DateFormatService); private router = inject(Router); - - afterInit(): void { - this.displayedColumns = [ - 'name', - 'lastModified', - 'createdAt', - 'actions', - ]; + private assetFilterService = inject(SpAssetBrowserService); + private routingService = inject(DataExplorerRoutingService); + private dialogService = inject(DialogService); + + assetFilter$: Subscription; + currentFilterIds = new Set<string>(); + + ngOnInit(): void { + this.assetFilterService.applyAssetLinkType('dashboard'); + this.assetFilter$ = + this.assetFilterService.currentAssetFilter$.subscribe(filter => { + this.currentFilterIds = + filter?.activeElementIds || new Set<string>(); + this.applyDashboardFilters(this.currentFilterIds); + }); this.getDashboards(); } @@ -134,8 +151,7 @@ export class DashboardOverviewTableComponent extends SpDataExplorerOverviewDirec getDashboards() { this.dashboardService.getDashboards().subscribe(data => { this.dashboards = data.sort((a, b) => a.name.localeCompare(b.name)); - this.resourceCountEmitter.emit(this.dashboards.length); - this.applyDashboardFilters(); + this.applyDashboardFilters(this.currentFilterIds); }); } @@ -184,4 +200,8 @@ export class DashboardOverviewTableComponent extends SpDataExplorerOverviewDirec onRowClicked(dashboard: Dashboard) { this.showDashboard(dashboard); } + + ngOnDestroy() { + this.assetFilter$?.unsubscribe(); + } } diff --git a/ui/src/app/dashboard/components/overview/dashboard-overview.component.html b/ui/src/app/dashboard/components/overview/dashboard-overview.component.html index 01afecf4f5..92776f0d6a 100644 --- a/ui/src/app/dashboard/components/overview/dashboard-overview.component.html +++ b/ui/src/app/dashboard/components/overview/dashboard-overview.component.html @@ -39,7 +39,8 @@ </div> <div fxFlex="100" fxLayout="column"> <sp-dashboard-overview-table - (resourceCountEmitter)="resourceCount = $event" + [admin]="isAdmin" + [hasDashboardWritePrivileges]="hasDashboardWritePrivileges" ></sp-dashboard-overview-table> </div> </sp-basic-view> 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 577d98f408..a8a4413369 100644 --- a/ui/src/app/dashboard/components/overview/dashboard-overview.component.ts +++ b/ui/src/app/dashboard/components/overview/dashboard-overview.component.ts @@ -16,7 +16,7 @@ * */ -import { Component, inject, OnInit, ViewChild } from '@angular/core'; +import { Component, inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { CurrentUserService, @@ -30,6 +30,7 @@ import { Dashboard } from '@streampipes/platform-services'; import { DataExplorerDashboardService } from '../../../dashboard-shared/services/dashboard.service'; import { DashboardOverviewTableComponent } from './dashboard-overview-table/dashboard-overview-table.component'; import { TranslateService } from '@ngx-translate/core'; +import { Subscription } from 'rxjs'; @Component({ selector: 'sp-dashboard-overview', @@ -37,12 +38,11 @@ import { TranslateService } from '@ngx-translate/core'; styleUrls: ['./dashboard-overview.component.scss'], standalone: false, }) -export class DashboardOverviewComponent implements OnInit { - displayedColumns: string[] = []; +export class DashboardOverviewComponent implements OnInit, OnDestroy { + displayedColumns: string[] = ['name', 'actions']; isAdmin = false; hasDashboardWritePrivileges = false; - resourceCount = 0; @ViewChild(DashboardOverviewTableComponent) dashboardOverview: DashboardOverviewTableComponent; @@ -54,16 +54,17 @@ export class DashboardOverviewComponent implements OnInit { private breadcrumbService = inject(SpBreadcrumbService); private translateService = inject(TranslateService); + private user$: Subscription; + ngOnInit(): void { this.breadcrumbService.updateBreadcrumb( this.breadcrumbService.getRootLink(SpDashboardRoutes.BASE), ); - this.currentUserService.user$.subscribe(user => { + this.user$ = this.currentUserService.user$.subscribe(user => { this.isAdmin = user.roles.indexOf(UserRole.ROLE_ADMIN) > -1; this.hasDashboardWritePrivileges = this.authService.hasRole( UserPrivilege.PRIVILEGE_WRITE_DASHBOARD, ); - this.displayedColumns = ['name', 'actions']; }); } @@ -99,7 +100,7 @@ export class DashboardOverviewComponent implements OnInit { }); } - applyDashboardFilters(elementIds: Set<string> = new Set<string>()): void { - this.dashboardOverview.applyDashboardFilters(elementIds); + ngOnDestroy() { + this.user$?.unsubscribe(); } } 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 65e04ab49d..d8f6a2d3d3 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,8 +16,7 @@ * */ -import { Component, EventEmitter, inject, Output } from '@angular/core'; -import { SpDataExplorerOverviewDirective } from '../data-explorer-overview.directive'; +import { Component, inject, Input, OnInit } from '@angular/core'; import { MatTableDataSource } from '@angular/material/table'; import { ChartService, @@ -25,16 +24,14 @@ import { } from '@streampipes/platform-services'; import { ConfirmDialogComponent, - CurrentUserService, DateFormatService, - DialogService, + SpAssetBrowserService, } from '@streampipes/shared-ui'; -import { AuthService } from '../../../../services/auth.service'; -import { DataExplorerRoutingService } from '../../../../data-explorer-shared/services/data-explorer-routing.service'; 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'; +import { DataExplorerRoutingService } from '../../../../data-explorer-shared/services/data-explorer-routing.service'; +import { Subscription } from 'rxjs'; @Component({ selector: 'sp-data-explorer-overview-table', @@ -42,28 +39,42 @@ import { DataExplorerDashboardService } from '../../../../dashboard-shared/servi styleUrls: ['../data-explorer-overview.component.scss'], standalone: false, }) -export class SpDataExplorerDataViewOverviewComponent extends SpDataExplorerOverviewDirective { +export class SpDataExplorerDataViewOverviewComponent implements OnInit { + @Input() + hasDataExplorerWritePrivileges: boolean; + + @Input() + admin: boolean; + dataSource = new MatTableDataSource<DataExplorerWidgetModel>(); - displayedColumns: string[] = []; + displayedColumns: string[] = [ + 'name', + 'lastModified', + 'createdAt', + 'actions', + ]; charts: DataExplorerWidgetModel[] = []; filteredCharts: DataExplorerWidgetModel[] = []; - @Output() - resourceCountEmitter: EventEmitter<number> = new EventEmitter(); - private dataViewService = inject(ChartService); private dataExplorerDashboardService = inject(DataExplorerSharedService); private dialog = inject(MatDialog); private translateService = inject(TranslateService); private dateFormatService = inject(DateFormatService); + private routingService = inject(DataExplorerRoutingService); + private assetFilterService = inject(SpAssetBrowserService); + + assetFilter$: Subscription; + currentFilterIds = new Set<string>(); - afterInit(): void { - this.displayedColumns = [ - 'name', - 'lastModified', - 'createdAt', - 'actions', - ]; + ngOnInit(): void { + this.assetFilterService.applyAssetLinkType('chart'); + this.assetFilter$ = + this.assetFilterService.currentAssetFilter$.subscribe(filter => { + this.currentFilterIds = + filter?.activeElementIds || new Set<string>(); + this.applyChartFilters(this.currentFilterIds); + }); this.getDataViews(); } @@ -74,8 +85,7 @@ export class SpDataExplorerDataViewOverviewComponent extends SpDataExplorerOverv b.baseAppearanceConfig.widgetTitle, ), ); - this.resourceCountEmitter.emit(this.charts.length); - this.applyChartFilters(); + this.applyChartFilters(this.currentFilterIds); }); } 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 index ff21cb9392..df3ae65c13 100644 --- 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 @@ -39,7 +39,8 @@ </div> <div fxFlex="100" fxLayout="column"> <sp-data-explorer-overview-table - (resourceCountEmitter)="resourceCount = $event" + [admin]="isAdmin" + [hasDataExplorerWritePrivileges]="hasDataExplorerWritePrivileges" ></sp-data-explorer-overview-table> </div> </sp-basic-view> 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 8d9f50bcbf..374a70d231 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,18 +16,18 @@ * */ -import { Component, inject, ViewChild } from '@angular/core'; +import { Component, inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { CurrentUserService, - DialogService, SpBreadcrumbService, } from '@streampipes/shared-ui'; import { AuthService } from '../../../services/auth.service'; import { SpDataExplorerRoutes } from '../../data-explorer.routes'; -import { SpDataExplorerOverviewDirective } from './data-explorer-overview.directive'; import { DataExplorerRoutingService } from '../../../data-explorer-shared/services/data-explorer-routing.service'; -import { DashboardOverviewTableComponent } from '../../../dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component'; import { SpDataExplorerDataViewOverviewComponent } from './data-explorer-overview-table/data-explorer-overview-table.component'; +import { UserPrivilege } from '../../../_enums/user-privilege.enum'; +import { UserRole } from '../../../_enums/user-role.enum'; +import { Subscription } from 'rxjs'; @Component({ selector: 'sp-data-explorer-overview', @@ -35,25 +35,37 @@ import { SpDataExplorerDataViewOverviewComponent } from './data-explorer-overvie styleUrls: ['./data-explorer-overview.component.scss'], standalone: false, }) -export class DataExplorerOverviewComponent extends SpDataExplorerOverviewDirective { - resourceCount = 0; - +export class DataExplorerOverviewComponent implements OnInit, OnDestroy { @ViewChild(SpDataExplorerDataViewOverviewComponent) chartsOverview: SpDataExplorerDataViewOverviewComponent; + auth$: Subscription; + + isAdmin = false; + hasDataExplorerWritePrivileges = false; + private breadcrumbService = inject(SpBreadcrumbService); + private routingService = inject(DataExplorerRoutingService); + private currentUserService = inject(CurrentUserService); + private authService = inject(AuthService); - afterInit(): void { + ngOnInit(): void { this.breadcrumbService.updateBreadcrumb( this.breadcrumbService.getRootLink(SpDataExplorerRoutes.BASE), ); + this.auth$ = this.currentUserService.user$.subscribe(user => { + this.hasDataExplorerWritePrivileges = this.authService.hasRole( + UserPrivilege.PRIVILEGE_WRITE_DATA_EXPLORER_VIEW, + ); + this.isAdmin = user.roles.indexOf(UserRole.ROLE_ADMIN) > -1; + }); } createNewDataView(): void { this.routingService.navigateToDataView(true); } - applyChartFilters(elementIds: Set<string> = new Set<string>()): void { - this.chartsOverview.applyChartFilters(elementIds); + ngOnDestroy() { + this.auth$?.unsubscribe(); } } 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 deleted file mode 100644 index 7a9a887c53..0000000000 --- a/ui/src/app/data-explorer/components/overview/data-explorer-overview.directive.ts +++ /dev/null @@ -1,59 +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 { 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'; -import { CurrentUserService, DialogService } from '@streampipes/shared-ui'; -import { AuthService } from '../../../services/auth.service'; -import { DataExplorerRoutingService } from '../../../data-explorer-shared/services/data-explorer-routing.service'; - -@Directive() -export abstract class SpDataExplorerOverviewDirective - implements OnInit, OnDestroy -{ - isAdmin = false; - - public hasDataExplorerWritePrivileges = false; - - authSubscription: Subscription; - - public dialogService = inject(DialogService); - protected authService = inject(AuthService); - protected currentUserService = inject(CurrentUserService); - protected routingService = inject(DataExplorerRoutingService); - - ngOnInit() { - this.authSubscription = this.currentUserService.user$.subscribe( - user => { - this.hasDataExplorerWritePrivileges = this.authService.hasRole( - UserPrivilege.PRIVILEGE_WRITE_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/login/components/auth-box/auth-box.component.scss b/ui/src/app/login/components/auth-box/auth-box.component.scss index 2074dd9f4c..5dd9fcd023 100644 --- a/ui/src/app/login/components/auth-box/auth-box.component.scss +++ b/ui/src/app/login/components/auth-box/auth-box.component.scss @@ -17,7 +17,7 @@ */ .background-auth-box { - background: var(--color-navigation-bg); + background: var(--color-menubar-bg); } .auth-box-footer { @@ -62,6 +62,6 @@ .app-name-text { padding: 30px; - color: var(--color-navigation-link-text); + color: var(--color-menubar-text-menu-item); font-size: 6vmax; } diff --git a/ui/src/app/pipelines/pipelines.component.ts b/ui/src/app/pipelines/pipelines.component.ts index f03920197f..0d692b4190 100644 --- a/ui/src/app/pipelines/pipelines.component.ts +++ b/ui/src/app/pipelines/pipelines.component.ts @@ -16,7 +16,7 @@ * */ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, inject, OnDestroy, OnInit } from '@angular/core'; import { FunctionId, FunctionsService, @@ -28,6 +28,7 @@ import { DialogRef, DialogService, PanelType, + SpAssetBrowserService, SpBreadcrumbService, } from '@streampipes/shared-ui'; import { StartAllPipelinesDialogComponent } from './dialog/start-all-pipelines/start-all-pipelines-dialog.component'; @@ -49,8 +50,8 @@ export class PipelinesComponent implements OnInit, OnDestroy { pipeline: Pipeline; pipelines: Pipeline[] = []; filteredPipelines: Pipeline[] = []; - starting: boolean; - stopping: boolean; + starting = false; + stopping = false; pipelinesReady = false; hasPipelineWritePrivileges = false; @@ -60,47 +61,48 @@ export class PipelinesComponent implements OnInit, OnDestroy { isAdminRole = false; tutorialActive = false; - tutorialActiveSubscription: Subscription; - userSubscription: Subscription; + tutorial$: Subscription; + user$: Subscription; + assetFilter$: Subscription; currentFilters: Set<string> = new Set<string>(); - constructor( - private pipelineService: PipelineService, - private dialogService: DialogService, - private authService: AuthService, - private currentUserService: CurrentUserService, - private router: Router, - private functionsService: FunctionsService, - private breadcrumbService: SpBreadcrumbService, - private shepherdService: ShepherdService, - ) { - this.starting = false; - this.stopping = false; - } + private pipelineService = inject(PipelineService); + private dialogService = inject(DialogService); + private currentUserService = inject(CurrentUserService); + private authService = inject(AuthService); + private router = inject(Router); + private functionsService = inject(FunctionsService); + private breadcrumbService = inject(SpBreadcrumbService); + private shepherdService = inject(ShepherdService); + private assetFilterService = inject(SpAssetBrowserService); ngOnInit() { + this.assetFilterService.applyAssetLinkType('pipeline'); + this.assetFilter$ = + this.assetFilterService.currentAssetFilter$.subscribe(filter => { + this.currentFilters = + filter?.activeElementIds || new Set<string>(); + this.applyPipelineFilters(this.currentFilters); + }); this.breadcrumbService.updateBreadcrumb( this.breadcrumbService.getRootLink(SpPipelineRoutes.BASE), ); - this.userSubscription = this.currentUserService.user$.subscribe( - user => { - this.hasPipelineWritePrivileges = this.authService.hasRole( - UserPrivilege.PRIVILEGE_WRITE_PIPELINE, - ); - this.isAdminRole = this.authService.hasRole( - UserRole.ROLE_ADMIN, - ); - }, - ); + this.user$ = this.currentUserService.user$.subscribe(user => { + this.hasPipelineWritePrivileges = this.authService.hasRole( + UserPrivilege.PRIVILEGE_WRITE_PIPELINE, + ); + this.isAdminRole = this.authService.hasRole(UserRole.ROLE_ADMIN); + }); if (this.shepherdService.isTourActive()) { this.shepherdService.trigger('pipeline-started'); } this.getPipelines(); this.getFunctions(); - this.tutorialActiveSubscription = - this.shepherdService.tutorialActive$.subscribe(tutorialActive => { + this.tutorial$ = this.shepherdService.tutorialActive$.subscribe( + tutorialActive => { this.tutorialActive = tutorialActive; - }); + }, + ); } getFunctions() { @@ -174,7 +176,8 @@ export class PipelinesComponent implements OnInit, OnDestroy { } ngOnDestroy() { - this.userSubscription?.unsubscribe(); - this.tutorialActiveSubscription?.unsubscribe(); + this.user$?.unsubscribe(); + this.tutorial$?.unsubscribe(); + this.assetFilter$?.unsubscribe(); } } diff --git a/ui/src/scss/sp/buttons-mat3.scss b/ui/src/scss/sp/buttons-mat3.scss index 4a9d09e424..f4aec978bc 100644 --- a/ui/src/scss/sp/buttons-mat3.scss +++ b/ui/src/scss/sp/buttons-mat3.scss @@ -39,7 +39,19 @@ .btn-secondary { @include mat.button-overrides( ( - filled-container-color: var(--color-bg-1), + filled-container-color: var(--mat-sys-surface-container-high), + filled-label-text-color: var(--mat-sys-secondary), + text-hover-state-layer-opacity: 1, + filled-label-text-weight: 400, + ) + ); + } + + .btn-secondary-outline { + border: 1px solid var(--color-primary); + @include mat.button-overrides( + ( + filled-container-color: var(--mat-sys-surface-container-high), filled-label-text-color: var(--mat-sys-primary), text-hover-state-layer-opacity: 1, filled-label-text-weight: 400,
