This is an automated email from the ASF dual-hosted git repository.

rfellows pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new aee5e61cd2 NIFI-15096: Also consider system diagnostics loading status 
in the cl… (#10423)
aee5e61cd2 is described below

commit aee5e61cd23a0079c0a5f93c73c03f98791a5556
Author: Matt Gilman <[email protected]>
AuthorDate: Wed Oct 15 17:16:36 2025 -0400

    NIFI-15096: Also consider system diagnostics loading status in the cl… 
(#10423)
    
    * NIFI-15096: Also consider system diagnostics loading status in the 
cluster listing spinner.
    
    * NIFI-15096: Considering the loaded timestamp for the system diagnostics 
for skeleton loaders on the tables in the cluster listing.
    
    * NIFI-15096: Using a computed signal for initialLoading.
    
    This closes #10423
---
 .../pages/cluster/feature/cluster.component.html   |  6 ++-
 .../cluster/feature/cluster.component.spec.ts      |  5 +-
 .../app/pages/cluster/feature/cluster.component.ts |  2 +
 .../cluster-content-storage-listing.component.html |  2 +-
 ...uster-content-storage-listing.component.spec.ts | 56 ++++++++++++++++++++-
 .../cluster-content-storage-listing.component.ts   | 17 +++++--
 ...luster-flow-file-storage-listing.component.html |  2 +-
 ...ter-flow-file-storage-listing.component.spec.ts | 57 ++++++++++++++++++++--
 .../cluster-flow-file-storage-listing.component.ts | 17 +++++--
 .../cluster-jvm-listing.component.html             |  2 +-
 .../cluster-jvm-listing.component.spec.ts          | 57 ++++++++++++++++++++--
 .../cluster-jvm-listing.component.ts               | 17 +++++--
 ...uster-provenance-storage-listing.component.html |  2 +-
 ...er-provenance-storage-listing.component.spec.ts | 57 ++++++++++++++++++++--
 ...cluster-provenance-storage-listing.component.ts | 17 +++++--
 .../cluster-system-listing.component.html          |  2 +-
 .../cluster-system-listing.component.spec.ts       | 57 ++++++++++++++++++++--
 .../cluster-system-listing.component.ts            | 17 +++++--
 .../cluster-version-listing.component.html         |  2 +-
 .../cluster-version-listing.component.spec.ts      | 57 ++++++++++++++++++++--
 .../cluster-version-listing.component.ts           | 17 +++++--
 21 files changed, 413 insertions(+), 55 deletions(-)

diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.html
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.html
index e26b27142e..4ddef40ebd 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.html
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.html
@@ -50,7 +50,11 @@
                 <div class="flex justify-between align-middle">
                     <div class="text-sm flex items-center gap-x-2">
                         <button mat-icon-button class="primary-icon-button" 
(click)="refresh()">
-                            <i class="fa fa-refresh" 
[class.fa-spin]="listingStatus() === 'loading'"></i>
+                            <i
+                                class="fa fa-refresh"
+                                [class.fa-spin]="
+                                    listingStatus() === 'loading' || 
systemDiagnosticsStatus() === 'loading'
+                                "></i>
                         </button>
                         <div>Last updated:</div>
                         <div class="tertiary-color font-medium">{{ 
loadedTimestamp() }}</div>
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.spec.ts
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.spec.ts
index 8dd5f41444..d892cf81e5 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.spec.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.spec.ts
@@ -33,6 +33,8 @@ import { initialState as initialErrorState } from 
'../../../state/error/error.re
 import { errorFeatureKey } from '../../../state/error';
 import { initialState as initialCurrentUserState } from 
'../../../state/current-user/current-user.reducer';
 import { currentUserFeatureKey } from '../../../state/current-user';
+import { initialSystemDiagnosticsState } from 
'../../../state/system-diagnostics/system-diagnostics.reducer';
+import { systemDiagnosticsFeatureKey } from 
'../../../state/system-diagnostics';
 
 describe('Cluster', () => {
     let component: Cluster;
@@ -44,7 +46,8 @@ describe('Cluster', () => {
             [currentUserFeatureKey]: initialCurrentUserState,
             [clusterFeatureKey]: {
                 [clusterListingFeatureKey]: initialClusterState
-            }
+            },
+            [systemDiagnosticsFeatureKey]: initialSystemDiagnosticsState
         };
         TestBed.configureTestingModule({
             declarations: [Cluster],
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.ts
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.ts
index 787a696a75..1fe2a43451 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.ts
@@ -34,6 +34,7 @@ import { takeUntilDestroyed } from 
'@angular/core/rxjs-interop';
 import { selectCurrentRoute } from '@nifi/shared';
 import { resetSystemDiagnostics } from 
'../../../state/system-diagnostics/system-diagnostics.actions';
 import { ErrorContextKey } from '../../../state/error';
+import { selectSystemDiagnosticsStatus } from 
'../../../state/system-diagnostics/system-diagnostics.selectors';
 
 interface TabLink {
     label: string;
@@ -62,6 +63,7 @@ export class Cluster implements OnInit, OnDestroy {
     ];
 
     listingStatus = this.store.selectSignal(selectClusterListingStatus);
+    systemDiagnosticsStatus = 
this.store.selectSignal(selectSystemDiagnosticsStatus);
     loadedTimestamp = 
this.store.selectSignal(selectClusterListingLoadedTimestamp);
     currentUser$ = this.store.select(selectCurrentUser);
     currentRoute = this.store.selectSignal(selectCurrentRoute);
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.html
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.html
index 66feaf7f0e..9ec108252d 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.html
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.html
@@ -16,7 +16,7 @@
   -->
 
 <ng-container>
-    @if (isInitialLoading(loadedTimestamp())) {
+    @if (isInitialLoading()) {
         <div>
             <ngx-skeleton-loader count="3"></ngx-skeleton-loader>
         </div>
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.spec.ts
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.spec.ts
index 30ae263cce..c0352111bd 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.spec.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.spec.ts
@@ -22,15 +22,23 @@ import { clusterListingFeatureKey } from 
'../../state/cluster-listing';
 import { initialClusterState } from 
'../../state/cluster-listing/cluster-listing.reducer';
 import { clusterFeatureKey } from '../../state';
 import { provideMockStore } from '@ngrx/store/testing';
-import { selectClusterListing } from 
'../../state/cluster-listing/cluster-listing.selectors';
+import {
+    selectClusterListing,
+    selectClusterListingLoadedTimestamp
+} from '../../state/cluster-listing/cluster-listing.selectors';
+import { selectSystemDiagnosticsLoadedTimestamp } from 
'../../../../state/system-diagnostics/system-diagnostics.selectors';
 import { initialState as initialErrorState } from 
'../../../../state/error/error.reducer';
 import { errorFeatureKey } from '../../../../state/error';
 import { initialState as initialCurrentUserState } from 
'../../../../state/current-user/current-user.reducer';
 import { currentUserFeatureKey } from '../../../../state/current-user';
+import { initialSystemDiagnosticsState } from 
'../../../../state/system-diagnostics/system-diagnostics.reducer';
+import { systemDiagnosticsFeatureKey } from 
'../../../../state/system-diagnostics';
+import { MockStore } from '@ngrx/store/testing';
 
 describe('ClusterContentStorageListing', () => {
     let component: ClusterContentStorageListing;
     let fixture: ComponentFixture<ClusterContentStorageListing>;
+    let store: MockStore;
 
     beforeEach(async () => {
         const initialState = {
@@ -38,7 +46,8 @@ describe('ClusterContentStorageListing', () => {
             [currentUserFeatureKey]: initialCurrentUserState,
             [clusterFeatureKey]: {
                 [clusterListingFeatureKey]: initialClusterState
-            }
+            },
+            [systemDiagnosticsFeatureKey]: initialSystemDiagnosticsState
         };
         await TestBed.configureTestingModule({
             imports: [ClusterContentStorageListing],
@@ -55,6 +64,7 @@ describe('ClusterContentStorageListing', () => {
             ]
         }).compileComponents();
 
+        store = TestBed.inject(MockStore);
         fixture = TestBed.createComponent(ClusterContentStorageListing);
         component = fixture.componentInstance;
         fixture.detectChanges();
@@ -63,4 +73,46 @@ describe('ClusterContentStorageListing', () => {
     it('should create', () => {
         expect(component).toBeTruthy();
     });
+
+    describe('isInitialLoading', () => {
+        it('should return true when both timestamps are empty (initial 
state)', () => {
+            expect(component.isInitialLoading()).toBe(true);
+        });
+
+        it('should return true when cluster timestamp is empty but system 
diagnostics has loaded', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, '');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, 
'10:30:00 UTC');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(true);
+        });
+
+        it('should return true when system diagnostics timestamp is empty but 
cluster has loaded', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, 
'10:30:00 UTC');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, '');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(true);
+        });
+
+        it('should return false when both timestamps are populated', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, 
'10:30:00 UTC');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, 
'10:30:05 UTC');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(false);
+        });
+
+        it('should return false when data has been loaded and user triggers a 
refresh', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, 
'10:30:00 UTC');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, 
'10:30:05 UTC');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(false);
+        });
+    });
 });
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.ts
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.ts
index 57e4e3c2ca..151fae204b 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.ts
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-import { Component, inject } from '@angular/core';
+import { Component, computed, inject } from '@angular/core';
 import { AsyncPipe } from '@angular/common';
 import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
 import { RepositoryStorageTable } from 
'../common/repository-storage-table/repository-storage-table.component';
@@ -25,7 +25,10 @@ import {
     selectClusterNodeIdFromRoute,
     selectClusterStorageRepositoryIdFromRoute
 } from '../../state/cluster-listing/cluster-listing.selectors';
-import { selectSystemNodeSnapshots } from 
'../../../../state/system-diagnostics/system-diagnostics.selectors';
+import {
+    selectSystemDiagnosticsLoadedTimestamp,
+    selectSystemNodeSnapshots
+} from '../../../../state/system-diagnostics/system-diagnostics.selectors';
 import { isDefinedAndNotNull } from '@nifi/shared';
 import { map } from 'rxjs';
 import { ClusterNodeRepositoryStorageUsage } from 
'../../../../state/system-diagnostics';
@@ -37,6 +40,7 @@ import {
     selectContentStorageNode
 } from '../../state/cluster-listing/cluster-listing.actions';
 import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { initialSystemDiagnosticsState } from 
'../../../../state/system-diagnostics/system-diagnostics.reducer';
 
 @Component({
     selector: 'cluster-content-storage-listing',
@@ -48,6 +52,7 @@ export class ClusterContentStorageListing {
     private store = inject<Store<NiFiState>>(Store);
 
     loadedTimestamp = 
this.store.selectSignal(selectClusterListingLoadedTimestamp);
+    systemDiagnosticsLoadedTimestamp = 
this.store.selectSignal(selectSystemDiagnosticsLoadedTimestamp);
     listingStatus = this.store.selectSignal(selectClusterListingStatus);
     selectedClusterNodeId = 
this.store.selectSignal(selectClusterNodeIdFromRoute);
     selectedClusterRepoId = 
this.store.selectSignal(selectClusterStorageRepositoryIdFromRoute);
@@ -70,9 +75,11 @@ export class ClusterContentStorageListing {
         })
     );
 
-    isInitialLoading(loadedTimestamp: string): boolean {
-        return loadedTimestamp == initialClusterState.loadedTimestamp;
-    }
+    isInitialLoading = computed(
+        () =>
+            this.loadedTimestamp() == initialClusterState.loadedTimestamp ||
+            this.systemDiagnosticsLoadedTimestamp() == 
initialSystemDiagnosticsState.loadedTimestamp
+    );
 
     selectStorageNode(node: ClusterNodeRepositoryStorageUsage): void {
         this.store.dispatch(
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.html
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.html
index 7276ea2ca4..9637602936 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.html
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.html
@@ -16,7 +16,7 @@
   -->
 
 <ng-container>
-    @if (isInitialLoading(loadedTimestamp())) {
+    @if (isInitialLoading()) {
         <div>
             <ngx-skeleton-loader count="3"></ngx-skeleton-loader>
         </div>
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.spec.ts
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.spec.ts
index 2b081a9f53..d3cc05d825 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.spec.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.spec.ts
@@ -21,16 +21,23 @@ import { ClusterFlowFileStorageListing } from 
'./cluster-flow-file-storage-listi
 import { clusterListingFeatureKey } from '../../state/cluster-listing';
 import { initialClusterState } from 
'../../state/cluster-listing/cluster-listing.reducer';
 import { clusterFeatureKey } from '../../state';
-import { provideMockStore } from '@ngrx/store/testing';
-import { selectClusterListing } from 
'../../state/cluster-listing/cluster-listing.selectors';
+import { provideMockStore, MockStore } from '@ngrx/store/testing';
+import {
+    selectClusterListing,
+    selectClusterListingLoadedTimestamp
+} from '../../state/cluster-listing/cluster-listing.selectors';
+import { selectSystemDiagnosticsLoadedTimestamp } from 
'../../../../state/system-diagnostics/system-diagnostics.selectors';
 import { initialState as initialErrorState } from 
'../../../../state/error/error.reducer';
 import { errorFeatureKey } from '../../../../state/error';
 import { initialState as initialCurrentUserState } from 
'../../../../state/current-user/current-user.reducer';
 import { currentUserFeatureKey } from '../../../../state/current-user';
+import { initialSystemDiagnosticsState } from 
'../../../../state/system-diagnostics/system-diagnostics.reducer';
+import { systemDiagnosticsFeatureKey } from 
'../../../../state/system-diagnostics';
 
 describe('ClusterFlowFileStorageListing', () => {
     let component: ClusterFlowFileStorageListing;
     let fixture: ComponentFixture<ClusterFlowFileStorageListing>;
+    let store: MockStore;
 
     beforeEach(async () => {
         const initialState = {
@@ -38,7 +45,8 @@ describe('ClusterFlowFileStorageListing', () => {
             [currentUserFeatureKey]: initialCurrentUserState,
             [clusterFeatureKey]: {
                 [clusterListingFeatureKey]: initialClusterState
-            }
+            },
+            [systemDiagnosticsFeatureKey]: initialSystemDiagnosticsState
         };
         await TestBed.configureTestingModule({
             imports: [ClusterFlowFileStorageListing],
@@ -55,6 +63,7 @@ describe('ClusterFlowFileStorageListing', () => {
             ]
         }).compileComponents();
 
+        store = TestBed.inject(MockStore);
         fixture = TestBed.createComponent(ClusterFlowFileStorageListing);
         component = fixture.componentInstance;
         fixture.detectChanges();
@@ -63,4 +72,46 @@ describe('ClusterFlowFileStorageListing', () => {
     it('should create', () => {
         expect(component).toBeTruthy();
     });
+
+    describe('isInitialLoading', () => {
+        it('should return true when both timestamps are empty (initial 
state)', () => {
+            expect(component.isInitialLoading()).toBe(true);
+        });
+
+        it('should return true when cluster timestamp is empty but system 
diagnostics has loaded', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, '');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, 
'10:30:00 UTC');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(true);
+        });
+
+        it('should return true when system diagnostics timestamp is empty but 
cluster has loaded', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, 
'10:30:00 UTC');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, '');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(true);
+        });
+
+        it('should return false when both timestamps are populated', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, 
'10:30:00 UTC');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, 
'10:30:05 UTC');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(false);
+        });
+
+        it('should return false when data has been loaded and user triggers a 
refresh', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, 
'10:30:00 UTC');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, 
'10:30:05 UTC');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(false);
+        });
+    });
 });
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.ts
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.ts
index 77db7efe96..4a3df3b9e3 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.ts
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-import { Component, inject } from '@angular/core';
+import { Component, computed, inject } from '@angular/core';
 import { RepositoryStorageTable } from 
