mirror of https://github.com/apache/nifi.git
NIFI-12597: Introducing a common navigation bar across all pages (#8237)
* NIFI-12597: - Introducing a common navigation bar across all pages. * NIFI-12597: - Introducing navigation bar to queue listing. This closes #8237
This commit is contained in:
parent
da7c9bcddb
commit
7c09aabb4a
|
@ -15,14 +15,14 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="p-4 flex flex-col h-screen justify-between gap-y-5">
|
||||
<div class="flex justify-between">
|
||||
<div class="pb-5 flex flex-col h-screen justify-between gap-y-5">
|
||||
<header class="nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="px-5">
|
||||
<h3 class="text-xl bold access-policies-header">Access Policies</h3>
|
||||
<button class="nifi-button" [routerLink]="['/']">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="px-5 flex-1">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -21,15 +21,23 @@ import { RouterModule } from '@angular/router';
|
|||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { provideMockStore } from '@ngrx/store/testing';
|
||||
import { initialState } from '../state/access-policy/access-policy.reducer';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
describe('AccessPolicies', () => {
|
||||
let component: AccessPolicies;
|
||||
let fixture: ComponentFixture<AccessPolicies>;
|
||||
|
||||
@Component({
|
||||
selector: 'navigation',
|
||||
standalone: true,
|
||||
template: ''
|
||||
})
|
||||
class MockNavigation {}
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [AccessPolicies],
|
||||
imports: [RouterModule, RouterTestingModule],
|
||||
imports: [RouterModule, RouterTestingModule, MockNavigation],
|
||||
providers: [
|
||||
provideMockStore({
|
||||
initialState
|
||||
|
|
|
@ -26,6 +26,7 @@ import { MatDialogModule } from '@angular/material/dialog';
|
|||
import { AccessPolicyEffects } from '../state/access-policy/access-policy.effects';
|
||||
import { TenantsEffects } from '../state/tenants/tenants.effects';
|
||||
import { PolicyComponentEffects } from '../state/policy-component/policy-component.effects';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AccessPolicies],
|
||||
|
@ -35,7 +36,8 @@ import { PolicyComponentEffects } from '../state/policy-component/policy-compone
|
|||
AccessPoliciesRoutingModule,
|
||||
StoreModule.forFeature(accessPoliciesFeatureKey, reducers),
|
||||
EffectsModule.forFeature(AccessPolicyEffects, TenantsEffects, PolicyComponentEffects),
|
||||
MatDialogModule
|
||||
MatDialogModule,
|
||||
Navigation
|
||||
]
|
||||
})
|
||||
export class AccessPoliciesModule {}
|
||||
|
|
|
@ -15,14 +15,12 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="p-4 flex flex-col h-screen justify-between gap-y-5">
|
||||
<div class="flex justify-between">
|
||||
<div class="pb-5 flex flex-col h-screen justify-between gap-y-5">
|
||||
<header class="nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="px-5 flex-1 flex flex-col">
|
||||
<h3 class="text-xl bold bulletin-board-header">NiFi Bulletin Board</h3>
|
||||
<button class="nifi-button" [routerLink]="['/']">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<bulletin-board></bulletin-board>
|
||||
<bulletin-board class="flex-1"></bulletin-board>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -25,6 +25,7 @@ import { BulletinBoardEffects } from '../state/bulletin-board/bulletin-board.eff
|
|||
import { BulletinsRoutingModule } from './bulletins-routing.module';
|
||||
import { CounterListingModule } from '../../counters/ui/counter-listing/counter-listing.module';
|
||||
import { BulletinBoard } from '../ui/bulletin-board/bulletin-board.component';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [Bulletins],
|
||||
|
@ -35,7 +36,8 @@ import { BulletinBoard } from '../ui/bulletin-board/bulletin-board.component';
|
|||
StoreModule.forFeature(bulletinsFeatureKey, reducers),
|
||||
EffectsModule.forFeature(BulletinBoardEffects),
|
||||
CounterListingModule,
|
||||
BulletinBoard
|
||||
BulletinBoard,
|
||||
Navigation
|
||||
]
|
||||
})
|
||||
export class BulletinsModule {}
|
||||
|
|
|
@ -15,14 +15,12 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="p-4 flex flex-col h-screen justify-between gap-y-5">
|
||||
<div class="flex justify-between">
|
||||
<div class="pb-5 flex flex-col h-screen justify-between gap-y-5">
|
||||
<header class="nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="px-5 flex-1 flex flex-col">
|
||||
<h3 class="text-xl bold counter-header">NiFi Counters</h3>
|
||||
<button class="nifi-button" [routerLink]="['/']">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<counter-listing></counter-listing>
|
||||
<counter-listing class="flex-1"></counter-listing>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -22,15 +22,23 @@ import { initialState } from '../state/counter-listing/counter-listing.reducer';
|
|||
import { CounterListing } from '../ui/counter-listing/counter-listing.component';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
describe('Counters', () => {
|
||||
let component: Counters;
|
||||
let fixture: ComponentFixture<Counters>;
|
||||
|
||||
@Component({
|
||||
selector: 'navigation',
|
||||
standalone: true,
|
||||
template: ''
|
||||
})
|
||||
class MockNavigation {}
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Counters, CounterListing],
|
||||
imports: [RouterModule, RouterTestingModule],
|
||||
imports: [RouterModule, RouterTestingModule, MockNavigation],
|
||||
providers: [provideMockStore({ initialState })]
|
||||
});
|
||||
fixture = TestBed.createComponent(Counters);
|
||||
|
|
|
@ -25,6 +25,7 @@ import { EffectsModule } from '@ngrx/effects';
|
|||
import { CounterListingEffects } from '../state/counter-listing/counter-listing.effects';
|
||||
import { CounterListingModule } from '../ui/counter-listing/counter-listing.module';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [Counters],
|
||||
|
@ -35,7 +36,8 @@ import { MatDialogModule } from '@angular/material/dialog';
|
|||
StoreModule.forFeature(countersFeatureKey, reducers),
|
||||
EffectsModule.forFeature(CounterListingEffects),
|
||||
CounterListingModule,
|
||||
MatDialogModule
|
||||
MatDialogModule,
|
||||
Navigation
|
||||
]
|
||||
})
|
||||
export class CountersModule {}
|
||||
|
|
|
@ -18,9 +18,6 @@
|
|||
.flow-status {
|
||||
border-bottom: 1px solid #aabbc3;
|
||||
box-sizing: content-box;
|
||||
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.25);
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
font-size: 15px;
|
||||
|
||||
.fa,
|
||||
|
|
|
@ -15,151 +15,37 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<header>
|
||||
<nav class="bg-nifi-primary nifi-header">
|
||||
<div class="flex justify-between items-center h-16 pl-4">
|
||||
<div class="flex">
|
||||
<div class="h-16 w-28 mr-6 relative">
|
||||
<img ngSrc="assets/icons/nifi-logo.svg" fill priority alt="NiFi Logo" />
|
||||
</div>
|
||||
<new-canvas-item
|
||||
[type]="ComponentType.Processor"
|
||||
iconClass="icon-processor"
|
||||
iconHoverClass="icon-processor-add"></new-canvas-item>
|
||||
<new-canvas-item
|
||||
[type]="ComponentType.InputPort"
|
||||
iconClass="icon-port-in"
|
||||
iconHoverClass="icon-port-in-add"></new-canvas-item>
|
||||
<new-canvas-item
|
||||
[type]="ComponentType.OutputPort"
|
||||
iconClass="icon-port-out"
|
||||
iconHoverClass="icon-port-out-add"></new-canvas-item>
|
||||
<new-canvas-item
|
||||
[type]="ComponentType.ProcessGroup"
|
||||
iconClass="icon-group"
|
||||
iconHoverClass="icon-group-add"></new-canvas-item>
|
||||
<new-canvas-item
|
||||
[type]="ComponentType.RemoteProcessGroup"
|
||||
iconClass="icon-group-remote"
|
||||
iconHoverClass="icon-group-remote-add"></new-canvas-item>
|
||||
<new-canvas-item
|
||||
[type]="ComponentType.Funnel"
|
||||
iconClass="icon-funnel"
|
||||
iconHoverClass="icon-funnel-add"></new-canvas-item>
|
||||
<new-canvas-item
|
||||
[type]="ComponentType.Label"
|
||||
iconClass="icon-label"
|
||||
iconHoverClass="icon-label-add"></new-canvas-item>
|
||||
</div>
|
||||
<div class="flex justify-between items-center gap-x-1" *ngIf="currentUser$ | async as user">
|
||||
<div class="flex flex-col justify-between items-end gap-y-1">
|
||||
<div class="current-user">{{ user.identity }}</div>
|
||||
<a href="#" *ngIf="allowLogin(user)">log in</a>
|
||||
<a (click)="logout()" *ngIf="hasToken()">log out</a>
|
||||
</div>
|
||||
<button
|
||||
mat-button
|
||||
[matMenuTriggerFor]="globalMenu"
|
||||
class="h-16 w-16 flex items-center justify-center icon global-menu">
|
||||
<i class="fa fa-navicon"></i>
|
||||
</button>
|
||||
<mat-menu #globalMenu="matMenu" xPosition="before">
|
||||
<button mat-menu-item class="global-menu-item" [routerLink]="['/summary']">
|
||||
<i class="fa fa-fw fa-table mr-2"></i>
|
||||
Summary
|
||||
</button>
|
||||
<button
|
||||
mat-menu-item
|
||||
class="global-menu-item"
|
||||
[routerLink]="['/counters']"
|
||||
[disabled]="!user.countersPermissions.canRead">
|
||||
<i class="icon fa-fw icon-counter mr-2"></i>
|
||||
Counter
|
||||
</button>
|
||||
<button mat-menu-item class="global-menu-item" [routerLink]="['/bulletins']">
|
||||
<i class="fa fa-fw fa-sticky-note-o mr-2"></i>
|
||||
Bulletin Board
|
||||
</button>
|
||||
<mat-divider></mat-divider>
|
||||
<button
|
||||
mat-menu-item
|
||||
class="global-menu-item"
|
||||
[routerLink]="['/provenance']"
|
||||
[disabled]="!user.provenancePermissions.canRead">
|
||||
<i class="icon fa-fw icon-provenance mr-2"></i>
|
||||
Data Provenance
|
||||
</button>
|
||||
<mat-divider></mat-divider>
|
||||
<button
|
||||
mat-menu-item
|
||||
class="global-menu-item"
|
||||
[routerLink]="['/settings']"
|
||||
[disabled]="!user.controllerPermissions.canRead">
|
||||
<i class="fa fa-fw fa-wrench mr-2"></i>
|
||||
Controller Settings
|
||||
</button>
|
||||
<button mat-menu-item class="global-menu-item" [routerLink]="['/parameter-contexts']">
|
||||
<i class="fa fa-fw mr-2"></i>
|
||||
Parameter Contexts
|
||||
</button>
|
||||
<button mat-menu-item class="global-menu-item">
|
||||
<i class="fa fa-fw fa-cubes mr-2"></i>
|
||||
Cluster
|
||||
</button>
|
||||
<button mat-menu-item class="global-menu-item">
|
||||
<i class="fa fa-fw fa-history mr-2"></i>
|
||||
Flow Configuration History
|
||||
</button>
|
||||
<button mat-menu-item class="global-menu-item" (click)="viewNodeStatusHistory()">
|
||||
<i class="fa fa-fw fa-area-chart mr-2"></i>
|
||||
Node Status History
|
||||
</button>
|
||||
<button
|
||||
mat-menu-item
|
||||
class="global-menu-item"
|
||||
[disabled]="!user.systemPermissions.canRead"
|
||||
(click)="viewSystemDiagnostics()">
|
||||
<i class="fa fa-fw mr-2"></i>
|
||||
System Diagnostics
|
||||
</button>
|
||||
<ng-container *ngIf="flowConfiguration$ | async as flowConfiguration">
|
||||
<ng-container *ngIf="flowConfiguration.supportsManagedAuthorizer">
|
||||
<mat-divider></mat-divider>
|
||||
<button
|
||||
mat-menu-item
|
||||
class="global-menu-item"
|
||||
[routerLink]="['/users']"
|
||||
[disabled]="!user.tenantsPermissions.canRead">
|
||||
<i class="fa fa-fw fa-users mr-2"></i>
|
||||
Users
|
||||
</button>
|
||||
<button
|
||||
mat-menu-item
|
||||
class="global-menu-item"
|
||||
[routerLink]="['/access-policies', 'global']"
|
||||
[disabled]="
|
||||
!user.tenantsPermissions.canRead ||
|
||||
!user.policiesPermissions.canRead ||
|
||||
!user.policiesPermissions.canWrite
|
||||
">
|
||||
<i class="fa fa-fw fa-key mr-2"></i>
|
||||
Policies
|
||||
</button>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<mat-divider></mat-divider>
|
||||
<button mat-menu-item class="global-menu-item">
|
||||
<i class="fa fa-fw fa-question-circle mr-2"></i>
|
||||
Help
|
||||
</button>
|
||||
<button mat-menu-item class="global-menu-item">
|
||||
<i class="fa fa-fw fa-info-circle mr-2"></i>
|
||||
About
|
||||
</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<header class="nifi-header">
|
||||
<navigation>
|
||||
<new-canvas-item
|
||||
[type]="ComponentType.Processor"
|
||||
iconClass="icon-processor"
|
||||
iconHoverClass="icon-processor-add"></new-canvas-item>
|
||||
<new-canvas-item
|
||||
[type]="ComponentType.InputPort"
|
||||
iconClass="icon-port-in"
|
||||
iconHoverClass="icon-port-in-add"></new-canvas-item>
|
||||
<new-canvas-item
|
||||
[type]="ComponentType.OutputPort"
|
||||
iconClass="icon-port-out"
|
||||
iconHoverClass="icon-port-out-add"></new-canvas-item>
|
||||
<new-canvas-item
|
||||
[type]="ComponentType.ProcessGroup"
|
||||
iconClass="icon-group"
|
||||
iconHoverClass="icon-group-add"></new-canvas-item>
|
||||
<new-canvas-item
|
||||
[type]="ComponentType.RemoteProcessGroup"
|
||||
iconClass="icon-group-remote"
|
||||
iconHoverClass="icon-group-remote-add"></new-canvas-item>
|
||||
<new-canvas-item
|
||||
[type]="ComponentType.Funnel"
|
||||
iconClass="icon-funnel"
|
||||
iconHoverClass="icon-funnel-add"></new-canvas-item>
|
||||
<new-canvas-item
|
||||
[type]="ComponentType.Label"
|
||||
iconClass="icon-label"
|
||||
iconHoverClass="icon-label-add"></new-canvas-item>
|
||||
</navigation>
|
||||
<flow-status
|
||||
[controllerStatus]="(controllerStatus$ | async)!"
|
||||
[lastRefreshed]="(lastRefreshed$ | async)!"
|
||||
|
|
|
@ -14,50 +14,3 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.nifi-header {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
|
||||
.icon {
|
||||
font-size: 32px;
|
||||
color: #004849;
|
||||
}
|
||||
|
||||
.current-user {
|
||||
font-family: 'Roboto Slab';
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-size: 12px;
|
||||
max-width: 250px;
|
||||
text-overflow: ellipsis;
|
||||
line-height: normal;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
a {
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.global-menu {
|
||||
height: 64px;
|
||||
width: 64px;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.global-menu:hover {
|
||||
background-color: #e3e8eb;
|
||||
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
button.global-menu-item {
|
||||
.fa,
|
||||
.icon {
|
||||
text-align: start;
|
||||
color: #728e9b;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,27 +24,35 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
|
|||
import { NewCanvasItem } from './new-canvas-item/new-canvas-item.component';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { FlowStatus } from './flow-status/flow-status.component';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import {
|
||||
selectClusterSummary,
|
||||
selectControllerBulletins,
|
||||
selectControllerStatus
|
||||
} from '../../../state/flow/flow.selectors';
|
||||
import { ClusterSummary, ControllerStatus } from '../../../state/flow';
|
||||
import { Search } from './search/search.component';
|
||||
import { CdkConnectedOverlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { selectCurrentUser } from '../../../../../state/current-user/current-user.selectors';
|
||||
import * as fromUser from '../../../../../state/current-user/current-user.reducer';
|
||||
import { selectFlowConfiguration } from '../../../../../state/flow-configuration/flow-configuration.selectors';
|
||||
import * as fromFlowConfiguration from '../../../../../state/flow-configuration/flow-configuration.reducer';
|
||||
import { Component } from '@angular/core';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
|
||||
describe('HeaderComponent', () => {
|
||||
let component: HeaderComponent;
|
||||
let fixture: ComponentFixture<HeaderComponent>;
|
||||
|
||||
@Component({
|
||||
selector: 'navigation',
|
||||
standalone: true,
|
||||
template: ''
|
||||
})
|
||||
class MockNavigation {}
|
||||
|
||||
@Component({
|
||||
selector: 'flow-status',
|
||||
standalone: true,
|
||||
template: ''
|
||||
})
|
||||
class MockFlowStatus {}
|
||||
|
||||
const clusterSummary: ClusterSummary = {
|
||||
clustered: false,
|
||||
connectedToCluster: false,
|
||||
|
@ -76,17 +84,16 @@ describe('HeaderComponent', () => {
|
|||
imports: [
|
||||
HeaderComponent,
|
||||
NewCanvasItem,
|
||||
FlowStatus,
|
||||
Search,
|
||||
HttpClientTestingModule,
|
||||
MockFlowStatus,
|
||||
MatMenuModule,
|
||||
MatDividerModule,
|
||||
RouterModule,
|
||||
RouterTestingModule,
|
||||
CdkOverlayOrigin,
|
||||
CdkConnectedOverlay,
|
||||
FormsModule,
|
||||
ReactiveFormsModule
|
||||
ReactiveFormsModule,
|
||||
MockNavigation
|
||||
],
|
||||
providers: [
|
||||
provideMockStore({
|
||||
|
@ -103,14 +110,6 @@ describe('HeaderComponent', () => {
|
|||
{
|
||||
selector: selectControllerBulletins,
|
||||
value: []
|
||||
},
|
||||
{
|
||||
selector: selectCurrentUser,
|
||||
value: fromUser.initialState.user
|
||||
},
|
||||
{
|
||||
selector: selectFlowConfiguration,
|
||||
value: fromFlowConfiguration.initialState.flowConfiguration
|
||||
}
|
||||
]
|
||||
})
|
||||
|
|
|
@ -26,10 +26,6 @@ import {
|
|||
selectCurrentProcessGroupId,
|
||||
selectLastRefreshed
|
||||
} from '../../../state/flow/flow.selectors';
|
||||
import { selectCurrentUser } from '../../../../../state/current-user/current-user.selectors';
|
||||
import { CurrentUser } from '../../../../../state/current-user';
|
||||
import { AuthStorage } from '../../../../../service/auth-storage.service';
|
||||
import { AuthService } from '../../../../../service/auth.service';
|
||||
import { LoadingService } from '../../../../../service/loading.service';
|
||||
import { NewCanvasItem } from './new-canvas-item/new-canvas-item.component';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
|
@ -38,9 +34,7 @@ import { AsyncPipe, NgIf, NgOptimizedImage } from '@angular/common';
|
|||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { FlowStatus } from './flow-status/flow-status.component';
|
||||
import { getNodeStatusHistoryAndOpenDialog } from '../../../../../state/status-history/status-history.actions';
|
||||
import { getSystemDiagnosticsAndOpenDialog } from '../../../../../state/system-diagnostics/system-diagnostics.actions';
|
||||
import { selectFlowConfiguration } from '../../../../../state/flow-configuration/flow-configuration.selectors';
|
||||
import { Navigation } from '../../../../../ui/common/navigation/navigation.component';
|
||||
|
||||
@Component({
|
||||
selector: 'fd-header',
|
||||
|
@ -55,7 +49,8 @@ import { selectFlowConfiguration } from '../../../../../state/flow-configuration
|
|||
RouterLink,
|
||||
NgIf,
|
||||
FlowStatus,
|
||||
NgOptimizedImage
|
||||
NgOptimizedImage,
|
||||
Navigation
|
||||
],
|
||||
styleUrls: ['./header.component.scss']
|
||||
})
|
||||
|
@ -66,46 +61,10 @@ export class HeaderComponent {
|
|||
lastRefreshed$ = this.store.select(selectLastRefreshed);
|
||||
clusterSummary$ = this.store.select(selectClusterSummary);
|
||||
controllerBulletins$ = this.store.select(selectControllerBulletins);
|
||||
currentUser$ = this.store.select(selectCurrentUser);
|
||||
flowConfiguration$ = this.store.select(selectFlowConfiguration);
|
||||
currentProcessGroupId$ = this.store.select(selectCurrentProcessGroupId);
|
||||
|
||||
constructor(
|
||||
private store: Store<CanvasState>,
|
||||
private authStorage: AuthStorage,
|
||||
private authService: AuthService,
|
||||
public loadingService: LoadingService
|
||||
) {}
|
||||
|
||||
allowLogin(user: CurrentUser): boolean {
|
||||
return user.anonymous && location.protocol === 'https:';
|
||||
}
|
||||
|
||||
hasToken(): boolean {
|
||||
return this.authStorage.hasToken();
|
||||
}
|
||||
|
||||
logout(): void {
|
||||
this.authService.logout();
|
||||
}
|
||||
|
||||
viewNodeStatusHistory(): void {
|
||||
this.store.dispatch(
|
||||
getNodeStatusHistoryAndOpenDialog({
|
||||
request: {
|
||||
source: 'menu'
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
viewSystemDiagnostics() {
|
||||
this.store.dispatch(
|
||||
getSystemDiagnosticsAndOpenDialog({
|
||||
request: {
|
||||
nodewise: false
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,14 +15,12 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="p-4 flex flex-col h-screen justify-between gap-y-5">
|
||||
<div class="flex justify-between">
|
||||
<div class="pb-5 flex flex-col h-screen justify-between gap-y-5">
|
||||
<header class="nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="px-5 flex-1 flex flex-col">
|
||||
<h3 class="text-xl bold parameter-context-header">Parameter Contexts</h3>
|
||||
<button class="nifi-button" [routerLink]="['/']">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<parameter-context-listing></parameter-context-listing>
|
||||
<parameter-context-listing class="flex-1"></parameter-context-listing>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -23,15 +23,23 @@ import { initialState } from '../state/parameter-context-listing/parameter-conte
|
|||
import { ParameterContextListing } from '../ui/parameter-context-listing/parameter-context-listing.component';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
describe('ParameterContexts', () => {
|
||||
let component: ParameterContexts;
|
||||
let fixture: ComponentFixture<ParameterContexts>;
|
||||
|
||||
@Component({
|
||||
selector: 'navigation',
|
||||
standalone: true,
|
||||
template: ''
|
||||
})
|
||||
class MockNavigation {}
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ParameterContexts, ParameterContextListing],
|
||||
imports: [RouterModule, RouterTestingModule],
|
||||
imports: [RouterModule, RouterTestingModule, MockNavigation],
|
||||
providers: [
|
||||
provideMockStore({
|
||||
initialState
|
||||
|
|
|
@ -24,6 +24,7 @@ import { ParameterContextsRoutingModule } from './parameter-contexts-routing.mod
|
|||
import { parameterContextsFeatureKey, reducers } from '../state';
|
||||
import { ParameterContextListingEffects } from '../state/parameter-context-listing/parameter-context-listing.effects';
|
||||
import { ParameterContextListingModule } from '../ui/parameter-context-listing/parameter-context-listing.module';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ParameterContexts],
|
||||
|
@ -33,7 +34,8 @@ import { ParameterContextListingModule } from '../ui/parameter-context-listing/p
|
|||
ParameterContextsRoutingModule,
|
||||
StoreModule.forFeature(parameterContextsFeatureKey, reducers),
|
||||
EffectsModule.forFeature(ParameterContextListingEffects),
|
||||
ParameterContextListingModule
|
||||
ParameterContextListingModule,
|
||||
Navigation
|
||||
]
|
||||
})
|
||||
export class ParameterContextsModule {}
|
||||
|
|
|
@ -15,14 +15,12 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="p-4 flex flex-col h-screen justify-between gap-y-5">
|
||||
<div class="flex justify-between">
|
||||
<h3 class="text-xl bold provenance-header">Provenance</h3>
|
||||
<button class="nifi-button" [routerLink]="['/']">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="pb-5 flex flex-col h-screen justify-between gap-y-5">
|
||||
<header class="nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<h3 class="px-5 text-xl bold provenance-header">Provenance</h3>
|
||||
<div class="px-5 flex-1">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -22,15 +22,25 @@ import { provideMockStore } from '@ngrx/store/testing';
|
|||
import { RouterModule } from '@angular/router';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { initialState } from '../state/provenance-event-listing/provenance-event-listing.reducer';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
describe('Provenance', () => {
|
||||
let component: Provenance;
|
||||
let fixture: ComponentFixture<Provenance>;
|
||||
|
||||
@Component({
|
||||
selector: 'navigation',
|
||||
standalone: true,
|
||||
template: ''
|
||||
})
|
||||
class MockNavigation {}
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Provenance],
|
||||
imports: [RouterModule, RouterTestingModule],
|
||||
imports: [RouterModule, RouterTestingModule, MockNavigation],
|
||||
providers: [
|
||||
provideMockStore({
|
||||
initialState
|
||||
|
|
|
@ -25,6 +25,7 @@ import { provenanceFeatureKey, reducers } from '../state';
|
|||
import { ProvenanceEventListingEffects } from '../state/provenance-event-listing/provenance-event-listing.effects';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { LineageEffects } from '../state/lineage/lineage.effects';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [Provenance],
|
||||
|
@ -34,7 +35,8 @@ import { LineageEffects } from '../state/lineage/lineage.effects';
|
|||
MatDialogModule,
|
||||
ProvenanceRoutingModule,
|
||||
StoreModule.forFeature(provenanceFeatureKey, reducers),
|
||||
EffectsModule.forFeature(ProvenanceEventListingEffects, LineageEffects)
|
||||
EffectsModule.forFeature(ProvenanceEventListingEffects, LineageEffects),
|
||||
Navigation
|
||||
]
|
||||
})
|
||||
export class ProvenanceModule {}
|
||||
|
|
|
@ -15,13 +15,11 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="p-4 flex flex-col h-screen justify-between gap-y-5">
|
||||
<div class="flex justify-end">
|
||||
<button class="nifi-button" [routerLink]="['/']">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="pb-5 flex flex-col h-screen justify-between gap-y-5">
|
||||
<header class="nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="px-5 flex-1">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -22,15 +22,23 @@ import { provideMockStore } from '@ngrx/store/testing';
|
|||
import { RouterModule } from '@angular/router';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { initialState } from '../state/queue-listing/queue-listing.reducer';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
describe('Queue', () => {
|
||||
let component: Queue;
|
||||
let fixture: ComponentFixture<Queue>;
|
||||
|
||||
@Component({
|
||||
selector: 'navigation',
|
||||
standalone: true,
|
||||
template: ''
|
||||
})
|
||||
class MockNavigation {}
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Queue],
|
||||
imports: [RouterModule, RouterTestingModule],
|
||||
imports: [RouterModule, RouterTestingModule, MockNavigation],
|
||||
providers: [
|
||||
provideMockStore({
|
||||
initialState
|
||||
|
|
|
@ -17,17 +17,14 @@
|
|||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { StoreModule } from '@ngrx/store';
|
||||
import { EffectsModule } from '@ngrx/effects';
|
||||
import { Queue } from './queue.component';
|
||||
import { QueueRoutingModule } from './queue-routing.module';
|
||||
import { queueFeatureKey, reducers } from '../state';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { QueueListingEffects } from '../state/queue-listing/queue-listing.effects';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [Queue],
|
||||
exports: [Queue],
|
||||
imports: [CommonModule, MatDialogModule, QueueRoutingModule]
|
||||
imports: [CommonModule, MatDialogModule, QueueRoutingModule, Navigation]
|
||||
})
|
||||
export class QueueModule {}
|
||||
|
|
|
@ -16,16 +16,10 @@
|
|||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { Observable } from 'rxjs';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { NiFiCommon } from '../../../service/nifi-common.service';
|
||||
import { ParameterContextUpdateRequest, SubmitParameterContextUpdate } from '../../../state/shared';
|
||||
import {
|
||||
FlowFileSummary,
|
||||
ListingRequest,
|
||||
ListingRequestEntity,
|
||||
SubmitQueueListingRequest
|
||||
} from '../state/queue-listing';
|
||||
import { FlowFileSummary, ListingRequest, SubmitQueueListingRequest } from '../state/queue-listing';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class QueueService {
|
||||
|
|
|
@ -22,11 +22,9 @@ import { BulletinsTip } from '../../../../../ui/common/tooltips/bulletins-tip/bu
|
|||
import { ValidationErrorsTip } from '../../../../../ui/common/tooltips/validation-errors-tip/validation-errors-tip.component';
|
||||
import { NiFiCommon } from '../../../../../service/nifi-common.service';
|
||||
import { NgForOf, NgIf } from '@angular/common';
|
||||
import { ProvenanceEventSummary } from '../../../../../state/shared';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { FlowFileSummary, ListingRequest } from '../../../state/queue-listing';
|
||||
import { CurrentUser } from '../../../../../state/current-user';
|
||||
import { Flow } from '../../../../flow-designer/state/flow';
|
||||
|
||||
@Component({
|
||||
selector: 'flowfile-table',
|
||||
|
|
|
@ -15,14 +15,12 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="p-4 flex flex-col h-screen justify-between gap-y-5">
|
||||
<div class="flex justify-between">
|
||||
<div class="pb-5 flex flex-col h-screen justify-between gap-y-5">
|
||||
<header class="nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="px-5 flex-1 flex flex-col">
|
||||
<h3 class="text-xl bold settings-header">NiFi Settings</h3>
|
||||
<button class="nifi-button" [routerLink]="['/']">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex-1 flex flex-col">
|
||||
<div class="settings-tabs">
|
||||
<nav mat-tab-nav-bar color="primary" [tabPanel]="tabPanel">
|
||||
<a
|
||||
|
|
|
@ -23,15 +23,23 @@ import { MatTabsModule } from '@angular/material/tabs';
|
|||
import { RouterModule } from '@angular/router';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { initialState } from '../state/general/general.reducer';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
describe('SettingsComponent', () => {
|
||||
describe('Settings', () => {
|
||||
let component: Settings;
|
||||
let fixture: ComponentFixture<Settings>;
|
||||
|
||||
@Component({
|
||||
selector: 'navigation',
|
||||
standalone: true,
|
||||
template: ''
|
||||
})
|
||||
class MockNavigation {}
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Settings],
|
||||
imports: [MatTabsModule, RouterModule, RouterTestingModule],
|
||||
imports: [MatTabsModule, RouterModule, RouterTestingModule, MockNavigation],
|
||||
providers: [
|
||||
provideMockStore({
|
||||
initialState
|
||||
|
|
|
@ -34,6 +34,7 @@ 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';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [Settings],
|
||||
|
@ -55,7 +56,8 @@ import { FlowAnalysisRulesEffects } from '../state/flow-analysis-rules/flow-anal
|
|||
FlowAnalysisRulesEffects,
|
||||
RegistryClientsEffects
|
||||
),
|
||||
MatTabsModule
|
||||
MatTabsModule,
|
||||
Navigation
|
||||
]
|
||||
})
|
||||
export class SettingsModule {}
|
||||
|
|
|
@ -15,14 +15,12 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="p-4 flex flex-col h-screen justify-between gap-y-5">
|
||||
<div class="flex justify-between">
|
||||
<div class="pb-5 flex flex-col h-screen justify-between gap-y-5">
|
||||
<header class="nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="px-5 flex-1 flex flex-col">
|
||||
<h3 class="text-xl bold summary-header">NiFi Summary</h3>
|
||||
<button class="nifi-button" [routerLink]="['/']">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex-1 flex flex-col">
|
||||
<div class="summary-tabs">
|
||||
<nav mat-tab-nav-bar color="primary" [tabPanel]="tabPanel">
|
||||
<a
|
||||
|
|
|
@ -22,15 +22,23 @@ import { RouterTestingModule } from '@angular/router/testing';
|
|||
import { provideMockStore } from '@ngrx/store/testing';
|
||||
import { Summary } from './summary.component';
|
||||
import { initialState } from '../state/summary-listing/summary-listing.reducer';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
describe('Summary', () => {
|
||||
let component: Summary;
|
||||
let fixture: ComponentFixture<Summary>;
|
||||
|
||||
@Component({
|
||||
selector: 'navigation',
|
||||
standalone: true,
|
||||
template: ''
|
||||
})
|
||||
class MockNavigation {}
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Summary],
|
||||
imports: [MatTabsModule, RouterModule, RouterTestingModule],
|
||||
imports: [MatTabsModule, RouterModule, RouterTestingModule, MockNavigation],
|
||||
providers: [
|
||||
provideMockStore({
|
||||
initialState
|
||||
|
|
|
@ -31,6 +31,7 @@ import { OutputPortStatusListingModule } from '../ui/output-port-status-listing/
|
|||
import { InputPortStatusListingModule } from '../ui/input-port-status-listing/input-port-status-listing.module';
|
||||
import { SummaryListingEffects } from '../state/summary-listing/summary-listing.effects';
|
||||
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [Summary],
|
||||
|
@ -48,7 +49,8 @@ import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
|||
InputPortStatusListingModule,
|
||||
StoreModule.forFeature(summaryFeatureKey, reducers),
|
||||
EffectsModule.forFeature(SummaryListingEffects),
|
||||
NgxSkeletonLoaderModule
|
||||
NgxSkeletonLoaderModule,
|
||||
Navigation
|
||||
]
|
||||
})
|
||||
export class SummaryModule {}
|
||||
|
|
|
@ -15,14 +15,12 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="p-4 flex flex-col h-screen justify-between gap-y-5">
|
||||
<div class="flex justify-between">
|
||||
<div class="pb-5 flex flex-col h-screen justify-between gap-y-5">
|
||||
<header class="nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="px-5 flex-1 flex flex-col">
|
||||
<h3 class="text-xl bold user-header">NiFi Users</h3>
|
||||
<button class="nifi-button" [routerLink]="['/']">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<user-listing></user-listing>
|
||||
<user-listing class="flex-1"></user-listing>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -22,15 +22,23 @@ import { RouterModule } from '@angular/router';
|
|||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { UserListing } from '../ui/user-listing/user-listing.component';
|
||||
import { initialState } from '../state/user-listing/user-listing.reducer';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
describe('Users', () => {
|
||||
let component: Users;
|
||||
let fixture: ComponentFixture<Users>;
|
||||
|
||||
@Component({
|
||||
selector: 'navigation',
|
||||
standalone: true,
|
||||
template: ''
|
||||
})
|
||||
class MockNavigation {}
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Users, UserListing],
|
||||
imports: [RouterModule, RouterTestingModule],
|
||||
imports: [RouterModule, RouterTestingModule, MockNavigation],
|
||||
providers: [provideMockStore({ initialState })]
|
||||
});
|
||||
fixture = TestBed.createComponent(Users);
|
||||
|
|
|
@ -25,6 +25,7 @@ import { EffectsModule } from '@ngrx/effects';
|
|||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { UserListingEffects } from '../state/user-listing/user-listing.effects';
|
||||
import { UserListingModule } from '../ui/user-listing/user-listing.module';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [Users],
|
||||
|
@ -35,7 +36,8 @@ import { UserListingModule } from '../ui/user-listing/user-listing.module';
|
|||
StoreModule.forFeature(usersFeatureKey, reducers),
|
||||
EffectsModule.forFeature(UserListingEffects),
|
||||
MatDialogModule,
|
||||
UserListingModule
|
||||
UserListingModule,
|
||||
Navigation
|
||||
]
|
||||
})
|
||||
export class UsersModule {}
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<nav class="bg-nifi-primary nifi-navigation">
|
||||
<div class="flex justify-between items-center h-16 pl-4">
|
||||
<div class="flex">
|
||||
<div class="h-16 w-28 mr-6 relative">
|
||||
<img
|
||||
ngSrc="assets/icons/nifi-logo.svg"
|
||||
fill
|
||||
priority
|
||||
alt="NiFi Logo"
|
||||
class="pointer"
|
||||
[routerLink]="['/']" />
|
||||
</div>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
<div class="flex justify-between items-center gap-x-1" *ngIf="currentUser$ | async as user">
|
||||
<div class="flex flex-col justify-between items-end gap-y-1">
|
||||
<div class="current-user">{{ user.identity }}</div>
|
||||
<a href="#" *ngIf="allowLogin(user)">log in</a>
|
||||
<a (click)="logout()" *ngIf="hasToken()">log out</a>
|
||||
</div>
|
||||
<button
|
||||
mat-button
|
||||
[matMenuTriggerFor]="globalMenu"
|
||||
class="h-16 w-16 flex items-center justify-center icon global-menu">
|
||||
<i class="fa fa-navicon"></i>
|
||||
</button>
|
||||
<mat-menu #globalMenu="matMenu" xPosition="before">
|
||||
<button mat-menu-item class="global-menu-item" [routerLink]="['/']">
|
||||
<i class="icon icon-drop mr-2"></i>
|
||||
Canvas
|
||||
</button>
|
||||
<mat-divider></mat-divider>
|
||||
<button mat-menu-item class="global-menu-item" [routerLink]="['/summary']">
|
||||
<i class="fa fa-fw fa-table mr-2"></i>
|
||||
Summary
|
||||
</button>
|
||||
<button
|
||||
mat-menu-item
|
||||
class="global-menu-item"
|
||||
[routerLink]="['/counters']"
|
||||
[disabled]="!user.countersPermissions.canRead">
|
||||
<i class="icon fa-fw icon-counter mr-2"></i>
|
||||
Counter
|
||||
</button>
|
||||
<button mat-menu-item class="global-menu-item" [routerLink]="['/bulletins']">
|
||||
<i class="fa fa-fw fa-sticky-note-o mr-2"></i>
|
||||
Bulletin Board
|
||||
</button>
|
||||
<mat-divider></mat-divider>
|
||||
<button
|
||||
mat-menu-item
|
||||
class="global-menu-item"
|
||||
[routerLink]="['/provenance']"
|
||||
[disabled]="!user.provenancePermissions.canRead">
|
||||
<i class="icon fa-fw icon-provenance mr-2"></i>
|
||||
Data Provenance
|
||||
</button>
|
||||
<mat-divider></mat-divider>
|
||||
<button
|
||||
mat-menu-item
|
||||
class="global-menu-item"
|
||||
[routerLink]="['/settings']"
|
||||
[disabled]="!user.controllerPermissions.canRead">
|
||||
<i class="fa fa-fw fa-wrench mr-2"></i>
|
||||
Controller Settings
|
||||
</button>
|
||||
<button mat-menu-item class="global-menu-item" [routerLink]="['/parameter-contexts']">
|
||||
<i class="fa fa-fw mr-2"></i>
|
||||
Parameter Contexts
|
||||
</button>
|
||||
<button mat-menu-item class="global-menu-item">
|
||||
<i class="fa fa-fw fa-cubes mr-2"></i>
|
||||
Cluster
|
||||
</button>
|
||||
<button mat-menu-item class="global-menu-item">
|
||||
<i class="fa fa-fw fa-history mr-2"></i>
|
||||
Flow Configuration History
|
||||
</button>
|
||||
<button mat-menu-item class="global-menu-item" (click)="viewNodeStatusHistory()">
|
||||
<i class="fa fa-fw fa-area-chart mr-2"></i>
|
||||
Node Status History
|
||||
</button>
|
||||
<button
|
||||
mat-menu-item
|
||||
class="global-menu-item"
|
||||
[disabled]="!user.systemPermissions.canRead"
|
||||
(click)="viewSystemDiagnostics()">
|
||||
<i class="fa fa-fw mr-2"></i>
|
||||
System Diagnostics
|
||||
</button>
|
||||
<ng-container *ngIf="flowConfiguration$ | async as flowConfiguration">
|
||||
<ng-container *ngIf="flowConfiguration.supportsManagedAuthorizer">
|
||||
<mat-divider></mat-divider>
|
||||
<button
|
||||
mat-menu-item
|
||||
class="global-menu-item"
|
||||
[routerLink]="['/users']"
|
||||
[disabled]="!user.tenantsPermissions.canRead">
|
||||
<i class="fa fa-fw fa-users mr-2"></i>
|
||||
Users
|
||||
</button>
|
||||
<button
|
||||
mat-menu-item
|
||||
class="global-menu-item"
|
||||
[routerLink]="['/access-policies', 'global']"
|
||||
[disabled]="
|
||||
!user.tenantsPermissions.canRead ||
|
||||
!user.policiesPermissions.canRead ||
|
||||
!user.policiesPermissions.canWrite
|
||||
">
|
||||
<i class="fa fa-fw fa-key mr-2"></i>
|
||||
Policies
|
||||
</button>
|
||||
<mat-divider></mat-divider>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<button mat-menu-item class="global-menu-item">
|
||||
<i class="fa fa-fw fa-question-circle mr-2"></i>
|
||||
Help
|
||||
</button>
|
||||
<button mat-menu-item class="global-menu-item">
|
||||
<i class="fa fa-fw fa-info-circle mr-2"></i>
|
||||
About
|
||||
</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.nifi-navigation {
|
||||
.icon {
|
||||
font-size: 32px;
|
||||
color: #004849;
|
||||
}
|
||||
|
||||
.current-user {
|
||||
font-family: 'Roboto Slab';
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-size: 12px;
|
||||
max-width: 250px;
|
||||
text-overflow: ellipsis;
|
||||
line-height: normal;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
a {
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.global-menu {
|
||||
height: 64px;
|
||||
width: 64px;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.global-menu:hover {
|
||||
background-color: #e3e8eb;
|
||||
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
button.global-menu-item {
|
||||
.fa,
|
||||
.icon {
|
||||
text-align: start;
|
||||
color: #728e9b;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 { Navigation } from './navigation.component';
|
||||
import { provideMockStore } from '@ngrx/store/testing';
|
||||
import { initialState } from '../../../state/current-user/current-user.reducer';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
|
||||
describe('Navigation', () => {
|
||||
let component: Navigation;
|
||||
let fixture: ComponentFixture<Navigation>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [Navigation, HttpClientTestingModule, RouterTestingModule],
|
||||
providers: [
|
||||
provideMockStore({
|
||||
initialState
|
||||
})
|
||||
]
|
||||
});
|
||||
fixture = TestBed.createComponent(Navigation);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* 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 } from '@angular/core';
|
||||
import { AsyncPipe, NgIf, NgOptimizedImage } from '@angular/common';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { getNodeStatusHistoryAndOpenDialog } from '../../../state/status-history/status-history.actions';
|
||||
import { getSystemDiagnosticsAndOpenDialog } from '../../../state/system-diagnostics/system-diagnostics.actions';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AuthStorage } from '../../../service/auth-storage.service';
|
||||
import { AuthService } from '../../../service/auth.service';
|
||||
import { CurrentUser } from '../../../state/current-user';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { selectCurrentUser } from '../../../state/current-user/current-user.selectors';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { NiFiState } from '../../../state';
|
||||
import { selectFlowConfiguration } from '../../../state/flow-configuration/flow-configuration.selectors';
|
||||
|
||||
@Component({
|
||||
selector: 'navigation',
|
||||
standalone: true,
|
||||
imports: [NgOptimizedImage, AsyncPipe, MatDividerModule, MatMenuModule, NgIf, RouterLink, MatButtonModule],
|
||||
templateUrl: './navigation.component.html',
|
||||
styleUrls: ['./navigation.component.scss']
|
||||
})
|
||||
export class Navigation {
|
||||
currentUser$ = this.store.select(selectCurrentUser);
|
||||
flowConfiguration$ = this.store.select(selectFlowConfiguration);
|
||||
|
||||
constructor(
|
||||
private store: Store<NiFiState>,
|
||||
private authStorage: AuthStorage,
|
||||
private authService: AuthService
|
||||
) {}
|
||||
|
||||
allowLogin(user: CurrentUser): boolean {
|
||||
return user.anonymous && location.protocol === 'https:';
|
||||
}
|
||||
|
||||
hasToken(): boolean {
|
||||
return this.authStorage.hasToken();
|
||||
}
|
||||
|
||||
logout(): void {
|
||||
this.authService.logout();
|
||||
}
|
||||
|
||||
viewNodeStatusHistory(): void {
|
||||
this.store.dispatch(
|
||||
getNodeStatusHistoryAndOpenDialog({
|
||||
request: {
|
||||
source: 'menu'
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
viewSystemDiagnostics() {
|
||||
this.store.dispatch(
|
||||
getSystemDiagnosticsAndOpenDialog({
|
||||
request: {
|
||||
nodewise: false
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
|
@ -248,6 +248,11 @@ a:hover {
|
|||
text-decoration-color: #004849;
|
||||
}
|
||||
|
||||
header.nifi-header {
|
||||
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.25);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/*
|
||||
Icon for styling mat-icon in forms throughout the application.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue