]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard :NVMe-oF – Design cards for the gateway group page when the table has...
authorpujaoshahu <pshahu@redhat.com>
Wed, 20 May 2026 08:25:14 +0000 (13:55 +0530)
committerpujaoshahu <pshahu@redhat.com>
Tue, 26 May 2026 12:40:59 +0000 (18:10 +0530)
Fixes:https://tracker.ceph.com/issues/75684
Signed-off-by: pujaoshahu <pshahu@redhat.com>
20 files changed:
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/block.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group-filter/nvmeof-gateway-group-filter.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group-filter/nvmeof-gateway-group-filter.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group-filter/nvmeof-gateway-group-filter.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group-filter/nvmeof-gateway-group-filter.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group/nvmeof-gateway-group.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group/nvmeof-gateway-group.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-setup-cards/nvmeof-setup-cards.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-setup-cards/nvmeof-setup-cards.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-setup-cards/nvmeof-setup-cards.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems/nvmeof-subsystems.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems/nvmeof-subsystems.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems/nvmeof-subsystems.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-tabs/nvmeof-tabs.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-tabs/nvmeof-tabs.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.html
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.ts

index cb9b96abcd8a77a1730587bf4585f978666f4d6f..8e1e7c13a19ed5c1e7e1e2a6173dc118a03b7a5e 100644 (file)
@@ -105,6 +105,8 @@ import { NvmeSubsystemViewBreadcrumbResolver } from './nvme-subsystem-view/nvme-
 import { NvmeSubsystemViewComponent } from './nvme-subsystem-view/nvme-subsystem-view.component';
 import { NvmeofSubsystemPerformanceComponent } from './nvmeof-subsystem-performance/nvmeof-subsystem-performance.component';
 import { NvmeofTabsComponent } from './nvmeof-tabs/nvmeof-tabs.component';