'../common/repository-storage-table/repository-storage-table.component';
 import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
 import {
@@ -23,7 +23,10 @@ import {
     selectClusterListingStatus,
     selectClusterNodeIdFromRoute
 } from '../../state/cluster-listing/cluster-listing.selectors';
-import { selectSystemNodeSnapshots } from 
'../../../../state/system-diagnostics/system-diagnostics.selectors';
+import {
+    selectSystemDiagnosticsLoadedTimestamp,
+    selectSystemNodeSnapshots
+} from '../../../../state/system-diagnostics/system-diagnostics.selectors';
 import { Store } from '@ngrx/store';
 import { NiFiState } from '../../../../state';
 import { initialClusterState } from 
'../../state/cluster-listing/cluster-listing.reducer';
@@ -36,6 +39,7 @@ import {
     selectFlowFileStorageNode
 } from '../../state/cluster-listing/cluster-listing.actions';
 import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { initialSystemDiagnosticsState } from 
'../../../../state/system-diagnostics/system-diagnostics.reducer';
 
 @Component({
     selector: 'cluster-flow-file-storage-listing',
@@ -47,6 +51,7 @@ export class ClusterFlowFileStorageListing {
     private store = inject<Store<NiFiState>>(Store);
 
     loadedTimestamp = 
this.store.selectSignal(selectClusterListingLoadedTimestamp);
+    systemDiagnosticsLoadedTimestamp = 
this.store.selectSignal(selectSystemDiagnosticsLoadedTimestamp);
     listingStatus = this.store.selectSignal(selectClusterListingStatus);
     selectedClusterNodeId = 
this.store.selectSignal(selectClusterNodeIdFromRoute);
     components$ = this.store.select(selectSystemNodeSnapshots).pipe(
@@ -64,9 +69,11 @@ export class ClusterFlowFileStorageListing {
         })
     );
 
-    isInitialLoading(loadedTimestamp: string): boolean {
-        return loadedTimestamp == initialClusterState.loadedTimestamp;
-    }
+    isInitialLoading = computed(
+        () =>
+            this.loadedTimestamp() == initialClusterState.loadedTimestamp ||
+            this.systemDiagnosticsLoadedTimestamp() == 
initialSystemDiagnosticsState.loadedTimestamp
+    );
 
     selectStorageNode(node: ClusterNodeRepositoryStorageUsage): void {
         this.store.dispatch(selectFlowFileStorageNode({ request: { id: 
node.nodeId } }));
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.html
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.html
index ea7c03025c..5ec125e6fd 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.html
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.html
@@ -16,7 +16,7 @@
   -->
 
 <ng-container>
-    @if (isInitialLoading(loadedTimestamp())) {
+    @if (isInitialLoading()) {
         <div>
             <ngx-skeleton-loader count="3"></ngx-skeleton-loader>
         </div>
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.spec.ts
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.spec.ts
index 3775a04b17..f9f9c4d67d 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.spec.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.spec.ts
@@ -21,16 +21,23 @@ import { ClusterJvmListing } from 
'./cluster-jvm-listing.component';
 import { clusterListingFeatureKey } from '../../state/cluster-listing';
 import { initialClusterState } from 
'../../state/cluster-listing/cluster-listing.reducer';
 import { clusterFeatureKey } from '../../state';
-import { provideMockStore } from '@ngrx/store/testing';
-import { selectClusterListing } from 
'../../state/cluster-listing/cluster-listing.selectors';
+import { provideMockStore, MockStore } from '@ngrx/store/testing';
+import {
+    selectClusterListing,
+    selectClusterListingLoadedTimestamp
+} from '../../state/cluster-listing/cluster-listing.selectors';
+import { selectSystemDiagnosticsLoadedTimestamp } from 
'../../../../state/system-diagnostics/system-diagnostics.selectors';
 import { initialState as initialErrorState } from 
'../../../../state/error/error.reducer';
 import { errorFeatureKey } from '../../../../state/error';
 import { initialState as initialCurrentUserState } from 
'../../../../state/current-user/current-user.reducer';
 import { currentUserFeatureKey } from '../../../../state/current-user';
+import { initialSystemDiagnosticsState } from 
'../../../../state/system-diagnostics/system-diagnostics.reducer';
+import { systemDiagnosticsFeatureKey } from 
'../../../../state/system-diagnostics';
 
 describe('ClusterJvmListing', () => {
     let component: ClusterJvmListing;
     let fixture: ComponentFixture<ClusterJvmListing>;
+    let store: MockStore;
 
     beforeEach(async () => {
         const initialState = {
@@ -38,7 +45,8 @@ describe('ClusterJvmListing', () => {
             [currentUserFeatureKey]: initialCurrentUserState,
             [clusterFeatureKey]: {
                 [clusterListingFeatureKey]: initialClusterState
-            }
+            },
+            [systemDiagnosticsFeatureKey]: initialSystemDiagnosticsState
         };
 
         await TestBed.configureTestingModule({
@@ -56,6 +64,7 @@ describe('ClusterJvmListing', () => {
             ]
         }).compileComponents();
 
+        store = TestBed.inject(MockStore);
         fixture = TestBed.createComponent(ClusterJvmListing);
         component = fixture.componentInstance;
         fixture.detectChanges();
@@ -64,4 +73,46 @@ describe('ClusterJvmListing', () => {
     it('should create', () => {
         expect(component).toBeTruthy();
     });
+
+    describe('isInitialLoading', () => {
+        it('should return true when both timestamps are empty (initial 
state)', () => {
+            expect(component.isInitialLoading()).toBe(true);
+        });
+
+        it('should return true when cluster timestamp is empty but system 
diagnostics has loaded', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, '');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, 
'10:30:00 UTC');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(true);
+        });
+
+        it('should return true when system diagnostics timestamp is empty but 
cluster has loaded', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, 
'10:30:00 UTC');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, '');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(true);
+        });
+
+        it('should return false when both timestamps are populated', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, 
'10:30:00 UTC');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, 
'10:30:05 UTC');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(false);
+        });
+
+        it('should return false when data has been loaded and user triggers a 
refresh', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, 
'10:30:00 UTC');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, 
'10:30:05 UTC');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(false);
+        });
+    });
 });
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.ts
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.ts
index 6715a2fb1a..f0478b8979 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.ts
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-import { Component, inject } from '@angular/core';
+import { Component, computed, inject } from '@angular/core';
 import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
 import { ClusterJvmTable } from 
'./cluster-jvm-table/cluster-jvm-table.component';
 import {
@@ -23,12 +23,16 @@ import {
     selectClusterListingStatus,
     selectClusterNodeIdFromRoute
 } from '../../state/cluster-listing/cluster-listing.selectors';
-import { selectSystemNodeSnapshots } from 
'../../../../state/system-diagnostics/system-diagnostics.selectors';
+import {
+    selectSystemDiagnosticsLoadedTimestamp,
+    selectSystemNodeSnapshots
+} from '../../../../state/system-diagnostics/system-diagnostics.selectors';
 import { Store } from '@ngrx/store';
 import { NiFiState } from '../../../../state';
 import { initialClusterState } from 
'../../state/cluster-listing/cluster-listing.reducer';
 import { NodeSnapshot } from '../../../../state/system-diagnostics';
 import { clearJvmNodeSelection, selectJvmNode } from 
'../../state/cluster-listing/cluster-listing.actions';
+import { initialSystemDiagnosticsState } from 
'../../../../state/system-diagnostics/system-diagnostics.reducer';
 
 @Component({
     selector: 'cluster-jvm-listing',
@@ -40,13 +44,16 @@ export class ClusterJvmListing {
     private store = inject<Store<NiFiState>>(Store);
 
     loadedTimestamp = 
this.store.selectSignal(selectClusterListingLoadedTimestamp);
+    systemDiagnosticsLoadedTimestamp = 
this.store.selectSignal(selectSystemDiagnosticsLoadedTimestamp);
     listingStatus = this.store.selectSignal(selectClusterListingStatus);
     selectedClusterNodeId = 
this.store.selectSignal(selectClusterNodeIdFromRoute);
     nodes = this.store.selectSignal(selectSystemNodeSnapshots);
 
-    isInitialLoading(loadedTimestamp: string): boolean {
-        return loadedTimestamp == initialClusterState.loadedTimestamp;
-    }
+    isInitialLoading = computed(
+        () =>
+            this.loadedTimestamp() == initialClusterState.loadedTimestamp ||
+            this.systemDiagnosticsLoadedTimestamp() == 
initialSystemDiagnosticsState.loadedTimestamp
+    );
 
     selectNode(node: NodeSnapshot): void {
         this.store.dispatch(selectJvmNode({ request: { id: node.nodeId } }));
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.html
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.html
index 66feaf7f0e..9ec108252d 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.html
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.html
@@ -16,7 +16,7 @@
   -->
 
 <ng-container>
-    @if (isInitialLoading(loadedTimestamp())) {
+    @if (isInitialLoading()) {
         <div>
             <ngx-skeleton-loader count="3"></ngx-skeleton-loader>
         </div>
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.spec.ts
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.spec.ts
index 04ebe65f10..a4196c4a8e 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.spec.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.spec.ts
@@ -21,16 +21,23 @@ import { ClusterProvenanceStorageListing } from 
'./cluster-provenance-storage-li
 import { clusterListingFeatureKey } from '../../state/cluster-listing';
 import { initialClusterState } from 
'../../state/cluster-listing/cluster-listing.reducer';
 import { clusterFeatureKey } from '../../state';
-import { provideMockStore } from '@ngrx/store/testing';
-import { selectClusterListing } from 
'../../state/cluster-listing/cluster-listing.selectors';
+import { provideMockStore, MockStore } from '@ngrx/store/testing';
+import {
+    selectClusterListing,
+    selectClusterListingLoadedTimestamp
+} from '../../state/cluster-listing/cluster-listing.selectors';
+import { selectSystemDiagnosticsLoadedTimestamp } from 
'../../../../state/system-diagnostics/system-diagnostics.selectors';
 import { initialState as initialErrorState } from 
'../../../../state/error/error.reducer';
 import { errorFeatureKey } from '../../../../state/error';
 import { initialState as initialCurrentUserState } from 
'../../../../state/current-user/current-user.reducer';
 import { currentUserFeatureKey } from '../../../../state/current-user';
+import { initialSystemDiagnosticsState } from 
'../../../../state/system-diagnostics/system-diagnostics.reducer';
+import { systemDiagnosticsFeatureKey } from 
'../../../../state/system-diagnostics';
 
 describe('ClusterProvenanceStorageListing', () => {
     let component: ClusterProvenanceStorageListing;
     let fixture: ComponentFixture<ClusterProvenanceStorageListing>;
+    let store: MockStore;
 
     beforeEach(async () => {
         const initialState = {
@@ -38,7 +45,8 @@ describe('ClusterProvenanceStorageListing', () => {
             [currentUserFeatureKey]: initialCurrentUserState,
             [clusterFeatureKey]: {
                 [clusterListingFeatureKey]: initialClusterState
-            }
+            },
+            [systemDiagnosticsFeatureKey]: initialSystemDiagnosticsState
         };
 
         await TestBed.configureTestingModule({
@@ -56,6 +64,7 @@ describe('ClusterProvenanceStorageListing', () => {
             ]
         }).compileComponents();
 
+        store = TestBed.inject(MockStore);
         fixture = TestBed.createComponent(ClusterProvenanceStorageListing);
         component = fixture.componentInstance;
         fixture.detectChanges();
@@ -64,4 +73,46 @@ describe('ClusterProvenanceStorageListing', () => {
     it('should create', () => {
         expect(component).toBeTruthy();
     });
+
+    describe('isInitialLoading', () => {
+        it('should return true when both timestamps are empty (initial 
state)', () => {
+            expect(component.isInitialLoading()).toBe(true);
+        });
+
+        it('should return true when cluster timestamp is empty but system 
diagnostics has loaded', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, '');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, 
'10:30:00 UTC');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(true);
+        });
+
+        it('should return true when system diagnostics timestamp is empty but 
cluster has loaded', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, 
'10:30:00 UTC');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, '');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(true);
+        });
+
+        it('should return false when both timestamps are populated', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, 
'10:30:00 UTC');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, 
'10:30:05 UTC');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(false);
+        });
+
+        it('should return false when data has been loaded and user triggers a 
refresh', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, 
'10:30:00 UTC');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, 
'10:30:05 UTC');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(false);
+        });
+    });
 });
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.ts
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.ts
index 334b7bca58..f47c8896ef 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.ts
@@ -15,14 +15,17 @@
  * limitations under the License.
  */
 
