From 9bdbbdb0eff75221e1a7a4b04eef4fa24b029ec8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adina=20=C8=9Aeudan?= Date: Thu, 5 Nov 2020 17:21:35 +0200 Subject: [PATCH 1/3] Style suggestion annotation icon --- .../annotation-icon/annotation-icon.component.html | 7 ++++++- .../annotation-icon/annotation-icon.component.scss | 5 ++--- .../annotation-icon/annotation-icon.component.ts | 14 +++++++++++++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/apps/red-ui/src/app/components/annotation-icon/annotation-icon.component.html b/apps/red-ui/src/app/components/annotation-icon/annotation-icon.component.html index 44a90abcb..e66fe0a43 100644 --- a/apps/red-ui/src/app/components/annotation-icon/annotation-icon.component.html +++ b/apps/red-ui/src/app/components/annotation-icon/annotation-icon.component.html @@ -1,4 +1,9 @@ -
+
{{ (typeValue?.type)[0] }}
diff --git a/apps/red-ui/src/app/components/annotation-icon/annotation-icon.component.scss b/apps/red-ui/src/app/components/annotation-icon/annotation-icon.component.scss index cb9a7dd42..e34c2741a 100644 --- a/apps/red-ui/src/app/components/annotation-icon/annotation-icon.component.scss +++ b/apps/red-ui/src/app/components/annotation-icon/annotation-icon.component.scss @@ -14,13 +14,13 @@ color: $white; } -.suggestion { +.request { width: 0; height: 0; border: 9px solid transparent; - border-bottom-color: $grey-1; position: relative; top: -9px; + background-color: transparent !important; &:after { content: ''; @@ -30,7 +30,6 @@ width: 0; height: 0; border: 9px solid transparent; - border-top-color: $grey-1; } span { diff --git a/apps/red-ui/src/app/components/annotation-icon/annotation-icon.component.ts b/apps/red-ui/src/app/components/annotation-icon/annotation-icon.component.ts index 35dc28774..18d7351d8 100644 --- a/apps/red-ui/src/app/components/annotation-icon/annotation-icon.component.ts +++ b/apps/red-ui/src/app/components/annotation-icon/annotation-icon.component.ts @@ -11,5 +11,17 @@ export class AnnotationIconComponent implements OnInit { constructor() {} - ngOnInit(): void {} + ngOnInit(): void { + if (this.typeValue?.type === 'request') { + let styleSheet = document.styleSheets[0]; + styleSheet.insertRule( + `.request:after { border-top-color: ${this.typeValue.hexColor} !important; }`, + styleSheet.cssRules.length + ); + styleSheet.insertRule( + `.request { border-bottom-color: ${this.typeValue.hexColor} !important; }`, + styleSheet.cssRules.length + ); + } + } } From 4ce1592207baa6a7beab17dea867e86b5f3d176c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adina=20=C8=9Aeudan?= Date: Fri, 6 Nov 2020 00:06:23 +0200 Subject: [PATCH 2/3] Refactor filters --- .../app/common/filter/utils/filter-utils.ts | 59 ++++++++++++++ .../annotation-icon.component.ts | 2 +- apps/red-ui/src/app/icons/icons.module.ts | 1 + .../project-listing-screen.component.ts | 63 ++++----------- .../project-overview-screen.component.html | 7 ++ .../project-overview-screen.component.ts | 76 ++++++++----------- apps/red-ui/src/assets/i18n/en.json | 9 ++- .../src/assets/icons/general/needs-work.svg | 7 ++ 8 files changed, 129 insertions(+), 95 deletions(-) create mode 100644 apps/red-ui/src/assets/icons/general/needs-work.svg diff --git a/apps/red-ui/src/app/common/filter/utils/filter-utils.ts b/apps/red-ui/src/app/common/filter/utils/filter-utils.ts index 31e40ef20..868ccddd7 100644 --- a/apps/red-ui/src/app/common/filter/utils/filter-utils.ts +++ b/apps/red-ui/src/app/common/filter/utils/filter-utils.ts @@ -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; +} diff --git a/apps/red-ui/src/app/components/annotation-icon/annotation-icon.component.ts b/apps/red-ui/src/app/components/annotation-icon/annotation-icon.component.ts index 18d7351d8..ec379aa5e 100644 --- a/apps/red-ui/src/app/components/annotation-icon/annotation-icon.component.ts +++ b/apps/red-ui/src/app/components/annotation-icon/annotation-icon.component.ts @@ -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 diff --git a/apps/red-ui/src/app/icons/icons.module.ts b/apps/red-ui/src/app/icons/icons.module.ts index 81d515fe0..b09c0760f 100644 --- a/apps/red-ui/src/app/icons/icons.module.ts +++ b/apps/red-ui/src/app/icons/icons.module.ts @@ -31,6 +31,7 @@ export class IconsModule { 'lightning', 'logout', 'menu', + 'needs-work', 'pages', 'plus', 'preview', diff --git a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.ts b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.ts index 7771d3457..3cc12487d 100644 --- a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.ts +++ b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.ts @@ -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); diff --git a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html index db7f5cea1..ac2f72043 100644 --- a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html +++ b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html @@ -34,6 +34,13 @@ [hasArrow]="false" [icon]="'red:calendar'" > + diff --git a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.ts b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.ts index a109f4bcb..7c83dca71 100644 --- a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.ts +++ b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.ts @@ -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(); const allDistinctPeople = new Set(); const allDistinctAddedDates = new Set(); + const allDistinctNeedsWork = new Set(); // 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(() => { diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json index 8f5e5094d..133db5695 100644 --- a/apps/red-ui/src/assets/i18n/en.json +++ b/apps/red-ui/src/assets/i18n/en.json @@ -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", diff --git a/apps/red-ui/src/assets/icons/general/needs-work.svg b/apps/red-ui/src/assets/icons/general/needs-work.svg new file mode 100644 index 000000000..9f85f65ce --- /dev/null +++ b/apps/red-ui/src/assets/icons/general/needs-work.svg @@ -0,0 +1,7 @@ + + + Needs work + + + + From 91b4e77c2ed51adcffa4c7f0c46d8ce7c36e4bcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adina=20=C8=9Aeudan?= Date: Fri, 6 Nov 2020 00:30:09 +0200 Subject: [PATCH 3/3] Needs work filter --- .../app/common/filter/utils/filter-utils.ts | 25 +++++++++++++------ .../project-listing-screen.component.html | 2 +- .../project-overview-screen.component.html | 3 +-- .../project-overview-screen.component.ts | 2 +- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/apps/red-ui/src/app/common/filter/utils/filter-utils.ts b/apps/red-ui/src/app/common/filter/utils/filter-utils.ts index 868ccddd7..71141a64d 100644 --- a/apps/red-ui/src/app/common/filter/utils/filter-utils.ts +++ b/apps/red-ui/src/app/common/filter/utils/filter-utils.ts @@ -16,18 +16,29 @@ export function handleCheckedValue(filter: FilterModel) { } } -export function checkFilter(entity: any, filters: FilterModel[], validate: Function) { +export function checkFilter( + entity: any, + filters: FilterModel[], + validate: Function, + matchAll: boolean = false +) { const hasChecked = filters.find((f) => f.checked); + if (!hasChecked) { return true; } - let filterMatched = false; + + let filterMatched = matchAll; for (const filter of filters) { - if (filter.checked && validate(entity, filter)) { - filterMatched = true; - break; + if (filter.checked) { + if (matchAll) { + filterMatched = filterMatched && validate(entity, filter); + } else { + filterMatched = filterMatched || validate(entity, filter); + } } } + return filterMatched; } @@ -57,13 +68,13 @@ export const addedDateChecker = (pw: ProjectWrapper, filter: FilterModel) => export function getFilteredEntities( entities: any[], - filters: { values: FilterModel[]; checker: Function }[] + filters: { values: FilterModel[]; checker: Function; matchAll?: boolean }[] ) { const filteredEntities = []; for (const entity of entities) { let add = true; for (const filter of filters) { - add = add && checkFilter(entity, filter.values, filter.checker); + add = add && checkFilter(entity, filter.values, filter.checker, filter.matchAll); } if (add) { filteredEntities.push(entity); diff --git a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.html b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.html index 1eef5059f..eaff2adb0 100644 --- a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.html +++ b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.html @@ -62,7 +62,7 @@ {{ 'project-listing.table-header.title' - | translate: { length: appStateService.allProjects?.length || 0 } + | translate: { length: displayedProjects.length || 0 } }}
diff --git a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html index ac2f72043..a3ff90a56 100644 --- a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html +++ b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html @@ -80,8 +80,7 @@ {{ 'project-overview.table-header.title' - | translate - : { length: appStateService.activeProject?.files.length || 0 } + | translate: { length: displayedFiles.length || 0 } }} diff --git a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.ts b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.ts index 7c83dca71..5be518bcc 100644 --- a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.ts +++ b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.ts @@ -333,7 +333,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy { { values: this.statusFilters, checker: keyChecker('status') }, { values: this.peopleFilters, checker: keyChecker('currentReviewer') }, { values: this.addedDateFilters, checker: fileAddedFilterChecker }, - { values: this.needsWorkFilters, checker: annotationFilterChecker } + { values: this.needsWorkFilters, checker: annotationFilterChecker, matchAll: true } ]; this.displayedFiles = getFilteredEntities( this.appStateService.activeProject.files,