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

mcgilman 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 d14686cb3a [NIFI-12415] Counters (#8077)
d14686cb3a is described below

commit d14686cb3ae16a501187e527bc32c91449674bb6
Author: Rob Fellows <[email protected]>
AuthorDate: Wed Nov 29 18:00:28 2023 -0500

    [NIFI-12415] Counters (#8077)
    
    * [NIFI-12415] Counters page
    * populate counter the table.
    * support counter reset.
    * filtering by name and by context
    * sorting, including initial sort
    * added basic tests
    
    * Formatted with Prettier
    
    * review feedback - removing unused things.
    
    * align mat-table usage to a common style
    
    * disable the Counter menu item if the user doesn't have 
counterPermissions.canRead
    
    This closes #8077
---
 .../nifi-web-frontend/src/main/nifi/package.json   |   1 +
 .../src/main/nifi/src/app/app-routing.module.ts    |   5 +
 .../pages/canvas/ui/header/header.component.html   | 116 +++++++++--------
 .../counters/feature/counters-routing.module.ts    |  33 +++++
 .../pages/counters/feature/counters.component.html |  28 ++++
 .../pages/counters/feature/counters.component.scss |  20 +++
 .../counters/feature/counters.component.spec.ts    |  44 +++++++
 .../pages/counters/feature/counters.component.ts   |  38 ++++++
 .../app/pages/counters/feature/counters.module.ts  |  41 ++++++
 .../app/pages/counters/service/counters.service.ts |  40 ++++++
 .../counter-listing/counter-listing.actions.ts     |  45 +++++++
 .../counter-listing/counter-listing.effects.ts     | 106 +++++++++++++++
 .../counter-listing/counter-listing.reducer.ts     |  61 +++++++++
 .../counter-listing/counter-listing.selectors.ts   |  27 ++++
 .../pages/counters/state/counter-listing/index.ts  |  45 +++++++
 .../nifi/src/app/pages/counters/state/index.ts     |  34 +++++
 .../counter-listing/counter-listing.component.html |  42 ++++++
 .../counter-listing/counter-listing.component.scss |  16 +++
 .../counter-listing.component.spec.ts              |  40 ++++++
 .../counter-listing/counter-listing.component.ts   |  58 +++++++++
 .../ui/counter-listing/counter-listing.module.ts   |  42 ++++++
 .../counter-table/counter-table.component.html     |  93 ++++++++++++++
 .../counter-table/counter-table.component.scss     |  16 +++
 .../counter-table/counter-table.component.spec.ts  |  52 ++++++++
 .../counter-table/counter-table.component.ts       | 142 +++++++++++++++++++++
 .../parameter-context-table.component.html         |   2 +-
 .../parameter-context-table.component.scss         |  60 ---------
 .../parameter-context-table.component.ts           |   2 +-
 .../parameter-table/parameter-table.component.html |   2 +-
 .../parameter-table/parameter-table.component.scss |  35 +----
 .../parameter-table/parameter-table.component.ts   |   2 +-
 .../reporting-task-table.component.html            |   2 +-
 .../reporting-task-table.component.scss            |  54 +-------
 .../reporting-task-table.component.ts              |   2 +-
 .../controller-service-table.component.html        |   2 +-
 .../controller-service-table.component.scss        |  46 +------
 .../controller-service-table.component.ts          |   2 +-
 .../property-table/property-table.component.html   |   2 +-
 .../property-table/property-table.component.scss   |  37 +-----
 .../property-table/property-table.component.ts     |   2 +-
 .../styles/listing-table.scss}                     |  26 ++--
 41 files changed, 1156 insertions(+), 307 deletions(-)

diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/package.json
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/package.json
index 1f33078257..88400f65cc 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/package.json
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/package.json
@@ -7,6 +7,7 @@
         "build": "ng build",
         "watch": "ng build --watch --configuration development",
         "test": "ng test --karma-config=karma.conf.js --watch=false",
+        "prettier": "prettier --config .prettierrc . --check",
         "prettier-format": "prettier --config .prettierrc . --write",
         "ci": "npm ci --ignore-scripts"
     },
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/app-routing.module.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/app-routing.module.ts
index 4ba1aad916..7e39ca6974 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/app-routing.module.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/app-routing.module.ts
@@ -37,6 +37,11 @@ const routes: Routes = [
                 (m) => m.ParameterContextsModule
             )
     },