+import { NvmeofSetupCardsComponent } from './nvmeof-setup-cards/nvmeof-setup-cards.component';
+import { NvmeofGatewayGroupFilterComponent } from './nvmeof-gateway-group-filter/nvmeof-gateway-group-filter.component';
 
 @NgModule({
   imports: [
@@ -141,7 +143,9 @@ import { NvmeofTabsComponent } from './nvmeof-tabs/nvmeof-tabs.component';
     ContainedListModule,
     SideNavModule,
     LayoutModule,
-    ThemeModule
+    ThemeModule,
+    NvmeofSetupCardsComponent,
+    NvmeofGatewayGroupFilterComponent
   ],
   declarations: [
     RbdListComponent,
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group-filter/nvmeof-gateway-group-filter.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group-filter/nvmeof-gateway-group-filter.component.html
new file mode 100644 (file)
index 0000000..730b304
--- /dev/null
@@ -0,0 +1,13 @@
+<div class="nvmeof-gateway-group-filter">
+  <span class="nvmeof-gateway-group-filter__label cds--type-body-compact-01"
+        i18n>Gateway group:</span>
+  <cds-combo-box type="single"
+                 class="nvmeof-gateway-group-filter__combo"
+                 [placeholder]="placeholder"
+                 [items]="items"
+                 [disabled]="disabled"
+                 (selected)="onSelected($event)"
+                 (clear)="onClear()">
+    <cds-dropdown-list></cds-dropdown-list>
+  </cds-combo-box>
+</div>
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group-filter/nvmeof-gateway-group-filter.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group-filter/nvmeof-gateway-group-filter.component.scss
new file mode 100644 (file)
index 0000000..0644927
--- /dev/null
@@ -0,0 +1,20 @@
+:host {
+  display: contents;
+}
+
+.nvmeof-gateway-group-filter {
+  display: flex;
+  align-items: center;
+  gap: var(--cds-spacing-03);
+  margin-left: var(--cds-spacing-05);
+
+  &__label {
+    white-space: nowrap;
+    color: var(--cds-text-secondary);
+  }
+
+  &__combo {
+    min-inline-size: 14rem;
+    max-inline-size: 20rem;
+  }
+}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group-filter/nvmeof-gateway-group-filter.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group-filter/nvmeof-gateway-group-filter.component.spec.ts
new file mode 100644 (file)
index 0000000..665e906
--- /dev/null
@@ -0,0 +1,26 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { NvmeofGatewayGroupFilterComponent } from './nvmeof-gateway-group-filter.component';
+
+describe('NvmeofGatewayGroupFilterComponent', () => {
+  let fixture: ComponentFixture<NvmeofGatewayGroupFilterComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [NvmeofGatewayGroupFilterComponent]
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(NvmeofGatewayGroupFilterComponent);
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(fixture.componentInstance).toBeTruthy();
+  });
+
+  it('should render combo box with filter class', () => {
+    const combo = fixture.nativeElement.querySelector('cds-combo-box');
+    expect(combo).toBeTruthy();
+    expect(combo.classList.contains('nvmeof-gateway-group-filter__combo')).toBe(true);
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group-filter/nvmeof-gateway-group-filter.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group-filter/nvmeof-gateway-group-filter.component.ts
new file mode 100644 (file)
index 0000000..e975a36
--- /dev/null
@@ -0,0 +1,27 @@
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { ComboBoxModule } from 'carbon-components-angular';
+import { GroupsComboboxItem } from '~/app/shared/api/nvmeof.service';
+
+@Component({
+  selector: 'cd-nvmeof-gateway-group-filter',
+  templateUrl: './nvmeof-gateway-group-filter.component.html',
+  styleUrls: ['./nvmeof-gateway-group-filter.component.scss'],
+  standalone: true,
+  imports: [ComboBoxModule]
+})
+export class NvmeofGatewayGroupFilterComponent {
+  @Input() items: GroupsComboboxItem[] = [];
+  @Input() disabled = false;
+  @Input() placeholder = $localize`Enter group name`;
+
+  @Output() selected = new EventEmitter<GroupsComboboxItem>();
+  @Output() cleared = new EventEmitter<void>();
+
+  onSelected(item: GroupsComboboxItem): void {
+    this.selected.emit(item);
+  }
+
+  onClear(): void {
+    this.cleared.emit();
+  }
+}
index 7f48a42cbf633bb1071ef12cff4f059e489b49b0..556e15236da1f11ace2a69d4e3d0ccf8f123b4b5 100644 (file)
@@ -1,4 +1,4 @@
-<cd-nvmeof-tabs></cd-nvmeof-tabs>
+<cd-nvmeof-tabs [showSetupCards]="(gatewayGroup$ | async)?.length === 0"></cd-nvmeof-tabs>
 
 <ng-container *ngIf="gatewayGroup$ | async as gateways">
   <cd-table
index 3ccef418bd08f4af86f5a96607ca802a2c9a8e51..4349ef4d16bebfbe99519e50a68cee77d46de783 100644 (file)
@@ -1,7 +1,7 @@
 import { Component, OnInit, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core';
 import { Router } from '@angular/router';
 import { BehaviorSubject, forkJoin, Observable, of } from 'rxjs';
-import { catchError, map, switchMap } from 'rxjs/operators';
+import { catchError, map, shareReplay, switchMap } from 'rxjs/operators';
 import { GatewayGroup, NvmeofService } from '~/app/shared/api/nvmeof.service';
 import { HostService } from '~/app/shared/api/host.service';
 import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
@@ -171,7 +171,8 @@ export class NvmeofGatewayGroupComponent implements OnInit {
             return of([]);
           })
         )
-      )
+      ),
+      shareReplay({ bufferSize: 1, refCount: true })
     );
     this.checkNodesAvailability();
   }
index 79c82c3c4a2ae8176af5bff97ea4db5910c4e849..b2eddd00df45291fe0b34ccce7c23f881366ad49 100644 (file)
@@ -1,30 +1,8 @@
-<cd-nvmeof-tabs></cd-nvmeof-tabs>
-
-<div cdsGrid
-     [useCssGrid]="true"
-     [narrow]="true"
-     [fullWidth]="true">
-<div cdsCol
-     [columnNumbers]="{sm: 4, md: 8}">
-  <div class="cds-mt-3 form-item"
-       cdsRow>
-    <cds-combo-box
-        type="single"
-        label="Selected Gateway Group"
-        i18n-label
-        [placeholder]="gwGroupPlaceholder"
-        [items]="gwGroups"
-        (selected)="onGroupSelection($event)"
-        (clear)="onGroupClear()"
-        [disabled]="gwGroupsEmpty">
-      <cds-dropdown-list></cds-dropdown-list>
-    </cds-combo-box>
-  </div>
-</div>
-</div>
+<cd-nvmeof-tabs [showSetupCards]="(namespaces$ | async)?.length === 0"></cd-nvmeof-tabs>
 
 <ng-container *ngIf="namespaces$ | async as namespaces">
   <cd-table [data]="namespaces"
