Fixed some issues

This commit is contained in:
Timo Bejan 2020-11-16 10:22:44 +02:00
parent 3eca36ec16
commit fb2d71cc87
18 changed files with 144 additions and 86 deletions

View File

@ -9,7 +9,7 @@ import { MatMenu, MatMenuTrigger } from '@angular/material/menu';
templateUrl: './filter.component.html',
styleUrls: ['./filter.component.scss']
})
export class FilterComponent implements OnChanges {
export class FilterComponent {
@Input() filterTemplate: TemplateRef<any>;
@Output() filtersChanged = new EventEmitter<FilterModel[]>();
@Input() filters: FilterModel[] = [];
@ -24,31 +24,6 @@ export class FilterComponent implements OnChanges {
constructor(public readonly appStateService: AppStateService, private readonly _changeDetectorRef: ChangeDetectorRef) {}
ngOnChanges(changes: SimpleChanges): void {
if (changes.filters) {
const oldFilters = changes.filters.previousValue;
this._copySettings(oldFilters, this.filters);
if (this.filters) {
this.filters.forEach((filter) => {
handleCheckedValue(filter);
});
}
}
}
private _copySettings(oldFilters: FilterModel[], newFilters: FilterModel[]) {
if (oldFilters && newFilters) {
for (const oldFilter of oldFilters) {
const newFilter = newFilters.find((f) => f.key === oldFilter.key);
if (newFilter) {
newFilter.checked = oldFilter.checked;
newFilter.indeterminate = oldFilter.indeterminate;
this._copySettings(oldFilter.filters, newFilter.filters);
}
}
}
}
filterCheckboxClicked($event: any, filter: FilterModel, parent?: FilterModel) {
filter.checked = !filter.checked;
if (parent) {

View File

@ -11,6 +11,29 @@ export const RedactionFilterSorter = {
none: 4
};
export function processFilters(oldFilters: FilterModel[], newFilters: FilterModel[]) {
copySettings(oldFilters, newFilters);
if (newFilters) {
newFilters.forEach((filter) => {
handleCheckedValue(filter);
});
}
return newFilters;
}
function copySettings(oldFilters: FilterModel[], newFilters: FilterModel[]) {
if (oldFilters && newFilters) {
for (const oldFilter of oldFilters) {
const newFilter = newFilters.find((f) => f.key === oldFilter.key);
if (newFilter) {
newFilter.checked = oldFilter.checked;
newFilter.indeterminate = oldFilter.indeterminate;
copySettings(oldFilter.filters, newFilter.filters);
}
}
}
}
export function handleCheckedValue(filter: FilterModel) {
if (filter.filters && filter.filters.length) {
filter.checked = filter.filters.reduce((acc, next) => acc && next.checked, true);

View File

@ -53,7 +53,8 @@ export class PermissionsService {
if (!fileStatus) {
fileStatus = this._appStateService.activeFile;
}
return (this.fileRequiresReanalysis(fileStatus) && this.isReviewerOrOwner(fileStatus)) || (fileStatus.isError && fileStatus.isUnassigned);
return true;
// return (this.fileRequiresReanalysis(fileStatus) && this.isReviewerOrOwner(fileStatus)) || (fileStatus.isError && fileStatus.isUnassigned);
}
isFileReviewer(fileStatus?: FileStatusWrapper) {

View File

@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { FileDetailsDialogComponent } from './file-details-dialog/file-details-dialog.component';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { FileStatus, FileUploadControllerService, ManualRedactionControllerService, Project } from '@redaction/red-ui-http';
import { ConfirmationDialogComponent } from '../common/confirmation-dialog/confirmation-dialog.component';
import { ConfirmationDialogComponent, ConfirmationDialogInput } from '../common/confirmation-dialog/confirmation-dialog.component';
import { NotificationService, NotificationType } from '../notification/notification.service';
import { TranslateService } from '@ngx-translate/core';
import { AppStateService } from '../state/app-state.service';
@ -44,7 +44,13 @@ export class DialogService {
public openDeleteFilesDialog($event: MouseEvent, projectId: string, fileIds: string[], cb?: Function): MatDialogRef<ConfirmationDialogComponent> {
$event?.stopPropagation();
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
const ref = this._dialog.open(ConfirmationDialogComponent, {
...dialogConfig,
data: new ConfirmationDialogInput({
title: 'confirmation-dialog.delete-file.title',
question: 'confirmation-dialog.delete-file.question'
})
});
ref.afterClosed().subscribe((result) => {
if (result) {
@ -118,13 +124,19 @@ export class DialogService {
return ref;
}
public openEditProjectDialog($event: MouseEvent, project: Project): MatDialogRef<AddEditProjectDialogComponent> {
public openEditProjectDialog($event: MouseEvent, project: Project, cb?: Function): MatDialogRef<AddEditProjectDialogComponent> {
$event.stopPropagation();
return this._dialog.open(AddEditProjectDialogComponent, {
const ref = this._dialog.open(AddEditProjectDialogComponent, {
...dialogConfig,
autoFocus: true,
data: project
});
ref.afterClosed().subscribe(async (result) => {
if (result) {
if (cb) cb();
}
});
return ref;
}
public openDeleteProjectDialog($event: MouseEvent, project: Project, cb?: Function): MatDialogRef<ConfirmationDialogComponent> {

View File

@ -16,7 +16,8 @@ export class LanguageService {
chooseAndSetInitialLanguage() {
let defaultLang: string;
const localStorageLang = localStorage.getItem('redaction.language');
const browserLang = this.translateService.getBrowserLang();
// const browserLang = this.translateService.getBrowserLang();
const browserLang = 'en'; // Force language to english until translations are ready
// @ts-ignore
if (this.translateService.getLangs().includes(localStorageLang)) {
defaultLang = localStorageLang;

View File

@ -7,13 +7,13 @@
.breadcrumb {
text-decoration: none;
color: $accent;
color: $grey-7;
font-weight: 600;
width: fit-content;
white-space: nowrap;
&:last-child {
color: $primary;
color: $accent;
@include line-clamp(1);
}

View File

@ -1,17 +1,32 @@
<div [class.visible]="menuOpen" *ngIf="canPerformAnnotationActions" class="annotation-actions">
<button (click)="acceptSuggestion($event, annotation)" mat-icon-button *ngIf="canAcceptSuggestion">
<button
(click)="acceptSuggestion($event, annotation)"
mat-icon-button
*ngIf="canAcceptSuggestion"
[matTooltip]="'annotation-actions.accept-suggestion.label' | translate"
>
<mat-icon svgIcon="red:check-alt"></mat-icon>
</button>
<button (click)="undoDirectAction($event, annotation)" *ngIf="canUndoAnnotation" mat-icon-button>
<mat-icon svgIcon="red:undo"></mat-icon>
<mat-icon svgIcon="red:undo" [matTooltip]="'annotation-actions.undo' | translate"></mat-icon>
</button>
<button (click)="rejectSuggestion($event, annotation)" mat-icon-button *ngIf="canRejectSuggestion">
<button
(click)="rejectSuggestion($event, annotation)"
mat-icon-button
*ngIf="canRejectSuggestion"
[matTooltip]="'annotation-actions.reject-suggestion' | translate"
>
<mat-icon svgIcon="red:close"></mat-icon>
</button>
<button (click)="suggestRemoveAnnotation($event, annotation, true)" mat-icon-button *ngIf="canDirectlySuggestToRemoveAnnotation">
<button
(click)="suggestRemoveAnnotation($event, annotation, true)"
mat-icon-button
*ngIf="canDirectlySuggestToRemoveAnnotation"
[matTooltip]="'annotation-actions.suggest-remove-annotation' | translate"
>
<mat-icon svgIcon="red:trash"></mat-icon>
</button>
@ -19,20 +34,21 @@
(click)="openMenu($event)"
[class.active]="menuOpen"
[matMenuTriggerFor]="menu"
[matTooltip]="'annotation-actions.suggest-remove-annotation' | translate"
class="confirm"
mat-icon-button
*ngIf="requiresSuggestionRemoveMenu"
>
<mat-icon svgIcon="red:close"></mat-icon>
<mat-icon svgIcon="red:trash"></mat-icon>
</button>
<mat-menu #menu="matMenu" (closed)="onMenuClosed()" xPosition="before">
<div (click)="suggestRemoveAnnotation($event, annotation, true)" mat-menu-item>
<redaction-annotation-icon [type]="'rhombus'" [label]="'S'" [color]="dictionaryColor"></redaction-annotation-icon>
<div [translate]="'file-preview.tabs.annotations.remove-annotation.remove-from-dict'"></div>
<div [translate]="'annotation-actions.remove-annotation.remove-from-dict'"></div>
</div>
<div (click)="suggestRemoveAnnotation($event, annotation, false)" mat-menu-item>
<redaction-annotation-icon [type]="'rhombus'" [label]="'S'" [color]="suggestionColor"></redaction-annotation-icon>
<div translate="file-preview.tabs.annotations.remove-annotation.only-here"></div>
<div translate="annotation-actions.remove-annotation.only-here"></div>
</div>
</mat-menu>
</div>

View File

@ -106,4 +106,8 @@ export class AnnotationActionsComponent implements OnInit {
get dictionaryColor() {
return this.appStateService.getDictionaryColor('suggestion-add-dictionary');
}
get suggestRemoveLabel() {
return 'suggestRemoveLabel';
}
}

View File

@ -40,7 +40,11 @@
<div class="right-fixed-container">
<div class="right-title heading" translate="file-preview.tabs.annotations.label">
<redaction-filter (filtersChanged)="filtersChanged($event)" [filterTemplate]="annotationFilterTemplate" [filters]="filters"></redaction-filter>
<redaction-filter
(filtersChanged)="filtersChanged($event)"
[filterTemplate]="annotationFilterTemplate"
[filters]="annotationFilters"
></redaction-filter>
</div>
<div class="right-content">

View File

@ -23,6 +23,7 @@ import { TranslateService } from '@ngx-translate/core';
import { FileStatusWrapper } from '../model/file-status.wrapper';
import { PermissionsService } from '../../../common/service/permissions.service';
import { Subscription, timer } from 'rxjs';
import { processFilters } from '../../../common/filter/utils/filter-utils';
const KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
@ -48,7 +49,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
selectedAnnotation: AnnotationWrapper;
pagesPanelActive = true;
viewReady = false;
filters: FilterModel[];
annotationFilters: FilterModel[];
loadingMessage: string;
canPerformAnnotationActions: boolean;
@ -160,8 +161,9 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
this.activeViewer.annotManager.deleteAnnotations(existingAnnotations, true, true);
}
this.annotations = this.fileData.getAnnotations(this.appStateService.dictionaryData, this.permissionsService.currentUser);
this.filters = this._annotationProcessingService.getAnnotationFilter(this.annotations);
this.filtersChanged(this.filters);
const annotationFilters = this._annotationProcessingService.getAnnotationFilter(this.annotations);
this.annotationFilters = processFilters(this.annotationFilters, annotationFilters);
this.filtersChanged(this.annotationFilters);
}
handleAnnotationSelected(annotationId: string) {

View File

@ -1,6 +1,7 @@
import { Comment, IdRemoval, ManualRedactionEntry, Point, Rectangle, RedactionLogEntry, TypeValue } from '@redaction/red-ui-http';
import { UserWrapper } from '../../../user/user.service';
import { FileStatusWrapper } from './file-status.wrapper';
import { humanize } from '../../../utils/functions';
export const SuperTypeSorter = {
'add-dictionary': 2,
@ -149,7 +150,11 @@ export class AnnotationWrapper {
AnnotationWrapper._setSuperType(annotationWrapper, redactionLogEntry, manualRedactionEntry, idRemoval);
annotationWrapper.typeLabel = 'annotation-type.' + annotationWrapper.superType;
if (annotationWrapper.superType === 'hint' || annotationWrapper.superType === 'redaction') {
annotationWrapper.typeLabel = humanize(annotationWrapper.dictionary);
} else {
annotationWrapper.typeLabel = 'annotation-type.' + annotationWrapper.superType;
}
return annotationWrapper;
}

View File

@ -48,7 +48,7 @@ export class FileStatusWrapper {
}
get hasRequests() {
return this.fileStatus.hasRequests;
return this.fileStatus.hasRequests || this.hasUnappliedSuggestions;
}
get hasNone() {

View File

@ -11,6 +11,7 @@ import {
annotationFilterChecker,
dueDateChecker,
getFilteredEntities,
processFilters,
projectMemberChecker,
projectStatusChecker,
RedactionFilterSorter
@ -38,6 +39,8 @@ export class ProjectListingScreenComponent implements OnInit, OnDestroy {
public detailsContainerFilters: {
statusFilters: FilterModel[];
} = {
statusFilters: []
};
public displayedProjects: ProjectWrapper[] = [];
@ -155,9 +158,11 @@ export class ProjectListingScreenComponent implements OnInit, OnDestroy {
.map((status) => ({ length: obj[status], color: status }));
}
public reanalyseProject($event: MouseEvent, project: Project) {
async reanalyseProject($event: MouseEvent, project: Project) {
$event.stopPropagation();
this.appStateService.reanalyseProject(project);
await this.appStateService.reanalyzeProject(project);
await this.appStateService.loadAllProjects();
this._calculateData();
}
private _computeAllFilters() {
@ -187,29 +192,32 @@ export class ProjectListingScreenComponent implements OnInit, OnDestroy {
});
});
this.statusFilters = [];
const statusFilters = [];
allDistinctFileStatus.forEach((status) => {
this.statusFilters.push({
statusFilters.push({
key: status,
label: humanize(status)
});
});
this.statusFilters = processFilters(this.statusFilters, statusFilters);
this.peopleFilters = [];
const peopleFilters = [];
allDistinctPeople.forEach((userId) => {
this.peopleFilters.push({
peopleFilters.push({
key: userId,
label: this.userService.getNameForId(userId)
});
});
this.peopleFilters = processFilters(this.peopleFilters, peopleFilters);
this.dueDateFilters = [];
const dueDateFilters = [];
allDistinctDueDates.forEach((date) => {
this.dueDateFilters.push({
dueDateFilters.push({
key: date,
label: date
});
});
this.dueDateFilters = processFilters(this.dueDateFilters, dueDateFilters);
const needsWorkFilters = [];
allDistinctNeedsWork.forEach((type) => {
@ -220,7 +228,7 @@ export class ProjectListingScreenComponent implements OnInit, OnDestroy {
});
needsWorkFilters.sort((a, b) => RedactionFilterSorter[a.key] - RedactionFilterSorter[b.key]);
this.needsWorkFilters = needsWorkFilters;
this.needsWorkFilters = processFilters(this.needsWorkFilters, needsWorkFilters);
}
filtersChanged(filters?: { [key: string]: FilterModel[] }): void {
@ -249,6 +257,8 @@ export class ProjectListingScreenComponent implements OnInit, OnDestroy {
}
openEditProjectDialog($event: MouseEvent, project: Project) {
this._dialogService.openEditProjectDialog($event, project);
this._dialogService.openEditProjectDialog($event, project, () => {
this._calculateData();
});
}
}

View File

@ -55,7 +55,6 @@ export class ProjectDetailsComponent implements OnInit {
this._dialogService.openAssignProjectMembersAndOwnerDialog(null, this.appStateService.activeProject.project, () => {
this.reloadProjects.emit();
this._changeDetectorRef.detectChanges();
console.log(this.appStateService.activeProject);
});
}

View File

@ -201,7 +201,7 @@
<div class="right-fixed-container">
<redaction-project-details
#projectDetailsComponent
(reloadProjects)="reloadProjects()"
(reloadProjects)="calculateData()"
[filters]="detailsContainerFilters"
(filtersChanged)="filtersChanged($event)"
></redaction-project-details>

View File

@ -14,7 +14,7 @@ import { FilterModel } from '../../common/filter/model/filter.model';
import * as moment from 'moment';
import { ProjectDetailsComponent } from './project-details/project-details.component';
import { FileStatusWrapper } from '../file/model/file-status.wrapper';
import { annotationFilterChecker, getFilteredEntities, keyChecker, RedactionFilterSorter } from '../../common/filter/utils/filter-utils';
import { annotationFilterChecker, getFilteredEntities, keyChecker, processFilters, RedactionFilterSorter } from '../../common/filter/utils/filter-utils';
import { SortingOption, SortingService } from '../../utils/sorting.service';
import { PermissionsService } from '../../common/service/permissions.service';
import { UserService } from '../../user/user.service';
@ -38,7 +38,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
detailsContainerFilters: {
needsWorkFilters: FilterModel[];
statusFilters: FilterModel[];
};
} = { needsWorkFilters: [], statusFilters: [] };
@ViewChild('projectDetailsComponent', { static: false })
private _projectDetailsComponent: ProjectDetailsComponent;
@ -104,11 +104,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
actions: [
{
title: this._translateService.instant('project-overview.new-rule.toast.actions.reanalyse-all'),
action: () =>
this.appStateService
.reanalyzeProject()
.toPromise()
.then(() => this.reloadProjects())
action: () => this.appStateService.reanalyzeProject().then(() => this.reloadProjects())
},
{
title: this._translateService.instant('project-overview.new-rule.toast.actions.later')
@ -226,26 +222,28 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
if (file.hasNone) allDistinctNeedsWork.add('none');
});
this.statusFilters = [];
const statusFilters = [];
allDistinctFileStatusWrapper.forEach((status) => {
this.statusFilters.push({
statusFilters.push({
key: status,
label: humanize(status)
});
});
this.statusFilters = processFilters(this.statusFilters, statusFilters);
this.peopleFilters = [];
const peopleFilters = [];
if (allDistinctPeople.has(undefined) || allDistinctPeople.has(null)) {
allDistinctPeople.delete(undefined);
allDistinctPeople.delete(null);
allDistinctPeople.add(null);
}
allDistinctPeople.forEach((userId) => {
this.peopleFilters.push({
peopleFilters.push({
key: userId,
label: userId ? this.userService.getNameForId(userId) : this._translateService.instant('initials-avatar.unassigned')
});
});
this.peopleFilters = processFilters(this.peopleFilters, peopleFilters);
const needsWorkFilters = [];
allDistinctNeedsWork.forEach((type) => {
@ -255,7 +253,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
});
});
needsWorkFilters.sort((a, b) => RedactionFilterSorter[a.key] - RedactionFilterSorter[b.key]);
this.needsWorkFilters = needsWorkFilters;
this.needsWorkFilters = processFilters(this.needsWorkFilters, needsWorkFilters);
}
filtersChanged(filters?: { [key: string]: FilterModel[] }): void {
@ -279,7 +277,12 @@ 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, checkerArgs: this.permissionsService }
{
values: this.needsWorkFilters,
checker: annotationFilterChecker,
matchAll: true,
checkerArgs: this.permissionsService
}
];
this.displayedFiles = getFilteredEntities(this.appStateService.activeProject.files, filters);
this.detailsContainerFilters = {

View File

@ -85,13 +85,6 @@ export class AppStateService {
return this._dictionaryData;
}
reanalyzeProject(project?: Project) {
if (!project) {
project = this.activeProject.project;
}
return this._reanalysisControllerService.reanalyzeProject(project.projectId);
}
getDictionaryColor(type?: string) {
const color = this._dictionaryData[type]?.hexColor;
return color ? color : this._dictionaryData['default'].hexColor;
@ -233,11 +226,11 @@ export class AppStateService {
return files;
}
reanalyseProject(project?: Project) {
async reanalyzeProject(project?: Project) {
if (!project) {
project = this.activeProject.project;
}
this._reanalysisControllerService.reanalyzeProject(project.projectId).subscribe(() => {});
await this._reanalysisControllerService.reanalyzeProject(project.projectId).toPromise();
}
downloadRedactionReport(project?: Project) {

View File

@ -85,10 +85,10 @@
},
"stats": {
"analyzed-pages": "Analyzed pages",
"total-people": "Total users",
"total-people": "Total user(s)",
"charts": {
"projects": "Projects",
"total-documents": "Total Documents"
"total-documents": "Total Document(s)"
}
},
"add-edit-dialog": {
@ -205,7 +205,7 @@
"stats": {
"documents": "{{count}} documents",
"analysed-pages": "{{count}} analyzed pages",
"people": "{{count}} people",
"people": "{{count}} user(s)",
"created-on": "Created on {{date}}",
"due-date": "Due {{date}}"
},
@ -236,17 +236,21 @@
"label": "Workload"
}
},
"reviewer": "Assignee",
"reviewer": "Assigned to",
"unassigned": "Unassigned"
},
"annotation-actions": {
"accept-suggestion": {
"label": "Approve",
"label": "Accept Suggestion",
"add-to-dict": "Approve and add to dictionary",
"remove-from-dict": "Approve and remove from dictionary",
"only-here": "Approve only here"
},
"suggest-remove-annotation": "Remove or Suggest to remove this entry",
"reject-suggestion": "Reject Suggestion",
"remove-annotation": {
"suggest-remove-from-dict": "Suggest to remove from dictionary",
"suggest-only-here": "Suggest to remove only here",
"remove-from-dict": "Remove from dictionary",
"only-here": "Remove only here"
},
@ -390,5 +394,11 @@
"success": "Redaction added.",
"error": "Failed to add redaction."
}
},
"confirmation-dialog": {
"delete-file": {
"title": "Confirm deletion",
"question": "Do you wish to proceed?"
}
}
}