Merge "Missing import"
diff --git a/webapp-frontend/src/app/ei-coordinator/jobs-list/jobs-list.component.html b/webapp-frontend/src/app/ei-coordinator/jobs-list/jobs-list.component.html
index c7f3a20..d325d4d 100644
--- a/webapp-frontend/src/app/ei-coordinator/jobs-list/jobs-list.component.html
+++ b/webapp-frontend/src/app/ei-coordinator/jobs-list/jobs-list.component.html
@@ -24,18 +24,31 @@
     </div>
     <mat-table id="jobsTable" [dataSource]="jobsDataSource" fixedLayout matSort (matSortChange)="sortJobs($event)"
         matSortDisableClear matSortDirection="asc" class="ei-coordinator-table mat-elevation-z8">
-        <ng-container matColumnDef="id">
+        <ng-container matColumnDef="jobId">
             <mat-header-cell *matHeaderCellDef mat-sort-header>
                 <div (click)="stopSort($event)">
                     <form style="display: flex" [formGroup]="jobForm">
                         <mat-form-field>
-                            <input id="jobIdFilter" matInput formControlName="id">
+                            <input id="jobIdFilter" matInput formControlName="jobId">
                             <mat-placeholder>Job ID</mat-placeholder>
                         </mat-form-field>
                     </form>
                 </div>
             </mat-header-cell>
-            <mat-cell *matCellDef="let eiJob"> {{eiJob.ei_job_identity}} </mat-cell>
+            <mat-cell *matCellDef="let eiJob"> {{eiJob.jobId}} </mat-cell>
+        </ng-container>
+        <ng-container matColumnDef="prodId">
+            <mat-header-cell *matHeaderCellDef mat-sort-header>
+                <div (click)="stopSort($event)">
+                    <form style="display: flex" [formGroup]="jobForm">
+                        <mat-form-field>
+                            <input id="jobProdIdFilter" matInput formControlName="prodId">
+                            <mat-placeholder>Producer ID</mat-placeholder>
+                        </mat-form-field>
+                    </form>
+                </div>
+            </mat-header-cell>
+            <mat-cell *matCellDef="let eiJob"> {{eiJob.prodId}} </mat-cell>
         </ng-container>
         <ng-container matColumnDef="typeId">
             <mat-header-cell *matHeaderCellDef mat-sort-header>
@@ -74,10 +87,10 @@
                     </form>
                 </div>
             </mat-header-cell>
-            <mat-cell *matCellDef="let eiJob"> {{eiJob.target_uri}} </mat-cell>
+            <mat-cell *matCellDef="let eiJob"> {{eiJob.targetUri}} </mat-cell>
         </ng-container>
-        <mat-header-row *matHeaderRowDef="['id', 'typeId', 'owner', 'targetUri']"></mat-header-row>
-        <mat-row *matRowDef="let row; columns: ['id', 'typeId', 'owner', 'targetUri'];"></mat-row>
+        <mat-header-row *matHeaderRowDef="['jobId', 'prodId', 'typeId', 'owner', 'targetUri']"></mat-header-row>
+        <mat-row *matRowDef="let row; columns: ['jobId', 'prodId', 'typeId', 'owner', 'targetUri'];"></mat-row>
     </mat-table>
     <mat-paginator [length]="jobs()?.length" [pageSize]="10" [pageSizeOptions]="[5, 10, 25, 100]" showFirstLastButtons
         class="ei-coordinator-table mat-elevation-z8"></mat-paginator>
diff --git a/webapp-frontend/src/app/ei-coordinator/jobs-list/jobs-list.component.spec.ts b/webapp-frontend/src/app/ei-coordinator/jobs-list/jobs-list.component.spec.ts
index f7d4924..28412a5 100644
--- a/webapp-frontend/src/app/ei-coordinator/jobs-list/jobs-list.component.spec.ts
+++ b/webapp-frontend/src/app/ei-coordinator/jobs-list/jobs-list.component.spec.ts
@@ -37,24 +37,53 @@
 import { EIService } from '@services/ei/ei.service';
 import { UiService } from '@services/ui/ui.service';
 
-import { JobsListComponent } from './jobs-list.component';
+import { Job, JobsListComponent } from './jobs-list.component';
 
 let component: JobsListComponent;
 let fixture: ComponentFixture<JobsListComponent>;
 
