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 f1cac06f2a NIFI-12588: Flow Analysis Rules (#8241)
f1cac06f2a is described below

commit f1cac06f2aa40a3b4f5f3e9415500cc007df37ee
Author: Scott Aslan <scottyas...@users.noreply.github.com>
AuthorDate: Fri Jan 12 17:15:23 2024 -0500

    NIFI-12588: Flow Analysis Rules (#8241)
    
    * [NIFI-12588] Flow Analysis Rules listing
    
    review feedback
    
    update goto dialog title
    
    remove transitional states of enable
    
    fix CS goto
    
    remove access policy UX
    
    check canModifyController
    
    update currentTime
    
    move interfaces
    
    update sorting, inline CS creation, CS goto
    
    use Edit in dialog titles
    
    * review feedback
    
    * input current user
    
    This closes #8241
---
 .../web/api/entity/FlowAnalysisRulesEntity.java    |  22 ++
 .../apache/nifi/web/api/ControllerResource.java    |   1 +
 .../pages/flow-designer/state/flow/flow.effects.ts |  12 -
 .../settings/feature/settings-routing.module.ts    |  17 +-
 .../app/pages/settings/feature/settings.module.ts  |   2 +
 .../settings/service/flow-analysis-rule.service.ts |  99 ++++++++
 .../flow-analysis-rules.actions.ts                 | 123 ++++++++++
 .../flow-analysis-rules.effects.ts}                | 224 +++++++++--------
 .../flow-analysis-rules.reducer.ts                 | 123 ++++++++++
 .../flow-analysis-rules.selectors.ts               |  54 +++++
 .../settings/state/flow-analysis-rules/index.ts    | 122 ++++++++++
 .../nifi/src/app/pages/settings/state/index.ts     |   4 +
 .../pages/settings/state/reporting-tasks/index.ts  |   5 +
 .../reporting-tasks/reporting-tasks.effects.ts     |  14 +-
 .../create-flow-analysis-rule.component.html}      |   6 +-
 .../create-flow-analysis-rule.component.scss}      |   9 -
 .../create-flow-analysis-rule.component.spec.ts    |  61 +++++
 .../create-flow-analysis-rule.component.ts         |  62 +++++
 .../edit-flow-analysis-rule.component.html         |  93 +++++++
 .../edit-flow-analysis-rule.component.scss}        |  34 ++-
 .../edit-flow-analysis-rule.component.spec.ts      | 105 ++++++++
 .../edit-flow-analysis-rule.component.ts           | 161 +++++++++++++
 .../flow-analysis-rule-table.component.html}       |  86 ++++---
 .../flow-analysis-rule-table.component.scss}       |  15 +-
 .../flow-analysis-rule-table.component.spec.ts}    |  14 +-
 .../flow-analysis-rule-table.component.ts          | 268 +++++++++++++++++++++
 .../flow-analysis-rules.component.html             |  35 ++-
 .../flow-analysis-rules.component.spec.ts          |   9 +-
 .../flow-analysis-rules.component.ts               | 131 +++++++++-
 .../flow-analysis-rules.module.ts                  |  16 +-
 .../edit-reporting-task.component.ts               |  13 +-
 .../reporting-task-table.component.html            |  46 ++--
 .../extension-types/extension-types.selectors.ts   |   5 +
 .../src/main/nifi/src/app/state/shared/index.ts    |   5 -
 .../controller-service-table.component.html        |  12 +-
 35 files changed, 1765 insertions(+), 243 deletions(-)

diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/FlowAnalysisRulesEntity.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/FlowAnalysisRulesEntity.java
index 3e91e1d3ee..e185620a54 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/FlowAnalysisRulesEntity.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/FlowAnalysisRulesEntity.java
@@ -16,7 +16,12 @@
  */
 package org.apache.nifi.web.api.entity;
 
