diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/connectors/ui/connector-table/connector-table.component.html b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/connectors/ui/connector-table/connector-table.component.html
index b7b9df274487..91d2e95578e9 100644
--- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/connectors/ui/connector-table/connector-table.component.html
+++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/connectors/ui/connector-table/connector-table.component.html
@@ -74,11 +74,16 @@
State |
- {{ formatState(item) }}
+ @if (canRead(item)) {
+
+
+ }
@if (canRead(item) && canDiscardConfig(item)) {
- (Edits not applied)
+
+
}
|
diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/connectors/ui/connector-table/connector-table.component.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/connectors/ui/connector-table/connector-table.component.ts
index e2d529606685..fb04927180a7 100644
--- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/connectors/ui/connector-table/connector-table.component.ts
+++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/connectors/ui/connector-table/connector-table.component.ts
@@ -26,11 +26,13 @@ import {
ConnectorActionName,
NifiTooltipDirective,
NiFiCommon,
+ StatusBadge,
canReadConnector,
canModifyConnector,
canOperateConnector,
isConnectorActionAllowed,
- getConnectorActionDisabledReason
+ getConnectorActionDisabledReason,
+ getConnectorStateVariant
} from '@nifi/shared';
import { ValidationErrorsTipInput } from '../../../../state/shared';
import { FlowConfiguration } from '../../../../state/flow-configuration';
@@ -41,7 +43,15 @@ import { ValidationErrorsTip } from '../../../../ui/common/tooltips/validation-e
selector: 'connector-table',
standalone: true,
templateUrl: './connector-table.component.html',
- imports: [MatButtonModule, MatTableModule, MatSortModule, MatMenuModule, MatTooltipModule, NifiTooltipDirective],
+ imports: [
+ MatButtonModule,
+ MatTableModule,
+ MatSortModule,
+ MatMenuModule,
+ MatTooltipModule,
+ NifiTooltipDirective,
+ StatusBadge
+ ],
styleUrls: ['./connector-table.component.scss']
})
export class ConnectorTable {
@@ -113,6 +123,8 @@ export class ConnectorTable {
return getConnectorActionDisabledReason(entity, actionName);
}
+ protected readonly getConnectorStateVariant = getConnectorStateVariant;
+
canConfigure(entity: ConnectorEntity): boolean {
return isConnectorActionAllowed(entity, 'CONFIGURE');
}
diff --git a/nifi-frontend/src/main/frontend/libs/shared/src/assets/themes/material.scss b/nifi-frontend/src/main/frontend/libs/shared/src/assets/themes/material.scss
index be93da637a46..5f6e7b73c7c0 100644
--- a/nifi-frontend/src/main/frontend/libs/shared/src/assets/themes/material.scss
+++ b/nifi-frontend/src/main/frontend/libs/shared/src/assets/themes/material.scss
@@ -361,6 +361,30 @@
)
);
}
+
+ // NOTE: The following styles are for the status badge and status banner components. The `.status-variant` class should only be applied to a div or span element that should adhear to the variant color and background.
+ .status-variant {
+ &.neutral {
+ color: var(--mat-sys-on-surface);
+ background-color: var(--mat-sys-surface-dim);
+ }
+ &.critical {
+ color: var(--mat-sys-on-error);
+ background-color: var(--mat-sys-error);
+ }
+ &.caution {
+ color: var(--nf-caution-contrast);
+ background-color: var(--nf-caution-default);
+ }
+ &.success {
+ color: var(--nf-success-contrast);
+ background-color: var(--nf-success-default);
+ }
+ &.info {
+ color: var(--nf-success-contrast);
+ background-color: var(--nf-success-variant);
+ }
+ }
}
// Note: Color palettes are generated from primary: #6750a4
@@ -854,4 +878,28 @@ html {
)
);
}
+
+ // NOTE: The following styles are for the status badge and status banner components. The `.status-variant` class should only be applied to a div or span element that should adhear to the variant color and background.
+ .status-variant {
+ &.neutral {
+ color: var(--mat-sys-on-surface);
+ background-color: var(--mat-sys-surface-dim);
+ }
+ &.critical {
+ color: var(--mat-sys-on-error);
+ background-color: var(--mat-sys-error);
+ }
+ &.caution {
+ color: var(--nf-caution-contrast);
+ background-color: var(--nf-caution-default);
+ }
+ &.success {
+ color: var(--nf-success-contrast);
+ background-color: var(--nf-success-default);
+ }
+ &.info {
+ color: var(--nf-success-contrast);
+ background-color: var(--nf-success-variant);
+ }
+ }
}
diff --git a/nifi-frontend/src/main/frontend/libs/shared/src/components/connector-detail-header/connector-detail-header.component.html b/nifi-frontend/src/main/frontend/libs/shared/src/components/connector-detail-header/connector-detail-header.component.html
index 7ecf984d0a37..4cb8a7fc210f 100644
--- a/nifi-frontend/src/main/frontend/libs/shared/src/components/connector-detail-header/connector-detail-header.component.html
+++ b/nifi-frontend/src/main/frontend/libs/shared/src/components/connector-detail-header/connector-detail-header.component.html
@@ -16,16 +16,13 @@
-->
-
- {{ connector().component.state }}
-
+
@if (hasUnappliedEdits()) {
-
- Edits not applied
-
+
}
diff --git a/nifi-frontend/src/main/frontend/libs/shared/src/components/connector-detail-header/connector-detail-header.component.spec.ts b/nifi-frontend/src/main/frontend/libs/shared/src/components/connector-detail-header/connector-detail-header.component.spec.ts
index 65321dd24b3a..35961829f9ca 100644
--- a/nifi-frontend/src/main/frontend/libs/shared/src/components/connector-detail-header/connector-detail-header.component.spec.ts
+++ b/nifi-frontend/src/main/frontend/libs/shared/src/components/connector-detail-header/connector-detail-header.component.spec.ts
@@ -18,6 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ConnectorDetailHeader } from './connector-detail-header.component';
import { ConnectorEntity, ConnectorState, ConnectorStatus } from '../../types';
+import { getConnectorStateVariant } from '../../utils/connector-permissions.utils';
interface SetupOptions {
connector?: ConnectorEntity;
@@ -108,78 +109,9 @@ describe('ConnectorDetailHeader', () => {
expect(stateBadge.textContent.trim()).toBe('RUNNING');
});
- describe('getStateVariant', () => {
- it('should return success for RUNNING', async () => {
- const { component } = await setup();
- expect(component.getStateVariant(ConnectorState.RUNNING)).toBe('success');
- });
-
- it('should return neutral for STOPPED', async () => {
- const { component } = await setup();
- expect(component.getStateVariant(ConnectorState.STOPPED)).toBe('neutral');
- });
-
- it('should return neutral for DISABLED', async () => {
- const { component } = await setup();
- expect(component.getStateVariant(ConnectorState.DISABLED)).toBe('neutral');
- });
-
- it('should return info for STARTING', async () => {
- const { component } = await setup();
- expect(component.getStateVariant(ConnectorState.STARTING)).toBe('info');
- });
-
- it('should return info for UPDATING', async () => {
- const { component } = await setup();
- expect(component.getStateVariant(ConnectorState.UPDATING)).toBe('info');
- });
-
- it('should return info for STOPPING', async () => {
- const { component } = await setup();
- expect(component.getStateVariant(ConnectorState.STOPPING)).toBe('info');
- });
-
- it('should return info for DRAINING', async () => {
- const { component } = await setup();
- expect(component.getStateVariant(ConnectorState.DRAINING)).toBe('info');
- });
-
- it('should return info for PREPARING_FOR_UPDATE', async () => {
- const { component } = await setup();
- expect(component.getStateVariant(ConnectorState.PREPARING_FOR_UPDATE)).toBe('info');
- });
-
- it('should return critical for UPDATE_FAILED', async () => {
- const { component } = await setup();
- expect(component.getStateVariant(ConnectorState.UPDATE_FAILED)).toBe('critical');
- });
-
- it('should return neutral for unknown state', async () => {
- const { component } = await setup();
- expect(component.getStateVariant('UNKNOWN')).toBe('neutral');
- });
- });
-
- describe('getStateColorClass', () => {
- it('should map success variant to success-color-default', async () => {
- const { component } = await setup();
- expect(component.getStateColorClass(ConnectorState.RUNNING)).toBe('success-color-default');
- });
-
- it('should map neutral variant to neutral-color', async () => {
- const { component } = await setup();
- expect(component.getStateColorClass(ConnectorState.STOPPED)).toBe('neutral-color');
- });
-
- it('should map info variant to primary-color', async () => {
- const { component } = await setup();
- expect(component.getStateColorClass(ConnectorState.STARTING)).toBe('primary-color');
- });
-
- it('should map critical variant to error-color', async () => {
- const { component } = await setup();
- expect(component.getStateColorClass(ConnectorState.UPDATE_FAILED)).toBe('error-color');
- });
+ it('should delegate getStateVariant to getConnectorStateVariant', async () => {
+ const { component } = await setup();
+ expect(component.getStateVariant).toBe(getConnectorStateVariant);
});
describe('hasUnappliedEdits', () => {
diff --git a/nifi-frontend/src/main/frontend/libs/shared/src/components/connector-detail-header/connector-detail-header.component.ts b/nifi-frontend/src/main/frontend/libs/shared/src/components/connector-detail-header/connector-detail-header.component.ts
index 6c55abceace1..6ef715607bee 100644
--- a/nifi-frontend/src/main/frontend/libs/shared/src/components/connector-detail-header/connector-detail-header.component.ts
+++ b/nifi-frontend/src/main/frontend/libs/shared/src/components/connector-detail-header/connector-detail-header.component.ts
@@ -17,14 +17,14 @@
import { Component, computed, input } from '@angular/core';
import { NgTemplateOutlet } from '@angular/common';
-import { ConnectorEntity, ConnectorState } from '../../types';
-
-export type ConnectorStateVariant = 'neutral' | 'critical' | 'caution' | 'success' | 'info';
+import { ConnectorEntity } from '../../types';
+import { StatusBadge } from '../status-badge/status-badge.component';
+import { getConnectorStateVariant } from '../../utils/connector-permissions.utils';
@Component({
selector: 'connector-detail-header',
standalone: true,
- imports: [NgTemplateOutlet],
+ imports: [NgTemplateOutlet, StatusBadge],
templateUrl: './connector-detail-header.component.html',
styleUrls: ['./connector-detail-header.component.scss']
})
@@ -48,39 +48,5 @@ export class ConnectorDetailHeader {
return action?.allowed ?? false;
});
- getStateVariant(state: string): ConnectorStateVariant {
- switch (state) {
- case ConnectorState.RUNNING:
- return 'success';
- case ConnectorState.STOPPED:
- case ConnectorState.DISABLED:
- return 'neutral';
- case ConnectorState.STARTING:
- case ConnectorState.UPDATING:
- case ConnectorState.STOPPING:
- case ConnectorState.DRAINING:
- case ConnectorState.PREPARING_FOR_UPDATE:
- return 'info';
- case ConnectorState.UPDATE_FAILED:
- return 'critical';
- default:
- return 'neutral';
- }
- }
-
- getStateColorClass(state: string): string {
- switch (this.getStateVariant(state)) {
- case 'success':
- return 'success-color-default';
- case 'critical':
- return 'error-color';
- case 'caution':
- return 'caution-color';
- case 'info':
- return 'primary-color';
- case 'neutral':
- default:
- return 'neutral-color';
- }
- }
+ protected readonly getStateVariant = getConnectorStateVariant;
}
diff --git a/nifi-frontend/src/main/frontend/libs/shared/src/components/index.ts b/nifi-frontend/src/main/frontend/libs/shared/src/components/index.ts
index 2ffa339d64af..112731a7a430 100644
--- a/nifi-frontend/src/main/frontend/libs/shared/src/components/index.ts
+++ b/nifi-frontend/src/main/frontend/libs/shared/src/components/index.ts
@@ -46,5 +46,6 @@ export * from './side-panel-container/side-panel-container.component';
export * from './connector-property-input/connector-property-input.component';
export * from './connector-detail-header/connector-detail-header.component';
export * from './searchable-select/searchable-select.component';
+export * from './status-badge/status-badge.component';
export * from './multi-select-option/multi-select-option.component';
export * from './asset-upload/asset-upload.component';
diff --git a/nifi-frontend/src/main/frontend/libs/shared/src/components/status-badge/status-badge.component.html b/nifi-frontend/src/main/frontend/libs/shared/src/components/status-badge/status-badge.component.html
new file mode 100644
index 000000000000..2a40397035b6
--- /dev/null
+++ b/nifi-frontend/src/main/frontend/libs/shared/src/components/status-badge/status-badge.component.html
@@ -0,0 +1,28 @@
+
+
+@if (label) {
+
+ {{ label }}
+ @if (message && message.length > 0) {
+
+ }
+
+}
diff --git a/nifi-frontend/src/main/frontend/libs/shared/src/components/status-badge/status-badge.component.scss b/nifi-frontend/src/main/frontend/libs/shared/src/components/status-badge/status-badge.component.scss
new file mode 100644
index 000000000000..1cead2c5dbee
--- /dev/null
+++ b/nifi-frontend/src/main/frontend/libs/shared/src/components/status-badge/status-badge.component.scss
@@ -0,0 +1,42 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@use '@angular/material' as mat;
+
+.status-badge {
+ .fa {
+ &.neutral {
+ color: var(--mat-sys-on-surface);
+ }
+
+ &.critical {
+ color: var(--mat-sys-on-error);
+ }
+
+ &.caution {
+ color: var(--nf-caution-contrast);
+ }
+
+ &.success {
+ color: var(--nf-success-contrast);
+ }
+
+ &.info {
+ color: var(--nf-success-contrast);
+ }
+ }
+}
diff --git a/nifi-frontend/src/main/frontend/libs/shared/src/components/status-badge/status-badge.component.spec.ts b/nifi-frontend/src/main/frontend/libs/shared/src/components/status-badge/status-badge.component.spec.ts
new file mode 100644
index 000000000000..d147d37bc017
--- /dev/null
+++ b/nifi-frontend/src/main/frontend/libs/shared/src/components/status-badge/status-badge.component.spec.ts
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { StatusBadge } from './status-badge.component';
+
+describe('StatusBadge', () => {
+ let component: StatusBadge;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [StatusBadge]
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(StatusBadge);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/nifi-frontend/src/main/frontend/libs/shared/src/components/status-badge/status-badge.component.ts b/nifi-frontend/src/main/frontend/libs/shared/src/components/status-badge/status-badge.component.ts
new file mode 100644
index 000000000000..af1147b4f867
--- /dev/null
+++ b/nifi-frontend/src/main/frontend/libs/shared/src/components/status-badge/status-badge.component.ts
@@ -0,0 +1,32 @@
+/*
+ * 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, Input } from '@angular/core';
+import { StatusVariant } from '../../types';
+import { MatTooltip } from '@angular/material/tooltip';
+
+@Component({
+ selector: 'status-badge',
+ imports: [MatTooltip],
+ templateUrl: './status-badge.component.html',
+ styleUrls: ['./status-badge.component.scss']
+})
+export class StatusBadge {
+ @Input() label: string | null = null;
+ @Input() variant: StatusVariant = 'neutral';
+ @Input() message: string | null | undefined = null;
+}
diff --git a/nifi-frontend/src/main/frontend/libs/shared/src/utils/connector-permissions.utils.spec.ts b/nifi-frontend/src/main/frontend/libs/shared/src/utils/connector-permissions.utils.spec.ts
index dc8959f7f755..f4e61db64546 100644
--- a/nifi-frontend/src/main/frontend/libs/shared/src/utils/connector-permissions.utils.spec.ts
+++ b/nifi-frontend/src/main/frontend/libs/shared/src/utils/connector-permissions.utils.spec.ts
@@ -15,14 +15,15 @@
* limitations under the License.
*/
-import { ConnectorEntity } from '../types';
+import { ConnectorEntity, ConnectorState } from '../types';
import {
canReadConnector,
canModifyConnector,
canOperateConnector,
getConnectorAction,
isConnectorActionAllowed,
- getConnectorActionDisabledReason
+ getConnectorActionDisabledReason,
+ getConnectorStateVariant
} from './connector-permissions.utils';
function createMockEntity(overrides: Partial = {}): ConnectorEntity {
@@ -182,4 +183,46 @@ describe('connector-permissions.utils', () => {
expect(getConnectorActionDisabledReason(entity, 'START')).toBe('');
});
});
+
+ describe('getConnectorStateVariant', () => {
+ it('should return success for RUNNING', () => {
+ expect(getConnectorStateVariant(ConnectorState.RUNNING)).toBe('success');
+ });
+
+ it('should return neutral for STOPPED', () => {
+ expect(getConnectorStateVariant(ConnectorState.STOPPED)).toBe('neutral');
+ });
+
+ it('should return neutral for DISABLED', () => {
+ expect(getConnectorStateVariant(ConnectorState.DISABLED)).toBe('neutral');
+ });
+
+ it('should return info for STARTING', () => {
+ expect(getConnectorStateVariant(ConnectorState.STARTING)).toBe('info');
+ });
+
+ it('should return info for UPDATING', () => {
+ expect(getConnectorStateVariant(ConnectorState.UPDATING)).toBe('info');
+ });
+
+ it('should return info for STOPPING', () => {
+ expect(getConnectorStateVariant(ConnectorState.STOPPING)).toBe('info');
+ });
+
+ it('should return info for DRAINING', () => {
+ expect(getConnectorStateVariant(ConnectorState.DRAINING)).toBe('info');
+ });
+
+ it('should return info for PREPARING_FOR_UPDATE', () => {
+ expect(getConnectorStateVariant(ConnectorState.PREPARING_FOR_UPDATE)).toBe('info');
+ });
+
+ it('should return critical for UPDATE_FAILED', () => {
+ expect(getConnectorStateVariant(ConnectorState.UPDATE_FAILED)).toBe('critical');
+ });
+
+ it('should return neutral for unknown state', () => {
+ expect(getConnectorStateVariant('UNKNOWN')).toBe('neutral');
+ });
+ });
});
diff --git a/nifi-frontend/src/main/frontend/libs/shared/src/utils/connector-permissions.utils.ts b/nifi-frontend/src/main/frontend/libs/shared/src/utils/connector-permissions.utils.ts
index bffe16bdc175..d78f5f31478c 100644
--- a/nifi-frontend/src/main/frontend/libs/shared/src/utils/connector-permissions.utils.ts
+++ b/nifi-frontend/src/main/frontend/libs/shared/src/utils/connector-permissions.utils.ts
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-import { ConnectorAction, ConnectorActionName, ConnectorEntity } from '../types';
+import { ConnectorAction, ConnectorActionName, ConnectorEntity, ConnectorState, StatusVariant } from '../types';
export function canReadConnector(entity: ConnectorEntity): boolean {
return entity.permissions.canRead;
@@ -45,3 +45,23 @@ export function getConnectorActionDisabledReason(entity: ConnectorEntity, action
const action = getConnectorAction(entity, actionName);
return action?.reasonNotAllowed ?? '';
}
+
+export function getConnectorStateVariant(state: string): StatusVariant {
+ switch (state) {
+ case ConnectorState.RUNNING:
+ return 'success';
+ case ConnectorState.STOPPED:
+ case ConnectorState.DISABLED:
+ return 'neutral';
+ case ConnectorState.STARTING:
+ case ConnectorState.UPDATING:
+ case ConnectorState.STOPPING:
+ case ConnectorState.DRAINING:
+ case ConnectorState.PREPARING_FOR_UPDATE:
+ return 'info';
+ case ConnectorState.UPDATE_FAILED:
+ return 'critical';
+ default:
+ return 'neutral';
+ }
+}