-import { Component, inject } from '@angular/core';
+import { Component, computed, inject } from '@angular/core';
 import {
     selectClusterListingLoadedTimestamp,
     selectClusterListingStatus,
     selectClusterNodeIdFromRoute,
     selectClusterStorageRepositoryIdFromRoute
 } from '../../state/cluster-listing/cluster-listing.selectors';
-import { selectSystemNodeSnapshots } from 
'../../../../state/system-diagnostics/system-diagnostics.selectors';
+import {
+    selectSystemDiagnosticsLoadedTimestamp,
+    selectSystemNodeSnapshots
+} from '../../../../state/system-diagnostics/system-diagnostics.selectors';
 import { isDefinedAndNotNull } from '@nifi/shared';
 import { map } from 'rxjs';
 import { ClusterNodeRepositoryStorageUsage } from 
'../../../../state/system-diagnostics';
@@ -37,6 +40,7 @@ import { AsyncPipe } from '@angular/common';
 import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
 import { RepositoryStorageTable } from 
'../common/repository-storage-table/repository-storage-table.component';
 import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { initialSystemDiagnosticsState } from 
'../../../../state/system-diagnostics/system-diagnostics.reducer';
 
 @Component({
     selector: 'cluster-provenance-storage-listing',
@@ -48,6 +52,7 @@ export class ClusterProvenanceStorageListing {
     private store = inject<Store<NiFiState>>(Store);
 
     loadedTimestamp = 
this.store.selectSignal(selectClusterListingLoadedTimestamp);
+    systemDiagnosticsLoadedTimestamp = 
this.store.selectSignal(selectSystemDiagnosticsLoadedTimestamp);
     listingStatus = this.store.selectSignal(selectClusterListingStatus);
     selectedClusterNodeId = 
this.store.selectSignal(selectClusterNodeIdFromRoute);
     selectedClusterRepoId = 
this.store.selectSignal(selectClusterStorageRepositoryIdFromRoute);
@@ -70,9 +75,11 @@ export class ClusterProvenanceStorageListing {
         })
     );
 
-    isInitialLoading(loadedTimestamp: string): boolean {
-        return loadedTimestamp == initialClusterState.loadedTimestamp;
-    }
+    isInitialLoading = computed(
+        () =>
+            this.loadedTimestamp() == initialClusterState.loadedTimestamp ||
+            this.systemDiagnosticsLoadedTimestamp() == 
initialSystemDiagnosticsState.loadedTimestamp
+    );
 
     selectStorageNode(node: ClusterNodeRepositoryStorageUsage): void {
         this.store.dispatch(
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.html
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.html
index aba7db4deb..2b797adf9d 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.html
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.html
@@ -16,7 +16,7 @@
   -->
 
 <ng-container>
-    @if (isInitialLoading(loadedTimestamp())) {
+    @if (isInitialLoading()) {
         <div>
             <ngx-skeleton-loader count="3"></ngx-skeleton-loader>
         </div>
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.spec.ts
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.spec.ts
index a388c959e8..b8ff559b10 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.spec.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.spec.ts
@@ -21,16 +21,23 @@ import { ClusterSystemListing } from 
'./cluster-system-listing.component';
 import { clusterListingFeatureKey } from '../../state/cluster-listing';
 import { initialClusterState } from 
'../../state/cluster-listing/cluster-listing.reducer';
 import { clusterFeatureKey } from '../../state';
-import { provideMockStore } from '@ngrx/store/testing';
-import { selectClusterListing } from 
'../../state/cluster-listing/cluster-listing.selectors';
+import { provideMockStore, MockStore } from '@ngrx/store/testing';
+import {
+    selectClusterListing,
+    selectClusterListingLoadedTimestamp
+} from '../../state/cluster-listing/cluster-listing.selectors';
+import { selectSystemDiagnosticsLoadedTimestamp } from 
'../../../../state/system-diagnostics/system-diagnostics.selectors';
 import { initialState as initialErrorState } from 
'../../../../state/error/error.reducer';
 import { errorFeatureKey } from '../../../../state/error';
 import { initialState as initialCurrentUserState } from 
'../../../../state/current-user/current-user.reducer';
 import { currentUserFeatureKey } from '../../../../state/current-user';
+import { initialSystemDiagnosticsState } from 
'../../../../state/system-diagnostics/system-diagnostics.reducer';
+import { systemDiagnosticsFeatureKey } from 
'../../../../state/system-diagnostics';
 
 describe('ClusterSystemListing', () => {
     let component: ClusterSystemListing;
     let fixture: ComponentFixture<ClusterSystemListing>;
+    let store: MockStore;
 
     beforeEach(async () => {
         const initialState = {
@@ -38,7 +45,8 @@ describe('ClusterSystemListing', () => {
             [currentUserFeatureKey]: initialCurrentUserState,
             [clusterFeatureKey]: {
                 [clusterListingFeatureKey]: initialClusterState
-            }
+            },
+            [systemDiagnosticsFeatureKey]: initialSystemDiagnosticsState
         };
         await TestBed.configureTestingModule({
             imports: [ClusterSystemListing],
@@ -55,6 +63,7 @@ describe('ClusterSystemListing', () => {
             ]
         }).compileComponents();
 
+        store = TestBed.inject(MockStore);
         fixture = TestBed.createComponent(ClusterSystemListing);
         component = fixture.componentInstance;
         fixture.detectChanges();
@@ -63,4 +72,46 @@ describe('ClusterSystemListing', () => {
     it('should create', () => {
         expect(component).toBeTruthy();
     });
+
+    describe('isInitialLoading', () => {
+        it('should return true when both timestamps are empty (initial 
state)', () => {
+            expect(component.isInitialLoading()).toBe(true);
+        });
+
+        it('should return true when cluster timestamp is empty but system 
diagnostics has loaded', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, '');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, 
'10:30:00 UTC');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(true);
+        });
+
+        it('should return true when system diagnostics timestamp is empty but 
cluster has loaded', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, 
'10:30:00 UTC');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, '');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(true);
+        });
+
+        it('should return false when both timestamps are populated', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, 
'10:30:00 UTC');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, 
'10:30:05 UTC');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(false);
+        });
+
+        it('should return false when data has been loaded and user triggers a 
refresh', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, 
'10:30:00 UTC');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, 
'10:30:05 UTC');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(false);
+        });
+    });
 });
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.ts
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.ts
index 8db35f7683..ebd7f66a60 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.ts
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-import { Component, inject } from '@angular/core';
+import { Component, computed, inject } from '@angular/core';
 import {
     selectClusterListingLoadedTimestamp,
     selectClusterListingStatus,
@@ -25,10 +25,14 @@ import { Store } from '@ngrx/store';
 import { NiFiState } from '../../../../state';
 import { initialClusterState } from 
'../../state/cluster-listing/cluster-listing.reducer';
 import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
-import { selectSystemNodeSnapshots } from 
'../../../../state/system-diagnostics/system-diagnostics.selectors';
+import {
+    selectSystemDiagnosticsLoadedTimestamp,
+    selectSystemNodeSnapshots
+} from '../../../../state/system-diagnostics/system-diagnostics.selectors';
 import { ClusterSystemTable } from 
'./cluster-system-table/cluster-system-table.component';
 import { clearSystemNodeSelection, selectSystemNode } from 
'../../state/cluster-listing/cluster-listing.actions';
 import { NodeSnapshot } from '../../../../state/system-diagnostics';
+import { initialSystemDiagnosticsState } from 
'../../../../state/system-diagnostics/system-diagnostics.reducer';
 
 @Component({
     selector: 'cluster-system-listing',
@@ -40,13 +44,16 @@ export class ClusterSystemListing {
     private store = inject<Store<NiFiState>>(Store);
 
     loadedTimestamp = 
this.store.selectSignal(selectClusterListingLoadedTimestamp);
+    systemDiagnosticsLoadedTimestamp = 
this.store.selectSignal(selectSystemDiagnosticsLoadedTimestamp);
     listingStatus = this.store.selectSignal(selectClusterListingStatus);
     selectedClusterNodeId = 
this.store.selectSignal(selectClusterNodeIdFromRoute);
     nodes = this.store.selectSignal(selectSystemNodeSnapshots);
 
-    isInitialLoading(loadedTimestamp: string): boolean {
-        return loadedTimestamp == initialClusterState.loadedTimestamp;
-    }
+    isInitialLoading = computed(
+        () =>
+            this.loadedTimestamp() == initialClusterState.loadedTimestamp ||
+            this.systemDiagnosticsLoadedTimestamp() == 
initialSystemDiagnosticsState.loadedTimestamp
+    );
 
     selectNode(node: NodeSnapshot): void {
         this.store.dispatch(selectSystemNode({ request: { id: node.nodeId } 
}));
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.html
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.html
index 96e1007bf4..1954fd9bd5 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.html
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.html
@@ -16,7 +16,7 @@
   -->
 
 <ng-container>
-    @if (isInitialLoading(loadedTimestamp())) {
+    @if (isInitialLoading()) {
         <div>
             <ngx-skeleton-loader count="3"></ngx-skeleton-loader>
         </div>
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.spec.ts
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.spec.ts
index de1a7cea75..b8f4ae5c05 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.spec.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.spec.ts
@@ -21,16 +21,23 @@ import { ClusterVersionListing } from 
'./cluster-version-listing.component';
 import { clusterListingFeatureKey } from '../../state/cluster-listing';
 import { initialClusterState } from 
'../../state/cluster-listing/cluster-listing.reducer';
 import { clusterFeatureKey } from '../../state';
-import { provideMockStore } from '@ngrx/store/testing';
-import { selectClusterListing } from 
'../../state/cluster-listing/cluster-listing.selectors';
+import { provideMockStore, MockStore } from '@ngrx/store/testing';
+import {
+    selectClusterListing,
+    selectClusterListingLoadedTimestamp
+} from '../../state/cluster-listing/cluster-listing.selectors';
+import { selectSystemDiagnosticsLoadedTimestamp } from 
'../../../../state/system-diagnostics/system-diagnostics.selectors';
 import { initialState as initialErrorState } from 
'../../../../state/error/error.reducer';
 import { errorFeatureKey } from '../../../../state/error';
 import { initialState as initialCurrentUserState } from 
'../../../../state/current-user/current-user.reducer';
 import { currentUserFeatureKey } from '../../../../state/current-user';
+import { initialSystemDiagnosticsState } from 
'../../../../state/system-diagnostics/system-diagnostics.reducer';
+import { systemDiagnosticsFeatureKey } from 
'../../../../state/system-diagnostics';
 
 describe('ClusterVersionListing', () => {
     let component: ClusterVersionListing;
     let fixture: ComponentFixture<ClusterVersionListing>;
+    let store: MockStore;
 
     beforeEach(async () => {
         const initialState = {
@@ -38,7 +45,8 @@ describe('ClusterVersionListing', () => {
             [currentUserFeatureKey]: initialCurrentUserState,
             [clusterFeatureKey]: {
                 [clusterListingFeatureKey]: initialClusterState
-            }
+            },
+            [systemDiagnosticsFeatureKey]: initialSystemDiagnosticsState
         };
 
         await TestBed.configureTestingModule({
@@ -56,6 +64,7 @@ describe('ClusterVersionListing', () => {
             ]
         }).compileComponents();
 
+        store = TestBed.inject(MockStore);
         fixture = TestBed.createComponent(ClusterVersionListing);
         component = fixture.componentInstance;
         fixture.detectChanges();
@@ -64,4 +73,46 @@ describe('ClusterVersionListing', () => {
     it('should create', () => {
         expect(component).toBeTruthy();
     });
+
+    describe('isInitialLoading', () => {
+        it('should return true when both timestamps are empty (initial 
state)', () => {
+            expect(component.isInitialLoading()).toBe(true);
+        });
+
+        it('should return true when cluster timestamp is empty but system 
diagnostics has loaded', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, '');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, 
'10:30:00 UTC');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(true);
+        });
+
+        it('should return true when system diagnostics timestamp is empty but 
cluster has loaded', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, 
'10:30:00 UTC');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, '');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(true);
+        });
+
+        it('should return false when both timestamps are populated', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, 
'10:30:00 UTC');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, 
'10:30:05 UTC');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(false);
+        });
+
+        it('should return false when data has been loaded and user triggers a 
refresh', () => {
+            store.overrideSelector(selectClusterListingLoadedTimestamp, 
'10:30:00 UTC');
+            store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, 
'10:30:05 UTC');
+            store.refreshState();
+            fixture.detectChanges();
+
+            expect(component.isInitialLoading()).toBe(false);
+        });
+    });
 });
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.ts
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.ts
index eec2279d9e..adfeabd14f 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.ts
@@ -15,13 +15,16 @@
  * limitations under the License.
  */
 
-import { Component, inject } from '@angular/core';
+import { Component, computed, inject } from '@angular/core';
 import {
     selectClusterListingLoadedTimestamp,
     selectClusterListingStatus,
     selectClusterNodeIdFromRoute
 } from '../../state/cluster-listing/cluster-listing.selectors';
-import { selectSystemNodeSnapshots } from 
'../../../../state/system-diagnostics/system-diagnostics.selectors';
+import {
+    selectSystemDiagnosticsLoadedTimestamp,
+    selectSystemNodeSnapshots
+} from '../../../../state/system-diagnostics/system-diagnostics.selectors';
 import { Store } from '@ngrx/store';
 import { NiFiState } from '../../../../state';
 import { initialClusterState } from 
'../../state/cluster-listing/cluster-listing.reducer';
@@ -29,6 +32,7 @@ import { NodeSnapshot } from 
'../../../../state/system-diagnostics';
 import { clearVersionsNodeSelection, selectVersionNode } from 
'../../state/cluster-listing/cluster-listing.actions';
 import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
 import { ClusterVersionTable } from 
'./cluster-version-table/cluster-version-table.component';
+import { initialSystemDiagnosticsState } from 
'../../../../state/system-diagnostics/system-diagnostics.reducer';
 
 @Component({
     selector: 'cluster-version-listing',
@@ -40,13 +44,16 @@ export class ClusterVersionListing {
     private store = inject<Store<NiFiState>>(Store);
 
     loadedTimestamp = 
this.store.selectSignal(selectClusterListingLoadedTimestamp);
+    systemDiagnosticsLoadedTimestamp = 
this.store.selectSignal(selectSystemDiagnosticsLoadedTimestamp);
     listingStatus = this.store.selectSignal(selectClusterListingStatus);
     selectedClusterNodeId = 
this.store.selectSignal(selectClusterNodeIdFromRoute);
     nodes = this.store.selectSignal(selectSystemNodeSnapshots);
 
-    isInitialLoading(loadedTimestamp: string): boolean {
-        return loadedTimestamp == initialClusterState.loadedTimestamp;
-    }
+    isInitialLoading = computed(
+        () =>
+            this.loadedTimestamp() == initialClusterState.loadedTimestamp ||
+            this.systemDiagnosticsLoadedTimestamp() == 
initialSystemDiagnosticsState.loadedTimestamp
+    );
 
     selectNode(node: NodeSnapshot): void {
         this.store.dispatch(selectVersionNode({ request: { id: node.nodeId } 
}));

Reply via email to