-const job1 = {
+const eijob1 = {
   ei_job_identity: 'job1',
   ei_type_identity: 'type1',
   owner: 'owner1',
   target_uri: 'http://one'
 } as EIJob;
-const job2 = {
+const eijob2 = {
   ei_job_identity: 'job2',
   ei_type_identity: 'type2',
   owner: 'owner2',
   target_uri: 'http://two'
 } as EIJob;
 
+const job1 = {
+  jobId: 'job1',
+  typeId: 'type1',
+  owner: 'owner1',
+  targetUri: 'http://one',
+  prodId: 'producer1'
+} as Job;
+const job2 = {
+  jobId: 'job2',
+  typeId: 'type2',
+  owner: 'owner2',
+  targetUri: 'http://two',
+  prodId: 'producer1'
+} as Job;
+const job3 = {
+  jobId: 'job1',
+  typeId: 'type1',
+  owner: 'owner1',
+  targetUri: 'http://one',
+  prodId: 'producer2'
+} as Job;
+const job4 = {
+  jobId: 'job2',
+  typeId: 'type2',
+  owner: 'owner2',
+  targetUri: 'http://two',
+  prodId: 'producer2'
+} as Job;
+
 describe('JobsListComponent', () => {
   let loader: HarnessLoader;
 
@@ -87,7 +116,7 @@
       });
   }));
 
-  const expectedJob1Row = { id: 'job1', typeId: 'type1', owner: 'owner1', targetUri: 'http://one' };
+  const expectedJob1Row = { jobId: 'job1', prodId: 'producer1', typeId: 'type1', owner: 'owner1', targetUri: 'http://one' };
 
   it('should create', () => {
     expect(component).toBeTruthy();
@@ -98,9 +127,9 @@
     it('should loadJobs', () => {
       setServiceSpy();
       component.loadJobs();
-      const actualJobs: EIJob[] = component.jobs();
+      const actualJobs: Job[] = component.jobs();
       expect(actualJobs.length).toEqual(4);
-      expect(actualJobs).toEqual([job1, job2, job1, job2]);
+      expect(actualJobs).toEqual([job1, job2, job3, job4]);
     });
 
     it('should contain job table with correct columns', async () => {
@@ -109,7 +138,7 @@
       let headerRow = (await jobsTable.getHeaderRows())[0];
       let headers = await headerRow.getCellTextByColumnName();
 
-      expect(headers).toEqual({ id: 'Job ID', typeId: 'Type ID', owner: 'Owner', targetUri: 'Target URI' });
+      expect(headers).toEqual({ jobId: 'Job ID', prodId: 'Producer ID', typeId: 'Type ID', owner: 'Owner', targetUri: 'Target URI' });
     });
 
     it('should set correct dark mode from UIService', () => {
@@ -131,7 +160,9 @@
       component.ngOnInit();
       const expectedJobRows = [
         expectedJob1Row,
-        { id: 'job2', typeId: 'type2', owner: 'owner2', targetUri: 'http://two' }
+        { jobId: 'job2', prodId: 'producer1', typeId: 'type2', owner: 'owner2', targetUri: 'http://two' },
+        { jobId: 'job1', prodId: 'producer2', typeId: 'type1', owner: 'owner1', targetUri: 'http://one' },
+        { jobId: 'job2', prodId: 'producer2', typeId: 'type2', owner: 'owner2', targetUri: 'http://two' }
       ];
       let jobsTable = await loader.getHarness(MatTableHarness.with({ selector: '#jobsTable' }));
       let jobRows = await jobsTable.getRows();
@@ -160,7 +191,7 @@
       eiServiceSpy.getJobsForProducer.and.returnValue(of([jobMissingProperties]));
 
       component.ngOnInit();
-      const expectedJobRow = { id: 'job1', typeId: '< No type >', owner: '< No owner >', targetUri: 'http://one' };
+      const expectedJobRow = { jobId: 'job1', prodId: 'producer1', 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);
@@ -204,7 +235,7 @@
         setServiceSpy();
         const sort = await loader.getHarness(MatSortHarness);
         let headers = await sort.getSortHeaders({ sortDirection: '' });
-        expect(headers.length).toBe(4);
+        expect(headers.length).toBe(5);
 
         await headers[0].click();
         expect(await headers[0].isActive()).toBe(true);
@@ -239,7 +270,7 @@
       it('should work properly on the table', async () => {
         let eiServiceSpy = TestBed.inject(EIService) as jasmine.SpyObj<EIService>;
         eiServiceSpy.getProducerIds.and.returnValue(of(['producer1', 'producer2']));
-        eiServiceSpy.getJobsForProducer.and.returnValue(of([job1, job2, job1]));
+        eiServiceSpy.getJobsForProducer.and.returnValue(of([eijob1, eijob2, eijob1]));
 
         const paging = await loader.getHarness(MatPaginatorHarness);
         await paging.setPageSize(5);
@@ -251,7 +282,8 @@
         await paging.goToNextPage();
         jobRows = await jobsTable.getRows();
         expect(jobRows.length).toEqual(1);
-        expect(await jobRows[jobRows.length - 1].getCellTextByColumnName()).toEqual(expectedJob1Row);
+        const expectedRow = { jobId: 'job1', prodId: 'producer2', typeId: 'type1', owner: 'owner1', targetUri: 'http://one' };
+        expect(await jobRows[jobRows.length - 1].getCellTextByColumnName()).toEqual(expectedRow);
       });
     });
   });
