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