added dossier dictionary dialog
This commit is contained in:
parent
8ebbf33a0d
commit
288fad9ff6
@ -3,16 +3,14 @@ import { DictionaryControllerService, TypeValue } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { NotificationService, NotificationType } from '@services/notification.service';
|
||||
import { NotificationService } from '@services/notification.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { ComponentHasChanges } from '@guards/can-deactivate.guard';
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
import { DictionaryManagerComponent } from '../../../shared/components/dictionary-manager/dictionary-manager.component';
|
||||
|
||||
const MIN_WORD_LENGTH = 2;
|
||||
import { DictionarySaveService } from '../../../shared/services/dictionary-save.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-dictionary-overview-screen',
|
||||
@ -30,6 +28,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
readonly permissionsService: PermissionsService,
|
||||
private readonly _notificationService: NotificationService,
|
||||
protected readonly _translateService: TranslateService,
|
||||
private readonly _dictionarySaveService: DictionarySaveService,
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService,
|
||||
private readonly _dialogService: AdminDialogService,
|
||||
private readonly _router: Router,
|
||||
@ -45,20 +44,6 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
this._loadEntries();
|
||||
}
|
||||
|
||||
private _loadEntries() {
|
||||
this.processing = true;
|
||||
this._dictionaryControllerService.getDictionaryForType(this.dictionary.ruleSetId, this.dictionary.type).subscribe(
|
||||
(data) => {
|
||||
this.processing = false;
|
||||
this.entries = data.entries.sort((str1, str2) => str1.localeCompare(str2, undefined, { sensitivity: 'accent' }));
|
||||
},
|
||||
() => {
|
||||
this.processing = false;
|
||||
this.entries = [];
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
get dictionary(): TypeValue {
|
||||
return this._appStateService.activeDictionary;
|
||||
}
|
||||
@ -81,52 +66,6 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
});
|
||||
}
|
||||
|
||||
async saveEntries(entries: string[]) {
|
||||
let entriesToAdd = [];
|
||||
entries.forEach((currentEntry) => {
|
||||
entriesToAdd.push(currentEntry);
|
||||
});
|
||||
// remove empty lines
|
||||
entriesToAdd = entriesToAdd.filter((e) => e && e.trim().length > 0).map((e) => e.trim());
|
||||
const invalidRowsExist = entriesToAdd.filter((e) => e.length < MIN_WORD_LENGTH);
|
||||
if (invalidRowsExist.length === 0) {
|
||||
// can add at least 1 - block UI
|
||||
this.processing = true;
|
||||
let obs: Observable<any>;
|
||||
if (entriesToAdd.length > 0) {
|
||||
obs = this._dictionaryControllerService.addEntry(entriesToAdd, this.dictionary.ruleSetId, this.dictionary.type, null, true);
|
||||
} else {
|
||||
obs = this._dictionaryControllerService.deleteEntries(this.entries, this.dictionary.ruleSetId, this.dictionary.type);
|
||||
}
|
||||
|
||||
obs.subscribe(
|
||||
() => {
|
||||
// TODO
|
||||
this._loadEntries();
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant('dictionary-overview.success.generic'),
|
||||
null,
|
||||
NotificationType.SUCCESS
|
||||
);
|
||||
},
|
||||
() => {
|
||||
this.processing = false;
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant('dictionary-overview.error.generic'),
|
||||
null,
|
||||
NotificationType.ERROR
|
||||
);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant('dictionary-overview.error.entries-too-short'),
|
||||
null,
|
||||
NotificationType.ERROR
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
download(): void {
|
||||
const content = this._dictionaryManager.editorValue;
|
||||
const blob = new Blob([content], {
|
||||
@ -147,4 +86,31 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
fileReader.readAsText(file);
|
||||
}
|
||||
}
|
||||
|
||||
saveEntries(entries: string[]) {
|
||||
this.processing = true;
|
||||
this._dictionarySaveService.saveEntries(entries, this.entries, this.dictionary.ruleSetId, this.dictionary.type, null).subscribe(
|
||||
() => {
|
||||
this.processing = false;
|
||||
this._loadEntries();
|
||||
},
|
||||
() => {
|
||||
this.processing = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private _loadEntries() {
|
||||
this.processing = true;
|
||||
this._dictionaryControllerService.getDictionaryForType(this.dictionary.ruleSetId, this.dictionary.type).subscribe(
|
||||
(data) => {
|
||||
this.processing = false;
|
||||
this.entries = data.entries.sort((str1, str2) => str1.localeCompare(str2, undefined, { sensitivity: 'accent' }));
|
||||
},
|
||||
() => {
|
||||
this.processing = false;
|
||||
this.entries = [];
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,6 +91,10 @@
|
||||
<mat-icon svgIcon="red:template"></mat-icon>
|
||||
<span>{{ appStateService.getRuleSetById(appStateService.activeProject.ruleSetId)?.name }} </span>
|
||||
</div>
|
||||
<div *ngIf="appStateService.activeProject.type" class="pointer" (click)="openDossierDictionaryDialog.emit()">
|
||||
<mat-icon svgIcon="red:dictionary"></mat-icon>
|
||||
<span>{{ 'project-overview.project-details.dictionary' | translate }} </span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!!appStateService.activeProject.project.description" class="pb-32">
|
||||
|
||||
@ -19,6 +19,7 @@ export class ProjectDetailsComponent implements OnInit {
|
||||
@Input() filters: { needsWorkFilters: FilterModel[]; statusFilters: FilterModel[] };
|
||||
@Output() filtersChanged = new EventEmitter();
|
||||
@Output() openAssignProjectMembersDialog = new EventEmitter();
|
||||
@Output() openDossierDictionaryDialog = new EventEmitter();
|
||||
@Output() toggleCollapse = new EventEmitter();
|
||||
|
||||
constructor(
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
<section class="dialog">
|
||||
<div [translate]="'dossier-dictionary-dialog.title'" class="dialog-header heading-l"></div>
|
||||
|
||||
<form (submit)="saveDossierDictionary()">
|
||||
<div class="dialog-content">
|
||||
<redaction-dictionary-manager
|
||||
#dictionaryManager
|
||||
[withFloatingActions]="false"
|
||||
[initialDictionaryEntries]="project.type?.entries"
|
||||
></redaction-dictionary-manager>
|
||||
</div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
<button [disabled]="!dictionaryManager.hasChanges" color="primary" mat-flat-button type="submit">
|
||||
{{ 'dossier-dictionary-dialog.save-changes' | translate }}
|
||||
</button>
|
||||
|
||||
<div [translate]="'dossier-dictionary-dialog.cancel'" class="all-caps-label pointer cancel" mat-dialog-close></div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
|
||||
</section>
|
||||
@ -0,0 +1,4 @@
|
||||
.dialog-content {
|
||||
height: calc(90vh - 160px);
|
||||
padding: 12px 12px 0;
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
import { Component, Inject, ViewChild } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { ProjectWrapper } from '../../../../state/model/project.wrapper';
|
||||
import { DictionaryManagerComponent } from '../../../shared/components/dictionary-manager/dictionary-manager.component';
|
||||
import { DictionarySaveService } from '../../../shared/services/dictionary-save.service';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-dossier-dictionary-dialog',
|
||||
templateUrl: './dossier-dictionary-dialog.component.html',
|
||||
styleUrls: ['./dossier-dictionary-dialog.component.scss']
|
||||
})
|
||||
export class DossierDictionaryDialogComponent {
|
||||
@ViewChild('dictionaryManager', { static: false }) private _dictionaryManager: DictionaryManagerComponent;
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<DossierDictionaryDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public project: ProjectWrapper,
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _dictionarySaveService: DictionarySaveService
|
||||
) {}
|
||||
|
||||
saveDossierDictionary() {
|
||||
this._dictionarySaveService
|
||||
.saveEntries(
|
||||
this._dictionaryManager.currentDictionaryEntries,
|
||||
this._dictionaryManager.initialDictionaryEntries,
|
||||
this.project.ruleSetId,
|
||||
'dossier_redaction',
|
||||
this.project.projectId
|
||||
)
|
||||
.subscribe(() => {
|
||||
this._appStateService.updateProjectDictionary(this.project.ruleSetId, this.project.projectId);
|
||||
this.dialogRef.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -35,6 +35,7 @@ import { ManualAnnotationService } from './services/manual-annotation.service';
|
||||
import { AnnotationDrawService } from './services/annotation-draw.service';
|
||||
import { AnnotationProcessingService } from './services/annotation-processing.service';
|
||||
import { AnnotationRemoveActionsComponent } from './components/annotation-remove-actions/annotation-remove-actions.component';
|
||||
import { DossierDictionaryDialogComponent } from './dialogs/dossier-dictionary-dialog/dossier-dictionary-dialog.component';
|
||||
|
||||
const screens = [ProjectListingScreenComponent, ProjectOverviewScreenComponent, FilePreviewScreenComponent];
|
||||
|
||||
@ -80,7 +81,7 @@ const services = [
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: [...components],
|
||||
declarations: [...components, DossierDictionaryDialogComponent],
|
||||
providers: [...services],
|
||||
imports: [CommonModule, SharedModule, FileUploadDownloadModule, ProjectsRoutingModule]
|
||||
})
|
||||
|
||||
@ -263,6 +263,7 @@
|
||||
<redaction-project-details
|
||||
#projectDetailsComponent
|
||||
(filtersChanged)="filtersChanged($event)"
|
||||
(openDossierDictionaryDialog)="openDossierDictionaryDialog()"
|
||||
(openAssignProjectMembersDialog)="openAssignProjectMembersDialog()"
|
||||
(toggleCollapse)="toggleCollapsedDetails()"
|
||||
[filters]="detailsContainerFilters"
|
||||
|
||||
@ -232,6 +232,12 @@ export class ProjectOverviewScreenComponent extends BaseListingComponent<FileSta
|
||||
});
|
||||
}
|
||||
|
||||
openDossierDictionaryDialog() {
|
||||
this._dialogService.openDossierDictionaryDialog(null, this.activeProject, () => {
|
||||
this.reloadProjects();
|
||||
});
|
||||
}
|
||||
|
||||
toggleCollapsedDetails() {
|
||||
this.collapsedDetails = !this.collapsedDetails;
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ import { ManualAnnotationService } from './manual-annotation.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { ManualAnnotationDialogComponent } from '../dialogs/manual-redaction-dialog/manual-annotation-dialog.component';
|
||||
import { AssignOwnerDialogComponent } from '../dialogs/assign-owner-dialog/assign-owner-dialog.component';
|
||||
import { DossierDictionaryDialogComponent } from '../dialogs/dossier-dictionary-dialog/dossier-dictionary-dialog.component';
|
||||
|
||||
const dialogConfig = {
|
||||
width: '662px',
|
||||
@ -201,6 +202,23 @@ export class ProjectsDialogService {
|
||||
return ref;
|
||||
}
|
||||
|
||||
openDossierDictionaryDialog($event: MouseEvent, project: ProjectWrapper, cb?: Function): MatDialogRef<DossierDictionaryDialogComponent> {
|
||||
$event?.stopPropagation();
|
||||
const ref = this._dialog.open(DossierDictionaryDialogComponent, {
|
||||
...dialogConfig,
|
||||
width: '90vw',
|
||||
height: '90vh',
|
||||
autoFocus: false,
|
||||
data: project
|
||||
});
|
||||
ref.afterClosed().subscribe((result) => {
|
||||
if (cb) {
|
||||
cb(result);
|
||||
}
|
||||
});
|
||||
return ref;
|
||||
}
|
||||
|
||||
openAssignFileReviewerDialog(file: FileStatus, cb?: Function, ignoreDialogChanges = false): MatDialogRef<AssignOwnerDialogComponent> {
|
||||
const ref = this._dialog.open(AssignOwnerDialogComponent, {
|
||||
...dialogConfig,
|
||||
|
||||
@ -73,7 +73,7 @@
|
||||
</ace-editor>
|
||||
</div>
|
||||
|
||||
<div *ngIf="hasChanges && permissionsService.isAdmin()" [class.offset]="compareForm.get('active').value" class="changes-box">
|
||||
<div *ngIf="withFloatingActions && hasChanges && permissionsService.isAdmin()" [class.offset]="compareForm.get('active').value" class="changes-box">
|
||||
<redaction-icon-button (action)="saveEntries()" icon="red:check" text="dictionary-overview.save-changes" type="primary"></redaction-icon-button>
|
||||
<div (click)="revert()" class="all-caps-label cancel" translate="dictionary-overview.revert-changes"></div>
|
||||
</div>
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
|
||||
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild } from '@angular/core';
|
||||
import { DictionaryControllerService, RuleSetModel, TypeValue } from '@redaction/red-ui-http';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { AceEditorComponent } from 'ng2-ace-editor';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { NotificationService } from '@services/notification.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { AdminDialogService } from '../../../admin/services/admin-dialog.service';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { debounce } from '@utils/debounce';
|
||||
@ -19,6 +18,9 @@ const MIN_WORD_LENGTH = 2;
|
||||
styleUrls: ['./dictionary-manager.component.scss']
|
||||
})
|
||||
export class DictionaryManagerComponent implements OnInit, OnChanges {
|
||||
@Input()
|
||||
withFloatingActions = true;
|
||||
|
||||
@Input()
|
||||
initialDictionaryEntries: string[];
|
||||
|
||||
@ -49,7 +51,6 @@ export class DictionaryManagerComponent implements OnInit, OnChanges {
|
||||
private readonly _notificationService: NotificationService,
|
||||
protected readonly _translateService: TranslateService,
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService,
|
||||
private readonly _dialogService: AdminDialogService,
|
||||
private readonly _router: Router,
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _appStateService: AppStateService,
|
||||
|
||||
@ -0,0 +1,65 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { NotificationService, NotificationType } from '../../../services/notification.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { DictionaryControllerService } from '@redaction/red-ui-http';
|
||||
import { tap } from 'rxjs/operators';
|
||||
|
||||
const MIN_WORD_LENGTH = 2;
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class DictionarySaveService {
|
||||
constructor(
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService
|
||||
) {}
|
||||
|
||||
saveEntries(entries: string[], initialEntries: string[], ruleSetId: string, type: string, dossierId: string): Observable<any> {
|
||||
let entriesToAdd = [];
|
||||
entries.forEach((currentEntry) => {
|
||||
entriesToAdd.push(currentEntry);
|
||||
});
|
||||
// remove empty lines
|
||||
entriesToAdd = entriesToAdd.filter((e) => e && e.trim().length > 0).map((e) => e.trim());
|
||||
const invalidRowsExist = entriesToAdd.filter((e) => e.length < MIN_WORD_LENGTH);
|
||||
if (invalidRowsExist.length === 0) {
|
||||
// can add at least 1 - block UI
|
||||
let obs: Observable<any>;
|
||||
if (entriesToAdd.length > 0) {
|
||||
obs = this._dictionaryControllerService.addEntry(entriesToAdd, ruleSetId, type, dossierId, true);
|
||||
} else {
|
||||
obs = this._dictionaryControllerService.deleteEntries(initialEntries, ruleSetId, type, dossierId);
|
||||
}
|
||||
|
||||
return obs.pipe(
|
||||
tap(
|
||||
() => {
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant('dictionary-overview.success.generic'),
|
||||
null,
|
||||
NotificationType.SUCCESS
|
||||
);
|
||||
},
|
||||
() => {
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant('dictionary-overview.error.generic'),
|
||||
null,
|
||||
NotificationType.ERROR
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
} else {
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant('dictionary-overview.error.entries-too-short'),
|
||||
null,
|
||||
NotificationType.ERROR
|
||||
);
|
||||
|
||||
return throwError('Entries to short');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -320,9 +320,23 @@ export class AppStateService {
|
||||
this._appState.activeProjectId = null;
|
||||
this._router.navigate(['/main/projects']);
|
||||
return;
|
||||
} else {
|
||||
this.updateProjectDictionary(this.activeProject.ruleSetId, projectId);
|
||||
}
|
||||
}
|
||||
|
||||
updateProjectDictionary(ruleSetId: string, projectId: string) {
|
||||
// project exists, load it's dictionary
|
||||
this._dictionaryControllerService.getDictionaryForType(ruleSetId, 'dossier_redaction', projectId).subscribe(
|
||||
(typeData) => {
|
||||
this.activeProject.type = typeData;
|
||||
},
|
||||
() => {
|
||||
this.activeProject.type = null;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
activateFile(projectId: string, fileId: string) {
|
||||
if (this._appState.activeProjectId === projectId && this._appState.activeFileId === fileId) return;
|
||||
this.activateProject(projectId);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||
import * as moment from 'moment';
|
||||
import { Project } from '@redaction/red-ui-http';
|
||||
import { Dictionary, Project } from '@redaction/red-ui-http';
|
||||
|
||||
export class ProjectWrapper {
|
||||
totalNumberOfPages?: number;
|
||||
@ -12,6 +12,7 @@ export class ProjectWrapper {
|
||||
hasPendingOrProcessing?: boolean;
|
||||
|
||||
allFilesApproved?: boolean;
|
||||
type: Dictionary;
|
||||
|
||||
constructor(public project: Project, files: FileStatusWrapper[]) {
|
||||
this._files = files ? files : [];
|
||||
|
||||
@ -185,6 +185,11 @@
|
||||
"expand": "Show Details",
|
||||
"collapse": "Hide Details"
|
||||
},
|
||||
"dossier-dictionary-dialog": {
|
||||
"title": "Dossier Dictionary",
|
||||
"save-changes": "Save Changes",
|
||||
"cancel": "cancel"
|
||||
},
|
||||
"project-overview": {
|
||||
"no-ocr": "No OCR",
|
||||
"ocr-performed": "OCR was performed for this file.",
|
||||
@ -272,6 +277,7 @@
|
||||
"created-on": "Created on {{date}}",
|
||||
"due-date": "Due {{date}}"
|
||||
},
|
||||
"dictionary": "Dossier Dictionary",
|
||||
"description": "Description"
|
||||
},
|
||||
"header": "Dossier Overview",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user