@@ -260,5 +292,5 @@
 function setServiceSpy() {
   let eiServiceSpy = TestBed.inject(EIService) as jasmine.SpyObj<EIService>;
   eiServiceSpy.getProducerIds.and.returnValue(of(['producer1', 'producer2']));
-  eiServiceSpy.getJobsForProducer.and.returnValue(of([job1, job2]));
+  eiServiceSpy.getJobsForProducer.and.returnValue(of([eijob1, eijob2]));
 }
diff --git a/webapp-frontend/src/app/ei-coordinator/jobs-list/jobs-list.component.ts b/webapp-frontend/src/app/ei-coordinator/jobs-list/jobs-list.component.ts
index b891021..7fea537 100644
--- a/webapp-frontend/src/app/ei-coordinator/jobs-list/jobs-list.component.ts
+++ b/webapp-frontend/src/app/ei-coordinator/jobs-list/jobs-list.component.ts
@@ -24,11 +24,20 @@
 import { MatTableDataSource } from '@angular/material/table';
 import { forkJoin } from 'rxjs';
 import { BehaviorSubject } from 'rxjs/BehaviorSubject';
-import { mergeMap, finalize } from 'rxjs/operators';
+import { mergeMap, finalize, tap } from 'rxjs/operators';
 import { EIJob } from '@interfaces/ei.types';
 import { EIService } from '@services/ei/ei.service';
 import { UiService } from '@services/ui/ui.service';
 
