Create component for EI Producers list
Change-Id: I9e39a731193f1253b2b2a1fc3b5e41fd524c0a3b
Signed-off-by: elinuxhenrik <henrik.b.andersson@est.tech>
Issue-ID: NONRTRIC-455
diff --git a/webapp-frontend/src/app/app.module.ts b/webapp-frontend/src/app/app.module.ts
index be4e434..20fe471 100644
--- a/webapp-frontend/src/app/app.module.ts
+++ b/webapp-frontend/src/app/app.module.ts
@@ -68,6 +68,7 @@
import { HttpMockRequestInterceptor } from './interceptor.mock';
import { environment } from 'src/environments/environment';
import { HttpRequestInterceptor } from './interceptor';
+import { ProducersListComponent } from './ei-coordinator/producers-list/producers-list.component';
export const isMock = environment.mock;
@@ -82,6 +83,7 @@
MainComponent,
NodeModulesComponent,
SidenavListComponent,
+ ProducersListComponent,
],
imports: [
AppRoutingModule,
@@ -144,6 +146,7 @@
providers: [
CookieService,
ErrorDialogService,
+ ProducersListComponent,
UiService,
{
provide: HTTP_INTERCEPTORS,
diff --git a/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.html b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.html
index fc560dd..57a87f9 100644
--- a/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.html
+++ b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.html
@@ -29,57 +29,7 @@
<br>
<h4>Producers</h4>
-
-<div class="table-container">
- <mat-table id="producersTable" [dataSource]="producersDataSource" fixedLayout
- matSort (matSortChange)="sortProducers($event)"
- class="ei-coordinator-table mat-elevation-z8">
- <ng-container matColumnDef="id">
- <mat-header-cell *matHeaderCellDef mat-sort-header>
- <div (click)="stopSort($event)">
- <form style="display: flex" [formGroup]="producersFormControl">
- <mat-form-field>
- <input id="producerIdFilter" matInput formControlName="ei_producer_id">
- <mat-placeholder>Producer ID</mat-placeholder>
- </mat-form-field>
- </form>
- </div>
- </mat-header-cell>
- <mat-cell *matCellDef="let eiProducer"> {{eiProducer.ei_producer_id}} </mat-cell>
- </ng-container>
-
- <ng-container matColumnDef="types">
- <mat-header-cell *matHeaderCellDef mat-sort-header>
- <div (click)="stopSort($event)">
- <form style="display: flex" [formGroup]="producersFormControl">
- <mat-form-field>
- <input id="producerTypesFilter" matInput formControlName="ei_producer_types">
- <mat-placeholder>Producer types</mat-placeholder>
- </mat-form-field>
- </form>
- </div>
- </mat-header-cell>
- <mat-cell *matCellDef="let eiProducer"> {{this.getProducerTypes(eiProducer)}} </mat-cell>
- </ng-container>
-
- <ng-container matColumnDef="status">
- <mat-header-cell *matHeaderCellDef mat-sort-header>
- <div (click)="stopSort($event)">
- <form style="display: flex" [formGroup]="producersFormControl">
- <mat-form-field>
- <input id="producerStatusFilter" matInput formControlName="status">
- <mat-placeholder>Producer status</mat-placeholder>
- </mat-form-field>
- </form>
- </div>
- </mat-header-cell>
- <mat-cell *matCellDef="let eiProducer"> {{this.getProducerStatus(eiProducer)}} </mat-cell>
- </ng-container>
-
- <mat-header-row *matHeaderRowDef="['id', 'types', 'status']"></mat-header-row>
- <mat-row *matRowDef="let row; columns: ['id', 'types', 'status'];"></mat-row>
- </mat-table>
-</div>
+ <nrcp-producers-list></nrcp-producers-list>
<br>
<h4>Jobs</h4>
diff --git a/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.spec.ts b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.spec.ts
index d516b14..5cda8e5 100644
--- a/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.spec.ts
+++ b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.spec.ts
@@ -22,37 +22,28 @@
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
import { HarnessLoader } from '@angular/cdk/testing';
+import { MatButtonModule } from '@angular/material/button';
+import { MatButtonHarness } from '@angular/material/button/testing';
import { MatIconModule } from '@angular/material/icon';
import { MatInputHarness } from '@angular/material/input/testing'
import { MatTableModule } from '@angular/material/table';
import { MatTableHarness } from '@angular/material/table/testing';
-import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
+import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { of } from 'rxjs';
import { EICoordinatorComponent } from './ei-coordinator.component';
import { EIJobDataSource } from './ei-job.datasource';
-import { EIProducerDataSource } from './ei-producer.datasource';
import { UiService } from '../services/ui/ui.service';
-import { EIJob, EIProducer } from '../interfaces/ei.types';
+import { EIJob } from '../interfaces/ei.types';
+import { ProducersListComponent } from './producers-list/producers-list.component';
describe('EICoordinatorComponent', () => {
let component: EICoordinatorComponent;
let fixture: ComponentFixture<EICoordinatorComponent>;
let loader: HarnessLoader;
- let producerDataSourceSpy: jasmine.SpyObj<EIProducerDataSource>;
+ let producersListSpy: jasmine.SpyObj<ProducersListComponent>;
let jobDataSourceSpy: jasmine.SpyObj<EIJobDataSource>;
- const producer1 = {
- ei_producer_id: 'producer1',
- ei_producer_types: [ 'type1', 'type2' ],
- status: 'ENABLED'
- } as EIProducer;
- const producer2 = {
- ei_producer_id: 'producer2',
- ei_producer_types: [ 'type2', 'type3' ],
- status: 'DISABLED'
- } as EIProducer;
-
const job1 = {
ei_job_identity: 'job1',
ei_type_identity: 'type1',
@@ -67,14 +58,14 @@
} as EIJob;
beforeEach(async () => {
- producerDataSourceSpy = jasmine.createSpyObj('EIProducerDataSource', [ 'loadProducers', 'eiProducers', 'eiProducersSubject' ]);
+ producersListSpy = jasmine.createSpyObj('producersListSpy', ['refresh']);
jobDataSourceSpy = jasmine.createSpyObj('EIJobDataSource', [ 'loadJobs', 'eiJobs', 'eiJobsSubject' ]);
-
- producerDataSourceSpy.eiProducersSubject.and.returnValue(of({ producers: [producer1, producer2] }));
- jobDataSourceSpy.eiJobsSubject.and.returnValue(of({ jobs: [job1, job2] }));
+
+ jobDataSourceSpy.eiJobsSubject.and.returnValue(of([ job1, job2 ]));
await TestBed.configureTestingModule({
imports: [
+ MatButtonModule,
MatIconModule,
MatTableModule,
BrowserAnimationsModule,
@@ -87,13 +78,13 @@
EICoordinatorComponent
],
providers: [
+ { provide: ProducersListComponent, useValue: producersListSpy },
{ provide: EIJobDataSource, useValue: jobDataSourceSpy },
- { provide: EIProducerDataSource, useValue: producerDataSourceSpy },
UiService,
FormBuilder,
]
})
- .compileComponents();
+ .compileComponents();
fixture = TestBed.createComponent(EICoordinatorComponent);
component = fixture.componentInstance;
@@ -106,26 +97,23 @@
});
describe('#content', () => {
- it('should contain refresh button with correct icon', () => {
- const button = fixture.debugElement.nativeElement.querySelector('#refreshButton');
- expect(button).toBeTruthy();
- expect(button.innerHTML).toContain('refresh');
+ it('should contain refresh button with coorect icon', async () => {
+ let refreshButton = await loader.getHarness(MatButtonHarness.with({ selector: '#refreshButton' }));
+ expect(refreshButton).toBeTruthy();
+ expect(await refreshButton.getText()).toEqual('refresh');
});
- it('should contain producers table with correct columns', async () => {
- let producersTable = await loader.getHarness(MatTableHarness.with({selector: '#producersTable'}));
- let headerRow = (await producersTable.getHeaderRows())[0];
- let headers = await headerRow.getCellTextByColumnName();
-
- expect(headers).toEqual({id: 'Producer ID', types: 'Producer types', status: 'Producer status'});
+ it('should contain producers table', async () => {
+ const producersTableComponent = fixture.debugElement.nativeElement.querySelector('nrcp-producers-list');
+ expect(producersTableComponent).toBeTruthy();
});
it('should contain jobs table with correct columns', async () => {
- let producersTable = await loader.getHarness(MatTableHarness.with({selector: '#jobsTable'}));
+ let producersTable = await loader.getHarness(MatTableHarness.with({ selector: '#jobsTable' }));
let headerRow = (await producersTable.getHeaderRows())[0];
let headers = await headerRow.getCellTextByColumnName();
- expect(headers).toEqual({id: 'Job ID', typeId: 'Type ID', owner: 'Owner', targetUri: 'Target URI'});
+ expect(headers).toEqual({ id: 'Job ID', typeId: 'Type ID', owner: 'Owner', targetUri: 'Target URI' });
});
it('should set correct dark mode from UIService', () => {
@@ -137,95 +125,19 @@
expect(component.darkMode).toBeFalsy();
});
- });
- describe('#producersTable', () => {
- const expectedProducer1Row = { id: 'producer1', types: 'type1,type2', status: 'ENABLED' };
- beforeEach(() => {
- const producers: EIProducer[] =[ producer1, producer2 ];
- producerDataSourceSpy.eiProducersSubject.and.returnValue(of(producers));
- });
+ it('should refresh tables', async () => {
+ let refreshButton = await loader.getHarness(MatButtonHarness.with({ selector: '#refreshButton' }));
+ await refreshButton.click();
- it('should contain data after initialization', async () => {
- component.ngOnInit();
- const expectedProducerRows = [
- expectedProducer1Row,
- {id: 'producer2', types: 'type2,type3', status: 'DISABLED'}
- ];
- let producersTable = await loader.getHarness(MatTableHarness.with({selector: '#producersTable'}));
- let producerRows = await producersTable.getRows();
- expect(producerRows.length).toEqual(2);
- producerRows.forEach(row => {
- row.getCellTextByColumnName().then(values => {
- expect(expectedProducerRows).toContain(jasmine.objectContaining(values));
- });
- });
- });
-
- describe('should display default values for non required properties', () => {
- it('producer defaults', async () => {
- const producerMissingProperties = {
- ei_producer_id: 'producer1'
- } as EIProducer;
- const producers: EIProducer[] =[ producerMissingProperties ];
- producerDataSourceSpy.eiProducersSubject.and.returnValue(of(producers));
- component.ngOnInit();
-
- const expectedProducerRow = { id: 'producer1', types: '< No types >', status: '< No status >' };
- let producersTable = await loader.getHarness(MatTableHarness.with({selector: '#producersTable'}));
- let producerRows = await producersTable.getRows();
- expect(await producerRows[0].getCellTextByColumnName()).toEqual(expectedProducerRow);
- });
-
- it('job defaults', async () => {
- const jobMissingProperties = {
- ei_job_identity: 'job1',
- target_uri: 'http://one'
- } as EIJob;
- const jobs: EIJob[] =[ jobMissingProperties ];
- jobDataSourceSpy.eiJobsSubject.and.returnValue(of(jobs));
- component.ngOnInit();
-
- const expectedJobRow = { id: 'job1', typeId: '< No type >', owner: '< No owner >', targetUri: 'http://one' };
- let jobsTable = await loader.getHarness(MatTableHarness.with({selector: '#jobsTable'}));
- let jobRows = await jobsTable.getRows();
- expect(await jobRows[0].getCellTextByColumnName()).toEqual(expectedJobRow);
- });
- });
-
- it('filtering', async () => {
- component.ngOnInit();
- let producersTable = await loader.getHarness(MatTableHarness.with({selector: '#producersTable'}));
-
- let idFilterInput = await loader.getHarness(MatInputHarness.with({selector: '#producerIdFilter'}));
- await idFilterInput.setValue("1");
- let producerRows = await producersTable.getRows();
- expect(producerRows.length).toEqual(1);
- expect(await producerRows[0].getCellTextByColumnName()).toEqual(expectedProducer1Row);
-
- idFilterInput.setValue('');
- let typesFilterInput = await loader.getHarness(MatInputHarness.with({selector: '#producerTypesFilter'}));
- await typesFilterInput.setValue("1");
- producerRows = await producersTable.getRows();
- expect(producerRows.length).toEqual(1);
- expect(await producerRows[0].getCellTextByColumnName()).toEqual(expectedProducer1Row);
- await typesFilterInput.setValue("2");
- producerRows = await producersTable.getRows();
- expect(producerRows.length).toEqual(2);
-
- typesFilterInput.setValue('');
- let statusFilterInput = await loader.getHarness(MatInputHarness.with({selector: '#producerStatusFilter'}));
- await statusFilterInput.setValue("enabled");
- producerRows = await producersTable.getRows();
- expect(producerRows.length).toEqual(1);
- expect(await producerRows[0].getCellTextByColumnName()).toEqual(expectedProducer1Row);
+ expect(producersListSpy.refresh).toHaveBeenCalled();
});
});
describe('#jobsTable', () => {
const expectedJob1Row = { id: 'job1', typeId: 'type1', owner: 'owner1', targetUri: 'http://one' };
beforeEach(() => {
- const jobs: EIJob[] =[ job1, job2 ];
+ const jobs: EIJob[] = [ job1, job2 ];
jobDataSourceSpy.eiJobsSubject.and.returnValue(of(jobs));
});
@@ -235,7 +147,7 @@
expectedJob1Row,
{ id: 'job2', typeId: 'type2', owner: 'owner2', targetUri: 'http://two' }
];
- let jobsTable = await loader.getHarness(MatTableHarness.with({selector: '#jobsTable'}));
+ let jobsTable = await loader.getHarness(MatTableHarness.with({ selector: '#jobsTable' }));
let jobRows = await jobsTable.getRows();
expect(jobRows.length).toEqual(2);
jobRows.forEach(row => {
@@ -245,31 +157,46 @@
});
});
+ it('job defaults', async () => {
+ const jobMissingProperties = {
+ ei_job_identity: 'job1',
+ target_uri: 'http://one'
+ } as EIJob;
+ const jobs: EIJob[] = [jobMissingProperties];
+ jobDataSourceSpy.eiJobsSubject.and.returnValue(of(jobs));
+ component.ngOnInit();
+
+ const expectedJobRow = { id: 'job1', typeId: '< No type >', owner: '< No owner >', targetUri: 'http://one' };
+ let jobsTable = await loader.getHarness(MatTableHarness.with({ selector: '#jobsTable' }));
+ let jobRows = await jobsTable.getRows();
+ expect(await jobRows[0].getCellTextByColumnName()).toEqual(expectedJobRow);
+ });
+
it('filtering', async () => {
component.ngOnInit();
- let jobsTable = await loader.getHarness(MatTableHarness.with({selector: '#jobsTable'}));
+ let jobsTable = await loader.getHarness(MatTableHarness.with({ selector: '#jobsTable' }));
- let idFilterInput = await loader.getHarness(MatInputHarness.with({selector: '#jobIdFilter'}));
+ let idFilterInput = await loader.getHarness(MatInputHarness.with({ selector: '#jobIdFilter' }));
await idFilterInput.setValue("1");
let jobRows = await jobsTable.getRows();
expect(jobRows.length).toEqual(1);
expect(await jobRows[0].getCellTextByColumnName()).toEqual(expectedJob1Row);
idFilterInput.setValue('');
- let typeIdFilterInput = await loader.getHarness(MatInputHarness.with({selector: '#jobTypeIdFilter'}));
+ let typeIdFilterInput = await loader.getHarness(MatInputHarness.with({ selector: '#jobTypeIdFilter' }));
await typeIdFilterInput.setValue("1");
jobRows = await jobsTable.getRows();
expect(jobRows.length).toEqual(1);
typeIdFilterInput.setValue('');
- let ownerFilterInput = await loader.getHarness(MatInputHarness.with({selector: '#jobOwnerFilter'}));
+ let ownerFilterInput = await loader.getHarness(MatInputHarness.with({ selector: '#jobOwnerFilter' }));
await ownerFilterInput.setValue("1");
jobRows = await jobsTable.getRows();
expect(jobRows.length).toEqual(1);
expect(await jobRows[0].getCellTextByColumnName()).toEqual(expectedJob1Row);
ownerFilterInput.setValue('');
- let targetUriFilterInput = await loader.getHarness(MatInputHarness.with({selector: '#jobTargetUriFilter'}));
+ let targetUriFilterInput = await loader.getHarness(MatInputHarness.with({ selector: '#jobTargetUriFilter' }));
await targetUriFilterInput.setValue("one");
jobRows = await jobsTable.getRows();
expect(jobRows.length).toEqual(1);
diff --git a/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.ts b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.ts
index 9698f55..79b7129 100644
--- a/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.ts
+++ b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.ts
@@ -22,11 +22,10 @@
import { FormBuilder, FormGroup, AbstractControl } from '@angular/forms';
import { MatTableDataSource } from '@angular/material/table';
-import { EIJob, EIProducer } from '../interfaces/ei.types';
+import { EIJob } from '../interfaces/ei.types';
import { EIJobDataSource } from './ei-job.datasource';
-import { EIProducerDataSource } from './ei-producer.datasource';
import { UiService } from '../services/ui/ui.service';
-import { Observable } from 'rxjs/Observable';
+import { ProducersListComponent } from './producers-list/producers-list.component';
@Component({
selector: 'nrcp-ei-coordinator',
@@ -36,17 +35,14 @@
export class EICoordinatorComponent implements OnInit {
darkMode: boolean;
- searchString: string;
formGroup: FormGroup;
jobsDataSource: MatTableDataSource<EIJob> = new MatTableDataSource<EIJob>();
- producersDataSource: MatTableDataSource<EIProducer>= new MatTableDataSource<EIProducer>();
readonly jobsFormControl: AbstractControl;
- readonly producersFormControl: AbstractControl;
constructor(
+ private producersList: ProducersListComponent,
private eiJobsDataSource: EIJobDataSource,
- private eiProducersDataSource: EIProducerDataSource,
private ui: UiService,
private formBuilder: FormBuilder) {
this.formGroup = formBuilder.group({ filter: [""] });
@@ -57,32 +53,19 @@
owner: '',
targetUri:''
});
- this.producersFormControl = formBuilder.group({
- ei_producer_id: '',
- ei_producer_types: '',
- status: ''
- });
}
ngOnInit() {
this.eiJobsDataSource.loadJobs();
- this.eiProducersDataSource.loadProducers();
this.eiJobsDataSource.eiJobsSubject().subscribe((data) => {
this.jobsDataSource.data = data;
});
- this.eiProducersDataSource.eiProducersSubject().subscribe((data) => {
- this.producersDataSource.data = data;
- });
this.jobsFormControl.valueChanges.subscribe(value => {
const filter = {...value, id: value.id.trim().toLowerCase()} as string;
this.jobsDataSource.filter = filter;
});
- this.producersFormControl.valueChanges.subscribe(value => {
- const filter = {...value, ei_producer_id: value.ei_producer_id.trim().toLowerCase()} as string;
- this.producersDataSource.filter = filter;
- });
this.jobsDataSource.filterPredicate = ((data: EIJob, filter) => {
return this.isDataIncluding(data.ei_job_identity, filter.id)
@@ -91,12 +74,6 @@
&& this.isDataIncluding(data.ei_type_identity, filter.typeId);
}) as (data: EIJob, filter: any) => boolean;
- this.producersDataSource.filterPredicate = ((data, filter) => {
- return this.isDataIncluding(data.ei_producer_id, filter.ei_producer_id)
- && this.isDataIncluding(data.ei_producer_types.join(','), filter.ei_producer_types)
- && this.isDataIncluding(data.status, filter.status);
- }) as (data: EIProducer, filter: any) => boolean;
-
this.ui.darkModeState.subscribe((isDark) => {
this.darkMode = isDark;
});
@@ -117,20 +94,6 @@
this.jobsDataSource.data = data;
}
- sortProducers(sort: Sort){
- const data = this.producersDataSource.data
- data.sort((a: EIProducer, b: EIProducer) => {
- const isAsc = sort.direction === 'asc';
- switch (sort.active) {
- case 'id': return this.compare(a.ei_producer_id, b.ei_producer_id, isAsc);
- case 'types': return this.compare(a.ei_producer_types, b.ei_producer_types, isAsc);
- case 'status': return this.compare(a.status, b.status, isAsc);
- default: return 0;
- }
- });
- this.producersDataSource.data = data;
- }
-
compare(a: any, b: any, isAsc: boolean) {
return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}
@@ -156,30 +119,12 @@
}
return '< No owner >';
}
-
- getProducerTypes(eiProducer: EIProducer): string[] {
- if (eiProducer.ei_producer_types) {
- return eiProducer.ei_producer_types;
- }
- return ['< No types >'];
- }
-
- getProducerStatus(eiProducer: EIProducer): string {
- if (eiProducer.status) {
- return eiProducer.status;
- }
- return '< No status >';
- }
-
refreshTables() {
this.eiJobsDataSource.loadJobs();
- this.eiProducersDataSource.loadProducers();
-
this.eiJobsDataSource.eiJobsSubject().subscribe((data) => {
this.jobsDataSource.data = data;
});
- this.eiProducersDataSource.eiProducersSubject().subscribe((data) => {
- this.producersDataSource.data = data;
- });
+
+ this.producersList.refresh();
}
}
diff --git a/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.html b/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.html
new file mode 100644
index 0000000..57e61c5
--- /dev/null
+++ b/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.html
@@ -0,0 +1,50 @@
+<div class="table-container">
+ <mat-table id="producersTable" [dataSource]="producersDataSource" fixedLayout
+ matSort (matSortChange)="sortProducers($event)"
+ class="ei-coordinator-table mat-elevation-z8">
+ <ng-container matColumnDef="id">
+ <mat-header-cell *matHeaderCellDef mat-sort-header>
+ <div (click)="stopSort($event)">
+ <form style="display: flex" [formGroup]="producersFormControl">
+ <mat-form-field>
+ <input id="producerIdFilter" matInput formControlName="ei_producer_id">
+ <mat-placeholder>Producer ID</mat-placeholder>
+ </mat-form-field>
+ </form>
+ </div>
+ </mat-header-cell>
+ <mat-cell *matCellDef="let eiProducer"> {{eiProducer.ei_producer_id}} </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="types">
+ <mat-header-cell *matHeaderCellDef mat-sort-header>
+ <div (click)="stopSort($event)">
+ <form style="display: flex" [formGroup]="producersFormControl">
+ <mat-form-field>
+ <input id="producerTypesFilter" matInput formControlName="ei_producer_types">
+ <mat-placeholder>Producer types</mat-placeholder>
+ </mat-form-field>
+ </form>
+ </div>
+ </mat-header-cell>
+ <mat-cell *matCellDef="let eiProducer"> {{this.getProducerTypes(eiProducer)}} </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="status">
+ <mat-header-cell *matHeaderCellDef mat-sort-header>
+ <div (click)="stopSort($event)">
+ <form style="display: flex" [formGroup]="producersFormControl">
+ <mat-form-field>
+ <input id="producerStatusFilter" matInput formControlName="status">
+ <mat-placeholder>Producer status</mat-placeholder>
+ </mat-form-field>
+ </form>
+ </div>
+ </mat-header-cell>
+ <mat-cell *matCellDef="let eiProducer"> {{this.getProducerStatus(eiProducer)}} </mat-cell>
+ </ng-container>
+
+ <mat-header-row *matHeaderRowDef="['id', 'types', 'status']"></mat-header-row>
+ <mat-row *matRowDef="let row; columns: ['id', 'types', 'status'];"></mat-row>
+ </mat-table>
+ </div>
diff --git a/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.scss b/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.scss
new file mode 100644
index 0000000..b1963bb
--- /dev/null
+++ b/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.scss
@@ -0,0 +1,7 @@
+.ei-coordinator-table {
+ width: 100%;
+ min-height: 150px;
+ margin-top: 10px;
+ margin-bottom: 10px;
+ background-color: transparent;
+ }
diff --git a/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.spec.ts b/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.spec.ts
new file mode 100644
index 0000000..2f537f4
--- /dev/null
+++ b/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.spec.ts
@@ -0,0 +1,140 @@
+import { HarnessLoader } from '@angular/cdk/testing';
+import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
+import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
+import { MatInputHarness } from '@angular/material/input/testing';
+import { MatTableModule } from '@angular/material/table';
+import { MatTableHarness } from '@angular/material/table/testing';
+import { of } from 'rxjs/observable/of';
+import { EIProducer } from 'src/app/interfaces/ei.types';
+import { UiService } from 'src/app/services/ui/ui.service';
+import { EIProducerDataSource } from '../ei-producer.datasource';
+
+import { ProducersListComponent } from './producers-list.component';
+
+describe('ProducersListComponent', () => {
+ let component: ProducersListComponent;
+ let fixture: ComponentFixture<ProducersListComponent>;
+ let loader: HarnessLoader;
+ let producerDataSourceSpy: jasmine.SpyObj<EIProducerDataSource>;
+
+ const producer1 = {
+ ei_producer_id: 'producer1',
+ ei_producer_types: ['type1', 'type2'],
+ status: 'ENABLED'
+ } as EIProducer;
+ const producer2 = {
+ ei_producer_id: 'producer2',
+ ei_producer_types: ['type2', 'type3'],
+ status: 'DISABLED'
+ } as EIProducer;
+
+ beforeEach(async(() => {
+ producerDataSourceSpy = jasmine.createSpyObj('EIProducerDataSource', ['loadProducers', 'eiProducers', 'eiProducersSubject']);
+
+ const producers: EIProducer[] = [producer1, producer2];
+ producerDataSourceSpy.eiProducersSubject.and.returnValue(of(producers));
+
+ TestBed.configureTestingModule({
+ imports: [
+ MatTableModule,
+ ReactiveFormsModule
+ ],
+ schemas: [
+ CUSTOM_ELEMENTS_SCHEMA
+ ],
+ declarations: [
+ ProducersListComponent
+ ],
+ providers: [
+ { provide: EIProducerDataSource, useValue: producerDataSourceSpy },
+ UiService,
+ FormBuilder,
+ ]
+ })
+ .compileComponents();
+ }));
+
+ const expectedProducer1Row = { id: 'producer1', types: 'type1,type2', status: 'ENABLED' };
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ProducersListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ loader = TestbedHarnessEnvironment.loader(fixture);
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should contain producers table with correct columns', async () => {
+ let producersTable = await loader.getHarness(MatTableHarness.with({ selector: '#producersTable' }));
+ let headerRow = (await producersTable.getHeaderRows())[0];
+ let headers = await headerRow.getCellTextByColumnName();
+
+ expect(headers).toEqual({ id: 'Producer ID', types: 'Producer types', status: 'Producer status' });
+ });
+
+
+ it('should contain data after initialization', async () => {
+ component.ngOnInit();
+ const expectedProducerRows = [
+ expectedProducer1Row,
+ { id: 'producer2', types: 'type2,type3', status: 'DISABLED' }
+ ];
+ let producersTable = await loader.getHarness(MatTableHarness.with({ selector: '#producersTable' }));
+ let producerRows = await producersTable.getRows();
+ expect(producerRows.length).toEqual(2);
+ producerRows.forEach(row => {
+ row.getCellTextByColumnName().then(values => {
+ expect(expectedProducerRows).toContain(jasmine.objectContaining(values));
+ });
+ });
+ });
+
+ describe('should display default values for non required properties', () => {
+ it('producer defaults', async () => {
+ const producerMissingProperties = {
+ ei_producer_id: 'producer1'
+ } as EIProducer;
+ const producers: EIProducer[] = [producerMissingProperties];
+ producerDataSourceSpy.eiProducersSubject.and.returnValue(of(producers));
+ component.ngOnInit();
+
+ const expectedProducerRow = { id: 'producer1', types: '< No types >', status: '< No status >' };
+ let producersTable = await loader.getHarness(MatTableHarness.with({ selector: '#producersTable' }));
+ let producerRows = await producersTable.getRows();
+ expect(await producerRows[0].getCellTextByColumnName()).toEqual(expectedProducerRow);
+ });
+ });
+
+ it('filtering', async () => {
+ const expectedProducer1Row = { id: 'producer1', types: 'type1,type2', status: 'ENABLED' };
+ component.ngOnInit();
+ let producersTable = await loader.getHarness(MatTableHarness.with({ selector: '#producersTable' }));
+
+ let idFilterInput = await loader.getHarness(MatInputHarness.with({ selector: '#producerIdFilter' }));
+ await idFilterInput.setValue("1");
+ let producerRows = await producersTable.getRows();
+ expect(producerRows.length).toEqual(1);
+ expect(await producerRows[0].getCellTextByColumnName()).toEqual(expectedProducer1Row);
+
+ idFilterInput.setValue('');
+ let typesFilterInput = await loader.getHarness(MatInputHarness.with({ selector: '#producerTypesFilter' }));
+ await typesFilterInput.setValue("1");
+ producerRows = await producersTable.getRows();
+ expect(producerRows.length).toEqual(1);
+ expect(await producerRows[0].getCellTextByColumnName()).toEqual(expectedProducer1Row);
+ await typesFilterInput.setValue("2");
+ producerRows = await producersTable.getRows();
+ expect(producerRows.length).toEqual(2);
+
+ typesFilterInput.setValue('');
+ let statusFilterInput = await loader.getHarness(MatInputHarness.with({ selector: '#producerStatusFilter' }));
+ await statusFilterInput.setValue("enabled");
+ producerRows = await producersTable.getRows();
+ expect(producerRows.length).toEqual(1);
+ expect(await producerRows[0].getCellTextByColumnName()).toEqual(expectedProducer1Row);
+ });
+});
diff --git a/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.ts b/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.ts
new file mode 100644
index 0000000..8fc57b0
--- /dev/null
+++ b/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.ts
@@ -0,0 +1,96 @@
+import { Component, OnInit } from '@angular/core';
+import { AbstractControl, FormBuilder } from '@angular/forms';
+import { Sort } from '@angular/material/sort';
+import { MatTableDataSource } from '@angular/material/table';
+import { EIProducer } from 'src/app/interfaces/ei.types';
+import { UiService } from 'src/app/services/ui/ui.service';
+import { EIProducerDataSource } from '../ei-producer.datasource';
+
+@Component({
+ selector: 'nrcp-producers-list',
+ templateUrl: './producers-list.component.html',
+ styleUrls: ['./producers-list.component.scss']
+})
+export class ProducersListComponent implements OnInit {
+ darkMode: boolean;
+ producersDataSource: MatTableDataSource<EIProducer> = new MatTableDataSource<EIProducer>();
+
+ readonly producersFormControl: AbstractControl;
+
+ constructor(
+ private eiProducersDataSource: EIProducerDataSource,
+ private ui: UiService,
+ private formBuilder: FormBuilder) {
+ this.producersFormControl = formBuilder.group({
+ ei_producer_id: '',
+ ei_producer_types: '',
+ status: ''
+ });
+ }
+
+ ngOnInit(): void {
+ this.refresh();
+
+ this.producersFormControl.valueChanges.subscribe(value => {
+ const filter = { ...value, ei_producer_id: value.ei_producer_id.trim().toLowerCase() } as string;
+ this.producersDataSource.filter = filter;
+ });
+
+ this.producersDataSource.filterPredicate = ((data, filter) => {
+ return this.isDataIncluding(data.ei_producer_id, filter.ei_producer_id)
+ && this.isDataIncluding(data.ei_producer_types.join(','), filter.ei_producer_types)
+ && this.isDataIncluding(data.status, filter.status);
+ }) as (data: EIProducer, filter: any) => boolean;
+
+ this.ui.darkModeState.subscribe((isDark) => {
+ this.darkMode = isDark;
+ });
+ }
+
+ isDataIncluding(data: string, filter: string): boolean {
+ return !filter || data.toLowerCase().includes(filter);
+ }
+
+ sortProducers(sort: Sort) {
+ const data = this.producersDataSource.data
+ data.sort((a: EIProducer, b: EIProducer) => {
+ const isAsc = sort.direction === 'asc';
+ switch (sort.active) {
+ case 'id': return this.compare(a.ei_producer_id, b.ei_producer_id, isAsc);
+ case 'types': return this.compare(a.ei_producer_types, b.ei_producer_types, isAsc);
+ case 'status': return this.compare(a.status, b.status, isAsc);
+ default: return 0;
+ }
+ });
+ this.producersDataSource.data = data;
+ }
+
+ compare(a: any, b: any, isAsc: boolean) {
+ return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
+ }
+
+ stopSort(event: any) {
+ event.stopPropagation();
+ }
+
+ getProducerTypes(eiProducer: EIProducer): string[] {
+ if (eiProducer.ei_producer_types) {
+ return eiProducer.ei_producer_types;
+ }
+ return ['< No types >'];
+ }
+
+ getProducerStatus(eiProducer: EIProducer): string {
+ if (eiProducer.status) {
+ return eiProducer.status;
+ }
+ return '< No status >';
+ }
+
+ refresh() {
+ this.eiProducersDataSource.loadProducers();
+ this.eiProducersDataSource.eiProducersSubject().subscribe((data) => {
+ this.producersDataSource.data = data;
+ });
+ }
+}