+import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.xml.bind.annotation.XmlRootElement;
+import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+import org.apache.nifi.web.api.dto.util.TimeAdapter;
+
+import java.util.Date;
 import java.util.Set;
 
 /**
@@ -25,6 +30,7 @@ import java.util.Set;
 @XmlRootElement(name = "flowAnalysisRulesEntity")
 public class FlowAnalysisRulesEntity extends Entity {
 
+    private Date currentTime;
     private Set<FlowAnalysisRuleEntity> flowAnalysisRules;
 
     /**
@@ -38,4 +44,20 @@ public class FlowAnalysisRulesEntity extends Entity {
         this.flowAnalysisRules = flowAnalysisRules;
     }
 
+    /**
+     * @return current time on the server
+     */
+    @XmlJavaTypeAdapter(TimeAdapter.class)
+    @Schema(
+            description = "The current time on the system.",
+            type = "string"
+    )
+    public Date getCurrentTime() {
+        return currentTime;
+    }
+
+    public void setCurrentTime(Date currentTime) {
+        this.currentTime = currentTime;
+    }
+
 }
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
index 380f1c2895..3b46b6f968 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
@@ -978,6 +978,7 @@ public class ControllerResource extends ApplicationResource 
{
         // create the response entity
         final FlowAnalysisRulesEntity entity = new FlowAnalysisRulesEntity();
         entity.setFlowAnalysisRules(flowAnalysisRules);
+        entity.setCurrentTime(new Date());
 
         // generate the response
         return generateOkResponse(entity).build();
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/flow/flow.effects.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/flow/flow.effects.ts
index 16020b5edf..4baa347825 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/flow/flow.effects.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/flow/flow.effects.ts
@@ -96,7 +96,6 @@ import { OkDialog } from 
'../../../../ui/common/ok-dialog/ok-dialog.component';
 import { GroupComponents } from 
'../../ui/canvas/items/process-group/group-components/group-components.component';
 import { EditProcessGroup } from 
'../../ui/canvas/items/process-group/edit-process-group/edit-process-group.component';
 import { CreateControllerService } from 
'../../../../ui/common/controller-service/create-controller-service/create-controller-service.component';
-import * as ControllerServicesActions from 
'../controller-services/controller-services.actions';
 import { ExtensionTypesService } from 
'../../../../service/extension-types.service';
 import { ControllerServiceService } from 
'../../service/controller-service.service';
 import { YesNoDialog } from 
'../../../../ui/common/yes-no-dialog/yes-no-dialog.component';
@@ -1057,17 +1056,6 @@ export class FlowEffects {
                                                 .pipe(
                                                     take(1),
                                                     switchMap((createReponse) 
=> {
-                                                        // dispatch an inline 
create service success action so the new service is in the state
-                                                        this.store.dispatch(
-                                                            
ControllerServicesActions.inlineCreateControllerServiceSuccess(
-                                                                {
-                                                                    response: {
-                                                                        
controllerService: createReponse
-                                                                    }
-                                                                }
-                                                            )
-                                                        );
-
                                                         // fetch an updated 
property descriptor
                                                         return this.flowService
                                                             
.getPropertyDescriptor(processorId, descriptor.name, false)
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings-routing.module.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings-routing.module.ts
index 85a5965a87..441a5bd06c 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings-routing.module.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings-routing.module.ts
@@ -67,7 +67,22 @@ const routes: Routes = [
                     }
                 ]
             },
-            { path: 'flow-analysis-rules', component: FlowAnalysisRules },
+            {
+                path: 'flow-analysis-rules',
+                component: FlowAnalysisRules,
+                children: [
+                    {
+                        path: ':id',
+                        component: FlowAnalysisRules,
+                        children: [
+                            {
+                                path: 'edit',
+                                component: FlowAnalysisRules
+                            }
+                        ]
+                    }
+                ]
+            },
             {
                 path: 'registry-clients',
                 component: RegistryClients,
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings.module.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings.module.ts
index ac7e9702c9..421a5b433c 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings.module.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings.module.ts
@@ -33,6 +33,7 @@ import { ReportingTasksModule } from 
'../ui/reporting-tasks/reporting-tasks.modu
 import { MatTabsModule } from '@angular/material/tabs';
 import { ReportingTasksEffects } from 
'../state/reporting-tasks/reporting-tasks.effects';
 import { RegistryClientsEffects } from 
'../state/registry-clients/registry-clients.effects';
+import { FlowAnalysisRulesEffects } from 
'../state/flow-analysis-rules/flow-analysis-rules.effects';
 
 @NgModule({
     declarations: [Settings],
@@ -51,6 +52,7 @@ import { RegistryClientsEffects } from 
'../state/registry-clients/registry-clien
             GeneralEffects,
             ManagementControllerServicesEffects,
             ReportingTasksEffects,
+            FlowAnalysisRulesEffects,
             RegistryClientsEffects
         ),
         MatTabsModule
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/flow-analysis-rule.service.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/flow-analysis-rule.service.ts
new file mode 100644
index 0000000000..b6f0a9ca14
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/flow-analysis-rule.service.ts
@@ -0,0 +1,99 @@
+/*
+ * 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 { Observable } from 'rxjs';
+import { HttpClient } from '@angular/common/http';
+import { Client } from '../../../service/client.service';
+import { NiFiCommon } from '../../../service/nifi-common.service';
+import {
+    ConfigureFlowAnalysisRuleRequest,
+    CreateFlowAnalysisRuleRequest,
+    DeleteFlowAnalysisRuleRequest,
+    EnableFlowAnalysisRuleRequest,
+    FlowAnalysisRuleEntity
+} from '../state/flow-analysis-rules';
+
+@Injectable({ providedIn: 'root' })
+export class FlowAnalysisRuleService {
+    private static readonly API: string = '../nifi-api';
+
+    /**
+     * The NiFi model contain the url for each component. That URL is an 
absolute URL. Angular CSRF handling
+     * does not work on absolute URLs, so we need to strip off the proto for 
the request header to be added.
+     *
+     * https://stackoverflow.com/a/59586462
+     *
+     * @param url
+     * @private
+     */
+    private stripProtocol(url: string): string {
+        return this.nifiCommon.substringAfterFirst(url, ':');
+    }
+
+    constructor(
+        private httpClient: HttpClient,
+        private client: Client,
+        private nifiCommon: NiFiCommon
+    ) {}
+
+    getFlowAnalysisRule(): Observable<any> {
+        return 
this.httpClient.get(`${FlowAnalysisRuleService.API}/controller/flow-analysis-rules`);
+    }
+
+    createFlowAnalysisRule(createFlowAnalysisRule: 
CreateFlowAnalysisRuleRequest): Observable<any> {
+        return 
this.httpClient.post(`${FlowAnalysisRuleService.API}/controller/flow-analysis-rules`,
 {
+            revision: createFlowAnalysisRule.revision,
+            component: {
+                bundle: createFlowAnalysisRule.flowAnalysisRuleBundle,
+                type: createFlowAnalysisRule.flowAnalysisRuleType
+            }
+        });
+    }
+
+    deleteFlowAnalysisRule(deleteFlowAnalysisRule: 
DeleteFlowAnalysisRuleRequest): Observable<any> {
+        const entity: FlowAnalysisRuleEntity = 
deleteFlowAnalysisRule.flowAnalysisRule;
+        const revision: any = this.client.getRevision(entity);
+        return this.httpClient.delete(this.stripProtocol(entity.uri), { 
params: revision });
+    }
+
+    getPropertyDescriptor(id: string, propertyName: string, sensitive: 
boolean): Observable<any> {
+        const params: any = {
+            propertyName,
+            sensitive
+        };
+        return 
this.httpClient.get(`${FlowAnalysisRuleService.API}/controller/flow-analysis-rules/${id}/descriptors`,
 {
+            params
+        });
+    }
+
+    updateFlowAnalysisRule(configureFlowAnalysisRule: 
ConfigureFlowAnalysisRuleRequest): Observable<any> {
+        return this.httpClient.put(
+            this.stripProtocol(configureFlowAnalysisRule.uri),
+            configureFlowAnalysisRule.payload
+        );
+    }
+
+    setEnable(flowAnalysisRule: EnableFlowAnalysisRuleRequest, enabled: 
boolean): Observable<any> {
+        const entity: FlowAnalysisRuleEntity = 
flowAnalysisRule.flowAnalysisRule;
+        return 
this.httpClient.put(`${this.stripProtocol(entity.uri)}/run-status`, {
+            revision: this.client.getRevision(entity),
+            state: enabled ? 'ENABLED' : 'DISABLED',
+            uiOnly: true
+        });
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.actions.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.actions.ts
new file mode 100644
index 0000000000..d783be1d47
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.actions.ts
@@ -0,0 +1,123 @@
+/*
+ * 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 {
+    ConfigureFlowAnalysisRuleRequest,
+    ConfigureFlowAnalysisRuleSuccess,
+    CreateFlowAnalysisRuleRequest,
+    CreateFlowAnalysisRuleSuccess,
+    DeleteFlowAnalysisRuleRequest,
+    DeleteFlowAnalysisRuleSuccess,
+    EditFlowAnalysisRuleDialogRequest,
+    LoadFlowAnalysisRulesResponse,
+    SelectFlowAnalysisRuleRequest,
+    DisableFlowAnalysisRuleRequest,
+    EnableFlowAnalysisRuleRequest,
+    EnableFlowAnalysisRuleSuccess,
+    DisableFlowAnalysisRuleSuccess
+} from './index';
+
+export const resetFlowAnalysisRulesState = createAction('[Flow Analysis Rules] 
Reset Flow Analysis Rules State');
+
+export const loadFlowAnalysisRules = createAction('[Flow Analysis Rules] Load 
Flow Analysis Rules');
+
+export const loadFlowAnalysisRulesSuccess = createAction(
+    '[Flow Analysis Rules] Load Flow Analysis Rules Success',
+    props<{ response: LoadFlowAnalysisRulesResponse }>()
+);
+
+export const openConfigureFlowAnalysisRuleDialog = createAction(
+    '[Flow Analysis Rules] Open Flow Analysis Rule Dialog',
+    props<{ request: EditFlowAnalysisRuleDialogRequest }>()
+);
+
+export const configureFlowAnalysisRule = createAction(
+    '[Flow Analysis Rules] Configure Flow Analysis Rule',
+    props<{ request: ConfigureFlowAnalysisRuleRequest }>()
+);
+
+export const configureFlowAnalysisRuleSuccess = createAction(
+    '[Flow Analysis Rules] Configure Flow Analysis Rule Success',
+    props<{ response: ConfigureFlowAnalysisRuleSuccess }>()
+);
+
+export const enableFlowAnalysisRule = createAction(
+    '[Enable Flow Analysis Rule] Submit Enable Request',
+    props<{
+        request: EnableFlowAnalysisRuleRequest;
+    }>()
+);
+
+export const enableFlowAnalysisRuleSuccess = createAction(
+    '[Flow Analysis Rules] Enable Flow Analysis Rule Success',
+    props<{ response: EnableFlowAnalysisRuleSuccess }>()
+);
+
+export const disableFlowAnalysisRule = createAction(
+    '[Enable Flow Analysis Rule] Submit Disable Request',
+    props<{
+        request: DisableFlowAnalysisRuleRequest;
+    }>()
+);
+
+export const disableFlowAnalysisRuleSuccess = createAction(
+    '[Flow Analysis Rules] Disable Flow Analysis Rule Success',
+    props<{ response: DisableFlowAnalysisRuleSuccess }>()
+);
+
+export const flowAnalysisRuleApiError = createAction(
+    '[Flow Analysis Rules] Load Flow Analysis Rules Error',
+    props<{ error: string }>()
+);
+
+export const openNewFlowAnalysisRuleDialog = createAction('[Flow Analysis 
Rules] Open New Flow Analysis Rule Dialog');
+
+export const createFlowAnalysisRule = createAction(
+    '[Flow Analysis Rules] Create Flow Analysis Rule',
+    props<{ request: CreateFlowAnalysisRuleRequest }>()
+);
+
+export const createFlowAnalysisRuleSuccess = createAction(
+    '[Flow Analysis Rules] Create Flow Analysis Rule Success',
+    props<{ response: CreateFlowAnalysisRuleSuccess }>()
+);
+
+export const navigateToEditFlowAnalysisRule = createAction(
+    '[Flow Analysis Rules] Navigate To Edit Flow Analysis Rule',
+    props<{ id: string }>()
+);
+
+export const promptFlowAnalysisRuleDeletion = createAction(
+    '[Flow Analysis Rules] Prompt Flow Analysis Rule Deletion',
+    props<{ request: DeleteFlowAnalysisRuleRequest }>()
+);
+
+export const deleteFlowAnalysisRule = createAction(
+    '[Flow Analysis Rules] Delete Flow Analysis Rule',
+    props<{ request: DeleteFlowAnalysisRuleRequest }>()
+);
+
+export const deleteFlowAnalysisRuleSuccess = createAction(
+    '[Flow Analysis Rules] Delete Flow Analysis Rule Success',
+    props<{ response: DeleteFlowAnalysisRuleSuccess }>()
+);
+
+export const selectFlowAnalysisRule = createAction(
+    '[Flow Analysis Rules] Select Flow Analysis Rule',
+    props<{ request: SelectFlowAnalysisRuleRequest }>()
+);
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/reporting-tasks.effects.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.effects.ts
similarity index 70%
copy from 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/reporting-tasks.effects.ts
copy to 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.effects.ts
index c08215a8ad..b643383421 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/reporting-tasks.effects.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.effects.ts
@@ -17,15 +17,17 @@
 
 import { Injectable } from '@angular/core';
 import { Actions, createEffect, ofType } from '@ngrx/effects';
-import * as ReportingTaskActions from './reporting-tasks.actions';
+import * as FlowAnalysisRuleActions from './flow-analysis-rules.actions';
 import { catchError, from, map, NEVER, Observable, of, switchMap, take, 
takeUntil, tap, withLatestFrom } from 'rxjs';
 import { MatDialog } from '@angular/material/dialog';
 import { Store } from '@ngrx/store';
 import { NiFiState } from '../../../../state';
-import { selectReportingTaskTypes } from 
'../../../../state/extension-types/extension-types.selectors';
+import { selectFlowAnalysisRuleTypes } from 
'../../../../state/extension-types/extension-types.selectors';
 import { YesNoDialog } from 
'../../../../ui/common/yes-no-dialog/yes-no-dialog.component';
-import { ReportingTaskService } from '../../service/reporting-task.service';
-import { CreateReportingTask } from 
'../../ui/reporting-tasks/create-reporting-task/create-reporting-task.component';
+import { FlowAnalysisRuleService } from 
'../../service/flow-analysis-rule.service';
+import { Client } from '../../../../service/client.service';
+import { ManagementControllerServiceService } from 
'../../service/management-controller-service.service';
+import { CreateFlowAnalysisRule } from 
'../../ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component';
 import { Router } from '@angular/router';
 import { selectSaving } from 
'../management-controller-services/management-controller-services.selectors';
 import {
@@ -37,44 +39,41 @@ import {
     PropertyDescriptor,
     UpdateControllerServiceRequest
 } from '../../../../state/shared';
+import { EditFlowAnalysisRule } from 
'../../ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component';
+import { CreateFlowAnalysisRuleSuccess } from './index';
 import { NewPropertyDialog } from 
'../../../../ui/common/new-property-dialog/new-property-dialog.component';
-import { EditReportingTask } from 
'../../ui/reporting-tasks/edit-reporting-task/edit-reporting-task.component';
-import { CreateReportingTaskSuccess } from './index';
-import { ExtensionTypesService } from 
'../../../../service/extension-types.service';
 import { CreateControllerService } from 
'../../../../ui/common/controller-service/create-controller-service/create-controller-service.component';
-import { ManagementControllerServiceService } from 
'../../service/management-controller-service.service';
-import * as ManagementControllerServicesActions from 
'../management-controller-services/management-controller-services.actions';
-import { Client } from '../../../../service/client.service';
+import { ExtensionTypesService } from 
'../../../../service/extension-types.service';
 
 @Injectable()
-export class ReportingTasksEffects {
+export class FlowAnalysisRulesEffects {
     constructor(
         private actions$: Actions,
         private store: Store<NiFiState>,
         private client: Client,
-        private reportingTaskService: ReportingTaskService,
         private managementControllerServiceService: 
ManagementControllerServiceService,
         private extensionTypesService: ExtensionTypesService,
+        private flowAnalysisRuleService: FlowAnalysisRuleService,
         private dialog: MatDialog,
         private router: Router
     ) {}
 
-    loadReportingTasks$ = createEffect(() =>
+    loadFlowAnalysisRule$ = createEffect(() =>
         this.actions$.pipe(
-            ofType(ReportingTaskActions.loadReportingTasks),
+            ofType(FlowAnalysisRuleActions.loadFlowAnalysisRules),
             switchMap(() =>
-                from(this.reportingTaskService.getReportingTasks()).pipe(
+                from(this.flowAnalysisRuleService.getFlowAnalysisRule()).pipe(
                     map((response) =>
-                        ReportingTaskActions.loadReportingTasksSuccess({
+                        FlowAnalysisRuleActions.loadFlowAnalysisRulesSuccess({
                             response: {
-                                reportingTasks: response.reportingTasks,
+                                flowAnalysisRules: response.flowAnalysisRules,
                                 loadedTimestamp: response.currentTime
                             }
                         })
                     ),
                     catchError((error) =>
                         of(
-                            ReportingTaskActions.reportingTasksApiError({
+                            FlowAnalysisRuleActions.flowAnalysisRuleApiError({
                                 error: error.error
                             })
                         )
@@ -84,15 +83,15 @@ export class ReportingTasksEffects {
         )
     );
 
-    openNewReportingTaskDialog$ = createEffect(
+    openNewFlowAnalysisRuleDialog$ = createEffect(
         () =>
             this.actions$.pipe(
-                ofType(ReportingTaskActions.openNewReportingTaskDialog),
-                withLatestFrom(this.store.select(selectReportingTaskTypes)),
-                tap(([action, reportingTaskTypes]) => {
-                    this.dialog.open(CreateReportingTask, {
+                ofType(FlowAnalysisRuleActions.openNewFlowAnalysisRuleDialog),
+                withLatestFrom(this.store.select(selectFlowAnalysisRuleTypes)),
+                tap(([action, flowAnalysisRuleTypes]) => {
+                    this.dialog.open(CreateFlowAnalysisRule, {
                         data: {
-                            reportingTaskTypes
+                            flowAnalysisRuleTypes
                         },
                         panelClass: 'medium-dialog'
                     });
@@ -101,22 +100,22 @@ export class ReportingTasksEffects {
         { dispatch: false }
     );
 
-    createReportingTask$ = createEffect(() =>
+    createFlowAnalysisRule$ = createEffect(() =>
         this.actions$.pipe(
-            ofType(ReportingTaskActions.createReportingTask),
+            ofType(FlowAnalysisRuleActions.createFlowAnalysisRule),
             map((action) => action.request),
             switchMap((request) =>
-                
from(this.reportingTaskService.createReportingTask(request)).pipe(
+                
from(this.flowAnalysisRuleService.createFlowAnalysisRule(request)).pipe(
                     map((response) =>
-                        ReportingTaskActions.createReportingTaskSuccess({
+                        FlowAnalysisRuleActions.createFlowAnalysisRuleSuccess({
                             response: {
-                                reportingTask: response
+                                flowAnalysisRule: response
                             }
                         })
                     ),
                     catchError((error) =>
                         of(
-                            ReportingTaskActions.reportingTasksApiError({
+                            FlowAnalysisRuleActions.flowAnalysisRuleApiError({
                                 error: error.error
                             })
                         )
@@ -126,18 +125,18 @@ export class ReportingTasksEffects {
         )
     );
 
-    createReportingTaskSuccess$ = createEffect(() =>
+    createFlowAnalysisRuleSuccess$ = createEffect(() =>
         this.actions$.pipe(
-            ofType(ReportingTaskActions.createReportingTaskSuccess),
+            ofType(FlowAnalysisRuleActions.createFlowAnalysisRuleSuccess),
             map((action) => action.response),
             tap(() => {
                 this.dialog.closeAll();
             }),
-            switchMap((response: CreateReportingTaskSuccess) =>
+            switchMap((response: CreateFlowAnalysisRuleSuccess) =>
                 of(
-                    ReportingTaskActions.selectReportingTask({
+                    FlowAnalysisRuleActions.selectFlowAnalysisRule({
                         request: {
-                            id: response.reportingTask.id
+                            id: response.flowAnalysisRule.id
                         }
                     })
                 )
@@ -145,23 +144,23 @@ export class ReportingTasksEffects {
         )
     );
 
-    promptReportingTaskDeletion$ = createEffect(
+    promptFlowAnalysisRuleDeletion$ = createEffect(
         () =>
             this.actions$.pipe(
-                ofType(ReportingTaskActions.promptReportingTaskDeletion),
+                ofType(FlowAnalysisRuleActions.promptFlowAnalysisRuleDeletion),
                 map((action) => action.request),
                 tap((request) => {
                     const dialogReference = this.dialog.open(YesNoDialog, {
                         data: {
-                            title: 'Delete Reporting Task',
-                            message: `Delete reporting task 
${request.reportingTask.component.name}?`
+                            title: 'Delete Flow Analysis Rule',
+                            message: `Delete reporting task 
${request.flowAnalysisRule.component.name}?`
                         },
                         panelClass: 'small-dialog'
                     });
 
                     
dialogReference.componentInstance.yes.pipe(take(1)).subscribe(() => {
                         this.store.dispatch(
-                            ReportingTaskActions.deleteReportingTask({
+                            FlowAnalysisRuleActions.deleteFlowAnalysisRule({
                                 request
                             })
                         );
@@ -171,22 +170,22 @@ export class ReportingTasksEffects {
         { dispatch: false }
     );
 
-    deleteReportingTask$ = createEffect(() =>
+    deleteFlowAnalysisRule$ = createEffect(() =>
         this.actions$.pipe(
-            ofType(ReportingTaskActions.deleteReportingTask),
+            ofType(FlowAnalysisRuleActions.deleteFlowAnalysisRule),
             map((action) => action.request),
             switchMap((request) =>
-                
from(this.reportingTaskService.deleteReportingTask(request)).pipe(
+                
from(this.flowAnalysisRuleService.deleteFlowAnalysisRule(request)).pipe(
                     map((response) =>
-                        ReportingTaskActions.deleteReportingTaskSuccess({
+                        FlowAnalysisRuleActions.deleteFlowAnalysisRuleSuccess({
                             response: {
-                                reportingTask: response
+                                flowAnalysisRule: response
                             }
                         })
                     ),
                     catchError((error) =>
                         of(
-                            ReportingTaskActions.reportingTasksApiError({
+                            FlowAnalysisRuleActions.flowAnalysisRuleApiError({
                                 error: error.error
                             })
                         )
@@ -196,31 +195,31 @@ export class ReportingTasksEffects {
         )
     );
 
-    navigateToEditReportingTask$ = createEffect(
+    navigateToEditFlowAnalysisRule$ = createEffect(
         () =>
             this.actions$.pipe(
-                ofType(ReportingTaskActions.navigateToEditReportingTask),
+                ofType(FlowAnalysisRuleActions.navigateToEditFlowAnalysisRule),
                 map((action) => action.id),
                 tap((id) => {
-                    this.router.navigate(['/settings', 'reporting-tasks', id, 
'edit']);
+                    this.router.navigate(['/settings', 'flow-analysis-rules', 
id, 'edit']);
                 })
             ),
         { dispatch: false }
     );
 
-    openConfigureReportingTaskDialog$ = createEffect(
+    openConfigureFlowAnalysisRuleDialog$ = createEffect(
         () =>
             this.actions$.pipe(
-                ofType(ReportingTaskActions.openConfigureReportingTaskDialog),
+                
ofType(FlowAnalysisRuleActions.openConfigureFlowAnalysisRuleDialog),
                 map((action) => action.request),
                 tap((request) => {
-                    const taskId: string = request.id;
+                    const ruleId: string = request.id;
 
-                    const editDialogReference = 
this.dialog.open(EditReportingTask, {
+                    const editDialogReference = 
this.dialog.open(EditFlowAnalysisRule, {
                         data: {
-                            reportingTask: request.reportingTask
+                            flowAnalysisRule: request.flowAnalysisRule
                         },
-                        id: taskId,
+                        id: ruleId,
                         panelClass: 'large-dialog'
                     });
 
@@ -239,7 +238,7 @@ export class ReportingTasksEffects {
                         return 
newPropertyDialogReference.componentInstance.newProperty.pipe(
                             take(1),
                             switchMap((dialogResponse: 
NewPropertyDialogResponse) => {
-                                return this.reportingTaskService
+                                return this.flowAnalysisRuleService
                                     .getPropertyDescriptor(request.id, 
dialogResponse.name, dialogResponse.sensitive)
                                     .pipe(
                                         take(1),
@@ -258,10 +257,10 @@ export class ReportingTasksEffects {
                     };
 
                     const goTo = (commands: string[], destination: string): 
void => {
-                        if 
(editDialogReference.componentInstance.editReportingTaskForm.dirty) {
+                        if 
(editDialogReference.componentInstance.editFlowAnalysisRuleForm.dirty) {
                             const saveChangesDialogReference = 
this.dialog.open(YesNoDialog, {
                                 data: {
-                                    title: 'Controller Service Configuration',
+                                    title: 'Flow Analysis Rule Configuration',
                                     message: `Save changes before going to 
this ${destination}?`
                                 },
                                 panelClass: 'small-dialog'
@@ -327,20 +326,9 @@ export class ReportingTasksEffects {
                                                 .pipe(
                                                     take(1),
                                                     switchMap((createResponse) 
=> {
-                                                        // dispatch an inline 
create service success action so the new service is in the state
-                                                        this.store.dispatch(
-                                                            
ManagementControllerServicesActions.inlineCreateControllerServiceSuccess(
-                                                                {
-                                                                    response: {
-                                                                        
controllerService: createResponse
-                                                                    }
-                                                                }
-                                                            )
-                                                        );
-
                                                         // fetch an updated 
property descriptor
-                                                        return 
this.reportingTaskService
-                                                            
.getPropertyDescriptor(taskId, descriptor.name, false)
+                                                        return 
this.flowAnalysisRuleService
+                                                            
.getPropertyDescriptor(ruleId, descriptor.name, false)
                                                             .pipe(
                                                                 take(1),
                                                                 
map((descriptorResponse) => {
@@ -365,14 +353,14 @@ export class ReportingTasksEffects {
                             );
                     };
 
-                    editDialogReference.componentInstance.editReportingTask
+                    editDialogReference.componentInstance.editFlowAnalysisRule
                         .pipe(takeUntil(editDialogReference.afterClosed()))
                         .subscribe((updateControllerServiceRequest: 
UpdateControllerServiceRequest) => {
                             this.store.dispatch(
-                                ReportingTaskActions.configureReportingTask({
+                                
FlowAnalysisRuleActions.configureFlowAnalysisRule({
                                     request: {
-                                        id: request.reportingTask.id,
-                                        uri: request.reportingTask.uri,
+                                        id: request.flowAnalysisRule.id,
+                                        uri: request.flowAnalysisRule.uri,
                                         payload: 
updateControllerServiceRequest.payload,
                                         postUpdateNavigation: 
updateControllerServiceRequest.postUpdateNavigation
                                     }
@@ -383,9 +371,9 @@ export class ReportingTasksEffects {
                     editDialogReference.afterClosed().subscribe((response) => {
                         if (response != 'ROUTED') {
                             this.store.dispatch(
-                                ReportingTaskActions.selectReportingTask({
+                                
FlowAnalysisRuleActions.selectFlowAnalysisRule({
                                     request: {
-                                        id: taskId
+                                        id: ruleId
                                     }
                                 })
                             );
@@ -396,24 +384,24 @@ export class ReportingTasksEffects {
         { dispatch: false }
     );
 
-    configureReportingTask$ = createEffect(() =>
+    configureFlowAnalysisRule$ = createEffect(() =>
         this.actions$.pipe(
-            ofType(ReportingTaskActions.configureReportingTask),
+            ofType(FlowAnalysisRuleActions.configureFlowAnalysisRule),
             map((action) => action.request),
             switchMap((request) =>
-                
from(this.reportingTaskService.updateReportingTask(request)).pipe(
+                
from(this.flowAnalysisRuleService.updateFlowAnalysisRule(request)).pipe(
                     map((response) =>
-                        ReportingTaskActions.configureReportingTaskSuccess({
+                        
FlowAnalysisRuleActions.configureFlowAnalysisRuleSuccess({
                             response: {
                                 id: request.id,
-                                reportingTask: response,
+                                flowAnalysisRule: response,
                                 postUpdateNavigation: 
request.postUpdateNavigation
                             }
                         })
                     ),
                     catchError((error) =>
                         of(
-                            ReportingTaskActions.reportingTasksApiError({
+                            FlowAnalysisRuleActions.flowAnalysisRuleApiError({
                                 error: error.error
                             })
                         )
@@ -423,10 +411,10 @@ export class ReportingTasksEffects {
         )
     );
 
-    configureReportingTaskSuccess$ = createEffect(
+    configureFlowAnalysisRuleSuccess$ = createEffect(
         () =>
             this.actions$.pipe(
-                ofType(ReportingTaskActions.configureReportingTaskSuccess),
+                
ofType(FlowAnalysisRuleActions.configureFlowAnalysisRuleSuccess),
                 map((action) => action.response),
                 tap((response) => {
                     if (response.postUpdateNavigation) {
@@ -440,22 +428,36 @@ export class ReportingTasksEffects {
         { dispatch: false }
     );
 
-    startReportingTask$ = createEffect(() =>
+    selectFlowAnalysisRule$ = createEffect(
+        () =>
+            this.actions$.pipe(
+                ofType(FlowAnalysisRuleActions.selectFlowAnalysisRule),
+                map((action) => action.request),
+                tap((request) => {
+                    this.router.navigate(['/settings', 'flow-analysis-rules', 
request.id]);
+                })
+            ),
+        { dispatch: false }
+    );
+
+    enableFlowAnalysisRule$ = createEffect(() =>
         this.actions$.pipe(
-            ofType(ReportingTaskActions.startReportingTask),
+            ofType(FlowAnalysisRuleActions.enableFlowAnalysisRule),
             map((action) => action.request),
             switchMap((request) =>
-                
from(this.reportingTaskService.startReportingTask(request)).pipe(
+                from(this.flowAnalysisRuleService.setEnable(request, 
true)).pipe(
                     map((response) =>
-                        ReportingTaskActions.startReportingTaskSuccess({
+                        FlowAnalysisRuleActions.enableFlowAnalysisRuleSuccess({
                             response: {
-                                reportingTask: response
+                                id: request.id,
+                                flowAnalysisRule: response,
+                                postUpdateNavigation: 
response.postUpdateNavigation
                             }
                         })
                     ),
                     catchError((error) =>
                         of(
-                            ReportingTaskActions.reportingTasksApiError({
+                            FlowAnalysisRuleActions.flowAnalysisRuleApiError({
                                 error: error.error
                             })
                         )
@@ -465,22 +467,38 @@ export class ReportingTasksEffects {
         )
     );
 
-    stopReportingTask$ = createEffect(() =>
+    enableFlowAnalysisRuleSuccess$ = createEffect(
+        () =>
+            this.actions$.pipe(
+                ofType(FlowAnalysisRuleActions.enableFlowAnalysisRuleSuccess),
+                map((action) => action.response),
+                tap((response) => {
+                    if (response.postUpdateNavigation) {
+                        this.router.navigate(response.postUpdateNavigation);
+                    }
+                })
+            ),
+        { dispatch: false }
+    );
+
+    disableFlowAnalysisRule$ = createEffect(() =>
         this.actions$.pipe(
-            ofType(ReportingTaskActions.stopReportingTask),
+            ofType(FlowAnalysisRuleActions.disableFlowAnalysisRule),
             map((action) => action.request),
             switchMap((request) =>
-                
from(this.reportingTaskService.stopReportingTask(request)).pipe(
+                from(this.flowAnalysisRuleService.setEnable(request, 
false)).pipe(
                     map((response) =>
-                        ReportingTaskActions.stopReportingTaskSuccess({
+                        
FlowAnalysisRuleActions.disableFlowAnalysisRuleSuccess({
                             response: {
-                                reportingTask: response
+                                id: request.id,
+                                flowAnalysisRule: response,
+                                postUpdateNavigation: 
response.postUpdateNavigation
                             }
                         })
                     ),
                     catchError((error) =>
                         of(
-                            ReportingTaskActions.reportingTasksApiError({
+                            FlowAnalysisRuleActions.flowAnalysisRuleApiError({
                                 error: error.error
                             })
                         )
@@ -490,13 +508,15 @@ export class ReportingTasksEffects {
         )
     );
 
-    selectReportingTask$ = createEffect(
+    disableFlowAnalysisRuleSuccess$ = createEffect(
         () =>
             this.actions$.pipe(
-                ofType(ReportingTaskActions.selectReportingTask),
-                map((action) => action.request),
-                tap((request) => {
-                    this.router.navigate(['/settings', 'reporting-tasks', 
request.id]);
+                ofType(FlowAnalysisRuleActions.disableFlowAnalysisRuleSuccess),
+                map((action) => action.response),
+                tap((response) => {
+                    if (response.postUpdateNavigation) {
+                        this.router.navigate(response.postUpdateNavigation);
+                    }
                 })
             ),
         { dispatch: false }
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.reducer.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.reducer.ts
new file mode 100644
index 0000000000..e7202b2408
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.reducer.ts
@@ -0,0 +1,123 @@
+/*
+ * 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 { createReducer, on } from '@ngrx/store';
+import { FlowAnalysisRulesState } from './index';
+import {
+    configureFlowAnalysisRule,
+    configureFlowAnalysisRuleSuccess,
+    createFlowAnalysisRule,
+    createFlowAnalysisRuleSuccess,
+    deleteFlowAnalysisRule,
+    deleteFlowAnalysisRuleSuccess,
+    loadFlowAnalysisRules,
+    loadFlowAnalysisRulesSuccess,
+    flowAnalysisRuleApiError,
+    resetFlowAnalysisRulesState,
+    disableFlowAnalysisRule,
+    enableFlowAnalysisRule,
+    enableFlowAnalysisRuleSuccess,
+    disableFlowAnalysisRuleSuccess
+} from './flow-analysis-rules.actions';
+import { produce } from 'immer';
+
+export const initialState: FlowAnalysisRulesState = {
+    flowAnalysisRules: [],
+    saving: false,
+    loadedTimestamp: '',
+    error: null,
+    status: 'pending'
+};
+
+export const flowAnalysisRulesReducer = createReducer(
+    initialState,
+    on(resetFlowAnalysisRulesState, (state) => ({
+        ...initialState
+    })),
+    on(loadFlowAnalysisRules, (state) => ({
+        ...state,
+        status: 'loading' as const
+    })),
+    on(loadFlowAnalysisRulesSuccess, (state, { response }) => ({
+        ...state,
+        flowAnalysisRules: response.flowAnalysisRules,
+        loadedTimestamp: response.loadedTimestamp,
+        error: null,
+        status: 'success' as const
+    })),
+    on(flowAnalysisRuleApiError, (state, { error }) => ({
+        ...state,
+        saving: false,
+        error,
+        status: 'error' as const
+    })),
+    on(enableFlowAnalysisRuleSuccess, (state, { response }) => {
+        return produce(state, (draftState) => {
+            const componentIndex: number = 
draftState.flowAnalysisRules.findIndex((f: any) => response.id === f.id);
+            if (componentIndex > -1) {
+                draftState.flowAnalysisRules[componentIndex] = 
response.flowAnalysisRule;
+            }
+            draftState.saving = false;
+        });
+    }),
+    on(disableFlowAnalysisRuleSuccess, (state, { response }) => {
+        return produce(state, (draftState) => {
+            const componentIndex: number = 
draftState.flowAnalysisRules.findIndex((f: any) => response.id === f.id);
+            if (componentIndex > -1) {
+                draftState.flowAnalysisRules[componentIndex] = 
response.flowAnalysisRule;
+            }
+            draftState.saving = false;
+        });
+    }),
+    on(configureFlowAnalysisRuleSuccess, (state, { response }) => {
+        return produce(state, (draftState) => {
+            const componentIndex: number = 
draftState.flowAnalysisRules.findIndex((f: any) => response.id === f.id);
+            if (componentIndex > -1) {
+                draftState.flowAnalysisRules[componentIndex] = 
response.flowAnalysisRule;
+            }
+            draftState.saving = false;
+        });
+    }),
+    on(
+        createFlowAnalysisRule,
+        deleteFlowAnalysisRule,
+        configureFlowAnalysisRule,
+        enableFlowAnalysisRule,
+        disableFlowAnalysisRule,
+        (state, { request }) => ({
+            ...state,
+            saving: true
+        })
+    ),
+    on(createFlowAnalysisRuleSuccess, (state, { response }) => {
+        return produce(state, (draftState) => {
+            draftState.flowAnalysisRules.push(response.flowAnalysisRule);
+            draftState.saving = false;
+        });
+    }),
+    on(deleteFlowAnalysisRuleSuccess, (state, { response }) => {
+        return produce(state, (draftState) => {
+            const componentIndex: number = 
draftState.flowAnalysisRules.findIndex(
+                (f: any) => response.flowAnalysisRule.id === f.id
+            );
+            if (componentIndex > -1) {
+                draftState.flowAnalysisRules.splice(componentIndex, 1);
+            }
+            draftState.saving = false;
+        });
+    })
+);
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.selectors.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.selectors.ts
new file mode 100644
index 0000000000..b69f9f2ae2
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.selectors.ts
@@ -0,0 +1,54 @@
+/*
+ * 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 { selectSettingsState, SettingsState } from '../index';
+import { FlowAnalysisRuleEntity, flowAnalysisRulesFeatureKey, 
FlowAnalysisRulesState } from './index';
+import { selectCurrentRoute } from '../../../../state/router/router.selectors';
+
+export const selectFlowAnalysisRulesState = createSelector(
+    selectSettingsState,
+    (state: SettingsState) => state[flowAnalysisRulesFeatureKey]
+);
+
+export const selectSaving = createSelector(
+    selectFlowAnalysisRulesState,
+    (state: FlowAnalysisRulesState) => state.saving
+);
+
+export const selectFlowAnalysisRuleIdFromRoute = 
createSelector(selectCurrentRoute, (route) => {
+    if (route) {
+        // always select the rule from the route
+        return route.params.id;
+    }
+    return null;
+});
+
+export const selectSingleEditedFlowAnalysisRule = 
createSelector(selectCurrentRoute, (route) => {
+    if (route?.routeConfig?.path == 'edit') {
+        return route.params.id;
+    }
+    return null;
+});
+
+export const selectFlowAnalysisRules = createSelector(
+    selectFlowAnalysisRulesState,
+    (state: FlowAnalysisRulesState) => state.flowAnalysisRules
+);
+
+export const selectRule = (id: string) =>
+    createSelector(selectFlowAnalysisRules, (tasks: FlowAnalysisRuleEntity[]) 
=> tasks.find((task) => id == task.id));
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/index.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/index.ts
new file mode 100644
index 0000000000..043230f9cf
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/index.ts
@@ -0,0 +1,122 @@
+/*
+ * 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 { BulletinEntity, Bundle, DocumentedType, Permissions, Revision } from 
'../../../../state/shared';
+
+export const flowAnalysisRulesFeatureKey = 'flowAnalysisRules';
+
+export interface CreateFlowAnalysisRuleDialogRequest {
+    flowAnalysisRuleTypes: DocumentedType[];
+}
+
+export interface LoadFlowAnalysisRulesResponse {
+    flowAnalysisRules: FlowAnalysisRuleEntity[];
+    loadedTimestamp: string;
+}
+
+export interface CreateFlowAnalysisRuleRequest {
+    flowAnalysisRuleType: string;
+    flowAnalysisRuleBundle: Bundle;
+    revision: Revision;
+}
+
+export interface CreateFlowAnalysisRuleSuccess {
+    flowAnalysisRule: FlowAnalysisRuleEntity;
+}
+
+export interface ConfigureFlowAnalysisRuleRequest {
+    id: string;
+    uri: string;
+    payload: any;
+    postUpdateNavigation?: string[];
+}
+
+export interface ConfigureFlowAnalysisRuleSuccess {
+    id: string;
+    flowAnalysisRule: FlowAnalysisRuleEntity;
+    postUpdateNavigation?: string[];
+}
+
+export interface UpdateFlowAnalysisRuleRequest {
+    payload: any;
+    postUpdateNavigation?: string[];
+}
+
+export interface EnableFlowAnalysisRuleSuccess {
+    id: string;
+    flowAnalysisRule: FlowAnalysisRuleEntity;
+    postUpdateNavigation?: string[];
+}
+
+export interface DisableFlowAnalysisRuleSuccess {
+    id: string;
+    flowAnalysisRule: FlowAnalysisRuleEntity;
+    postUpdateNavigation?: string[];
+}
+
+export interface EnableFlowAnalysisRuleRequest {
+    id: string;
+    flowAnalysisRule: FlowAnalysisRuleEntity;
+}
+
+export interface DisableFlowAnalysisRuleRequest {
+    id: string;
+    flowAnalysisRule: FlowAnalysisRuleEntity;
+}
+
+export interface ConfigureFlowAnalysisRuleRequest {
+    id: string;
+    uri: string;
+    payload: any;
+    postUpdateNavigation?: string[];
+}
+
+export interface EditFlowAnalysisRuleDialogRequest {
+    id: string;
+    flowAnalysisRule: FlowAnalysisRuleEntity;
+}
+
+export interface DeleteFlowAnalysisRuleRequest {
+    flowAnalysisRule: FlowAnalysisRuleEntity;
+}
+
+export interface DeleteFlowAnalysisRuleSuccess {
+    flowAnalysisRule: FlowAnalysisRuleEntity;
+}
+
+export interface SelectFlowAnalysisRuleRequest {
+    id: string;
+}
+
+export interface FlowAnalysisRuleEntity {
+    permissions: Permissions;
+    operatePermissions?: Permissions;
+    revision: Revision;
+    bulletins: BulletinEntity[];
+    id: string;
+    uri: string;
+    status: any;
+    component: any;
+}
+
+export interface FlowAnalysisRulesState {
+    flowAnalysisRules: FlowAnalysisRuleEntity[];
+    saving: boolean;
+    loadedTimestamp: string;
+    error: string | null;
+    status: 'pending' | 'loading' | 'error' | 'success';
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/index.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/index.ts
index 09a7a40156..1182b01024 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/index.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/index.ts
@@ -27,6 +27,8 @@ import { reportingTasksFeatureKey, ReportingTasksState } from 
'./reporting-tasks
 import { reportingTasksReducer } from 
'./reporting-tasks/reporting-tasks.reducer';
 import { registryClientsFeatureKey, RegistryClientsState } from 
'./registry-clients';
 import { registryClientsReducer } from 
'./registry-clients/registry-clients.reducer';
+import { flowAnalysisRulesFeatureKey, FlowAnalysisRulesState } from 
'./flow-analysis-rules';
+import { flowAnalysisRulesReducer } from 
'./flow-analysis-rules/flow-analysis-rules.reducer';
 
 export const settingsFeatureKey = 'settings';
 
@@ -34,6 +36,7 @@ export interface SettingsState {
     [generalFeatureKey]: GeneralState;
     [managementControllerServicesFeatureKey]: 
ManagementControllerServicesState;
     [reportingTasksFeatureKey]: ReportingTasksState;
+    [flowAnalysisRulesFeatureKey]: FlowAnalysisRulesState;
     [registryClientsFeatureKey]: RegistryClientsState;
 }
 
@@ -42,6 +45,7 @@ export function reducers(state: SettingsState | undefined, 
action: Action) {
         [generalFeatureKey]: generalReducer,
         [managementControllerServicesFeatureKey]: 
managementControllerServicesReducer,
         [reportingTasksFeatureKey]: reportingTasksReducer,
+        [flowAnalysisRulesFeatureKey]: flowAnalysisRulesReducer,
         [registryClientsFeatureKey]: registryClientsReducer
     })(state, action);
 }
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/index.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/index.ts
index bd3022812e..f097a08dc8 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/index.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/index.ts
@@ -58,6 +58,11 @@ export interface ConfigureReportingTaskRequest {
     postUpdateNavigation?: string[];
 }
 
+export interface UpdateReportingTaskRequest {
+    payload: any;
+    postUpdateNavigation?: string[];
+}
+
 export interface EditReportingTaskDialogRequest {
     id: string;
     reportingTask: ReportingTaskEntity;
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/reporting-tasks.effects.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/reporting-tasks.effects.ts
index c08215a8ad..12ba1183d7 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/reporting-tasks.effects.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/reporting-tasks.effects.ts
@@ -43,7 +43,6 @@ import { CreateReportingTaskSuccess } from './index';
 import { ExtensionTypesService } from 
'../../../../service/extension-types.service';
 import { CreateControllerService } from 
'../../../../ui/common/controller-service/create-controller-service/create-controller-service.component';
 import { ManagementControllerServiceService } from 
'../../service/management-controller-service.service';
-import * as ManagementControllerServicesActions from 
'../management-controller-services/management-controller-services.actions';
 import { Client } from '../../../../service/client.service';
 
 @Injectable()
@@ -261,7 +260,7 @@ export class ReportingTasksEffects {
                         if 
(editDialogReference.componentInstance.editReportingTaskForm.dirty) {
                             const saveChangesDialogReference = 
this.dialog.open(YesNoDialog, {
                                 data: {
-                                    title: 'Controller Service Configuration',
+                                    title: 'Reporting Task Configuration',
                                     message: `Save changes before going to 
this ${destination}?`
                                 },
                                 panelClass: 'small-dialog'
@@ -327,17 +326,6 @@ export class ReportingTasksEffects {
                                                 .pipe(
                                                     take(1),
                                                     switchMap((createResponse) 
=> {
-                                                        // dispatch an inline 
create service success action so the new service is in the state
-                                                        this.store.dispatch(
-                                                            
ManagementControllerServicesActions.inlineCreateControllerServiceSuccess(
-                                                                {
-                                                                    response: {
-                                                                        
controllerService: createResponse
-                                                                    }
-                                                                }
-                                                            )
-                                                        );
-
                                                         // fetch an updated 
property descriptor
                                                         return 
this.reportingTaskService
                                                             
.getPropertyDescriptor(taskId, descriptor.name, false)
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.html
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.html
similarity index 78%
copy from 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.html
copy to 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.html
index 7f6a64a463..ac46c8b944 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.html
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.html
@@ -15,4 +15,8 @@
   ~ limitations under the License.
   -->
 
-<p>flow-analysis-rules works!</p>
+<extension-creation
+    [componentType]="'Flow Analysis Rule'"
+    [documentedTypes]="flowAnalysisRules"
+    [saving]="(saving$ | async)!"
+    
(extensionTypeSelected)="createFlowAnalysisRule($event)"></extension-creation>
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.scss
similarity index 76%
copy from 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.ts
copy to 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.scss
index 721bf268a5..2944f98194 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.scss
@@ -14,12 +14,3 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-import { Component } from '@angular/core';
-
-@Component({
-    selector: 'flow-analysis-rules',
-    templateUrl: './flow-analysis-rules.component.html',
-    styleUrls: ['./flow-analysis-rules.component.scss']
-})
-export class FlowAnalysisRules {}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.spec.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.spec.ts
new file mode 100644
index 0000000000..35b4ae82a0
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.spec.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 { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { CreateFlowAnalysisRule } from './create-flow-analysis-rule.component';
+import { CreateFlowAnalysisRuleDialogRequest } from 
'../../../state/flow-analysis-rules';
+import { MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { provideMockStore } from '@ngrx/store/testing';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { initialState } from 
'../../../state/flow-analysis-rules/flow-analysis-rules.reducer';
+
+describe('CreateFlowAnalysisRule', () => {
+    let component: CreateFlowAnalysisRule;
+    let fixture: ComponentFixture<CreateFlowAnalysisRule>;
+
+    const data: CreateFlowAnalysisRuleDialogRequest = {
+        flowAnalysisRuleTypes: [
+            {
+                type: 
'org.apache.nifi.flowanalysis.rules.DisallowComponentType',
+                bundle: {
+                    group: 'org.apache.nifi',
+                    artifact: 'nifi-standard-nar',
+                    version: '2.0.0-SNAPSHOT'
+                },
+                description:
+                    'Produces rule violations for each component (i.e. 
processors or controller services) of a given type.',
+                restricted: false,
+                tags: ['component', 'controller service', 'type', 'processor']
+            }
+        ]
+    };
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            imports: [CreateFlowAnalysisRule, BrowserAnimationsModule],
+            providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, 
provideMockStore({ initialState })]
+        });
+        fixture = TestBed.createComponent(CreateFlowAnalysisRule);
+        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/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.ts
new file mode 100644
index 0000000000..6c8c5f83bc
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.ts
@@ -0,0 +1,62 @@
+/*
+ * 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, Inject } from '@angular/core';
+import { MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { Store } from '@ngrx/store';
+import { ExtensionCreation } from 
'../../../../../ui/common/extension-creation/extension-creation.component';
+import { CreateFlowAnalysisRuleDialogRequest, FlowAnalysisRulesState } from 
'../../../state/flow-analysis-rules';
+import { createFlowAnalysisRule } from 
'../../../state/flow-analysis-rules/flow-analysis-rules.actions';
+import { Client } from '../../../../../service/client.service';
+import { DocumentedType } from '../../../../../state/shared';
+import { selectSaving } from 
'../../../state/flow-analysis-rules/flow-analysis-rules.selectors';
+import { AsyncPipe } from '@angular/common';
+
+@Component({
+    selector: 'create-flow-analysis-rule',
+    standalone: true,
+    imports: [ExtensionCreation, AsyncPipe],
+    templateUrl: './create-flow-analysis-rule.component.html',
+    styleUrls: ['./create-flow-analysis-rule.component.scss']
+})
+export class CreateFlowAnalysisRule {
+    flowAnalysisRules: DocumentedType[];
+    saving$ = this.store.select(selectSaving);
+
+    constructor(
+        @Inject(MAT_DIALOG_DATA) private dialogRequest: 
CreateFlowAnalysisRuleDialogRequest,
+        private store: Store<FlowAnalysisRulesState>,
+        private client: Client
+    ) {
+        this.flowAnalysisRules = dialogRequest.flowAnalysisRuleTypes;
+    }
+
+    createFlowAnalysisRule(flowAnalysisRuleType: DocumentedType): void {
+        this.store.dispatch(
+            createFlowAnalysisRule({
+                request: {
+                    revision: {
+                        clientId: this.client.getClientId(),
+                        version: 0
+                    },
+                    flowAnalysisRuleType: flowAnalysisRuleType.type,
+                    flowAnalysisRuleBundle: flowAnalysisRuleType.bundle
+                }
+            })
+        );
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.html
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.html
new file mode 100644
index 0000000000..ef6270d2aa
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.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.
+  -->
+
+<h2 mat-dialog-title>Edit Flow Analysis Rule</h2>
+<form class="flow-analysis-rule-edit-form" 
[formGroup]="editFlowAnalysisRuleForm">
+    <mat-dialog-content>
+        <mat-tab-group>
+            <mat-tab label="Settings">
+                <div class="tab-content py-4 flex gap-x-4">
+                    <div class="w-full">
+                        <mat-form-field>
+                            <mat-label>Name</mat-label>
+                            <input matInput formControlName="name" type="text" 
/>
+                        </mat-form-field>
+                        <div class="flex flex-col mb-5">
+                            <div>Id</div>
+                            <div class="value">{{ request.flowAnalysisRule.id 
}}</div>
+                        </div>
+                        <div class="flex flex-col mb-5">
+                            <div>Type</div>
+                            <div class="value">{{ 
formatType(request.flowAnalysisRule) }}</div>
+                        </div>
+                        <div class="flex flex-col mb-5">
+                            <div>Bundle</div>
+                            <div class="value">{{ 
formatBundle(request.flowAnalysisRule) }}</div>
+                        </div>
+                    </div>
+                    <div class="flex flex-col w-full">
+                        <div>
+                            <mat-form-field>
+                                <mat-label>Enforcement Policy</mat-label>
+                                <mat-select 
formControlName="enforcementPolicy">
+                                    <mat-option
+                                        *ngFor="let option of strategies"
+                                        [value]="option.value"
+                                        nifiTooltip
+                                        [tooltipComponentType]="TextTip"
+                                        
[tooltipInputData]="getPropertyTipData(option)"
+                                        [delayClose]="false">
+                                        {{ option.text }}
+                                    </mat-option>
+                                </mat-select>
+                            </mat-form-field>
+                        </div>
+                    </div>
+                </div>
+            </mat-tab>
+            <mat-tab label="Properties">
+                <div class="tab-content py-4">
+                    <property-table
+                        formControlName="properties"
+                        [createNewProperty]="createNewProperty"
+                        [createNewService]="createNewService"
+                        [goToService]="goToService">
+                    </property-table>
+                </div>
+            </mat-tab>
+            <mat-tab label="Comments">
+                <div class="tab-content py-4">
+                    <mat-form-field>
+                        <mat-label>Comments</mat-label>
+                        <textarea matInput formControlName="comments" 
type="text" rows="5"></textarea>
+                    </mat-form-field>
+                </div>
+            </mat-tab>
+        </mat-tab-group>
+    </mat-dialog-content>
+    <mat-dialog-actions align="end" *ngIf="{ value: (saving$ | async)! } as 
saving">
+        <button color="accent" mat-raised-button 
mat-dialog-close>Cancel</button>
+        <button
+            [disabled]="!editFlowAnalysisRuleForm.dirty || 
editFlowAnalysisRuleForm.invalid || saving.value"
+            type="button"
+            color="primary"
+            (click)="submitForm()"
+            mat-raised-button>
+            <span *nifiSpinner="saving.value">Apply</span>
+        </button>
+    </mat-dialog-actions>
+</form>
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.module.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.scss
similarity index 63%
copy from 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.module.ts
copy to 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.scss
index 69b8ee0c3a..5f0d7da2a1 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.module.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.scss
@@ -15,13 +15,29 @@
  * limitations under the License.
  */
 
-import { NgModule } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { FlowAnalysisRules } from './flow-analysis-rules.component';
+@use '@angular/material' as mat;
 
-@NgModule({
-    declarations: [FlowAnalysisRules],
-    exports: [FlowAnalysisRules],
-    imports: [CommonModule]
-})
-export class FlowAnalysisRulesModule {}
+.flow-analysis-rule-edit-form {
+    @include mat.button-density(-1);
+
+    .mdc-dialog__content {
+        padding: 0 16px;
+        font-size: 14px;
+
+        .tab-content {
+            height: 475px;
+            overflow-y: auto;
+        }
+    }
+
+    .mat-mdc-form-field {
+        width: 100%;
+    }
+
+    .supports-flow-analysis-rules {
+        ul {
+            list-style: disc outside;
+            margin-left: 1em;
+        }
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.spec.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.spec.ts
new file mode 100644
index 0000000000..40c8b8222d
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.spec.ts
@@ -0,0 +1,105 @@
+/*
+ * 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 { EditFlowAnalysisRule } from './edit-flow-analysis-rule.component';
+import { MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { EditFlowAnalysisRuleDialogRequest } from 
'../../../state/flow-analysis-rules';
+
+describe('EditFlowAnalysisRule', () => {
+    let component: EditFlowAnalysisRule;
+    let fixture: ComponentFixture<EditFlowAnalysisRule>;
+
+    const data: EditFlowAnalysisRuleDialogRequest = {
+        id: 'd5142be7-018c-1000-7105-2b1163fe0355',
+        flowAnalysisRule: {
+            revision: {
+                clientId: '2be7f8d0-fad2-4909-918f-b9a4ef1675b2',
+                version: 3
+            },
+            id: 'f08ddf27-018c-1000-4970-2fa78a6ee3ed',
+            uri: 
'https://localhost:8443/nifi-api/controller/flow-analysis-rules/f08ddf27-018c-1000-4970-2fa78a6ee3ed',
+            permissions: {
+                canRead: true,
+                canWrite: true
+            },
+            bulletins: [],
+            component: {
+                id: 'f08ddf27-018c-1000-4970-2fa78a6ee3ed',
+                name: 'DisallowComponentType',
+                type: 
'org.apache.nifi.flowanalysis.rules.DisallowComponentType',
+                bundle: {
+                    group: 'org.apache.nifi',
+                    artifact: 'nifi-standard-nar',
+                    version: '2.0.0-SNAPSHOT'
+                },
+                state: 'DISABLED',
+                comments: 'dfghsdgh',
+                persistsState: false,
+                restricted: false,
+                deprecated: false,
+                multipleVersionsAvailable: false,
+                supportsSensitiveDynamicProperties: false,
+                enforcementPolicy: 'ENFORCE',
+                properties: {
+                    'component-type': null
+                },
+                descriptors: {
+                    'component-type': {
+                        name: 'component-type',
+                        displayName: 'Component Type',
+                        description:
+                            "Components of the given type will produce a rule 
violation (i.e. they shouldn't exist). Either the simple or the fully qualified 
name of the type should be provided.",
+                        required: true,
+                        sensitive: false,
+                        dynamic: false,
+                        supportsEl: false,
+                        expressionLanguageScope: 'Not Supported',
+                        dependencies: []
+                    }
+                },
+                validationErrors: ["'Component Type' is invalid because 
Component Type is required"],
+                validationStatus: 'INVALID',
+                extensionMissing: false
+            },
+            operatePermissions: {
+                canRead: true,
+                canWrite: true
+            },
+            status: {
+                runStatus: 'DISABLED',
+                validationStatus: 'INVALID'
+            }
+        }
+    };
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            imports: [EditFlowAnalysisRule, BrowserAnimationsModule],
+            providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
+        });
+        fixture = TestBed.createComponent(EditFlowAnalysisRule);
+        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/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.ts
new file mode 100644
index 0000000000..d55f09fa05
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.ts
@@ -0,0 +1,161 @@
+/*
+ * 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, EventEmitter, Inject, Input, Output, signal } from 
'@angular/core';
+import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
+import { MatTooltipModule } from '@angular/material/tooltip';
+import { MatInputModule } from '@angular/material/input';
+import { MatButtonModule } from '@angular/material/button';
+import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
+import { MatTabsModule } from '@angular/material/tabs';
+import { MatOptionModule } from '@angular/material/core';
+import { MatSelectModule } from '@angular/material/select';
+import { AbstractControl, FormBuilder, FormControl, FormGroup, 
ReactiveFormsModule, Validators } from '@angular/forms';
+import { Observable } from 'rxjs';
+import { Client } from '../../../../../service/client.service';
+import {
+    InlineServiceCreationRequest,
+    InlineServiceCreationResponse,
+    Property,
+    SelectOption,
+    TextTipInput
+} from '../../../../../state/shared';
+import { NiFiCommon } from '../../../../../service/nifi-common.service';
+import { PropertyTable } from 
'../../../../../ui/common/property-table/property-table.component';
+import { NifiSpinnerDirective } from 
'../../../../../ui/common/spinner/nifi-spinner.directive';
+import { NifiTooltipDirective } from 
'../../../../../ui/common/tooltips/nifi-tooltip.directive';
+import { TextTip } from 
'../../../../../ui/common/tooltips/text-tip/text-tip.component';
+import {
+    EditFlowAnalysisRuleDialogRequest,
+    FlowAnalysisRuleEntity,
+    UpdateFlowAnalysisRuleRequest
+} from '../../../state/flow-analysis-rules';
+import { FlowAnalysisRuleTable } from 
'../flow-analysis-rule-table/flow-analysis-rule-table.component';
+
+@Component({
+    selector: 'edit-flow-analysis-rule',
+    standalone: true,
+    templateUrl: './edit-flow-analysis-rule.component.html',
+    imports: [
+        ReactiveFormsModule,
+        MatDialogModule,
+        MatInputModule,
+        MatButtonModule,
+        NgIf,
+        MatTabsModule,
+        MatOptionModule,
+        MatSelectModule,
+        NgForOf,
+        PropertyTable,
+        AsyncPipe,
+        NifiSpinnerDirective,
+        MatTooltipModule,
+        NifiTooltipDirective,
+        FlowAnalysisRuleTable
+    ],
+    styleUrls: ['./edit-flow-analysis-rule.component.scss']
+})
+export class EditFlowAnalysisRule {
+    @Input() createNewProperty!: (existingProperties: string[], 
allowsSensitive: boolean) => Observable<Property>;
+    @Input() createNewService!: (request: InlineServiceCreationRequest) => 
Observable<InlineServiceCreationResponse>;
+    @Input() saving$!: Observable<boolean>;
+    @Input() goToService!: (serviceId: string) => void;
+    @Output() editFlowAnalysisRule: 
EventEmitter<UpdateFlowAnalysisRuleRequest> =
+        new EventEmitter<UpdateFlowAnalysisRuleRequest>();
+
+    editFlowAnalysisRuleForm: FormGroup;
+
+    strategies: SelectOption[] = [
+        {
+            text: 'Enforce',
+            value: 'ENFORCE',
+            description: 'Treat violations of this rule as errors the 
correction of which is mandatory.'
+        }
+    ];
+
+    constructor(
+        @Inject(MAT_DIALOG_DATA) public request: 
EditFlowAnalysisRuleDialogRequest,
+        private formBuilder: FormBuilder,
+        private client: Client,
+        private nifiCommon: NiFiCommon
+    ) {
+        const serviceProperties: any = 
request.flowAnalysisRule.component.properties;
+        const properties: Property[] = 
Object.entries(serviceProperties).map((entry: any) => {
+            const [property, value] = entry;
+            return {
+                property,
+                value,
+                descriptor: 
request.flowAnalysisRule.component.descriptors[property]
+            };
+        });
+
+        // build the form
+        this.editFlowAnalysisRuleForm = this.formBuilder.group({
+            name: new FormControl(request.flowAnalysisRule.component.name, 
Validators.required),
+            state: new FormControl(request.flowAnalysisRule.component.state 
=== 'STOPPED', Validators.required),
+            enforcementPolicy: new FormControl('ENFORCE', Validators.required),
+            properties: new FormControl(properties),
+            comments: new 
FormControl(request.flowAnalysisRule.component.comments)
+        });
+    }
+
+    formatType(entity: FlowAnalysisRuleEntity): string {
+        return this.nifiCommon.formatType(entity.component);
+    }
+
+    formatBundle(entity: FlowAnalysisRuleEntity): string {
+        return this.nifiCommon.formatBundle(entity.component.bundle);
+    }
+
+    submitForm(postUpdateNavigation?: string[]) {
+        const payload: any = {
+            revision: this.client.getRevision(this.request.flowAnalysisRule),
+            component: {
+                id: this.request.flowAnalysisRule.id,
+                name: this.editFlowAnalysisRuleForm.get('name')?.value,
+                comments: this.editFlowAnalysisRuleForm.get('comments')?.value,
+                enforcementPolicy: 
this.editFlowAnalysisRuleForm.get('enforcementPolicy')?.value,
+                state: this.editFlowAnalysisRuleForm.get('state')?.value ? 
'STOPPED' : 'DISABLED'
+            }
+        };
+
+        const propertyControl: AbstractControl | null = 
this.editFlowAnalysisRuleForm.get('properties');
+        if (propertyControl && propertyControl.dirty) {
+            const properties: Property[] = propertyControl.value;
+            const values: { [key: string]: string | null } = {};
+            properties.forEach((property) => (values[property.property] = 
property.value));
+            payload.component.properties = values;
+            payload.component.sensitiveDynamicPropertyNames = properties
+                .filter((property) => property.descriptor.dynamic && 
property.descriptor.sensitive)
+                .map((property) => property.descriptor.name);
+        }
+
+        this.editFlowAnalysisRule.next({
+            payload,
+            postUpdateNavigation
+        });
+    }
+
+    getPropertyTipData(option: SelectOption): TextTipInput {
+        return {
+            // @ts-ignore
+            text: option.description
+        };
+    }
+
+    protected readonly TextTip = TextTip;
+}
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/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.html
similarity index 64%
copy from 
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
copy to 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.html
index eba96b9dfa..32802aa136 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/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.html
@@ -16,34 +16,47 @@
   -->
 
 <div class="relative h-full border">
-    <div class="reporting-task-table listing-table absolute inset-0 
overflow-y-auto">
-        <table mat-table [dataSource]="dataSource" matSort matSortDisableClear>
+    <div class="flow-analysis-rule-table listing-table absolute inset-0 
overflow-y-auto">
+        <table
+            mat-table
+            [dataSource]="dataSource"
+            matSort
+            matSortDisableClear
+            (matSortChange)="updateSort($event)"
+            [matSortActive]="sort.active"
+            [matSortDirection]="sort.direction">
             <!-- More Details Column -->
             <ng-container matColumnDef="moreDetails">
                 <th mat-header-cell *matHeaderCellDef></th>
                 <td mat-cell *matCellDef="let item">
                     <ng-container *ngIf="canRead(item)">
-                        <div class="flex items-center">
-                            <div class="mr-3 pointer fa fa-book" 
title="Usage"></div>
+                        <div class="flex items-center gap-x-3">
+                            <div class="pointer fa fa-book" 
title="Usage"></div>
                             <!-- TODO - handle read only in configure 
component? -->
-                            <div
-                                class="mr-3 pointer fa fa-comment"
-                                *ngIf="hasComments(item)"
-                                nifiTooltip
-                                [tooltipComponentType]="TextTip"
-                                
[tooltipInputData]="getCommentsTipData(item)"></div>
-                            <div
-                                class="mr-3 pointer fa fa-warning has-errors"
-                                *ngIf="hasErrors(item)"
-                                nifiTooltip
-                                [tooltipComponentType]="ValidationErrorsTip"
-                                
[tooltipInputData]="getValidationErrorsTipData(item)"></div>
-                            <div
-                                class="mr-3 pointer fa fa-sticky-note-o"
-                                *ngIf="hasBulletins(item)"
-                                nifiTooltip
-                                [tooltipComponentType]="BulletinsTip"
-                                
[tooltipInputData]="getBulletinsTipData(item)"></div>
+                            <div *ngIf="hasComments(item)">
+                                <div
+                                    class="pointer fa fa-comment"
+                                    [delayClose]="false"
+                                    nifiTooltip
+                                    [tooltipComponentType]="TextTip"
+                                    
[tooltipInputData]="getCommentsTipData(item)"></div>
+                            </div>
+                            <div *ngIf="hasErrors(item)">
+                                <div
+                                    class="pointer fa fa-warning has-errors"
+                                    [delayClose]="false"
+                                    nifiTooltip
+                                    
[tooltipComponentType]="ValidationErrorsTip"
+                                    
[tooltipInputData]="getValidationErrorsTipData(item)"></div>
+                            </div>
+                            <div *ngIf="hasBulletins(item)">
+                                <div
+                                    class="pointer fa fa-sticky-note-o"
+                                    [delayClose]="false"
+                                    nifiTooltip
+                                    [tooltipComponentType]="BulletinsTip"
+                                    
[tooltipInputData]="getBulletinsTipData(item)"></div>
+                            </div>
                         </div>
                     </ng-container>
                 </td>
@@ -100,20 +113,21 @@
                 <td mat-cell *matCellDef="let item">
                     <div class="flex items-center gap-x-3">
                         <div
-                            class="pointer fa fa-stop"
-                            *ngIf="canStop(item)"
-                            (click)="stopClicked(item)"
-                            title="Stop"></div>
-                        <div
-                            class="pointer fa fa-pencil"
-                            *ngIf="canEdit(item)"
+                            class="pointer fa fa-gear"
+                            *ngIf="canConfigure(item)"
                             (click)="configureClicked(item, $event)"
                             title="Edit"></div>
+                        <!-- TODO - handle read only in configure component? 
-->
+                        <div
+                            class="pointer fa icon icon-enable-false"
+                            *ngIf="canDisable(item)"
+                            (click)="disableClicked(item, $event)"
+                            title="Disable"></div>
                         <div
-                            class="pointer fa fa-play"
-                            *ngIf="canStart(item)"
-                            (click)="startClicked(item)"
-                            title="Start"></div>
+                            class="pointer fa fa-flash"
+                            *ngIf="canEnable(item)"
+                            (click)="enabledClicked(item, $event)"
+                            title="Enable"></div>
                         <div class="pointer fa fa-exchange" 
*ngIf="canChangeVersion(item)" title="Change Version"></div>
                         <div
                             class="pointer fa fa-trash"
@@ -121,12 +135,6 @@
                             (click)="deleteClicked(item)"
                             title="Delete"></div>
                         <div class="pointer fa fa-tasks" 
*ngIf="canViewState(item)" title="View State"></div>
-                        <div
-                            class="pointer fa fa-key"
-                            *ngIf="canManageAccessPolicies()"
-                            (click)="$event.stopPropagation()"
-                            [routerLink]="getPolicyLink(item)"
-                            title="Access Policies"></div>
                     </div>
                 </td>
             </ng-container>
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.scss
similarity index 76%
copy from 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.ts
copy to 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.scss
index 721bf268a5..fad04378a6 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.scss
@@ -15,11 +15,10 @@
  * limitations under the License.
  */
 
-import { Component } from '@angular/core';
-
-@Component({
-    selector: 'flow-analysis-rules',
-    templateUrl: './flow-analysis-rules.component.html',
-    styleUrls: ['./flow-analysis-rules.component.scss']
-})
-export class FlowAnalysisRules {}
+.flow-analysis-rule-table.listing-table {
+    table {
+        .mat-column-moreDetails {
+            min-width: 100px;
+        }
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.spec.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.spec.ts
similarity index 68%
copy from 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.spec.ts
copy to 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.spec.ts
index 76a8dee5b2..0d9de74b27 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.spec.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.spec.ts
@@ -17,17 +17,19 @@
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
 
-import { FlowAnalysisRules } from './flow-analysis-rules.component';
+import { FlowAnalysisRuleTable } from './flow-analysis-rule-table.component';
+import { MatTableModule } from '@angular/material/table';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
 
-describe('FlowAnalysisRules', () => {
-    let component: FlowAnalysisRules;
-    let fixture: ComponentFixture<FlowAnalysisRules>;
+describe('FlowAnalysisRuleTable', () => {
+    let component: FlowAnalysisRuleTable;
+    let fixture: ComponentFixture<FlowAnalysisRuleTable>;
 
     beforeEach(() => {
         TestBed.configureTestingModule({
-            declarations: [FlowAnalysisRules]
+            imports: [BrowserAnimationsModule, MatTableModule, 
FlowAnalysisRuleTable]
         });
-        fixture = TestBed.createComponent(FlowAnalysisRules);
+        fixture = TestBed.createComponent(FlowAnalysisRuleTable);
         component = fixture.componentInstance;
         fixture.detectChanges();
     });
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-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/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.ts
new file mode 100644
index 0000000000..5108c5d352
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.ts
@@ -0,0 +1,268 @@
+/*
+ * 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, EventEmitter, Input, Output, ViewChild } from 
'@angular/core';
+import { MatButtonModule } from '@angular/material/button';
+import { MatDialogModule } from '@angular/material/dialog';
+import { RouterLink } from '@angular/router';
+import { NgClass, NgIf } from '@angular/common';
+import { MatTableDataSource, MatTableModule } from '@angular/material/table';
+import { MatSortModule, Sort } from '@angular/material/sort';
+import { FlowAnalysisRuleEntity } from '../../../state/flow-analysis-rules';
+import { TextTip } from 
'../../../../../ui/common/tooltips/text-tip/text-tip.component';
+import { BulletinsTip } from 
'../../../../../ui/common/tooltips/bulletins-tip/bulletins-tip.component';
+import { ValidationErrorsTip } from 
'../../../../../ui/common/tooltips/validation-errors-tip/validation-errors-tip.component';
+import { NiFiCommon } from '../../../../../service/nifi-common.service';
+import { BulletinsTipInput, TextTipInput, ValidationErrorsTipInput } from 
'../../../../../state/shared';
+import { NifiTooltipDirective } from 
'../../../../../ui/common/tooltips/nifi-tooltip.directive';
+import { ReportingTaskEntity } from '../../../state/reporting-tasks';
+import { CurrentUser } from '../../../../../state/current-user';
+
+@Component({
+    selector: 'flow-analysis-rule-table',
+    standalone: true,
+    templateUrl: './flow-analysis-rule-table.component.html',
+    imports: [
+        MatButtonModule,
+        MatDialogModule,
+        MatTableModule,
+        MatSortModule,
+        NgIf,
+        NgClass,
+        NifiTooltipDirective,
+        RouterLink
+    ],
+    styleUrls: ['./flow-analysis-rule-table.component.scss', 
'../../../../../../assets/styles/listing-table.scss']
+})
+export class FlowAnalysisRuleTable {
+    @Input() set flowAnalysisRules(flowAnalysisRuleEntities: 
FlowAnalysisRuleEntity[]) {
+        this.dataSource.data = 
this.sortFlowAnalysisRules(flowAnalysisRuleEntities, this.sort);
+    }
+    @Input() selectedFlowAnalysisRuleId!: string;
+    @Input() currentUser!: CurrentUser;
+
+    @Output() selectFlowAnalysisRule: EventEmitter<FlowAnalysisRuleEntity> = 
new EventEmitter<FlowAnalysisRuleEntity>();
+    @Output() deleteFlowAnalysisRule: EventEmitter<FlowAnalysisRuleEntity> = 
new EventEmitter<FlowAnalysisRuleEntity>();
+    @Output() configureFlowAnalysisRule: EventEmitter<FlowAnalysisRuleEntity> =
+        new EventEmitter<FlowAnalysisRuleEntity>();
+    @Output() enableFlowAnalysisRule: EventEmitter<FlowAnalysisRuleEntity> = 
new EventEmitter<FlowAnalysisRuleEntity>();
+    @Output() disableFlowAnalysisRule: EventEmitter<FlowAnalysisRuleEntity> =
+        new EventEmitter<FlowAnalysisRuleEntity>();
+
+    sort: Sort = {
+        active: 'name',
+        direction: 'asc'
+    };
+
+    protected readonly TextTip = TextTip;
+    protected readonly BulletinsTip = BulletinsTip;
+    protected readonly ValidationErrorsTip = ValidationErrorsTip;
+
+    displayedColumns: string[] = ['moreDetails', 'name', 'type', 'bundle', 
'state', 'actions'];
+    dataSource: MatTableDataSource<FlowAnalysisRuleEntity> = new 
MatTableDataSource<FlowAnalysisRuleEntity>();
+
+    constructor(private nifiCommon: NiFiCommon) {}
+
+    updateSort(sort: Sort): void {
+        this.sort = sort;
+        this.dataSource.data = 
this.sortFlowAnalysisRules(this.dataSource.data, sort);
+    }
+
+    sortFlowAnalysisRules(items: FlowAnalysisRuleEntity[], sort: Sort): 
FlowAnalysisRuleEntity[] {
+        const data: FlowAnalysisRuleEntity[] = items.slice();
+        return data.sort((a, b) => {
+            const isAsc = sort.direction === 'asc';
+
+            let retVal: number = 0;
+            switch (sort.active) {
+                case 'name':
+                    retVal = this.nifiCommon.compareString(a.component.name, 
b.component.name);
+                    break;
+                case 'type':
+                    retVal = this.nifiCommon.compareString(this.formatType(a), 
this.formatType(b));
+                    break;
+                case 'bundle':
+                    retVal = 
this.nifiCommon.compareString(this.formatBundle(a), this.formatBundle(b));
+                    break;
+                case 'state':
+                    retVal = 
this.nifiCommon.compareString(this.formatState(a), this.formatState(b));
+                    break;
+            }
+
+            return retVal * (isAsc ? 1 : -1);
+        });
+    }
+
+    canRead(entity: FlowAnalysisRuleEntity): boolean {
+        return entity.permissions.canRead;
+    }
+
+    canWrite(entity: FlowAnalysisRuleEntity): boolean {
+        return entity.permissions.canWrite;
+    }
+
+    canOperate(entity: FlowAnalysisRuleEntity): boolean {
+        if (this.canWrite(entity)) {
+            return true;
+        }
+        return !!entity.operatePermissions?.canWrite;
+    }
+
+    hasComments(entity: FlowAnalysisRuleEntity): boolean {
+        return !this.nifiCommon.isBlank(entity.component.comments);
+    }
+
+    getCommentsTipData(entity: FlowAnalysisRuleEntity): TextTipInput {
+        return {
+            text: entity.component.comments
+        };
+    }
+
+    hasErrors(entity: FlowAnalysisRuleEntity): boolean {
+        return !this.nifiCommon.isEmpty(entity.component.validationErrors);
+    }
+
+    getValidationErrorsTipData(entity: FlowAnalysisRuleEntity): 
ValidationErrorsTipInput {
+        return {
+            isValidating: entity.status.validationStatus === 'VALIDATING',
+            validationErrors: entity.component.validationErrors
+        };
+    }
+
+    hasBulletins(entity: FlowAnalysisRuleEntity): boolean {
+        return !this.nifiCommon.isEmpty(entity.bulletins);
+    }
+
+    getBulletinsTipData(entity: FlowAnalysisRuleEntity): BulletinsTipInput {
+        return {
+            bulletins: entity.bulletins
+        };
+    }
+
+    getStateIcon(entity: FlowAnalysisRuleEntity): string {
+        if (entity.status.validationStatus === 'VALIDATING') {
+            return 'validating fa fa-spin fa-circle-o-notch';
+        } else if (entity.status.validationStatus === 'INVALID') {
+            return 'invalid fa fa-warning';
+        } else {
+            if (entity.status.runStatus === 'DISABLED') {
+                return 'disabled icon icon-enable-false';
+            } else if (entity.status.runStatus === 'ENABLED') {
+                return 'enabled fa fa-flash';
+            }
+        }
+        return '';
+    }
+
+    formatState(entity: FlowAnalysisRuleEntity): string {
+        if (entity.status.validationStatus === 'VALIDATING') {
+            return 'Validating';
+        } else if (entity.status.validationStatus === 'INVALID') {
+            return 'Invalid';
+        } else {
+            if (entity.status.runStatus === 'DISABLED') {
+                return 'Disabled';
+            } else if (entity.status.runStatus === 'ENABLED') {
+                return 'Enabled';
+            }
+        }
+        return '';
+    }
+
+    formatType(entity: FlowAnalysisRuleEntity): string {
+        return this.nifiCommon.formatType(entity.component);
+    }
+
+    formatBundle(entity: FlowAnalysisRuleEntity): string {
+        return this.nifiCommon.formatBundle(entity.component.bundle);
+    }
+
+    isDisabled(entity: FlowAnalysisRuleEntity): boolean {
+        return entity.status.runStatus === 'DISABLED';
+    }
+
+    isEnabled(entity: FlowAnalysisRuleEntity): boolean {
+        return entity.status.runStatus === 'ENABLED';
+    }
+
+    hasActiveThreads(entity: ReportingTaskEntity): boolean {
+        return entity.status?.activeThreadCount > 0;
+    }
+
+    canConfigure(entity: FlowAnalysisRuleEntity): boolean {
+        return this.canRead(entity) && this.canWrite(entity) && 
this.isDisabled(entity);
+    }
+
+    configureClicked(entity: FlowAnalysisRuleEntity, event: MouseEvent): void {
+        event.stopPropagation();
+        this.configureFlowAnalysisRule.next(entity);
+    }
+
+    canEnable(entity: FlowAnalysisRuleEntity): boolean {
+        const userAuthorized: boolean = this.canRead(entity) && 
this.canOperate(entity);
+        return userAuthorized && this.isDisabled(entity) && 
entity.status.validationStatus === 'VALID';
+    }
+
+    enabledClicked(entity: FlowAnalysisRuleEntity, event: MouseEvent): void {
+        this.enableFlowAnalysisRule.next(entity);
+    }
+
+    canDisable(entity: FlowAnalysisRuleEntity): boolean {
+        const userAuthorized: boolean = this.canRead(entity) && 
this.canOperate(entity);
+        return userAuthorized && this.isEnabled(entity);
+    }
+
+    disableClicked(entity: FlowAnalysisRuleEntity, event: MouseEvent): void {
+        this.disableFlowAnalysisRule.next(entity);
+    }
+
+    canChangeVersion(entity: FlowAnalysisRuleEntity): boolean {
+        return (
+            this.isDisabled(entity) &&
+            this.canRead(entity) &&
+            this.canWrite(entity) &&
+            entity.component.multipleVersionsAvailable === true
+        );
+    }
+
+    canDelete(entity: FlowAnalysisRuleEntity): boolean {
+        return this.isDisabled(entity) && this.canRead(entity) && 
this.canWrite(entity) && this.canModifyParent();
+    }
+
+    canModifyParent(): boolean {
+        return this.currentUser.controllerPermissions.canRead && 
this.currentUser.controllerPermissions.canWrite;
+    }
+
+    deleteClicked(entity: FlowAnalysisRuleEntity): void {
+        this.deleteFlowAnalysisRule.next(entity);
+    }
+
+    canViewState(entity: FlowAnalysisRuleEntity): boolean {
+        return this.canRead(entity) && this.canWrite(entity) && 
entity.component.persistsState === true;
+    }
+
+    select(entity: FlowAnalysisRuleEntity): void {
+        this.selectFlowAnalysisRule.next(entity);
+    }
+
+    isSelected(entity: FlowAnalysisRuleEntity): boolean {
+        if (this.selectedFlowAnalysisRuleId) {
+            return entity.id == this.selectedFlowAnalysisRuleId;
+        }
+        return false;
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.html
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.html
index 7f6a64a463..219b7dd22e 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.html
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.html
@@ -15,4 +15,37 @@
   ~ limitations under the License.
   -->
 
-<p>flow-analysis-rules works!</p>
+<ng-container *ngIf="flowAnalysisRuleState$ | async; let 
flowAnalysisRuleState">
+    <div *ngIf="isInitialLoading(flowAnalysisRuleState); else loaded">
+        <ngx-skeleton-loader count="3"></ngx-skeleton-loader>
+    </div>
+    <ng-template #loaded>
+        <div class="flex flex-col h-full gap-y-2" *ngIf="currentUser$ | async; 
let currentUser">
+            <div class="flex justify-end" 
*ngIf="currentUser.controllerPermissions.canWrite">
+                <button class="nifi-button" 
(click)="openNewFlowAnalysisRuleDialog()">
+                    <i class="fa fa-plus"></i>
+                </button>
+            </div>
+            <div class="flex-1">
+                <flow-analysis-rule-table
+                    [currentUser]="currentUser"
+                    [selectedFlowAnalysisRuleId]="selectedFlowAnalysisRuleId$ 
| async"
+                    
[flowAnalysisRules]="flowAnalysisRuleState.flowAnalysisRules"
+                    
(configureFlowAnalysisRule)="configureFlowAnalysisRule($event)"
+                    (selectFlowAnalysisRule)="selectFlowAnalysisRule($event)"
+                    (enableFlowAnalysisRule)="enableFlowAnalysisRule($event)"
+                    (disableFlowAnalysisRule)="disableFlowAnalysisRule($event)"
+                    
(deleteFlowAnalysisRule)="deleteFlowAnalysisRule($event)"></flow-analysis-rule-table>
+            </div>
+            <div class="flex justify-between">
+                <div class="refresh-container flex items-center gap-x-2">
+                    <button class="nifi-button" 
(click)="refreshFlowAnalysisRuleListing()">
+                        <i class="fa fa-refresh" 
[class.fa-spin]="flowAnalysisRuleState.status === 'loading'"></i>
+                    </button>
+                    <div>Last updated:</div>
+                    <div class="refresh-timestamp">{{ 
flowAnalysisRuleState.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/settings/ui/flow-analysis-rules/flow-analysis-rules.component.spec.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.spec.ts
index 76a8dee5b2..2faeeaa9f5 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.spec.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.spec.ts
@@ -18,6 +18,8 @@
 import { ComponentFixture, TestBed } from '@angular/core/testing';
 
 import { FlowAnalysisRules } from './flow-analysis-rules.component';
+import { provideMockStore } from '@ngrx/store/testing';
+import { initialState } from 
'../../state/flow-analysis-rules/flow-analysis-rules.reducer';
 
 describe('FlowAnalysisRules', () => {
     let component: FlowAnalysisRules;
@@ -25,7 +27,12 @@ describe('FlowAnalysisRules', () => {
 
     beforeEach(() => {
         TestBed.configureTestingModule({
-            declarations: [FlowAnalysisRules]
+            declarations: [FlowAnalysisRules],
+            providers: [
+                provideMockStore({
+                    initialState
+                })
+            ]
         });
         fixture = TestBed.createComponent(FlowAnalysisRules);
         component = fixture.componentInstance;
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.ts
index 721bf268a5..42f882ceba 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.ts
@@ -15,11 +15,138 @@
  * limitations under the License.
  */
 
-import { Component } from '@angular/core';
+import { Component, OnDestroy, OnInit } from '@angular/core';
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { Store } from '@ngrx/store';
+import { filter, switchMap, take } from 'rxjs';
+import {
+    selectFlowAnalysisRuleIdFromRoute,
+    selectFlowAnalysisRulesState,
+    selectSingleEditedFlowAnalysisRule,
+    selectRule
+} from '../../state/flow-analysis-rules/flow-analysis-rules.selectors';
+import {
+    loadFlowAnalysisRules,
+    navigateToEditFlowAnalysisRule,
+    openConfigureFlowAnalysisRuleDialog,
+    openNewFlowAnalysisRuleDialog,
+    promptFlowAnalysisRuleDeletion,
+    resetFlowAnalysisRulesState,
+    selectFlowAnalysisRule,
+    enableFlowAnalysisRule,
+    disableFlowAnalysisRule
+} from '../../state/flow-analysis-rules/flow-analysis-rules.actions';
+import { initialState } from 
'../../state/flow-analysis-rules/flow-analysis-rules.reducer';
+import { selectCurrentUser } from 
'../../../../state/current-user/current-user.selectors';
+import { NiFiState } from '../../../../state';
+import { FlowAnalysisRuleEntity, FlowAnalysisRulesState } from 
'../../state/flow-analysis-rules';
+import { CurrentUser } from '../../../../state/current-user';
 
 @Component({
     selector: 'flow-analysis-rules',
     templateUrl: './flow-analysis-rules.component.html',
     styleUrls: ['./flow-analysis-rules.component.scss']
 })
-export class FlowAnalysisRules {}
+export class FlowAnalysisRules implements OnInit, OnDestroy {
+    flowAnalysisRuleState$ = this.store.select(selectFlowAnalysisRulesState);
+    selectedFlowAnalysisRuleId$ = 
this.store.select(selectFlowAnalysisRuleIdFromRoute);
+    currentUser$ = this.store.select(selectCurrentUser);
+
+    constructor(private store: Store<NiFiState>) {
+        this.store
+            .select(selectSingleEditedFlowAnalysisRule)
+            .pipe(
+                filter((id: string) => id != null),
+                switchMap((id: string) =>
+                    this.store.select(selectRule(id)).pipe(
+                        filter((entity) => entity != null),
+                        take(1)
+                    )
+                ),
+                takeUntilDestroyed()
+            )
+            .subscribe((entity) => {
+                if (entity) {
+                    this.store.dispatch(
+                        openConfigureFlowAnalysisRuleDialog({
+                            request: {
+                                id: entity.id,
+                                flowAnalysisRule: entity
+                            }
+                        })
+                    );
+                }
+            });
+    }
+
+    ngOnInit(): void {
+        this.store.dispatch(loadFlowAnalysisRules());
+    }
+
+    isInitialLoading(state: FlowAnalysisRulesState): boolean {
+        // using the current timestamp to detect the initial load event
+        return state.loadedTimestamp == initialState.loadedTimestamp;
+    }
+
+    openNewFlowAnalysisRuleDialog(): void {
+        this.store.dispatch(openNewFlowAnalysisRuleDialog());
+    }
+
+    refreshFlowAnalysisRuleListing(): void {
+        this.store.dispatch(loadFlowAnalysisRules());
+    }
+
+    selectFlowAnalysisRule(entity: FlowAnalysisRuleEntity): void {
+        this.store.dispatch(
+            selectFlowAnalysisRule({
+                request: {
+                    id: entity.id
+                }
+            })
+        );
+    }
+
+    enableFlowAnalysisRule(entity: FlowAnalysisRuleEntity): void {
+        this.store.dispatch(
+            enableFlowAnalysisRule({
+                request: {
+                    id: entity.id,
+                    flowAnalysisRule: entity
+                }
+            })
+        );
+    }
+
+    disableFlowAnalysisRule(entity: FlowAnalysisRuleEntity): void {
+        this.store.dispatch(
+            disableFlowAnalysisRule({
+                request: {
+                    id: entity.id,
+                    flowAnalysisRule: entity
+                }
+            })
+        );
+    }
+
+    deleteFlowAnalysisRule(entity: FlowAnalysisRuleEntity): void {
+        this.store.dispatch(
+            promptFlowAnalysisRuleDeletion({
+                request: {
+                    flowAnalysisRule: entity
+                }
+            })
+        );
+    }
+
+    configureFlowAnalysisRule(entity: FlowAnalysisRuleEntity): void {
+        this.store.dispatch(
+            navigateToEditFlowAnalysisRule({
+                id: entity.id
+            })
+        );
+    }
+
+    ngOnDestroy(): void {
+        this.store.dispatch(resetFlowAnalysisRulesState());
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.module.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.module.ts
index 69b8ee0c3a..4337832580 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.module.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.module.ts
@@ -18,10 +18,24 @@
 import { NgModule } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { FlowAnalysisRules } from './flow-analysis-rules.component';
+import { FlowAnalysisRuleTable } from 
'./flow-analysis-rule-table/flow-analysis-rule-table.component';
+import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
+import { MatSortModule } from '@angular/material/sort';
+import { MatTableModule } from '@angular/material/table';
+import { NifiTooltipDirective } from 
'../../../../ui/common/tooltips/nifi-tooltip.directive';
+import { PropertyTable } from 
'../../../../ui/common/property-table/property-table.component';
 
 @NgModule({
     declarations: [FlowAnalysisRules],
     exports: [FlowAnalysisRules],
-    imports: [CommonModule]
+    imports: [
+        CommonModule,
+        NgxSkeletonLoaderModule,
+        MatSortModule,
+        MatTableModule,
+        NifiTooltipDirective,
+        FlowAnalysisRuleTable,
+        PropertyTable
+    ]
 })
 export class FlowAnalysisRulesModule {}
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/edit-reporting-task/edit-reporting-task.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/edit-reporting-task/edit-reporting-task.component.ts
index 75d6ec9a28..36192b4ed5 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/edit-reporting-task/edit-reporting-task.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/edit-reporting-task/edit-reporting-task.component.ts
@@ -32,19 +32,18 @@ import {
     ControllerServiceReferencingComponent,
     InlineServiceCreationRequest,
     InlineServiceCreationResponse,
-    Parameter,
-    ParameterContextReferenceEntity,
     Property,
-    PropertyTipInput,
     SelectOption,
-    TextTipInput,
-    UpdateControllerServiceRequest,
-    UpdateReportingTaskRequest
+    TextTipInput
 } from '../../../../../state/shared';
 import { NiFiCommon } from '../../../../../service/nifi-common.service';
 import { PropertyTable } from 
'../../../../../ui/common/property-table/property-table.component';
 import { NifiSpinnerDirective } from 
'../../../../../ui/common/spinner/nifi-spinner.directive';
-import { EditReportingTaskDialogRequest, ReportingTaskEntity } from 
'../../../state/reporting-tasks';
+import {
+    EditReportingTaskDialogRequest,
+    ReportingTaskEntity,
+    UpdateReportingTaskRequest
+} from '../../../state/reporting-tasks';
 import { ControllerServiceApi } from 
'../../../../../ui/common/controller-service/controller-service-api/controller-service-api.component';
 import { NifiTooltipDirective } from 
'../../../../../ui/common/tooltips/nifi-tooltip.directive';
 import { TextTip } from 
'../../../../../ui/common/tooltips/text-tip/text-tip.component';
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 eba96b9dfa..fd5b0c3062 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
@@ -23,27 +23,33 @@
                 <th mat-header-cell *matHeaderCellDef></th>
                 <td mat-cell *matCellDef="let item">
                     <ng-container *ngIf="canRead(item)">
-                        <div class="flex items-center">
-                            <div class="mr-3 pointer fa fa-book" 
title="Usage"></div>
+                        <div class="flex items-center gap-x-3">
+                            <div class="pointer fa fa-book" 
title="Usage"></div>
                             <!-- TODO - handle read only in configure 
component? -->
-                            <div
-                                class="mr-3 pointer fa fa-comment"
-                                *ngIf="hasComments(item)"
-                                nifiTooltip
-                                [tooltipComponentType]="TextTip"
-                                
[tooltipInputData]="getCommentsTipData(item)"></div>
-                            <div
-                                class="mr-3 pointer fa fa-warning has-errors"
-                                *ngIf="hasErrors(item)"
-                                nifiTooltip
-                                [tooltipComponentType]="ValidationErrorsTip"
-                                
[tooltipInputData]="getValidationErrorsTipData(item)"></div>
-                            <div
-                                class="mr-3 pointer fa fa-sticky-note-o"
-                                *ngIf="hasBulletins(item)"
-                                nifiTooltip
-                                [tooltipComponentType]="BulletinsTip"
-                                
[tooltipInputData]="getBulletinsTipData(item)"></div>
+                            <div *ngIf="hasComments(item)">
+                                <div
+                                    class="pointer fa fa-comment"
+                                    [delayClose]="false"
+                                    nifiTooltip
+                                    [tooltipComponentType]="TextTip"
+                                    
[tooltipInputData]="getCommentsTipData(item)"></div>
+                            </div>
+                            <div *ngIf="hasErrors(item)">
+                                <div
+                                    class="pointer fa fa-warning has-errors"
+                                    [delayClose]="false"
+                                    nifiTooltip
+                                    
[tooltipComponentType]="ValidationErrorsTip"
+                                    
[tooltipInputData]="getValidationErrorsTipData(item)"></div>
+                            </div>
+                            <div *ngIf="hasBulletins(item)">
+                                <div
+                                    class="pointer fa fa-sticky-note-o"
+                                    [delayClose]="false"
+                                    nifiTooltip
+                                    [tooltipComponentType]="BulletinsTip"
+                                    
[tooltipInputData]="getBulletinsTipData(item)"></div>
+                            </div>
                         </div>
                     </ng-container>
                 </td>
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/extension-types/extension-types.selectors.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/extension-types/extension-types.selectors.ts
index aecd46c816..db29215cb9 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/extension-types/extension-types.selectors.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/extension-types/extension-types.selectors.ts
@@ -46,6 +46,11 @@ export const selectRegistryClientTypes = createSelector(
     (state: ExtensionTypesState) => state.registryClientTypes
 );
 
+export const selectFlowAnalysisRuleTypes = createSelector(
+    selectExtensionTypesState,
+    (state: ExtensionTypesState) => state.flowAnalysisRuleTypes
+);
+
 export const selectTypesToIdentifyComponentRestrictions = createSelector(
     selectExtensionTypesState,
     (state: ExtensionTypesState) => {
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/shared/index.ts
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/shared/index.ts
index 199e5b03c7..dcd872cace 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/shared/index.ts
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/shared/index.ts
@@ -126,11 +126,6 @@ export interface UpdateControllerServiceRequest {
     postUpdateNavigation?: string[];
 }
 
-export interface UpdateReportingTaskRequest {
-    payload: any;
-    postUpdateNavigation?: string[];
-}
-
 export interface SetEnableControllerServiceDialogRequest {
     id: string;
     controllerService: ControllerServiceEntity;
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 4aab0499f9..5508fadd88 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
@@ -31,27 +31,27 @@
                                 because of the gap between items. simple 
solution is to
                                 just wrap the target.
                             -->
-                            <div>
+                            <div *ngIf="hasComments(item)">
                                 <div
                                     class="pointer fa fa-comment"
-                                    *ngIf="hasComments(item)"
                                     nifiTooltip
+                                    [delayClose]="false"
                                     [tooltipComponentType]="TextTip"
                                     
[tooltipInputData]="getCommentsTipData(item)"></div>
                             </div>
-                            <div>
+                            <div *ngIf="hasErrors(item)">
                                 <div
                                     class="pointer fa fa-warning has-errors"
-                                    *ngIf="hasErrors(item)"
                                     nifiTooltip
+                                    [delayClose]="false"
                                     
[tooltipComponentType]="ValidationErrorsTip"
                                     
[tooltipInputData]="getValidationErrorsTipData(item)"></div>
                             </div>
-                            <div>
+                            <div *ngIf="hasBulletins(item)">
                                 <div
                                     class="pointer fa fa-sticky-note-o"
-                                    *ngIf="hasBulletins(item)"
                                     nifiTooltip
+                                    [delayClose]="false"
                                     [tooltipComponentType]="BulletinsTip"
                                     
[tooltipInputData]="getBulletinsTipData(item)"></div>
                             </div>


Reply via email to