+export interface Job {
+  jobId: string;
+  jobData: any;
+  typeId: string;
+  targetUri: string;
+  owner: string;
+  prodId: string;
+}
+
 @Component({
   selector: 'nrcp-jobs-list',
   templateUrl: './jobs-list.component.html',
@@ -39,10 +48,10 @@
   @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
 
   darkMode: boolean;
-  jobsDataSource: MatTableDataSource<EIJob>;
+  jobsDataSource: MatTableDataSource<Job>;
   jobForm: FormGroup;
   private loadingSubject = new BehaviorSubject<boolean>(false);
-  private jobsSubject = new BehaviorSubject<EIJob[]>([]);
+  private jobsSubject = new BehaviorSubject<Job[]>([]);
   public loading$ = this.loadingSubject.asObservable();
 
   constructor(
@@ -50,26 +59,28 @@
     private ui: UiService
   ) {
     this.jobForm = new FormGroup({
-      id: new FormControl(''),
+      jobId: new FormControl(''),
       typeId: new FormControl(''),
       owner: new FormControl(''),
-      targetUri: new FormControl('')
+      targetUri: new FormControl(''),
+      prodId: new FormControl('')
     });
   }
 
   ngOnInit(): void {
     this.loadJobs();
     this.jobsSubject.subscribe((data) => {
-      this.jobsDataSource = new MatTableDataSource<EIJob>(data);
+      this.jobsDataSource = new MatTableDataSource<Job>(data);
       this.jobsDataSource.paginator = this.paginator;
 
-      this.jobsDataSource.filterPredicate = ((data: EIJob, filter) => {
+      this.jobsDataSource.filterPredicate = ((data: Job, filter) => {
         let searchTerms = JSON.parse(filter);
-        return this.isDataIncluding(data.ei_job_identity, searchTerms.id)
-          && this.isDataIncluding(data.target_uri, searchTerms.targetUri)
+        return this.isDataIncluding(data.targetUri, searchTerms.targetUri)
+          && this.isDataIncluding(data.jobId, searchTerms.jobId)
           && this.isDataIncluding(data.owner, searchTerms.owner)
-          && this.isDataIncluding(data.ei_type_identity, searchTerms.typeId);
-      }) as (data: EIJob, filter: any) => boolean;
+          && this.isDataIncluding(data.typeId, searchTerms.typeId)
+          && this.isDataIncluding(data.prodId, searchTerms.prodId);
+      }) as (data: Job, filter: any) => boolean;
     });
 
     this.jobForm.valueChanges.subscribe(value => {
@@ -88,21 +99,23 @@
   }
 
   clearFilter() {
-    this.jobForm.get('id').setValue('');
+    this.jobForm.get('jobId').setValue('');
     this.jobForm.get('typeId').setValue('');
     this.jobForm.get('owner').setValue('');
     this.jobForm.get('targetUri').setValue('');
+    this.jobForm.get('prodId').setValue('');
   }
 
   sortJobs(sort: Sort) {
     const data = this.jobsDataSource.data
-    data.sort((a: EIJob, b: EIJob) => {
+    data.sort((a: Job, b: Job) => {
       const isAsc = sort.direction === 'asc';
       switch (sort.active) {
-        case 'id': return this.compare(a.ei_job_identity, b.ei_job_identity, isAsc);
-        case 'typeId': return this.compare(a.ei_type_identity, b.ei_type_identity, isAsc);
+        case 'jobId': return this.compare(a.jobId, b.jobId, isAsc);
+        case 'typeId': return this.compare(a.typeId, b.typeId, isAsc);
         case 'owner': return this.compare(a.owner, b.owner, isAsc);
-        case 'targetUri': return this.compare(a.target_uri, b.owner, isAsc);
+        case 'targetUri': return this.compare(a.targetUri, b.targetUri, isAsc);
+        case 'prodId': return this.compare(a.prodId, b.prodId, isAsc);
         default: return 0;
       }
     });
@@ -122,36 +135,54 @@
     return data.toLowerCase().includes(transformedFilter);
   }
 
-  getJobTypeId(eiJob: EIJob): string {
-    if (eiJob.ei_type_identity) {
-      return eiJob.ei_type_identity;
+  getJobTypeId(eiJob: Job): string {
+    if (eiJob.typeId) {
+      return eiJob.typeId;
     }
     return '< No type >';
   }
 
-  getJobOwner(eiJob: EIJob): string {
+  getJobOwner(eiJob: Job): string {
     if (eiJob.owner) {
       return eiJob.owner;
     }
     return '< No owner >';
   }
 
-  public jobs(): EIJob[] {
+  public jobs(): Job[] {
     return this.jobsSubject.value;
   }
 
   loadJobs() {
     this.loadingSubject.next(true);
     let jobs = [];
+    let prodId = [];
     this.eiSvc.getProducerIds().pipe(
+      tap(data => prodId = data),
       mergeMap(prodIds =>
         forkJoin(prodIds.map(id => this.eiSvc.getJobsForProducer(id)))),
-      mergeMap(result => result),
       finalize(() => this.loadingSubject.next(false))
     ).subscribe(result => {
-      jobs = jobs.concat(result);
+      jobs = this.createJobList(prodId, result);
       this.jobsSubject.next(jobs);
     });
   }
+  createJobList(prodId: any[], result: EIJob[][]) {
+    let jobList = [];
+    prodId.forEach((element, index) => {
+      let jobs = result[index];
+      jobList = jobList.concat(jobs.map(job => this.createJob(element, job)));
+    });
+    return jobList;
+  }
+  createJob(element: any, job: EIJob): any {
+    let eiJob = <Job>{};
+    eiJob.jobId = job.ei_job_identity;
+    eiJob.typeId = job.ei_type_identity;
+    eiJob.owner = job.owner;
+    eiJob.targetUri = job.target_uri;
+    eiJob.prodId = element;
+    return eiJob;
+  }
 
 }