Pull request #242: RED-1689

Merge in RED/ui from RED-1689 to master

* commit 'c8906a04fc3e8abcce4154f52846ff032ce1141a':
  Permissions
  Preview dossier attributes in overview
  Edit dossier attributes
  Edit dossier attributes WIP
This commit is contained in:
Adina Teudan 2021-07-16 17:55:52 +02:00
commit cb1b08e58a
58 changed files with 994 additions and 1040 deletions

View File

@ -0,0 +1,3 @@
import { DossierAttributeConfig } from '@redaction/red-ui-http';
export type DossierAttributeWithValue = DossierAttributeConfig & { value: any };

View File

@ -4,7 +4,7 @@ import { AppStateService } from '@state/app-state.service';
import { Router } from '@angular/router';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { DossierTemplateControllerService } from '@redaction/red-ui-http';
import { LoadingService } from '../../../../services/loading.service';
import { LoadingService } from '@services/loading.service';
@Component({
selector: 'redaction-dossier-template-actions',
@ -33,22 +33,15 @@ export class DossierTemplateActionsComponent {
}
openEditDossierTemplateDialog($event: any) {
this._dialogService.openDialog(
'addEditDossierTemplate',
$event,
this.dossierTemplate,
() => {
this.loadDossierTemplatesData?.emit();
}
);
this._dialogService.openDialog('addEditDossierTemplate', $event, this.dossierTemplate, () => {
this.loadDossierTemplatesData?.emit();
});
}
openDeleteDossierTemplateDialog($event?: MouseEvent) {
this._dialogService.openDialog('confirm', $event, null, async () => {
this._loadingService.start();
await this._dossierTemplateControllerService
.deleteDossierTemplates([this.dossierTemplateId])
.toPromise();
await this._dossierTemplateControllerService.deleteDossierTemplates([this.dossierTemplateId]).toPromise();
await this._appStateService.loadAllDossierTemplates();
await this._appStateService.loadDictionaryData();
await this._router.navigate(['main', 'admin']);

View File

@ -5,7 +5,7 @@ import { DictionaryControllerService, TypeValue } from '@redaction/red-ui-http';
import { Observable } from 'rxjs';
import { NotificationService, NotificationType } from '@services/notification.service';
import { TranslateService } from '@ngx-translate/core';
import { TypeValueWrapper } from '../../../../models/file/type-value.wrapper';
import { TypeValueWrapper } from '@models/file/type-value.wrapper';
import { humanize } from '../../../../utils/functions';
@Component({
@ -78,11 +78,7 @@ export class AddEditDictionaryDialogComponent {
if (this.dictionary) {
// edit mode
observable = this._dictionaryControllerService.updateType(
typeValue,
this._dossierTemplateId,
typeValue.type
);
observable = this._dictionaryControllerService.updateType(typeValue, this._dossierTemplateId, typeValue.type);
} else {
// create mode
typeValue.dossierTemplateId = this._dossierTemplateId;
@ -104,11 +100,7 @@ export class AddEditDictionaryDialogComponent {
}
private _notifyError(message: string) {
this._notificationService.showToastNotification(
this._translateService.instant(message),
null,
NotificationType.ERROR
);
this._notificationService.showToastNotification(this._translateService.instant(message), null, NotificationType.ERROR);
}
private _formToObject(): TypeValue {

View File

@ -1,12 +1,13 @@
import { Component, Inject } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { DossierAttributeConfig, DossierAttributesControllerService, FileAttributeConfig } from '@redaction/red-ui-http';
import { AppStateService } from '../../../../state/app-state.service';
import { DossierAttributeConfig, FileAttributeConfig } from '@redaction/red-ui-http';
import { AppStateService } from '@state/app-state.service';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { LoadingService } from '../../../../services/loading.service';
import { ErrorMessageService } from '../../../../services/error-message.service';
import { NotificationService, NotificationType } from '../../../../services/notification.service';
import { LoadingService } from '@services/loading.service';
import { ErrorMessageService } from '@services/error-message.service';
import { NotificationService, NotificationType } from '@services/notification.service';
import { HttpErrorResponse } from '@angular/common/http';
import { DossierAttributesService } from '@shared/services/controller-wrappers/dossier-attributes.service';
@Component({
selector: 'redaction-add-edit-dossier-attribute-dialog',
@ -28,7 +29,7 @@ export class AddEditDossierAttributeDialogComponent {
private readonly _appStateService: AppStateService,
private readonly _formBuilder: FormBuilder,
private readonly _loadingService: LoadingService,
private readonly _dossierAttributesService: DossierAttributesControllerService,
private readonly _dossierAttributesService: DossierAttributesService,
private readonly _errorMessageService: ErrorMessageService,
private readonly _notificationService: NotificationService,
public dialogRef: MatDialogRef<AddEditDossierAttributeDialogComponent>,
@ -71,20 +72,18 @@ export class AddEditDossierAttributeDialogComponent {
...this.dossierAttributeForm.getRawValue()
};
this._dossierAttributesService
.addOrUpdateDossierAttributesConfig(attribute, this._appStateService.activeDossierTemplateId)
.subscribe(
() => {
this.dialogRef.close(true);
},
(err: HttpErrorResponse) => {
this._loadingService.stop();
this._notificationService.showToastNotification(
this._errorMessageService.getMessage(err, 'add-edit-dossier-attribute.error.generic'),
null,
NotificationType.ERROR
);
}
);
this._dossierAttributesService.addOrUpdateConfig(attribute).subscribe(
() => {
this.dialogRef.close(true);
},
(err: HttpErrorResponse) => {
this._loadingService.stop();
this._notificationService.showToastNotification(
this._errorMessageService.getMessage(err, 'add-edit-dossier-attribute.error.generic'),
null,
NotificationType.ERROR
);
}
);
}
}

View File

@ -1,8 +1,8 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { User, UserControllerService } from '@redaction/red-ui-http';
import { UserService } from '../../../../../services/user.service';
import { LoadingService } from '../../../../../services/loading.service';
import { UserService } from '@services/user.service';
import { LoadingService } from '@services/loading.service';
@Component({
selector: 'redaction-reset-password',

View File

@ -2,7 +2,7 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { User, UserControllerService } from '@redaction/red-ui-http';
import { AdminDialogService } from '../../../services/admin-dialog.service';
import { LoadingService } from '../../../../../services/loading.service';
import { LoadingService } from '@services/loading.service';
@Component({
selector: 'redaction-user-details',
@ -64,10 +64,7 @@ export class UserDetailsComponent implements OnInit {
disabled:
this.user &&
Object.keys(this._ROLE_REQUIREMENTS).reduce(
(value, key) =>
value ||
(role === this._ROLE_REQUIREMENTS[key] &&
this.user.roles.indexOf(key) !== -1),
(value, key) => value || (role === this._ROLE_REQUIREMENTS[key] && this.user.roles.indexOf(key) !== -1),
false
)
}
@ -79,7 +76,10 @@ export class UserDetailsComponent implements OnInit {
firstName: [this.user?.firstName, Validators.required],
lastName: [this.user?.lastName, Validators.required],
email: [
{ value: this.user?.email, disabled: !!this.user },
{
value: this.user?.email,
disabled: !!this.user
},
[Validators.required, Validators.email]
],
...rolesControls

View File

@ -2,7 +2,7 @@ import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { User, UserControllerService } from '@redaction/red-ui-http';
import { AppStateService } from '@state/app-state.service';
import { LoadingService } from '../../../../services/loading.service';
import { LoadingService } from '@services/loading.service';
@Component({
selector: 'redaction-confirm-delete-users-dialog',
@ -45,9 +45,7 @@ export class ConfirmDeleteUsersDialogComponent {
async deleteUser() {
if (this.valid) {
this._loadingService.start();
await this._userControllerService
.deleteUsers(this.users.map(u => u.userId))
.toPromise();
await this._userControllerService.deleteUsers(this.users.map(u => u.userId)).toPromise();
this.dialogRef.close(true);
} else {
this.showToast = true;

View File

@ -1,19 +1,11 @@
import {
Component,
EventEmitter,
Injector,
Input,
OnChanges,
Output,
SimpleChanges
} from '@angular/core';
import { Component, EventEmitter, Injector, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { Field } from '../file-attributes-csv-import-dialog.component';
import { FileAttributeConfig } from '@redaction/red-ui-http';
import { FilterService } from '../../../../shared/services/filter.service';
import { SearchService } from '../../../../shared/services/search.service';
import { ScreenStateService } from '../../../../shared/services/screen-state.service';
import { SortingService } from '../../../../../services/sorting.service';
import { BaseListingComponent } from '../../../../shared/base/base-listing.component';
import { FilterService } from '@shared/services/filter.service';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
@Component({
selector: 'redaction-active-fields-listing',
@ -27,11 +19,7 @@ export class ActiveFieldsListingComponent extends BaseListingComponent<Field> im
@Output() setHoveredColumn = new EventEmitter<string>();
@Output() toggleFieldActive = new EventEmitter<Field>();
readonly typeOptions = [
FileAttributeConfig.TypeEnum.TEXT,
FileAttributeConfig.TypeEnum.NUMBER,
FileAttributeConfig.TypeEnum.DATE
];
readonly typeOptions = [FileAttributeConfig.TypeEnum.TEXT, FileAttributeConfig.TypeEnum.NUMBER, FileAttributeConfig.TypeEnum.DATE];
constructor(protected readonly _injector: Injector) {
super(_injector);
@ -47,12 +35,8 @@ export class ActiveFieldsListingComponent extends BaseListingComponent<Field> im
}
deactivateSelection() {
this.allEntities
.filter(field => this.isSelected(field))
.forEach(field => (field.primaryAttribute = false));
this._screenStateService.setEntities([
...this.allEntities.filter(field => !this.isSelected(field))
]);
this.allEntities.filter(field => this.isSelected(field)).forEach(field => (field.primaryAttribute = false));
this._screenStateService.setEntities([...this.allEntities.filter(field => !this.isSelected(field))]);
this.entitiesChange.emit(this.allEntities);
this._screenStateService.setSelectedEntitiesIds([]);
}

View File

@ -3,20 +3,16 @@ import { AbstractControl, FormBuilder, FormGroup, ValidatorFn, Validators } from
import { AppStateService } from '@state/app-state.service';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import * as Papa from 'papaparse';
import {
FileAttributeConfig,
FileAttributesConfig,
FileAttributesControllerService
} from '@redaction/red-ui-http';
import { FileAttributeConfig, FileAttributesConfig, FileAttributesControllerService } from '@redaction/red-ui-http';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
import { NotificationService, NotificationType } from '@services/notification.service';
import { TranslateService } from '@ngx-translate/core';
import { FilterService } from '../../../shared/services/filter.service';
import { SearchService } from '../../../shared/services/search.service';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
import { SortingService } from '../../../../services/sorting.service';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
import { FilterService } from '@shared/services/filter.service';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
export interface Field {
id?: string;
@ -70,10 +66,7 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
this.dossierTemplateId = data.dossierTemplateId;
this.baseConfigForm = this._formBuilder.group({
filenameMappingColumnHeaderName: [
'',
[Validators.required, this._autocompleteStringValidator()]
],
filenameMappingColumnHeaderName: ['', [Validators.required, this._autocompleteStringValidator()]],
delimiter: [undefined, Validators.required],
encoding: ['UTF-8', Validators.required]
});
@ -103,16 +96,12 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
this.parseResult.meta.fields = Object.keys(this.parseResult.data[0]);
}
this._screenStateService.setEntities(
this.parseResult.meta.fields.map(field => this._buildAttribute(field))
);
this._screenStateService.setEntities(this.parseResult.meta.fields.map(field => this._buildAttribute(field)));
this._screenStateService.setDisplayedEntities(this.allEntities);
this.activeFields = [];
for (const entity of this.allEntities) {
const existing = this.data.existingConfiguration.fileAttributeConfigs.find(
a => a.csvColumnHeader === entity.csvColumn
);
const existing = this.data.existingConfiguration.fileAttributeConfigs.find(a => a.csvColumnHeader === entity.csvColumn);
if (existing) {
entity.id = existing.id;
entity.name = existing.label;
@ -124,34 +113,21 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
}
}
this.filteredKeyOptions = this.baseConfigForm
.get('filenameMappingColumnHeaderName')
.valueChanges.pipe(
startWith(
this.baseConfigForm.get('filenameMappingColumnHeaderName').value as string
),
map((value: string) =>
this.allEntities
.filter(
field =>
field.csvColumn.toLowerCase().indexOf(value.toLowerCase()) !==
-1
)
.map(field => field.csvColumn)
)
);
this.filteredKeyOptions = this.baseConfigForm.get('filenameMappingColumnHeaderName').valueChanges.pipe(
startWith(this.baseConfigForm.get('filenameMappingColumnHeaderName').value as string),
map((value: string) =>
this.allEntities
.filter(field => field.csvColumn.toLowerCase().indexOf(value.toLowerCase()) !== -1)
.map(field => field.csvColumn)
)
);
if (
this.data.existingConfiguration &&
this.allEntities.find(
entity =>
entity.csvColumn ===
this.data.existingConfiguration.filenameMappingColumnHeaderName
)
this.allEntities.find(entity => entity.csvColumn === this.data.existingConfiguration.filenameMappingColumnHeaderName)
) {
this.baseConfigForm.patchValue({
filenameMappingColumnHeaderName:
this.data.existingConfiguration.filenameMappingColumnHeaderName
filenameMappingColumnHeaderName: this.data.existingConfiguration.filenameMappingColumnHeaderName
});
}
@ -206,9 +182,7 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
const newPrimary = !!this.activeFields.find(attr => attr.primaryAttribute);
if (newPrimary) {
this.data.existingConfiguration.fileAttributeConfigs.forEach(
attr => (attr.primaryAttribute = false)
);
this.data.existingConfiguration.fileAttributeConfigs.forEach(attr => (attr.primaryAttribute = false));
}
const fileAttributes = {
@ -229,9 +203,7 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
};
try {
await this._fileAttributesControllerService
.setFileAttributesConfig(fileAttributes, this.dossierTemplateId)
.toPromise();
await this._fileAttributesControllerService.setFileAttributesConfig(fileAttributes, this.dossierTemplateId).toPromise();
this._notificationService.showToastNotification(
this._translateService.instant('file-attributes-csv-import.save.success', {
count: this.activeFields.length
@ -260,9 +232,7 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
if (!column) {
this.columnSample = [];
} else {
this.columnSample = this.parseResult.data
.filter(row => !!row[column])
.map(row => row[column]);
this.columnSample = this.parseResult.data.filter(row => !!row[column]).map(row => row[column]);
}
}, 0);
}
@ -283,9 +253,7 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
csvColumn,
name: csvColumn,
temporaryName: csvColumn,
type: isNumber
? FileAttributeConfig.TypeEnum.NUMBER
: FileAttributeConfig.TypeEnum.TEXT,
type: isNumber ? FileAttributeConfig.TypeEnum.NUMBER : FileAttributeConfig.TypeEnum.TEXT,
readonly: false,
primaryAttribute: false
};

View File

@ -4,12 +4,12 @@ import { Colors, DictionaryControllerService } from '@redaction/red-ui-http';
import { ActivatedRoute } from '@angular/router';
import { PermissionsService } from '@services/permissions.service';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { LoadingService } from '../../../../services/loading.service';
import { FilterService } from '../../../shared/services/filter.service';
import { SearchService } from '../../../shared/services/search.service';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
import { LoadingService } from '@services/loading.service';
import { FilterService } from '@shared/services/filter.service';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { ScreenNames, SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
@Component({
templateUrl: './default-colors-screen.component.html',
@ -60,9 +60,7 @@ export class DefaultColorsScreenComponent
private async _loadColors() {
this._loadingService.start();
const data = await this._dictionaryControllerService
.getColors(this._appStateService.activeDossierTemplateId)
.toPromise();
const data = await this._dictionaryControllerService.getColors(this._appStateService.activeDossierTemplateId).toPromise();
this._colorsObj = data;
this._screenStateService.setEntities(
Object.keys(data).map(key => ({

View File

@ -6,15 +6,15 @@ import { catchError, defaultIfEmpty, tap } from 'rxjs/operators';
import { forkJoin, of } from 'rxjs';
import { PermissionsService } from '@services/permissions.service';
import { ActivatedRoute } from '@angular/router';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { TypeValueWrapper } from '../../../../models/file/type-value.wrapper';
import { TypeValueWrapper } from '@models/file/type-value.wrapper';
import { TranslateService } from '@ngx-translate/core';
import { LoadingService } from '../../../../services/loading.service';
import { FilterService } from '../../../shared/services/filter.service';
import { SearchService } from '../../../shared/services/search.service';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
import { LoadingService } from '@services/loading.service';
import { FilterService } from '@shared/services/filter.service';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { ScreenNames, SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { AdminDialogService } from '../../services/admin-dialog.service';
const toChartConfig = (dict: TypeValueWrapper): DoughnutChartConfig => ({
value: dict.entries ? dict.entries.length : 0,
@ -28,10 +28,7 @@ const toChartConfig = (dict: TypeValueWrapper): DoughnutChartConfig => ({
styleUrls: ['./dictionary-listing-screen.component.scss'],
providers: [FilterService, SearchService, ScreenStateService, SortingService]
})
export class DictionaryListingScreenComponent
extends BaseListingComponent<TypeValueWrapper>
implements OnInit
{
export class DictionaryListingScreenComponent extends BaseListingComponent<TypeValueWrapper> implements OnInit {
chartData: DoughnutChartConfig[] = [];
constructor(
@ -56,15 +53,10 @@ export class DictionaryListingScreenComponent
this._loadDictionaryData();
}
openDeleteDictionariesDialog(
$event?: MouseEvent,
types = this._screenStateService.selectedEntitiesIds
) {
openDeleteDictionariesDialog($event?: MouseEvent, types = this._screenStateService.selectedEntitiesIds) {
this._dialogService.openDialog('confirm', $event, null, async () => {
this._loadingService.start();
await this._dictionaryControllerService
.deleteTypes(types, this._appStateService.activeDossierTemplateId)
.toPromise();
await this._dictionaryControllerService.deleteTypes(types, this._appStateService.activeDossierTemplateId).toPromise();
this._screenStateService.setSelectedEntitiesIds([]);
await this._appStateService.loadDictionaryData();
this._loadDictionaryData(false);
@ -92,8 +84,7 @@ export class DictionaryListingScreenComponent
}
private _loadDictionaryData(loadEntries = true): void {
const appStateDictionaryData =
this._appStateService.dictionaryData[this._appStateService.activeDossierTemplateId];
const appStateDictionaryData = this._appStateService.dictionaryData[this._appStateService.activeDossierTemplateId];
const entities = Object.values(appStateDictionaryData).filter(d => !d.virtual);
if (!loadEntries)
@ -110,16 +101,14 @@ export class DictionaryListingScreenComponent
if (!loadEntries) return;
const dataObs = this.allEntities.map(dict =>
this._dictionaryControllerService
.getDictionaryForType(this._appStateService.activeDossierTemplateId, dict.type)
.pipe(
tap(values => (dict.entries = values.entries ?? [])),
catchError(() => {
console.log('error');
dict.entries = [];
return of({});
})
)
this._dictionaryControllerService.getDictionaryForType(this._appStateService.activeDossierTemplateId, dict.type).pipe(
tap(values => (dict.entries = values.entries ?? [])),
catchError(() => {
console.log('error');
dict.entries = [];
return of({});
})
)
);
forkJoin(dataObs)

View File

@ -9,8 +9,8 @@ import { ComponentHasChanges } from '@guards/can-deactivate.guard';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { DictionaryManagerComponent } from '@shared/components/dictionary-manager/dictionary-manager.component';
import { DictionarySaveService } from '@shared/services/dictionary-save.service';
import { TypeValueWrapper } from '../../../../models/file/type-value.wrapper';
import { LoadingService } from '../../../../services/loading.service';
import { TypeValueWrapper } from '@models/file/type-value.wrapper';
import { LoadingService } from '@services/loading.service';
@Component({
selector: 'redaction-dictionary-overview-screen',
@ -75,9 +75,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
$event?.stopPropagation();
this._dialogService.openDialog('confirm', $event, null, async () => {
await this._dictionaryControllerService
.deleteTypes([this.dictionary.type], this.dictionary.dossierTemplateId)
.toPromise();
await this._dictionaryControllerService.deleteTypes([this.dictionary.type], this.dictionary.dossierTemplateId).toPromise();
await this._appStateService.loadDictionaryData();
await this._router.navigate([
'/main',
@ -113,13 +111,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
saveEntries(entries: string[]) {
this.processing = true;
this._dictionarySaveService
.saveEntries(
entries,
this.entries,
this.dictionary.dossierTemplateId,
this.dictionary.type,
null
)
.saveEntries(entries, this.entries, this.dictionary.dossierTemplateId, this.dictionary.type, null)
.subscribe(
() => {
this.processing = false;
@ -133,19 +125,15 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
private _loadEntries() {
this.processing = true;
this._dictionaryControllerService
.getDictionaryForType(this.dictionary.dossierTemplateId, 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 = [];
}
);
this._dictionaryControllerService.getDictionaryForType(this.dictionary.dossierTemplateId, 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 = [];
}
);
}
}

View File

@ -1,15 +1,16 @@
import { Component, Injector, OnInit } from '@angular/core';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
import { DossierAttributeConfig, DossierAttributesControllerService } from '@redaction/red-ui-http';
import { AppStateService } from '../../../../state/app-state.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { DossierAttributeConfig } from '@redaction/red-ui-http';
import { AppStateService } from '@state/app-state.service';
import { ActivatedRoute } from '@angular/router';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { LoadingService } from '../../../../services/loading.service';
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { FilterService } from '../../../shared/services/filter.service';
import { SearchService } from '../../../shared/services/search.service';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
import { LoadingService } from '@services/loading.service';
import { ScreenNames, SortingService } from '@services/sorting.service';
import { FilterService } from '@shared/services/filter.service';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { PermissionsService } from '@services/permissions.service';
import { DossierAttributesService } from '@shared/services/controller-wrappers/dossier-attributes.service';
@Component({
templateUrl: './dossier-attributes-listing-screen.component.html',
@ -23,7 +24,7 @@ export class DossierAttributesListingScreenComponent extends BaseListingComponen
private readonly _activatedRoute: ActivatedRoute,
private readonly _dialogService: AdminDialogService,
private readonly _loadingService: LoadingService,
private readonly _dossierAttributesService: DossierAttributesControllerService,
private readonly _dossierAttributesService: DossierAttributesService,
readonly permissionsService: PermissionsService
) {
super(_injector);
@ -41,9 +42,7 @@ export class DossierAttributesListingScreenComponent extends BaseListingComponen
this._dialogService.openDialog('confirm', $event, null, async () => {
this._loadingService.start();
const ids = dossierAttribute ? [dossierAttribute.id] : this._screenStateService.selectedEntitiesIds;
await this._dossierAttributesService
.deleteDossierAttributesConfig(ids, this._appStateService.activeDossierTemplateId)
.toPromise();
await this._dossierAttributesService.deleteConfigs(ids);
await this._loadData();
});
}
@ -61,10 +60,8 @@ export class DossierAttributesListingScreenComponent extends BaseListingComponen
private async _loadData() {
this._loadingService.start();
const response = await this._dossierAttributesService
.getDossierAttributesConfig(this._appStateService.activeDossierTemplateId)
.toPromise();
this._screenStateService.setEntities(response?.dossierAttributeConfigs || []);
const attributes = await this._dossierAttributesService.getConfig();
this._screenStateService.setEntities(attributes);
this.filterService.filterEntities();
this._loadingService.stop();
}

View File

@ -3,14 +3,14 @@ import { AppStateService } from '@state/app-state.service';
import { PermissionsService } from '@services/permissions.service';
import { UserPreferenceService } from '@services/user-preference.service';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { DossierTemplateModelWrapper } from '../../../../models/file/dossier-template-model.wrapper';
import { LoadingService } from '../../../../services/loading.service';
import { DossierTemplateModelWrapper } from '@models/file/dossier-template-model.wrapper';
import { LoadingService } from '@services/loading.service';
import { DossierTemplateControllerService } from '@redaction/red-ui-http';
import { FilterService } from '../../../shared/services/filter.service';
import { SearchService } from '../../../shared/services/search.service';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
import { FilterService } from '@shared/services/filter.service';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { ScreenNames, SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
@Component({
templateUrl: './dossier-templates-listing-screen.component.html',
@ -18,10 +18,7 @@ import { BaseListingComponent } from '../../../shared/base/base-listing.componen
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [FilterService, SearchService, ScreenStateService, SortingService]
})
export class DossierTemplatesListingScreenComponent
extends BaseListingComponent<DossierTemplateModelWrapper>
implements OnInit
{
export class DossierTemplatesListingScreenComponent extends BaseListingComponent<DossierTemplateModelWrapper> implements OnInit {
constructor(
private readonly _dialogService: AdminDialogService,
private readonly _appStateService: AppStateService,
@ -44,9 +41,7 @@ export class DossierTemplatesListingScreenComponent
openDeleteTemplatesDialog($event?: MouseEvent) {
return this._dialogService.openDialog('confirm', $event, null, async () => {
this._loadingService.start();
await this._dossierTemplateControllerService
.deleteDossierTemplates(this._screenStateService.selectedEntitiesIds)
.toPromise();
await this._dossierTemplateControllerService.deleteDossierTemplates(this._screenStateService.selectedEntitiesIds).toPromise();
this._screenStateService.setSelectedEntitiesIds([]);
await this._appStateService.loadAllDossierTemplates();
await this._appStateService.loadDictionaryData();
@ -64,16 +59,11 @@ export class DossierTemplatesListingScreenComponent
}
openAddDossierTemplateDialog() {
this._dialogService.openDialog(
'addEditDossierTemplate',
null,
null,
async newDossierTemplate => {
if (newDossierTemplate) {
this.loadDossierTemplatesData();
}
this._dialogService.openDialog('addEditDossierTemplate', null, null, async newDossierTemplate => {
if (newDossierTemplate) {
this.loadDossierTemplatesData();
}
);
});
}
private _loadDossierTemplateStats() {

View File

@ -1,22 +1,15 @@
import {
ChangeDetectionStrategy,
Component,
ElementRef,
Injector,
OnInit,
ViewChild
} from '@angular/core';
import { ChangeDetectionStrategy, Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
import { FileAttributeConfig, FileAttributesConfig, FileAttributesControllerService } from '@redaction/red-ui-http';
import { AppStateService } from '@state/app-state.service';
import { ActivatedRoute } from '@angular/router';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { LoadingService } from '../../../../services/loading.service';
import { FilterService } from '../../../shared/services/filter.service';
import { SearchService } from '../../../shared/services/search.service';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
import { LoadingService } from '@services/loading.service';
import { FilterService } from '@shared/services/filter.service';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { ScreenNames, SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
@Component({
templateUrl: './file-attributes-listing-screen.component.html',
@ -24,10 +17,7 @@ import { BaseListingComponent } from '../../../shared/base/base-listing.componen
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [FilterService, SearchService, ScreenStateService, SortingService]
})
export class FileAttributesListingScreenComponent
extends BaseListingComponent<FileAttributeConfig>
implements OnInit
{
export class FileAttributesListingScreenComponent extends BaseListingComponent<FileAttributeConfig> implements OnInit {
private _existingConfiguration: FileAttributesConfig;
@ViewChild('fileInput') private _fileInput: ElementRef;
@ -75,10 +65,7 @@ export class FileAttributesListingScreenComponent
.toPromise();
} else {
await this._fileAttributesService
.deleteFileAttributes(
this._screenStateService.selectedEntitiesIds,
this._appStateService.activeDossierTemplateId
)
.deleteFileAttributes(this._screenStateService.selectedEntitiesIds, this._appStateService.activeDossierTemplateId)
.toPromise();
}
await this._loadData();

View File

@ -1,11 +1,11 @@
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { AppStateService } from '../../../../state/app-state.service';
import { AppStateService } from '@state/app-state.service';
import { ReportTemplate, ReportTemplateControllerService } from '@redaction/red-ui-http';
import { download } from '../../../../utils/file-download-utils';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { LoadingService } from '../../../../services/loading.service';
import { PermissionsService } from '../../../../services/permissions.service';
import { LoadingService } from '@services/loading.service';
import { PermissionsService } from '@services/permissions.service';
@Component({
selector: 'redaction-reports-screen',

View File

@ -2,14 +2,14 @@ import { ChangeDetectionStrategy, Component, Injector, OnInit } from '@angular/c
import { AppStateService } from '@state/app-state.service';
import { PermissionsService } from '@services/permissions.service';
import { Dossier, StatusControllerService } from '@redaction/red-ui-http';
import { LoadingService } from '../../../../services/loading.service';
import { LoadingService } from '@services/loading.service';
import { AppConfigKey, AppConfigService } from '../../../app-config/app-config.service';
import * as moment from 'moment';
import { FilterService } from '../../../shared/services/filter.service';
import { SearchService } from '../../../shared/services/search.service';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
import { FilterService } from '@shared/services/filter.service';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { ScreenNames, SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { DossiersService } from '../../../dossier/services/dossiers.service';
@Component({

View File

@ -1,11 +1,4 @@
import {
ChangeDetectionStrategy,
Component,
Injector,
OnInit,
QueryList,
ViewChildren
} from '@angular/core';
import { ChangeDetectionStrategy, Component, Injector, OnInit, QueryList, ViewChildren } from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
import { UserService } from '@services/user.service';
import { User, UserControllerService } from '@redaction/red-ui-http';
@ -13,13 +6,13 @@ import { AdminDialogService } from '../../services/admin-dialog.service';
import { TranslateService } from '@ngx-translate/core';
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
import { TranslateChartService } from '@services/translate-chart.service';
import { LoadingService } from '../../../../services/loading.service';
import { InitialsAvatarComponent } from '../../../shared/components/initials-avatar/initials-avatar.component';
import { FilterService } from '../../../shared/services/filter.service';
import { SearchService } from '../../../shared/services/search.service';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
import { SortingService } from '../../../../services/sorting.service';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
import { LoadingService } from '@services/loading.service';
import { InitialsAvatarComponent } from '@shared/components/initials-avatar/initials-avatar.component';
import { FilterService } from '@shared/services/filter.service';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@ -86,9 +79,7 @@ export class UserListingScreenComponent extends BaseListingComponent<User> imple
}
bulkDelete() {
this.openDeleteUsersDialog(
this._screenStateService.entities.filter(u => this.isSelected(u))
);
this.openDeleteUsersDialog(this._screenStateService.entities.filter(u => this.isSelected(u)));
}
trackById(index: number, user: User) {
@ -96,9 +87,7 @@ export class UserListingScreenComponent extends BaseListingComponent<User> imple
}
private async _loadData() {
this._screenStateService.setEntities(
await this._userControllerService.getAllUsers().toPromise()
);
this._screenStateService.setEntities(await this._userControllerService.getAllUsers().toPromise());
await this.userService.loadAllUsers();
this.filterService.filterEntities();
this._computeStats();
@ -114,38 +103,27 @@ export class UserListingScreenComponent extends BaseListingComponent<User> imple
label: 'INACTIVE'
},
{
value: this.allEntities.filter(
user => user.roles.length === 1 && user.roles[0] === 'RED_USER'
).length,
value: this.allEntities.filter(user => user.roles.length === 1 && user.roles[0] === 'RED_USER').length,
color: 'REGULAR',
label: 'REGULAR'
},
{
value: this.allEntities.filter(
user => this.userService.isManager(user) && !this.userService.isAdmin(user)
).length,
value: this.allEntities.filter(user => this.userService.isManager(user) && !this.userService.isAdmin(user)).length,
color: 'MANAGER',
label: 'RED_MANAGER'
},
{
value: this.allEntities.filter(
user => this.userService.isManager(user) && this.userService.isAdmin(user)
).length,
value: this.allEntities.filter(user => this.userService.isManager(user) && this.userService.isAdmin(user)).length,
color: 'MANAGER_ADMIN',
label: 'MANAGER_ADMIN'
},
{
value: this.allEntities.filter(
user =>
this.userService.isUserAdmin(user) && !this.userService.isAdmin(user)
).length,
value: this.allEntities.filter(user => this.userService.isUserAdmin(user) && !this.userService.isAdmin(user)).length,
color: 'USER_ADMIN',
label: 'RED_USER_ADMIN'
},
{
value: this.allEntities.filter(
user => this.userService.isAdmin(user) && !this.userService.isManager(user)
).length,
value: this.allEntities.filter(user => this.userService.isAdmin(user) && !this.userService.isManager(user)).length,
color: 'ADMIN',
label: 'RED_ADMIN'
}

View File

@ -11,7 +11,7 @@ import { AddEditUserDialogComponent } from '../dialogs/add-edit-user-dialog/add-
import { ConfirmDeleteUsersDialogComponent } from '../dialogs/confirm-delete-users-dialog/confirm-delete-users-dialog.component';
import { FileAttributesCsvImportDialogComponent } from '../dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component';
import { ComponentType } from '@angular/cdk/portal';
import { DialogService } from '../../shared/services/dialog.service';
import { DialogService } from '@shared/services/dialog.service';
import { AddEditDossierAttributeDialogComponent } from '../dialogs/add-edit-dossier-attribute-dialog/add-edit-dossier-attribute-dialog.component';
type DialogType =

View File

@ -1,18 +1,15 @@
import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
import { AppStateService } from '@state/app-state.service';
import { UserService } from '@services/user.service';
import {
FileManagementControllerService,
ReanalysisControllerService
} from '@redaction/red-ui-http';
import { FileManagementControllerService, ReanalysisControllerService } from '@redaction/red-ui-http';
import { PermissionsService } from '@services/permissions.service';
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
import { FileActionService } from '../../services/file-action.service';
import { from, Observable } from 'rxjs';
import { StatusOverlayService } from '@upload-download/services/status-overlay.service';
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { LoadingService } from '../../../../services/loading.service';
import { ConfirmationDialogInput } from '../../../shared/dialogs/confirmation-dialog/confirmation-dialog.component';
import { LoadingService } from '@services/loading.service';
import { ConfirmationDialogInput } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component';
@Component({
selector: 'redaction-dossier-overview-bulk-actions',
@ -44,10 +41,7 @@ export class DossierOverviewBulkActionsComponent {
get selectedFiles(): FileStatusWrapper[] {
return this.selectedFileIds.map(fileId =>
this._appStateService.getFileById(
this._appStateService.activeDossier.dossier.dossierId,
fileId
)
this._appStateService.getFileById(this._appStateService.activeDossier.dossier.dossierId, fileId)
);
}
@ -69,10 +63,7 @@ export class DossierOverviewBulkActionsComponent {
(acc, file) => acc && (file.isUnderReview || file.isUnassigned),
true
);
const allFilesAreUnderApproval = selectedFiles.reduce(
(acc, file) => acc && file.isUnderApproval,
true
);
const allFilesAreUnderApproval = selectedFiles.reduce((acc, file) => acc && file.isUnderApproval, true);
return allFilesAreUnderReviewOrUnassigned || allFilesAreUnderApproval;
}
return false;
@ -81,42 +72,27 @@ export class DossierOverviewBulkActionsComponent {
get canAssignToSelf() {
return (
this.allSelectedFilesCanBeAssignedIntoSameState &&
this.selectedFiles.reduce(
(acc, file) => acc && this._permissionsService.canAssignToSelf(file),
true
)
this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canAssignToSelf(file), true)
);
}
get canAssign() {
return (
this.allSelectedFilesCanBeAssignedIntoSameState &&
this.selectedFiles.reduce(
(acc, file) => acc && this._permissionsService.canAssignUser(file),
true
)
this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canAssignUser(file), true)
);
}
get canDelete() {
return this.selectedFiles.reduce(
(acc, file) => acc && this._permissionsService.canDeleteFile(file),
true
);
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canDeleteFile(file), true);
}
get canReanalyse() {
return this.selectedFiles.reduce(
(acc, file) => acc && this._permissionsService.canReanalyseFile(file),
true
);
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canReanalyseFile(file), true);
}
get canOcr() {
return this.selectedFiles.reduce(
(acc, file) => acc && this._permissionsService.canOcrFile(file),
true
);
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canOcrFile(file), true);
}
get fileStatuses() {
@ -125,51 +101,31 @@ export class DossierOverviewBulkActionsComponent {
// Under review
get canSetToUnderReview() {
return this.selectedFiles.reduce(
(acc, file) => acc && this._permissionsService.canSetUnderReview(file),
true
);
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canSetUnderReview(file), true);
}
// Under approval
get canSetToUnderApproval() {
return this.selectedFiles.reduce(
(acc, file) => acc && this._permissionsService.canSetUnderApproval(file),
true
);
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canSetUnderApproval(file), true);
}
// Approve
get isReadyForApproval() {
return this.selectedFiles.reduce(
(acc, file) => acc && this._permissionsService.isReadyForApproval(file),
true
);
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.isReadyForApproval(file), true);
}
get canApprove() {
return this.selectedFiles.reduce(
(acc, file) => acc && this._permissionsService.canApprove(file),
true
);
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canApprove(file), true);
}
// Undo approval
get canUndoApproval() {
return this.selectedFiles.reduce(
(acc, file) => acc && this._permissionsService.canUndoApproval(file),
true
);
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canUndoApproval(file), true);
}
get assignTooltip() {
const allFilesAreUnderApproval = this.selectedFiles.reduce(
(acc, file) => acc && file.isUnderApproval,
true
);
return allFilesAreUnderApproval
? 'dossier-overview.assign-approver'
: 'dossier-overview.assign-reviewer';
const allFilesAreUnderApproval = this.selectedFiles.reduce((acc, file) => acc && file.isUnderApproval, true);
return allFilesAreUnderApproval ? 'dossier-overview.assign-approver' : 'dossier-overview.assign-reviewer';
}
delete() {
@ -212,23 +168,15 @@ export class DossierOverviewBulkActionsComponent {
);
} else {
this._performBulkAction(
this._fileActionService.setFileUnderApproval(
this.selectedFiles,
this._appStateService.activeDossier.approverIds[0]
)
this._fileActionService.setFileUnderApproval(this.selectedFiles, this._appStateService.activeDossier.approverIds[0])
);
}
}
async reanalyse() {
const fileIds = this.selectedFiles
.filter(file => this._permissionsService.fileRequiresReanalysis(file))
.map(file => file.fileId);
const fileIds = this.selectedFiles.filter(file => this._permissionsService.fileRequiresReanalysis(file)).map(file => file.fileId);
this._performBulkAction(
this._reanalysisControllerService.reanalyzeFilesForDossier(
fileIds,
this._appStateService.activeDossier.dossierId
)
this._reanalysisControllerService.reanalyzeFilesForDossier(fileIds, this._appStateService.activeDossier.dossierId)
);
}
@ -250,9 +198,7 @@ export class DossierOverviewBulkActionsComponent {
assign() {
this._loadingService.start();
const files = this.selectedFileIds.map(fileId =>
this._appStateService.getFileById(this._appStateService.activeDossierId, fileId)
);
const files = this.selectedFileIds.map(fileId => this._appStateService.getFileById(this._appStateService.activeDossierId, fileId));
const mode = files[0].isUnderApproval ? 'approver' : 'reviewer';

View File

@ -0,0 +1,66 @@
<div>
<mat-icon svgIcon="red:document"></mat-icon>
<span>{{ 'dossier-overview.dossier-details.stats.documents' | translate: { count: activeDossier.files.length } }}</span>
</div>
<div>
<mat-icon svgIcon="red:user"></mat-icon>
<span>{{ 'dossier-overview.dossier-details.stats.people' | translate: { count: activeDossier.memberCount } }}</span>
</div>
<div>
<mat-icon svgIcon="red:pages"></mat-icon>
<span>{{
'dossier-overview.dossier-details.stats.analysed-pages' | translate: { count: activeDossier.totalNumberOfPages | number }
}}</span>
</div>
<div>
<mat-icon svgIcon="red:calendar"></mat-icon>
<span
>{{
'dossier-overview.dossier-details.stats.created-on'
| translate
: {
date: activeDossier.dossier.date | date: 'd MMM. yyyy'
}
}}
</span>
</div>
<div *ngIf="activeDossier.dossier.dueDate">
<mat-icon svgIcon="red:lightning"></mat-icon>
<span>{{
'dossier-overview.dossier-details.stats.due-date'
| translate
: {
date: activeDossier.dossier.dueDate | date: 'd MMM. yyyy'
}
}}</span>
</div>
<div>
<mat-icon svgIcon="red:template"></mat-icon>
<span>{{ dossierTemplate?.name }} </span>
</div>
<div (click)="openDossierDictionaryDialog.emit()" *ngIf="activeDossier.type" class="link-property">
<mat-icon svgIcon="red:dictionary"></mat-icon>
<span>{{ 'dossier-overview.dossier-details.dictionary' | translate }} </span>
</div>
<ng-container *ngIf="dossierAttributes?.length">
<div (click)="attributesExpanded = true" *ngIf="!attributesExpanded" class="all-caps-label show-attributes">
{{ 'dossier-overview.dossier-details.attributes.expand' | translate: { count: dossierAttributes.length } }}
</div>
<ng-container *ngIf="attributesExpanded">
<div (click)="openEditDossierAttributesDialog()" *ngFor="let attr of dossierAttributes" class="link-property">
<mat-icon svgIcon="red:attribute"></mat-icon>
<span *ngIf="!attr.value"> {{ attr.label + ': -' }}</span>
<span *ngIf="attr.value && attr.type === 'DATE'"> {{ attr.label + ': ' + (attr.value | date: 'd MMM. yyyy') }}</span>
<span *ngIf="attr.value && attr.type === 'IMAGE'">
{{ attr.label + ': ' + ('dossier-overview.dossier-details.attributes.image-uploaded' | translate) }}</span
>
<span *ngIf="attr.value && (attr.type === 'TEXT' || attr.type === 'NUMBER')"> {{ attr.label + ': ' + attr.value }}</span>
</div>
<div (click)="attributesExpanded = false" class="all-caps-label hide-attributes">
{{ 'dossier-overview.dossier-details.attributes.show-less' | translate }}
</div>
</ng-container>
</ng-container>

View File

@ -0,0 +1,47 @@
@import '../../../../../assets/styles/red-variables';
@import '../../../../../assets/styles/red-components';
@import '../../../../../assets/styles/red-text-styles';
:host {
@extend .stats-subtitle;
@extend .small-label;
flex-direction: column;
align-items: flex-start;
> div {
margin-right: 0;
border-radius: 4px;
width: 100%;
justify-content: flex-start;
padding: 0 8px;
margin-left: -8px;
&.link-property {
cursor: pointer;
transition: background-color 0.2;
&:hover {
background-color: $grey-6;
}
}
}
.show-attributes,
.hide-attributes {
margin-top: 12px;
cursor: pointer;
&::before {
margin-right: 3px;
}
}
.show-attributes::before {
content: '+';
}
.hide-attributes::before {
content: '-';
}
}

View File

@ -0,0 +1,34 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { DossierAttributeWithValue } from '@models/dossier-attributes.model';
import { AppStateService } from '@state/app-state.service';
import { DossierWrapper } from '@state/model/dossier.wrapper';
import { DossierTemplateModel } from '@redaction/red-ui-http';
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
@Component({
selector: 'redaction-dossier-details-stats',
templateUrl: './dossier-details-stats.component.html',
styleUrls: ['./dossier-details-stats.component.scss']
})
export class DossierDetailsStatsComponent {
attributesExpanded = false;
@Input() dossierAttributes: DossierAttributeWithValue[];
@Output() openDossierDictionaryDialog = new EventEmitter();
constructor(private readonly _appStateService: AppStateService, private readonly _dialogService: DossiersDialogService) {}
get activeDossier(): DossierWrapper {
return this._appStateService.activeDossier;
}
get dossierTemplate(): DossierTemplateModel {
return this._appStateService.getDossierTemplateById(this.activeDossier.dossierTemplateId);
}
openEditDossierAttributesDialog() {
this._dialogService.openDialog('editDossier', null, {
dossierWrapper: this.activeDossier,
section: 'dossier-attributes'
});
}
}

View File

@ -6,21 +6,14 @@
<div class="header-wrapper mt-8">
<div class="heading-xl flex-1">{{ appStateService.activeDossier.dossier.dossierName }}</div>
<ng-container
*ngTemplateOutlet="collapsible; context: { action: 'collapse' }"
></ng-container>
<ng-container *ngTemplateOutlet="collapsible; context: { action: 'collapse' }"></ng-container>
</div>
<div class="mt-24">
<div class="all-caps-label" translate="dossier-details.owner"></div>
<div class="mt-12 d-flex">
<ng-container *ngIf="!editingOwner; else editOwner">
<redaction-initials-avatar
[userId]="owner.userId"
[withName]="true"
color="gray"
size="large"
></redaction-initials-avatar>
<redaction-initials-avatar [userId]="owner.userId" [withName]="true" color="gray" size="large"></redaction-initials-avatar>
<redaction-circle-button
(action)="editingOwner = true"
@ -65,73 +58,11 @@
</div>
</div>
<div [class.mt-24]="!hasFiles" class="pb-32 small-label stats-subtitle">
<div>
<mat-icon svgIcon="red:document"></mat-icon>
<span>{{
'dossier-overview.dossier-details.stats.documents'
| translate: { count: appStateService.activeDossier.files.length }
}}</span>
</div>
<div>
<mat-icon svgIcon="red:user"></mat-icon>
<span>{{
'dossier-overview.dossier-details.stats.people'
| translate: { count: appStateService.activeDossier.memberCount }
}}</span>
</div>
<div>
<mat-icon svgIcon="red:pages"></mat-icon>
<span>{{
'dossier-overview.dossier-details.stats.analysed-pages'
| translate
: { count: appStateService.activeDossier.totalNumberOfPages | number }
}}</span>
</div>
<div>
<mat-icon svgIcon="red:calendar"></mat-icon>
<span
>{{
'dossier-overview.dossier-details.stats.created-on'
| translate
: {
date:
appStateService.activeDossier.dossier.date
| date: 'd MMM. yyyy'
}
}}
</span>
</div>
<div *ngIf="appStateService.activeDossier.dossier.dueDate">
<mat-icon svgIcon="red:lightning"></mat-icon>
<span>{{
'dossier-overview.dossier-details.stats.due-date'
| translate
: {
date:
appStateService.activeDossier.dossier.dueDate
| date: 'd MMM. yyyy'
}
}}</span>
</div>
<div>
<mat-icon svgIcon="red:template"></mat-icon>
<span
>{{
appStateService.getDossierTemplateById(
appStateService.activeDossier.dossierTemplateId
)?.name
}}
</span>
</div>
<div
(click)="openDossierDictionaryDialog.emit()"
*ngIf="appStateService.activeDossier.type"
class="pointer"
>
<mat-icon svgIcon="red:dictionary"></mat-icon>
<span>{{ 'dossier-overview.dossier-details.dictionary' | translate }} </span>
</div>
<div [class.mt-24]="!hasFiles" class="pb-32">
<redaction-dossier-details-stats
(openDossierDictionaryDialog)="openDossierDictionaryDialog.emit()"
[dossierAttributes]="dossierAttributes"
></redaction-dossier-details-stats>
</div>
<div *ngIf="!!appStateService.activeDossier.dossier.description" class="pb-32">

View File

@ -42,15 +42,6 @@
}
}
.stats-subtitle {
flex-direction: column;
align-items: flex-start;
> * {
margin-right: 0;
}
}
.mt-12 {
margin-top: 12px;
}

View File

@ -1,4 +1,4 @@
import { ChangeDetectorRef, Component, EventEmitter, OnInit, Output } from '@angular/core';
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { AppStateService } from '@state/app-state.service';
import { groupBy } from '@utils/functions';
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
@ -8,8 +8,9 @@ import { StatusSorter } from '@utils/sorters/status-sorter';
import { UserService } from '@services/user.service';
import { User } from '@redaction/red-ui-http';
import { NotificationService } from '@services/notification.service';
import { FilterService } from '../../../shared/services/filter.service';
import { FileStatusWrapper } from '../../../../models/file/file-status.wrapper';
import { FilterService } from '@shared/services/filter.service';
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
import { DossierAttributeWithValue } from '@models/dossier-attributes.model';
@Component({
selector: 'redaction-dossier-details',
@ -20,6 +21,7 @@ export class DossierDetailsComponent implements OnInit {
documentsChartData: DoughnutChartConfig[] = [];
owner: User;
editingOwner = false;
@Input() dossierAttributes: DossierAttributeWithValue[];
@Output() openAssignDossierMembersDialog = new EventEmitter();
@Output() openDossierDictionaryDialog = new EventEmitter();
@Output() toggleCollapse = new EventEmitter();
@ -70,9 +72,7 @@ export class DossierDetailsComponent implements OnInit {
});
}
this.documentsChartData.sort((a, b) => StatusSorter[a.key] - StatusSorter[b.key]);
this.documentsChartData = this.translateChartService.translateStatus(
this.documentsChartData
);
this.documentsChartData = this.translateChartService.translateStatus(this.documentsChartData);
this._changeDetectorRef.detectChanges();
}

View File

@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
import { AppStateService } from '@state/app-state.service';
import { FilterService } from '../../../shared/services/filter.service';
import { FilterService } from '@shared/services/filter.service';
@Component({
selector: 'redaction-dossier-listing-details',
@ -13,8 +13,5 @@ export class DossierListingDetailsComponent<T> {
@Input() dossiersChartData: DoughnutChartConfig[];
@Input() documentsChartData: DoughnutChartConfig[];
constructor(
readonly appStateService: AppStateService,
readonly filterService: FilterService<T>
) {}
constructor(readonly appStateService: AppStateService, readonly filterService: FilterService<T>) {}
}

View File

@ -4,8 +4,8 @@ import { FileStatusWrapper } from '@models/file/file-status.wrapper';
import { AppStateService } from '@state/app-state.service';
import { FileActionService } from '../../services/file-action.service';
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { ConfirmationDialogInput } from '../../../shared/dialogs/confirmation-dialog/confirmation-dialog.component';
import { LoadingService } from '../../../../services/loading.service';
import { ConfirmationDialogInput } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component';
import { LoadingService } from '@services/loading.service';
import { FileManagementControllerService } from '@redaction/red-ui-http';
@Component({
@ -48,9 +48,7 @@ export class FileActionsComponent implements OnInit {
return 'file-preview.toggle-analysis.only-managers';
}
return this.fileStatus?.isExcluded
? 'file-preview.toggle-analysis.enable'
: 'file-preview.toggle-analysis.disable';
return this.fileStatus?.isExcluded ? 'file-preview.toggle-analysis.enable' : 'file-preview.toggle-analysis.disable';
}
get canAssignToSelf() {
@ -86,9 +84,7 @@ export class FileActionsComponent implements OnInit {
}
get assignTooltip() {
return this.fileStatus.isUnderApproval
? 'dossier-overview.assign-approver'
: 'dossier-overview.assign-reviewer';
return this.fileStatus.isUnderApproval ? 'dossier-overview.assign-approver' : 'dossier-overview.assign-reviewer';
}
ngOnInit(): void {
@ -124,9 +120,7 @@ export class FileActionsComponent implements OnInit {
}),
async () => {
this._loadingService.start();
await this._fileManagementControllerService
.deleteFiles([this.fileStatus.fileId], this.fileStatus.dossierId)
.toPromise();
await this._fileManagementControllerService.deleteFiles([this.fileStatus.fileId], this.fileStatus.dossierId).toPromise();
await this.appStateService.reloadActiveDossierFiles();
this.actionPerformed.emit('delete');
this._loadingService.stop();
@ -162,11 +156,7 @@ export class FileActionsComponent implements OnInit {
setFileUnderApproval($event: MouseEvent) {
$event.stopPropagation();
if (this.appStateService.activeDossier.approverIds.length > 1) {
this._fileActionService.assignDossierApprover(
this.fileStatus,
() => this.actionPerformed.emit('assign-reviewer'),
true
);
this._fileActionService.assignDossierApprover(this.fileStatus, () => this.actionPerformed.emit('assign-reviewer'), true);
} else {
this._fileActionService.setFileUnderApproval(this.fileStatus).subscribe(() => {
this.reloadDossiers('set-under-approval');
@ -206,8 +196,6 @@ export class FileActionsComponent implements OnInit {
async toggleAnalysis() {
await this._fileActionService.toggleAnalysis(this.fileStatus).toPromise();
await this.appStateService.getFiles();
this.actionPerformed.emit(
this.fileStatus?.isExcluded ? 'enable-analysis' : 'disable-analysis'
);
this.actionPerformed.emit(this.fileStatus?.isExcluded ? 'enable-analysis' : 'disable-analysis');
}
}

View File

@ -19,7 +19,7 @@ import { debounce } from '@utils/debounce';
import { FileDataModel } from '@models/file/file-data.model';
import { FilterModel } from '@shared/components/filters/popup-filter/model/filter.model';
import { CommentsComponent } from '../comments/comments.component';
import { PermissionsService } from '../../../../services/permissions.service';
import { PermissionsService } from '@services/permissions.service';
import { TranslateService } from '@ngx-translate/core';
import { WebViewerInstance } from '@pdftron/webviewer';

View File

@ -1,10 +1,10 @@
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { PermissionsService } from '../../../../services/permissions.service';
import { PermissionsService } from '@services/permissions.service';
import { FormBuilder, FormGroup } from '@angular/forms';
import { PageRange, ReanalysisControllerService } from '@redaction/red-ui-http';
import { FileDataModel } from '../../../../models/file/file-data.model';
import { NotificationService, NotificationType } from '../../../../services/notification.service';
import { LoadingService } from '../../../../services/loading.service';
import { FileDataModel } from '@models/file/file-data.model';
import { NotificationService, NotificationType } from '@services/notification.service';
import { LoadingService } from '@services/loading.service';
import { TranslateService } from '@ngx-translate/core';
@Component({
@ -34,9 +34,7 @@ export class PageExclusionComponent implements OnChanges {
ngOnChanges(changes: SimpleChanges) {
if (changes.fileData) {
const excludedPages = (this.fileData?.fileStatus?.excludedPages || []).sort(
(p1, p2) => p1 - p2
);
const excludedPages = (this.fileData?.fileStatus?.excludedPages || []).sort((p1, p2) => p1 - p2);
this.excludedPagesRanges = excludedPages.reduce((ranges, page) => {
if (!ranges.length) {
return [{ startPage: page, endPage: page }];

View File

@ -24,12 +24,12 @@ import { AnnotationActionsService } from '../../services/annotation-actions.serv
import { UserPreferenceService } from '@services/user-preference.service';
import { BASE_HREF } from '../../../../tokens';
import { AppConfigKey, AppConfigService } from '../../../app-config/app-config.service';
import { LoadingService } from '../../../../services/loading.service';
import { LoadingService } from '@services/loading.service';
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { ConfirmationDialogInput } from '../../../shared/dialogs/confirmation-dialog/confirmation-dialog.component';
import { ConfirmationDialogInput } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component';
import { loadCompareDocumentWrapper } from '../../utils/compare-mode.utils';
import { PdfViewerUtils } from '../../utils/pdf-viewer.utils';
import { ViewMode } from '../../../../models/file/view-mode';
import { ViewMode } from '@models/file/view-mode';
import TextTool = Tools.TextTool;
@Component({
@ -105,9 +105,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
// viewer init
this.instance.setFitMode('FitPage');
const instanceDisplayMode = this.instance.docViewer
.getDisplayModeManager()
.getDisplayMode();
const instanceDisplayMode = this.instance.docViewer.getDisplayModeManager().getDisplayMode();
instanceDisplayMode.mode = this.viewMode === 'STANDARD' ? 'Single' : 'Facing';
this.instance.docViewer.getDisplayModeManager().setDisplayMode(instanceDisplayMode);
}
@ -123,15 +121,11 @@ export class PdfViewerComponent implements OnInit, OnChanges {
const pdfData = fileReader.result;
const PDFNet = this.instance.PDFNet;
await PDFNet.initialize(
environment.licenseKey ? atob(environment.licenseKey) : null
);
await PDFNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null);
const mergedDocument = await PDFNet.PDFDoc.create();
const compareDocument = await PDFNet.PDFDoc.createFromBuffer(<any>pdfData);
const currentDocument = await PDFNet.PDFDoc.createFromBuffer(
await this.fileData.arrayBuffer()
);
const currentDocument = await PDFNet.PDFDoc.createFromBuffer(await this.fileData.arrayBuffer());
const currentDocumentPageCount = await currentDocument.getPageCount();
const compareDocumentPageCount = await compareDocument.getPageCount();
@ -186,9 +180,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
this.viewMode = 'STANDARD';
const PDFNet = this.instance.PDFNet;
await PDFNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null);
const currentDocument = await PDFNet.PDFDoc.createFromBuffer(
await this.fileData.arrayBuffer()
);
const currentDocument = await PDFNet.PDFDoc.createFromBuffer(await this.fileData.arrayBuffer());
this.instance.loadDocument(currentDocument, {
filename: this.fileStatus ? this.fileStatus.filename : 'document.pdf'
});
@ -221,16 +213,12 @@ export class PdfViewerComponent implements OnInit, OnChanges {
this._configureTextPopup();
this.instance.annotManager.on('annotationSelected', (annotations, action) => {
this.annotationSelected.emit(
this.instance.annotManager.getSelectedAnnotations().map(ann => ann.Id)
);
this.annotationSelected.emit(this.instance.annotManager.getSelectedAnnotations().map(ann => ann.Id));
if (action === 'deselected') {
this._toggleRectangleAnnotationAction(true);
} else {
this._configureAnnotationSpecificActions(annotations);
this._toggleRectangleAnnotationAction(
annotations.length === 1 && annotations[0].ReadOnly
);
this._toggleRectangleAnnotationAction(annotations.length === 1 && annotations[0].ReadOnly);
}
});
@ -238,10 +226,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
// when a rectangle is drawn,
// it returns one annotation with tool name 'AnnotationCreateRectangle;
// this will auto select rectangle after drawing
if (
annotations.length === 1 &&
annotations[0].ToolName === 'AnnotationCreateRectangle'
) {
if (annotations.length === 1 && annotations[0].ToolName === 'AnnotationCreateRectangle') {
this.instance.annotManager.selectAnnotations(annotations);
}
});
@ -284,9 +269,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
this.instance.iframeWindow.addEventListener('visibilityChanged', (event: any) => {
if (event.detail.element === 'searchPanel') {
const inputElement = this.instance.iframeWindow.document.getElementById(
'SearchPanel__input'
) as HTMLInputElement;
const inputElement = this.instance.iframeWindow.document.getElementById('SearchPanel__input') as HTMLInputElement;
setTimeout(() => {
inputElement.value = '';
}, 0);
@ -364,9 +347,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
type: 'actionButton',
element: 'closeCompare',
dataElement: 'closeCompareButton',
img: this._convertPath(
'/assets/icons/general/pdftron-action-close-compare.svg'
),
img: this._convertPath('/assets/icons/general/pdftron-action-close-compare.svg'),
title: 'Leave Compare Mode',
onClick: () => {
this.closeCompareMode();
@ -394,9 +375,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
return;
}
const annotationWrappers = viewerAnnotations
.map(va => this.annotations.find(a => a.id === va.Id))
.filter(va => !!va);
const annotationWrappers = viewerAnnotations.map(va => this.annotations.find(a => a.id === va.Id)).filter(va => !!va);
this.instance.annotationPopup.update([]);
if (annotationWrappers.length === 0) {
@ -405,15 +384,9 @@ export class PdfViewerComponent implements OnInit, OnChanges {
}
// Add hide action as last item
const allAnnotationsHaveImageAction = annotationWrappers.reduce(
(acc, next) => acc && next.isImage,
true
);
const allAnnotationsHaveImageAction = annotationWrappers.reduce((acc, next) => acc && next.isImage, true);
if (allAnnotationsHaveImageAction) {
const allAreVisible = viewerAnnotations.reduce(
(acc, next) => next.isVisible() && acc,
true
);
const allAreVisible = viewerAnnotations.reduce((acc, next) => next.isVisible() && acc, true);
this.instance.annotationPopup.add([
{
@ -437,10 +410,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
}
this.instance.annotationPopup.add(
this._annotationActionsService.getViewerAvailableActions(
annotationWrappers,
this.annotationsChanged
)
this._annotationActionsService.getViewerAvailableActions(annotationWrappers, this.annotationsChanged)
);
}
@ -449,17 +419,12 @@ export class PdfViewerComponent implements OnInit, OnChanges {
type: 'actionButton',
dataElement: 'add-rectangle',
img: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg'),
title: this._translateService.instant(
this._manualAnnotationService.getTitle('REDACTION')
),
title: this._translateService.instant(this._manualAnnotationService.getTitle('REDACTION')),
onClick: () => {
const selectedAnnotations = this.instance.annotManager.getSelectedAnnotations();
const activeAnnotation = selectedAnnotations[0];
const activePage = selectedAnnotations[0].getPageNumber();
const quad = this._annotationDrawService.annotationToQuads(
activeAnnotation,
this.instance
);
const quad = this._annotationDrawService.annotationToQuads(activeAnnotation, this.instance);
const quadsObject = {};
quadsObject[activePage] = [quad];
const mre = this._getManualRedactionEntry(quadsObject, 'Rectangle');
@ -469,13 +434,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
this.instance.enableElements(['shapeToolGroupButton', 'rectangleToolDivider']);
// dispatch event
this.manualAnnotationRequested.emit(
new ManualRedactionEntryWrapper(
[quad],
mre,
'REDACTION',
'RECTANGLE',
activeAnnotation.Id
)
new ManualRedactionEntryWrapper([quad], mre, 'REDACTION', 'RECTANGLE', activeAnnotation.Id)
);
}
});
@ -509,19 +468,13 @@ export class PdfViewerComponent implements OnInit, OnChanges {
type: 'actionButton',
dataElement: 'add-false-positive',
img: this._convertPath('/assets/icons/general/pdftron-action-false-positive.svg'),
title: this._translateService.instant(
this._manualAnnotationService.getTitle('FALSE_POSITIVE')
),
title: this._translateService.instant(this._manualAnnotationService.getTitle('FALSE_POSITIVE')),
onClick: () => {
const selectedQuads = this.instance.docViewer.getSelectedTextQuads();
const text = this.instance.docViewer.getSelectedText();
const mre = this._getManualRedactionEntry(selectedQuads, text, true);
this.manualAnnotationRequested.emit(
new ManualRedactionEntryWrapper(
this.instance.docViewer.getSelectedTextQuads(),
mre,
'FALSE_POSITIVE'
)
new ManualRedactionEntryWrapper(this.instance.docViewer.getSelectedTextQuads(), mre, 'FALSE_POSITIVE')
);
}
});
@ -531,19 +484,13 @@ export class PdfViewerComponent implements OnInit, OnChanges {
type: 'actionButton',
dataElement: 'add-dictionary',
img: this._convertPath('/assets/icons/general/pdftron-action-add-dict.svg'),
title: this._translateService.instant(
this._manualAnnotationService.getTitle('DICTIONARY')
),
title: this._translateService.instant(this._manualAnnotationService.getTitle('DICTIONARY')),
onClick: () => {
const selectedQuads = this.instance.docViewer.getSelectedTextQuads();
const text = this.instance.docViewer.getSelectedText();
const mre = this._getManualRedactionEntry(selectedQuads, text, true);
this.manualAnnotationRequested.emit(
new ManualRedactionEntryWrapper(
this.instance.docViewer.getSelectedTextQuads(),
mre,
'DICTIONARY'
)
new ManualRedactionEntryWrapper(this.instance.docViewer.getSelectedTextQuads(), mre, 'DICTIONARY')
);
}
});
@ -552,19 +499,13 @@ export class PdfViewerComponent implements OnInit, OnChanges {
type: 'actionButton',
dataElement: 'add-redaction',
img: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg'),
title: this._translateService.instant(
this._manualAnnotationService.getTitle('REDACTION')
),
title: this._translateService.instant(this._manualAnnotationService.getTitle('REDACTION')),
onClick: () => {
const selectedQuads = this.instance.docViewer.getSelectedTextQuads();
const text = this.instance.docViewer.getSelectedText();
const mre = this._getManualRedactionEntry(selectedQuads, text, true);
this.manualAnnotationRequested.emit(
new ManualRedactionEntryWrapper(
this.instance.docViewer.getSelectedTextQuads(),
mre,
'REDACTION'
)
new ManualRedactionEntryWrapper(this.instance.docViewer.getSelectedTextQuads(), mre, 'REDACTION')
);
}
});
@ -600,22 +541,13 @@ export class PdfViewerComponent implements OnInit, OnChanges {
}
}
private _getManualRedactionEntry(
quads: any,
text: string,
convertQuads: boolean = false
): ManualRedactionEntry {
private _getManualRedactionEntry(quads: any, text: string, convertQuads: boolean = false): ManualRedactionEntry {
text = text.replace(/-\n/gi, '');
const entry: ManualRedactionEntry = { positions: [] };
for (const key of Object.keys(quads)) {
for (const quad of quads[key]) {
const page = parseInt(key, 10);
entry.positions.push(
this.utils.toPosition(
page,
convertQuads ? this.utils.translateQuads(page, quad) : quad
)
);
entry.positions.push(this.utils.toPosition(page, convertQuads ? this.utils.translateQuads(page, quad) : quad));
}
}
entry.value = text;

View File

@ -4,8 +4,8 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { LegalBasisMappingControllerService } from '@redaction/red-ui-http';
import { AppStateService } from '../../../../state/app-state.service';
import { PermissionsService } from '../../../../services/permissions.service';
import { AppStateService } from '@state/app-state.service';
import { PermissionsService } from '@services/permissions.service';
export interface LegalBasisOption {
label?: string;

View File

@ -1,10 +1,10 @@
import { Component, Inject, ViewChild } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { DossierWrapper } from '../../../../state/model/dossier.wrapper';
import { DossierWrapper } from '@state/model/dossier.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';
import { PermissionsService } from '../../../../services/permissions.service';
import { AppStateService } from '@state/app-state.service';
import { PermissionsService } from '@services/permissions.service';
@Component({
selector: 'redaction-dossier-dictionary-dialog',
@ -23,9 +23,7 @@ export class DossierDictionaryDialogComponent {
private readonly _appStateService: AppStateService,
private readonly _dictionarySaveService: DictionarySaveService
) {
this.canEdit =
this.permissionsService.isDossierMember(this.dossier) ||
this.permissionsService.isAdmin();
this.canEdit = this.permissionsService.isDossierMember(this.dossier) || this.permissionsService.isAdmin();
}
saveDossierDictionary() {
@ -39,10 +37,7 @@ export class DossierDictionaryDialogComponent {
)
.subscribe(async () => {
await this._appStateService.reloadActiveDossierFiles();
this._appStateService.updateDossierDictionary(
this.dossier.dossierTemplateId,
this.dossier.dossierId
);
this._appStateService.updateDossierDictionary(this.dossier.dossierTemplateId, this.dossier.dossierId);
this.dialogRef.close();
});
}

View File

@ -0,0 +1,90 @@
<form (submit)="save()" *ngIf="attributesForm" [formGroup]="attributesForm">
<div>
<div *ngIf="customAttributes.length" class="heading">
{{ 'edit-dossier-dialog.attributes.custom-attributes' | translate }}
</div>
<redaction-empty-state
*ngIf="!customAttributes.length"
icon="red:attribute"
text="edit-dossier-dialog.attributes.no-custom-attributes"
></redaction-empty-state>
<div *ngFor="let attr of customAttributes" [class.datepicker-wrapper]="isDate(attr)" class="red-input-group">
<label>{{ attr.label }}</label>
<input
*ngIf="isNumber(attr) || isText(attr)"
[formControlName]="attr.id"
[name]="attr.id"
[type]="isNumber(attr) ? 'number' : 'text'"
/>
<ng-container *ngIf="isDate(attr)">
<input [formControlName]="attr.id" [matDatepicker]="picker" placeholder="dd/mm/yy" />
<mat-datepicker-toggle [for]="picker" matSuffix>
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
</mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</ng-container>
</div>
</div>
<div class="separator"></div>
<div class="image-attributes-container">
<div *ngIf="imageAttributes.length" class="heading">
{{ 'edit-dossier-dialog.attributes.image-attributes' | translate }}
</div>
<redaction-empty-state
*ngIf="!imageAttributes.length"
icon="red:attribute"
text="edit-dossier-dialog.attributes.no-image-attributes"
></redaction-empty-state>
<div
*ngFor="let attr of imageAttributes"
[class.displayed-preview]="currentAttrValue(attr)"
class="red-input-group image-attribute"
>
<div>
<img *ngIf="currentAttrValue(attr)" [alt]="attr.label" [src]="currentAttrValue(attr)" />
<label>{{ attr.label }}</label>
<redaction-icon-button
(action)="fileInputClick(attr)"
*ngIf="!currentAttrValue(attr)"
[disabled]="!canEdit"
class="upload-button"
icon="red:upload"
text="edit-dossier-dialog.attributes.upload-image"
type="show-bg"
></redaction-icon-button>
<input
#fileInput
(change)="uploadImage($event, attr)"
[id]="attr.id"
[name]="attr.id"
accept="image/*"
class="file-upload-input"
hidden
type="file"
/>
</div>
<div *ngIf="currentAttrValue(attr) && canEdit">
<redaction-circle-button
(action)="deleteAttr(attr)"
icon="red:trash"
tooltip="edit-dossier-dialog.attributes.delete-image"
type="dark-bg"
></redaction-circle-button>
<redaction-circle-button
(action)="fileInputClick(attr)"
icon="red:upload"
tooltip="edit-dossier-dialog.attributes.upload-image"
type="dark-bg"
></redaction-circle-button>
</div>
</div>
</div>
</form>

View File

@ -0,0 +1,72 @@
@import '../../../../../../assets/styles/red-variables';
:host {
display: flex;
min-height: 100%;
form {
display: flex;
flex: 1;
> div {
flex: 1;
padding: 24px 32px;
}
.separator {
flex: none;
height: 100%;
width: 1px;
padding: 0;
background-color: $separator;
}
.datepicker-wrapper {
width: initial;
}
.datepicker-wrapper input {
width: initial;
margin-top: 3px;
}
.datepicker-wrapper:not(:first-child) {
margin-top: 14px;
}
.image-attribute {
&.displayed-preview {
flex-direction: row;
justify-content: space-between;
> div {
flex-direction: row;
align-items: center;
display: flex;
}
}
&:not(first-of-type) {
margin-top: 24px;
}
.upload-button {
margin-top: 6px;
width: fit-content;
}
img {
width: 50px;
height: 50px;
object-fit: cover;
object-position: center;
border: 1px solid $grey-5;
margin-right: 15px;
}
redaction-circle-button:not(:last-child) {
margin-right: 2px;
}
}
}
}

View File

@ -0,0 +1,148 @@
import { Component, ElementRef, EventEmitter, Input, OnInit, Output, QueryList, ViewChildren } from '@angular/core';
import { EditDossierSectionInterface } from '../edit-dossier-section.interface';
import { DossierWrapper } from '@state/model/dossier.wrapper';
import { AppStateService } from '@state/app-state.service';
import { PermissionsService } from '@services/permissions.service';
import { LoadingService } from '@services/loading.service';
import { FormBuilder, FormGroup } from '@angular/forms';
import * as moment from 'moment';
import { DossierAttributeWithValue } from '@models/dossier-attributes.model';
import { DossierAttributesService } from '@shared/services/controller-wrappers/dossier-attributes.service';
@Component({
selector: 'redaction-edit-dossier-attributes',
templateUrl: './edit-dossier-attributes.component.html',
styleUrls: ['./edit-dossier-attributes.component.scss']
})
export class EditDossierAttributesComponent implements EditDossierSectionInterface, OnInit {
@Input() dossierWrapper: DossierWrapper;
@Output() updateDossier = new EventEmitter<any>();
customAttributes: DossierAttributeWithValue[] = [];
imageAttributes: DossierAttributeWithValue[] = [];
attributes: DossierAttributeWithValue[] = [];
attributesForm: FormGroup;
@ViewChildren('fileInput') private _fileInputs: QueryList<ElementRef>;
constructor(
private readonly _appStateService: AppStateService,
private readonly _permissionsService: PermissionsService,
private readonly _dossierAttributesService: DossierAttributesService,
private readonly _loadingService: LoadingService,
private readonly _formBuilder: FormBuilder
) {}
get changed() {
for (const attr of this.attributes) {
if (this.isDate(attr)) {
if (!moment(attr.value).isSame(moment(this.currentAttrValue(attr)))) {
return true;
}
} else if (this._parseAttrValue(attr.value) !== this._parseAttrValue(this.currentAttrValue(attr))) {
return true;
}
}
return false;
}
get disabled() {
return !this._permissionsService.isOwner(this.dossierWrapper);
}
get canEdit(): boolean {
return this._permissionsService.isOwner(this.dossierWrapper);
}
async ngOnInit() {
this._loadingService.start();
await this._loadAttributes();
this._initForm();
this._loadingService.stop();
}
async save() {
this._loadingService.start();
const dossierAttributeList = this.attributes.map(attr => ({
dossierAttributeId: attr.id,
value: this.currentAttrValue(attr)
}));
await this._dossierAttributesService.setValues(this.dossierWrapper, dossierAttributeList);
await this._loadAttributes();
this.updateDossier.emit();
this._loadingService.stop();
}
fileInputClick(attr: DossierAttributeWithValue) {
this._getFileInputById(attr.id).nativeElement.click();
}
isNumber(attr: DossierAttributeWithValue): boolean {
return attr.type === 'NUMBER';
}
isDate(attr: DossierAttributeWithValue): boolean {
return attr.type === 'DATE';
}
isImage(attr: DossierAttributeWithValue): boolean {
return attr.type === 'IMAGE';
}
isText(attr: DossierAttributeWithValue): boolean {
return attr.type === 'TEXT';
}
async uploadImage($event, attr: DossierAttributeWithValue) {
const toBase64 = file =>
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
const image = $event.target.files[0];
const result = await toBase64(image);
this.attributesForm.patchValue({
[attr.id]: result
});
this._getFileInputById(attr.id).nativeElement.value = null;
}
revert() {
this._initForm();
}
currentAttrValue(attr: DossierAttributeWithValue): string {
return this.attributesForm.get(attr.id).value;
}
deleteAttr(attr: DossierAttributeWithValue) {
this.attributesForm.patchValue({
[attr.id]: null
});
}
private _getFileInputById(id: string): ElementRef {
return this._fileInputs.find(el => el.nativeElement.id === id);
}
private _parseAttrValue(value: any) {
return [null, undefined, ''].includes(value) ? undefined : value;
}
private async _loadAttributes() {
this.attributes = await this._dossierAttributesService.getValues(this.dossierWrapper);
this.customAttributes = this.attributes.filter(attr => !this.isImage(attr));
this.imageAttributes = this.attributes.filter(attr => this.isImage(attr));
}
private _initForm() {
const controlsConfig = {};
for (const attribute of this.attributes) {
controlsConfig[attribute.id] = [{ value: attribute.value, disabled: !this.canEdit }];
}
this.attributesForm = this._formBuilder.group(controlsConfig);
}
}

View File

@ -1,8 +1,8 @@
import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import { AppStateService } from '../../../../../state/app-state.service';
import { DossierWrapper } from '../../../../../state/model/dossier.wrapper';
import { AppStateService } from '@state/app-state.service';
import { DossierWrapper } from '@state/model/dossier.wrapper';
import { EditDossierSectionInterface } from '../edit-dossier-section.interface';
import { PermissionsService } from '../../../../../services/permissions.service';
import { PermissionsService } from '@services/permissions.service';
import { DictionaryManagerComponent } from '@shared/components/dictionary-manager/dictionary-manager.component';
import { DictionarySaveService } from '@shared/services/dictionary-save.service';
@ -23,9 +23,7 @@ export class EditDossierDictionaryComponent implements EditDossierSectionInterfa
private readonly _dictionarySaveService: DictionarySaveService,
private readonly _permissionsService: PermissionsService
) {
this.canEdit =
this._permissionsService.isDossierMember(this.dossierWrapper) ||
this._permissionsService.isAdmin();
this.canEdit = this._permissionsService.isDossierMember(this.dossierWrapper) || this._permissionsService.isAdmin();
}
get changed() {
@ -47,10 +45,7 @@ export class EditDossierDictionaryComponent implements EditDossierSectionInterfa
false
)
.subscribe(async () => {
this._appStateService.updateDossierDictionary(
this.dossierWrapper.dossierTemplateId,
this.dossierWrapper.dossierId
);
this._appStateService.updateDossierDictionary(this.dossierWrapper.dossierTemplateId, this.dossierWrapper.dossierId);
this.updateDossier.emit();
});
}

View File

@ -1,8 +1,8 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { DossierTemplateModel } from '@redaction/red-ui-http';
import { FormBuilder, FormGroup } from '@angular/forms';
import { AppStateService } from '../../../../../state/app-state.service';
import { DossierWrapper } from '../../../../../state/model/dossier.wrapper';
import { AppStateService } from '@state/app-state.service';
import { DossierWrapper } from '@state/model/dossier.wrapper';
import { EditDossierSectionInterface } from '../edit-dossier-section.interface';
@Component({
@ -19,10 +19,7 @@ export class EditDossierDownloadPackageComponent implements OnInit, EditDossierS
@Input() dossierWrapper: DossierWrapper;
@Output() updateDossier = new EventEmitter<any>();
constructor(
private readonly _appStateService: AppStateService,
private readonly _formBuilder: FormBuilder
) {}
constructor(private readonly _appStateService: AppStateService, private readonly _formBuilder: FormBuilder) {}
get reportTypesLength() {
return this.dossierForm.controls['reportTypes']?.value?.length || 0;
@ -34,9 +31,7 @@ export class EditDossierDownloadPackageComponent implements OnInit, EditDossierS
get changed() {
for (const key of Object.keys(this.dossierForm.getRawValue())) {
if (
this.dossierWrapper.dossier[key].length !== this.dossierForm.get(key).value.length
) {
if (this.dossierWrapper.dossier[key].length !== this.dossierForm.get(key).value.length) {
return true;
}
@ -65,10 +60,7 @@ export class EditDossierDownloadPackageComponent implements OnInit, EditDossierS
},
{
validators: control =>
control.value.reportTypes?.length > 0 ||
control.value.downloadFileTypes?.length > 0
? null
: { downloadPackage: true }
control.value.reportTypes?.length > 0 || control.value.downloadFileTypes?.length > 0 ? null : { downloadPackage: true }
}
);
}

View File

@ -14,23 +14,16 @@
></div>
</redaction-side-nav>
<div>
<div class="content">
<div class="heading">
{{
'edit-dossier-dialog.nav-items.' +
(activeNavItem.title || activeNavItem.key) | translate
}}
<div [class.no-padding]="noPaddingTab" class="content">
<div *ngIf="showHeading" class="heading">
{{ 'edit-dossier-dialog.nav-items.' + (activeNavItem.title || activeNavItem.key) | translate }}
<div
*ngIf="activeNav === 'dossier-dictionary'"
class="small-label stats-subtitle"
>
<div *ngIf="showSubtitle" class="small-label stats-subtitle">
<div>
<mat-icon svgIcon="red:entries"></mat-icon>
{{
'edit-dossier-dialog.dictionary.entries'
| translate
: { length: (dossierWrapper.type?.entries || []).length }
| translate: { length: (dossierWrapper.type?.entries || []).length }
}}
</div>
</div>
@ -59,6 +52,12 @@
*ngIf="activeNav === 'members'"
[dossierWrapper]="dossierWrapper"
></redaction-edit-dossier-team-members>
<redaction-edit-dossier-attributes
(updateDossier)="updatedDossier($event)"
*ngIf="activeNav === 'dossier-attributes'"
[dossierWrapper]="dossierWrapper"
></redaction-edit-dossier-attributes>
</div>
<div class="dialog-actions">
@ -71,18 +70,10 @@
{{ 'edit-dossier-dialog.actions.save' | translate }}
</button>
<div
(click)="revert()"
class="all-caps-label cancel"
translate="edit-dossier-dialog.actions.revert"
></div>
<div (click)="revert()" class="all-caps-label cancel" translate="edit-dossier-dialog.actions.revert"></div>
</div>
</div>
</div>
<redaction-circle-button
class="dialog-close"
icon="red:close"
mat-dialog-close
></redaction-circle-button>
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
</section>

View File

@ -18,7 +18,11 @@
height: calc(100% - 81px);
box-sizing: border-box;
.heading {
&.no-padding {
padding: 0;
}
::ng-deep .heading {
margin-bottom: 24px;
}
}

View File

@ -6,12 +6,13 @@ import { DossierWrapper } from '../../../../state/model/dossier.wrapper';
import { EditDossierGeneralInfoComponent } from './general-info/edit-dossier-general-info.component';
import { EditDossierDownloadPackageComponent } from './download-package/edit-dossier-download-package.component';
import { EditDossierSectionInterface } from './edit-dossier-section.interface';
import { NotificationService, NotificationType } from '../../../../services/notification.service';
import { NotificationService, NotificationType } from '@services/notification.service';
import { TranslateService } from '@ngx-translate/core';
import { EditDossierDictionaryComponent } from './dictionary/edit-dossier-dictionary.component';
import { EditDossierTeamMembersComponent } from './team-members/edit-dossier-team-members.component';
import { EditDossierAttributesComponent } from './attributes/edit-dossier-attributes.component';
type Section = 'dossier-info' | 'download-package' | 'dossier-dictionary' | 'members';
type Section = 'dossier-info' | 'download-package' | 'dossier-dictionary' | 'members' | 'dossier-attributes';
@Component({
templateUrl: './edit-dossier-dialog.component.html',
@ -22,20 +23,19 @@ export class EditDossierDialogComponent {
{ key: 'dossier-info', title: 'general-info' },
{ key: 'download-package', title: 'choose-download' },
{ key: 'dossier-dictionary', title: 'dossier-dictionary' },
{ key: 'members', title: 'team-members' }
{ key: 'members', title: 'team-members' },
{ key: 'dossier-attributes' }
// TODO:
// { key: 'dossier-attributes' },
// { key: 'report-attributes' }
];
activeNav: Section;
dossierWrapper: DossierWrapper;
@ViewChild(EditDossierGeneralInfoComponent)
generalInfoComponent: EditDossierGeneralInfoComponent;
@ViewChild(EditDossierDownloadPackageComponent)
downloadPackageComponent: EditDossierDownloadPackageComponent;
@ViewChild(EditDossierGeneralInfoComponent) generalInfoComponent: EditDossierGeneralInfoComponent;
@ViewChild(EditDossierDownloadPackageComponent) downloadPackageComponent: EditDossierDownloadPackageComponent;
@ViewChild(EditDossierDictionaryComponent) dictionaryComponent: EditDossierDictionaryComponent;
@ViewChild(EditDossierTeamMembersComponent) membersComponent: EditDossierTeamMembersComponent;
@ViewChild(EditDossierAttributesComponent) attributesComponent: EditDossierAttributesComponent;
constructor(
private readonly _appStateService: AppStateService,
@ -64,10 +64,23 @@ export class EditDossierDialogComponent {
'dossier-info': this.generalInfoComponent,
'download-package': this.downloadPackageComponent,
'dossier-dictionary': this.dictionaryComponent,
members: this.membersComponent
members: this.membersComponent,
'dossier-attributes': this.attributesComponent
}[this.activeNav];
}
get noPaddingTab(): boolean {
return ['dossier-attributes'].includes(this.activeNav);
}
get showHeading(): boolean {
return !['dossier-attributes'].includes(this.activeNav);
}
get showSubtitle(): boolean {
return ['dossier-dictionary'].includes(this.activeNav);
}
updatedDossier(updatedDossier: DossierWrapper) {
this._notificationService.showToastNotification(
this._translateService.instant('edit-dossier-dialog.change-successful'),

View File

@ -6,14 +6,11 @@ import * as moment from 'moment';
import { DossierWrapper } from '../../../../../state/model/dossier.wrapper';
import { EditDossierSectionInterface } from '../edit-dossier-section.interface';
import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
import { PermissionsService } from '../../../../../services/permissions.service';
import { PermissionsService } from '@services/permissions.service';
import { Router } from '@angular/router';
import { MatDialogRef } from '@angular/material/dialog';
import { EditDossierDialogComponent } from '../edit-dossier-dialog.component';
import {
NotificationService,
NotificationType
} from '../../../../../services/notification.service';
import { NotificationService, NotificationType } from '@services/notification.service';
import { TranslateService } from '@ngx-translate/core';
@Component({
@ -47,12 +44,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
if (this.hasDueDate !== !!this.dossierWrapper.dueDate) {
return true;
}
if (
this.hasDueDate &&
!moment(this.dossierWrapper.dueDate).isSame(
moment(this.dossierForm.get(key).value)
)
) {
if (this.hasDueDate && !moment(this.dossierWrapper.dueDate).isSame(moment(this.dossierForm.get(key).value))) {
return true;
}
} else if (this.dossierWrapper[key] !== this.dossierForm.get(key).value) {

View File

@ -2,7 +2,7 @@ import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core
import { AppStateService } from '../../../../../state/app-state.service';
import { DossierWrapper } from '../../../../../state/model/dossier.wrapper';
import { EditDossierSectionInterface } from '../edit-dossier-section.interface';
import { PermissionsService } from '../../../../../services/permissions.service';
import { PermissionsService } from '@services/permissions.service';
import { TeamMembersManagerComponent } from '../../../components/team-members-manager/team-members-manager.component';
@Component({
@ -16,10 +16,7 @@ export class EditDossierTeamMembersComponent implements EditDossierSectionInterf
@ViewChild(TeamMembersManagerComponent) managerComponent: TeamMembersManagerComponent;
constructor(
private readonly _appStateService: AppStateService,
private readonly _permissionsService: PermissionsService
) {}
constructor(private readonly _appStateService: AppStateService, private readonly _permissionsService: PermissionsService) {}
get changed() {
return this.managerComponent.changed;

View File

@ -10,7 +10,7 @@ import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry
import { ManualAnnotationService } from '../../services/manual-annotation.service';
import { ManualAnnotationResponse } from '@models/file/manual-annotation-response';
import { PermissionsService } from '@services/permissions.service';
import { TypeValueWrapper } from '../../../../models/file/type-value.wrapper';
import { TypeValueWrapper } from '@models/file/type-value.wrapper';
export interface LegalBasisOption {
label?: string;
@ -76,8 +76,7 @@ export class ManualAnnotationDialogComponent implements OnInit {
this.isDocumentAdmin = this._permissionsService.isApprover();
this.isFalsePositiveRequest = this.manualRedactionEntryWrapper.type === 'FALSE_POSITIVE';
this.isDictionaryRequest =
this.manualRedactionEntryWrapper.type === 'DICTIONARY' || this.isFalsePositiveRequest;
this.isDictionaryRequest = this.manualRedactionEntryWrapper.type === 'DICTIONARY' || this.isFalsePositiveRequest;
this.redactionForm = this._formBuilder.group({
reason: this.isDictionaryRequest ? [null] : [null, Validators.required],
@ -87,11 +86,7 @@ export class ManualAnnotationDialogComponent implements OnInit {
comment: this.isDocumentAdmin ? [null] : [null, Validators.required]
});
for (const key of Object.keys(
this._appStateService.dictionaryData[
this._appStateService.activeDossier.dossierTemplateId
]
)) {
for (const key of Object.keys(this._appStateService.dictionaryData[this._appStateService.activeDossier.dossierTemplateId])) {
const dictionaryData = this._appStateService.getDictionaryTypeValue(key);
if (!dictionaryData.virtual && dictionaryData.addToDictionaryAction) {
this.redactionDictionaries.push(dictionaryData);
@ -102,15 +97,10 @@ export class ManualAnnotationDialogComponent implements OnInit {
handleAddRedaction() {
this._enhanceManualRedaction(this.manualRedactionEntryWrapper.manualRedactionEntry);
this._manualAnnotationService
.addAnnotation(this.manualRedactionEntryWrapper.manualRedactionEntry)
.subscribe(
response =>
this.dialogRef.close(
new ManualAnnotationResponse(this.manualRedactionEntryWrapper, response)
),
() => this.dialogRef.close()
);
this._manualAnnotationService.addAnnotation(this.manualRedactionEntryWrapper.manualRedactionEntry).subscribe(
response => this.dialogRef.close(new ManualAnnotationResponse(this.manualRedactionEntryWrapper, response)),
() => this.dialogRef.close()
);
}
private _enhanceManualRedaction(addRedactionRequest: AddRedactionRequest) {

View File

@ -1,8 +1,8 @@
import { Component, Inject, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { PermissionsService } from '../../../../services/permissions.service';
import { PermissionsService } from '@services/permissions.service';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { AnnotationWrapper } from '../../../../models/file/annotation.wrapper';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
@Component({
selector: 'redaction-recategorize-image-dialog',

View File

@ -46,13 +46,11 @@ import { ScrollButtonComponent } from './components/scroll-button/scroll-button.
import { ChangeLegalBasisDialogComponent } from './dialogs/change-legal-basis-dialog/change-legal-basis-dialog.component';
import { PageExclusionComponent } from './components/page-exclusion/page-exclusion.component';
import { RecategorizeImageDialogComponent } from './dialogs/recategorize-image-dialog/recategorize-image-dialog.component';
import { EditDossierAttributesComponent } from './dialogs/edit-dossier-dialog/attributes/edit-dossier-attributes.component';
import { DossiersService } from './services/dossiers.service';
import { DossierDetailsStatsComponent } from './components/dossier-details-stats/dossier-details-stats.component';
const screens = [
DossierListingScreenComponent,
DossierOverviewScreenComponent,
FilePreviewScreenComponent
];
const screens = [DossierListingScreenComponent, DossierOverviewScreenComponent, FilePreviewScreenComponent];
const dialogs = [
AddDossierDialogComponent,
@ -87,9 +85,11 @@ const components = [
EditDossierDownloadPackageComponent,
EditDossierDictionaryComponent,
EditDossierTeamMembersComponent,
EditDossierAttributesComponent,
TeamMembersManagerComponent,
ScrollButtonComponent,
PageExclusionComponent,
DossierDetailsStatsComponent,
...screens,
...dialogs

View File

@ -22,13 +22,13 @@ import {
dossierStatusChecker,
dossierTemplateChecker
} from '@shared/components/filters/popup-filter/utils/filter-utils';
import { UserPreferenceService } from '../../../../services/user-preference.service';
import { ButtonConfig } from '../../../shared/components/page-header/models/button-config.model';
import { FilterService } from '../../../shared/services/filter.service';
import { SearchService } from '../../../shared/services/search.service';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { UserPreferenceService } from '@services/user-preference.service';
import { ButtonConfig } from '@shared/components/page-header/models/button-config.model';
import { FilterService } from '@shared/services/filter.service';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { ScreenNames, SortingService } from '@services/sorting.service';
@Component({
templateUrl: './dossier-listing-screen.component.html',

View File

@ -230,6 +230,7 @@
(openAssignDossierMembersDialog)="openAssignDossierMembersDialog()"
(openDossierDictionaryDialog)="openDossierDictionaryDialog()"
(toggleCollapse)="toggleCollapsedDetails()"
[dossierAttributes]="dossierAttributes"
></redaction-dossier-details>
</div>
</div>

View File

@ -1,14 +1,4 @@
import {
ChangeDetectorRef,
Component,
ElementRef,
HostListener,
Injector,
OnDestroy,
OnInit,
TemplateRef,
ViewChild
} from '@angular/core';
import { ChangeDetectorRef, Component, ElementRef, HostListener, Injector, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { NavigationStart, Router } from '@angular/router';
import { NotificationService, NotificationType } from '@services/notification.service';
import { AppStateService } from '@state/app-state.service';
@ -31,19 +21,18 @@ import { convertFiles, handleFileDrop } from '@utils/file-drop-utils';
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { DossierWrapper } from '@state/model/dossier.wrapper';
import { OnAttach, OnDetach } from '@utils/custom-route-reuse.strategy';
import {
annotationFilterChecker,
keyChecker
} from '@shared/components/filters/popup-filter/utils/filter-utils';
import { annotationFilterChecker, keyChecker } from '@shared/components/filters/popup-filter/utils/filter-utils';
import { FilterModel } from '@shared/components/filters/popup-filter/model/filter.model';
import { AppConfigKey, AppConfigService } from '../../../app-config/app-config.service';
import { FilterConfig } from '@shared/components/page-header/models/filter-config.model';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { ActionConfig } from '@shared/components/page-header/models/action-config.model';
import { FilterService } from '../../../shared/services/filter.service';
import { SearchService } from '../../../shared/services/search.service';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
import { FilterService } from '@shared/services/filter.service';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { ScreenNames, SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { LoadingService } from '@services/loading.service';
import { DossierAttributesService } from '@shared/services/controller-wrappers/dossier-attributes.service';
import { DossierAttributeWithValue } from '@models/dossier-attributes.model';
@Component({
templateUrl: './dossier-overview-screen.component.html',
@ -56,9 +45,8 @@ export class DossierOverviewScreenComponent
{
collapsedDetails = false;
readonly itemSize = 80;
filterConfigs: FilterConfig[];
actionConfigs: ActionConfig[];
dossierAttributes: DossierAttributeWithValue[] = [];
@ViewChild(DossierDetailsComponent, { static: false })
private readonly _dossierDetailsComponent: DossierDetailsComponent;
private _filesAutoUpdateTimer: Subscription;
@ -66,7 +54,6 @@ export class DossierOverviewScreenComponent
private _fileChangedSub: Subscription;
private _lastScrollPosition: number;
private _lastOpenedFileId = '';
@ViewChild('needsWorkTemplate', { read: TemplateRef, static: true })
private readonly _needsWorkTemplate: TemplateRef<any>;
@ViewChild('fileInput') private _fileInput: ElementRef;
@ -85,6 +72,8 @@ export class DossierOverviewScreenComponent
private readonly _userPreferenceControllerService: UserPreferenceControllerService,
private readonly _appConfigService: AppConfigService,
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _loadingService: LoadingService,
private readonly _dossierAttributesService: DossierAttributesService,
protected readonly _injector: Injector
) {
super(_injector);
@ -103,30 +92,18 @@ export class DossierOverviewScreenComponent
}
get checkedRequiredFilters() {
return this.filterService
.getFilter('quickFilters')
?.values.filter(f => f.required && f.checked);
return this.filterService.getFilter('quickFilters')?.values.filter(f => f.required && f.checked);
}
get checkedNotRequiredFilters() {
return this.filterService
.getFilter('quickFilters')
?.values.filter(f => !f.required && f.checked);
return this.filterService.getFilter('quickFilters')?.values.filter(f => !f.required && f.checked);
}
isLastOpenedFile(fileStatus: FileStatusWrapper): boolean {
return this._lastOpenedFileId === fileStatus.fileId;
}
ngOnInit(): void {
this._userPreferenceControllerService.getAllUserAttributes().subscribe(attributes => {
if (attributes === null || attributes === undefined) return;
const key = 'Dossier-Recent-' + this.activeDossier.dossierId;
if (attributes[key]?.length > 0) {
this._lastOpenedFileId = attributes[key][0];
}
});
async ngOnInit() {
this._fileDropOverlayService.initFileDropHandling();
this.calculateData();
@ -151,6 +128,19 @@ export class DossierOverviewScreenComponent
this._lastScrollPosition = this.scrollViewport.measureScrollOffset('top');
}
});
this._loadingService.start();
const userAttributes = await this._userPreferenceControllerService.getAllUserAttributes();
if (userAttributes === null || userAttributes === undefined) return;
const key = 'Dossier-Recent-' + this.activeDossier.dossierId;
if (userAttributes[key]?.length > 0) {
this._lastOpenedFileId = userAttributes[key][0];
}
this.dossierAttributes = await this._dossierAttributesService.getValues(this.activeDossier);
this._loadingService.stop();
}
ngOnDestroy(): void {
@ -160,9 +150,9 @@ export class DossierOverviewScreenComponent
this._routerEventsScrollPositionSub.unsubscribe();
}
ngOnAttach() {
async ngOnAttach() {
this._loadEntitiesFromState();
this.ngOnInit();
await this.ngOnInit();
this.scrollViewport.scrollTo({ top: this._lastScrollPosition });
}
@ -195,11 +185,9 @@ export class DossierOverviewScreenComponent
}
isProcessing(fileStatusWrapper: FileStatusWrapper) {
return [
FileStatus.StatusEnum.REPROCESS,
FileStatus.StatusEnum.FULLREPROCESS,
FileStatus.StatusEnum.PROCESSING
].includes(fileStatusWrapper.status);
return [FileStatus.StatusEnum.REPROCESS, FileStatus.StatusEnum.FULLREPROCESS, FileStatus.StatusEnum.PROCESSING].includes(
fileStatusWrapper.status
);
}
reloadDossiers() {
@ -280,9 +268,7 @@ export class DossierOverviewScreenComponent
}
recentlyModifiedChecker = (file: FileStatusWrapper) =>
moment(file.lastUpdated)
.add(this._appConfigService.getConfig(AppConfigKey.RECENT_PERIOD_IN_HOURS), 'hours')
.isAfter(moment());
moment(file.lastUpdated).add(this._appConfigService.getConfig(AppConfigKey.RECENT_PERIOD_IN_HOURS), 'hours').isAfter(moment());
private _loadEntitiesFromState() {
if (this.activeDossier) this._screenStateService.setEntities(this.activeDossier.files);
@ -309,8 +295,7 @@ export class DossierOverviewScreenComponent
allDistinctFileStatusWrapper.add(file.status);
allDistinctAddedDates.add(moment(file.added).format('DD/MM/YYYY'));
if (this.permissionsService.fileRequiresReanalysis(file))
allDistinctNeedsWork.add('analysis');
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');
@ -377,10 +362,7 @@ export class DossierOverviewScreenComponent
checker: (file: FileStatusWrapper) =>
this.checkedRequiredFilters.reduce((acc, f) => acc && f.checker(file), true) &&
(this.checkedNotRequiredFilters.length === 0 ||
this.checkedNotRequiredFilters.reduce(
(acc, f) => acc || f.checker(file),
false
))
this.checkedNotRequiredFilters.reduce((acc, f) => acc || f.checker(file), false))
});
this._createActionConfigs();
@ -389,9 +371,7 @@ export class DossierOverviewScreenComponent
private _createQuickFilters() {
let quickFilters = [];
if (this._screenStateService.entities.filter(this.recentlyModifiedChecker).length > 0) {
const recentPeriod = this._appConfigService.getConfig(
AppConfigKey.RECENT_PERIOD_IN_HOURS
);
const recentPeriod = this._appConfigService.getConfig(AppConfigKey.RECENT_PERIOD_IN_HOURS);
quickFilters = [
{
key: 'recent',
@ -408,9 +388,7 @@ export class DossierOverviewScreenComponent
...quickFilters,
{
key: 'assigned-to-me',
label: this._translateService.instant(
'dossier-overview.quick-filters.assigned-to-me'
),
label: this._translateService.instant('dossier-overview.quick-filters.assigned-to-me'),
checker: (file: FileStatusWrapper) => file.currentReviewer === this.user.id
},
{
@ -420,11 +398,8 @@ export class DossierOverviewScreenComponent
},
{
key: 'assigned-to-others',
label: this._translateService.instant(
'dossier-overview.quick-filters.assigned-to-others'
),
checker: (file: FileStatusWrapper) =>
!!file.currentReviewer && file.currentReviewer !== this.user.id
label: this._translateService.instant('dossier-overview.quick-filters.assigned-to-others'),
checker: (file: FileStatusWrapper) => !!file.currentReviewer && file.currentReviewer !== this.user.id
}
];
}

View File

@ -1,12 +1,4 @@
import {
ChangeDetectorRef,
Component,
HostListener,
NgZone,
OnDestroy,
OnInit,
ViewChild
} from '@angular/core';
import { ChangeDetectorRef, Component, HostListener, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router';
import { AppStateService } from '@state/app-state.service';
import { Annotations, WebViewerInstance } from '@pdftron/webviewer';
@ -41,11 +33,8 @@ import { FileWorkloadComponent } from '../../components/file-workload/file-workl
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { OnAttach, OnDetach } from '@utils/custom-route-reuse.strategy';
import { FilterModel } from '@shared/components/filters/popup-filter/model/filter.model';
import {
handleFilterDelta,
processFilters
} from '@shared/components/filters/popup-filter/utils/filter-utils';
import { LoadingService } from '../../../../services/loading.service';
import { handleFilterDelta, processFilters } from '@shared/components/filters/popup-filter/utils/filter-utils';
import { LoadingService } from '@services/loading.service';
import { stampPDFPage } from '../../../../utils/page-stamper';
import { TranslateService } from '@ngx-translate/core';
@ -117,9 +106,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
}
get assignTooltip(): string {
return this.appStateService.activeFile.isUnderApproval
? 'dossier-overview.assign-approver'
: this.assignOrChangeReviewerTooltip;
return this.appStateService.activeFile.isUnderApproval ? 'dossier-overview.assign-approver' : this.assignOrChangeReviewerTooltip;
}
get annotations(): AnnotationWrapper[] {
@ -139,25 +126,15 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
}
get canSwitchToRedactedView() {
return (
this.fileData &&
!this.permissionsService.fileRequiresReanalysis() &&
!this.fileData.fileStatus.isExcluded
);
return this.fileData && !this.permissionsService.fileRequiresReanalysis() && !this.fileData.fileStatus.isExcluded;
}
get canSwitchToDeltaView() {
return (
this.fileData?.redactionChangeLog?.redactionLogEntry?.length > 0 &&
!this.fileData.fileStatus.isExcluded
);
return this.fileData?.redactionChangeLog?.redactionLogEntry?.length > 0 && !this.fileData.fileStatus.isExcluded;
}
get canAssign(): boolean {
return (
!this.editingReviewer &&
(this.permissionsService.canAssignUser() || this.permissionsService.canAssignToSelf())
);
return !this.editingReviewer && (this.permissionsService.canAssignUser() || this.permissionsService.canAssignToSelf());
}
get displayData() {
@ -181,9 +158,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
}
get assignOrChangeReviewerTooltip() {
return this.currentReviewer
? 'file-preview.change-reviewer'
: 'file-preview.assign-reviewer';
return this.currentReviewer ? 'file-preview.change-reviewer' : 'file-preview.assign-reviewer';
}
get currentReviewer(): string {
@ -204,9 +179,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
get canAssignReviewer(): boolean {
return (
!this.currentReviewer &&
this.permissionsService.canAssignUser() &&
this.appStateService.activeDossier.hasMoreThanOneReviewer
!this.currentReviewer && this.permissionsService.canAssignUser() && this.appStateService.activeDossier.hasMoreThanOneReviewer
);
}
@ -217,12 +190,8 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
switch (this.viewMode) {
case 'STANDARD': {
this._setAnnotationsColor(redactions, 'annotationColor');
const standardEntries = annotations.filter(
a => !a.getCustomData('changeLogRemoved')
);
const nonStandardEntries = annotations.filter(a =>
a.getCustomData('changeLogRemoved')
);
const standardEntries = annotations.filter(a => !a.getCustomData('changeLogRemoved'));
const nonStandardEntries = annotations.filter(a => a.getCustomData('changeLogRemoved'));
this._show(standardEntries);
this._hide(nonStandardEntries);
break;
@ -271,9 +240,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
this._updateCanPerformActions();
try {
const key = 'Dossier-Recent-' + this.dossierId;
await this._userPreferenceControllerService
.savePreferences([this.fileId], key)
.toPromise();
await this._userPreferenceControllerService.savePreferences([this.fileId], key).toPromise();
} catch (error) {
console.error(error);
}
@ -299,35 +266,22 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
console.error(error);
}
}
console.log(
'[REDACTION] Delete previous annotations time: ' +
(new Date().getTime() - startTime) +
'ms'
);
console.log('[REDACTION] Delete previous annotations time: ' + (new Date().getTime() - startTime) + 'ms');
const processStartTime = new Date().getTime();
this.annotationData = this.fileData.getAnnotations(
this.appStateService.dictionaryData[
this.appStateService.activeDossier.dossierTemplateId
],
this.appStateService.dictionaryData[this.appStateService.activeDossier.dossierTemplateId],
this.permissionsService.currentUser,
this.viewMode,
this.userPreferenceService.areDevFeaturesEnabled
);
const annotationFilters = this._annotationProcessingService.getAnnotationFilter(
this.annotations
);
const annotationFilters = this._annotationProcessingService.getAnnotationFilter(this.annotations);
this.primaryFilters = processFilters(this.primaryFilters, annotationFilters);
this.secondaryFilters = processFilters(
this.secondaryFilters,
AnnotationProcessingService.secondaryAnnotationFilters
);
this.secondaryFilters = processFilters(this.secondaryFilters, AnnotationProcessingService.secondaryAnnotationFilters);
this._workloadComponent?.filtersChanged({
primary: this.primaryFilters,
secondary: this.secondaryFilters
});
console.log(
'[REDACTION] Process time: ' + (new Date().getTime() - processStartTime) + 'ms'
);
console.log('[REDACTION] Process time: ' + (new Date().getTime() - processStartTime) + 'ms');
console.log(
'[REDACTION] Annotation Redraw and filter rebuild time: ' +
(new Date().getTime() - startTime) +
@ -348,11 +302,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
this._changeDetectorRef.detectChanges();
}
selectAnnotations(
annotations?:
| AnnotationWrapper[]
| { annotations: AnnotationWrapper[]; multiSelect: boolean }
) {
selectAnnotations(annotations?: AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }) {
if (annotations) {
this.viewerComponent.utils.selectAnnotations(annotations);
} else {
@ -371,24 +321,19 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
openManualAnnotationDialog($event: ManualRedactionEntryWrapper) {
this._ngZone.run(() => {
this.dialogRef = this._dialogService.openManualAnnotationDialog(
$event,
async (response: ManualAnnotationResponse) => {
if (response?.annotationId) {
const annotation = this._instance.annotManager.getAnnotationById(
response.manualRedactionEntryWrapper.rectId
);
this._instance.annotManager.deleteAnnotation(annotation);
this.fileData.fileStatus = await this.appStateService.reloadActiveFile();
const distinctPages = $event.manualRedactionEntry.positions
.map(p => p.page)
.filter((item, pos, self) => self.indexOf(item) === pos);
distinctPages.forEach(page => {
this._cleanupAndRedrawManualAnnotationsForEntirePage(page);
});
}
this.dialogRef = this._dialogService.openManualAnnotationDialog($event, async (response: ManualAnnotationResponse) => {
if (response?.annotationId) {
const annotation = this._instance.annotManager.getAnnotationById(response.manualRedactionEntryWrapper.rectId);
this._instance.annotManager.deleteAnnotation(annotation);
this.fileData.fileStatus = await this.appStateService.reloadActiveFile();
const distinctPages = $event.manualRedactionEntry.positions
.map(p => p.page)
.filter((item, pos, self) => self.indexOf(item) === pos);
distinctPages.forEach(page => {
this._cleanupAndRedrawManualAnnotationsForEntirePage(page);
});
}
);
});
});
}
@ -407,10 +352,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
return;
}
if (
!ALL_HOTKEY_ARRAY.includes($event.key) ||
this.dialogRef?.getState() === MatDialogState.OPEN
) {
if (!ALL_HOTKEY_ARRAY.includes($event.key) || this.dialogRef?.getState() === MatDialogState.OPEN) {
return;
}
@ -524,9 +466,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
const reviewerName = this.userService.getNameForId(reviewerId);
const { dossierId, fileId, filename } = this.fileData.fileStatus;
await this._statusControllerService
.setFileReviewer(dossierId, fileId, reviewerId)
.toPromise();
await this._statusControllerService.setFileReviewer(dossierId, fileId, reviewerId).toPromise();
const msg = `Successfully assigned ${reviewerName} to file: ${filename}`;
this._notificationService.showToastNotification(msg);
@ -549,13 +489,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
downloadOriginalFile() {
this._fileManagementControllerService
.downloadOriginalFile(
this.dossierId,
this.fileId,
true,
this.fileData.fileStatus.cacheIdentifier,
'response'
)
.downloadOriginalFile(this.dossierId, this.fileId, true, this.fileData.fileStatus.cacheIdentifier, 'response')
.subscribe(data => {
download(data, this.fileData.fileStatus.filename);
});
@ -608,17 +542,15 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
this.filesAutoUpdateTimer = timer(0, 5000)
.pipe(tap(async () => await this.appStateService.reloadActiveFile()))
.subscribe();
this.fileReanalysedSubscription = this.appStateService.fileReanalysed.subscribe(
async (fileStatus: FileStatusWrapper) => {
if (fileStatus.fileId === this.fileId) {
await this._loadFileData(!this._reloadFileOnReanalysis);
this._reloadFileOnReanalysis = false;
this.viewReady = true;
this._updateCanPerformActions();
this._cleanupAndRedrawManualAnnotations();
}
this.fileReanalysedSubscription = this.appStateService.fileReanalysed.subscribe(async (fileStatus: FileStatusWrapper) => {
if (fileStatus.fileId === this.fileId) {
await this._loadFileData(!this._reloadFileOnReanalysis);
this._reloadFileOnReanalysis = false;
this.viewReady = true;
this._updateCanPerformActions();
this._cleanupAndRedrawManualAnnotations();
}
);
});
}
private _unsubscribeFromFileUpdates(): void {
@ -663,10 +595,8 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
/* Get the documentElement (<html>) to display the page in fullscreen */
private _cleanupAndRedrawManualAnnotations() {
this._fileDownloadService
.loadActiveFileRedactionLogPreview()
.subscribe(redactionLogPreview => {
this.fileData.redactionLog = redactionLogPreview;
this._fileDownloadService.loadActiveFileRedactionLogPreview().subscribe(redactionLogPreview => {
this.fileData.redactionLog = redactionLogPreview;
this._annotationDrawService.drawAnnotations(
this._instance,
this.annotationData.allAnnotations,
@ -681,18 +611,14 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
const currentPageAnnotationIds = currentPageAnnotations.map(a => a.id);
this.fileData.fileStatus = await this.appStateService.reloadActiveFile();
this._fileDownloadService
.loadActiveFileRedactionLogPreview()
.subscribe(redactionLogPreview => {
this.fileData.redactionLog = redactionLogPreview;
this._fileDownloadService.loadActiveFileRedactionLogPreview().subscribe(redactionLogPreview => {
this.fileData.redactionLog = redactionLogPreview;
this.rebuildFilters();
if (this.viewMode === 'STANDARD') {
currentPageAnnotationIds.forEach(id => {
this._findAndDeleteAnnotation(id);
});
const newPageAnnotations = this.annotations.filter(
item => item.pageNumber === page
);
const newPageAnnotations = this.annotations.filter(item => item.pageNumber === page);
this._handleDeltaAnnotationFilters(currentPageAnnotations, newPageAnnotations);
this._annotationDrawService.drawAnnotations(
this._instance,
@ -704,22 +630,16 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
});
}
private _handleDeltaAnnotationFilters(
currentPageAnnotations: AnnotationWrapper[],
newPageAnnotations: AnnotationWrapper[]
) {
private _handleDeltaAnnotationFilters(currentPageAnnotations: AnnotationWrapper[], newPageAnnotations: AnnotationWrapper[]) {
const hasAnyFilterSet =
this.primaryFilters.find(f => f.checked || f.indeterminate) ||
this.secondaryFilters.find(f => f.checked || f.indeterminate);
this.primaryFilters.find(f => f.checked || f.indeterminate) || this.secondaryFilters.find(f => f.checked || f.indeterminate);
if (!hasAnyFilterSet) {
return;
}
const oldPageSpecificFilters =
this._annotationProcessingService.getAnnotationFilter(currentPageAnnotations);
const newPageSpecificFilters =
this._annotationProcessingService.getAnnotationFilter(newPageAnnotations);
const oldPageSpecificFilters = this._annotationProcessingService.getAnnotationFilter(currentPageAnnotations);
const newPageSpecificFilters = this._annotationProcessingService.getAnnotationFilter(newPageAnnotations);
handleFilterDelta(oldPageSpecificFilters, newPageSpecificFilters, this.primaryFilters);
this._workloadComponent.filtersChanged({
primary: this.primaryFilters,

View File

@ -17,12 +17,12 @@ import { ManualAnnotationService } from './manual-annotation.service';
import { ManualAnnotationDialogComponent } from '../dialogs/manual-redaction-dialog/manual-annotation-dialog.component';
import { DossierDictionaryDialogComponent } from '../dialogs/dossier-dictionary-dialog/dossier-dictionary-dialog.component';
import { EditDossierDialogComponent } from '../dialogs/edit-dossier-dialog/edit-dossier-dialog.component';
import { FileStatusWrapper } from '../../../models/file/file-status.wrapper';
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
import { AssignReviewerApproverDialogComponent } from '../dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component';
import { AppConfigService } from '../../app-config/app-config.service';
import { ChangeLegalBasisDialogComponent } from '../dialogs/change-legal-basis-dialog/change-legal-basis-dialog.component';
import { RecategorizeImageDialogComponent } from '../dialogs/recategorize-image-dialog/recategorize-image-dialog.component';
import { DialogService } from '../../shared/services/dialog.service';
import { DialogService } from '@shared/services/dialog.service';
import { ComponentType } from '@angular/cdk/portal';
const dialogConfig = {
@ -54,7 +54,7 @@ export class DossiersDialogService extends DialogService<DialogType> {
},
editDossier: {
component: EditDossierDialogComponent,
dialogConfig: { ...this._largeConfig, autoFocus: true }
dialogConfig: { ...this._largeConfig }
}
};
@ -67,10 +67,7 @@ export class DossiersDialogService extends DialogService<DialogType> {
super(_dialog);
}
openManualAnnotationDialog(
$event: ManualRedactionEntryWrapper,
cb?: Function
): MatDialogRef<ManualAnnotationDialogComponent> {
openManualAnnotationDialog($event: ManualRedactionEntryWrapper, cb?: Function): MatDialogRef<ManualAnnotationDialogComponent> {
const ref = this._dialog.open(ManualAnnotationDialogComponent, {
...dialogConfig,
autoFocus: true,
@ -96,13 +93,11 @@ export class DossiersDialogService extends DialogService<DialogType> {
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
ref.afterClosed().subscribe(result => {
if (result) {
this._manualAnnotationService
.approveRequest(annotation.id)
.subscribe(acceptResult => {
if (callback) {
callback(acceptResult);
}
});
this._manualAnnotationService.approveRequest(annotation.id).subscribe(acceptResult => {
if (callback) {
callback(acceptResult);
}
});
}
});
@ -132,10 +127,7 @@ export class DossiersDialogService extends DialogService<DialogType> {
return ref;
}
openForceRedactionDialog(
$event: MouseEvent,
cb?: Function
): MatDialogRef<ForceRedactionDialogComponent> {
openForceRedactionDialog($event: MouseEvent, cb?: Function): MatDialogRef<ForceRedactionDialogComponent> {
$event?.stopPropagation();
const ref = this._dialog.open(ForceRedactionDialogComponent, {
...dialogConfig
@ -203,11 +195,7 @@ export class DossiersDialogService extends DialogService<DialogType> {
return ref;
}
openDeleteDossierDialog(
$event: MouseEvent,
dossier: DossierWrapper,
cb?: Function
): MatDialogRef<ConfirmationDialogComponent> {
openDeleteDossierDialog($event: MouseEvent, dossier: DossierWrapper, cb?: Function): MatDialogRef<ConfirmationDialogComponent> {
$event.stopPropagation();
const period = this._appConfigService.getConfig('DELETE_RETENTION_HOURS');
const ref = this._dialog.open(ConfirmationDialogComponent, {
@ -299,11 +287,7 @@ export class DossiersDialogService extends DialogService<DialogType> {
return ref;
}
openRemoveAnnotationModal(
$event: MouseEvent,
annotation: AnnotationWrapper,
callback: () => void
) {
openRemoveAnnotationModal($event: MouseEvent, annotation: AnnotationWrapper, callback: () => void) {
$event?.stopPropagation();
const ref = this._dialog.open(ConfirmationDialogComponent, {
@ -313,13 +297,11 @@ export class DossiersDialogService extends DialogService<DialogType> {
ref.afterClosed().subscribe(result => {
if (result) {
this._manualAnnotationService
.removeOrSuggestRemoveAnnotation(annotation)
.subscribe(() => {
if (callback) {
callback();
}
});
this._manualAnnotationService.removeOrSuggestRemoveAnnotation(annotation).subscribe(() => {
if (callback) {
callback();
}
});
}
});

View File

@ -1,8 +1,8 @@
import { Annotations, WebViewerInstance } from '@pdftron/webviewer';
import { ViewMode } from '../../../models/file/view-mode';
import { ViewMode } from '@models/file/view-mode';
import { translateQuads } from '../../../utils/pdf-coordinates';
import { Rectangle } from '@redaction/red-ui-http';
import { AnnotationWrapper } from '../../../models/file/annotation.wrapper';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
const DISABLED_HOTKEYS = [
'CTRL+SHIFT+EQUAL',
@ -74,9 +74,7 @@ export class PdfViewerUtils {
}
try {
return this.isCompareMode
? Math.ceil(this.instance?.docViewer?.getPageCount() / 2)
: this.instance?.docViewer?.getPageCount();
return this.isCompareMode ? Math.ceil(this.instance?.docViewer?.getPageCount() / 2) : this.instance?.docViewer?.getPageCount();
} catch (e) {
console.error(e);
return null;
@ -93,9 +91,7 @@ export class PdfViewerUtils {
navigateToPage(pageNumber: string | number) {
const parsedNumber = typeof pageNumber === 'string' ? parseInt(pageNumber, 10) : pageNumber;
this._navigateToPage(
this.paginationOffset === 2 ? parsedNumber * this.paginationOffset - 1 : parsedNumber
);
this._navigateToPage(this.paginationOffset === 2 ? parsedNumber * this.paginationOffset - 1 : parsedNumber);
}
previousPage() {
@ -106,12 +102,7 @@ export class PdfViewerUtils {
nextPage() {
if (this._currentInternalPage < this._totalInternalPages) {
this._navigateToPage(
Math.min(
this._currentInternalPage + this.paginationOffset,
this._totalInternalPages
)
);
this._navigateToPage(Math.min(this._currentInternalPage + this.paginationOffset, this._totalInternalPages));
}
}
@ -144,9 +135,7 @@ export class PdfViewerUtils {
this.instance.annotManager.deselectAllAnnotations();
}
selectAnnotations(
$event: AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }
) {
selectAnnotations($event: AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }) {
let annotations: AnnotationWrapper[];
let multiSelect: boolean;
if ($event instanceof Array) {

View File

@ -14,11 +14,7 @@ export function processFilters(oldFilters: FilterModel[], newFilters: FilterMode
return newFilters;
}
export function handleFilterDelta(
oldFilters: FilterModel[],
newFilters: FilterModel[],
allFilters: FilterModel[]
) {
export function handleFilterDelta(oldFilters: FilterModel[], newFilters: FilterModel[], allFilters: FilterModel[]) {
const newFiltersDelta = {};
for (const newFilter of newFilters) {
const oldFilter = oldFilters.find(f => f.key === newFilter.key);
@ -87,13 +83,7 @@ export function handleCheckedValue(filter: FilterModel) {
}
}
export function checkFilter(
entity: any,
filters: FilterModel[],
validate: Function,
validateArgs: any = [],
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) {
@ -121,8 +111,7 @@ export function checkFilter(
return filterMatched;
}
export const keyChecker = (key: string) => (entity: any, filter: FilterModel) =>
entity[key] === filter.key;
export const keyChecker = (key: string) => (entity: any, filter: FilterModel) => entity[key] === filter.key;
export const annotationFilterChecker = (
input: FileStatusWrapper | DossierWrapper,
@ -158,38 +147,24 @@ export const annotationFilterChecker = (
}
};
export const dossierStatusChecker = (dw: DossierWrapper, filter: FilterModel) =>
dw.hasStatus(filter.key);
export const dossierStatusChecker = (dw: DossierWrapper, filter: FilterModel) => dw.hasStatus(filter.key);
export const dossierMemberChecker = (dw: DossierWrapper, filter: FilterModel) =>
dw.hasMember(filter.key);
export const dossierMemberChecker = (dw: DossierWrapper, filter: FilterModel) => dw.hasMember(filter.key);
export const dossierTemplateChecker = (dw: DossierWrapper, filter: FilterModel) =>
dw.dossierTemplateId === filter.key;
export const dossierTemplateChecker = (dw: DossierWrapper, filter: FilterModel) => dw.dossierTemplateId === filter.key;
export const dueDateChecker = (dw: DossierWrapper, filter: FilterModel) =>
dw.dueDateMatches(filter.key);
export const dueDateChecker = (dw: DossierWrapper, filter: FilterModel) => dw.dueDateMatches(filter.key);
export const addedDateChecker = (dw: DossierWrapper, filter: FilterModel) =>
dw.addedDateMatches(filter.key);
export const addedDateChecker = (dw: DossierWrapper, filter: FilterModel) => dw.addedDateMatches(filter.key);
export const dossierApproverChecker = (dw: DossierWrapper, filter: FilterModel) =>
dw.approverIds.includes(filter.key);
export const dossierApproverChecker = (dw: DossierWrapper, filter: FilterModel) => dw.approverIds.includes(filter.key);
export function getFilteredEntities<T>(entities: T[], filters: FilterWrapper[]) {
const filteredEntities: T[] = [];
for (const entity of entities) {
let add = true;
for (const filter of filters) {
add =
add &&
checkFilter(
entity,
filter.values,
filter.checker,
filter.checkerArgs,
filter.matchAll
);
add = add && checkFilter(entity, filter.values, filter.checker, filter.checkerArgs, filter.matchAll);
}
if (add) {
filteredEntities.push(entity);

View File

@ -0,0 +1,57 @@
import { Injectable } from '@angular/core';
import {
DossierAttributeConfig,
DossierAttributeReq,
DossierAttributesControllerService,
DossierAttributesRes
} from '@redaction/red-ui-http';
import { DossierWrapper } from '@state/model/dossier.wrapper';
import { DossierAttributeWithValue } from '@models/dossier-attributes.model';
import { AppStateService } from '@state/app-state.service';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class DossierAttributesService {
constructor(
private readonly _dossierAttributesControllerService: DossierAttributesControllerService,
private readonly _appStateService: AppStateService
) {}
async getValues(dossierWrapper: DossierWrapper): Promise<DossierAttributeWithValue[]> {
const attributes = await this._dossierAttributesControllerService.getDossierAttributes(dossierWrapper.dossierId).toPromise();
const attributesConfig = await this._dossierAttributesControllerService
.getDossierAttributesConfig(dossierWrapper.dossierTemplateId)
.toPromise();
return attributesConfig.dossierAttributeConfigs.map(config => ({
...config,
value: attributes.dossierAttributeList.find(attr => attr.dossierAttributeId === config.id)?.value
}));
}
setValues(dossierWrapper: DossierWrapper, dossierAttributeList: DossierAttributeReq[]): Promise<DossierAttributesRes> {
return this._dossierAttributesControllerService
.setDossierAttributes({ dossierAttributeList }, dossierWrapper.dossierId)
.toPromise();
}
deleteConfigs(ids: string[], dossierTemplateId = this._appStateService.activeDossierTemplateId): Promise<void> {
return this._dossierAttributesControllerService.deleteDossierAttributesConfig(ids, dossierTemplateId).toPromise();
}
async getConfig(dossierTemplateId = this._appStateService.activeDossierTemplateId): Promise<DossierAttributeConfig[]> {
return (
(await this._dossierAttributesControllerService.getDossierAttributesConfig(dossierTemplateId).toPromise())
?.dossierAttributeConfigs || []
);
}
addOrUpdateConfig(
attribute: DossierAttributeConfig,
dossierTemplateId = this._appStateService.activeDossierTemplateId
): Observable<DossierAttributeConfig> {
return this._dossierAttributesControllerService.addOrUpdateDossierAttributesConfig(attribute, dossierTemplateId);
}
}

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { Observable, throwError } from 'rxjs';
import { NotificationService, NotificationType } from '../../../services/notification.service';
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';
@ -36,20 +36,9 @@ export class DictionarySaveService {
// can add at least 1 - block UI
let obs: Observable<any>;
if (entriesToAdd.length > 0) {
obs = this._dictionaryControllerService.addEntry(
entriesToAdd,
dossierTemplateId,
type,
dossierId,
true
);
obs = this._dictionaryControllerService.addEntry(entriesToAdd, dossierTemplateId, type, dossierId, true);
} else {
obs = this._dictionaryControllerService.deleteEntries(
initialEntries,
dossierTemplateId,
type,
dossierId
);
obs = this._dictionaryControllerService.deleteEntries(initialEntries, dossierTemplateId, type, dossierId);
}
return obs.pipe(
@ -57,9 +46,7 @@ export class DictionarySaveService {
() => {
if (showToast) {
this._notificationService.showToastNotification(
this._translateService.instant(
'dictionary-overview.success.generic'
),
this._translateService.instant('dictionary-overview.success.generic'),
null,
NotificationType.SUCCESS
);

View File

@ -40,13 +40,7 @@ import { InputWithActionComponent } from '@shared/components/input-with-action/i
import { PageHeaderComponent } from './components/page-header/page-header.component';
import { DatePipe } from '@shared/pipes/date.pipe';
const buttons = [
ChevronButtonComponent,
CircleButtonComponent,
FileDownloadBtnComponent,
IconButtonComponent,
UserButtonComponent
];
const buttons = [ChevronButtonComponent, CircleButtonComponent, FileDownloadBtnComponent, IconButtonComponent, UserButtonComponent];
const components = [
FullPageLoadingIndicatorComponent,
@ -74,22 +68,9 @@ const components = [
...buttons
];
const utils = [
HumanizePipe,
DatePipe,
SyncWidthDirective,
HasScrollbarDirective,
NavigateLastDossiersScreenDirective
];
const utils = [HumanizePipe, DatePipe, SyncWidthDirective, HasScrollbarDirective, NavigateLastDossiersScreenDirective];
const modules = [
MatConfigModule,
TranslateModule,
ScrollingModule,
IconsModule,
FormsModule,
ReactiveFormsModule
];
const modules = [MatConfigModule, TranslateModule, ScrollingModule, IconsModule, FormsModule, ReactiveFormsModule];
@NgModule({
declarations: [...components, ...utils],

View File

@ -657,6 +657,11 @@
"action": "Delete File"
},
"dossier-details": {
"attributes": {
"image-uploaded": "Image uploaded",
"expand": "{{count}} custom attributes",
"show-less": "show less"
},
"charts": {
"documents-in-dossier": "Documents in Dossier"
},
@ -833,6 +838,14 @@
"report-attributes": "Report Attributes",
"team-members": "Team Members"
},
"attributes": {
"custom-attributes": "Custom Dossier Attributes",
"image-attributes": "Image Attributes",
"upload-image": "Upload Image",
"delete-image": "Delete Image",
"no-custom-attributes": "There are no text attributes",
"no-image-attributes": "There are no image attributes"
},
"unsaved-changes": "You have unsaved changes. Save or revert before changing the tab."
},
"efsa": "EFSA Approval",

View File

@ -196,6 +196,7 @@ form {
.mat-datepicker-toggle {
position: absolute;
right: 0;
bottom: 0;
color: $accent;
&.mat-datepicker-toggle-active {