Refactor filters

This commit is contained in:
Adina Țeudan 2020-11-06 00:06:23 +02:00
parent 9bdbbdb0ef
commit 4ce1592207
8 changed files with 129 additions and 95 deletions

View File

@ -1,4 +1,7 @@
import { FilterModel } from '../model/filter.model';
import { FileStatusWrapper } from '../../../screens/file/model/file-status.wrapper';
import * as moment from 'moment';
import { ProjectWrapper } from '../../../state/app-state.service';
export function handleCheckedValue(filter: FilterModel) {
if (filter.filters && filter.filters.length) {
@ -12,3 +15,59 @@ export function handleCheckedValue(filter: FilterModel) {
filter.indeterminate = false;
}
}
export function checkFilter(entity: any, filters: FilterModel[], validate: Function) {
const hasChecked = filters.find((f) => f.checked);
if (!hasChecked) {
return true;
}
let filterMatched = false;
for (const filter of filters) {
if (filter.checked && validate(entity, filter)) {
filterMatched = true;
break;
}
}
return filterMatched;
}
export const keyChecker = (key: string) => (entity: any, filter: FilterModel) =>
entity[key] === filter.key;
export const annotationFilterChecker = (f: FileStatusWrapper, filter: FilterModel) => {
const getter = 'has' + filter.key[0].toUpperCase() + filter.key.slice(1);
return f[getter];
};
export const fileAddedFilterChecker = (f: FileStatusWrapper, filter: FilterModel) =>
moment(f.added).format('DD/MM/YYYY') === filter.key;
export const projectStatusChecker = (pw: ProjectWrapper, filter: FilterModel) =>
pw.hasStatus(filter.key);
export const projectMemberChecker = (pw: ProjectWrapper, filter: FilterModel) => {
return pw.hasMember(filter.key);
};
export const dueDateChecker = (pw: ProjectWrapper, filter: FilterModel) =>
pw.dueDateMatches(filter.key);
export const addedDateChecker = (pw: ProjectWrapper, filter: FilterModel) =>
pw.addedDateMatches(filter.key);
export function getFilteredEntities(
entities: any[],
filters: { values: FilterModel[]; checker: Function }[]
) {
const filteredEntities = [];
for (const entity of entities) {
let add = true;
for (const filter of filters) {
add = add && checkFilter(entity, filter.values, filter.checker);
}
if (add) {
filteredEntities.push(entity);
}
}
return filteredEntities;
}

View File

@ -13,7 +13,7 @@ export class AnnotationIconComponent implements OnInit {
ngOnInit(): void {
if (this.typeValue?.type === 'request') {
let styleSheet = document.styleSheets[0];
const styleSheet = document.styleSheets[0];
styleSheet.insertRule(
`.request:after { border-top-color: ${this.typeValue.hexColor} !important; }`,
styleSheet.cssRules.length

View File

@ -31,6 +31,7 @@ export class IconsModule {
'lightning',
'logout',
'menu',
'needs-work',
'pages',
'plus',
'preview',

View File

@ -8,6 +8,13 @@ import { DialogService } from '../../dialogs/dialog.service';
import { FilterModel } from '../../common/filter/model/filter.model';
import * as moment from 'moment';
import { SortingComponent, SortingOption } from '../../components/sorting/sorting.component';
import {
addedDateChecker,
dueDateChecker,
getFilteredEntities,
projectMemberChecker,
projectStatusChecker
} from '../../common/filter/utils/filter-utils';
@Component({
selector: 'redaction-project-listing-screen',
@ -205,59 +212,17 @@ export class ProjectListingScreenComponent implements OnInit {
}
private _filterProjects() {
const filteredProjects = [];
const filters = [
{ values: this.statusFilters, checker: projectStatusChecker },
{ values: this.peopleFilters, checker: projectMemberChecker },
{ values: this.dueDateFilters, checker: dueDateChecker },
{ values: this.addedDateFilters, checker: addedDateChecker }
];
for (const project of this.appStateService.allProjects) {
const statusFilterMatched = this._checkFilter(
project,
this.statusFilters,
(pw: ProjectWrapper, filter: FilterModel) => pw.hasStatus(filter.key)
);
const peopleFilterMatched = this._checkFilter(
project,
this.peopleFilters,
(pw: ProjectWrapper, filter: FilterModel) => pw.hasMember(filter.key)
);
const dueDateFilterMatched = this._checkFilter(
project,
this.dueDateFilters,
(pw: ProjectWrapper, filter: FilterModel) => pw.dueDateMatches(filter.key)
);
const addedFilterMatched = this._checkFilter(
project,
this.addedDateFilters,
(pw: ProjectWrapper, filter: FilterModel) => pw.addedDateMatches(filter.key)
);
if (
statusFilterMatched &&
peopleFilterMatched &&
addedFilterMatched &&
dueDateFilterMatched
) {
filteredProjects.push(project);
}
}
this.displayedProjects = filteredProjects;
this.displayedProjects = getFilteredEntities(this.appStateService.allProjects, filters);
this._changeDetectorRef.detectChanges();
}
private _checkFilter(project: ProjectWrapper, filters: FilterModel[], validate: Function) {
const hasChecked = filters.find((f) => f.checked);
if (!hasChecked) {
return true;
}
let filterMatched = false;
for (const filter of filters) {
if (filter.checked && validate(project, filter)) {
filterMatched = true;
break;
}
}
return filterMatched;
}
public toggleProjectSelected($event: MouseEvent, pw: ProjectWrapper) {
$event.stopPropagation();
const idx = this._selectedProjectIds.indexOf(pw.project.projectId);

View File

@ -34,6 +34,13 @@
[hasArrow]="false"
[icon]="'red:calendar'"
></redaction-filter>
<redaction-filter
(filtersChanged)="filtersChanged()"
[filterLabel]="'filters.needs-work'"
[filters]="needsWorkFilters"
[hasArrow]="false"
[icon]="'red:needs-work'"
></redaction-filter>
<!-- <button mat-button translate="filters.project">-->
<!-- <mat-icon svgIcon="red:folder"></mat-icon>-->
<!-- </button>-->

View File

@ -22,6 +22,12 @@ import * as moment from 'moment';
import { SortingComponent, SortingOption } from '../../components/sorting/sorting.component';
import { ProjectDetailsComponent } from './project-details/project-details.component';
import { FileStatusWrapper } from '../file/model/file-status.wrapper';
import {
annotationFilterChecker,
fileAddedFilterChecker,
getFilteredEntities,
keyChecker
} from '../../common/filter/utils/filter-utils';
@Component({
selector: 'redaction-project-overview-screen',
@ -35,6 +41,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
public statusFilters: FilterModel[];
public peopleFilters: FilterModel[];
public addedDateFilters: FilterModel[];
public needsWorkFilters: FilterModel[];
public displayedFiles: FileStatusWrapper[] = [];
@ -260,6 +267,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
const allDistinctFileStatusWrapper = new Set<string>();
const allDistinctPeople = new Set<string>();
const allDistinctAddedDates = new Set<string>();
const allDistinctNeedsWork = new Set<string>();
// All people
this.appStateService.activeProject.project.memberIds.forEach((memberId) =>
@ -276,6 +284,13 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
allDistinctAddedDates.add(moment(file.added).format('DD/MM/YYYY'))
);
// Needs work
this.appStateService.activeProject.files.forEach((file) => {
if (file.hasHints) allDistinctNeedsWork.add('hints');
if (file.hasRedactions) allDistinctNeedsWork.add('redactions');
if (file.hasRequests) allDistinctNeedsWork.add('requests');
});
this.statusFilters = [];
allDistinctFileStatusWrapper.forEach((status) => {
this.statusFilters.push({
@ -299,6 +314,14 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
label: date
});
});
this.needsWorkFilters = [];
allDistinctNeedsWork.forEach((type) => {
this.needsWorkFilters.push({
key: type,
label: this._translateService.instant('filter.' + type)
});
});
}
filtersChanged() {
@ -306,52 +329,19 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
}
private _filterFiles() {
const filteredFiles = [];
for (const file of this.appStateService.activeProject.files) {
const statusFilterMatched = this._checkFilter(
file,
this.statusFilters,
(f: FileStatusWrapper, filter: FilterModel) => f.status === filter.key
);
const peopleFilterMatched = this._checkFilter(
file,
this.peopleFilters,
(f: FileStatusWrapper, filter: FilterModel) => f.currentReviewer === filter.key
);
const addedFilterMatched = this._checkFilter(
file,
this.addedDateFilters,
(f: FileStatusWrapper, filter: FilterModel) =>
moment(f.added).format('DD/MM/YYYY') === filter.key
);
if (statusFilterMatched && peopleFilterMatched && addedFilterMatched) {
filteredFiles.push(file);
}
}
this.displayedFiles = filteredFiles;
const filters = [
{ values: this.statusFilters, checker: keyChecker('status') },
{ values: this.peopleFilters, checker: keyChecker('currentReviewer') },
{ values: this.addedDateFilters, checker: fileAddedFilterChecker },
{ values: this.needsWorkFilters, checker: annotationFilterChecker }
];
this.displayedFiles = getFilteredEntities(
this.appStateService.activeProject.files,
filters
);
this._changeDetectorRef.detectChanges();
}
private _checkFilter(file: FileStatusWrapper, filters: FilterModel[], validate: Function) {
const hasChecked = filters.find((f) => f.checked);
if (!hasChecked) {
return true;
}
let filterMatched = false;
for (const filter of filters) {
if (filter.checked && validate(file, filter)) {
filterMatched = true;
break;
}
}
return filterMatched;
}
requestApprovalOrApproveFile($event: MouseEvent, fileStatusWrapper: FileStatusWrapper) {
$event.stopPropagation();
this._fileActionService.requestApprovalOrApproveFile(fileStatusWrapper).subscribe(() => {

View File

@ -89,7 +89,8 @@
"due-date": "Due Date",
"created-on": "Created On",
"project": "Project",
"document": "Document"
"document": "Document",
"needs-work": "Needs Work"
},
"project-listing": {
"report": {
@ -295,7 +296,11 @@
"dictionary": "Dictionary",
"content": "Content",
"page": "Page",
"filter": {},
"filter": {
"hints": "Hints",
"redactions": "Redactions",
"requests": "Requests"
},
"annotation-filter": {
"super-type": {
"redaction": "Redaction",

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Needs work</title>
<g id="Needs-work" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M100,90 L100,100 L39,100 L39,90 L100,90 Z M70,0 L70,45 L60,45 L60,10 L10,10 L10,80 L30,80 L30,90 L0,90 L0,0 L70,0 Z M86,72 L86,82 L39,82 L39,72 L86,72 Z M62,54 L62,64 L39,64 L39,54 L62,54 Z" id="Combined-Shape" fill="currentColor" fill-rule="nonzero"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 597 B