+            [compactSearchField]="true"
             columnMode="flex"
             (fetchData)="fetchData()"
             [columns]="namespacesColumns"
             emptyStateMessage="Namespaces are storage volumes mapped to subsystems for host access. Create a namespace to start provisioning storage within a subsystem."
             i18n-emptyStateMessage>
 
+    <div class="table-filter">
+      <cd-nvmeof-gateway-group-filter [items]="gwGroups"
+                                      [placeholder]="gwGroupPlaceholder"
+                                      [disabled]="gwGroupsEmpty"
+                                      (selected)="onGroupSelection($event)"
+                                      (cleared)="onGroupClear()">
+      </cd-nvmeof-gateway-group-filter>
+    </div>
+
   <div class="table-actions">
     <cd-table-actions [permission]="permission"
                       [selection]="selection"
index 6ac05c63b699f3d1459a18950096987b08733375..a730bada6729715744d70ad1636951dcfe33cb2a 100644 (file)
@@ -1,6 +1,5 @@
 import { ComponentFixture, TestBed } from '@angular/core/testing';
 import { HttpClientModule } from '@angular/common/http';
-import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
 import { of } from 'rxjs';
 import { take } from 'rxjs/operators';
 import { RouterTestingModule } from '@angular/router/testing';
@@ -12,6 +11,7 @@ import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
 import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
 import { NvmeofSubsystemsDetailsComponent } from '../nvmeof-subsystems-details/nvmeof-subsystems-details.component';
 import { NvmeofNamespacesListComponent } from './nvmeof-namespaces-list.component';
