remove unused methods, add AutoUnsubscribeComponent

This commit is contained in:
Dan Percic 2021-07-14 23:12:28 +03:00
parent cb3cd4db11
commit 6932fa6b30
46 changed files with 466 additions and 734 deletions

View File

@ -47,15 +47,11 @@ export class EditColorDialogComponent {
};
try {
await this._dictionaryControllerService
.setColors(colors, this._dossierTemplateId)
.toPromise();
await this._dictionaryControllerService.setColors(colors, this._dossierTemplateId).toPromise();
this._dialogRef.close(true);
this._notificationService.showToastNotification(
this._translateService.instant('edit-color-dialog.success', {
color: this._translateService.instant(
'default-colors-screen.types.' + this.colorKey
)
color: this._translateService.instant('default-colors-screen.types.' + this.colorKey)
})
);
} catch (e) {

View File

@ -7,7 +7,7 @@
></redaction-round-checkbox>
</div>
<span class="all-caps-label">
{{ 'file-attributes-csv-import.table-header.title' | translate: { length: allEntities.length } }}
{{ 'file-attributes-csv-import.table-header.title' | translate: { length: (allEntities$ | async)?.length } }}
</span>
<ng-container *ngIf="areSomeEntitiesSelected$ | async">

View File

@ -23,38 +23,36 @@ export class ActiveFieldsListingComponent extends BaseListingComponent<Field> im
constructor(protected readonly _injector: Injector) {
super(_injector);
this._screenStateService.setIdKey('csvColumn');
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.entities) {
this._screenStateService.setEntities(this.entities);
this._screenStateService.setDisplayedEntities(this.entities);
this._screenStateService.updateSelection();
this.screenStateService.setEntities(this.entities);
this.screenStateService.setDisplayedEntities(this.entities);
this.screenStateService.updateSelection();
}
}
deactivateSelection() {
this.allEntities.filter(field => this.isSelected(field)).forEach(field => (field.primaryAttribute = false));
this._screenStateService.setEntities([...this.allEntities.filter(field => !this.isSelected(field))]);
this.screenStateService.setEntities(this.allEntities.filter(field => !this.isSelected(field)));
this.entitiesChange.emit(this.allEntities);
this._screenStateService.setSelectedEntitiesIds([]);
this.screenStateService.setSelectedEntities([]);
}
setAttributeForSelection(attribute: string, value: any) {
for (const csvColumn of this._screenStateService.selectedEntitiesIds) {
this.allEntities.find(f => f.csvColumn === csvColumn)[attribute] = value;
for (const item of this.screenStateService.selectedEntities) {
this.allEntities.find(f => f.csvColumn === item.csvColumn)[attribute] = value;
}
}
togglePrimary(field: Field) {
if (field.primaryAttribute) {
field.primaryAttribute = false;
} else {
for (const f of this.allEntities) {
f.primaryAttribute = false;
}
field.primaryAttribute = true;
return;
}
for (const f of this.allEntities) f.primaryAttribute = false;
field.primaryAttribute = true;
}
}

View File

@ -4,37 +4,25 @@
<div class="dialog-content">
<div class="sub-header">
<div class="left">
<div class="info">
<span translate="file-attributes-csv-import.file"> </span> {{ csvFile.name }}
</div>
<div class="info"><span translate="file-attributes-csv-import.file"> </span> {{ csvFile.name }}</div>
<div class="large-label">
{{
'file-attributes-csv-import.total-rows'
| translate: { rows: parseResult?.data?.length }
}}
{{ 'file-attributes-csv-import.total-rows' | translate: { rows: parseResult?.data?.length } }}
</div>
</div>
<div class="right">
<form (submit)="changedParseConfig && readFile()" [formGroup]="baseConfigForm">
<div class="red-input-group required w-250">
<mat-form-field floatLabel="always">
<mat-label>{{
'file-attributes-csv-import.key-column' | translate
}}</mat-label>
<mat-label>{{ 'file-attributes-csv-import.key-column' | translate }}</mat-label>
<input
[matAutocomplete]="auto"
[placeholder]="
'file-attributes-csv-import.key-column-placeholder' | translate
"
[placeholder]="'file-attributes-csv-import.key-column-placeholder' | translate"
formControlName="filenameMappingColumnHeaderName"
matInput
type="text"
/>
<mat-autocomplete #auto="matAutocomplete" autoActiveFirstOption>
<mat-option
*ngFor="let field of filteredKeyOptions | async"
[value]="field"
>
<mat-option *ngFor="let field of filteredKeyOptions | async" [value]="field">
{{ field }}
</mat-option>
</mat-autocomplete>
@ -44,9 +32,7 @@
<div class="red-input-group required w-110">
<label translate="file-attributes-csv-import.delimiter"></label>
<input
[placeholder]="
'file-attributes-csv-import.delimiter-placeholder' | translate
"
[placeholder]="'file-attributes-csv-import.delimiter-placeholder' | translate"
formControlName="delimiter"
name="delimiter"
type="text"
@ -56,9 +42,7 @@
<div class="red-input-group required w-160">
<label translate="file-attributes-csv-import.encoding"></label>
<input
[placeholder]="
'file-attributes-csv-import.encoding-placeholder' | translate
"
[placeholder]="'file-attributes-csv-import.encoding-placeholder' | translate"
formControlName="encoding"
name="encoding"
type="text"
@ -78,12 +62,10 @@
<div class="csv-part-header">
<div>
<span class="all-caps-label">{{
'file-attributes-csv-import.available'
| translate: { value: parseResult?.meta?.fields.length }
'file-attributes-csv-import.available' | translate: { value: parseResult?.meta?.fields.length }
}}</span>
<span class="all-caps-label">{{
'file-attributes-csv-import.selected'
| translate: { value: activeFields.length }
'file-attributes-csv-import.selected' | translate: { value: activeFields.length }
}}</span>
</div>
<div class="actions">
@ -108,7 +90,7 @@
</div>
<div *ngIf="isSearchOpen" class="search-input-container">
<redaction-input-with-action
[form]="searchForm"
[form]="searchService.searchForm"
placeholder="file-attributes-csv-import.search.placeholder"
type="search"
width="full"
@ -127,12 +109,8 @@
{{ field.csvColumn }}
</div>
<div class="secondary">
<div class="entry-count small-label">
{{ getEntries(field.csvColumn) }} entries
</div>
<div class="sample small-label">
Sample: {{ getSample(field.csvColumn) }}
</div>
<div class="entry-count small-label">{{ getEntries(field.csvColumn) }} entries</div>
<div class="sample small-label">Sample: {{ getSample(field.csvColumn) }}</div>
</div>
</div>
</div>
@ -146,10 +124,7 @@
>
<div class="csv-part-header">
<div class="all-caps-label">
{{
'file-attributes-csv-import.csv-column' +
(previewExpanded ? '' : '-preview') | translate
}}
{{ 'file-attributes-csv-import.csv-column' + (previewExpanded ? '' : '-preview') | translate }}
</div>
<redaction-circle-button
(click)="previewExpanded = !previewExpanded"
@ -157,16 +132,9 @@
></redaction-circle-button>
</div>
<div [class.hidden]="!previewExpanded" class="csv-part-content">
<div
*ngIf="!hoveredColumn"
class="no-column-data"
translate="file-attributes-csv-import.no-hovered-column"
></div>
<div *ngIf="!hoveredColumn" class="no-column-data" translate="file-attributes-csv-import.no-hovered-column"></div>
<div *ngIf="hoveredColumn && !columnSample.length" class="no-column-data">
{{
'file-attributes-csv-import.no-sample-data-for'
| translate: { column: hoveredColumn }
}}
{{ 'file-attributes-csv-import.no-sample-data-for' | translate: { column: hoveredColumn } }}
</div>
<div *ngFor="let row of columnSample">
{{ row }}
@ -184,12 +152,7 @@
</div>
<div class="dialog-actions">
<button
(click)="save()"
[disabled]="changedParseConfig || baseConfigForm.invalid"
color="primary"
mat-flat-button
>
<button (click)="save()" [disabled]="changedParseConfig || baseConfigForm.invalid" color="primary" mat-flat-button>
{{ 'file-attributes-csv-import.save.label' | translate }}
</button>
@ -198,9 +161,5 @@
</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

