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 fb14c03376129aea1a7c17883ddf183d8a64d0a3 Author: Dominik Riemer <[email protected]> AuthorDate: Wed Oct 29 23:12:52 2025 +0100 Add expandable menubar --- ui/deployment/base-navigation.component.mst | 80 ++- ui/deployment/categories.yml | 12 + ui/deployment/modules.yml | 21 +- ui/deployment/prebuild.js | 15 +- ui/deployment/theme/_custom-variables.scss | 32 +- ...asset-browser-filter-asset-model.component.html | 0 .../asset-browser-filter-asset-model.component.ts | 0 .../asset-browser-toolbar.component.html | 53 +- .../asset-browser-toolbar.component.ts | 66 ++- .../basic-view/basic-view.component.html | 6 +- .../basic-view/basic-view.component.scss | 8 +- .../shared-ui/src/lib/shared-ui.module.ts | 1 + .../streampipes/shared-ui/src/public-api.ts | 1 + .../existing-adapters.component.html | 565 ++++++++++----------- ui/src/app/core/components/bars.scss | 34 -- .../breadcrumb/breadcrumb.component.scss | 5 +- .../core/components/iconbar/iconbar.component.html | 198 ++++++-- .../core/components/iconbar/iconbar.component.scss | 251 +++++++++ .../core/components/iconbar/iconbar.component.ts | 2 +- .../streampipes/streampipes.component.html | 39 +- .../streampipes/streampipes.component.scss | 58 ++- .../core/components/toolbar/toolbar.component.html | 44 +- .../core/components/toolbar/toolbar.component.scss | 17 +- .../core/components/toolbar/toolbar.component.ts | 2 +- ui/src/app/core/navigation.backup | 238 +++++++++ .../overview/dashboard-overview.component.html | 59 +-- .../overview/data-explorer-overview.component.html | 59 +-- ui/src/app/pipelines/pipelines.component.html | 177 +++---- ui/src/scss/sp/layout.scss | 4 +- ui/src/scss/sp/main.scss | 12 +- 30 files changed, 1378 insertions(+), 681 deletions(-) diff --git a/ui/deployment/base-navigation.component.mst b/ui/deployment/base-navigation.component.mst index cf4a84b36d..d7d25a1ab6 100644 --- a/ui/deployment/base-navigation.component.mst +++ b/ui/deployment/base-navigation.component.mst @@ -23,14 +23,49 @@ 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 } from '@angular/core'; +import { inject, signal } from '@angular/core'; + +export interface MenuItem { + link: string; + title: string; + icon: string; + pageNames: any[]; + privileges: any[]; + visible?: boolean; + category: string; +} + +export interface MenuGroup { + title: string; + items: MenuItem[]; + collapsed?: boolean; +} + +export interface MenuCategory { + id: string; + title: string; + collapsed: boolean; +} export abstract class BaseNavigationComponent { activePageName: string; - activePage: any; - + activePage = 'home'; authenticated = true; + topItems: MenuItem[] = []; + menuGroups: MenuGroup[] = []; + notificationsVisible = false; + isCollapsed = signal<boolean>(this.loadCollapsed()); + + public categories: MenuCategory[] = [ + {{#categoriesActive}} + { + id: '{{{id}}}', + title: '{{{title}}}', + collapsed: {{{collapsed}}}, + }, + {{/categoriesActive}} + ] public menu = [ {{#modulesActive}} @@ -40,14 +75,12 @@ export abstract class BaseNavigationComponent { icon: '{{{icon}}}', pageNames: [{{{pageNames}}}], privileges: {{{privileges}}}, - visible: false + visible: false, + category: '{{{category}}}' }, {{/modulesActive}} ]; - notificationsVisible = false; - - protected authService = inject(AuthService); protected currentUserService = inject(CurrentUserService); protected router = inject(Router); @@ -56,6 +89,16 @@ export abstract class BaseNavigationComponent { onInit() { this.currentUserService.user$.subscribe(user => { this.menu.forEach(m => m.visible = this.isNavItemVisible(m.privileges)); + const menuGroups = this.categories.map(category => { + return { + title: category.title, + collapsed: category.collapsed, + items: this.menu.filter(m => m.category === category.id && m.visible) + } + }).filter(m => m.items.length > 0); + + this.topItems = menuGroups.find(m => m.title === 'top')?.items || []; + this.menuGroups = menuGroups.filter(m => m.title !== 'top'); this.notificationsVisible = this.isNavItemVisible([ UserPrivilege.PRIVILEGE_READ_PIPELINE, UserPrivilege.PRIVILEGE_WRITE_PIPELINE @@ -71,10 +114,6 @@ export abstract class BaseNavigationComponent { }); } - getActivePage() { - return this.activePage; - } - getPageTitle(path) { let currentTitle = this.appConstants.APP_NAME; this.menu.forEach(m => { @@ -103,4 +142,23 @@ 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; + } + + private loadCollapsed(): boolean { + try { + const v = localStorage.getItem('sp-menubar-collapsed'); + return v ? JSON.parse(v) : false; + } catch { + return false; + } + } + } diff --git a/ui/deployment/categories.yml b/ui/deployment/categories.yml new file mode 100644 index 0000000000..d6c12ef162 --- /dev/null +++ b/ui/deployment/categories.yml @@ -0,0 +1,12 @@ +- id: top + title: top + collapsed: false +- id: analytics + title: Analytics + collapsed: false +- id: visualization + title: Visualization + collapsed: false +- id: management + title: Management + collapsed: true diff --git a/ui/deployment/modules.yml b/ui/deployment/modules.yml index 23564fedbb..f11fd16949 100644 --- a/ui/deployment/modules.yml +++ b/ui/deployment/modules.yml @@ -27,6 +27,7 @@ spAssets: privileges: '[UserPrivilege.PRIVILEGE_READ_ASSETS, UserPrivilege.PRIVILEGE_WRITE_ASSETS]' pageNames: 'PageName.ASSETS' showStatusBox: false + category: 'management' spHome: streamPipesModule: True moduleName: 'HomeModule' @@ -40,6 +41,7 @@ spHome: privileges: '[]' guard: 'canActivate: [ConfiguredCanActivateGuard], ' showStatusBox: false + category: 'top' spConnect: componentImport: False moduleName: 'ConnectModule' @@ -62,6 +64,7 @@ spConnect: dataFns: '[this.adapterService.getAdapters()]' viewRoles: '[UserPrivilege.PRIVILEGE_READ_ADAPTER, UserPrivilege.PRIVILEGE_WRITE_ADAPTER]' createRoles: '[UserPrivilege.PRIVILEGE_WRITE_ADAPTER]' + category: 'management' spPipelines: componentImport: False moduleName: 'PipelinesModule' @@ -84,6 +87,7 @@ spPipelines: dataFns: '[this.pipelineService.getPipelines()]' viewRoles: '[UserPrivilege.PRIVILEGE_READ_PIPELINE, UserPrivilege.PRIVILEGE_WRITE_PIPELINE]' createRoles: '[UserPrivilege.PRIVILEGE_WRITE_PIPELINE]' + category: 'analytics' spConfiguration: componentImport: False moduleName: 'ConfigurationModule' @@ -98,20 +102,7 @@ spConfiguration: pageNames: 'PageName.SETTINGS' privileges: '[UserPrivilege.PRIVILEGE_WRITE_ASSETS, UserPrivilege.PRIVILEGE_WRITE_LABELS, UserPrivilege.PRIVILEGE_WRITE_FILES]' showStatusBox: false -spAppOverview: - componentImport: False - moduleName: 'AppOverviewModule' - component: 'AppOverviewComponent' - componentPath: './app-overview/app-overview.component' - path: './app-overview/app-overview.module' - link: 'apps' - url: '/apps' - title: 'Apps' - description: 'The app overview lets you access additional plugins.' - icon: 'apps' - pageNames: 'PageName.APPS' - privileges: '[UserPrivilege.PRIVILEGE_READ_APPS, UserPrivilege.PRIVILEGE_WRITE_APPS]' - showStatusBox: false + category: 'management' spDashboard: componentImport: False moduleName: 'DashboardModule' @@ -134,6 +125,7 @@ spDashboard: dataFns: '[this.dashboardService.getDashboards()]' viewRoles: '[UserPrivilege.PRIVILEGE_READ_DASHBOARD, UserPrivilege.PRIVILEGE_WRITE_DASHBOARD]' createRoles: '[UserPrivilege.PRIVILEGE_WRITE_DASHBOARD]' + category: 'visualization' spDataExplorer: componentImport: False moduleName: 'DataExplorerModule' @@ -156,3 +148,4 @@ spDataExplorer: dataFns: '[this.chartService.getAllCharts()]' viewRoles: '[UserPrivilege.PRIVILEGE_READ_DATA_EXPLORER_VIEW, UserPrivilege.PRIVILEGE_WRITE_DATA_EXPLORER_VIEW]' createRoles: '[UserPrivilege.PRIVILEGE_WRITE_DATA_EXPLORER_VIEW]' + category: 'visualization' diff --git a/ui/deployment/prebuild.js b/ui/deployment/prebuild.js index 3e00297dcd..5e2448203c 100644 --- a/ui/deployment/prebuild.js +++ b/ui/deployment/prebuild.js @@ -54,6 +54,7 @@ try { // Read Modules-File and check if it is valid let modules = {}; +let categories = []; try { modules = yaml.load(fs.readFileSync('deployment/modules.yml', 'utf8')); } catch (error) { @@ -61,8 +62,19 @@ try { process.exit(1); } +try { + categories = yaml.load( + fs.readFileSync('deployment/categories.yml', 'utf8'), + ); +} catch (error) { + console.log( + 'Invalid file categories.yml. Check if the file exists in your build configuration. Pre-Build failed.', + ); + process.exit(1); +} + // Add active Modules to Template-Variable -let modulesActive = { modulesActive: [] }; +let modulesActive = { modulesActive: [], categoriesActive: categories }; for (let module of config.modules) { modulesActive['modulesActive'].push({ module: module, @@ -81,6 +93,7 @@ for (let module of config.modules) { description: modules[module]['description'], showStatusBox: modules[module]['showStatusBox'], statusBox: modules[module]['statusBox'], + category: modules[module]['category'], }); console.log('Active Angular Module: ' + module); } diff --git a/ui/deployment/theme/_custom-variables.scss b/ui/deployment/theme/_custom-variables.scss index 97cdb76054..7c20248217 100644 --- a/ui/deployment/theme/_custom-variables.scss +++ b/ui/deployment/theme/_custom-variables.scss @@ -27,13 +27,33 @@ --color-loading-bar: var(--color-primary); --color-primary-alt: rgb(59, 92, 149); - --color-navigation-bg: var(--color-secondary); - --color-navigation-link-text: var(--color-bg-0); + --color-menubar-bg: var(--color-secondary); + --color-menubar-bg-menu-item: var(--color-secondary); + --color-menubar-bg-menu-item-hover: var(--color-secondary); + --color-menubar-bg-menu-item-selected: var(--color-secondary); + --color-menubar-bg-menu-group-hover: var(--color-secondary); + --color-menubar-bg-menu-group-selected: var(--color-secondary); + --color-menubar-text-menu-item: var(--color-bg-1); + --color-menubar-text-menu-group: var(--color-primary); + --color-menubar-text-menu-item-hover: var(--color-bg-1); + --color-menubar-text-menu-group-hover: var(--color-primary); + //--color-menubar-bg: var(--color-bg-0); + //--color-menubar-bg-menu-item: var(--color-bg-0); + //--color-menubar-bg-menu-item-hover: var(--color-bg-2); + //--color-menubar-bg-menu-item-selected: var(--color-bg-2); + //--color-menubar-bg-menu-group-hover: var(--color-bg-1); + //--color-menubar-bg-menu-group-selected: var(--color-bg-1); + //--color-menubar-text-menu-item: var(--color-secondary); + //--color-menubar-text-menu-group: var(--color-primary); + //--color-menubar-text-menu-item-hover: var(--color-secondary-dark); + //--color-menubar-text-menu-group-hover: var(--color-bg-1); - --color-navigation-selected: var(--color-secondary); - --color-navigation-hover: var(--color-secondary-dark); - --color-navigation-bg-selected: var(--color-bg-1); - --color-navigation-divider: var(--color-secondary); + --color-toolbar-bg: var(--color-bg-0); + --color-toolbar-text: var(--color-secondary); + --color-toolbar-text-hover: var(--color-secondary-dark); + --color-toolbar-icon: var(--color-secondary); + --color-toolbar-icon-hover: var(--color-secondary-dark); + --color-toolbar-icon-selected: var(--color-secondary-dark); } .dark-mode { 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 new file mode 100644 index 0000000000..e69de29bb2 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 new file mode 100644 index 0000000000..e69de29bb2 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 4ebd2f1850..cbf4d703c8 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 @@ -16,37 +16,24 @@ ~ --> -<div fxFlex fxLayoutAlign="start center" *ngIf="expanded"> - <span class="ml-5">{{ 'Browse assets' | translate }}</span> -</div> -<div fxLayoutAlign="end center"> - <button - mat-icon-button - *ngIf="expanded" - color="accent" - [matMenuTriggerFor]="menu" - #menuTrigger="matMenuTrigger" - [matTooltip]="'Filter assets' | translate" - (menuClosed)="menuTrigger.closeMenu()" - > - <mat-icon>filter_alt</mat-icon> - </button> - <mat-menu #menu="matMenu" fxFlex="100" class="large-menu"> - <sp-asset-browser-filter - (closeMenu)="closeMenu()" - [assetBrowserData]="assetBrowserData" +@if (showAssetBrowser) { + <div fxLayoutAlign="end center"> + <button + mat-icon-button + color="accent" + [matMenuTriggerFor]="menu" + #menuTrigger="matMenuTrigger" + [matTooltip]="'Filter assets' | translate" + (menuClosed)="menuTrigger.closeMenu()" > - </sp-asset-browser-filter> - </mat-menu> - <button - mat-icon-button - color="accent" - (click)="toggleExpanded.emit(!expanded)" - > - <mat-icon>{{ - expanded - ? 'keyboard_double_arrow_left' - : 'keyboard_double_arrow_right' - }}</mat-icon> - </button> -</div> + <mat-icon>filter_alt</mat-icon> + </button> + <mat-menu #menu="matMenu" fxFlex="100" class="large-menu"> + <sp-asset-browser-filter + (closeMenu)="closeMenu()" + [assetBrowserData]="assetBrowserData" + > + </sp-asset-browser-filter> + </mat-menu> + </div> +} 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 1352e35da8..9390c2ade1 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 @@ -19,30 +19,88 @@ import { Component, EventEmitter, + inject, Input, + OnInit, Output, 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', standalone: false, }) -export class AssetBrowserToolbarComponent { +export class AssetBrowserToolbarComponent implements OnInit { + private currentUserService = inject(CurrentUserService); + private assetBrowserService = inject(SpAssetBrowserService); + private translateService = inject(TranslateService); + @Input() - expanded: boolean; + allResourcesAlias = this.translateService.instant('Resources'); - @Output() - toggleExpanded = new EventEmitter<boolean>(); + @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; @ViewChild('menuTrigger') menu: MatMenuTrigger; + ngOnInit() { + this.showAssetBrowser = this.currentUserService.hasAnyRole([ + 'PRIVILEGE_READ_ASSETS', + 'PRIVILEGE_WRITE_ASSETS', + ]); + if (this.showAssetBrowser) { + this.assetBrowserDataSub = + this.assetBrowserService.assetData$.subscribe(assetData => { + this.assetBrowserData = assetData; + console.log(assetData); + }); + } + } + + 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); + } + closeMenu(): void { this.menu.closeMenu(); } diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/basic-view/basic-view.component.html b/ui/projects/streampipes/shared-ui/src/lib/components/basic-view/basic-view.component.html index 868bf057b7..ae4739788b 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/basic-view/basic-view.component.html +++ b/ui/projects/streampipes/shared-ui/src/lib/components/basic-view/basic-view.component.html @@ -17,11 +17,7 @@ --> <div fxLayout="column" class="page-container"> - <div - fxLayout="row" - class="p-0 sp-bg-lightgray page-container-nav" - *ngIf="!hideNavbar" - > + <div fxLayout="row" class="p-0 page-container-nav" *ngIf="!hideNavbar"> <div fxLayout="fill" fxFlex="100" class="pl-5 pr-5"> <div fxLayout="row" diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/basic-view/basic-view.component.scss b/ui/projects/streampipes/shared-ui/src/lib/components/basic-view/basic-view.component.scss index 1200a6529b..454f054f9e 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/basic-view/basic-view.component.scss +++ b/ui/projects/streampipes/shared-ui/src/lib/components/basic-view/basic-view.component.scss @@ -29,8 +29,8 @@ line-height: 24px; height: 44px; border-bottom: 1px solid var(--color-bg-3); - border-top-left-radius: 10px; - border-top-right-radius: 10px; + border-top-left-radius: 6px; + border-top-right-radius: 6px; } .sp-bg-lightgray { @@ -44,9 +44,9 @@ .page-container { margin: 10px; border: 1px solid var(--color-bg-3); - min-height: calc(100vh - 90px); + min-height: calc(100vh - 75px); background-color: var(--color-bg-main-panel-content); - border-radius: 10px; + border-radius: 6px; } .page-container-padding-inner { 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 176c333794..61f40fc93c 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 @@ -192,6 +192,7 @@ import { AssetLinkConfigurationComponent } from './components/asset-link-configu ], exports: [ AssetBrowserComponent, + AssetBrowserToolbarComponent, AssetLinkConfigurationComponent, ConfirmDialogComponent, DataDownloadDialogComponent, diff --git a/ui/projects/streampipes/shared-ui/src/public-api.ts b/ui/projects/streampipes/shared-ui/src/public-api.ts index e25e3ad2b4..4fab61f8d4 100644 --- a/ui/projects/streampipes/shared-ui/src/public-api.ts +++ b/ui/projects/streampipes/shared-ui/src/public-api.ts @@ -29,6 +29,7 @@ export * from './lib/dialog/standard-dialog/standard-dialog.component'; export * from './lib/dialog/pipeline-element-help/pipeline-element-help.component'; export * from './lib/components/asset-browser/asset-browser.component'; +export * from './lib/components/asset-browser/asset-browser-toolbar/asset-browser-toolbar.component'; export * from './lib/components/basic-header-title/header-title.component'; export * from './lib/components/basic-inner-panel/basic-inner-panel.component'; export * from './lib/components/basic-field-description/basic-field-description.component'; 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 9b85f61375..a4b1d5caec 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 @@ -16,333 +16,278 @@ ~ --> -<sp-asset-browser - filteredAssetLinkType="adapter" - [allResourcesAlias]="'Adapters' | translate" - [resourceCount]="existingAdapters.length" - (filterIdsEmitter)="applyAdapterFilters($event)" -> - <sp-basic-view [showBackLink]="false" [padding]="true"> - <div - nav - fxFlex="100" - fxLayoutAlign="start center" - fxLayout="row" - fxLayoutGap="10px" - class="pl-10" +<sp-basic-view [showBackLink]="false" [padding]="true"> + <div + nav + fxFlex="100" + fxLayoutAlign="start center" + fxLayout="row" + fxLayoutGap="10px" + class="pl-10" + > + <button + mat-flat-button + data-cy="connect-create-new-adapter-button" + (click)="createNewAdapter()" > - <button - mat-flat-button - data-cy="connect-create-new-adapter-button" - (click)="createNewAdapter()" - > - <i class="material-icons">add</i> {{ - 'New adapter' | translate - }} - </button> - <button - mat-flat-button - class="mat-basic" - data-cy="start-all-adapters-btn" - [disabled]="checkCurrentSelectionStatus(false)" - (click)="startAllAdapters(true)" + <i class="material-icons">add</i> {{ + 'New adapter' | translate + }} + </button> + <button + mat-flat-button + class="mat-basic" + data-cy="start-all-adapters-btn" + [disabled]="checkCurrentSelectionStatus(false)" + (click)="startAllAdapters(true)" + > + <mat-icon>play_arrow</mat-icon> + <span>{{ 'Start all adapters' | translate }}</span> + </button> + <button + mat-flat-button + class="mat-basic" + data-cy="stop-all-adapters-btn" + [disabled]="checkCurrentSelectionStatus(true)" + (click)="startAllAdapters(false)" + > + <mat-icon>stop</mat-icon> + <span>{{ 'Stop all adapters' | translate }}</span> + </button> + <div fxFlex fxLayout="row" fxLayoutAlign="end center"> + <sp-connect-filter-toolbar + class="filter-bar-margin" + (filterChangedEmitter)="applyFilter($event)" > - <mat-icon>play_arrow</mat-icon> - <span>{{ 'Start all adapters' | translate }}</span> - </button> - <button - mat-flat-button - class="mat-basic" - data-cy="stop-all-adapters-btn" - [disabled]="checkCurrentSelectionStatus(true)" - (click)="startAllAdapters(false)" + </sp-connect-filter-toolbar> + <div + fxFlex="100" + fxLayout="row" + fxLayoutAlign="end center" + class="page-container-nav" > - <mat-icon>stop</mat-icon> - <span>{{ 'Stop all adapters' | translate }}</span> - </button> - <div fxFlex fxLayout="row" fxLayoutAlign="end center"> - <sp-connect-filter-toolbar - class="filter-bar-margin" - (filterChangedEmitter)="applyFilter($event)" + <sp-asset-browser-toolbar + filteredAssetLinkType="adapter" + [allResourcesAlias]="'Adapters' | translate" + [resourceCount]="existingAdapters.length" + (filterIdsEmitter)="applyAdapterFilters($event)" > - </sp-connect-filter-toolbar> - <div - fxFlex="100" - fxLayout="row" - fxLayoutAlign="end center" - class="page-container-nav" - > - <button - mat-icon-button - color="accent" - id="startAdapterTutorial3" - (click)="startAdapterTutorial()" - matTooltip="Tutorial: Generic Adapter" - [disabled]="tutorialActive" - > - <mat-icon>school</mat-icon> - </button> - </div> + </sp-asset-browser-toolbar> <button mat-icon-button - [matTooltip]="'Refresh adapters' | translate" - matTooltipPosition="below" color="accent" - (click)="getAdaptersRunning()" + id="startAdapterTutorial3" + (click)="startAdapterTutorial()" + matTooltip="Tutorial: Generic Adapter" + [disabled]="tutorialActive" > - <i class="material-icons"> refresh </i> + <mat-icon>school</mat-icon> </button> </div> + <button + mat-icon-button + [matTooltip]="'Refresh adapters' | translate" + matTooltipPosition="below" + color="accent" + (click)="getAdaptersRunning()" + > + <i class="material-icons"> refresh </i> + </button> </div> - <div fxFlex="100" fxLayout="column"> - <sp-basic-header-title-component - [title]="'Adapters' | translate" - ></sp-basic-header-title-component> - <div fxFlex="100" fxLayout="row" fxLayoutAlign="center start"> - <sp-table - fxFlex="100" - [columns]="displayedColumns" - [dataSource]="dataSource" - [showActionsMenu]="true" - [rowsClickable]="true" - (rowClicked)="navigateToDetailsOverviewPage($event)" - data-cy="all-adapters-table" - matSort - > - <ng-container matColumnDef="status"> - <th mat-header-cell mat-sort-header *matHeaderCellDef> - {{ 'Status' | translate }} - </th> - <td mat-cell *matCellDef="let adapter"> - <sp-adapter-status-light - [adapterRunning]="adapter.running" - ></sp-adapter-status-light> - </td> - </ng-container> - <ng-container matColumnDef="start"> - <th mat-header-cell *matHeaderCellDef>Start</th> - <td - mat-cell - *matCellDef="let adapter" - data-cy="adapters-table" - > - @if ( - adapter.elementId === - operationInProgressAdapterId - ) { - <div - data-cy="adapter-operation-in-progress-spinner" - fxLayoutAlign="center center" - > - <mat-spinner - color="accent" - [diameter]="20" - ></mat-spinner> - </div> - } @else if (!adapter.running) { - <button - color="accent" - mat-icon-button - matTooltip="Start adapter" - matTooltipPosition="above" - data-cy="start-adapter" - (click)=" - startAdapter(adapter); - $event.stopPropagation() - " - > - <i class="material-icons">play_arrow</i> - </button> - } @else if (adapter.running) { - <button - color="accent" - mat-icon-button - matTooltip="Stop adapter" - matTooltipPosition="above" - data-cy="stop-adapter" - (click)=" - stopAdapter(adapter); - $event.stopPropagation() - " - > - <i class="material-icons">stop</i> - </button> - } - </td> - </ng-container> - - <ng-container matColumnDef="name"> - <th mat-header-cell mat-sort-header *matHeaderCellDef> - {{ 'Name' | translate }} - </th> - <td mat-cell *matCellDef="let adapter"> + </div> + <div fxFlex="100" fxLayout="column"> + <sp-basic-header-title-component + [title]="'Adapters' | translate" + ></sp-basic-header-title-component> + <div fxFlex="100" fxLayout="row" fxLayoutAlign="center start"> + <sp-table + fxFlex="100" + [columns]="displayedColumns" + [dataSource]="dataSource" + [showActionsMenu]="true" + [rowsClickable]="true" + (rowClicked)="navigateToDetailsOverviewPage($event)" + data-cy="all-adapters-table" + matSort + > + <ng-container matColumnDef="status"> + <th mat-header-cell mat-sort-header *matHeaderCellDef> + {{ 'Status' | translate }} + </th> + <td mat-cell *matCellDef="let adapter"> + <sp-adapter-status-light + [adapterRunning]="adapter.running" + ></sp-adapter-status-light> + </td> + </ng-container> + <ng-container matColumnDef="start"> + <th mat-header-cell *matHeaderCellDef>Start</th> + <td + mat-cell + *matCellDef="let adapter" + data-cy="adapters-table" + > + @if ( + adapter.elementId === operationInProgressAdapterId + ) { <div - fxLayout="column" - fxLayoutAlign="start start" - class="truncate" + data-cy="adapter-operation-in-progress-spinner" + fxLayoutAlign="center center" > - <b data-cy="adapter-name">{{ adapter.name }}</b> - <small> {{ adapter.description }}</small> + <mat-spinner + color="accent" + [diameter]="20" + ></mat-spinner> </div> - </td> - </ng-container> - <ng-container matColumnDef="adapterBase"> - <th mat-header-cell *matHeaderCellDef>Adapter</th> - <td mat-cell *matCellDef="let adapter"> - <img - class="adapter-icon" - *ngIf="getIconUrl(adapter) && !adapter.icon" - [src]="getIconUrl(adapter)" - [alt]="adapter.name" - /> - <img - class="adapter-icon" - *ngIf="adapter.icon" - [alt]="adapter.name" - [src]="adapter.icon" - /> - </td> - </ng-container> - <ng-container matColumnDef="lastModified"> - <th mat-header-cell *matHeaderCellDef> - {{ 'Created' | translate }} - </th> - <td mat-cell *matCellDef="let adapter"> - <h5> - {{ - adapter.createdAt | date: 'dd.MM.yyyy HH:mm' - }} - </h5> - </td> - </ng-container> - - <ng-container matColumnDef="messagesSent"> - <th - mat-header-cell - *matHeaderCellDef - matTooltip="Messages sent since last start" - > - #{{ 'Messages' | translate }} - </th> - <td mat-cell *matCellDef="let adapter"> - <sp-label - size="small" - [labelText]=" - adapterMetrics[adapter.elementId] - ?.messagesOut.counter || 0 + } @else if (!adapter.running) { + <button + color="accent" + mat-icon-button + matTooltip="Start adapter" + matTooltipPosition="above" + data-cy="start-adapter" + (click)=" + startAdapter(adapter); + $event.stopPropagation() " - ></sp-label> - </td> - </ng-container> - - <ng-container matColumnDef="lastMessage"> - <th mat-header-cell *matHeaderCellDef> - {{ 'Last message' | translate }} - </th> - <td mat-cell *matCellDef="let adapter"> - <h5> - {{ - adapterMetrics[adapter.elementId] && - adapterMetrics[adapter.elementId] - .lastTimestamp > 0 - ? (adapterMetrics[adapter.elementId] - .lastTimestamp - | date: 'dd.MM.yyyy HH:mm') - : 'n/a' - }} - </h5> - </td> - </ng-container> + > + <i class="material-icons">play_arrow</i> + </button> + } @else if (adapter.running) { + <button + color="accent" + mat-icon-button + matTooltip="Stop adapter" + matTooltipPosition="above" + data-cy="stop-adapter" + (click)=" + stopAdapter(adapter); + $event.stopPropagation() + " + > + <i class="material-icons">stop</i> + </button> + } + </td> + </ng-container> - <ng-template spTableActions let-element> - <button - mat-menu-item - data-cy="details-adapter" - (click)="navigateToDetailsOverviewPage(element)" + <ng-container matColumnDef="name"> + <th mat-header-cell mat-sort-header *matHeaderCellDef> + {{ 'Name' | translate }} + </th> + <td mat-cell *matCellDef="let adapter"> + <div + fxLayout="column" + fxLayoutAlign="start start" + class="truncate" > - <mat-icon>visibility</mat-icon> - <span>{{ 'Show' | translate }}</span> - </button> - <button - mat-menu-item - data-cy="edit-adapter" - (click)="editAdapter(element)" - > - <mat-icon>edit</mat-icon> - <span>{{ 'Edit' | translate }}</span> - </button> - <button - mat-menu-item - data-cy="open-manage-permissions" - *ngIf="isAdmin" - (click)="showPermissionsDialog(element)" - > - <mat-icon>share</mat-icon> - <span>{{ 'Manage permissions' | translate }}</span> - </button> - <button - mat-menu-item - data-cy="delete-adapter" - (click)="deleteAdapter(element)" - > - <mat-icon>delete</mat-icon> - <span>{{ 'Delete' | translate }}</span> - </button> - </ng-template> + <b data-cy="adapter-name">{{ adapter.name }}</b> + <small> {{ adapter.description }}</small> + </div> + </td> + </ng-container> + <ng-container matColumnDef="adapterBase"> + <th mat-header-cell *matHeaderCellDef>Adapter</th> + <td mat-cell *matCellDef="let adapter"> + <img + class="adapter-icon" + *ngIf="getIconUrl(adapter) && !adapter.icon" + [src]="getIconUrl(adapter)" + [alt]="adapter.name" + /> + <img + class="adapter-icon" + *ngIf="adapter.icon" + [alt]="adapter.name" + [src]="adapter.icon" + /> + </td> + </ng-container> + <ng-container matColumnDef="lastModified"> + <th mat-header-cell *matHeaderCellDef> + {{ 'Created' | translate }} + </th> + <td mat-cell *matCellDef="let adapter"> + <h5> + {{ adapter.createdAt | date: 'dd.MM.yyyy HH:mm' }} + </h5> + </td> + </ng-container> - <!-- <ng-container matColumnDef="action">--> - <!-- <th mat-header-cell *matHeaderCellDef></th>--> - <!-- <td mat-cell *matCellDef="let adapter">--> - <!-- <div fxLayout="row" fxLayoutAlign="end center">--> - <!-- <button--> - <!-- color="accent"--> - <!-- mat-icon-button--> - <!-- matTooltip="Show details"--> - <!-- matTooltipPosition="above"--> - <!-- data-cy="details-adapter"--> - <!-- (click)="--> - <!-- navigateToDetailsOverviewPage(adapter)--> - <!-- "--> - <!-- >--> - <!-- <i class="material-icons">search</i>--> - <!-- </button>--> + <ng-container matColumnDef="messagesSent"> + <th + mat-header-cell + *matHeaderCellDef + matTooltip="Messages sent since last start" + > + #{{ 'Messages' | translate }} + </th> + <td mat-cell *matCellDef="let adapter"> + <sp-label + size="small" + [labelText]=" + adapterMetrics[adapter.elementId]?.messagesOut + .counter || 0 + " + ></sp-label> + </td> + </ng-container> - <!-- <button--> - <!-- color="accent"--> - <!-- mat-icon-button--> - <!-- matTooltip="Edit adapter"--> - <!-- data-cy="edit-adapter"--> - <!-- matTooltipPosition="above"--> - <!-- (click)="editAdapter(adapter)"--> - <!-- >--> - <!-- <i class="material-icons">edit</i>--> - <!-- </button>--> - <!-- <button--> - <!-- color="accent"--> - <!-- mat-icon-button--> - <!-- matTooltip="Manage permissions"--> - <!-- matTooltipPosition="above"--> - <!-- data-cy="open-manage-permissions"--> - <!-- *ngIf="isAdmin"--> - <!-- (click)="showPermissionsDialog(adapter)"--> - <!-- >--> - <!-- <i class="material-icons">share</i>--> - <!-- </button>--> - <!-- <button--> - <!-- color="accent"--> - <!-- mat-icon-button--> - <!-- matTooltip="Delete adapter"--> - <!-- data-cy="delete-adapter"--> - <!-- matTooltipPosition="above"--> - <!-- (click)="deleteAdapter(adapter)"--> - <!-- >--> - <!-- <i class="material-icons">delete</i>--> - <!-- </button>--> - <!-- </div>--> - <!-- </td>--> - <!-- </ng-container>--> - </sp-table> - </div> + <ng-container matColumnDef="lastMessage"> + <th mat-header-cell *matHeaderCellDef> + {{ 'Last message' | translate }} + </th> + <td mat-cell *matCellDef="let adapter"> + <h5> + {{ + adapterMetrics[adapter.elementId] && + adapterMetrics[adapter.elementId] + .lastTimestamp > 0 + ? (adapterMetrics[adapter.elementId] + .lastTimestamp + | date: 'dd.MM.yyyy HH:mm') + : 'n/a' + }} + </h5> + </td> + </ng-container> + + <ng-template spTableActions let-element> + <button + mat-menu-item + data-cy="details-adapter" + (click)="navigateToDetailsOverviewPage(element)" + > + <mat-icon>visibility</mat-icon> + <span>{{ 'Show' | translate }}</span> + </button> + <button + mat-menu-item + data-cy="edit-adapter" + (click)="editAdapter(element)" + > + <mat-icon>edit</mat-icon> + <span>{{ 'Edit' | translate }}</span> + </button> + <button + mat-menu-item + data-cy="open-manage-permissions" + *ngIf="isAdmin" + (click)="showPermissionsDialog(element)" + > + <mat-icon>share</mat-icon> + <span>{{ 'Manage permissions' | translate }}</span> + </button> + <button + mat-menu-item + data-cy="delete-adapter" + (click)="deleteAdapter(element)" + > + <mat-icon>delete</mat-icon> + <span>{{ 'Delete' | translate }}</span> + </button> + </ng-template> + </sp-table> </div> - </sp-basic-view> -</sp-asset-browser> + </div> +</sp-basic-view> diff --git a/ui/src/app/core/components/bars.scss b/ui/src/app/core/components/bars.scss deleted file mode 100644 index c81c822360..0000000000 --- a/ui/src/app/core/components/bars.scss +++ /dev/null @@ -1,34 +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. - * - */ - -.sp-navbar-item-selected { - background: var(--color-navigation-bg-selected); -} - -.sp-navbar-icon { - color: var(--color-navigation-link-text); - background: var(--color-bg-navbar-icon); -} -.sp-navbar-icon-selected { - color: var(--color-navigation-selected); -} - -.sp-navbar-item:hover { - color: var(--color-navigation-selected); - background: var(--color-navigation-hover); -} diff --git a/ui/src/app/core/components/breadcrumb/breadcrumb.component.scss b/ui/src/app/core/components/breadcrumb/breadcrumb.component.scss index 6649e25cff..ae16cff034 100644 --- a/ui/src/app/core/components/breadcrumb/breadcrumb.component.scss +++ b/ui/src/app/core/components/breadcrumb/breadcrumb.component.scss @@ -17,11 +17,10 @@ */ .breadcrumb-outer { - margin: 10px 10px 0 10px; max-height: 20px; height: 20px; font-size: 10pt; - color: var(--color-primary); + color: var(--color-toolbar-text); } .divider { @@ -31,6 +30,6 @@ } .bc-item:hover { - color: var(--color-secondary); + color: var(--color-toolbar-text-hover); cursor: pointer; } diff --git a/ui/src/app/core/components/iconbar/iconbar.component.html b/ui/src/app/core/components/iconbar/iconbar.component.html index e90d1b9d12..f7a3d2b104 100644 --- a/ui/src/app/core/components/iconbar/iconbar.component.html +++ b/ui/src/app/core/components/iconbar/iconbar.component.html @@ -16,36 +16,176 @@ ~ --> -<div style="padding-top: 0" fxFlex="100" fxLayout="column"> - @for (item of menu; track item.title) { - @if (item.visible) { - <div - style="min-width: 0; padding: 5px 0" - [ngClass]=" - item.link === activePage - ? 'sp-navbar-item-selected' - : 'sp-navbar-item' - " +<div class="menubar-root" [class.collapsed]="isCollapsed()" fxLayout="column"> + <!-- Header / brand --> + <div + class="menubar-header" + fxLayout="row" + fxLayoutAlign="space-between center" + > + <div class="brand" [class.hidden]="isCollapsed()"> + <img alt="logo" src="assets/img/sp/logo-navigation.png" /> + </div> + + <button + type="button" + class="collapse-btn" + (click)="toggleMenubar()" + [attr.aria-label]="isCollapsed() ? 'Expand menu' : 'Collapse menu'" + [title]="isCollapsed() ? 'Expand' : 'Collapse'" + > + <mat-icon class="collapse-icon" [class.rotated]="isCollapsed()" + >chevron_left</mat-icon > - <button - mat-icon-button - class="button-margin-iconbar iconbar-size" - (click)="go(item.link)" - matTooltip="{{ item.title }}" - matTooltipPosition="right" - > - <mat-icon - [ngClass]=" - item.link === activePage - ? 'sp-navbar-icon-selected' - : 'sp-navbar-icon' + </button> + </div> + + <!-- Scrollable content --> + <div class="menubar-scroll" fxFlex fxLayout="column"> + <!-- Top items (no group) --> + <div class="top-items" role="list"> + @for (item of topItems; track item.title) { + @if (item.visible !== false) { + <button + type="button" + role="listitem" + class="menu-item" + [class.menu-item-selected]="item.link === activePage" + (click)="go(item.link)" + [title]="isCollapsed() ? item.title : null" + [attr.aria-current]=" + item.link === activePage ? 'page' : null + " + > + <mat-icon class="item-icon">{{ item.icon }}</mat-icon> + <span + class="item-title text-ellipsis" + [class.hidden]="isCollapsed()" + >{{ item.title }}</span + > + </button> + } + } + </div> + + <!-- Grouped sections --> + <div class="groups"> + @for (group of menuGroups; track group.title) { + <section class="menu-group"> + <!-- Group header --> + <button + type="button" + [class.hidden]="isCollapsed()" + class="menu-group-title" + (click)="toggleGroup(group)" + [attr.aria-expanded]="!group.collapsed" + [title]="isCollapsed() ? group.title : null" + > + <span + class="group-title-text text-ellipsis" + [class.hidden]="isCollapsed()" + >{{ group.title }}</span + > + <mat-icon + class="chevron" + [class.rotate]="!group.collapsed" + [class.hidden]="isCollapsed()" + >expand_more</mat-icon + > + </button> + + <!-- Group items (accordion) --> + <div + class="menu-group-items" + [style.maxHeight]=" + group.collapsed && !isCollapsed() ? '0px' : '500px' " - [attr.data-cy]="'navigation-icon-' + item.link" + [class.instant]="isCollapsed()" + role="list" > - {{ item.icon }} - </mat-icon> - </button> - </div> - } - } + @for (item of group.items; track item.title) { + @if (item.visible !== false) { + <button + type="button" + role="listitem" + class="menu-item" + [class.menu-item-selected]=" + item.link === activePage + " + (click)="go(item.link)" + [title]="isCollapsed() ? item.title : null" + [attr.aria-current]=" + item.link === activePage ? 'page' : '' + " + > + <mat-icon class="item-icon">{{ + item.icon + }}</mat-icon> + <span + class="item-title text-ellipsis" + [class.hidden]="isCollapsed()" + >{{ item.title }}</span + > + </button> + } + } + </div> + </section> + @if (isCollapsed()) { + <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 f02f95dffc..9483c23acd 100644 --- a/ui/src/app/core/components/iconbar/iconbar.component.scss +++ b/ui/src/app/core/components/iconbar/iconbar.component.scss @@ -24,3 +24,254 @@ .mat-mdc-icon-button.iconbar-size mat-icon { margin-top: 5px; } + +.md-toolbar-tools { + width: 100%; + align-items: center; +} + +.menu-bar { + margin-top: 20px; + margin-left: 10px; + margin-right: 10px; +} + +.iconbar-item:hover { + //background: var(--color-bg-1); + cursor: pointer; +} + +.menubar-root { + --mb-width: 260px; + --mb-width-collapsed: 62px; + --mb-radius: 18px; + --mb-gap: 10px; + --mb-item-h: 34px; + --mb-surface: color-mix(in oklab, var(--color-menubar-bg) 90%, transparent); + --mb-surface-2: color-mix( + in oklab, + var(--color-menubar-bg) 75%, + transparent + ); + --mb-outline: rgba(255, 255, 255, 0.08); + --mb-outline-strong: rgba(255, 255, 255, 0.14); + --mb-text-dim: var(--color-primary); + + position: sticky; + top: 0; + height: 100vh; + width: var(--mb-width); + background: var(--mb-surface); + backdrop-filter: saturate(140%) blur(8px); + -webkit-backdrop-filter: saturate(140%) blur(8px); + box-shadow: + 0 10px 30px rgba(0, 0, 0, 0.25), + inset -1px 0 0 var(--mb-outline); + color: var(--color-menubar-link-text); + display: flex; + flex-direction: column; + transition: width 0.22s ease; + z-index: 3; +} + +.menubar-root.collapsed { + width: var(--mb-width-collapsed); +} + +/* Header */ +.menubar-header { + height: var(--mat-toolbar-standard-height); + padding: 0 10px 0 12px; + border-bottom: 1px solid var(--mb-outline); + display: flex; + align-items: center; + gap: 8px; + //background: var(--color-bg-1); +} + +.brand img { + max-width: 100%; + display: block; + padding: 10px 0px; +} + +.collapse-btn { + border: 0; + background: transparent; + width: 36px; + height: 36px; + border-radius: 10px; + display: grid; + place-items: center; + cursor: pointer; + transition: background 0.18s ease; +} + +.collapse-btn:hover { + background: var(--mb-surface-2); +} + +.collapse-icon { + transition: transform 0.22s ease; +} + +.menubar-root.collapsed .collapse-icon.rotated { + transform: rotate(180deg); +} + +.hidden { + display: none !important; +} + +/* Scroll area */ +.menubar-scroll { + padding: 12px 8px 14px; + overflow-y: auto; + overscroll-behavior: contain; +} + +/* Ungrouped items */ +.top-items { + margin-top: 32px; +} + +/* Group header */ +.menu-group { + margin: 6px 0 10px; +} + +.menu-group-title { + width: 100%; + border: 0; + background: var(--color-menubar-bg-menu-group); + color: var(--color-menubar-text-menu-group); + font-size: 0.75rem; + letter-spacing: 0.05em; + text-transform: uppercase; + padding: 8px 10px; + border-radius: var(--mb-radius); + display: flex; + align-items: center; + gap: 8px; + cursor: pointer; + transition: + background 0.18s ease, + color 0.18s ease; +} + +.menu-group-title:hover { + background: var(--color-menubar-bg-menu-group-hover); + outline-color: var(--mb-outline-strong); +} + +.group-icon { + font-size: 18px; + width: 18px; + height: 18px; + opacity: 0.9; +} + +.chevron { + margin-left: auto; + transition: + transform 0.22s ease, + opacity 0.18s ease; + opacity: 0.9; +} + +.chevron.rotate { + transform: rotate(180deg); +} + +/* Accordion container */ +.menu-group-items { + margin-top: 4px; + overflow: hidden; + transition: max-height 0.22s ease; +} + +.menu-group-items.instant { + transition: none; +} + +/* Menu item button */ +.menu-item { + width: 100%; + height: var(--mb-item-h); + padding: 8px 10px; + margin: 3px 0; + border: 0; + border-radius: calc(var(--mb-radius) + 2px); + background: transparent; + display: inline-flex; + align-items: center; + gap: 12px; + cursor: pointer; + text-align: left; + transition: + background 0.18s ease, + transform 0.06s ease, + outline-color 0.18s ease; + outline: 1px solid transparent; + color: var(--color-menubar-text-menu-item); +} + +.menu-item:hover { + background: var(--color-menubar-bg-menu-item-hover); + outline-color: var(--mb-outline-strong); + color: var(--color-menubar-text-menu-item-hover); +} + +.menu-item:active { + transform: translateY(1px); +} + +.menu-item-selected { + background: color-mix( + in oklab, + var(--color-menubar-bg-menu-item-selected) 95%, + transparent + ); + outline-color: var(--mb-outline-strong); + //box-shadow: + // inset 0 0 0 1px rgba(255,255,255,.05), + // 0 6px 16px rgba(0,0,0,.18); +} + +/* Icon + text + badge */ +.item-icon { + font-size: 18px; + width: 18px; + height: 18px; + opacity: 0.96; + margin-left: auto; + margin-right: auto; +} + +.item-title { + font-size: 0.86rem; + flex: 1 1 auto; +} + +.text-ellipsis { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* Collapsed (icon-only) refinements */ +.menubar-root.collapsed .iconbar-item-btn { + justify-content: center; +} + +.menubar-root.collapsed .menu-group-title { + justify-content: center; + padding: 10px 0; +} + +/* Responsive hint (optional) */ +@media (max-width: 960px) { + :root { + --mb-width: 248px; + } +} diff --git a/ui/src/app/core/components/iconbar/iconbar.component.ts b/ui/src/app/core/components/iconbar/iconbar.component.ts index 7bef05b7b2..c02d09ea20 100644 --- a/ui/src/app/core/components/iconbar/iconbar.component.ts +++ b/ui/src/app/core/components/iconbar/iconbar.component.ts @@ -22,7 +22,7 @@ import { BaseNavigationComponent } from '../base-navigation.component'; @Component({ selector: 'sp-iconbar', templateUrl: './iconbar.component.html', - styleUrls: ['./iconbar.component.scss', '../bars.scss'], + styleUrls: ['./iconbar.component.scss'], standalone: false, }) export class IconbarComponent diff --git a/ui/src/app/core/components/streampipes/streampipes.component.html b/ui/src/app/core/components/streampipes/streampipes.component.html index 1b78eef964..b9465e5f01 100644 --- a/ui/src/app/core/components/streampipes/streampipes.component.html +++ b/ui/src/app/core/components/streampipes/streampipes.component.html @@ -20,16 +20,39 @@ [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"> + <div class="layout-container"> + <!-- Left Menubar --> + <div class="menubar"> <sp-iconbar></sp-iconbar> </div> - <div style="width: 100%; height: 100%; overflow-y: auto" class="base"> - <sp-breadcrumb></sp-breadcrumb> - <router-outlet></router-outlet> + + <!-- Right Section (Toolbar + Main Content) --> + <div class="main-section"> + <div class="sp-toolbar-outer"> + <sp-toolbar></sp-toolbar> + </div> + + <div class="content"> + <router-outlet></router-outlet> + </div> </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 4a8cc83c9f..4f4b41c6f0 100644 --- a/ui/src/app/core/components/streampipes/streampipes.component.scss +++ b/ui/src/app/core/components/streampipes/streampipes.component.scss @@ -16,16 +16,62 @@ * */ -.iconbar { - background: var(--color-navigation-bg); - width: var(--iconbar-width); +.layout-container { + display: flex; + flex-direction: row; + height: 100vh; } -.base { +.menubar { + background: var(--color-bg-1); color: var(--color-default-text); - background: var(--color-bg-outer); + display: flex; + flex-direction: column; + border-right: 1px solid var(--color-bg-3); + box-shadow: 0 -4px 0 0 #000; +} + +.menu-header { + text-align: center; + padding: 10px 0; + font-weight: 600; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); +} + +.menu-items { + flex-grow: 1; + display: flex; + flex-direction: column; + padding-top: 10px; +} + +.menu-item { + padding: 10px 20px; + cursor: pointer; + transition: background 0.2s ease; + font-size: 14px; +} + +.menu-item:hover { + background: rgba(255, 255, 255, 0.1); +} + +/* Right side layout */ +.main-section { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + background: var(--color-bg-1); } .sp-toolbar-outer { - height: 40px; + background: var(--color-toolbar-bg); +} + +.content { + flex-grow: 1; + overflow-y: auto; + background: var(--color-bg-1); + color: var(--color-default-text); } diff --git a/ui/src/app/core/components/toolbar/toolbar.component.html b/ui/src/app/core/components/toolbar/toolbar.component.html index d9f404272d..960d5f75b6 100644 --- a/ui/src/app/core/components/toolbar/toolbar.component.html +++ b/ui/src/app/core/components/toolbar/toolbar.component.html @@ -16,32 +16,10 @@ ~ --> -<mat-toolbar class="md-primary md-hue-2 top-nav toolbar-bg"> +<mat-toolbar class="md-primary md-hue-2 toolbar"> <div class="md-toolbar-tools sp-toolbar"> <div fxFlex="100" fxLayout fxLayoutAlign="start center"> - <div - class="md-toolbar-tools" - style="height: 40px; max-height: 40px" - fxFlex - fxLayout="row" - fxLayoutAlign="start center" - > - <div - style=" - padding: 5px; - border-radius: 0px; - margin-right: 15px; - position: relative; - left: 20px; - " - > - <img - alt="icon" - src="assets/img/sp/logo-navigation.png" - style="max-height: 30px; max-width: 250px" - /> - </div> - </div> + <sp-breadcrumb></sp-breadcrumb> <div fxFlex fxLayout @@ -106,26 +84,22 @@ mat-icon-button fxLayout fxLayoutAlign="center center" - [ngClass]=" + [class.sp-navbar-icon-button-selected]=" accountMenuOpen.menuOpen - ? 'sp-icon-button-no-hover' - : 'sp-icon-button' " - style="min-width: 0px" + style="min-width: 0" [matMenuTriggerFor]="menu" #accountMenuOpen="matMenuTrigger" matTooltip="User Preferences" matTooltipPosition="below" data-cy="sp-user-preferences" > - <i - [ngClass]=" - accountMenuOpen.menuOpen - ? 'sp-navbar-icon-selected' - : 'sp-navbar-icon' + <mat-icon + class="sp-navbar-icon" + [class.sp-navbar-icon-selected]=" + accountMenuOpen " - class="material-icons" - >account_circle</i + >account_circle</mat-icon > </button> </div> diff --git a/ui/src/app/core/components/toolbar/toolbar.component.scss b/ui/src/app/core/components/toolbar/toolbar.component.scss index 1e8ec24281..1b2256c490 100644 --- a/ui/src/app/core/components/toolbar/toolbar.component.scss +++ b/ui/src/app/core/components/toolbar/toolbar.component.scss @@ -24,6 +24,18 @@ height: 41px; } +.sp-navbar-icon-selected { + color: var(--color-toolbar-icon-selected); +} + +.sp-navbar-icon { + color: var(--color-toolbar-icon); +} + +.sp-navbar-icon:hover { + color: var(--color-toolbar-icon-hover); +} + .current-user { color: var(--color-default-text); display: block; @@ -36,9 +48,8 @@ position: relative; } -.toolbar-bg { - background: var(--color-navigation-bg); - border-bottom: 2px solid var(--color-navigation-divider); +.toolbar { + background: var(--color-bg-1); color: var(--color-navigation-text); } diff --git a/ui/src/app/core/components/toolbar/toolbar.component.ts b/ui/src/app/core/components/toolbar/toolbar.component.ts index af568ad5c2..249fc23871 100644 --- a/ui/src/app/core/components/toolbar/toolbar.component.ts +++ b/ui/src/app/core/components/toolbar/toolbar.component.ts @@ -31,7 +31,7 @@ import { LoginService } from '../../../login/services/login.service'; @Component({ selector: 'sp-toolbar', templateUrl: './toolbar.component.html', - styleUrls: ['./toolbar.component.scss', '../bars.scss'], + styleUrls: ['./toolbar.component.scss'], standalone: false, }) export class ToolbarComponent diff --git a/ui/src/app/core/navigation.backup b/ui/src/app/core/navigation.backup new file mode 100644 index 0000000000..0888e86b31 --- /dev/null +++ b/ui/src/app/core/navigation.backup @@ -0,0 +1,238 @@ +/* + * 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 { NavigationEnd, Router } from '@angular/router'; +import { PageName } from '../../_enums/page-name.enum'; +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'; + +export interface MenuItem { + link: string; + title: string; + icon: string; + pageNames: any[]; + privileges: any[]; + visible?: boolean; +} + +export interface MenuGroup { + title: string; + items: MenuItem[]; + collapsed?: boolean; +} + +export interface MenuCategory { + id: string; + title: string; + collapsed: boolean; +} + +export abstract class BaseNavigationComponent { + + activePageName: string; + 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 + }, + ] + + public menu = [ + { + link: '', + title: 'Home', + icon: 'home', + pageNames: [PageName.HOME], + privileges: [], + visible: false, + category: 'top' + }, + { + link: 'pipelines', + title: 'Pipelines', + icon: 'play_arrow', + pageNames: [PageName.PIPELINE_OVERVIEW], + privileges: [UserPrivilege.PRIVILEGE_READ_PIPELINE, UserPrivilege.PRIVILEGE_WRITE_PIPELINE], + visible: false, + category: 'analytics' + }, + { + link: 'connect', + title: 'Connect', + icon: 'power', + pageNames: [PageName.CONNECT], + privileges: [UserPrivilege.PRIVILEGE_READ_ADAPTER, UserPrivilege.PRIVILEGE_WRITE_ADAPTER], + visible: false, + category: 'management' + }, + { + link: 'dashboard', + title: 'Dashboard', + icon: 'dashboard', + pageNames: [PageName.DASHBOARD], + privileges: [UserPrivilege.PRIVILEGE_READ_DASHBOARD, UserPrivilege.PRIVILEGE_WRITE_DASHBOARD], + visible: false, + category: 'visualization' + }, + { + link: 'dataexplorer', + title: 'Data Explorer', + icon: 'query_stats', + pageNames: [PageName.DATA_EXPLORER], + privileges: [UserPrivilege.PRIVILEGE_READ_DATA_EXPLORER_VIEW, UserPrivilege.PRIVILEGE_WRITE_DATA_EXPLORER_VIEW], + visible: false, + category: 'visualization' + }, + { + link: 'assets', + title: 'Asset Management', + icon: 'precision_manufacturing', + pageNames: [PageName.ASSETS], + privileges: [UserPrivilege.PRIVILEGE_READ_ASSETS, UserPrivilege.PRIVILEGE_WRITE_ASSETS], + visible: false, + category: 'management' + }, + { + link: 'configuration', + title: 'Configuration', + icon: 'settings', + pageNames: [PageName.SETTINGS], + privileges: [UserPrivilege.PRIVILEGE_WRITE_ASSETS, UserPrivilege.PRIVILEGE_WRITE_LABELS, UserPrivilege.PRIVILEGE_WRITE_FILES], + visible: false, + category: 'management' + }, + ]; + + + + + protected authService = inject(AuthService); + protected currentUserService = inject(CurrentUserService); + protected router = inject(Router); + protected appConstants = inject(AppConstants); + + onInit() { + this.currentUserService.user$.subscribe(user => { + this.menu.forEach(m => m.visible = this.isNavItemVisible(m.privileges)); + const menuGroups = this.categories.map(category => { + return { + title: category.title, + collapsed: category.collapsed, + items: this.menu.filter(m => m.category === category.id && m.visible) + } + }).filter(m => m.items.length > 0); + + this.topItems = menuGroups.find(m => m.title === 'top')?.items || []; + this.menuGroups = menuGroups.filter(m => m.title !== 'top'); + this.notificationsVisible = this.isNavItemVisible([ + UserPrivilege.PRIVILEGE_READ_PIPELINE, + UserPrivilege.PRIVILEGE_WRITE_PIPELINE + ]); + }); + this.activePage = this.router.url.replace('/', ''); + this.activePageName = this.getPageTitle(this.activePage); + this.router.events.subscribe(event => { + if (event instanceof NavigationEnd) { + this.activePage = event.url.split('/')[1]; + this.activePageName = this.getPageTitle(this.activePage); + } + }); + } + + getActivePage() { + return this.activePage; + } + + getPageTitle(path) { + let currentTitle = this.appConstants.APP_NAME; + this.menu.forEach(m => { + if (m.link === path) { + currentTitle = m.title; + } + }); + if (path === 'pipeline-details') { + currentTitle = 'Pipeline Details'; + } + return currentTitle; + } + + go(path, payload?) { + if (payload === undefined) { + this.router.navigateByUrl(path); + this.activePage = path; + } else { + this.router.navigateByUrl(path, payload); + this.activePage = path; + } + this.activePageName = this.getPageTitle(this.activePage); + } + + public isNavItemVisible(privileges: string[]): boolean { + 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; + } + } + +} 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 843bdd968b..01afecf4f5 100644 --- a/ui/src/app/dashboard/components/overview/dashboard-overview.component.html +++ b/ui/src/app/dashboard/components/overview/dashboard-overview.component.html @@ -16,37 +16,30 @@ ~ --> -<sp-asset-browser - filteredAssetLinkType="dashboard" - allResourcesAlias="Dashboards" - [resourceCount]="resourceCount" - (filterIdsEmitter)="applyDashboardFilters($event)" -> - <sp-basic-view [showBackLink]="false" [padding]="true"> - <div - nav - fxFlex="100" - fxLayoutAlign="start center" - fxLayout="row" - class="pl-10" +<sp-basic-view [showBackLink]="false" [padding]="true"> + <div + nav + fxFlex="100" + fxLayoutAlign="start center" + fxLayout="row" + class="pl-10" + > + <button + mat-button + mat-flat-button + color="accent" + data-cy="open-new-dashboard-dialog" + (click)="openNewDashboardDialog()" + class="mr-10" + *ngIf="hasDashboardWritePrivileges" > - <button - mat-button - mat-flat-button - color="accent" - data-cy="open-new-dashboard-dialog" - (click)="openNewDashboardDialog()" - class="mr-10" - *ngIf="hasDashboardWritePrivileges" - > - <i class="material-icons">add</i> - <span>{{ 'New dashboard' | translate }}</span> - </button> - </div> - <div fxFlex="100" fxLayout="column"> - <sp-dashboard-overview-table - (resourceCountEmitter)="resourceCount = $event" - ></sp-dashboard-overview-table> - </div> - </sp-basic-view> -</sp-asset-browser> + <i class="material-icons">add</i> + <span>{{ 'New dashboard' | translate }}</span> + </button> + </div> + <div fxFlex="100" fxLayout="column"> + <sp-dashboard-overview-table + (resourceCountEmitter)="resourceCount = $event" + ></sp-dashboard-overview-table> + </div> +</sp-basic-view> 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 26d9e355d8..ff21cb9392 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 @@ -16,37 +16,30 @@ ~ --> -<sp-asset-browser - filteredAssetLinkType="chart" - [allResourcesAlias]="'Charts' | translate" - [resourceCount]="resourceCount" - (filterIdsEmitter)="applyChartFilters($event)" -> - <sp-basic-view [showBackLink]="false" [padding]="true"> - <div - nav - fxFlex="100" - fxLayoutAlign="start center" - fxLayout="row" - class="pl-10" +<sp-basic-view [showBackLink]="false" [padding]="true"> + <div + nav + fxFlex="100" + fxLayoutAlign="start center" + fxLayout="row" + class="pl-10" + > + <button + mat-button + mat-flat-button + color="accent" + data-cy="open-new-data-view" + (click)="createNewDataView()" + class="mr-10" + *ngIf="hasDataExplorerWritePrivileges" > - <button - mat-button - mat-flat-button - color="accent" - data-cy="open-new-data-view" - (click)="createNewDataView()" - class="mr-10" - *ngIf="hasDataExplorerWritePrivileges" - > - <i class="material-icons">add</i> - <span>{{ 'New chart' | translate }}</span> - </button> - </div> - <div fxFlex="100" fxLayout="column"> - <sp-data-explorer-overview-table - (resourceCountEmitter)="resourceCount = $event" - ></sp-data-explorer-overview-table> - </div> - </sp-basic-view> -</sp-asset-browser> + <i class="material-icons">add</i> + <span>{{ 'New chart' | translate }}</span> + </button> + </div> + <div fxFlex="100" fxLayout="column"> + <sp-data-explorer-overview-table + (resourceCountEmitter)="resourceCount = $event" + ></sp-data-explorer-overview-table> + </div> +</sp-basic-view> diff --git a/ui/src/app/pipelines/pipelines.component.html b/ui/src/app/pipelines/pipelines.component.html index 150c514dbc..5ec8323d18 100644 --- a/ui/src/app/pipelines/pipelines.component.html +++ b/ui/src/app/pipelines/pipelines.component.html @@ -16,106 +16,95 @@ ~ --> -<sp-asset-browser - filteredAssetLinkType="pipeline" - allResourcesAlias="Pipelines" - [resourceCount]="pipelines.length" - (filterIdsEmitter)="applyPipelineFilters($event)" -> - <sp-basic-view [showBackLink]="false" [padding]="true"> - <div - nav - fxFlex="100" - fxLayoutAlign="start center" - fxLayout="row" - fxLayoutGap="10px" - class="pl-10" +<sp-basic-view [showBackLink]="false" [padding]="true"> + <div + nav + fxFlex="100" + fxLayoutAlign="start center" + fxLayout="row" + fxLayoutGap="10px" + class="pl-10" + > + <button + mat-button + mat-flat-button + color="accent" + *ngIf="hasPipelineWritePrivileges" + (click)="navigateToPipelineEditor()" + data-cy="pipelines-navigate-to-editor" > - <button - mat-button - mat-flat-button - color="accent" - *ngIf="hasPipelineWritePrivileges" - (click)="navigateToPipelineEditor()" - data-cy="pipelines-navigate-to-editor" - > - <i class="material-icons">add</i> {{ - 'New Pipeline' | translate - }} - </button> - <button - mat-flat-button - class="mat-basic" - (click)="startAllPipelines(true)" - [disabled]="checkCurrentSelectionStatus(false)" - *ngIf="hasPipelineWritePrivileges" - > - <mat-icon>play_arrow</mat-icon> - <span>{{ 'Start All Pipelines' | translate }}</span> - </button> - <button - mat-flat-button - class="mat-basic" - (click)="startAllPipelines(false)" - [disabled]="checkCurrentSelectionStatus(true)" - *ngIf="hasPipelineWritePrivileges" - > - <mat-icon>stop</mat-icon> - <span>{{ 'Stop all pipelines' | translate }}</span> - </button> - <span fxFlex></span> - <button - mat-icon-button - color="accent" - (click)="startPipelineTour()" - [matTooltip]="'Tutorial'" - [disabled]="tutorialActive" - > - <i class="material-icons"> school </i> - </button> - <button - mat-icon-button - color="accent" - matTooltip="Refresh pipelines" - matTooltipPosition="above" - (click)="getPipelines()" - > - <i class="material-icons"> refresh </i> - </button> - </div> - <div fxFlex="100" fxLayout="column"> - <div fxLayout="column"> + <i class="material-icons">add</i> {{ + 'New Pipeline' | translate + }} + </button> + <button + mat-flat-button + class="mat-basic" + (click)="startAllPipelines(true)" + [disabled]="checkCurrentSelectionStatus(false)" + *ngIf="hasPipelineWritePrivileges" + > + <mat-icon>play_arrow</mat-icon> + <span>{{ 'Start All Pipelines' | translate }}</span> + </button> + <button + mat-flat-button + class="mat-basic" + (click)="startAllPipelines(false)" + [disabled]="checkCurrentSelectionStatus(true)" + *ngIf="hasPipelineWritePrivileges" + > + <mat-icon>stop</mat-icon> + <span>{{ 'Stop all pipelines' | translate }}</span> + </button> + <span fxFlex></span> + <button + mat-icon-button + color="accent" + (click)="startPipelineTour()" + [matTooltip]="'Tutorial'" + [disabled]="tutorialActive" + > + <i class="material-icons"> school </i> + </button> + <button + mat-icon-button + color="accent" + matTooltip="Refresh pipelines" + matTooltipPosition="above" + (click)="getPipelines()" + > + <i class="material-icons"> refresh </i> + </button> + </div> + <div fxFlex="100" fxLayout="column"> + <div fxLayout="column"> + <sp-basic-header-title-component + title="Pipelines" + ></sp-basic-header-title-component> + <div fxFlex="100" fxLayout="row" fxLayoutAlign="center start"> + <div fxFlex="100"> + <sp-pipeline-overview + [pipelines]="filteredPipelines" + (refreshPipelinesEmitter)="getPipelines()" + *ngIf="pipelinesReady" + ></sp-pipeline-overview> + </div> + </div> + <div fxFlex="100" fxLayout="column" style="margin-top: 20px"> <sp-basic-header-title-component - title="Pipelines" + title="Functions" ></sp-basic-header-title-component> <div fxFlex="100" fxLayout="row" fxLayoutAlign="center start"> <div fxFlex="100"> - <sp-pipeline-overview - [pipelines]="filteredPipelines" - (refreshPipelinesEmitter)="getPipelines()" - *ngIf="pipelinesReady" - ></sp-pipeline-overview> - </div> - </div> - <div fxFlex="100" fxLayout="column" style="margin-top: 20px"> - <sp-basic-header-title-component - title="Functions" - ></sp-basic-header-title-component> - <div - fxFlex="100" - fxLayout="row" - fxLayoutAlign="center start" - > - <div fxFlex="100"> - <sp-functions-overview - [functions]="functions" - *ngIf="functionsReady" - > - </sp-functions-overview> - </div> + <sp-functions-overview + [functions]="functions" + *ngIf="functionsReady" + > + </sp-functions-overview> </div> </div> </div> </div> - </sp-basic-view> -</sp-asset-browser> + </div> +</sp-basic-view> diff --git a/ui/src/scss/sp/layout.scss b/ui/src/scss/sp/layout.scss index 6b736888c5..0a7ae5aba8 100644 --- a/ui/src/scss/sp/layout.scss +++ b/ui/src/scss/sp/layout.scss @@ -139,8 +139,8 @@ .page-container-nav { line-height: 24px; height: 44px; - border-bottom: 1px solid var(--color-bg-3); - background: var(--color-bg-main-panel-header); + //border-bottom: 1px solid var(--color-bg-3); + //background: var(--color-bg-main-panel-header); } .text-center { diff --git a/ui/src/scss/sp/main.scss b/ui/src/scss/sp/main.scss index 474e00fce5..2b6de54f6c 100644 --- a/ui/src/scss/sp/main.scss +++ b/ui/src/scss/sp/main.scss @@ -57,11 +57,6 @@ pre.version { border: 0; } -.md-toolbar-tools { - width: 100%; - align-items: center; -} - code[class*='language-'], pre[class*='language-'] { white-space: pre-wrap; @@ -152,7 +147,7 @@ md-content { .page-container { margin: 10px; - border: 1px solid var(--color-bg-3); + //border: 1px solid var(--color-bg-3); min-height: calc(100% - 60px); background: var(--color-bg-page-container); } @@ -196,11 +191,6 @@ md-content { border-bottom-right-radius: 20em; } -.top-nav { - height: 40px !important; - border-bottom: 0px solid var(--color-primary); -} - .angular-google-map-container { height: 400px; }