+import { NvmeofGatewayGroupFilterComponent } from '../nvmeof-gateway-group-filter/nvmeof-gateway-group-filter.component';
 
 const mockNamespaces = [
   {
@@ -65,14 +65,18 @@ describe('NvmeofNamespacesListComponent', () => {
   beforeEach(async () => {
     await TestBed.configureTestingModule({
       declarations: [NvmeofNamespacesListComponent, NvmeofSubsystemsDetailsComponent],
-      imports: [HttpClientModule, RouterTestingModule, SharedModule],
+      imports: [
+        HttpClientModule,
+        RouterTestingModule,
+        SharedModule,
+        NvmeofGatewayGroupFilterComponent
+      ],
       providers: [
         { provide: NvmeofService, useClass: MockNvmeOfService },
         { provide: AuthStorageService, useClass: MockAuthStorageService },
         { provide: ModalCdsService, useClass: MockModalCdsService },
         { provide: TaskWrapperService, useClass: MockTaskWrapperService }
-      ],
-      schemas: [CUSTOM_ELEMENTS_SCHEMA]
+      ]
     }).compileComponents();
 
     fixture = TestBed.createComponent(NvmeofNamespacesListComponent);
index 37b1aa4d3a6803d55802df433697d6d7918c6df5..ed6dcbec2e1c59b937b5fd7db0f89a670d85ab95 100644 (file)
@@ -18,7 +18,7 @@ import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
 
 import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
 import { BehaviorSubject, Observable, of, Subject } from 'rxjs';
-import { catchError, map, switchMap, takeUntil } from 'rxjs/operators';
+import { catchError, map, shareReplay, switchMap, takeUntil } from 'rxjs/operators';
 
 const DEFAULT_PLACEHOLDER = $localize`Enter group name`;
 
@@ -164,6 +164,7 @@ export class NvmeofNamespacesListComponent implements OnInit, OnDestroy {
           catchError(() => of([]))
         );
       }),
+      shareReplay({ bufferSize: 1, refCount: true }),
       takeUntil(this.destroy$)
     );
   }
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-setup-cards/nvmeof-setup-cards.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-setup-cards/nvmeof-setup-cards.component.html
new file mode 100644 (file)
index 0000000..52bbd41
--- /dev/null
@@ -0,0 +1,79 @@
+<cd-productive-card class="nvmeof-setup-cards">
+  <ng-template #header>
+    <div cdsStack="vertical"
+         gap="3">
+      <h2 class="cds--type-heading-compact-03"
+          i18n>Recommended first-time setup</h2>
+      <p class="cds--type-body-01"
+         i18n>
+        Start your NVMe over Fabrics configuration by creating the essential resources in sequence.
+      </p>
+    </div>
+  </ng-template>
+
+  <div class="nvmeof-setup-cards__columns">
+
+    <div class="nvmeof-setup-cards__column">
+      <div class="nvmeof-setup-cards__column-content">
+        <div class="nvmeof-setup-cards__step-row">
+          <span class="cds--type-label-01"
+                i18n>1.</span>
+          <span
+             class="cds--type-heading-compact-01"
+             i18n>Create Gateway groups</span>
+        </div>
+        <p class="cds--type-label-01"
+           i18n>
+          Group NVMe gateway nodes to enable high availability and<br>load balancing for storage targets.
+        </p>
+        <div class="nvmeof-setup-cards__info">
+          <cd-icon type="info"></cd-icon>
+          <span class="cds--type-label-01 "
+                i18n>No gateway groups configured for this cluster yet.</span>
+        </div>
+      </div>
+    </div>
+
+    <div class="nvmeof-setup-cards__column">
+      <div class="nvmeof-setup-cards__column-content">
+        <div class="nvmeof-setup-cards__step-row">
+          <span class="cds--type-label-01"
+                i18n>2.</span>
+          <span
+             class="cds--type-heading-compact-01"
+             i18n>Create Subsystems</span>
+        </div>
+        <p class="cds--type-label-01"
+           i18n>
+         Define storage targets by creating NVMe subsystems and <br> configuring security, listeners, and host access.
+        </p>
+        <div class="nvmeof-setup-cards__info">
+          <cd-icon type="info"></cd-icon>
+          <span class="cds--type-label-01"
+                i18n>No subsystem configured for this cluster yet.</span>
+        </div>
+      </div>
+    </div>
+
+    <div class="nvmeof-setup-cards__column">
+      <div class="nvmeof-setup-cards__column-content">
+        <div class="nvmeof-setup-cards__step-row">
+          <span class="cds--type-label-01"
+                i18n>3.</span>
+          <span class="cds--type-heading-compact-01"
+                i18n>Create Namespaces</span>
+        </div>
+        <p class="cds--type-label-01"
+           i18n>
+          Create storage namespaces backed by Ceph block images. This <br>completes your NVMe over Fabrics setup.
+        </p>
+        <div class="nvmeof-setup-cards__info">
+          <cd-icon type="info"></cd-icon>
+          <span class="cds--type-label-01"
+                i18n>No namespace available or mapped yet.</span>
+        </div>
+      </div>
+    </div>
+
+  </div>
+</cd-productive-card>
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-setup-cards/nvmeof-setup-cards.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-setup-cards/nvmeof-setup-cards.component.scss
new file mode 100644 (file)
index 0000000..80caa2b
--- /dev/null
@@ -0,0 +1,48 @@
+.nvmeof-setup-cards__columns {
+  display: flex;
+  align-items: stretch;
+  padding-bottom: var(--cds-spacing-05);
+}
+
+.nvmeof-setup-cards__column {
+  flex: 1 1 0;
+  min-width: 0;
+  padding: var(--cds-spacing-03) var(--cds-spacing-05) var(--cds-spacing-05);
+  display: flex;
+  flex-direction: column;
+}
+
+.nvmeof-setup-cards__column-content {
+  display: flex;
+  flex-direction: column;
+  flex: 1;
+  gap: var(--cds-spacing-03);
+}
+
+.nvmeof-setup-cards__step-row {
+  display: flex;
+  align-items: center;
+  gap: var(--cds-spacing-03);
+}
+
+.nvmeof-setup-cards__info {
+  display: flex;
+  align-items: center;
+  margin-top: auto;
+  gap: var(--cds-spacing-03);
+}
+
+:host ::ng-deep .nvmeof-setup-cards {
+  &.productive-card .productive-card-header {
+    padding: var(--cds-spacing-03) var(--cds-spacing-05);
+    border-bottom: 1px solid var(--cds-border-subtle);
+  }
+
+  &.productive-card .productive-card-section {
+    padding: 0;
+  }
+
+  .productive-card-section {
+    padding: var(--cds-spacing-02);
+  }
+}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-setup-cards/nvmeof-setup-cards.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-setup-cards/nvmeof-setup-cards.component.ts
new file mode 100644 (file)
index 0000000..21d3a60
--- /dev/null
@@ -0,0 +1,24 @@
+import { CommonModule } from '@angular/common';
+import { Component } from '@angular/core';
+import { RouterModule } from '@angular/router';
+import { LayoutModule, LayerModule, LinkModule, TilesModule } from 'carbon-components-angular';
+import { ProductiveCardComponent } from '~/app/shared/components/productive-card/productive-card.component';
+import { ComponentsModule } from '~/app/shared/components/components.module';
+
+@Component({
+  selector: 'cd-nvmeof-setup-cards',
+  templateUrl: './nvmeof-setup-cards.component.html',
+  styleUrl: './nvmeof-setup-cards.component.scss',
+  standalone: true,
+  imports: [
+    CommonModule,
+    RouterModule,
+    LayoutModule,
+    LayerModule,
+    TilesModule,
+    LinkModule,
+    ProductiveCardComponent,
+    ComponentsModule
+  ]
+})
+export class NvmeofSetupCardsComponent {}
index 3111ae2e8d4dbed1bcc786ba62b609a9037eb9a6..8ae7ec62e35b53f54f5d22c4da4d0792e33684f3 100644 (file)
@@ -1,31 +1,10 @@
-<cd-nvmeof-tabs></cd-nvmeof-tabs>
+<cd-nvmeof-tabs [showSetupCards]="(subsystems$ | async)?.length === 0"></cd-nvmeof-tabs>
 
-<div cdsGrid
-     [useCssGrid]="true"
-     [narrow]="true"
-     [fullWidth]="true">
-<div cdsCol
-     [columnNumbers]="{sm: 4, md: 8}">
-  <div class="cds-mt-3 form-item"
-       cdsRow>
-    <cds-combo-box
-        type="single"
-        label="Selected Gateway Group"
-        i18n-label
-        [placeholder]="gwGroupPlaceholder"
-        [items]="gwGroups"
-        (selected)="onGroupSelection($event)"
-        (clear)="onGroupClear()"
-        [disabled]="gwGroupsEmpty">
-      <cds-dropdown-list></cds-dropdown-list>
-    </cds-combo-box>
-  </div>
-</div>
-</div>
 <ng-container *ngIf="subsystems$ | async as subsystems">
   <cd-table #table
             [data]="subsystems"
             [columns]="subsystemsColumns"
+            [compactSearchField]="true"
             columnMode="flex"
             selectionType="single"
             (updateSelection)="updateSelection($event)"
             emptyStateMessage="Subsystems group NVMe namespaces and manage host access. Create a subsystem to start mapping NVMe volumes to hosts."
             i18n-emptyStateMessage>
 
+    <div class="table-filter">
+      <cd-nvmeof-gateway-group-filter [items]="gwGroups"
+                                      [placeholder]="gwGroupPlaceholder"
+                                      [disabled]="gwGroupsEmpty"
+                                      (selected)="onGroupSelection($event)"
+                                      (cleared)="onGroupClear()">
+      </cd-nvmeof-gateway-group-filter>
+    </div>
+
     <div class="table-actions">
       <cd-table-actions [permission]="permissions.nvmeof"
                         [selection]="selection"
index a3532ec1e12e420d08409a8bbbf9a9246d7833e4..976390827263ed710612a1f9cfbb7d768802e5f0 100644 (file)
@@ -10,7 +10,7 @@ import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
 import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
 import { NvmeofSubsystemsComponent } from './nvmeof-subsystems.component';
 import { NvmeofSubsystemsDetailsComponent } from '../nvmeof-subsystems-details/nvmeof-subsystems-details.component';
-import { ComboBoxModule, GridModule } from 'carbon-components-angular';
+import { NvmeofGatewayGroupFilterComponent } from '../nvmeof-gateway-group-filter/nvmeof-gateway-group-filter.component';
 import { CephServiceSpec } from '~/app/shared/models/service.interface';
 
 const mockSubsystems = [
@@ -93,7 +93,12 @@ describe('NvmeofSubsystemsComponent', () => {
   beforeEach(async () => {
     await TestBed.configureTestingModule({
       declarations: [NvmeofSubsystemsComponent, NvmeofSubsystemsDetailsComponent],
-      imports: [HttpClientModule, RouterTestingModule, SharedModule, ComboBoxModule, GridModule],
+      imports: [
+        HttpClientModule,
+        RouterTestingModule,
+        SharedModule,
+        NvmeofGatewayGroupFilterComponent
+      ],
       providers: [
         { provide: NvmeofService, useClass: MockNvmeOfService },
         { provide: AuthStorageService, useClass: MockAuthStorageService },
index 71c954d34ae0c33936d966f85078fe743e2a8cde..1a239359b3baaede015d7b2a8265adb4b8d597f3 100644 (file)
@@ -22,7 +22,7 @@ import { NvmeofService, GroupsComboboxItem } from '~/app/shared/api/nvmeof.servi
 import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
 import { CephServiceSpec } from '~/app/shared/models/service.interface';
 import { BehaviorSubject, forkJoin, Observable, of, Subject } from 'rxjs';
-import { catchError, map, switchMap, takeUntil, tap } from 'rxjs/operators';
+import { catchError, map, shareReplay, switchMap, takeUntil, tap } from 'rxjs/operators';
 import { DeletionImpact } from '~/app/shared/enum/delete-confirmation-modal-impact.enum';
 
 const BASE_URL = 'block/nvmeof/subsystems';
@@ -150,6 +150,7 @@ export class NvmeofSubsystemsComponent extends ListWithDetails implements OnInit
       tap((subs) => {
         this.subsystems = subs;
       }),
+      shareReplay({ bufferSize: 1, refCount: true }),
       takeUntil(this.destroy$)
     );
   }
index 9b3388838d5d0ead225b80ac655816949fb863d0..31530e009fc4a1109b5364f18bc1cf9be224b4f8 100644 (file)
@@ -1,10 +1,13 @@
 <fieldset>
-  <legend>
+  <legend class="cds-mb-5">
     <h1 class="cds--type-heading-03">NVMe over Fabrics (TCP)</h1>
-  <cd-help-text>Monitor and manage NVMe-over-TCP resources for high-performance block storage.</cd-help-text>
+  <cd-help-text>Monitor and manage NVMe-over-TCP resources for high-<br>performance block storage.</cd-help-text>
   </legend>
 </fieldset>
-<section>
+@if (showSetupCards) {
+<cd-nvmeof-setup-cards></cd-nvmeof-setup-cards>
+}
+<section class="cds-mt-5">
   <cds-tabs type="contained"
             followFocus="true"
             isNavigation="true"
index 8b74346db920a81721c7b75945dfdd184f0cf9a1..eac0908a79569acec45de1e82c8c95324e2efa41 100644 (file)
@@ -1,4 +1,4 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, Input, OnInit } from '@angular/core';
 import { Router } from '@angular/router';
 
 const NVMEOF_PATH = 'block/nvmeof';
@@ -16,6 +16,8 @@ enum TABS {
   standalone: false
 })
 export class NvmeofTabsComponent implements OnInit {
+  @Input() showSetupCards = false;
+
   selectedTab: TABS;
   activeTab: TABS = TABS.gateways;
 
index ebace42a6b7499ac81c6c1cc5f134cb11161ad7e..cd6cc42e3662ad1a21baa12fb2c6a09e53937949 100644 (file)
@@ -41,6 +41,9 @@
     </cds-table-toolbar-actions>
     <!-- end batch actions -->
     <cds-table-toolbar-content>
+      <!-- gateway group / custom filter slot -->
+      <ng-content select=".table-filter"></ng-content>
+      <!-- end custom filter slot -->
       <!-- search -->
       <cds-table-toolbar-search *ngIf="searchField"
                                 [expandable]="false"
index 3ee596f212e172c154f5923fbb6ae56a0a3e498d..70c9f7851cdfc95429cd45a61f2ca89589a8de6c 100644 (file)
@@ -130,6 +130,9 @@ export class TableComponent implements AfterViewInit, OnInit, OnChanges, OnDestr
   // Display search field inside tool header?
   @Input()
   searchField? = true;
+  // Limit toolbar search width (e.g. when a prefix filter is shown).
+  @Input()
+  compactSearchField? = false;
   // Display the table header?
   @Input()
   header? = true;