@ -26,7 +26,6 @@ export interface Field {
}
@Component({
selector: 'redaction-file-attributes-csv-import-dialog',
templateUrl: './file-attributes-csv-import-dialog.component.html',
styleUrls: ['./file-attributes-csv-import-dialog.component.scss'],
providers: [FilterService, SearchService, ScreenStateService, SortingService]
@ -44,7 +43,6 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
keepPreview = false;
columnSample = [];
initialParseConfig: { delimiter?: string; encoding?: string } = {};
protected readonly _searchKey = 'csvColumn';
constructor(
private readonly _appStateService: AppStateService,
@ -96,8 +94,8 @@ 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.setDisplayedEntities(this.allEntities);
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) {

View File

@ -35,7 +35,7 @@ export class DefaultColorsScreenComponent
protected readonly _injector: Injector
) {
super(_injector);
this._sortingService.setScreenName(ScreenNames.DEFAULT_COLORS);
this.sortingService.setScreenName(ScreenNames.DEFAULT_COLORS);
_appStateService.activateDossierTemplate(_activatedRoute.snapshot.params.dossierTemplateId);
}
@ -62,7 +62,7 @@ export class DefaultColorsScreenComponent
this._loadingService.start();
const data = await this._dictionaryControllerService.getColors(this._appStateService.activeDossierTemplateId).toPromise();
this._colorsObj = data;
this._screenStateService.setEntities(
this.screenStateService.setEntities(
Object.keys(data).map(key => ({
key,
value: data[key]

View File

@ -25,17 +25,12 @@
<redaction-round-checkbox
(click)="toggleSelectAll()"
[active]="areAllEntitiesSelected"
[indeterminate]="
(areSomeEntitiesSelected$ | async) && !areAllEntitiesSelected
"
[indeterminate]="(areSomeEntitiesSelected$ | async) && !areAllEntitiesSelected"
></redaction-round-checkbox>
</div>
<span class="all-caps-label">
{{
'dictionary-listing.table-header.title'
| translate: { length: (displayedEntities$ | async)?.length }
}}
{{ 'dictionary-listing.table-header.title' | translate: { length: (displayedEntities$ | async)?.length } }}
</span>
<redaction-circle-button
@ -48,7 +43,7 @@
<div class="attributes-actions-container">
<redaction-input-with-action
[form]="searchForm"
[form]="searchService.searchForm"
placeholder="dictionary-listing.search"
type="search"
></redaction-input-with-action>
@ -64,11 +59,7 @@
</div>
</div>
<div
[class.no-data]="!allEntities.length"
class="table-header"
redactionSyncWidth="table-item"
>
<div [class.no-data]="(allEntities$ | async)?.length === 0" class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<redaction-table-col-name
@ -98,39 +89,30 @@
<redaction-empty-state
(action)="openAddEditDictionaryDialog()"
*ngIf="!allEntities.length"
*ngIf="(allEntities$ | async)?.length === 0"
[showButton]="permissionsService.isAdmin()"
icon="red:dictionary"
screen="dictionary-listing"
></redaction-empty-state>
<redaction-empty-state
*ngIf="allEntities.length && (displayedEntities$ | async)?.length === 0"
*ngIf="(allEntities$ | async)?.length && (displayedEntities$ | async)?.length === 0"
screen="dictionary-listing"
type="no-match"
></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<div
*cdkVirtualFor="
let dict of displayedEntities$
| async
| sortBy: sortingOption.order:sortingOption.column
"
*cdkVirtualFor="let dict of displayedEntities$ | async | sortBy: sortingOption.order:sortingOption.column"
[routerLink]="[dict.type]"
class="table-item pointer"
>
<div (click)="toggleEntitySelected($event, dict)" class="selection-column">
<redaction-round-checkbox
[active]="isSelected(dict)"
></redaction-round-checkbox>
<redaction-round-checkbox [active]="isSelected(dict)"></redaction-round-checkbox>
</div>
<div>
<div
[ngStyle]="{ 'background-color': dict.hexColor }"
class="color-square"
></div>
<div [ngStyle]="{ 'background-color': dict.hexColor }" class="color-square"></div>
<div class="dict-name">
<div class="table-item-title heading">
{{ dict.label }}
@ -153,16 +135,13 @@
</div>
<div class="center">
<redaction-annotation-icon
[dictType]="dict"
[type]="dict.hint ? 'circle' : 'square'"
></redaction-annotation-icon>
<redaction-annotation-icon [dictType]="dict" [type]="dict.hint ? 'circle' : 'square'"></redaction-annotation-icon>
</div>
<div class="actions-container">
<div *ngIf="permissionsService.isAdmin()" class="action-buttons">
<redaction-circle-button
(action)="openDeleteDictionariesDialog($event, [dict.type])"
(action)="openDeleteDictionariesDialog($event, [dict])"
icon="red:trash"
tooltip="dictionary-listing.action.delete"
type="dark-bg"
@ -183,7 +162,7 @@
<div class="right-container" redactionHasScrollbar>
<redaction-simple-doughnut-chart
*ngIf="allEntities.length"
*ngIf="(allEntities$ | async)?.length"
[config]="chartData"
[counterText]="'dictionary-listing.stats.charts.entries' | translate"
[radius]="82"

View File

@ -43,9 +43,8 @@ export class DictionaryListingScreenComponent extends BaseListingComponent<TypeV
) {
super(_injector);
_loadingService.start();
this._sortingService.setScreenName(ScreenNames.DOSSIER_LISTING);
this._searchService.setSearchKey('label');
this._screenStateService.setIdKey('type');
this.sortingService.setScreenName(ScreenNames.DOSSIER_LISTING);
this.searchService.setSearchKey('label');
_appStateService.activateDossierTemplate(_activatedRoute.snapshot.params.dossierTemplateId);
}
@ -53,11 +52,16 @@ export class DictionaryListingScreenComponent extends BaseListingComponent<TypeV
this._loadDictionaryData();
}
openDeleteDictionariesDialog($event?: MouseEvent, types = this._screenStateService.selectedEntitiesIds) {
openDeleteDictionariesDialog($event?: MouseEvent, types = this.screenStateService.selectedEntities) {
this._dialogService.openDialog('confirm', $event, null, async () => {
this._loadingService.start();
await this._dictionaryControllerService.deleteTypes(types, this._appStateService.activeDossierTemplateId).toPromise();
this._screenStateService.setSelectedEntitiesIds([]);
await this._dictionaryControllerService
.deleteTypes(
types.map(t => t.type),
this._appStateService.activeDossierTemplateId
)
.toPromise();
this.screenStateService.setSelectedEntities([]);
await this._appStateService.loadDictionaryData();
this._loadDictionaryData(false);
this._calculateData();
@ -88,15 +92,15 @@ export class DictionaryListingScreenComponent extends BaseListingComponent<TypeV
const entities = Object.values(appStateDictionaryData).filter(d => !d.virtual);
if (!loadEntries)
this._screenStateService.setEntities(
this.screenStateService.setEntities(
entities.map(dict => {
dict.entries = this.allEntities.find(d => d.type === dict.type)?.entries || [];
return dict;
})
);
else this._screenStateService.setEntities(entities);
else this.screenStateService.setEntities(entities);
this._screenStateService.setDisplayedEntities(this.allEntities);
this.screenStateService.setDisplayedEntities(this.allEntities);
if (!loadEntries) return;

View File

@ -29,10 +29,7 @@ export class DigitalSignatureScreenComponent {
}
get hasDigitalSignatureSet() {
return (
this.digitalSignatureExists ||
!!this.digitalSignatureForm.get('base64EncodedPrivateKey').value
);
return this.digitalSignatureExists || !!this.digitalSignatureForm.get('base64EncodedPrivateKey').value;
}
saveDigitalSignature() {
@ -58,17 +55,13 @@ export class DigitalSignatureScreenComponent {
error => {
if (error.status === 400) {
this._notificationService.showToastNotification(
this._translateService.instant(
'digital-signature-screen.action.certificate-not-valid-error'
),
this._translateService.instant('digital-signature-screen.action.certificate-not-valid-error'),
null,
NotificationType.ERROR
);
} else {
this._notificationService.showToastNotification(
this._translateService.instant(
'digital-signature-screen.action.save-error'
),
this._translateService.instant('digital-signature-screen.action.save-error'),
null,
NotificationType.ERROR
);
@ -82,9 +75,7 @@ export class DigitalSignatureScreenComponent {
() => {
this.loadDigitalSignatureAndInitializeForm();
this._notificationService.showToastNotification(
this._translateService.instant(
'digital-signature-screen.action.delete-success'
),
this._translateService.instant('digital-signature-screen.action.delete-success'),
null,
NotificationType.SUCCESS
);
@ -139,9 +130,7 @@ export class DigitalSignatureScreenComponent {
certificateName: [this.digitalSignature.certificateName, Validators.required],
contactInfo: this.digitalSignature.contactInfo,
location: this.digitalSignature.location,
keySecret: this.digitalSignatureExists
? null
: [this.digitalSignature.password, Validators.required],
keySecret: this.digitalSignatureExists ? null : [this.digitalSignature.password, Validators.required],
reason: this.digitalSignature.reason,
base64EncodedPrivateKey: this.digitalSignatureExists
? null

View File

@ -43,7 +43,7 @@
<div class="attributes-actions-container">
<redaction-input-with-action
[form]="searchForm"
[form]="searchService.searchForm"
placeholder="dossier-attributes-listing.search"
type="search"
></redaction-input-with-action>

View File

@ -28,9 +28,8 @@ export class DossierAttributesListingScreenComponent extends BaseListingComponen
readonly permissionsService: PermissionsService
) {
super(_injector);
this._searchService.setSearchKey('label');
this._screenStateService.setIdKey('id');
this._sortingService.setScreenName(ScreenNames.DOSSIER_ATTRIBUTES_LISTING);
this.searchService.setSearchKey('label');
this.sortingService.setScreenName(ScreenNames.DOSSIER_ATTRIBUTES_LISTING);
_appStateService.activateDossierTemplate(_activatedRoute.snapshot.params.dossierTemplateId);
}
@ -41,7 +40,7 @@ export class DossierAttributesListingScreenComponent extends BaseListingComponen
openConfirmDeleteAttributeDialog($event: MouseEvent, dossierAttribute?: DossierAttributeConfig) {
this._dialogService.openDialog('confirm', $event, null, async () => {
this._loadingService.start();
const ids = dossierAttribute ? [dossierAttribute.id] : this._screenStateService.selectedEntitiesIds;
const ids = dossierAttribute ? [dossierAttribute.id] : this.screenStateService.selectedEntities.map(item => item.id);
await this._dossierAttributesService.deleteConfigs(ids);
await this._loadData();
});
@ -61,7 +60,7 @@ export class DossierAttributesListingScreenComponent extends BaseListingComponen
private async _loadData() {
this._loadingService.start();
const attributes = await this._dossierAttributesService.getConfig();
this._screenStateService.setEntities(attributes);
this.screenStateService.setEntities(attributes);
this.filterService.filterEntities();
this._loadingService.stop();
}

View File

@ -43,7 +43,7 @@
<div class="actions flex-1">
<redaction-input-with-action
[form]="searchForm"
[form]="searchService.searchForm"
placeholder="dossier-templates-listing.search"
type="search"
></redaction-input-with-action>
@ -58,7 +58,7 @@
</div>
</div>
<div [class.no-data]="noData" class="table-header" redactionSyncWidth="table-item">
<div [class.no-data]="(allEntities$ | async)?.length === 0" class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<redaction-table-col-name

View File

@ -29,9 +29,8 @@ export class DossierTemplatesListingScreenComponent extends BaseListingComponent
readonly userPreferenceService: UserPreferenceService
) {
super(_injector);
this._sortingService.setScreenName(ScreenNames.DOSSIER_TEMPLATES_LISTING);
this._searchService.setSearchKey('name');
this._screenStateService.setIdKey('dossierTemplateId');
this.sortingService.setScreenName(ScreenNames.DOSSIER_TEMPLATES_LISTING);
this.searchService.setSearchKey('name');
}
ngOnInit(): void {
@ -41,8 +40,10 @@ export class DossierTemplatesListingScreenComponent extends BaseListingComponent
openDeleteTemplatesDialog($event?: MouseEvent) {
return this._dialogService.openDialog('confirm', $event, null, async () => {
this._loadingService.start();
await this._dossierTemplateControllerService.deleteDossierTemplates(this._screenStateService.selectedEntitiesIds).toPromise();
this._screenStateService.setSelectedEntitiesIds([]);
await this._dossierTemplateControllerService
.deleteDossierTemplates(this.screenStateService.selectedEntities.map(d => d.dossierTemplateId))
.toPromise();
this.screenStateService.setSelectedEntities([]);
await this._appStateService.loadAllDossierTemplates();
await this._appStateService.loadDictionaryData();
this.loadDossierTemplatesData();
@ -52,7 +53,7 @@ export class DossierTemplatesListingScreenComponent extends BaseListingComponent
loadDossierTemplatesData() {
this._loadingService.start();
this._appStateService.reset();
this._screenStateService.setEntities(this._appStateService.dossierTemplates);
this.screenStateService.setEntities(this._appStateService.dossierTemplates);
this.filterService.filterEntities();
this._loadDossierTemplateStats();
this._loadingService.stop();
@ -67,7 +68,7 @@ export class DossierTemplatesListingScreenComponent extends BaseListingComponent
}
private _loadDossierTemplateStats() {
this._screenStateService.entities.forEach(rs => {
this.screenStateService.allEntities.forEach(rs => {
const dictionaries = this._appStateService.dictionaryData[rs.dossierTemplateId];
if (dictionaries) {
rs.dictionariesCount = Object.keys(dictionaries)

View File

@ -44,7 +44,7 @@
<div class="attributes-actions-container">
<redaction-input-with-action
[form]="searchForm"
[form]="searchService.searchForm"
placeholder="file-attributes-listing.search"
type="search"
></redaction-input-with-action>

View File

@ -31,9 +31,8 @@ export class FileAttributesListingScreenComponent extends BaseListingComponent<F
protected readonly _injector: Injector
) {
super(_injector);
this._sortingService.setScreenName(ScreenNames.FILE_ATTRIBUTES_LISTING);
this._searchService.setSearchKey('label');
this._screenStateService.setIdKey('id');
this.sortingService.setScreenName(ScreenNames.FILE_ATTRIBUTES_LISTING);
this.searchService.setSearchKey('label');
_appStateService.activateDossierTemplate(_activatedRoute.snapshot.params.dossierTemplateId);
}
@ -65,7 +64,10 @@ export class FileAttributesListingScreenComponent extends BaseListingComponent<F
.toPromise();
} else {
await this._fileAttributesService
.deleteFileAttributes(this._screenStateService.selectedEntitiesIds, this._appStateService.activeDossierTemplateId)
.deleteFileAttributes(
this.screenStateService.selectedEntities.map(f => f.id),
this._appStateService.activeDossierTemplateId
)
.toPromise();
}
await this._loadData();
@ -95,7 +97,7 @@ export class FileAttributesListingScreenComponent extends BaseListingComponent<F
.getFileAttributesConfiguration(this._appStateService.activeDossierTemplateId)
.toPromise();
this._existingConfiguration = response;
this._screenStateService.setEntities(response?.fileAttributeConfigs || []);
this.screenStateService.setEntities(response?.fileAttributeConfigs || []);
} catch (e) {
} finally {
this.filterService.filterEntities();

View File

@ -44,9 +44,7 @@ export class RulesScreenComponent extends ComponentHasChanges {
private readonly _activatedRoute: ActivatedRoute
) {
super(_translateService);
this._appStateService.activateDossierTemplate(
_activatedRoute.snapshot.params.dossierTemplateId
);
this._appStateService.activateDossierTemplate(_activatedRoute.snapshot.params.dossierTemplateId);
this._initialize();
}
@ -78,9 +76,7 @@ export class RulesScreenComponent extends ComponentHasChanges {
@debounce()
codeEditorTextChanged() {
const newDecorations = this.currentLines
.filter(entry => this._isNew(entry))
.map(entry => this._makeDecorationFor(entry));
const newDecorations = this.currentLines.filter(entry => this._isNew(entry)).map(entry => this._makeDecorationFor(entry));
this._decorations = this._codeEditor.deltaDecorations(this._decorations, newDecorations);
}
@ -153,14 +149,12 @@ export class RulesScreenComponent extends ComponentHasChanges {
}
private _initialize() {
this._rulesControllerService
.downloadRules(this._appStateService.activeDossierTemplateId)
.subscribe(
rules => {
this.currentLines = this.initialLines = rules.rules.split('\n');
this.revert();
},
() => (this.processing = false)
);
this._rulesControllerService.downloadRules(this._appStateService.activeDossierTemplateId).subscribe(
rules => {
this.currentLines = this.initialLines = rules.rules.split('\n');
this.revert();
},
() => (this.processing = false)
);
}
}

View File

@ -79,51 +79,37 @@ export class SmtpConfigScreenComponent implements OnInit {
async ngOnInit() {
await this._loadData();
this.generalSettings = await this._generalSettingsControllerService
.getGeneralConfigurations()
.toPromise();
this.generalSettings = await this._generalSettingsControllerService.getGeneralConfigurations().toPromise();
this._initialGeneralSettings = Object.assign({}, this.generalSettings);
}
async save() {
this.viewReady = false;
await this._smtpConfigService
.updateSMTPConfiguration(this.configForm.getRawValue())
.toPromise();
await this._smtpConfigService.updateSMTPConfiguration(this.configForm.getRawValue()).toPromise();
this._initialValue = this.configForm.getRawValue();
this.viewReady = true;
}
async saveGeneralConfig() {
this.viewReady = false;
await this._generalSettingsControllerService
.updateGeneralConfigurations(this.generalSettings)
.toPromise();
await this._generalSettingsControllerService.updateGeneralConfigurations(this.generalSettings).toPromise();
this.viewReady = true;
}
openAuthConfigDialog(skipDisableOnCancel?: boolean) {
this._dialogService.openDialog(
'smtpAuthConfig',
null,
this.configForm.getRawValue(),
null,
authConfig => {
if (authConfig) {
this.configForm.patchValue(authConfig);
} else if (!skipDisableOnCancel) {
this.configForm.patchValue({ auth: false }, { emitEvent: false });
}
this._dialogService.openDialog('smtpAuthConfig', null, this.configForm.getRawValue(), null, authConfig => {
if (authConfig) {
this.configForm.patchValue(authConfig);
} else if (!skipDisableOnCancel) {
this.configForm.patchValue({ auth: false }, { emitEvent: false });
}
);
});
}
async testConnection() {
this.viewReady = false;
try {
await this._smtpConfigService
.testSMTPConfiguration(this.configForm.getRawValue())
.toPromise();
await this._smtpConfigService.testSMTPConfiguration(this.configForm.getRawValue()).toPromise();
this._notificationService.showToastNotification(
this._translateService.instant('smtp-config-screen.test.success'),
undefined,
@ -142,9 +128,7 @@ export class SmtpConfigScreenComponent implements OnInit {
private async _loadData() {
try {
this._initialValue = await this._smtpConfigService
.getCurrentSMTPConfiguration()
.toPromise();
this._initialValue = await this._smtpConfigService.getCurrentSMTPConfiguration().toPromise();
this.configForm.patchValue(this._initialValue, { emitEvent: false });
} catch (e) {
} finally {

View File

@ -32,8 +32,7 @@ export class TrashScreenComponent extends BaseListingComponent<Dossier> implemen
private readonly _appConfigService: AppConfigService
) {
super(_injector);
this._sortingService.setScreenName(ScreenNames.DOSSIER_LISTING);
this._screenStateService.setIdKey('dossierId');
this.sortingService.setScreenName(ScreenNames.DOSSIER_LISTING);
}
async ngOnInit(): Promise<void> {
@ -46,7 +45,7 @@ export class TrashScreenComponent extends BaseListingComponent<Dossier> implemen
}
async loadDossierTemplatesData(): Promise<void> {
this._screenStateService.setEntities(await this._dossiersService.getDeletedDossiers());
this.screenStateService.setEntities(await this._dossiersService.getDeleted());
}
getRestoreDate(softDeletedTime: string): string {
@ -57,11 +56,11 @@ export class TrashScreenComponent extends BaseListingComponent<Dossier> implemen
return dossier.dossierId;
}
bulkDelete(dossierIds = this._screenStateService.selectedEntitiesIds) {
bulkDelete(dossierIds = this.screenStateService.selectedEntities.map(d => d.dossierId)) {
this._loadingService.loadWhile(this._hardDelete(dossierIds));
}
bulkRestore(dossierIds = this._screenStateService.selectedEntitiesIds) {
bulkRestore(dossierIds = this.screenStateService.selectedEntities.map(d => d.dossierId)) {
this._loadingService.loadWhile(this._restore(dossierIds));
}
@ -76,9 +75,9 @@ export class TrashScreenComponent extends BaseListingComponent<Dossier> implemen
}
private _removeFromList(ids: string[]): void {
const entities = this._screenStateService.entities.filter(e => !ids.includes(e.dossierId));
this._screenStateService.setEntities(entities);
this._screenStateService.setSelectedEntitiesIds([]);
const entities = this.screenStateService.allEntities.filter(e => !ids.includes(e.dossierId));
this.screenStateService.setEntities(entities);
this.screenStateService.setSelectedEntities([]);
this.filterService.filterEntities();
}
}

View File

@ -9,7 +9,7 @@
<div class="actions">
<redaction-input-with-action
[form]="searchForm"
[form]="searchService.searchForm"
placeholder="user-listing.search"
type="search"
></redaction-input-with-action>
@ -38,28 +38,19 @@
<redaction-round-checkbox
(click)="toggleSelectAll()"
[active]="areAllEntitiesSelected"
[indeterminate]="
(areSomeEntitiesSelected$ | async) && !areAllEntitiesSelected
"
[indeterminate]="(areSomeEntitiesSelected$ | async) && !areAllEntitiesSelected"
></redaction-round-checkbox>
</div>
<span class="all-caps-label">
{{
'user-listing.table-header.title'
| translate: { length: (displayedEntities$ | async)?.length }
}}
{{ 'user-listing.table-header.title' | translate: { length: (displayedEntities$ | async)?.length } }}
</span>
<redaction-circle-button
(action)="bulkDelete()"
*ngIf="areSomeEntitiesSelected$ | async"
[disabled]="(canDeleteSelected$ | async) === false"
[tooltip]="
(canDeleteSelected$ | async)
? 'user-listing.bulk.delete'
: 'user-listing.bulk.delete-disabled'
"
[tooltip]="(canDeleteSelected$ | async) ? 'user-listing.bulk.delete' : 'user-listing.bulk.delete-disabled'"
icon="red:trash"
tooltipPosition="after"
type="dark-bg"
@ -69,22 +60,13 @@
<div class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<redaction-table-col-name
label="user-listing.table-col-names.name"
></redaction-table-col-name>
<redaction-table-col-name label="user-listing.table-col-names.name"></redaction-table-col-name>
<redaction-table-col-name
label="user-listing.table-col-names.email"
></redaction-table-col-name>
<redaction-table-col-name label="user-listing.table-col-names.email"></redaction-table-col-name>
<redaction-table-col-name
class="flex-center"
label="user-listing.table-col-names.active"
></redaction-table-col-name>
<redaction-table-col-name class="flex-center" label="user-listing.table-col-names.active"></redaction-table-col-name>
<redaction-table-col-name
label="user-listing.table-col-names.roles"
></redaction-table-col-name>
<redaction-table-col-name label="user-listing.table-col-names.roles"></redaction-table-col-name>
<div></div>
<div class="scrollbar-placeholder"></div>
@ -98,14 +80,9 @@
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<!-- Table lines -->
<div
*cdkVirtualFor="let user of displayedEntities$ | async; trackBy: trackById"
class="table-item"
>
<div *cdkVirtualFor="let user of displayedEntities$ | async; trackBy: trackById" class="table-item">
<div (click)="toggleEntitySelected($event, user)" class="selection-column">
<redaction-round-checkbox
[active]="isSelected(user)"
></redaction-round-checkbox>
<redaction-round-checkbox [active]="isSelected(user)"></redaction-round-checkbox>
</div>
<div>

View File

@ -39,12 +39,11 @@ export class UserListingScreenComponent extends BaseListingComponent<User> imple
protected readonly _injector: Injector
) {
super(_injector);
this._screenStateService.setIdKey('userId');
}
get canDeleteSelected$(): Observable<boolean> {
const entities$ = this._screenStateService.selectedEntitiesIds$;
return entities$.pipe(map(all => all.indexOf(this.userService.userId) === -1));
const entities$ = this.screenStateService.selectedEntities$;
return entities$.pipe(map(all => all.indexOf(this.userService.user) === -1));
}
async ngOnInit() {
@ -79,7 +78,7 @@ export class UserListingScreenComponent extends BaseListingComponent<User> imple
}
bulkDelete() {
this.openDeleteUsersDialog(this._screenStateService.entities.filter(u => this.isSelected(u)));
this.openDeleteUsersDialog(this.screenStateService.allEntities.filter(u => this.isSelected(u)));
}
trackById(index: number, user: User) {
@ -87,7 +86,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();

View File

@ -1,4 +1,4 @@
import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
import { ChangeDetectorRef, Component, EventEmitter, 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';
@ -8,8 +8,9 @@ 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';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
@Component({
selector: 'redaction-dossier-overview-bulk-actions',
@ -17,8 +18,6 @@ import { ConfirmationDialogInput } from '@shared/dialogs/confirmation-dialog/con
styleUrls: ['./dossier-overview-bulk-actions.component.scss']
})
export class DossierOverviewBulkActionsComponent {
@Input()
selectedFileIds: string[];
@Output()
reload = new EventEmitter();
@ -32,7 +31,8 @@ export class DossierOverviewBulkActionsComponent {
private readonly _fileActionService: FileActionService,
private readonly _statusOverlayService: StatusOverlayService,
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _loadingService: LoadingService
private readonly _loadingService: LoadingService,
private readonly _screenStateService: ScreenStateService<FileStatusWrapper>
) {}
get dossier() {
@ -40,30 +40,27 @@ export class DossierOverviewBulkActionsComponent {
}
get selectedFiles(): FileStatusWrapper[] {
return this.selectedFileIds.map(fileId =>
this._appStateService.getFileById(this._appStateService.activeDossier.dossier.dossierId, fileId)
);
return this._screenStateService.selectedEntities;
}
get areAllFilesSelected() {
return (
this._appStateService.activeDossier.files.length !== 0 &&
this.selectedFileIds.length === this._appStateService.activeDossier.files.length
this.selectedFiles.length === this._appStateService.activeDossier.files.length
);
}
get areSomeFilesSelected() {
return this.selectedFileIds.length > 0;
return this.selectedFiles.length > 0;
}
get allSelectedFilesCanBeAssignedIntoSameState() {
if (this.areSomeFilesSelected) {
const selectedFiles = this.selectedFiles;
const allFilesAreUnderReviewOrUnassigned = selectedFiles.reduce(
const allFilesAreUnderReviewOrUnassigned = this.selectedFiles.reduce(
(acc, file) => acc && (file.isUnderReview || file.isUnassigned),
true
);
const allFilesAreUnderApproval = selectedFiles.reduce((acc, file) => acc && file.isUnderApproval, true);
const allFilesAreUnderApproval = this.selectedFiles.reduce((acc, file) => acc && file.isUnderApproval, true);
return allFilesAreUnderReviewOrUnassigned || allFilesAreUnderApproval;
}
return false;
@ -139,11 +136,14 @@ export class DossierOverviewBulkActionsComponent {
async () => {
this._loadingService.start();
await this._fileManagementControllerService
.deleteFiles(this.selectedFileIds, this._appStateService.activeDossierId)
.deleteFiles(
this.selectedFiles.map(item => item.fileId),
this._appStateService.activeDossierId
)
.toPromise();
await this._appStateService.reloadActiveDossierFiles();
this.reload.emit();
this.selectedFileIds.splice(0, this.selectedFileIds.length);
this._screenStateService.setSelectedEntities([]);
this._loadingService.stop();
}
);
@ -153,12 +153,9 @@ export class DossierOverviewBulkActionsComponent {
// If more than 1 approver - show dialog and ask who to assign
if (this._appStateService.activeDossier.approverIds.length > 1) {
this._loadingService.start();
const files = this.selectedFileIds.map(fileId =>
this._appStateService.getFileById(this._appStateService.activeDossierId, fileId)
);
this._dialogService.openAssignFileToUserDialog(
files,
this.selectedFiles,
'approver',
() => {
this.reload.emit();
@ -198,11 +195,10 @@ export class DossierOverviewBulkActionsComponent {
assign() {
this._loadingService.start();
const files = this.selectedFileIds.map(fileId => this._appStateService.getFileById(this._appStateService.activeDossierId, fileId));
const mode = files[0].isUnderApproval ? 'approver' : 'reviewer';
const mode = this.selectedFiles[0].isUnderApproval ? 'approver' : 'reviewer';
this._dialogService.openAssignFileToUserDialog(files, mode, () => {
this._dialogService.openAssignFileToUserDialog(this.selectedFiles, mode, () => {
this.reload.emit();
this._loadingService.stop();
});

View File

@ -38,12 +38,10 @@
<div *ngIf="hasFiles" class="mt-24">
<redaction-simple-doughnut-chart
(toggleFilter)="filterService.filterEntities()"
[config]="documentsChartData"
[filter]="filterService.getFilter$('statusFilters') | async"
[radius]="63"
[strokeWidth]="15"
[subtitle]="'dossier-overview.dossier-details.charts.documents-in-dossier'"
[subtitle]="i18nKey + 'charts.documents-in-dossier'"
direction="row"
></redaction-simple-doughnut-chart>
</div>
@ -65,9 +63,9 @@
></redaction-dossier-details-stats>
</div>
<div *ngIf="!!appStateService.activeDossier.dossier.description" class="pb-32">
<div *ngIf="appStateService.activeDossier.dossier.description as description" class="pb-32">
<div class="heading" translate="dossier-overview.dossier-details.description"></div>
<div class="mt-8">{{ appStateService.activeDossier.dossier.description }}</div>
<div class="mt-8">{{ description }}</div>
</div>
</ng-container>

View File

@ -18,6 +18,7 @@ import { DossierAttributeWithValue } from '@models/dossier-attributes.model';
styleUrls: ['./dossier-details.component.scss']
})
export class DossierDetailsComponent implements OnInit {
readonly i18nKey = 'dossier-overview.dossier-details.';
documentsChartData: DoughnutChartConfig[] = [];
owner: User;
editingOwner = false;
@ -71,7 +72,7 @@ export class DossierDetailsComponent implements OnInit {
key: key
});
}
this.documentsChartData.sort((a, b) => StatusSorter[a.key] - StatusSorter[b.key]);
this.documentsChartData.sort(StatusSorter.byKey);
this.documentsChartData = this.translateChartService.translateStatus(this.documentsChartData);
this._changeDetectorRef.detectChanges();
}
@ -80,7 +81,7 @@ export class DossierDetailsComponent implements OnInit {
this.owner = typeof user === 'string' ? this._userService.getRedUserById(user) : user;
const dw = Object.assign({}, this.appStateService.activeDossier);
dw.dossier.ownerId = this.owner.userId;
await this.appStateService.addOrUpdateDossier(dw.dossier);
await this.appStateService.createOrUpdateDossier(dw.dossier);
const ownerName = this._userService.getNameForId(this.owner.userId);
const dossierName = this.appStateService.activeDossier.name;

View File

@ -27,7 +27,6 @@
<div>
<redaction-simple-doughnut-chart
[config]="documentsChartData"
[filter]="filterService.getFilter$('statusFilters') | async"
[radius]="80"
[strokeWidth]="15"
[subtitle]="'dossier-listing.stats.charts.total-documents'"

View File

@ -51,12 +51,7 @@ export class TeamMembersManagerComponent implements OnInit {
get membersSelectOptions() {
const searchQuery = this.searchForm.get('query').value;
return this.userService.eligibleUsers
.filter(user =>
this.userService
.getNameForId(user.userId)
.toLowerCase()
.includes(searchQuery.toLowerCase())
)
.filter(user => this.userService.getNameForId(user.userId).toLowerCase().includes(searchQuery.toLowerCase()))
.filter(user => this.selectedOwnerId !== user.userId)
.map(user => user.userId);
}
@ -76,10 +71,7 @@ export class TeamMembersManagerComponent implements OnInit {
const initialApprovers = this.dossierWrapper.approverIds.sort();
const currentApprovers = this.selectedApproversList.sort();
return (
this._compareLists(initialMembers, currentMembers) ||
this._compareLists(initialApprovers, currentApprovers)
);
return this._compareLists(initialMembers, currentMembers) || this._compareLists(initialApprovers, currentApprovers);
}
isOwner(userId: string): boolean {
@ -96,7 +88,7 @@ export class TeamMembersManagerComponent implements OnInit {
dw.dossier.memberIds = memberIds;
dw.dossier.approverIds = approverIds;
dw.dossier.ownerId = ownerId;
result = await this._appStateService.addOrUpdateDossier(dw.dossier);
result = await this._appStateService.createOrUpdateDossier(dw.dossier);
this.save.emit(result);
} catch (error) {
this._notificationService.showToastNotification(

View File

@ -35,10 +35,7 @@ export class AddDossierDialogComponent {
},
{
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 }
}
);
}
@ -62,14 +59,12 @@ export class AddDossierDialogComponent {
async saveDossier() {
const dossier: Dossier = this._formToObject();
const foundDossier = this._appStateService.allDossiers.find(
p => p.dossier.dossierId === dossier.dossierId
);
const foundDossier = this._appStateService.allDossiers.find(p => p.dossier.dossierId === dossier.dossierId);
if (foundDossier) {
dossier.memberIds = foundDossier.memberIds;
}
const savedDossier = await this._appStateService.addOrUpdateDossier(dossier);
const savedDossier = await this._appStateService.createOrUpdateDossier(dossier);
if (savedDossier) {
this.dialogRef.close({ dossier: savedDossier });
}
@ -77,7 +72,7 @@ export class AddDossierDialogComponent {
async saveDossierAndAddMembers() {
const dossier: Dossier = this._formToObject();
const savedDossier = await this._appStateService.addOrUpdateDossier(dossier);
const savedDossier = await this._appStateService.createOrUpdateDossier(dossier);
if (savedDossier) {
this.dialogRef.close({ addMembers: true, dossier: savedDossier });
}
@ -85,9 +80,7 @@ export class AddDossierDialogComponent {
dossierTemplateChanged(dossierTemplateId) {
// get current selected dossierTemplate
const dossierTemplate = this.dossierTemplates.find(
r => r.dossierTemplateId === dossierTemplateId
);
const dossierTemplate = this.dossierTemplates.find(r => r.dossierTemplateId === dossierTemplateId);
if (dossierTemplate) {
// update dropdown values
this.dossierForm.patchValue(

View File

@ -111,15 +111,9 @@ export class AssignReviewerApproverDialogComponent {
uniqueReviewers.add(file.currentReviewer);
}
}
let singleUser =
uniqueReviewers.size === 1
? uniqueReviewers.values().next().value
: this.userService.userId;
let singleUser = uniqueReviewers.size === 1 ? uniqueReviewers.values().next().value : this.userService.userId;
singleUser =
this.singleUsersSelectOptions.indexOf(singleUser) >= 0
? singleUser
: this.singleUsersSelectOptions[0];
singleUser = this.singleUsersSelectOptions.indexOf(singleUser) >= 0 ? singleUser : this.singleUsersSelectOptions[0];
this.usersForm = this._formBuilder.group({
singleUser: [singleUser, Validators.required]

View File

@ -71,7 +71,7 @@ export class EditDossierDownloadPackageComponent implements OnInit, EditDossierS
downloadFileTypes: this.dossierForm.get('downloadFileTypes').value,
reportTypes: this.dossierForm.get('reportTypes').value
};
const updatedDossier = await this._appStateService.addOrUpdateDossier(dossier);
const updatedDossier = await this._appStateService.createOrUpdateDossier(dossier);
this.updateDossier.emit(updatedDossier);
}

View File

@ -10,7 +10,7 @@ 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({
@ -100,7 +100,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
dueDate: this.hasDueDate ? this.dossierForm.get('dueDate').value : undefined,
dossierTemplateId: this.dossierForm.get('dossierTemplateId').value
};
const updatedDossier = await this._appStateService.addOrUpdateDossier(dossier);
const updatedDossier = await this._appStateService.createOrUpdateDossier(dossier);
if (updatedDossier) this.updateDossier.emit(updatedDossier);
}

View File

@ -7,8 +7,8 @@ import { groupBy } from '@utils/functions';
import { TranslateService } from '@ngx-translate/core';
import { PermissionsService } from '@services/permissions.service';
import { DossierWrapper } from '@state/model/dossier.wrapper';
import { Subscription, timer } from 'rxjs';
import { filter, tap } from 'rxjs/operators';
import { timer } from 'rxjs';
import { filter } from 'rxjs/operators';
import { TranslateChartService } from '@services/translate-chart.service';
import { RedactionFilterSorter } from '@utils/sorters/redaction-filter-sorter';
import { StatusSorter } from '@utils/sorters/status-sorter';
@ -30,12 +30,15 @@ import { ScreenStateService } from '@shared/services/screen-state.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { ScreenNames, SortingService } from '@services/sorting.service';
const isLeavingScreen = event => event instanceof NavigationStart && event.url !== '/main/dossiers';
@Component({
templateUrl: './dossier-listing-screen.component.html',
styleUrls: ['./dossier-listing-screen.component.scss'],
providers: [FilterService, SearchService, ScreenStateService, SortingService]
})
export class DossierListingScreenComponent extends BaseListingComponent<DossierWrapper> implements OnInit, OnDestroy, OnAttach, OnDetach {
readonly itemSize = 95;
dossiersChartData: DoughnutChartConfig[] = [];
documentsChartData: DoughnutChartConfig[] = [];
buttonConfigs: ButtonConfig[] = [
@ -48,12 +51,7 @@ export class DossierListingScreenComponent extends BaseListingComponent<DossierW
}
];
readonly itemSize = 85;
private _dossierAutoUpdateTimer: Subscription;
private _lastScrollPosition: number;
private _routerEventsScrollPositionSub: Subscription;
private _fileChangedSub: Subscription;
@ViewChild('needsWorkTemplate', { read: TemplateRef, static: true })
private readonly _needsWorkTemplate: TemplateRef<any>;
@ -70,65 +68,43 @@ export class DossierListingScreenComponent extends BaseListingComponent<DossierW
protected readonly _injector: Injector
) {
super(_injector);
this._sortingService.setScreenName(ScreenNames.DOSSIER_LISTING);
this._searchService.setSearchKey('name');
this.sortingService.setScreenName(ScreenNames.DOSSIER_LISTING);
this.searchService.setSearchKey('name');
this._appStateService.reset();
this._loadEntitiesFromState();
}
private get _user() {
return this._userService.user;
}
private get _activeDossiersCount(): number {
return this._screenStateService.entities.filter(p => p.dossier.status === Dossier.StatusEnum.ACTIVE).length;
}
private get _inactiveDossiersCount(): number {
return this._screenStateService.entities.length - this._activeDossiersCount;
}
ngOnInit(): void {
this.calculateData();
this._dossierAutoUpdateTimer = timer(0, 10000)
.pipe(
tap(async () => {
await this._appStateService.loadAllDossiers();
this._loadEntitiesFromState();
this.calculateData();
})
)
.subscribe();
this._fileChangedSub = this._appStateService.fileChanged.subscribe(() => {
this.addSubscription = timer(0, 10000).subscribe(async () => {
await this._appStateService.loadAllDossiers();
this._loadEntitiesFromState();
this.calculateData();
});
this._routerEventsScrollPositionSub = this._router.events
.pipe(filter(event => event instanceof NavigationStart))
.subscribe((event: NavigationStart) => {
if (event.url !== '/main/dossiers') {
this._lastScrollPosition = this.scrollViewport.measureScrollOffset('top');
}
});
this.addSubscription = this._appStateService.fileChanged.subscribe(() => {
this.calculateData();
});
this.addSubscription = this._router.events.pipe(filter(isLeavingScreen)).subscribe(() => {
this._lastScrollPosition = this.scrollViewport.measureScrollOffset('top');
});
}
ngOnAttach() {
this.scrollViewport.scrollTo({ top: this._lastScrollPosition });
this._appStateService.reset();
this._loadEntitiesFromState();
this.ngOnInit();
this.scrollViewport.scrollTo({ top: this._lastScrollPosition });
}
ngOnDetach() {
ngOnDetach(): void {
this.ngOnDestroy();
}
ngOnDestroy(): void {
this._dossierAutoUpdateTimer.unsubscribe();
this._routerEventsScrollPositionSub.unsubscribe();
this._fileChangedSub.unsubscribe();
super.ngOnDestroy();
}
getDossierTemplate(dw: DossierWrapper): DossierTemplateModel {
@ -147,6 +123,18 @@ export class DossierListingScreenComponent extends BaseListingComponent<DossierW
});
}
private get _userId() {
return this._userService.userId;
}
private get _activeDossiersCount(): number {
return this.screenStateService.allEntities.filter(p => p.dossier.status === Dossier.StatusEnum.ACTIVE).length;
}
private get _inactiveDossiersCount(): number {
return this.screenStateService.allEntities.length - this._activeDossiersCount;
}
calculateData() {
this._computeAllFilters();
@ -165,12 +153,12 @@ export class DossierListingScreenComponent extends BaseListingComponent<DossierW
key: key
});
}
this.documentsChartData.sort((a, b) => StatusSorter[a.key] - StatusSorter[b.key]);
this.documentsChartData.sort(StatusSorter.byKey);
this.documentsChartData = this._translateChartService.translateStatus(this.documentsChartData);
}
private _loadEntitiesFromState() {
this._screenStateService.setEntities(this._appStateService.allDossiers);
this.screenStateService.setEntities(this._appStateService.allDossiers);
}
private _computeAllFilters() {
@ -179,7 +167,7 @@ export class DossierListingScreenComponent extends BaseListingComponent<DossierW
const allDistinctNeedsWork = new Set<string>();
const allDistinctDossierTemplates = new Set<string>();
this._screenStateService?.entities?.forEach(entry => {
this.screenStateService?.allEntities?.forEach(entry => {
// all people
entry.dossier.memberIds.forEach(f => allDistinctPeople.add(f));
// Needs work
@ -267,22 +255,22 @@ export class DossierListingScreenComponent extends BaseListingComponent<DossierW
{
key: 'my-dossiers',
label: myDossiersLabel,
checker: (dw: DossierWrapper) => dw.ownerId === this._user.id
checker: (dw: DossierWrapper) => dw.ownerId === this._userId
},
{
key: 'to-approve',
label: this._translateService.instant('dossier-listing.quick-filters.to-approve'),
checker: (dw: DossierWrapper) => dw.approverIds.includes(this._user.id)
checker: (dw: DossierWrapper) => dw.approverIds.includes(this._userId)
},
{
key: 'to-review',
label: this._translateService.instant('dossier-listing.quick-filters.to-review'),
checker: (dw: DossierWrapper) => dw.memberIds.includes(this._user.id)
checker: (dw: DossierWrapper) => dw.memberIds.includes(this._userId)
},
{
key: 'other',
label: this._translateService.instant('dossier-listing.quick-filters.other'),
checker: (dw: DossierWrapper) => !dw.memberIds.includes(this._user.id)
checker: (dw: DossierWrapper) => !dw.memberIds.includes(this._userId)
}
];

View File

@ -49,10 +49,7 @@
{{ 'dossier-overview.table-header.title' | translate: { length: (displayedEntities$ | async)?.length || 0 } }}
</span>
<redaction-dossier-overview-bulk-actions
(reload)="bulkActionPerformed()"
[selectedFileIds]="selectedEntitiesIds$ | async"
></redaction-dossier-overview-bulk-actions>
<redaction-dossier-overview-bulk-actions (reload)="bulkActionPerformed()"></redaction-dossier-overview-bulk-actions>
<redaction-quick-filters></redaction-quick-filters>
</div>

View File

@ -12,9 +12,8 @@ import { DossierDetailsComponent } from '../../components/dossier-details/dossie
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
import { PermissionsService } from '@services/permissions.service';
import { UserService } from '@services/user.service';
import { FileStatus, UserPreferenceControllerService } from '@redaction/red-ui-http';
import { Subscription, timer } from 'rxjs';
import { filter, tap } from 'rxjs/operators';
import { timer } from 'rxjs';
import { filter } from 'rxjs/operators';
import { RedactionFilterSorter } from '@utils/sorters/redaction-filter-sorter';
import { StatusSorter } from '@utils/sorters/status-sorter';
import { convertFiles, handleFileDrop } from '@utils/file-drop-utils';
@ -33,6 +32,7 @@ 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';
import { UserPreferenceService } from '../../../../services/user-preference.service';
@Component({
templateUrl: './dossier-overview-screen.component.html',
@ -49,11 +49,9 @@ export class DossierOverviewScreenComponent
dossierAttributes: DossierAttributeWithValue[] = [];
@ViewChild(DossierDetailsComponent, { static: false })
private readonly _dossierDetailsComponent: DossierDetailsComponent;
private _filesAutoUpdateTimer: Subscription;
private _routerEventsScrollPositionSub: Subscription;
private _fileChangedSub: Subscription;
private _lastScrollPosition: number;
private _lastOpenedFileId = '';
private _lastOpenedFileKey = 'Dossier-Recent-' + this.activeDossier.dossierId;
@ViewChild('needsWorkTemplate', { read: TemplateRef, static: true })
private readonly _needsWorkTemplate: TemplateRef<any>;
@ViewChild('fileInput') private _fileInput: ElementRef;
@ -69,7 +67,7 @@ export class DossierOverviewScreenComponent
private readonly _translateService: TranslateService,
private readonly _fileDropOverlayService: FileDropOverlayService,
private readonly _appStateService: AppStateService,
private readonly _userPreferenceControllerService: UserPreferenceControllerService,
private readonly _userPreferenceService: UserPreferenceService,
private readonly _appConfigService: AppConfigService,
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _loadingService: LoadingService,
@ -77,9 +75,8 @@ export class DossierOverviewScreenComponent
protected readonly _injector: Injector
) {
super(_injector);
this._sortingService.setScreenName(ScreenNames.DOSSIER_OVERVIEW);
this._searchService.setSearchKey('searchField');
this._screenStateService.setIdKey('fileId');
this.sortingService.setScreenName(ScreenNames.DOSSIER_OVERVIEW);
this.searchService.setSearchKey('searchField');
this._loadEntitiesFromState();
}
@ -99,30 +96,26 @@ export class DossierOverviewScreenComponent
return this.filterService.getFilter('quickFilters')?.values.filter(f => !f.required && f.checked);
}
isLastOpenedFile(fileStatus: FileStatusWrapper): boolean {
return this._lastOpenedFileId === fileStatus.fileId;
isLastOpenedFile({ fileId }: FileStatusWrapper): boolean {
return this._userPreferenceService.getLastOpenedFileId(this._lastOpenedFileKey) === fileId;
}
async ngOnInit() {
async ngOnInit(): Promise<void> {
this._fileDropOverlayService.initFileDropHandling();
this.calculateData();
this._filesAutoUpdateTimer = timer(0, 7500)
.pipe(
tap(async () => {
await this._appStateService.reloadActiveDossierFilesIfNecessary();
this._loadEntitiesFromState();
})
)
.subscribe();
this.addSubscription = timer(0, 7500).subscribe(async () => {
await this._appStateService.reloadActiveDossierFilesIfNecessary();
this._loadEntitiesFromState();
});
this._fileChangedSub = this._appStateService.fileChanged.subscribe(() => {
this.addSubscription = this._appStateService.fileChanged.subscribe(() => {
this.calculateData();
});
this._routerEventsScrollPositionSub = this._router.events
.pipe(filter(events => events instanceof NavigationStart))
this.addSubscription = this._router.events
.pipe(filter(event => event instanceof NavigationStart))
.subscribe((event: NavigationStart) => {
if (!event.url.endsWith(this._appStateService.activeDossierId)) {
this._lastScrollPosition = this.scrollViewport.measureScrollOffset('top');
@ -131,13 +124,6 @@ export class DossierOverviewScreenComponent
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();
@ -145,9 +131,7 @@ export class DossierOverviewScreenComponent
ngOnDestroy(): void {
this._fileDropOverlayService.cleanupFileDropHandling();
this._filesAutoUpdateTimer.unsubscribe();
this._fileChangedSub.unsubscribe();
this._routerEventsScrollPositionSub.unsubscribe();
super.ngOnDestroy();
}
async ngOnAttach() {
@ -180,18 +164,8 @@ export class DossierOverviewScreenComponent
});
}
isError(fileStatusWrapper: FileStatusWrapper) {
return fileStatusWrapper.status === FileStatus.StatusEnum.ERROR;
}
isProcessing(fileStatusWrapper: FileStatusWrapper) {
return [FileStatus.StatusEnum.REPROCESS, FileStatus.StatusEnum.FULLREPROCESS, FileStatus.StatusEnum.PROCESSING].includes(
fileStatusWrapper.status
);
}
reloadDossiers() {
this._appStateService.getFiles(this._appStateService.activeDossier, false).then(() => {
this._appStateService.getFiles(this.activeDossier, false).then(() => {
this.calculateData();
});
}
@ -235,7 +209,7 @@ export class DossierOverviewScreenComponent
}
bulkActionPerformed() {
this._screenStateService.selectedEntitiesIds$.next([]);
this.screenStateService.setSelectedEntities([]);
this.reloadDossiers();
}
@ -271,15 +245,12 @@ export class DossierOverviewScreenComponent
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);
if (this.activeDossier) this.screenStateService.setEntities(this.activeDossier.files);
}
private async _uploadFiles(files: FileUploadModel[]) {
const fileCount = await this._fileUploadService.uploadFiles(files);
if (fileCount) {
this._statusOverlayService.openUploadStatusOverlay();
}
// this._changeDetectorRef.detectChanges();
if (fileCount) this._statusOverlayService.openUploadStatusOverlay();
}
private _computeAllFilters() {
@ -290,7 +261,7 @@ export class DossierOverviewScreenComponent
const allDistinctAddedDates = new Set<string>();
const allDistinctNeedsWork = new Set<string>();
this._screenStateService.entities.forEach(file => {
this.screenStateService.allEntities.forEach(file => {
allDistinctPeople.add(file.currentReviewer);
allDistinctFileStatusWrapper.add(file.status);
allDistinctAddedDates.add(moment(file.added).format('DD/MM/YYYY'));
@ -370,7 +341,7 @@ export class DossierOverviewScreenComponent
private _createQuickFilters() {
let quickFilters = [];
if (this._screenStateService.entities.filter(this.recentlyModifiedChecker).length > 0) {
if (this.screenStateService.allEntities.filter(this.recentlyModifiedChecker).length > 0) {
const recentPeriod = this._appConfigService.getConfig(AppConfigKey.RECENT_PERIOD_IN_HOURS);
quickFilters = [
{

View File

@ -34,7 +34,7 @@ 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 { LoadingService } from '../../../../services/loading.service';
import { stampPDFPage } from '../../../../utils/page-stamper';
import { TranslateService } from '@ngx-translate/core';

View File

@ -1,11 +1,25 @@
import { Injectable } from '@angular/core';
import { DossierControllerService } from '@redaction/red-ui-http';
import { Dossier, DossierControllerService } from '@redaction/red-ui-http';
@Injectable()
@Injectable({
providedIn: 'root'
})
export class DossiersService {
constructor(private readonly _dossierControllerService: DossierControllerService) {}
getDeletedDossiers() {
createOrUpdate(dossier: Dossier): Promise<Dossier> {
return this._dossierControllerService.createOrUpdateDossier(dossier).toPromise();
}
delete(dossierId: string): Promise<unknown> {
return this._dossierControllerService.deleteDossier(dossierId).toPromise();
}
getAll(): Promise<Dossier[]> {
return this._dossierControllerService.getDossiers().toPromise();
}
getDeleted(): Promise<Dossier[]> {
return this._dossierControllerService.getDeletedDossiers().toPromise();
}

View File

@ -77,12 +77,7 @@ export class ManualAnnotationService {
return obs.pipe(
tap(
() => this._notify(this._getMessage(mode)),
error =>
this._notify(
this._getMessage(mode, modifyDictionary, true),
NotificationType.ERROR,
error
)
error => this._notify(this._getMessage(mode, modifyDictionary, true), NotificationType.ERROR, error)
)
);
}
@ -124,9 +119,7 @@ export class ManualAnnotationService {
// /manualRedaction/redaction/legalBasisChange
// /manualRedaction/request/legalBasis
changeLegalBasis(annotationId: string, legalBasis: string, comment?: string) {
const mode: Mode = this._permissionsService.isApprover()
? 'change-legal-basis'
: 'request-change-legal-basis';
const mode: Mode = this._permissionsService.isApprover() ? 'change-legal-basis' : 'request-change-legal-basis';
return this._makeRequest(mode, { annotationId, legalBasis, comment });
}
@ -134,9 +127,7 @@ export class ManualAnnotationService {
// /manualRedaction/redaction/recategorize
// /manualRedaction/request/recategorize
recategorizeImage(annotationId: string, type: string, comment: string) {
const mode: Mode = this._permissionsService.isApprover()
? 'recategorize-image'
: 'request-image-recategorization';
const mode: Mode = this._permissionsService.isApprover() ? 'recategorize-image' : 'request-image-recategorization';
return this._makeRequest(mode, { annotationId, type, comment });
}
@ -145,21 +136,14 @@ export class ManualAnnotationService {
// /manualRedaction/request/add
addAnnotation(manualRedactionEntry: AddRedactionRequest) {
const mode: Mode = this._permissionsService.isApprover() ? 'add' : 'suggest';
return this._makeRequest(
mode,
manualRedactionEntry,
null,
manualRedactionEntry.addToDictionary
);
return this._makeRequest(mode, manualRedactionEntry, null, manualRedactionEntry.addToDictionary);
}
// this wraps
// /manualRedaction/redaction/force
// /manualRedaction/request/force
forceRedaction(request: ForceRedactionRequest) {
const mode: Mode = this._permissionsService.isApprover()
? 'force-redaction'
: 'request-force-redaction';
const mode: Mode = this._permissionsService.isApprover() ? 'force-redaction' : 'request-force-redaction';
return this._makeRequest(mode, request);
}
@ -167,21 +151,11 @@ export class ManualAnnotationService {
// /manualRedaction/approve
approveRequest(annotationId: string, addToDictionary: boolean = false) {
// for only here - approve the request
return this._makeRequest(
'approve',
{ addOrRemoveFromDictionary: addToDictionary },
annotationId,
addToDictionary
);
return this._makeRequest('approve', { addOrRemoveFromDictionary: addToDictionary }, annotationId, addToDictionary);
}
undoRequest(annotationWrapper: AnnotationWrapper) {
return this._makeRequest(
'undo',
annotationWrapper.id,
null,
annotationWrapper.isModifyDictionary
);
return this._makeRequest('undo', annotationWrapper.id, null, annotationWrapper.isModifyDictionary);
}
// this wraps
@ -189,21 +163,13 @@ export class ManualAnnotationService {
// /manualRedaction/undo
declineOrRemoveRequest(annotationWrapper: AnnotationWrapper) {
const mode: Mode = this._permissionsService.isApprover() ? 'decline' : 'undo';
return this._makeRequest(
mode,
annotationWrapper.id,
null,
annotationWrapper.isModifyDictionary
);
return this._makeRequest(mode, annotationWrapper.id, null, annotationWrapper.isModifyDictionary);
}
// this wraps
// /manualRedaction/redaction/remove/
// /manualRedaction/request/remove/
removeOrSuggestRemoveAnnotation(
annotationWrapper: AnnotationWrapper,
removeFromDictionary: boolean = false
) {
removeOrSuggestRemoveAnnotation(annotationWrapper: AnnotationWrapper, removeFromDictionary: boolean = false) {
let mode: Mode,
body: any,
removeDict = false;
@ -258,15 +224,10 @@ export class ManualAnnotationService {
}
private _notify(key: string, type: NotificationType = NotificationType.SUCCESS, data?: any) {
this._notificationService.showToastNotification(
this._translateService.instant(key, data),
null,
type,
{
positionClass: 'toast-file-preview',
actions: []
}
);
this._notificationService.showToastNotification(this._translateService.instant(key, data), null, type, {
positionClass: 'toast-file-preview',
actions: []
});
}
private _getMessage(mode: Mode, modifyDictionary?: boolean, error: boolean = false) {

View File

@ -0,0 +1,29 @@
import { Component, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
/**
* Inherit this class when you need to subscribe to observables in your components
*
* @remarks You must explicitly call super.ngOnDestroy in you component to unsubscribe
*/
@Component({ template: '' })
export abstract class AutoUnsubscribeComponent implements OnDestroy {
private _subscriptions = new Subscription();
/**
* Call this method when you want to subscribe to an observable
* @param subscription - the new subscription to add to subscriptions array
*/
set addSubscription(subscription: Subscription) {
this._subscriptions.closed = false;
this._subscriptions.add(subscription);
}
/**
* This method unsubscribes active subscriptions
* Explicitly call this method in your component's ngOnDestroy
*/
ngOnDestroy(): void {
this._subscriptions.unsubscribe();
}
}

View File

@ -1,4 +1,4 @@
import { Component, Injector, ViewChild } from '@angular/core';
import { Component, Injector, OnDestroy, ViewChild } from '@angular/core';
import { SortingOption, SortingService } from '@services/sorting.service';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { FilterService } from '../services/filter.service';
@ -6,58 +6,60 @@ import { SearchService } from '../services/search.service';
import { ScreenStateService } from '../services/screen-state.service';
import { Observable } from 'rxjs';
import { FilterModel } from '../components/filters/popup-filter/model/filter.model';
import { AutoUnsubscribeComponent } from './auto-unsubscribe.component';
@Component({ template: '' })
export abstract class BaseListingComponent<T> {
export abstract class BaseListingComponent<T> extends AutoUnsubscribeComponent implements OnDestroy {
@ViewChild(CdkVirtualScrollViewport)
readonly scrollViewport: CdkVirtualScrollViewport;
readonly filterService: FilterService<T>;
protected readonly _sortingService: SortingService;
protected readonly _searchService: SearchService<T>;
protected readonly _screenStateService: ScreenStateService<T>;
readonly sortingService: SortingService;
readonly searchService: SearchService<T>;
readonly screenStateService: ScreenStateService<T>;
protected constructor(protected readonly _injector: Injector) {
super();
this.filterService = this._injector.get<FilterService<T>>(FilterService);
this._sortingService = this._injector.get<SortingService>(SortingService);
this._searchService = this._injector.get<SearchService<T>>(SearchService);
this._screenStateService = this._injector.get<ScreenStateService<T>>(ScreenStateService);
this.sortingService = this._injector.get<SortingService>(SortingService);
this.searchService = this._injector.get<SearchService<T>>(SearchService);
this.screenStateService = this._injector.get<ScreenStateService<T>>(ScreenStateService);
}
get selectedEntitiesIds$(): Observable<string[]> {
return this._screenStateService.selectedEntitiesIds$;
ngOnDestroy(): void {
super.ngOnDestroy();
}
get selectedEntities$(): Observable<T[]> {
return this.screenStateService.selectedEntities$;
}
get displayedEntities$(): Observable<T[]> {
return this._screenStateService.displayedEntities$;
return this.screenStateService.displayedEntities$;
}
get allEntities$(): Observable<T[]> {
return this._screenStateService.entities$;
return this.screenStateService.allEntities$;
}
get displayedEntities(): T[] {
return this._screenStateService.displayedEntities;
return this.screenStateService.displayedEntities;
}
get allEntities(): T[] {
return this._screenStateService.entities;
return this.screenStateService.allEntities;
}
get areAllEntitiesSelected() {
return this._screenStateService.areAllEntitiesSelected;
return this.screenStateService.areAllEntitiesSelected;
}
get areSomeEntitiesSelected$() {
return this._screenStateService.areSomeEntitiesSelected$;
get areSomeEntitiesSelected$(): Observable<boolean> {
return this.screenStateService.areSomeEntitiesSelected$;
}
get sortingOption(): SortingOption {
return this._sortingService.getSortingOption();
}
get searchForm() {
return this._searchService.searchForm;
return this.sortingService.getSortingOption();
}
get noMatch(): boolean {
@ -68,28 +70,20 @@ export abstract class BaseListingComponent<T> {
return this.allEntities.length === 0;
}
getFilter$(slug: string): Observable<FilterModel[]> {
return this.filterService.getFilter$(slug);
}
resetFilters() {
this.filterService.reset();
}
toggleSort($event) {
this._sortingService.toggleSort($event);
this.sortingService.toggleSort($event);
}
toggleSelectAll() {
return this._screenStateService.toggleSelectAll();
return this.screenStateService.toggleSelectAll();
}
toggleEntitySelected(event: MouseEvent, entity: T) {
event.stopPropagation();
return this._screenStateService.toggleEntitySelected(entity);
return this.screenStateService.toggleEntitySelected(entity);
}
isSelected(entity: T) {
return this._screenStateService.isSelected(entity);
isSelected(entity: T): boolean {
return this.screenStateService.isSelected(entity);
}
}

View File

@ -36,25 +36,17 @@ export class FileDownloadBtnComponent {
return this._permissionsService.canDownloadFiles(this.file);
}
return (
this.file.length > 0 &&
this.file.reduce(
(acc, file) => acc && this._permissionsService.canDownloadFiles(file),
true
)
);
return this.file.length > 0 && this.file.reduce((acc, file) => acc && this._permissionsService.canDownloadFiles(file), true);
}
downloadFiles($event: MouseEvent) {
$event.stopPropagation();
this._fileDownloadService
.downloadFiles(Array.isArray(this.file) ? this.file : [this.file], this.dossier)
.subscribe(() => {
this._notificationService.showToastNotification(
this._translateService.instant('download-status.queued', {
baseUrl: this._baseHref
})
);
});
this._fileDownloadService.downloadFiles(Array.isArray(this.file) ? this.file : [this.file], this.dossier).subscribe(() => {
this._notificationService.showToastNotification(
this._translateService.instant('download-status.queued', {
baseUrl: this._baseHref
})
);
});
}
}

View File

@ -8,7 +8,7 @@
>
<g *ngFor="let value of config; let i = index">
<circle
*ngIf="exists(i)"
*ngIf="!!chartData[i]"
[attr.stroke]="value.color.includes('#') ? value.color : ''"
[class]="value.color"
attr.cx="{{ cx }}"
@ -23,10 +23,7 @@
</g>
</svg>
<div
[style]="'height: ' + size + 'px; width: ' + size + 'px; padding: ' + strokeWidth + 'px;'"
class="text-container"
>
<div [style]="'height: ' + size + 'px; width: ' + size + 'px; padding: ' + strokeWidth + 'px;'" class="text-container">
<div class="heading-xl">{{ displayedDataTotal }}</div>
<div class="mt-5">{{ subtitle | translate }}</div>
</div>
@ -37,7 +34,7 @@
(click)="selectValue(val.key)"
*ngFor="let val of config"
[class.active]="filterService.filterChecked$('statusFilters', val.key) | async"
[class.filter-disabled]="!filter"
[class.filter-disabled]="(filterService.getFilter$('statusFilters') | async)?.length === 0"
>
<redaction-status-bar
[config]="[
@ -45,10 +42,7 @@
length: val.value,
color: val.color,
label: getLabel(val),
cssClass:
val.color === 'PROCESSING' || val.color === 'OCR_PROCESSING'
? 'loading'
: ''
cssClass: val.color === 'PROCESSING' || val.color === 'OCR_PROCESSING' ? 'loading' : ''
}
]"
[small]="true"

View File

@ -1,6 +1,5 @@
import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
import { Component, Input, OnChanges } from '@angular/core';
import { Color } from '@utils/types';
import { FilterModel } from '@shared/components/filters/popup-filter/model/filter.model';
import { FilterService } from '@shared/services/filter.service';
export interface DoughnutChartConfig {
@ -22,11 +21,8 @@ export class SimpleDoughnutChartComponent<T> implements OnChanges {
@Input() radius = 85;
@Input() strokeWidth = 20;
@Input() direction: 'row' | 'column' = 'column';
@Input() filter: FilterModel[];
@Input() totalType: 'sum' | 'count' = 'sum';
@Input() counterText: string;
@Output()
toggleFilter = new EventEmitter<string>();
chartData: any[] = [];
perimeter: number;
@ -36,11 +32,11 @@ export class SimpleDoughnutChartComponent<T> implements OnChanges {
constructor(readonly filterService: FilterService<T>) {}
get circumference() {
get circumference(): number {
return 2 * Math.PI * this.radius;
}
get dataTotal() {
get dataTotal(): number {
return this.config.map(v => v.value).reduce((acc, val) => acc + val, 0);
}
@ -56,48 +52,34 @@ export class SimpleDoughnutChartComponent<T> implements OnChanges {
}
calculateChartData() {
const newData = [];
let angleOffset = -90;
this.config.forEach(dataVal => {
if (dataVal.value > 0) {
const data = {
degrees: angleOffset
};
newData.push(data);
angleOffset = this.dataPercentage(dataVal.value) * 360 + angleOffset;
} else {
newData.push(null);
}
this.chartData = this.config.map(dataVal => {
if (dataVal.value === 0) return null;
const res = { degrees: angleOffset };
angleOffset = this.dataPercentage(dataVal.value) * 360 + angleOffset;
return res;
});
this.chartData = newData;
}
calculateStrokeDashOffset(dataVal) {
calculateStrokeDashOffset(dataVal: number): number {
const strokeDiff = this.dataPercentage(dataVal) * this.circumference;
return this.circumference - strokeDiff;
}
dataPercentage(dataVal) {
dataPercentage(dataVal: number): number {
return dataVal / this.dataTotal;
}
returnCircleTransformValue(index) {
returnCircleTransformValue(index: number) {
return `rotate(${this.chartData[index].degrees}, ${this.cx}, ${this.cy})`;
}
getLabel(config: DoughnutChartConfig): string {
return this.totalType === 'sum'
? `${config.value} ${config.label}`
: `${config.label} (${config.value} ${this.counterText})`;
getLabel({ label, value }: DoughnutChartConfig): string {
return this.totalType === 'sum' ? `${value} ${label}` : `${label} (${value} ${this.counterText})`;
}
selectValue(key: string) {
selectValue(key: string): void {
this.filterService.toggleFilter('statusFilters', key);
this.filterService.filterEntities();
this.toggleFilter.emit(key);
}
exists(index: number) {
return !!this.chartData[index];
}
}

View File

@ -1,9 +1,6 @@
import { ChangeDetectorRef, Injectable } from '@angular/core';
import { FilterModel } from '@shared/components/filters/popup-filter/model/filter.model';
import {
getFilteredEntities,
processFilters
} from '@shared/components/filters/popup-filter/utils/filter-utils';
import { getFilteredEntities, processFilters } from '@shared/components/filters/popup-filter/utils/filter-utils';
import { FilterWrapper } from '@shared/components/filters/popup-filter/model/filter-wrapper.model';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { SearchService } from '@shared/services/search.service';
@ -48,7 +45,7 @@ export class FilterService<T> {
}
filterEntities(): void {
const filtered = getFilteredEntities(this._screenStateService.entities, this.filters);
const filtered = getFilteredEntities(this._screenStateService.allEntities, this.filters);
this._screenStateService.setFilteredEntities(filtered);
this._searchService.executeSearchImmediately();
@ -98,8 +95,7 @@ export class FilterService<T> {
}
private _toFlatFilters(entities: FilterWrapper[]): FilterModel[] {
const flatChildren = (filters: FilterModel[]) =>
(filters ?? []).reduce((acc, f) => [...acc, ...(f?.filters ?? [])], []);
const flatChildren = (filters: FilterModel[]) => (filters ?? []).reduce((acc, f) => [...acc, ...(f?.filters ?? [])], []);
return entities.reduce((acc, f) => [...acc, ...f.values, ...flatChildren(f.values)], []);
}

View File

@ -4,23 +4,21 @@ import { distinctUntilChanged, map } from 'rxjs/operators';
@Injectable()
export class ScreenStateService<T> {
entities$ = new BehaviorSubject<T[]>([]);
allEntities$ = new BehaviorSubject<T[]>([]);
filteredEntities$ = new BehaviorSubject<T[]>([]);
displayedEntities$ = new BehaviorSubject<T[]>([]);
selectedEntitiesIds$ = new BehaviorSubject<string[]>([]);
selectedEntities$ = new BehaviorSubject<T[]>([]);
private _idKey: string;
get entities(): T[] {
return Object.values(this.entities$.getValue());
get allEntities(): T[] {
return Object.values(this.allEntities$.getValue());
}
get filteredEntities(): T[] {
return Object.values(this.filteredEntities$.getValue());
}
get selectedEntitiesIds(): string[] {
return Object.values(this.selectedEntitiesIds$.getValue());
get selectedEntities(): T[] {
return Object.values(this.selectedEntities$.getValue());
}
get displayedEntities(): T[] {
@ -28,85 +26,62 @@ export class ScreenStateService<T> {
}
map<K>(func: (state: T[]) => K): Observable<K> {
return this.entities$.asObservable().pipe(
return this.allEntities$.asObservable().pipe(
map((state: T[]) => func(state)),
distinctUntilChanged()
);
}
setEntities(newEntities: Partial<T[]>): void {
this.entities$.next(newEntities);
this.allEntities$.next(newEntities);
}
setFilteredEntities(newEntities: Partial<T[]>): void {
this.filteredEntities$.next(newEntities);
}
setSelectedEntitiesIds(newEntities: Partial<string[]>): void {
this.selectedEntitiesIds$.next(newEntities);
setSelectedEntities(newEntities: Partial<T[]>): void {
this.selectedEntities$.next(newEntities);
}
setDisplayedEntities(newEntities: Partial<T[]>): void {
this.displayedEntities$.next(newEntities);
}
setIdKey(value: string): void {
this._idKey = value;
}
get areAllEntitiesSelected(): boolean {
return (
this.displayedEntities.length !== 0 &&
this.selectedEntitiesIds.length === this.displayedEntities.length
);
return this.displayedEntities.length !== 0 && this.selectedEntities.length === this.displayedEntities.length;
}
get areSomeEntitiesSelected$(): Observable<boolean> {
return this.selectedEntitiesIds$.pipe(map(all => all.length > 0));
return this.selectedEntities$.pipe(map(all => all.length > 0));
}
isSelected(entity: T): boolean {
return this.selectedEntitiesIds.indexOf(entity[this._getIdKey]) !== -1;
return this.selectedEntities.indexOf(entity) !== -1;
}
toggleEntitySelected(entity: T): void {
const currentEntityIdx = this.selectedEntitiesIds.indexOf(entity[this._getIdKey]);
const currentEntityIdx = this.selectedEntities.indexOf(entity);
if (currentEntityIdx === -1) {
const currentEntityId = entity[this._getIdKey];
return this.setSelectedEntitiesIds([...this.selectedEntitiesIds, currentEntityId]);
return this.setSelectedEntities([...this.selectedEntities, entity]);
}
this.setSelectedEntitiesIds(
this.selectedEntitiesIds.filter((el, idx) => idx !== currentEntityIdx)
);
this.setSelectedEntities(this.selectedEntities.filter((el, idx) => idx !== currentEntityIdx));
}
toggleSelectAll(): void {
if (this.areAllEntitiesSelected) return this.setSelectedEntitiesIds([]);
this.setSelectedEntitiesIds(this._displayedEntitiesIds);
if (this.areAllEntitiesSelected) return this.setSelectedEntities([]);
this.setSelectedEntities(this.displayedEntities);
}
updateSelection(): void {
if (!this._idKey) return;
const ids = this._displayedEntitiesIds.filter(id => this.selectedEntitiesIds.includes(id));
this.setSelectedEntitiesIds(ids);
const items = this.displayedEntities.filter(item => this.selectedEntities.includes(item));
this.setSelectedEntities(items);
}
logCurrentState(): void {
console.log('Entities', this.entities);
console.log('Entities', this.allEntities);
console.log('Displayed', this.displayedEntities);
console.log('Filtered', this.filteredEntities);
console.log('Selected', this.selectedEntitiesIds);
}
private get _displayedEntitiesIds(): string[] {
return this.displayedEntities.map(entity => entity[this._getIdKey]);
}
private get _getIdKey(): string {
if (!this._idKey) throw new Error('Not implemented');
return this._idKey;
console.log('Selected', this.selectedEntities);
}
}

View File

@ -12,10 +12,7 @@ export class SearchService<T> {
query: ['']
});
constructor(
private readonly _screenStateService: ScreenStateService<T>,
private readonly _formBuilder: FormBuilder
) {
constructor(private readonly _screenStateService: ScreenStateService<T>, private readonly _formBuilder: FormBuilder) {
this.searchForm.valueChanges.subscribe(() => this.executeSearch());
}
@ -26,17 +23,14 @@ export class SearchService<T> {
}
executeSearchImmediately(): void {
const displayed =
this._screenStateService.filteredEntities || this._screenStateService.entities;
const displayed = this._screenStateService.filteredEntities || this._screenStateService.allEntities;
if (!this._searchKey) {
return this._screenStateService.setDisplayedEntities(displayed);
}
this._screenStateService.setDisplayedEntities(
displayed.filter(entity =>
this._searchField(entity).toLowerCase().includes(this._searchValue)
)
displayed.filter(entity => this._searchField(entity).toLowerCase().includes(this._searchValue))
);
this._screenStateService.updateSelection();
}

View File

@ -1,15 +1,36 @@
import { Injectable } from '@angular/core';
import { UserPreferenceControllerService } from '@redaction/red-ui-http';
interface UserAttributes {
[p: string]: string[];
}
@Injectable({
providedIn: 'root'
})
export class UserPreferenceService {
private _userAttributes: UserAttributes = {};
constructor(private readonly _userPreferenceControllerService: UserPreferenceControllerService) {
_userPreferenceControllerService.getAllUserAttributes().subscribe(attributes => {
this._userAttributes = attributes ?? {};
});
}
get userAttributes(): UserAttributes {
return this._userAttributes;
}
get areDevFeaturesEnabled() {
const value = sessionStorage.getItem('redaction.enable-dev-features');
if (value) {
return value === 'true';
return value ? value === 'true' : false;
}
getLastOpenedFileId(key: string): string {
if (this.userAttributes[key]?.length > 0) {
return this.userAttributes[key][0];
}
return false;
return '';
}
toggleDevFeatures() {

View File

@ -2,7 +2,6 @@ import { EventEmitter, Injectable } from '@angular/core';
import {
DictionaryControllerService,
Dossier,
DossierControllerService,
DossierTemplateControllerService,
FileAttributesConfig,
FileAttributesControllerService,
@ -21,6 +20,7 @@ import { FileStatusWrapper } from '@models/file/file-status.wrapper';
import { DossierWrapper } from './model/dossier.wrapper';
import { TypeValueWrapper } from '@models/file/type-value.wrapper';
import { DossierTemplateModelWrapper } from '@models/file/dossier-template-model.wrapper';
import { DossiersService } from '../modules/dossier/services/dossiers.service';
export interface AppState {
dossiers: DossierWrapper[];
@ -46,7 +46,7 @@ export class AppStateService {
constructor(
private readonly _router: Router,
private readonly _userService: UserService,
private readonly _dossierControllerService: DossierControllerService,
private readonly _dossiersService: DossiersService,
private readonly _notificationService: NotificationService,
private readonly _reanalysisControllerService: ReanalysisControllerService,
private readonly _translateService: TranslateService,
@ -65,15 +65,15 @@ export class AppStateService {
activeDictionaryType: null
};
this._router.events.subscribe((event: Event) => {
_router.events.subscribe((event: Event) => {
if (AppStateService._isFileOverviewRoute(event)) {
const url = (event as ResolveStart).url.replace('/main/dossiers/', '');
const [dossierId, , fileId] = url.split(/[/?]/);
this.activateFile(dossierId, fileId);
return this.activateFile(dossierId, fileId);
}
if (AppStateService._isDossierOverviewRoute(event)) {
const dossierId = (event as ResolveStart).url.replace('/main/dossiers/', '');
this.activateDossier(dossierId);
return this.activateDossier(dossierId);
}
if (AppStateService._isRandomRoute(event)) {
this._appState.activeDossierId = null;
@ -88,11 +88,7 @@ export class AppStateService {
}
get aggregatedFiles(): FileStatusWrapper[] {
const result: FileStatusWrapper[] = [];
this._appState.dossiers.forEach(p => {
result.push(...p.files);
});
return result;
return this.allDossiers.reduce((acc, { files }) => [...acc, ...files], []);
}
get activeDossierTemplateId(): string {
@ -128,7 +124,7 @@ export class AppStateService {
}
get activeDossier(): DossierWrapper | undefined {
return this._appState.dossiers.find(p => p.dossierId === this.activeDossierId);
return this.allDossiers.find(p => p.dossierId === this.activeDossierId);
}
get allDossiers(): DossierWrapper[] {
@ -147,18 +143,14 @@ export class AppStateService {
return this._appState.activeFileId;
}
get totalAnalysedPages() {
get totalAnalysedPages(): number {
return this._appState.totalAnalysedPages;
}
get totalPeople() {
get totalPeople(): number {
return this._appState.totalPeople;
}
get totalDocuments() {
return this._appState.totalDocuments;
}
private static _isFileOverviewRoute(event: Event) {
return event instanceof ResolveStart && event.url.includes('/main/dossiers/') && event.url.includes('/file/');
}
@ -177,18 +169,15 @@ export class AppStateService {
}
}
getDictionaryColor(type?: string, dossierTemplateId?: string) {
if (!dossierTemplateId && this.activeDossier) {
dossierTemplateId = this.activeDossier.dossierTemplateId;
}
getDictionaryColor(type?: string, dossierTemplateId = this.activeDossier?.dossierTemplateId) {
if (!dossierTemplateId) {
dossierTemplateId = this.dossierTemplates.length > 0 ? this.dossierTemplates[0].dossierTemplateId : undefined;
dossierTemplateId = this.dossierTemplates[0]?.dossierTemplateId;
}
if (!dossierTemplateId) {
return undefined;
}
const color = this._dictionaryData[dossierTemplateId][type]?.hexColor;
return color ? color : this._dictionaryData[dossierTemplateId]['default'].hexColor;
return color ?? this._dictionaryData[dossierTemplateId]['default'].hexColor;
}
getDossierTemplateById(id: string): DossierTemplateModelWrapper {
@ -220,20 +209,21 @@ export class AppStateService {
}
async loadAllDossiers(emitEvents: boolean = true) {
const dossiers = await this._dossierControllerService.getDossiers().toPromise();
if (dossiers) {
const mappedDossiers = dossiers.map(p => new DossierWrapper(p, this._getExistingFiles(p.dossierId)));
const fileData = await this._statusControllerService.getFileStatusForDossiers(mappedDossiers.map(p => p.dossierId)).toPromise();
for (const dossierId of Object.keys(fileData)) {
const dossier = mappedDossiers.find(p => p.dossierId === dossierId);
this._processFiles(dossier, fileData[dossierId], emitEvents);
}
this._appState.dossiers = mappedDossiers;
this._computeStats();
const dossiers = await this._dossiersService.getAll();
if (!dossiers) {
return;
}
const mappedDossiers = dossiers.map(p => new DossierWrapper(p, this._getExistingFiles(p.dossierId)));
const fileData = await this._statusControllerService.getFileStatusForDossiers(mappedDossiers.map(p => p.dossierId)).toPromise();
for (const dossierId of Object.keys(fileData)) {
const dossier = mappedDossiers.find(p => p.dossierId === dossierId);
this._processFiles(dossier, fileData[dossierId], emitEvents);
}
this._appState.dossiers = mappedDossiers;
this._computeStats();
}
async reloadActiveFile() {
@ -246,8 +236,8 @@ export class AppStateService {
const activeFileWrapper = new FileStatusWrapper(
activeFile,
this._userService.getNameForId(activeFile.currentReviewer),
this.activeDossier.dossierTemplateId,
this._appState.fileAttributesConfig[this.activeDossier.dossierTemplateId]
this.activeDossierTemplateId,
this._appState.fileAttributesConfig[this.activeDossierTemplateId]
);
this.activeDossier.files = this.activeDossier.files.map(file =>
file.fileId === activeFileWrapper.fileId ? activeFileWrapper : file
@ -261,32 +251,26 @@ export class AppStateService {
return activeFileWrapper;
}
async getFiles(dossier?: DossierWrapper, emitEvents: boolean = true) {
if (!dossier) {
dossier = this.activeDossier;
}
async getFiles(dossier: DossierWrapper = this.activeDossier, emitEvents = true) {
const files = await this._statusControllerService.getDossierStatus(dossier.dossierId).toPromise();
return this._processFiles(dossier, files, emitEvents);
}
async reanalyzeDossier(dossier?: DossierWrapper) {
if (!dossier) {
dossier = this.activeDossier;
}
await this._reanalysisControllerService.reanalyzeDossier(dossier.dossierId).toPromise();
async reanalyzeDossier({ dossierId }: DossierWrapper = this.activeDossier) {
await this._reanalysisControllerService.reanalyzeDossier(dossierId).toPromise();
}
activateDossier(dossierId: string) {
activateDossier(dossierId: string): void {
this._appState.activeFileId = null;
this._appState.activeDossierId = dossierId;
if (!this.activeDossier) {
this._appState.activeDossierId = null;
this._router.navigate(['/main/dossiers']);
this._router.navigate(['/main/dossiers']).then();
return;
} else {
this.updateDossierDictionary(this.activeDossier.dossierTemplateId, dossierId);
}
this.updateDossierDictionary(this.activeDossier.dossierTemplateId, dossierId);
}
updateDossierDictionary(dossierTemplateId: string, dossierId: string) {
@ -302,7 +286,7 @@ export class AppStateService {
}
activateFile(dossierId: string, fileId: string) {
if (this._appState.activeDossierId === dossierId && this._appState.activeFileId === fileId) return;
if (this.activeDossierId === dossierId && this.activeFileId === fileId) return;
this.activateDossier(dossierId);
if (this.activeDossier) {
this._appState.activeFileId = fileId;
@ -341,29 +325,25 @@ export class AppStateService {
}
deleteDossier(dossier: DossierWrapper) {
return this._dossierControllerService
.deleteDossier(dossier.dossierId)
.toPromise()
.then(
() => {
const index = this._appState.dossiers.findIndex(p => p.dossier.dossierId === dossier.dossierId);
this._appState.dossiers.splice(index, 1);
this._appState.dossiers = [...this._appState.dossiers];
},
() => {
this._notificationService.showToastNotification(
this._translateService.instant('dossiers.delete.delete-failed', dossier),
null,
NotificationType.ERROR
);
}
);
return this._dossiersService.delete(dossier.dossierId).then(
() => {
const index = this.allDossiers.findIndex(p => p.dossierId === dossier.dossierId);
this._appState.dossiers.splice(index, 1);
},
() => {
this._notificationService.showToastNotification(
this._translateService.instant('dossiers.delete.delete-failed', dossier),
null,
NotificationType.ERROR
);
}
);
}
async addOrUpdateDossier(dossier: Dossier) {
async createOrUpdateDossier(dossier: Dossier) {
try {
const updatedDossier = await this._dossierControllerService.createOrUpdateDossier(dossier).toPromise();
let foundDossier = this._appState.dossiers.find(p => p.dossier.dossierId === updatedDossier.dossierId);
const updatedDossier = await this._dossiersService.createOrUpdate(dossier);
let foundDossier = this.allDossiers.find(p => p.dossierId === updatedDossier.dossierId);
if (foundDossier) {
Object.assign((foundDossier.dossier = updatedDossier));
} else {
@ -385,7 +365,7 @@ export class AppStateService {
async reloadActiveDossierFiles() {
if (this.activeDossierId) {
await this.getFiles(this.activeDossier);
await this.getFiles();
}
}
@ -408,7 +388,7 @@ export class AppStateService {
}
async loadAllDossiersIfNecessary() {
if (!this._appState.dossiers.length) {
if (!this.allDossiers.length) {
await this.loadAllDossiers();
}
}
@ -642,7 +622,7 @@ export class AppStateService {
return [typeObs, colorsObs];
}
async loadDictionaryData() {
async loadDictionaryData(): Promise<void> {
const obj = {};
const observables = [];
@ -660,9 +640,9 @@ export class AppStateService {
this._dictionaryData = obj;
}
private _getExistingFiles(dossierId: string) {
const found = this._appState.dossiers.find(p => p.dossier.dossierId === dossierId);
return found ? found.files : [];
private _getExistingFiles(dossierId: string): FileStatusWrapper[] {
const dossier = this.allDossiers.find(p => p.dossierId === dossierId);
return dossier?.files ?? [];
}
private _processFiles(dossier: DossierWrapper, files: FileStatus[], emitEvents: boolean = true) {
@ -727,25 +707,18 @@ export class AppStateService {
let totalAnalysedPages = 0;
let totalDocuments = 0;
const totalPeople = new Set<string>();
this._appState.dossiers.forEach(p => {
totalDocuments += p.files.length;
if (p.dossier.memberIds) {
p.dossier.memberIds.forEach(m => totalPeople.add(m));
this.allDossiers.forEach(d => {
totalDocuments += d.files.length;
if (d.dossier.memberIds) {
d.dossier.memberIds.forEach(m => totalPeople.add(m));
}
let numberOfPages = 0;
p.files.forEach(f => {
numberOfPages += f.numberOfPages;
});
p.totalNumberOfPages = numberOfPages;
totalAnalysedPages += numberOfPages;
d.totalNumberOfPages = d.files.reduce((acc, file) => acc + file.numberOfPages, 0);
totalAnalysedPages += d.totalNumberOfPages;
});
this._appState.totalPeople = totalPeople.size;
this._appState.totalAnalysedPages = totalAnalysedPages;
this._appState.totalDocuments = totalDocuments;
if (this.activeDossierId && this.activeFileId) {
this.activateFile(this.activeDossierId, this.activeFileId);
}
}
}

View File

@ -9,5 +9,5 @@ export const StatusSorter = {
UNDER_REVIEW: 15,
UNDER_APPROVAL: 20,
APPROVED: 25,
byKey: (a: { key: string }, b: { key: string }) => StatusSorter[a.key] - StatusSorter[b.key]
byKey: (a: { key?: string }, b: { key?: string }) => StatusSorter[a.key] - StatusSorter[b.key]
};