+    {
+        path: 'counters',
+        canMatch: [authGuard],
+        loadChildren: () => 
import('./pages/counters/feature/counters.module').then((m) => m.CountersModule)
+    },
     {
         path: '',
         canMatch: [authGuard],
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/canvas/ui/header/header.component.html
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/canvas/ui/header/header.component.html
index 6421d46024..af4af65ca4 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/canvas/ui/header/header.component.html
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/canvas/ui/header/header.component.html
@@ -64,62 +64,66 @@
                     <i class="fa fa-navicon"></i>
                 </button>
                 <mat-menu #globalMenu="matMenu" xPosition="before">
-                    <button mat-menu-item class="global-menu-item">
-                        <i class="fa fa-fw fa-table mr-2"></i>
-                        Summary
-                    </button>
-                    <button mat-menu-item class="global-menu-item">
-                        <i class="icon fa-fw icon-counter mr-2"></i>
-                        Counter
-                    </button>
-                    <button mat-menu-item class="global-menu-item">
-                        <i class="fa fa-fw fa-sticky-note-o mr-2"></i>
-                        Bulletin Board
-                    </button>
-                    <mat-divider></mat-divider>
-                    <button mat-menu-item class="global-menu-item">
-                        <i class="icon fa-fw icon-provenance mr-2"></i>
-                        Data Provenance
-                    </button>
-                    <mat-divider></mat-divider>
-                    <button mat-menu-item class="global-menu-item" 
[routerLink]="['/settings']">
-                        <i class="fa fa-fw fa-wrench mr-2"></i>
-                        Controller Settings
-                    </button>
-                    <button mat-menu-item class="global-menu-item" 
[routerLink]="['/parameter-contexts']">
-                        <i class="fa fa-fw mr-2"></i>
-                        Parameter Contexts
-                    </button>
-                    <button mat-menu-item class="global-menu-item">
-                        <i class="fa fa-fw fa-cubes mr-2"></i>
-                        Cluster
-                    </button>
-                    <button mat-menu-item class="global-menu-item">
-                        <i class="fa fa-fw fa-history mr-2"></i>
-                        Flow Configuration History
-                    </button>
-                    <button mat-menu-item class="global-menu-item">
-                        <i class="fa fa-fw fa-area-chart mr-2"></i>
-                        Node Status History
-                    </button>
-                    <mat-divider></mat-divider>
-                    <button mat-menu-item class="global-menu-item">
-                        <i class="fa fa-fw fa-users mr-2"></i>
-                        Users
-                    </button>
-                    <button mat-menu-item class="global-menu-item">
-                        <i class="fa fa-fw fa-key mr-2"></i>
-                        Policies
-                    </button>
-                    <mat-divider></mat-divider>
-                    <button mat-menu-item class="global-menu-item">
-                        <i class="fa fa-fw fa-question-circle mr-2"></i>
-                        Help
-                    </button>
-                    <button mat-menu-item class="global-menu-item">
-                        <i class="fa fa-fw fa-info-circle mr-2"></i>
-                        About
-                    </button>
+                    <ng-container *ngIf="currentUser$ | async as user">
+                        <button mat-menu-item class="global-menu-item">
+                            <i class="fa fa-fw fa-table mr-2"></i>
+                            Summary
+                        </button>
+                        <button mat-menu-item class="global-menu-item"
+                                [routerLink]="['/counters']"
+                                [disabled]="!user.countersPermissions.canRead">
+                            <i class="icon fa-fw icon-counter mr-2"></i>
+                            Counter
+                        </button>
+                        <button mat-menu-item class="global-menu-item">
+                            <i class="fa fa-fw fa-sticky-note-o mr-2"></i>
+                            Bulletin Board
+                        </button>
+                        <mat-divider></mat-divider>
+                        <button mat-menu-item class="global-menu-item">
+                            <i class="icon fa-fw icon-provenance mr-2"></i>
+                            Data Provenance
+                        </button>
+                        <mat-divider></mat-divider>
+                        <button mat-menu-item class="global-menu-item" 
[routerLink]="['/settings']">
+                            <i class="fa fa-fw fa-wrench mr-2"></i>
+                            Controller Settings
+                        </button>
+                        <button mat-menu-item class="global-menu-item" 
[routerLink]="['/parameter-contexts']">
+                            <i class="fa fa-fw mr-2"></i>
+                            Parameter Contexts
+                        </button>
+                        <button mat-menu-item class="global-menu-item">
+                            <i class="fa fa-fw fa-cubes mr-2"></i>
+                            Cluster
+                        </button>
+                        <button mat-menu-item class="global-menu-item">
+                            <i class="fa fa-fw fa-history mr-2"></i>
+                            Flow Configuration History
+                        </button>
+                        <button mat-menu-item class="global-menu-item">
+                            <i class="fa fa-fw fa-area-chart mr-2"></i>
+                            Node Status History
+                        </button>
+                        <mat-divider></mat-divider>
+                        <button mat-menu-item class="global-menu-item">
+                            <i class="fa fa-fw fa-users mr-2"></i>
+                            Users
+                        </button>
+                        <button mat-menu-item class="global-menu-item">
+                            <i class="fa fa-fw fa-key mr-2"></i>
+                            Policies
+                        </button>
+                        <mat-divider></mat-divider>
+                        <button mat-menu-item class="global-menu-item">
+                            <i class="fa fa-fw fa-question-circle mr-2"></i>
+                            Help
+                        </button>
+                        <button mat-menu-item class="global-menu-item">
+                            <i class="fa fa-fw fa-info-circle mr-2"></i>
+                            About
+                        </button>
+                    </ng-container>
                 </mat-menu>
             </div>
         </div>
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/feature/counters-routing.module.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/feature/counters-routing.module.ts
new file mode 100644
index 0000000000..bce25cce01
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/feature/counters-routing.module.ts
@@ -0,0 +1,33 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { RouterModule, Routes } from '@angular/router';
+import { NgModule } from '@angular/core';
+import { Counters } from './counters.component';
+
+const routes: Routes = [
+    {
+        path: '',
+        component: Counters
+    }
+];
+
+@NgModule({
+    imports: [RouterModule.forChild(routes)],
+    exports: [RouterModule]
+})
+export class CountersRoutingModule {}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/feature/counters.component.html
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/feature/counters.component.html
new file mode 100644
index 0000000000..73fcf5e390
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/feature/counters.component.html
@@ -0,0 +1,28 @@
+<!--
+  ~  Licensed to the Apache Software Foundation (ASF) under one or more
+  ~  contributor license agreements.  See the NOTICE file distributed with
+  ~  this work for additional information regarding copyright ownership.
+  ~  The ASF licenses this file to You under the Apache License, Version 2.0
+  ~  (the "License"); you may not use this file except in compliance with
+  ~  the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  -->
+
+<div class="p-4 flex flex-col h-screen justify-between gap-y-5">
+    <div class="flex justify-between">
+        <h3 class="text-xl bold counter-header">NiFi Counters</h3>
+        <button class="nifi-button" [routerLink]="['/']">
+            <i class="fa fa-times"></i>
+        </button>
+    </div>
+    <div class="flex-1">
+        <counter-listing></counter-listing>
+    </div>
+</div>
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/feature/counters.component.scss
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/feature/counters.component.scss
new file mode 100644
index 0000000000..7efe128f47
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/feature/counters.component.scss
@@ -0,0 +1,20 @@
+/*!
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+.counter-header {
+    color: #728e9b;
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/feature/counters.component.spec.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/feature/counters.component.spec.ts
new file mode 100644
index 0000000000..e30779c978
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/feature/counters.component.spec.ts
@@ -0,0 +1,44 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { Counters } from './counters.component';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { provideMockStore } from '@ngrx/store/testing';
+import { initialState } from 
'../state/counter-listing/counter-listing.reducer';
+import { CounterListing } from 
'../ui/counter-listing/counter-listing.component';
+import { RouterModule } from '@angular/router';
+import { RouterTestingModule } from '@angular/router/testing';
+
+describe('Counters', () => {
+    let component: Counters;
+    let fixture: ComponentFixture<Counters>;
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [Counters, CounterListing],
+            imports: [RouterModule, RouterTestingModule],
+            providers: [provideMockStore({ initialState })]
+        });
+        fixture = TestBed.createComponent(Counters);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+});
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/feature/counters.component.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/feature/counters.component.ts
new file mode 100644
index 0000000000..6d3998b6d2
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/feature/counters.component.ts
@@ -0,0 +1,38 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { Component, OnDestroy, OnInit } from '@angular/core';
+import { Store } from '@ngrx/store';
+import { NiFiState } from '../../../state';
+import { startUserPolling, stopUserPolling } from 
'../../../state/user/user.actions';
+
+@Component({
+    selector: 'counters',
+    templateUrl: './counters.component.html',
+    styleUrls: ['./counters.component.scss']
+})
+export class Counters implements OnInit, OnDestroy {
+    constructor(private store: Store<NiFiState>) {}
+
+    ngOnInit(): void {
+        this.store.dispatch(startUserPolling());
+    }
+
+    ngOnDestroy(): void {
+        this.store.dispatch(stopUserPolling());
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/feature/counters.module.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/feature/counters.module.ts
new file mode 100644
index 0000000000..33ea2bf6a2
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/feature/counters.module.ts
@@ -0,0 +1,41 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Counters } from './counters.component';
+import { CountersRoutingModule } from './counters-routing.module';
+import { StoreModule } from '@ngrx/store';
+import { countersFeatureKey, reducers } from '../state';
+import { EffectsModule } from '@ngrx/effects';
+import { CounterListingEffects } from 
'../state/counter-listing/counter-listing.effects';
+import { CounterListingModule } from 
'../ui/counter-listing/counter-listing.module';
+import { ParameterContextListingModule } from 
'../../parameter-contexts/ui/parameter-context-listing/parameter-context-listing.module';
+
+@NgModule({
+    declarations: [Counters],
+    exports: [Counters],
+    imports: [
+        CommonModule,
+        CountersRoutingModule,
+        StoreModule.forFeature(countersFeatureKey, reducers),
+        EffectsModule.forFeature(CounterListingEffects),
+        CounterListingModule,
+        ParameterContextListingModule
+    ]
+})
+export class CountersModule {}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/service/counters.service.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/service/counters.service.ts
new file mode 100644
index 0000000000..987d23e757
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/service/counters.service.ts
@@ -0,0 +1,40 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Client } from '../../../service/client.service';
+import { Observable } from 'rxjs';
+import { CounterEntity, ResetCounterRequest } from '../state/counter-listing';
+
+@Injectable({ providedIn: 'root' })
+export class CountersService {
+    private static readonly API: string = '../nifi-api';
+
+    constructor(
+        private httpClient: HttpClient,
+        private client: Client
+    ) {}
+
+    getCounters(): Observable<any> {
+        return this.httpClient.get(`${CountersService.API}/counters`);
+    }
+
+    resetCounter(counterResetRequest: ResetCounterRequest): Observable<any> {
+        return 
this.httpClient.put(`${CountersService.API}/counters/${counterResetRequest.counter.id}`,
 null);
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/state/counter-listing/counter-listing.actions.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/state/counter-listing/counter-listing.actions.ts
new file mode 100644
index 0000000000..cb2eb9dd70
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/state/counter-listing/counter-listing.actions.ts
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { createAction, props } from '@ngrx/store';
+import { LoadCounterListingResponse, ResetCounterRequest, ResetCounterSuccess 
} from './index';
+
+const COUNTER_PREFIX: string = '[Counter Listing]';
+
+export const loadCounters = createAction(`${COUNTER_PREFIX} Load Counter 
Listing`);
+
+export const loadCountersSuccess = createAction(
+    `${COUNTER_PREFIX} Load Counter Listing Success`,
+    props<{ response: LoadCounterListingResponse }>()
+);
+
+export const counterListingApiError = createAction(
+    `${COUNTER_PREFIX} Load Counter Listing Errors`,
+    props<{ error: string }>()
+);
+
+export const promptCounterReset = createAction(
+    `${COUNTER_PREFIX} Prompt Counter Reset`,
+    props<{ request: ResetCounterRequest }>()
+);
+
+export const resetCounter = createAction(`${COUNTER_PREFIX} Reset Counter`, 
props<{ request: ResetCounterRequest }>());
+
+export const resetCounterSuccess = createAction(
+    `${COUNTER_PREFIX} Reset Counter Success`,
+    props<{ response: ResetCounterSuccess }>()
+);
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/state/counter-listing/counter-listing.effects.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/state/counter-listing/counter-listing.effects.ts
new file mode 100644
index 0000000000..0f35e30e66
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/state/counter-listing/counter-listing.effects.ts
@@ -0,0 +1,106 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { Injectable } from '@angular/core';
+import { Actions, createEffect, ofType } from '@ngrx/effects';
+import { NiFiState } from '../../../../state';
+import { Store } from '@ngrx/store';
+import { Router } from '@angular/router';
+import * as CounterListingActions from './counter-listing.actions';
+import { catchError, from, map, of, switchMap, take, tap } from 'rxjs';
+import { CountersService } from '../../service/counters.service';
+import { MatDialog } from '@angular/material/dialog';
+import { YesNoDialog } from 
'../../../../ui/common/yes-no-dialog/yes-no-dialog.component';
+
+@Injectable()
+export class CounterListingEffects {
+    constructor(
+        private actions$: Actions,
+        private store: Store<NiFiState>,
+        private router: Router,
+        private countersService: CountersService,
+        private dialog: MatDialog
+    ) {}
+
+    loadCounters$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(CounterListingActions.loadCounters),
+            switchMap(() =>
+                from(this.countersService.getCounters()).pipe(
+                    map((response) =>
+                        CounterListingActions.loadCountersSuccess({
+                            response: {
+                                counters: 
response.counters.aggregateSnapshot.counters,
+                                loadedTimestamp: 
response.counters.aggregateSnapshot.generated
+                            }
+                        })
+                    ),
+                    catchError((error) =>
+                        of(
+                            CounterListingActions.counterListingApiError({
+                                error: error.error
+                            })
+                        )
+                    )
+                )
+            )
+        )
+    );
+
+    promptCounterReset$ = createEffect(
+        () =>
+            this.actions$.pipe(
+                ofType(CounterListingActions.promptCounterReset),
+                map((action) => action.request),
+                tap((request) => {
+                    const dialogReference = this.dialog.open(YesNoDialog, {
+                        data: {
+                            title: 'Reset Counter',
+                            message: `Reset counter '${request.counter.name}' 
to default value?`
+                        },
+                        panelClass: 'small-dialog'
+                    });
+
+                    
dialogReference.componentInstance.yes.pipe(take(1)).subscribe(() => {
+                        this.store.dispatch(
+                            CounterListingActions.resetCounter({
+                                request
+                            })
+                        );
+                    });
+                })
+            ),
+        { dispatch: false }
+    );
+
+    resetCounter$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(CounterListingActions.resetCounter),
+            map((action) => action.request),
+            switchMap((request) =>
+                from(this.countersService.resetCounter(request)).pipe(
+                    map((response) =>
+                        CounterListingActions.resetCounterSuccess({
+                            response
+                        })
+                    ),
+                    catchError((error) => 
of(CounterListingActions.counterListingApiError({ error: error.error })))
+                )
+            )
+        )
+    );
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/state/counter-listing/counter-listing.reducer.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/state/counter-listing/counter-listing.reducer.ts
new file mode 100644
index 0000000000..bc1904ff7d
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/state/counter-listing/counter-listing.reducer.ts
@@ -0,0 +1,61 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { CounterListingState } from './index';
+import { createReducer, on } from '@ngrx/store';
+import { loadCounters, loadCountersSuccess, resetCounterSuccess } from 
'./counter-listing.actions';
+import { parameterContextListingApiError } from 
'../../../parameter-contexts/state/parameter-context-listing/parameter-context-listing.actions';
+import { produce } from 'immer';
+
+export const initialState: CounterListingState = {
+    counters: [],
+    saving: false,
+    loadedTimestamp: '',
+    error: null,
+    status: 'pending'
+};
+
+export const counterListingReducer = createReducer(
+    initialState,
+    on(loadCounters, (state) => ({
+        ...state,
+        status: 'loading' as const
+    })),
+    on(loadCountersSuccess, (state, { response }) => ({
+        ...state,
+        counters: response.counters,
+        loadedTimestamp: response.loadedTimestamp,
+        error: null,
+        status: 'success' as const
+    })),
+    on(parameterContextListingApiError, (state, { error }) => ({
+        ...state,
+        saving: false,
+        error,
+        status: 'error' as const
+    })),
+    on(resetCounterSuccess, (state, { response }) => {
+        return produce(state, (draftState) => {
+            const index: number = draftState.counters.findIndex((c: any) => 
c.id === response.counter.id);
+            if (index > -1) {
+                draftState.counters[index] = {
+                    ...response.counter
+                };
+            }
+        });
+    })
+);
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/state/counter-listing/counter-listing.selectors.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/state/counter-listing/counter-listing.selectors.ts
new file mode 100644
index 0000000000..5855fef5ca
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/state/counter-listing/counter-listing.selectors.ts
@@ -0,0 +1,27 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { createSelector } from '@ngrx/store';
+import { countersFeatureKey, CountersState, selectCounterState } from 
'../index';
+import { CounterListingState } from './index';
+
+export const selectCounterListingState = createSelector(
+    selectCounterState,
+    (state: CountersState) => state[countersFeatureKey]
+);
+
+export const selectCounters = createSelector(selectCounterListingState, 
(state: CounterListingState) => state.counters);
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/state/counter-listing/index.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/state/counter-listing/index.ts
new file mode 100644
index 0000000000..7459519b37
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/state/counter-listing/index.ts
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+export interface CounterEntity {
+    id: string;
+    context: string;
+    name: string;
+    valueCount: number;
+    value: string;
+}
+
+export interface CounterListingState {
+    counters: CounterEntity[];
+    saving: boolean;
+    loadedTimestamp: string;
+    error: string | null;
+    status: 'pending' | 'loading' | 'error' | 'success';
+}
+
+export interface LoadCounterListingResponse {
+    counters: CounterEntity[];
+    loadedTimestamp: string;
+}
+
+export interface ResetCounterRequest {
+    counter: CounterEntity;
+}
+
+export interface ResetCounterSuccess {
+    counter: CounterEntity;
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/state/index.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/state/index.ts
new file mode 100644
index 0000000000..cb8f2597ca
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/state/index.ts
@@ -0,0 +1,34 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { CounterListingState } from './counter-listing';
+import { Action, combineReducers, createFeatureSelector } from '@ngrx/store';
+import { counterListingReducer } from 
'./counter-listing/counter-listing.reducer';
+
+export const countersFeatureKey = 'counters';
+
+export interface CountersState {
+    [countersFeatureKey]: CounterListingState;
+}
+
+export function reducers(state: CountersState | undefined, action: Action) {
+    return combineReducers({
+        [countersFeatureKey]: counterListingReducer
+    })(state, action);
+}
+
+export const selectCounterState = 
createFeatureSelector<CountersState>(countersFeatureKey);
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-listing.component.html
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-listing.component.html
new file mode 100644
index 0000000000..2e9cf2ec02
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-listing.component.html
@@ -0,0 +1,42 @@
+<!--
+  ~  Licensed to the Apache Software Foundation (ASF) under one or more
+  ~  contributor license agreements.  See the NOTICE file distributed with
+  ~  this work for additional information regarding copyright ownership.
+  ~  The ASF licenses this file to You under the Apache License, Version 2.0
+  ~  (the "License"); you may not use this file except in compliance with
+  ~  the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  -->
+
+<ng-container *ngIf="counterListingState$ | async; let counterListingState">
+    <div *ngIf="isInitialLoading(counterListingState); else loaded">
+        <ngx-skeleton-loader count="3"></ngx-skeleton-loader>
+    </div>
+
+    <ng-template #loaded>
+        <div class="flex flex-col h-full gap-y-2">
+            <div class="flex-1" *ngIf="currentUser$ | async as user">
+                <counter-table
+                    [counters]="counterListingState.counters"
+                    [canModifyCounters]="user.countersPermissions.canWrite"
+                    (resetCounter)="resetCounter($event)"></counter-table>
+            </div>
+            <div class="flex justify-between">
+                <div class="refresh-container flex items-center gap-x-2">
+                    <button class="nifi-button" 
(click)="refreshCounterListing()">
+                        <i class="fa fa-refresh" 
[class.fa-spin]="counterListingState.status === 'loading'"></i>
+                    </button>
+                    <div>Last updated:</div>
+                    <div class="refresh-timestamp">{{ 
counterListingState.loadedTimestamp }}</div>
+                </div>
+            </div>
+        </div>
+    </ng-template>
+</ng-container>
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-listing.component.scss
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-listing.component.scss
new file mode 100644
index 0000000000..3651a8ab70
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-listing.component.scss
@@ -0,0 +1,16 @@
+/*!
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-listing.component.spec.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-listing.component.spec.ts
new file mode 100644
index 0000000000..a868ca1e5f
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-listing.component.spec.ts
@@ -0,0 +1,40 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { CounterListing } from './counter-listing.component';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { provideMockStore } from '@ngrx/store/testing';
+import { initialState } from 
'../../state/counter-listing/counter-listing.reducer';
+
+describe('CounterListing', () => {
+    let component: CounterListing;
+    let fixture: ComponentFixture<CounterListing>;
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [CounterListing],
+            providers: [provideMockStore({ initialState })]
+        });
+        fixture = TestBed.createComponent(CounterListing);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+});
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-listing.component.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-listing.component.ts
new file mode 100644
index 0000000000..3cf0994b49
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-listing.component.ts
@@ -0,0 +1,58 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { Component, OnInit } from '@angular/core';
+import { CounterEntity, CounterListingState } from 
'../../state/counter-listing';
+import { Store } from '@ngrx/store';
+import { loadCounters, promptCounterReset } from 
'../../state/counter-listing/counter-listing.actions';
+import { selectCounterListingState } from 
'../../state/counter-listing/counter-listing.selectors';
+import { initialState } from 
'../../state/counter-listing/counter-listing.reducer';
+import { selectUser } from '../../../../state/user/user.selectors';
+
+@Component({
+    selector: 'counter-listing',
+    templateUrl: './counter-listing.component.html',
+    styleUrls: ['./counter-listing.component.scss']
+})
+export class CounterListing implements OnInit {
+    counterListingState$ = this.store.select(selectCounterListingState);
+    currentUser$ = this.store.select(selectUser);
+
+    constructor(private store: Store<CounterListingState>) {}
+
+    ngOnInit(): void {
+        this.store.dispatch(loadCounters());
+    }
+
+    isInitialLoading(state: CounterListingState): boolean {
+        return state.loadedTimestamp == initialState.loadedTimestamp;
+    }
+
+    refreshCounterListing() {
+        this.store.dispatch(loadCounters());
+    }
+
+    resetCounter(entity: CounterEntity): void {
+        this.store.dispatch(
+            promptCounterReset({
+                request: {
+                    counter: entity
+                }
+            })
+        );
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-listing.module.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-listing.module.ts
new file mode 100644
index 0000000000..7ac4eb7aaf
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-listing.module.ts
@@ -0,0 +1,42 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { NgModule } from '@angular/core';
+import { CounterListing } from './counter-listing.component';
+import { CommonModule } from '@angular/common';
+import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
+import { CounterTable } from './counter-table/counter-table.component';
+import { MatTableModule } from '@angular/material/table';
+import { MatSortModule } from '@angular/material/sort';
+import { MatInputModule } from '@angular/material/input';
+import { ReactiveFormsModule } from '@angular/forms';
+import { MatSelectModule } from '@angular/material/select';
+
+@NgModule({
+    declarations: [CounterListing, CounterTable],
+    exports: [CounterListing],
+    imports: [
+        CommonModule,
+        NgxSkeletonLoaderModule,
+        MatTableModule,
+        MatSortModule,
+        MatInputModule,
+        ReactiveFormsModule,
+        MatSelectModule
+    ]
+})
+export class CounterListingModule {}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-table/counter-table.component.html
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-table/counter-table.component.html
new file mode 100644
index 0000000000..ee1eb3580f
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-table/counter-table.component.html
@@ -0,0 +1,93 @@
+<!--
+  ~  Licensed to the Apache Software Foundation (ASF) under one or more
+  ~  contributor license agreements.  See the NOTICE file distributed with
+  ~  this work for additional information regarding copyright ownership.
+  ~  The ASF licenses this file to You under the Apache License, Version 2.0
+  ~  (the "License"); you may not use this file except in compliance with
+  ~  the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  -->
+<div class="counter-table h-full flex flex-col">
+    <div class="counter-table-filter-container">
+        <div class="value">Displaying {{ filteredCount }} of {{ totalCount 
}}</div>
+        <form [formGroup]="filterForm">
+            <div class="flex pt-2">
+                <div class="mr-2">
+                    <mat-form-field>
+                        <mat-label>Filter</mat-label>
+                        <input matInput type="text" class="small" 
formControlName="filterTerm" />
+                    </mat-form-field>
+                </div>
+                <div>
+                    <mat-form-field>
+                        <mat-label>Filter By</mat-label>
+                        <mat-select formControlName="filterColumn">
+                            <mat-option value="name"> name </mat-option>
+                            <mat-option value="context"> context </mat-option>
+                        </mat-select>
+                    </mat-form-field>
+                </div>
+            </div>
+        </form>
+    </div>
+    <div class="flex-1 relative">
+        <div class="listing-table overflow-y-auto border absolute inset-0">
+            <table
+                mat-table
+                [dataSource]="dataSource"
+                matSort
+                matSortDisableClear
+                [matSortActive]="initialSortColumn"
+                [matSortDirection]="initialSortDirection">
+                <!-- Context column -->
+                <ng-container matColumnDef="context">
+                    <th mat-header-cell *matHeaderCellDef 
mat-sort-header>Context</th>
+                    <td mat-cell *matCellDef="let item">
+                        {{ formatContext(item) }}
+                    </td>
+                </ng-container>
+
+                <!-- Name Column -->
+                <ng-container matColumnDef="name">
+                    <th mat-header-cell *matHeaderCellDef 
mat-sort-header>Name</th>
+                    <td mat-cell *matCellDef="let item">
+                        {{ formatName(item) }}
+                    </td>
+                </ng-container>
+
+                <!-- Value column -->
+                <ng-container matColumnDef="value">
+                    <th mat-header-cell *matHeaderCellDef 
mat-sort-header>Value</th>
+                    <td mat-cell *matCellDef="let item">
+                        {{ formatValue(item) }}
+                    </td>
+                </ng-container>
+
+                <ng-container matColumnDef="reset">
+                    <th mat-header-cell *matHeaderCellDef></th>
+                    <td mat-cell *matCellDef="let item">
+                        <ng-container *ngIf="canModifyCounters">
+                            <div class="flex items-center gap-x-3">
+                                <div
+                                    class="pointer fa fa-undo"
+                                    title="Reset Counter"
+                                    *ngIf="canModifyCounters"
+                                    (click)="resetClicked(item, $event)"></div>
+                            </div>
+                        </ng-container>
+                    </td>
+                </ng-container>
+
+                <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: 
true"></tr>
+                <tr mat-row *matRowDef="let row; let even = even; columns: 
displayedColumns" [class.even]="even"></tr>
+            </table>
+        </div>
+    </div>
+</div>
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-table/counter-table.component.scss
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-table/counter-table.component.scss
new file mode 100644
index 0000000000..3651a8ab70
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-table/counter-table.component.scss
@@ -0,0 +1,16 @@
+/*!
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-table/counter-table.component.spec.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-table/counter-table.component.spec.ts
new file mode 100644
index 0000000000..b41dc7647a
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-table/counter-table.component.spec.ts
@@ -0,0 +1,52 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { CounterTable } from './counter-table.component';
+import { MatTableModule } from '@angular/material/table';
+import { MatSortModule } from '@angular/material/sort';
+import { MatInputModule } from '@angular/material/input';
+import { ReactiveFormsModule } from '@angular/forms';
+import { MatSelectModule } from '@angular/material/select';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+
+describe('CounterTableComponent', () => {
+    let component: CounterTable;
+    let fixture: ComponentFixture<CounterTable>;
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [CounterTable],
+            imports: [
+                MatTableModule,
+                MatSortModule,
+                MatInputModule,
+                ReactiveFormsModule,
+                MatSelectModule,
+                NoopAnimationsModule
+            ]
+        });
+        fixture = TestBed.createComponent(CounterTable);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+});
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-table/counter-table.component.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-table/counter-table.component.ts
new file mode 100644
index 0000000000..0052424805
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-table/counter-table.component.ts
@@ -0,0 +1,142 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { AfterViewInit, Component, EventEmitter, Input, Output, ViewChild } 
from '@angular/core';
+import { CounterEntity } from '../../../state/counter-listing';
+import { MatTableDataSource } from '@angular/material/table';
+import { MatSort } from '@angular/material/sort';
+import { FormBuilder, FormGroup } from '@angular/forms';
+import { debounceTime } from 'rxjs';
+
+@Component({
+    selector: 'counter-table',
+    templateUrl: './counter-table.component.html',
+    styleUrls: ['./counter-table.component.scss', 
'../../../../../../assets/styles/listing-table.scss']
+})
+export class CounterTable implements AfterViewInit {
+    private _canModifyCounters: boolean = false;
+    filterTerm: string = '';
+    filterColumn: 'context' | 'name' = 'name';
+    totalCount: number = 0;
+    filteredCount: number = 0;
+
+    displayedColumns: string[] = ['context', 'name', 'value'];
+    dataSource: MatTableDataSource<CounterEntity> = new 
MatTableDataSource<CounterEntity>();
+    filterForm: FormGroup;
+
+    @Input() initialSortColumn: 'context' | 'name' = 'context';
+    @Input() initialSortDirection: 'asc' | 'desc' = 'asc';
+
+    @Input() set counters(counterEntities: CounterEntity[]) {
+        this.dataSource = new 
MatTableDataSource<CounterEntity>(counterEntities);
+        this.dataSource.sort = this.sort;
+        this.dataSource.sortingDataAccessor = (data: CounterEntity, 
displayColumn: string) => {
+            switch (displayColumn) {
+                case 'context':
+                    return this.formatContext(data);
+                case 'name':
+                    return this.formatName(data);
+                case 'value':
+                    return data.valueCount;
+                default:
+                    return '';
+            }
+        };
+
+        this.dataSource.filterPredicate = (data: CounterEntity, filter: 
string) => {
+            const filterArray = filter.split('|');
+            const filterTerm = filterArray[0];
+            const filterColumn = filterArray[1];
+
+            if (filterColumn === 'name') {
+                return 
data.name.toLowerCase().indexOf(filterTerm.toLowerCase()) >= 0;
+            } else {
+                return 
data.context.toLowerCase().indexOf(filterTerm.toLowerCase()) >= 0;
+            }
+        };
+        this.totalCount = counterEntities.length;
+        this.filteredCount = counterEntities.length;
+
+        // apply any filtering to the new data
+        const filterTerm = this.filterForm.get('filterTerm')?.value;
+        if (filterTerm?.length > 0) {
+            const filterColumn = this.filterForm.get('filterColumn')?.value;
+            this.applyFilter(filterTerm, filterColumn);
+        }
+    }
+
+    @Input()
+    set canModifyCounters(canWrite: boolean) {
+        if (canWrite) {
+            this.displayedColumns = ['context', 'name', 'value', 'reset'];
+        } else {
+            this.displayedColumns = ['context', 'name', 'value'];
+        }
+        this._canModifyCounters = canWrite;
+    }
+
+    get canModifyCounters(): boolean {
+        return this._canModifyCounters;
+    }
+
+    @Output() resetCounter: EventEmitter<CounterEntity> = new 
EventEmitter<CounterEntity>();
+
+    @ViewChild(MatSort) sort!: MatSort;
+
+    constructor(private formBuilder: FormBuilder) {
+        this.filterForm = this.formBuilder.group({ filterTerm: '', 
filterColumn: 'name' });
+    }
+
+    ngAfterViewInit(): void {
+        this.dataSource.sort = this.sort;
+
+        this.filterForm
+            .get('filterTerm')
+            ?.valueChanges.pipe(debounceTime(500))
+            .subscribe((filterTerm: string) => {
+                const filterColumn = 
this.filterForm.get('filterColumn')?.value;
+                this.applyFilter(filterTerm, filterColumn);
+            });
+
+        
this.filterForm.get('filterColumn')?.valueChanges.subscribe((filterColumn: 
string) => {
+            const filterTerm = this.filterForm.get('filterTerm')?.value;
+            this.applyFilter(filterTerm, filterColumn);
+        });
+    }
+
+    applyFilter(filterTerm: string, filterColumn: string) {
+        this.dataSource.filter = `${filterTerm}|${filterColumn}`;
+        this.filteredCount = this.dataSource.filteredData.length;
+    }
+
+    formatContext(counter: CounterEntity): string {
+        return counter.context;
+    }
+
+    formatName(counter: CounterEntity): string {
+        return counter.name;
+    }
+
+    formatValue(counter: CounterEntity): string {
+        return counter.value;
+    }
+
+    resetClicked(counter: CounterEntity, event: MouseEvent) {
+        event.stopPropagation();
+        this.resetCounter.next(counter);
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-table/parameter-context-table.component.html
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-table/parameter-context-table.component.html
index a75ad02c0d..0af15cd41d 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-table/parameter-context-table.component.html
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-table/parameter-context-table.component.html
@@ -16,7 +16,7 @@
   -->
 
 <div class="relative h-full border">
-    <div class="parameter-context-table absolute inset-0 overflow-y-auto">
+    <div class="listing-table absolute inset-0 overflow-y-auto">
         <table mat-table [dataSource]="dataSource" matSort matSortDisableClear>
             <!-- More Details Column -->
             <ng-container matColumnDef="moreDetails">
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-table/parameter-context-table.component.scss
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-table/parameter-context-table.component.scss
index 10d9962109..2944f98194 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-table/parameter-context-table.component.scss
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-table/parameter-context-table.component.scss
@@ -14,63 +14,3 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-.parameter-context-table {
-    table {
-        width: 100%;
-
-        td,
-        th {
-            max-width: 300px;
-            white-space: nowrap;
-            overflow: hidden;
-            text-overflow: ellipsis;
-            padding: 0 8px;
-        }
-
-        th {
-            background-color: #728e9b;
-            color: #fff;
-        }
-
-        tr:hover {
-            background-color: #dce3e6 !important;
-        }
-
-        .selected {
-            background-color: #ffef85 !important;
-        }
-
-        .even {
-            background-color: #f4f6f7;
-        }
-
-        .fa,
-        .icon {
-            color: #004849;
-            width: 10px;
-            height: 14px;
-            text-align: center;
-        }
-
-        .mat-column-moreDetails {
-            min-width: 30px;
-        }
-
-        .mat-column-actions {
-            min-width: 115px;
-        }
-    }
-
-    .mat-mdc-table .mdc-data-table__header-row {
-        height: 35px;
-    }
-
-    .mat-mdc-table .mdc-data-table__row {
-        height: 35px;
-    }
-}
-
-::ng-deep .mat-sort-header-arrow {
-    color: #fff;
-}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-table/parameter-context-table.component.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-table/parameter-context-table.component.ts
index 45b20048c2..ea0f8deac4 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-table/parameter-context-table.component.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-table/parameter-context-table.component.ts
@@ -24,7 +24,7 @@ import { ParameterContextEntity } from 
'../../../state/parameter-context-listing
 @Component({
     selector: 'parameter-context-table',
     templateUrl: './parameter-context-table.component.html',
-    styleUrls: ['./parameter-context-table.component.scss']
+    styleUrls: ['./parameter-context-table.component.scss', 
'../../../../../../assets/styles/listing-table.scss']
 })
 export class ParameterContextTable implements AfterViewInit {
     @Input() set parameterContexts(parameterContextEntities: 
ParameterContextEntity[]) {
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.html
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.html
index d899927fd6..dab0d4ae1a 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.html
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.html
@@ -15,7 +15,7 @@
   ~ limitations under the License.
   -->
 
-<div class="parameter-table flex gap-x-3">
+<div class="parameter-table listing-table flex gap-x-3">
     <div class="flex flex-col gap-y-3" style="flex-grow: 3">
         <div class="flex justify-end items-center">
             <button class="nifi-button" type="button" 
(click)="newParameterClicked()">
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.scss
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.scss
index f0b830c6c1..c6fc0792cf 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.scss
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.scss
@@ -17,51 +17,18 @@
 
 @use '@angular/material' as mat;
 
-.parameter-table {
+.parameter-table.listing-table {
     @include mat.table-density(-4);
     min-width: 740px;
 
     table {
-        width: 100%;
-
         td,
         th {
-            max-width: 300px;
-            white-space: nowrap;
-            overflow: hidden;
-            text-overflow: ellipsis;
-            padding: 0 8px;
             cursor: default;
         }
 
-        th {
-            background-color: #728e9b;
-            color: #fff;
-        }
-
-        tr:hover {
-            background-color: #dce3e6 !important;
-        }
-
-        .selected {
-            background-color: #ffef85 !important;
-        }
-
-        .even {
-            background-color: #f4f6f7;
-        }
-
-        .fa {
-            width: 10px;
-            height: 14px;
-        }
-
         .mat-column-actions {
             width: 75px;
         }
     }
 }
-
-::ng-deep .mat-sort-header-arrow {
-    color: #fff;
-}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.ts
index bc677cf4ef..e0260c6887 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.ts
@@ -57,7 +57,7 @@ export interface ParameterItem {
         NifiTooltipDirective,
         ParameterReferences
     ],
-    styleUrls: ['./parameter-table.component.scss'],
+    styleUrls: ['./parameter-table.component.scss', 
'../../../../../../assets/styles/listing-table.scss'],
     providers: [
         {
             provide: NG_VALUE_ACCESSOR,
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.html
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.html
index be00de4d6a..8f791bac2d 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.html
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.html
@@ -16,7 +16,7 @@
   -->
 
 <div class="relative h-full border">
-    <div class="reporting-task-table absolute inset-0 overflow-y-auto">
+    <div class="reporting-task-table listing-table absolute inset-0 
overflow-y-auto">
         <table mat-table [dataSource]="dataSource" matSort matSortDisableClear>
             <!-- More Details Column -->
             <ng-container matColumnDef="moreDetails">
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.scss
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.scss
index 11b26e5415..6411871761 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.scss
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.scss
@@ -15,62 +15,10 @@
  * limitations under the License.
  */
 
-.reporting-task-table {
+.reporting-task-table.listing-table {
     table {
-        width: 100%;
-
-        td,
-        th {
-            max-width: 300px;
-            white-space: nowrap;
-            overflow: hidden;
-            text-overflow: ellipsis;
-            padding: 0 8px;
-        }
-
-        th {
-            background-color: #728e9b;
-            color: #fff;
-        }
-
-        tr:hover {
-            background-color: #dce3e6 !important;
-        }
-
-        .selected {
-            background-color: #ffef85 !important;
-        }
-
-        .even {
-            background-color: #f4f6f7;
-        }
-
-        .fa,
-        .icon {
-            color: #004849;
-            width: 10px;
-            height: 14px;
-            text-align: center;
-        }
-
         .mat-column-moreDetails {
             min-width: 100px;
         }
-
-        .mat-column-actions {
-            min-width: 115px;
-        }
-    }
-
-    .mat-mdc-table .mdc-data-table__header-row {
-        height: 35px;
-    }
-
-    .mat-mdc-table .mdc-data-table__row {
-        height: 35px;
     }
 }
-
-::ng-deep .mat-sort-header-arrow {
-    color: #fff;
-}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.ts
index e5c7e9a3ba..8134d344c2 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.ts
@@ -28,7 +28,7 @@ import { BulletinsTipInput, TextTipInput, 
ValidationErrorsTipInput } from '../..
 @Component({
     selector: 'reporting-task-table',
     templateUrl: './reporting-task-table.component.html',
-    styleUrls: ['./reporting-task-table.component.scss']
+    styleUrls: ['./reporting-task-table.component.scss', 
'../../../../../../assets/styles/listing-table.scss']
 })
 export class ReportingTaskTable implements AfterViewInit {
     @Input() set reportingTasks(reportingTaskEntities: ReportingTaskEntity[]) {
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.html
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.html
index 9411785c7a..1ea07471c8 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.html
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.html
@@ -16,7 +16,7 @@
   -->
 
 <div class="relative h-full border">
-    <div class="controller-service-table absolute inset-0 overflow-y-auto">
+    <div class="controller-service-table listing-table absolute inset-0 
overflow-y-auto">
         <table mat-table [dataSource]="dataSource" matSort matSortDisableClear>
             <!-- More Details Column -->
             <ng-container matColumnDef="moreDetails">
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.scss
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.scss
index 3de9d43ec1..3a513b3e55 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.scss
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.scss
@@ -17,56 +17,12 @@
 
 @use '@angular/material' as mat;
 
-.controller-service-table {
+.controller-service-table.listing-table {
     @include mat.table-density(-4);
 
     table {
-        width: 100%;
-
-        td,
-        th {
-            max-width: 300px;
-            white-space: nowrap;
-            overflow: hidden;
-            text-overflow: ellipsis;
-            padding: 0 8px;
-        }
-
-        th {
-            background-color: #728e9b;
-            color: #fff;
-        }
-
-        tr:hover {
-            background-color: #dce3e6 !important;
-        }
-
-        .selected {
-            background-color: #ffef85 !important;
-        }
-
-        .even {
-            background-color: #f4f6f7;
-        }
-
-        .fa,
-        .icon {
-            color: #004849;
-            width: 10px;
-            height: 14px;
-            text-align: center;
-        }
-
         .mat-column-moreDetails {
             min-width: 90px;
         }
-
-        .mat-column-actions {
-            min-width: 115px;
-        }
     }
 }
-
-::ng-deep .mat-sort-header-arrow {
-    color: #fff;
-}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.ts
index e3f517706c..6e27c87c04 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.ts
@@ -38,7 +38,7 @@ import { ValidationErrorsTip } from 
'../../tooltips/validation-errors-tip/valida
     standalone: true,
     templateUrl: './controller-service-table.component.html',
     imports: [MatButtonModule, MatDialogModule, MatTableModule, MatSortModule, 
NgIf, NgClass, NifiTooltipDirective],
-    styleUrls: ['./controller-service-table.component.scss']
+    styleUrls: ['./controller-service-table.component.scss', 
'../../../../../assets/styles/listing-table.scss']
 })
 export class ControllerServiceTable implements AfterViewInit {
     @Input() set controllerServices(controllerServiceEntities: 
ControllerServiceEntity[]) {
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/property-table.component.html
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/property-table.component.html
index 563f9cc304..5f52eeed03 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/property-table.component.html
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/property-table.component.html
@@ -15,7 +15,7 @@
   ~ limitations under the License.
   -->
 
-<div class="property-table flex flex-col gap-y-3">
+<div class="property-table listing-table flex flex-col gap-y-3">
     <div class="flex justify-between items-center">
         <div class="font-bold">Required field</div>
         <div>
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/property-table.component.scss
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/property-table.component.scss
index bd5acec8f9..9517ea1a8b 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/property-table.component.scss
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/property-table.component.scss
@@ -17,7 +17,7 @@
 
 @use '@angular/material' as mat;
 
-.property-table {
+.property-table.listing-table {
     @include mat.table-density(-4);
     min-width: 740px;
 
@@ -26,46 +26,11 @@
 
         td,
         th {
-            max-width: 300px;
-            white-space: nowrap;
-            overflow: hidden;
-            text-overflow: ellipsis;
-            padding: 0 8px;
             cursor: default;
         }
 
-        th {
-            background-color: #728e9b;
-            color: #fff;
-        }
-
-        tr:hover {
-            background-color: #dce3e6 !important;
-        }
-
-        .selected {
-            background-color: #ffef85 !important;
-        }
-
-        .even {
-            background-color: #f4f6f7;
-        }
-
-        .fa {
-            width: 10px;
-            height: 14px;
-        }
-
-        .mat-column-actions {
-            min-width: 115px;
-        }
-
         .mat-column-property {
             min-width: 230px;
         }
     }
 }
-
-::ng-deep .mat-sort-header-arrow {
-    color: #fff;
-}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/property-table.component.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/property-table.component.ts
index 0900ead04d..75b2ca8b1f 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/property-table.component.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/property-table.component.ts
@@ -87,7 +87,7 @@ export interface PropertyItem extends Property {
         RouterLink,
         AsyncPipe
     ],
-    styleUrls: ['./property-table.component.scss'],
+    styleUrls: ['./property-table.component.scss', 
'../../../../assets/styles/listing-table.scss'],
     providers: [
         {
             provide: NG_VALUE_ACCESSOR,
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-table/parameter-context-table.component.scss
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/assets/styles/listing-table.scss
similarity index 59%
copy from 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-table/parameter-context-table.component.scss
copy to 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/assets/styles/listing-table.scss
index 10d9962109..33ca896721 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-table/parameter-context-table.component.scss
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/assets/styles/listing-table.scss
@@ -1,21 +1,21 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
+/*!
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
  *
  *     http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
  */
 
-.parameter-context-table {
+.listing-table {
     table {
         width: 100%;
 


Reply via email to