]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: move monitoring tabs to a single page
authorPatrick Seidensal <pseidensal@suse.com>
Wed, 27 Nov 2019 20:34:12 +0000 (20:34 +0000)
committerPatrick Seidensal <pseidensal@suse.com>
Tue, 3 Dec 2019 15:54:24 +0000 (16:54 +0100)
with a tab for 'active alerts', 'all alerts' and 'silences'. Due to
ambiguity with existing names, `AlertListComponent` has been renamed to
`ActiveAlertListComponent`. Introduces `MonitoringListComponent` as
first page for monitoring concerns, using path `/monitoring`.

Keeps the activated tab open, independent of the way that's used to go
back to the previous page, be it the cancel button or submit button or
the link on the breadcrumb. Also keeps the active tab open even when the
page is reloaded.

Fixes: https://tracker.ceph.com/issues/42877
Signed-off-by: Patrick Seidensal <pseidensal@suse.com>
30 files changed:
src/pybind/mgr/dashboard/frontend/e2e/cluster/alerts.e2e-spec.ts [deleted file]
src/pybind/mgr/dashboard/frontend/e2e/cluster/alerts.po.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/cluster.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.html [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.scss [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.spec.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.html [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.scss [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.spec.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-form/silence-form.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-form/silence-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-list/silence-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-list/silence-list.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-list/silence-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/core/navigation/breadcrumbs/breadcrumbs.component.html
src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.html
src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.html
src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-action.ts

diff --git a/src/pybind/mgr/dashboard/frontend/e2e/cluster/alerts.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/e2e/cluster/alerts.e2e-spec.ts
deleted file mode 100644 (file)
index 51639d2..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-import { AlertsPageHelper } from './alerts.po';
-
-describe('Alerts page', () => {
-  let alerts: AlertsPageHelper;
-
-  beforeAll(() => {
-    alerts = new AlertsPageHelper();
-  });
-
-  afterEach(async () => {
-    await AlertsPageHelper.checkConsole();
-  });
-
-  describe('breadcrumb test', () => {
-    beforeAll(async () => {
-      await alerts.navigateTo();
-    });
-
-    it('should open and show breadcrumb', async () => {
-      await alerts.waitTextToBePresent(alerts.getBreadcrumb(), 'Alerts');
-    });
-  });
-});
diff --git a/src/pybind/mgr/dashboard/frontend/e2e/cluster/alerts.po.ts b/src/pybind/mgr/dashboard/frontend/e2e/cluster/alerts.po.ts
deleted file mode 100644 (file)
index 8a50662..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-import { PageHelper } from '../page-helper.po';
-
-export class AlertsPageHelper extends PageHelper {
-  pages = { index: '/#/alerts' };
-}
index a483ba78bd615f77a1d3a6a20c8616734db406ed..2913b293dc391e37c4c6604aedaa3932b361ca3c 100644 (file)
@@ -16,9 +16,8 @@ import { MgrModuleListComponent } from './ceph/cluster/mgr-modules/mgr-module-li
 import { MonitorComponent } from './ceph/cluster/monitor/monitor.component';
 import { OsdFormComponent } from './ceph/cluster/osd/osd-form/osd-form.component';
 import { OsdListComponent } from './ceph/cluster/osd/osd-list/osd-list.component';
-import { AlertListComponent } from './ceph/cluster/prometheus/alert-list/alert-list.component';
+import { MonitoringListComponent } from './ceph/cluster/prometheus/monitoring-list/monitoring-list.component';
 import { SilenceFormComponent } from './ceph/cluster/prometheus/silence-form/silence-form.component';
-import { SilenceListComponent } from './ceph/cluster/prometheus/silence-list/silence-list.component';
 import { ServicesComponent } from './ceph/cluster/services/services.component';
 import { DashboardComponent } from './ceph/dashboard/dashboard/dashboard.component';
 import { Nfs501Component } from './ceph/nfs/nfs-501/nfs-501.component';
@@ -142,34 +141,31 @@ const routes: Routes = [
     data: { breadcrumbs: 'Cluster/Logs' }
   },
   {
-    path: 'alerts',
-    component: AlertListComponent,
+    path: 'monitoring',
     canActivate: [AuthGuardService],
-    data: { breadcrumbs: 'Cluster/Alerts' }
-  },
-  {
-    path: 'silence',
-    canActivate: [AuthGuardService],
-    data: { breadcrumbs: 'Cluster/Silences' },
+    data: { breadcrumbs: 'Cluster/Monitoring' },
     children: [
-      { path: '', component: SilenceListComponent },
       {
-        path: URLVerbs.CREATE,
+        path: '',
+        component: MonitoringListComponent
+      },
+      {
+        path: 'silence/' + URLVerbs.CREATE,
         component: SilenceFormComponent,
-        data: { breadcrumbs: ActionLabels.CREATE }
+        data: { breadcrumbs: `${ActionLabels.CREATE} Silence` }
       },
       {
-        path: `${URLVerbs.CREATE}/:id`,
+        path: `silence/${URLVerbs.CREATE}/:id`,
         component: SilenceFormComponent,
         data: { breadcrumbs: ActionLabels.CREATE }
       },
       {
-        path: `${URLVerbs.EDIT}/:id`,
+        path: `silence/${URLVerbs.EDIT}/:id`,
         component: SilenceFormComponent,
         data: { breadcrumbs: ActionLabels.EDIT }
       },
       {
-        path: `${URLVerbs.RECREATE}/:id`,
+        path: `silence/${URLVerbs.RECREATE}/:id`,
         component: SilenceFormComponent,
         data: { breadcrumbs: ActionLabels.RECREATE }
       }
index 666b216698a01867035dade965fd0f41f435f705..e3bae17c0cafe6ae6152fb46ed03cf46052b0fca 100644 (file)
@@ -42,8 +42,8 @@ import { OsdRecvSpeedModalComponent } from './osd/osd-recv-speed-modal/osd-recv-
 import { OsdReweightModalComponent } from './osd/osd-reweight-modal/osd-reweight-modal.component';
 import { OsdScrubModalComponent } from './osd/osd-scrub-modal/osd-scrub-modal.component';
 import { OsdSmartListComponent } from './osd/osd-smart-list/osd-smart-list.component';
-import { AlertListComponent } from './prometheus/alert-list/alert-list.component';
-import { PrometheusTabsComponent } from './prometheus/prometheus-tabs/prometheus-tabs.component';
+import { ActiveAlertListComponent } from './prometheus/active-alert-list/active-alert-list.component';
+import { MonitoringListComponent } from './prometheus/monitoring-list/monitoring-list.component';
 import { RulesListComponent } from './prometheus/rules-list/rules-list.component';
 import { SilenceFormComponent } from './prometheus/silence-form/silence-form.component';
 import { SilenceListComponent } from './prometheus/silence-list/silence-list.component';
@@ -101,11 +101,10 @@ import { ServicesComponent } from './services/services.component';
     LogsComponent,
     OsdRecvSpeedModalComponent,
     OsdPgScrubModalComponent,
-    AlertListComponent,
+    ActiveAlertListComponent,
     OsdRecvSpeedModalComponent,
     SilenceFormComponent,
     SilenceListComponent,
-    PrometheusTabsComponent,
     SilenceMatcherModalComponent,
     ServicesComponent,
     InventoryComponent,
@@ -117,7 +116,8 @@ import { ServicesComponent } from './services/services.component';
     OsdDevicesSelectionGroupsComponent,
     OsdCreationPreviewModalComponent,
     RulesListComponent,
-    AlertListComponent
+    ActiveAlertListComponent,
+    MonitoringListComponent
   ]
 })
 export class ClusterModule {}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.html
new file mode 100644 (file)
index 0000000..64a446c
--- /dev/null
@@ -0,0 +1,33 @@
+<cd-table [data]="prometheusAlertService.alerts"
+          [columns]="columns"
+          identifier="fingerprint"
+          [forceIdentifier]="true"
+          [customCss]="customCss"
+          selectionType="single"
+          (updateSelection)="updateSelection($event)">
+  <cd-table-actions class="table-actions"
+                    [permission]="permission"
+                    [selection]="selection"
+                    [tableActions]="tableActions">
+  </cd-table-actions>
+  <tabset cdTableDetail
+          *ngIf="selection.hasSingleSelection">
+    <tab i18n-heading
+         heading="Details">
+      <cd-table-key-value [renderObjects]="true"
+                          [hideEmpty]="true"
+                          [appendParentKey]="false"
+                          [data]="selection.first()"
+                          [customCss]="customCss"
+                          [autoReload]="false">
+      </cd-table-key-value>
+    </tab>
+  </tabset>
+</cd-table>
+
+<ng-template #externalLinkTpl
+             let-row="row"
+             let-value="value">
+  <a [href]="value"
+     target="_blank"><i [ngClass]="[icons.lineChart]"></i> Source</a>
+</ng-template>
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.scss
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.spec.ts
new file mode 100644 (file)
index 0000000..1b2c18a
--- /dev/null
@@ -0,0 +1,107 @@
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+
+import { TabsModule } from 'ngx-bootstrap/tabs';
+import { ToastrModule } from 'ngx-toastr';
+
+import {
+  configureTestBed,
+  i18nProviders,
+  PermissionHelper
+} from '../../../../../testing/unit-test-helper';
+import { CoreModule } from '../../../../core/core.module';
+import { TableActionsComponent } from '../../../../shared/datatable/table-actions/table-actions.component';
+import { SharedModule } from '../../../../shared/shared.module';
+import { CephModule } from '../../../ceph.module';
+import { DashboardModule } from '../../../dashboard/dashboard.module';
+import { ClusterModule } from '../../cluster.module';
+import { ActiveAlertListComponent } from './active-alert-list.component';
+
+describe('ActiveAlertListComponent', () => {
+  let component: ActiveAlertListComponent;
+  let fixture: ComponentFixture<ActiveAlertListComponent>;
+
+  configureTestBed({
+    imports: [
+      HttpClientTestingModule,
+      TabsModule.forRoot(),
+      RouterTestingModule,
+      ToastrModule.forRoot(),
+      SharedModule,
+      ClusterModule,
+      DashboardModule,
+      CephModule,
+      CoreModule
+    ],
+    declarations: [],
+    providers: [i18nProviders]
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ActiveAlertListComponent);
+    component = fixture.componentInstance;
+  });
+
+  it('should create', () => {
+    fixture.detectChanges();
+    expect(component).toBeTruthy();
+  });
+
+  it('should test all TableActions combinations', () => {
+    component.ngOnInit();
+    const permissionHelper: PermissionHelper = new PermissionHelper(component.permission);
+    const tableActions: TableActionsComponent = permissionHelper.setPermissionsAndGetActions(
+      component.tableActions
+    );
+
+    expect(tableActions).toEqual({
+      'create,update,delete': {
+        actions: ['Create Silence'],
+        primary: {
+          multiple: 'Create Silence',
+          executing: 'Create Silence',
+          single: 'Create Silence',
+          no: 'Create Silence'
+        }
+      },
+      'create,update': {
+        actions: ['Create Silence'],
+        primary: {
+          multiple: 'Create Silence',
+          executing: 'Create Silence',
+          single: 'Create Silence',
+          no: 'Create Silence'
+        }
+      },
+      'create,delete': {
+        actions: ['Create Silence'],
+        primary: {
+          multiple: 'Create Silence',
+          executing: 'Create Silence',
+          single: 'Create Silence',
+          no: 'Create Silence'
+        }
+      },
+      create: {
+        actions: ['Create Silence'],
+        primary: {
+          multiple: 'Create Silence',
+          executing: 'Create Silence',
+          single: 'Create Silence',
+          no: 'Create Silence'
+        }
+      },
+      'update,delete': {
+        actions: [],
+        primary: { multiple: '', executing: '', single: '', no: '' }
+      },
+      update: { actions: [], primary: { multiple: '', executing: '', single: '', no: '' } },
+      delete: { actions: [], primary: { multiple: '', executing: '', single: '', no: '' } },
+      'no-permissions': {
+        actions: [],
+        primary: { multiple: '', executing: '', single: '', no: '' }
+      }
+    });
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.ts
new file mode 100644 (file)
index 0000000..2f09b1d
--- /dev/null
@@ -0,0 +1,97 @@
+import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
+import { I18n } from '@ngx-translate/i18n-polyfill';
+import { CellTemplate } from '../../../../shared/enum/cell-template.enum';
+import { Icons } from '../../../../shared/enum/icons.enum';
+import { CdTableAction } from '../../../../shared/models/cd-table-action';
+import { CdTableColumn } from '../../../../shared/models/cd-table-column';
+import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
+import { Permission } from '../../../../shared/models/permissions';
+import { CdDatePipe } from '../../../../shared/pipes/cd-date.pipe';
+import { AuthStorageService } from '../../../../shared/services/auth-storage.service';
+import { PrometheusAlertService } from '../../../../shared/services/prometheus-alert.service';
+import { URLBuilderService } from '../../../../shared/services/url-builder.service';
+
+const BASE_URL = 'silence'; // as only silence actions can be used
+
+@Component({
+  selector: 'cd-active-alert-list',
+  providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }],
+  templateUrl: './active-alert-list.component.html',
+  styleUrls: ['./active-alert-list.component.scss']
+})
+export class ActiveAlertListComponent implements OnInit {
+  @ViewChild('externalLinkTpl', { static: true })
+  externalLinkTpl: TemplateRef<any>;
+  columns: CdTableColumn[];
+  tableActions: CdTableAction[];
+  permission: Permission;
+  selection = new CdTableSelection();
+  icons = Icons;
+  customCss = {
+    'badge badge-danger': 'active',
+    'badge badge-warning': 'unprocessed',
+    'badge badge-info': 'suppressed'
+  };
+
+  constructor(
+    // NotificationsComponent will refresh all alerts every 5s (No need to do it here as well)
+    private authStorageService: AuthStorageService,
+    public prometheusAlertService: PrometheusAlertService,
+    private urlBuilder: URLBuilderService,
+    private i18n: I18n,
+    private cdDatePipe: CdDatePipe
+  ) {
+    this.permission = this.authStorageService.getPermissions().prometheus;
+    this.tableActions = [
+      {
+        permission: 'create',
+        canBePrimary: (selection: CdTableSelection) => selection.hasSingleSelection,
+        disable: (selection: CdTableSelection) =>
+          !selection.hasSingleSelection || selection.first().cdExecuting,
+        icon: Icons.add,
+        routerLink: () =>
+          '/monitoring' + this.urlBuilder.getCreateFrom(this.selection.first().fingerprint),
+        name: this.i18n('Create Silence')
+      }
+    ];
+  }
+
+  ngOnInit() {
+    this.columns = [
+      {
+        name: this.i18n('Name'),
+        prop: 'labels.alertname',
+        flexGrow: 2
+      },
+      {
+        name: this.i18n('Job'),
+        prop: 'labels.job',
+        flexGrow: 2
+      },
+      {
+        name: this.i18n('Severity'),
+        prop: 'labels.severity'
+      },
+      {
+        name: this.i18n('State'),
+        prop: 'status.state',
+        cellTransformation: CellTemplate.classAdding
+      },
+      {
+        name: this.i18n('Started'),
+        prop: 'startsAt',
+        pipe: this.cdDatePipe
+      },
+      {
+        name: this.i18n('URL'),
+        prop: 'generatorURL',
+        sortable: false,
+        cellTemplate: this.externalLinkTpl
+      }
+    ];
+  }
+
+  updateSelection(selection: CdTableSelection) {
+    this.selection = selection;
+  }
+}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.html
deleted file mode 100644 (file)
index 45c5a7b..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-<cd-prometheus-tabs></cd-prometheus-tabs>
-
-<h3 class="cd-header"
-    i18n>All Alerts</h3>
-<cd-rules-list [data]="prometheusAlertService.rules"></cd-rules-list>
-
-<h3 class="cd-header"
-    i18n>Active Alerts</h3>
-<cd-table [data]="prometheusAlertService.alerts"
-          [columns]="columns"
-          identifier="fingerprint"
-          [forceIdentifier]="true"
-          [customCss]="customCss"
-          selectionType="single"
-          (updateSelection)="updateSelection($event)">
-  <cd-table-actions class="table-actions"
-                    [permission]="permission"
-                    [selection]="selection"
-                    [tableActions]="tableActions">
-  </cd-table-actions>
-  <tabset cdTableDetail
-          *ngIf="selection.hasSingleSelection">
-    <tab i18n-heading
-         heading="Details">
-      <cd-table-key-value [renderObjects]="true"
-                          [hideEmpty]="true"
-                          [appendParentKey]="false"
-                          [data]="selection.first()"
-                          [customCss]="customCss"
-                          [autoReload]="false">
-      </cd-table-key-value>
-    </tab>
-  </tabset>
-</cd-table>
-
-<ng-template #externalLinkTpl
-             let-row="row"
-             let-value="value">
-  <a [href]="value"
-     target="_blank"><i [ngClass]="[icons.lineChart]"></i> Source</a>
-</ng-template>
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.scss
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.spec.ts
deleted file mode 100644 (file)
index 247c20c..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-import { HttpClientTestingModule } from '@angular/common/http/testing';
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { RouterTestingModule } from '@angular/router/testing';
-
-import { TabsModule } from 'ngx-bootstrap/tabs';
-import { ToastrModule } from 'ngx-toastr';
-
-import {
-  configureTestBed,
-  i18nProviders,
-  PermissionHelper
-} from '../../../../../testing/unit-test-helper';
-import { CoreModule } from '../../../../core/core.module';
-import { TableActionsComponent } from '../../../../shared/datatable/table-actions/table-actions.component';
-import { SharedModule } from '../../../../shared/shared.module';
-import { CephModule } from '../../../ceph.module';
-import { DashboardModule } from '../../../dashboard/dashboard.module';
-import { ClusterModule } from '../../cluster.module';
-import { AlertListComponent } from './alert-list.component';
-
-describe('AlertListComponent', () => {
-  let component: AlertListComponent;
-  let fixture: ComponentFixture<AlertListComponent>;
-
-  configureTestBed({
-    imports: [
-      HttpClientTestingModule,
-      TabsModule.forRoot(),
-      RouterTestingModule,
-      ToastrModule.forRoot(),
-      SharedModule,
-      ClusterModule,
-      DashboardModule,
-      CephModule,
-      CoreModule
-    ],
-    declarations: [],
-    providers: [i18nProviders]
-  });
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(AlertListComponent);
-    component = fixture.componentInstance;
-  });
-
-  it('should create', () => {
-    fixture.detectChanges();
-    expect(component).toBeTruthy();
-  });
-
-  it('should test all TableActions combinations', () => {
-    component.ngOnInit();
-    const permissionHelper: PermissionHelper = new PermissionHelper(component.permission);
-    const tableActions: TableActionsComponent = permissionHelper.setPermissionsAndGetActions(
-      component.tableActions
-    );
-
-    expect(tableActions).toEqual({
-      'create,update,delete': {
-        actions: ['Create silence'],
-        primary: {
-          multiple: 'Create silence',
-          executing: 'Create silence',
-          single: 'Create silence',
-          no: 'Create silence'
-        }
-      },
-      'create,update': {
-        actions: ['Create silence'],
-        primary: {
-          multiple: 'Create silence',
-          executing: 'Create silence',
-          single: 'Create silence',
-          no: 'Create silence'
-        }
-      },
-      'create,delete': {
-        actions: ['Create silence'],
-        primary: {
-          multiple: 'Create silence',
-          executing: 'Create silence',
-          single: 'Create silence',
-          no: 'Create silence'
-        }
-      },
-      create: {
-        actions: ['Create silence'],
-        primary: {
-          multiple: 'Create silence',
-          executing: 'Create silence',
-          single: 'Create silence',
-          no: 'Create silence'
-        }
-      },
-      'update,delete': {
-        actions: [],
-        primary: { multiple: '', executing: '', single: '', no: '' }
-      },
-      update: { actions: [], primary: { multiple: '', executing: '', single: '', no: '' } },
-      delete: { actions: [], primary: { multiple: '', executing: '', single: '', no: '' } },
-      'no-permissions': {
-        actions: [],
-        primary: { multiple: '', executing: '', single: '', no: '' }
-      }
-    });
-  });
-});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.ts
deleted file mode 100644 (file)
index 2b68bae..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
-import { I18n } from '@ngx-translate/i18n-polyfill';
-import { CellTemplate } from '../../../../shared/enum/cell-template.enum';
-import { Icons } from '../../../../shared/enum/icons.enum';
-import { CdTableAction } from '../../../../shared/models/cd-table-action';
-import { CdTableColumn } from '../../../../shared/models/cd-table-column';
-import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
-import { Permission } from '../../../../shared/models/permissions';
-import { CdDatePipe } from '../../../../shared/pipes/cd-date.pipe';
-import { AuthStorageService } from '../../../../shared/services/auth-storage.service';
-import { PrometheusAlertService } from '../../../../shared/services/prometheus-alert.service';
-import { URLBuilderService } from '../../../../shared/services/url-builder.service';
-
-const BASE_URL = 'silence'; // as only silence actions can be used
-
-@Component({
-  selector: 'cd-prometheus-list',
-  providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }],
-  templateUrl: './alert-list.component.html',
-  styleUrls: ['./alert-list.component.scss']
-})
-export class AlertListComponent implements OnInit {
-  @ViewChild('externalLinkTpl', { static: true })
-  externalLinkTpl: TemplateRef<any>;
-  columns: CdTableColumn[];
-  tableActions: CdTableAction[];
-  permission: Permission;
-  selection = new CdTableSelection();
-  icons = Icons;
-  customCss = {
-    'badge badge-danger': 'active',
-    'badge badge-warning': 'unprocessed',
-    'badge badge-info': 'suppressed'
-  };
-
-  constructor(
-    // NotificationsComponent will refresh all alerts every 5s (No need to do it here as well)
-    private authStorageService: AuthStorageService,
-    public prometheusAlertService: PrometheusAlertService,
-    private urlBuilder: URLBuilderService,
-    private i18n: I18n,
-    private cdDatePipe: CdDatePipe
-  ) {
-    this.permission = this.authStorageService.getPermissions().prometheus;
-    this.tableActions = [
-      {
-        permission: 'create',
-        canBePrimary: (selection: CdTableSelection) => selection.hasSingleSelection,
-        disable: (selection: CdTableSelection) =>
-          !selection.hasSingleSelection || selection.first().cdExecuting,
-        icon: Icons.add,
-        routerLink: () => this.urlBuilder.getCreateFrom(this.selection.first().fingerprint),
-        name: this.i18n('Create silence')
-      }
-    ];
-  }
-
-  ngOnInit() {
-    this.columns = [
-      {
-        name: this.i18n('Name'),
-        prop: 'labels.alertname',
-        flexGrow: 2
-      },
-      {
-        name: this.i18n('Job'),
-        prop: 'labels.job',
-        flexGrow: 2
-      },
-      {
-        name: this.i18n('Severity'),
-        prop: 'labels.severity'
-      },
-      {
-        name: this.i18n('State'),
-        prop: 'status.state',
-        cellTransformation: CellTemplate.classAdding
-      },
-      {
-        name: this.i18n('Started'),
-        prop: 'startsAt',
-        pipe: this.cdDatePipe
-      },
-      {
-        name: this.i18n('URL'),
-        prop: 'generatorURL',
-        sortable: false,
-        cellTemplate: this.externalLinkTpl
-      }
-    ];
-  }
-
-  updateSelection(selection: CdTableSelection) {
-    this.selection = selection;
-  }
-}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.html
new file mode 100644 (file)
index 0000000..55f571d
--- /dev/null
@@ -0,0 +1,20 @@
+<tabset #tabs>
+  <tab id="active-alerts"
+       heading="Active Alerts"
+       (selectTab)="setFragment($event)"
+       i18n-heading>
+    <cd-active-alert-list></cd-active-alert-list>
+  </tab>
+  <tab id="all-alerts"
+       heading="All Alerts"
+       (selectTab)="setFragment($event)"
+       i18n-heading>
+    <cd-rules-list [data]="prometheusAlertService.rules"></cd-rules-list>
+  </tab>
+  <tab id="silences"
+       heading="Silences"
+       (selectTab)="setFragment($event)"
+       i18n-heading>
+    <cd-silences-list></cd-silences-list>
+  </tab>
+</tabset>
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.scss
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.spec.ts
new file mode 100644 (file)
index 0000000..bf0bf6c
--- /dev/null
@@ -0,0 +1,46 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+
+import { ToastrModule } from 'ngx-toastr';
+
+import { i18nProviders } from '../../../../../testing/unit-test-helper';
+import { AuthModule } from '../../../../core/auth/auth.module';
+import { CoreModule } from '../../../../core/core.module';
+import { CephfsModule } from '../../../cephfs/cephfs.module';
+import { DashboardModule } from '../../../dashboard/dashboard.module';
+import { NfsModule } from '../../../nfs/nfs.module';
+import { ClusterModule } from '../../cluster.module';
+import { MonitoringListComponent } from './monitoring-list.component';
+
+describe('MonitoringListComponent', () => {
+  let component: MonitoringListComponent;
+  let fixture: ComponentFixture<MonitoringListComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        ClusterModule,
+        DashboardModule,
+        CephfsModule,
+        AuthModule,
+        NfsModule,
+        CoreModule,
+        ToastrModule.forRoot(),
+        HttpClientTestingModule
+      ],
+      declarations: [],
+      providers: [i18nProviders]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(MonitoringListComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.ts
new file mode 100644 (file)
index 0000000..f6f57a9
--- /dev/null
@@ -0,0 +1,40 @@
+import { Component, OnInit, ViewChild } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+
+import { TabDirective, TabsetComponent } from 'ngx-bootstrap/tabs';
+
+import { PrometheusAlertService } from '../../../../shared/services/prometheus-alert.service';
+
+@Component({
+  selector: 'cd-monitoring-list',
+  templateUrl: './monitoring-list.component.html',
+  styleUrls: ['./monitoring-list.component.scss']
+})
+export class MonitoringListComponent implements OnInit {
+  @ViewChild('tabs', { static: true })
+  tabs: TabsetComponent;
+
+  constructor(
+    public prometheusAlertService: PrometheusAlertService,
+    private route: ActivatedRoute,
+    private router: Router
+  ) {}
+
+  ngOnInit() {
+    // Activate tab according to given fragment
+    if (this.route.snapshot.fragment) {
+      const tab = this.tabs.tabs.find(
+        (t) => t.elementRef.nativeElement.id === this.route.snapshot.fragment
+      );
+      if (tab) {
+        tab.active = true;
+      }
+      // Ensure fragment is not removed, so page can always be reloaded with the same tab open.
+      this.router.navigate([], { fragment: this.route.snapshot.fragment });
+    }
+  }
+
+  setFragment(element: TabDirective) {
+    this.router.navigate([], { fragment: element.id });
+  }
+}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.html
deleted file mode 100644 (file)
index b73a09f..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<tabset>
-  <tab heading="Alerts"
-       i18n-heading
-       [active]="url === '/alerts'"
-       (selectTab)="navigateTo('/alerts')">
-  </tab>
-  <tab heading="Silences"
-       i18n-heading
-       [active]="url === '/silence'"
-       (selectTab)="navigateTo('/silence')">
-  </tab>
-</tabset>
-
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.scss
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.spec.ts
deleted file mode 100644 (file)
index 47760b5..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-import { HttpClientTestingModule } from '@angular/common/http/testing';
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { By } from '@angular/platform-browser';
-import { Router } from '@angular/router';
-import { RouterTestingModule } from '@angular/router/testing';
-
-import { TabsModule } from 'ngx-bootstrap/tabs';
-
-import { configureTestBed } from '../../../../../testing/unit-test-helper';
-import { PrometheusTabsComponent } from './prometheus-tabs.component';
-
-describe('PrometheusTabsComponent', () => {
-  let component: PrometheusTabsComponent;
-  let fixture: ComponentFixture<PrometheusTabsComponent>;
-  let router: Router;
-
-  const selectTab = (index) => {
-    fixture.debugElement.queryAll(By.css('tab'))[index].triggerEventHandler('selectTab', null);
-  };
-
-  configureTestBed({
-    declarations: [PrometheusTabsComponent],
-    imports: [RouterTestingModule, HttpClientTestingModule, TabsModule.forRoot()]
-  });
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(PrometheusTabsComponent);
-    component = fixture.componentInstance;
-    router = TestBed.get(Router);
-    spyOn(router, 'navigate').and.stub();
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-
-  it('should redirect to alert listing', () => {
-    selectTab(0);
-    expect(router.navigate).toHaveBeenCalledWith(['/alerts']);
-  });
-
-  it('should redirect to silence listing', () => {
-    selectTab(1);
-    expect(router.navigate).toHaveBeenCalledWith(['/silence']);
-  });
-});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.ts
deleted file mode 100644 (file)
index 5675eb7..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-import { Component } from '@angular/core';
-import { Router } from '@angular/router';
-
-@Component({
-  selector: 'cd-prometheus-tabs',
-  templateUrl: './prometheus-tabs.component.html',
-  styleUrls: ['./prometheus-tabs.component.scss']
-})
-export class PrometheusTabsComponent {
-  url: string;
-
-  constructor(private router: Router) {
-    this.url = this.router.url;
-  }
-
-  navigateTo(url) {
-    this.router.navigate([url]);
-  }
-}
index cd338b4355d7bc6948f62354eb70ee5fefef7479..73d5fd8a620ca9976aeed15a8f21cc5f0bfc636d 100644 (file)
@@ -45,7 +45,7 @@ describe('SilenceFormComponent', () => {
   let ifPrometheusSpy;
   // Helper
   let prometheus: PrometheusHelper;
-  let formH: FormHelper;
+  let formHelper: FormHelper;
   let fixtureH: FixtureHelper;
   let params;
   // Date mocking related
@@ -86,10 +86,10 @@ describe('SilenceFormComponent', () => {
 
   const changeAction = (action: string) => {
     const modes = {
-      add: '/silence/add',
-      alertAdd: '/silence/add/someAlert',
-      recreate: '/silence/recreate/someExpiredId',
-      edit: '/silence/edit/someNotExpiredId'
+      add: '/monitoring/silence/add',
+      alertAdd: '/monitoring/silence/add/someAlert',
+      recreate: '/monitoring/silence/recreate/someExpiredId',
+      edit: '/monitoring/silence/edit/someNotExpiredId'
     };
     Object.defineProperty(router, 'url', { value: modes[action] });
     callInit();
@@ -138,7 +138,7 @@ describe('SilenceFormComponent', () => {
     fixtureH = new FixtureHelper(fixture);
     component = fixture.componentInstance;
     form = component.form;
-    formH = new FormHelper(form);
+    formHelper = new FormHelper(form);
     fixture.detectChanges();
   });
 
@@ -333,7 +333,7 @@ describe('SilenceFormComponent', () => {
 
       it('should raise invalid start date error', fakeAsync(() => {
         changeStartDate('No valid date');
-        formH.expectError('startsAt', 'bsDate');
+        formHelper.expectError('startsAt', 'bsDate');
         expect(form.getValue('startsAt').toString()).toBe('Invalid Date');
         expect(form.getValue('endsAt')).toEqual(new Date('2022-02-22T02:00:00'));
       }));
@@ -341,9 +341,9 @@ describe('SilenceFormComponent', () => {
 
     describe('on duration change', () => {
       it('changes end date if duration is changed', () => {
-        formH.setValue('duration', '15m');
+        formHelper.setValue('duration', '15m');
         expect(form.getValue('endsAt')).toEqual(new Date('2022-02-22T00:15'));
-        formH.setValue('duration', '5d 23h');
+        formHelper.setValue('duration', '5d 23h');
         expect(form.getValue('endsAt')).toEqual(new Date('2022-02-27T23:00'));
       });
     });
@@ -363,7 +363,7 @@ describe('SilenceFormComponent', () => {
 
       it('should raise invalid end date error', fakeAsync(() => {
         changeEndDate('No valid date');
-        formH.expectError('endsAt', 'bsDate');
+        formHelper.expectError('endsAt', 'bsDate');
         expect(form.getValue('endsAt').toString()).toBe('Invalid Date');
         expect(form.getValue('startsAt')).toEqual(baseTime);
       }));
@@ -371,20 +371,20 @@ describe('SilenceFormComponent', () => {
   });
 
   it('should have a creator field', () => {
-    formH.expectValid('createdBy');
-    formH.expectErrorChange('createdBy', '', 'required');
-    formH.expectValidChange('createdBy', 'Mighty FSM');
+    formHelper.expectValid('createdBy');
+    formHelper.expectErrorChange('createdBy', '', 'required');
+    formHelper.expectValidChange('createdBy', 'Mighty FSM');
   });
 
   it('should have a comment field', () => {
-    formH.expectError('comment', 'required');
-    formH.expectValidChange('comment', 'A pretty long comment');
+    formHelper.expectError('comment', 'required');
+    formHelper.expectValidChange('comment', 'A pretty long comment');
   });
 
   it('should be a valid form if all inputs are filled and at least one matcher was added', () => {
     expect(form.valid).toBeFalsy();
-    formH.expectValidChange('createdBy', 'Mighty FSM');
-    formH.expectValidChange('comment', 'A pretty long comment');
+    formHelper.expectValidChange('createdBy', 'Mighty FSM');
+    formHelper.expectValidChange('comment', 'A pretty long comment');
     addMatcher('job', 'someJob', false);
     expect(form.valid).toBeTruthy();
   });
@@ -525,7 +525,7 @@ describe('SilenceFormComponent', () => {
 
     const fillAndSubmit = () => {
       ['createdBy', 'comment'].forEach((attr) => {
-        formH.setValue(attr, silence[attr]);
+        formHelper.setValue(attr, silence[attr]);
       });
       silence.matchers.forEach((matcher) =>
         addMatcher(matcher.name, matcher.value, matcher.isRegex)
@@ -574,10 +574,10 @@ describe('SilenceFormComponent', () => {
       expect(router.navigate).not.toHaveBeenCalled();
     });
 
-    it('should route back to "/silence" on success', () => {
+    it('should route back to previous tab on success', () => {
       fillAndSubmit();
       expect(form.valid).toBeTruthy();
-      expect(router.navigate).toHaveBeenCalledWith(['/silence']);
+      expect(router.navigate).toHaveBeenCalledWith(['/monitoring'], { fragment: 'silences' });
     });
 
     it('should create a silence', () => {
index 8e984e907584e3d1e04529ba370fd2e169cd446c..d5ef6b35e6f596e2057b4a95e4a5227b391f490f 100644 (file)
@@ -96,8 +96,8 @@ export class SilenceFormComponent {
   }
 
   private chooseMode() {
-    this.edit = this.router.url.startsWith('/silence/edit');
-    this.recreate = this.router.url.startsWith('/silence/recreate');
+    this.edit = this.router.url.startsWith('/monitoring/silence/edit');
+    this.recreate = this.router.url.startsWith('/monitoring/silence/recreate');
     if (this.edit) {
       this.action = this.actionLabels.EDIT;
     } else if (this.recreate) {
@@ -294,7 +294,7 @@ export class SilenceFormComponent {
     }
     this.prometheusService.setSilence(this.getSubmitData()).subscribe(
       (resp) => {
-        this.router.navigate(['/silence']);
+        this.router.navigate(['/monitoring'], { fragment: 'silences' });
         this.notificationService.show(
           NotificationType.success,
           this.getNotificationTile(resp.body['silenceId']),
index 4d4f4d7b54373d9a4e3e4f061a8eabf934afa274..40dc8ffc8e541f43fe14d84fda49556c72fe2ba3 100644 (file)
@@ -1,5 +1,3 @@
-<cd-prometheus-tabs></cd-prometheus-tabs>
-
 <cd-table [data]="silences"
           [columns]="columns"
           [forceIdentifier]="true"
index be6127cb9c1ef8a0c712dc20e78909f617699aeb..ccacfd0fa784fbf6fd3742d7aff6984f5c7f7f21 100644 (file)
@@ -19,7 +19,6 @@ import { TableActionsComponent } from '../../../../shared/datatable/table-action
 import { NotificationType } from '../../../../shared/enum/notification-type.enum';
 import { NotificationService } from '../../../../shared/services/notification.service';
 import { SharedModule } from '../../../../shared/shared.module';
-import { PrometheusTabsComponent } from '../prometheus-tabs/prometheus-tabs.component';
 import { SilenceListComponent } from './silence-list.component';
 
 describe('SilenceListComponent', () => {
@@ -37,7 +36,7 @@ describe('SilenceListComponent', () => {
       RouterTestingModule,
       HttpClientTestingModule
     ],
-    declarations: [SilenceListComponent, PrometheusTabsComponent],
+    declarations: [SilenceListComponent],
     providers: [i18nProviders]
   });
 
index 50542a5a5cee7b3b3ca21b031c4edf8bc66c8df1..76de16a6b49a5e9b0b1ab0ce79051d5816f55fc9 100644 (file)
@@ -24,7 +24,7 @@ import { AuthStorageService } from '../../../../shared/services/auth-storage.ser
 import { NotificationService } from '../../../../shared/services/notification.service';
 import { URLBuilderService } from '../../../../shared/services/url-builder.service';
 
-const BASE_URL = 'silence';
+const BASE_URL = 'monitoring/silence';
 
 @Component({
   providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }],
@@ -65,6 +65,7 @@ export class SilenceListComponent {
         permission: 'create',
         icon: Icons.add,
         routerLink: () => this.urlBuilder.getCreate(),
+        preserveFragment: true,
         canBePrimary: (selection: CdTableSelection) => !selection.hasSingleSelection,
         name: this.actionLabels.CREATE
       },
@@ -79,6 +80,7 @@ export class SilenceListComponent {
           !selectionExpired(selection),
         icon: Icons.copy,
         routerLink: () => this.urlBuilder.getRecreate(this.selection.first().id),
+        preserveFragment: true,
         name: this.actionLabels.RECREATE
       },
       {
@@ -92,6 +94,7 @@ export class SilenceListComponent {
           (selection.first().cdExecuting && !selectionExpired(selection)) ||
           selectionExpired(selection),
         routerLink: () => this.urlBuilder.getEdit(this.selection.first().id),
+        preserveFragment: true,
         name: this.actionLabels.EDIT
       },
       {
index 8b66ec42fcb9620a78c6c57b7294a1b989e4b5ff..05232b7fa07554c80e98fb1134b5179990d40d8a 100644 (file)
@@ -4,7 +4,8 @@
       [ngClass]="{ 'active': last && finished }"
       class="breadcrumb-item">
     <a *ngIf="!last && crumb.path !== null"
-       [routerLink]="crumb.path">{{ crumb.text }}</a>
+       [routerLink]="crumb.path"
+       preserveFragment>{{ crumb.text }}</a>
     <span *ngIf="last || crumb.path === null">{{ crumb.text }}</span>
   </li>
 </ol>
index b49b33f08632e2957a4b910c90717213b102ab70..77b30500847e8aba30f766eb11e948f7e36b632b 100644 (file)
            routerLink="/logs">Logs</a>
       </li>
       <li routerLinkActive="active"
-          class="tc_submenuitem tc_submenuitem_prometheus"
+          class="tc_submenuitem tc_submenuitem_monitoring"
           *ngIf="prometheusConfigured && permissions.prometheus.read">
         <a i18n
            class="dropdown-item"
-           routerLink="/alerts">Alerts</a>
-      </li>
-      <li routerLinkActive="active"
-          class="tc_submenuitem tc_submenuitem_prometheus"
-          *ngIf="prometheusConfigured && permissions.prometheus.read">
-        <a i18n
-           class="dropdown-item"
-           routerLink="/silence">Silences</a>
+           routerLink="/monitoring">Monitoring</a>
       </li>
     </ul>
   </li>
index 85154c3a7afd1eac49764e11b64e996a46bf9aa5..94313d5a5615938058db6acf8742019928f83bef 100644 (file)
@@ -41,7 +41,9 @@ export class NavigationComponent implements OnInit {
       }
       this.summaryData = data;
     });
-    this.prometheusService.ifAlertmanagerConfigured(() => (this.prometheusConfigured = true));
+    this.prometheusService.ifAlertmanagerConfigured(() => {
+      this.prometheusConfigured = true;
+    });
   }
 
   blockHealthColor() {
index 28992932f5264511dfeb7bb4020aea3658f12156..1f20fc29f5abc7221d31a4ea7aa3cc168eeaa918 100644 (file)
@@ -5,7 +5,8 @@
             class="btn btn-{{btnColor}}"
             [ngClass]="{'disabled': disableSelectionAction(action)}"
             (click)="useClickAction(action)"
-            [routerLink]="useRouterLink(action)">
+            [routerLink]="useRouterLink(action)"
+            [preserveFragment]="action.preserveFragment ? '' : null">
       <i [ngClass]="[action.icon]"></i><span>{{ action.name }}</span>
     </button>
   </ng-container>
@@ -28,6 +29,7 @@
         <a class="dropdown-item"
            (click)="useClickAction(action)"
            [routerLink]="useRouterLink(action)"
+           [preserveFragment]="action.preserveFragment ? '' : null"
            [ngClass]="{'disabled': disableSelectionAction(action)}">
           <i [ngClass]="[action.icon]"></i><span>{{ action.name }}</span>
         </a>
index c5c6fab131e1d530d0de11e4901ff85717ec1b82..ac8dcb61a98d05712921687d93dd8b85bf93e113 100644 (file)
@@ -6,6 +6,8 @@ export class CdTableAction {
   // or none if it's not needed
   routerLink?: string | Function;
 
+  preserveFragment? = false;
+
   // This is the function that will be triggered on a click event if defined
   click?: Function;