mcgilman commented on code in PR #9548:
URL: https://github.com/apache/nifi/pull/9548#discussion_r1876724441


##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.ts:
##########
@@ -413,7 +630,7 @@ export class EditProcessor extends TabbedDialog {
     }
 
     override isDirty(): boolean {
-        return this.editProcessorForm.dirty;
+        return this.editProcessorForm.dirty || false;

Review Comment:
   Can this be removed?



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.ts:
##########
@@ -253,6 +275,32 @@ export class EditProcessor extends TabbedDialog {
                 new FormControl({ value: this.runDurationMillis, disabled: 
this.readonly }, Validators.required)
             );
         }
+
+        this.processRunStateUpdates(request.entity);
+    }
+
+    processRunStateUpdates(entity: any) {

Review Comment:
   ```suggestion
       private processRunStateUpdates(entity: any) {
   ```



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.html:
##########
@@ -290,19 +308,79 @@ <h2 mat-dialog-title>
         </mat-tab>
     </mat-tab-group>
     @if ({ value: (saving$ | async)! }; as saving) {
-        <mat-dialog-actions align="end">
-            @if (readonly) {
-                <button mat-flat-button mat-dialog-close>Close</button>
-            } @else {
-                <button mat-button mat-dialog-close>Cancel</button>
-                <button
-                    [disabled]="!editProcessorForm.dirty || 
editProcessorForm.invalid || saving.value"
-                    type="button"
-                    (click)="submitForm()"
-                    mat-flat-button>
-                    <span *nifiSpinner="saving.value">Apply</span>
-                </button>
-            }
+        <mat-dialog-actions align="start">
+            <div class="flex w-full justify-between items-center">
+                <div>
+                    @if (isStoppable()) {
+                        <button type="button" mat-stroked-button 
[matMenuTriggerFor]="operateMenu">
+                            <div class="flex items-center">
+                                <i class="mr-2 success-color-default fa 
fa-play"></i>Running<i
+                                    class="ml-2 -mt-1 fa fa-sort-desc"></i>
+                            </div>
+                        </button>
+                    } @else if (isRunnable()) {
+                        <button type="button" mat-stroked-button 
[matMenuTriggerFor]="operateMenu">
+                            <div class="flex items-center">
+                                <i class="mr-2 error-color-variant fa 
fa-stop"></i>Stopped<i
+                                    class="ml-2 -mt-1 fa fa-sort-desc"></i>
+                            </div>
+                        </button>
+                    } @else {
+                        <button
+                            type="button"
+                            mat-stroked-button
+                            [disabled]="isStopping()"
+                            [matMenuTriggerFor]="operateMenu">
+                            <div class="flex items-center">
+                                @if (isInvalid()) {
+                                    <i class="mr-2 fa fa-warning 
caution-color"></i>
+                                } @else if (isDisabled()) {
+                                    <i class="mr-2 icon icon-enable-false"></i>
+                                } @else {
+                                    <i class="mr-2 fa fa-circle-o-notch 
fa-spin primary-color"></i>
+                                }

Review Comment:
   If the user cannot operate the Processor, this will result in the showing 
the spinner.
   
   <img width="156" alt="Screenshot 2024-12-09 at 3 29 32 PM" 
src="https://github.com/user-attachments/assets/138a2d55-b681-432e-81d6-62c11baf75de";>
   
   I think in this case, the button should be disabled since they don't have 
permission to operate this Processor.
   



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.ts:
##########
@@ -401,8 +460,166 @@ export class EditProcessor extends TabbedDialog {
         });
     }
 
+    hasBulletins(): boolean {
+        return this.request.entity.permissions.canRead && 
!this.nifiCommon.isEmpty(this.bulletins);
+    }
+
+    getBulletinsTipData(): BulletinsTipInput {
+        return {
+            bulletins: this.bulletins
+        };
+    }
+
+    getBulletinTooltipPosition(): ConnectedPosition {
+        return {
+            originX: 'end',
+            originY: 'bottom',
+            overlayX: 'end',
+            overlayY: 'top',
+            offsetX: -8,
+            offsetY: 8
+        };
+    }
+
+    getMostSevereBulletinLevel(): string | null {
+        // determine the most severe of the bulletins
+        const mostSevere = 
this.canvasUtils.getMostSevereBulletin(this.bulletins);
+        return mostSevere ? mostSevere.bulletin.level.toLowerCase() : null;
+    }
+
+    isStoppable(): boolean {
+        if (!this.canOperate()) {
+            return false;
+        }
+
+        return this.status.aggregateSnapshot.runStatus === 'Running';
+    }
+
+    isStopping(): boolean {
+        if (!this.canOperate()) {
+            return false;
+        }
+
+        return (
+            this.status.aggregateSnapshot.runStatus === 'Stopped' && 
this.status.aggregateSnapshot.activeThreadCount > 0
+        );
+    }
+
+    isInvalid(): boolean {
+        if (!this.canOperate()) {
+            return false;
+        }
+
+        return this.status.aggregateSnapshot.runStatus === 'Invalid';
+    }
+
+    isDisabled(): boolean {
+        if (!this.canOperate()) {
+            return false;
+        }
+
+        return this.status.aggregateSnapshot.runStatus === 'Disabled';
+    }
+
+    isRunnable(): boolean {
+        if (!this.canOperate()) {
+            return false;
+        }
+
+        return (
+            !(
+                this.status.aggregateSnapshot.runStatus === 'Running' ||
+                this.status.aggregateSnapshot.activeThreadCount > 0
+            ) && this.status.aggregateSnapshot.runStatus === 'Stopped'
+        );
+    }
+
+    isDisableable(): boolean {
+        if (!this.canOperate()) {
+            return false;
+        }
+
+        return (
+            !(
+                this.status.aggregateSnapshot.runStatus === 'Running' ||
+                this.status.aggregateSnapshot.activeThreadCount > 0
+            ) &&
+            (this.status.aggregateSnapshot.runStatus === 'Stopped' ||
+                this.status.aggregateSnapshot.runStatus === 'Invalid')
+        );
+    }
+
+    isEnableable(): boolean {
+        if (!this.canOperate()) {
+            return false;
+        }
+
+        return (
+            !(
+                this.status.aggregateSnapshot.runStatus === 'Running' ||
+                this.status.aggregateSnapshot.activeThreadCount > 0
+            ) && this.status.aggregateSnapshot.runStatus === 'Disabled'
+        );
+    }
+
+    private canOperate(): boolean {
+        return this.request.entity.permissions.canWrite || 
this.request.entity.operatePermissions?.canWrite;
+    }
+
+    stop() {
+        this.stopComponentRequest.next({
+            id: this.request.entity.id,
+            uri: this.request.entity.uri,
+            type: ComponentType.Processor,
+            revision: this.client.getRevision({
+                ...this.request.entity,
+                revision: this.revision
+            }),
+            errorStrategy: 'snackbar'
+        });
+    }
+
+    start() {
+        this.startComponentRequest.next({
+            id: this.request.entity.id,
+            uri: this.request.entity.uri,
+            type: ComponentType.Processor,
+            revision: this.client.getRevision({
+                ...this.request.entity,
+                revision: this.revision
+            }),
+            errorStrategy: 'snackbar'
+        });
+    }
+
+    disable() {
+        this.disableComponentRequest.next({
+            id: this.request.entity.id,
+            uri: this.request.entity.uri,
+            type: ComponentType.Processor,
+            revision: this.client.getRevision({
+                ...this.request.entity,
+                revision: this.revision
+            }),
+            errorStrategy: 'snackbar'
+        });
+    }
+
+    enable() {
+        this.enableComponentRequest.next({
+            id: this.request.entity.id,
+            uri: this.request.entity.uri,
+            type: ComponentType.Processor,
+            revision: this.client.getRevision({
+                ...this.request.entity,
+                revision: this.revision
+            }),
+            errorStrategy: 'snackbar'
+        });
+    }
+
     private getModifiedProperties(): ModifiedProperties {
-        const propertyControl: AbstractControl | null = 
this.editProcessorForm.get('properties');
+        const propertyControl: AbstractControl | null | undefined = 
this.editProcessorForm.get('properties');

Review Comment:
   Is this needed? Locally, it appears this change isn't needed and we don't 
use `| undefined` elsewhere when getting controls from the form.



##########
nifi-frontend/src/main/frontend/.gitignore:
##########
@@ -45,3 +45,4 @@ Thumbs.db
 .nx/cache
 .nx/workspace-data
 .angular
+/.tool-versions

Review Comment:
   Can you move `tool-versions` entry into miscellaneous section. There are 
other version front end entries that we should probably group together.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to