Skip to content

Commit

Permalink
feat(assessments): add custom table filter matchMode to filter Tester…
Browse files Browse the repository at this point in the history
…s array
  • Loading branch information
bmayen-sr committed Oct 4, 2020
1 parent abc484d commit 0e555b4
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 65 deletions.
15 changes: 8 additions & 7 deletions frontend/src/app/app.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { environment } from '../environments/environment';
})
export class AppService {
constructor(private http: HttpClient, private sanitizer: DomSanitizer) {}

api = environment.apiUrl;
// TODO: Delete this monstrosity that I have created
// Please forgive me coding gods!
Expand Down Expand Up @@ -165,7 +166,7 @@ export class AppService {
.get(`${this.api}/assessment/${id}`)
.toPromise()
.then((res) => {
return res;
return res as object[];
});
}

Expand Down Expand Up @@ -271,7 +272,7 @@ export class AppService {
createAsset(asset: Asset) {
return this.http.post(
`${this.api}/organization/${asset.organization}/asset`,
asset
asset,
);
}

Expand All @@ -297,7 +298,7 @@ export class AppService {
updateAsset(asset: Asset) {
return this.http.patch(
`${this.api}/organization/${asset.organization}/asset/${asset.id}`,
asset
asset,
);
}

Expand Down Expand Up @@ -338,11 +339,11 @@ export class AppService {
updateAssessment(
assessment: Assessment,
assessmentId: number,
assetId: number
assetId: number,
) {
return this.http.patch(
`${this.api}/asset/${assetId}/assessment/${assessmentId}`,
assessment
assessment,
);
}

Expand All @@ -354,7 +355,7 @@ export class AppService {
*/
getAssessment(assetId: number, assessmentId: number) {
return this.http.get(
`${this.api}/asset/${assetId}/assessment/${assessmentId}`
`${this.api}/asset/${assetId}/assessment/${assessmentId}`,
);
}

Expand Down Expand Up @@ -406,7 +407,7 @@ export class AppService {
return this.http.post(
`${this.api}/report/generate`,
generateObject,
httpOptions
httpOptions,
);
}

Expand Down
113 changes: 65 additions & 48 deletions frontend/src/app/assessments/assessments.component.html
Original file line number Diff line number Diff line change
@@ -1,71 +1,88 @@
<div class="container-fluid">
<p-table #assessmentTable [value]="assessmentAry" [paginator]="true" [rows]="10" styleClass="p-datatable-striped">
<ng-template pTemplate="header">
<p-table #assessmentTable [value]="assessmentAry" [columns]="cols" [paginator]="true" [rows]="10"
styleClass="p-datatable-striped"
[globalFilterFields]="['testers']">

<ng-template pTemplate="header" let-columns>
<tr>
<th pSortableColumn="id">Assessment ID<p-sortIcon field="id"></p-sortIcon>
</th>
<th pSortableColumn="name">Assessment Name<p-sortIcon field="name"></p-sortIcon>
</th>
<th pSortableColumn="testers">Testers<p-sortIcon field="testers"></p-sortIcon>
</th>
<th>Jira ID</th>
<th pSortableColumn="startDate">Start Date<p-sortIcon field="startDate"></p-sortIcon>
</th>
<th pSortableColumn="endDate">End Date<p-sortIcon field="endDate"></p-sortIcon>
<th *ngFor="let col of columns" [pSortableColumn]="col.field">
{{col.header}}
<p-sortIcon [field]="col.field"></p-sortIcon>
</th>
<th scope="col"></th>
</tr>

<tr>
<th>
<input pInputText type="text" (input)="assessmentTable.filter($event.target.value, 'id', 'equals')"
placeholder="Search by ID" class="p-column-filter">
</th>
<th>
<input pInputText type="text" (input)="assessmentTable.filter($event.target.value, 'name', 'startsWith')"
placeholder="Search by Name" class="p-column-filter">
</th>
<th>
<!-- TODO: Fix multiselect for testers
<p-multiSelect [options]="testers" placeholder="All" (onChange)="onTesterChange($event)"
optionLabel="firstName" styleClass="p-column-filter">
<ng-template let-option pTemplate="item">
<div class="p-multiselect-representative-option">
<span class="p-ml-1">{{option.value.firstName}} {{option.value.lastName}}</span>
</div>
</ng-template>
</p-multiSelect> -->
<th>
<input pInputText type="text" (input)="assessmentTable.filter($event.target.value, 'jiraId', 'contains')"
placeholder="Search by Jira ID" class="p-column-filter">
</th>
<th>
<p-calendar (onSelect)="onDateSelect($event, 'startDate')"
(onClearClick)="assessmentTable.filter('', 'startDate', 'equals')" [showButtonBar]="true"
styleClass="p-column-filter" placeholder="Start Date" [readonlyInput]="true" dateFormat="m-d-yy">
</p-calendar>
</th>
<th>
<p-calendar (onSelect)="onDateSelect($event, 'endDate')"
(onClearClick)="assessmentTable.filter('', 'endDate', 'equals')" [showButtonBar]="true"
styleClass="p-column-filter" placeholder="End Date" [readonlyInput]="true" dateFormat="m-d-yy">
</p-calendar>
<th *ngFor="let col of columns" [ngSwitch]="col.field">
<input *ngSwitchCase="'id'"
pInputText
type="text"
placeholder="Search by ID"
class="p-column-filter"
(input)="assessmentTable.filter($event.target.value, col.field, col.filterMatchMode)"
>

<input *ngSwitchCase="'name'"
pInputText
type="text"
placeholder="Search by Name"
class="p-column-filter"
(input)="assessmentTable.filter($event.target.value, col.field, col.filterMatchMode)"
>

<p-multiSelect *ngSwitchCase="'testers'"
placeholder="All"
styleClass="p-column-filter"
optionLabel="name"
[options]="testers"
(onChange)=" assessmentTable.filter($event.value, col.field, col.filterMatchMode)"
>
</p-multiSelect>

<input *ngSwitchCase="'jiraId'"
pInputText type="text"
placeholder="Search by Jira ID"
class="p-column-filter"
(input)="assessmentTable.filter($event.target.value, col.field, col.filterMatchMode)"
>

<p-calendar *ngSwitchCase="'startDate'"
styleClass="p-column-filter"
placeholder="Start Date"
dateFormat="m-d-yy"
[readonlyInput]="true"
[showButtonBar]="true"
(onSelect)="onDateSelect($event, 'startDate')"
(onClearClick)="assessmentTable.filter('', col.field, col.filterMatchMode)"
></p-calendar>

<p-calendar *ngSwitchCase="'endDate'"
styleClass="p-column-filter"
placeholder="End Date"
dateFormat="m-d-yy"
[readonlyInput]="true"
[showButtonBar]="true"
(onSelect)="onDateSelect($event, 'endDate')"
(onClearClick)="assessmentTable.filter('', col.field, col.filterMatchMode)"
></p-calendar>
</th>
<th></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-assessment>

<ng-template pTemplate="body" let-assessment let-columns="columns">
<tr>
<td scope="row">{{ assessment.id }}</td>
<td>{{ assessment?.name }}</td>
<td>
<div *ngFor="let tester of assessment.testers;let last = last">
<div *ngFor="let tester of assessment.testers; let last = last">
{{tester?.firstName}} {{tester?.lastName}}<span *ngIf="!last">,</span>
</div>
</td>
<td>
<a target="_blank" [href]="assessment?.jiraId">{{
assessment?.jiraId
}}</a>
}}</a>
</td>
<td>{{ assessment?.startDate | date: "shortDate":'UTC' }}</td>
<td>{{ assessment?.endDate | date: "shortDate":'UTC' }}</td>
Expand Down
81 changes: 71 additions & 10 deletions frontend/src/app/assessments/assessments.component.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { FilterUtils } from 'primeng/utils';
import { AppService } from '../app.service';
import { AlertService } from '../alert/alert.service';
import { ActivatedRoute, Router } from '@angular/router';
import { Assessment } from '../assessment-form/Assessment';
import { Table } from 'primeng/table';
import { UserService } from '../user.service';
import { User } from '../interfaces/User';
import { UserService } from '../user.service';

// User decorated with compound name field
interface FormattedUser extends User {
name: string;
}

@Component({
selector: 'app-assessments',
Expand All @@ -16,28 +22,69 @@ export class AssessmentsComponent implements OnInit {
assessmentAry: any = [];
assetId: number;
orgId: number;
testers: User[];
testers: FormattedUser[];
@ViewChild('assessmentTable') table: Table;

cols = [
{
field: 'id',
filterMatchMode: 'contains',
header: 'Assessment ID',
},
{
field: 'name',
filterMatchMode: 'contains',
header: 'Assessment Name',
},
{
field: 'testers',
filterMatchMode: 'arrayCompare',
header: 'Testers',
},
{
field: 'jiraId',
filterMatchMode: 'contains',
header: 'Jira ID',
},
{
field: 'startDate',
filterMatchMode: 'equals',
header: 'Start Date',
},
{
field: 'startDate',
filterMatchMode: 'equals',
header: 'End Date',
},
];

constructor(
public activatedRoute: ActivatedRoute,
public router: Router,
public appService: AppService,
public alertService: AlertService,
public userService: UserService
public userService: UserService,
) {}

ngOnInit() {
this.activatedRoute.data.subscribe(
({ assessments }) => (this.assessmentAry = assessments)
({assessments}) => {
this.assessmentAry = assessments;
},
);
this.activatedRoute.params.subscribe((params) => {
this.assetId = params.assetId;
this.orgId = params.orgId;
});
this.userService.getUsers().subscribe((testers) => {
this.testers = testers;
// add a composite 'name' field to the Testers to display in MultiSelect
this.testers = testers.map((tester) => ({
...tester,
name: this.formatName(tester),
}));
});

this.addArrayCompareTableFilter();
}

/**
Expand Down Expand Up @@ -97,11 +144,6 @@ export class AssessmentsComponent implements OnInit {
}
}

onTesterChange(event) {
const selectedTesterAry = event.value.map((x) => x.id);
this.table.filter(selectedTesterAry, 'testers', 'in');
}

onDateSelect(value, type) {
const date = new Date(value);
date.setUTCHours(0, 0, 0, 0);
Expand All @@ -111,4 +153,23 @@ export class AssessmentsComponent implements OnInit {
this.table.filter(date.toISOString(), 'endDate', 'equals');
}
}

/**
* Create custom table filter "matchMode" to compare multiselect filter array values to array of values in a table row field
*/
private addArrayCompareTableFilter() {
// @ts-ignore-next-line
FilterUtils.arrayCompare = (values: User[], selections: FormattedUser[]) => {
return values.some((value) => {
return !!selections.some((selection) => selection.name === this.formatName(value));
});
};
}

/**
* Format composite name from first and last name fields
*/
private formatName(user) {
return `${user.firstName} ${user.lastName}`;
}
}

0 comments on commit 0e555b4

Please sign in to comment.