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 8942631b6..e5cb6a1dd 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,8 +1,10 @@
import { FilterModel } from '../model/filter.model';
import { FileStatusWrapper } from '../../../screens/file/model/file-status.wrapper';
import { ProjectWrapper } from '../../../state/model/project.wrapper';
+import { PermissionsService } from '../../service/permissions.service';
export const RedactionFilterSorter = {
+ analysis: 0,
hint: 1,
redaction: 2,
suggestion: 3,
@@ -22,9 +24,16 @@ export function handleCheckedValue(filter: FilterModel) {
}
}
-export function checkFilter(entity: any, filters: FilterModel[], validate: Function, matchAll: boolean = false) {
+export function checkFilter(entity: any, filters: FilterModel[], validate: Function, validateArgs: any = [], matchAll: boolean = false) {
const hasChecked = filters.find((f) => f.checked);
+ if (validateArgs) {
+ if (!Array.isArray(validateArgs)) {
+ validateArgs = [validateArgs];
+ }
+ } else {
+ validateArgs = [];
+ }
if (!hasChecked) {
return true;
}
@@ -33,9 +42,9 @@ export function checkFilter(entity: any, filters: FilterModel[], validate: Funct
for (const filter of filters) {
if (filter.checked) {
if (matchAll) {
- filterMatched = filterMatched && validate(entity, filter);
+ filterMatched = filterMatched && validate(entity, filter, ...validateArgs);
} else {
- filterMatched = filterMatched || validate(entity, filter);
+ filterMatched = filterMatched || validate(entity, filter, ...validateArgs);
}
}
}
@@ -45,19 +54,26 @@ export function checkFilter(entity: any, filters: FilterModel[], validate: Funct
export const keyChecker = (key: string) => (entity: any, filter: FilterModel) => entity[key] === filter.key;
-export const annotationFilterChecker = (f: FileStatusWrapper, filter: FilterModel) => {
+export const annotationFilterChecker = (input: FileStatusWrapper | ProjectWrapper, filter: FilterModel, permissionsService: PermissionsService) => {
switch (filter.key) {
+ case 'analysis': {
+ if (input instanceof ProjectWrapper) {
+ return permissionsService.projectReanalysisRequired(input);
+ } else {
+ return permissionsService.fileRequiresReanalysis(input);
+ }
+ }
case 'suggestion': {
- return f.hasRequests;
+ return input.hasRequests;
}
case 'redaction': {
- return f.hasRedactions;
+ return input.hasRedactions;
}
case 'hint': {
- return f.hintsOnly;
+ return input.hintsOnly;
}
case 'none': {
- return f.hasNone;
+ return input.hasNone;
}
}
};
@@ -72,12 +88,12 @@ export const dueDateChecker = (pw: ProjectWrapper, filter: FilterModel) => pw.du
export const addedDateChecker = (pw: ProjectWrapper, filter: FilterModel) => pw.addedDateMatches(filter.key);
-export function getFilteredEntities(entities: any[], filters: { values: FilterModel[]; checker: Function; matchAll?: boolean }[]) {
+export function getFilteredEntities(entities: any[], filters: { values: FilterModel[]; checker: Function; matchAll?: boolean; checkerArgs?: any }[]) {
const filteredEntities = [];
for (const entity of entities) {
let add = true;
for (const filter of filters) {
- add = add && checkFilter(entity, filter.values, filter.checker, filter.matchAll);
+ add = add && checkFilter(entity, filter.values, filter.checker, filter.checkerArgs, filter.matchAll);
}
if (add) {
filteredEntities.push(entity);
diff --git a/apps/red-ui/src/app/common/service/permissions.service.ts b/apps/red-ui/src/app/common/service/permissions.service.ts
index 85fece71a..700ecd774 100644
--- a/apps/red-ui/src/app/common/service/permissions.service.ts
+++ b/apps/red-ui/src/app/common/service/permissions.service.ts
@@ -3,6 +3,7 @@ import { AppStateService } from '../../state/app-state.service';
import { UserService, UserWrapper } from '../../user/user.service';
import { FileStatusWrapper } from '../../screens/file/model/file-status.wrapper';
import { Project, User } from '@redaction/red-ui-http';
+import { ProjectWrapper } from '../../state/model/project.wrapper';
@Injectable({
providedIn: 'root'
@@ -22,6 +23,15 @@ export class PermissionsService {
return this.isFileReviewer(fileStatus) || this.isManagerAndOwner();
}
+ projectReanalysisRequired(project?: ProjectWrapper) {
+ for (let file of project.files) {
+ const fileReanalysisRequired = this.fileRequiresReanalysis(file);
+ if (fileReanalysisRequired) {
+ return true;
+ }
+ }
+ }
+
fileRequiresReanalysis(fileStatus?: FileStatusWrapper) {
if (!fileStatus) {
fileStatus = this._appStateService.activeFile;
diff --git a/apps/red-ui/src/app/screens/common/needs-work-badge/needs-work-badge.component.html b/apps/red-ui/src/app/screens/common/needs-work-badge/needs-work-badge.component.html
index 07dccc917..3159e57c2 100644
--- a/apps/red-ui/src/app/screens/common/needs-work-badge/needs-work-badge.component.html
+++ b/apps/red-ui/src/app/screens/common/needs-work-badge/needs-work-badge.component.html
@@ -1,4 +1,5 @@
+
-
+
{{
appStateService.activeFile.filename
}}
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 af966198f..b3533179a 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
@@ -167,10 +167,11 @@ export class ProjectListingScreenComponent implements OnInit {
// Needs work
entry.files.forEach((file) => {
- if (file.hintsOnly) allDistinctNeedsWork.add('hint');
- if (file.hasRedactions) allDistinctNeedsWork.add('redaction');
- if (file.hasRequests) allDistinctNeedsWork.add('suggestion');
- if (file.hasNone) allDistinctNeedsWork.add('none');
+ if (this.permissionsService.fileRequiresReanalysis(file)) allDistinctNeedsWork.add('analysis');
+ if (entry.hintsOnly) allDistinctNeedsWork.add('hint');
+ if (entry.hasRedactions) allDistinctNeedsWork.add('redaction');
+ if (entry.hasRequests) allDistinctNeedsWork.add('suggestion');
+ if (entry.hasNone) allDistinctNeedsWork.add('none');
});
});
@@ -226,7 +227,7 @@ export class ProjectListingScreenComponent implements OnInit {
{ values: this.statusFilters, checker: projectStatusChecker },
{ values: this.peopleFilters, checker: projectMemberChecker },
{ values: this.dueDateFilters, checker: dueDateChecker },
- { values: this.needsWorkFilters, checker: annotationFilterChecker, matchAll: true }
+ { values: this.needsWorkFilters, checker: annotationFilterChecker, matchAll: true, checkerArgs: this.permissionsService }
];
this.detailsContainerFilters = {
statusFilters: this.statusFilters.map((f) => ({ ...f }))
diff --git a/apps/red-ui/src/app/screens/project-overview-screen/project-details/project-details.component.html b/apps/red-ui/src/app/screens/project-overview-screen/project-details/project-details.component.html
index 6c96306b0..32776a8f7 100644
--- a/apps/red-ui/src/app/screens/project-overview-screen/project-details/project-details.component.html
+++ b/apps/red-ui/src/app/screens/project-overview-screen/project-details/project-details.component.html
@@ -53,7 +53,7 @@
- {{ 'project-overview.legend.' + filter.key | translate }}
+ {{ 'filter.' + filter.key | translate }}
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 da8548eea..7b6793b1b 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
@@ -122,7 +122,7 @@
{{ fileStatus.filename }}
-
+
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 eae2c23c0..f103d9f35 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
@@ -191,6 +191,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
// Needs work
this.appStateService.activeProject.files.forEach((file) => {
+ if (this.permissionsService.fileRequiresReanalysis(file)) allDistinctNeedsWork.add('analysis');
if (file.hintsOnly) allDistinctNeedsWork.add('hint');
if (file.hasRedactions) allDistinctNeedsWork.add('redaction');
if (file.hasRequests) allDistinctNeedsWork.add('suggestion');
@@ -250,7 +251,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
const filters = [
{ values: this.statusFilters, checker: keyChecker('status') },
{ values: this.peopleFilters, checker: keyChecker('currentReviewer') },
- { values: this.needsWorkFilters, checker: annotationFilterChecker, matchAll: true }
+ { values: this.needsWorkFilters, checker: annotationFilterChecker, matchAll: true, checkerArgs: this.permissionsService }
];
this.displayedFiles = getFilteredEntities(this.appStateService.activeProject.files, filters);
this.detailsContainerFilters = {
diff --git a/apps/red-ui/src/app/state/app-state.service.ts b/apps/red-ui/src/app/state/app-state.service.ts
index 3d71c83ce..f131e5ab0 100644
--- a/apps/red-ui/src/app/state/app-state.service.ts
+++ b/apps/red-ui/src/app/state/app-state.service.ts
@@ -390,6 +390,11 @@ export class AppStateService {
type: 'add',
virtual: true
};
+ this._dictionaryData['analysis'] = {
+ hexColor: '#dd4d50',
+ type: 'analysis',
+ virtual: true
+ };
})
);
diff --git a/apps/red-ui/src/app/state/model/project.wrapper.ts b/apps/red-ui/src/app/state/model/project.wrapper.ts
index 5f8374161..3297d2c0f 100644
--- a/apps/red-ui/src/app/state/model/project.wrapper.ts
+++ b/apps/red-ui/src/app/state/model/project.wrapper.ts
@@ -8,6 +8,7 @@ export class ProjectWrapper {
hintsOnly?: boolean;
hasRedactions?: boolean;
hasRequests?: boolean;
+ hasNone?: boolean;
allFilesApproved?: boolean;
@@ -63,6 +64,7 @@ export class ProjectWrapper {
this.hintsOnly = false;
this.hasRedactions = false;
this.hasRequests = false;
+ this.hasNone = false;
this.allFilesApproved = true;
this._files.forEach((f) => {
this.hintsOnly = this.hintsOnly || f.hintsOnly;
@@ -70,5 +72,6 @@ export class ProjectWrapper {
this.hasRequests = this.hasRequests || f.hasRequests;
this.allFilesApproved = this.allFilesApproved && f.isApproved;
});
+ this.hasNone = !this.hasRequests && !this.hasRedactions && !this.hintsOnly;
}
}
diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json
index 68f7191bf..76e93226f 100644
--- a/apps/red-ui/src/assets/i18n/en.json
+++ b/apps/red-ui/src/assets/i18n/en.json
@@ -239,13 +239,7 @@
},
"header": "Project Overview",
"upload-document": "Upload Document",
- "no-project": "Requested project: {{projectId}} does not exist! Back to Project Listing. ",
- "legend": {
- "hint": "Hints only",
- "redaction": "Redacted",
- "suggestion": "Suggested Redaction",
- "none": "No Annotations"
- }
+ "no-project": "Requested project: {{projectId}} does not exist! Back to Project Listing. "
},
"file-preview": {
"show-redacted-view": "Show Redacted Preview",
@@ -317,6 +311,7 @@
"hint": "Hints only",
"redaction": "Redacted",
"suggestion": "Suggested Redaction",
+ "analysis": "Re-analysis required",
"none": "No Annotations"
},
"annotation-filter": {