diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.ts
index 08527c58a..f329fc38c 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.ts
+++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.ts
@@ -1,14 +1,14 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
import { FilterService, mapEach } from '@iqser/common-ui';
-import { DossiersService } from '@services/entity-services/dossiers.service';
+import { ActiveDossiersService } from '../../../../../../services/dossiers/active-dossiers.service';
import { combineLatest, Observable } from 'rxjs';
import { DossierStats, FileCountPerWorkflowStatus, StatusSorter } from '@red/domain';
-import { workflowFileStatusTranslations } from '../../../../translations/file-status-translations';
+import { workflowFileStatusTranslations } from '../../../../../../translations/file-status-translations';
import { TranslateChartService } from '@services/translate-chart.service';
import { filter, map, switchMap } from 'rxjs/operators';
-import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
-import { DossierStateService } from '../../../../../../services/entity-services/dossier-state.service';
+import { DossierStatsService } from '../../../../../../services/dossiers/dossier-stats.service';
+import { DossierStateService } from '@services/entity-services/dossier-state.service';
import { TranslateService } from '@ngx-translate/core';
@Component({
@@ -23,25 +23,25 @@ export class DossiersListingDetailsComponent {
constructor(
readonly filterService: FilterService,
- readonly dossiersService: DossiersService,
+ readonly activeDossiersService: ActiveDossiersService,
private readonly _dossierStatsMap: DossierStatsService,
private readonly _translateChartService: TranslateChartService,
private readonly _dossierStateService: DossierStateService,
private readonly _translateService: TranslateService,
) {
- this.documentsChartData$ = this.dossiersService.all$.pipe(
+ this.documentsChartData$ = this.activeDossiersService.all$.pipe(
mapEach(dossier => _dossierStatsMap.watch$(dossier.dossierId)),
switchMap(stats$ => combineLatest(stats$)),
filter(stats => !stats.some(s => s === undefined)),
map(stats => this._toChartData(stats)),
);
- this.dossiersChartData$ = this.dossiersService.all$.pipe(map(() => this._toDossierChartData()));
+ this.dossiersChartData$ = this.activeDossiersService.all$.pipe(map(() => this._toDossierChartData()));
}
private _toDossierChartData(): DoughnutChartConfig[] {
this._dossierStateService.all.forEach(
- state => (state.dossierCount = this.dossiersService.getCountWithState(state.dossierStatusId)),
+ state => (state.dossierCount = this.activeDossiersService.getCountWithState(state.dossierStatusId)),
);
const configArray: DoughnutChartConfig[] = [
...this._dossierStateService.all
@@ -54,7 +54,7 @@ export class DossiersListingDetailsComponent {
.values(),
];
- const notAssignedLength = this.dossiersService.all.length - configArray.map(v => v.value).reduce((acc, val) => acc + val, 0);
+ const notAssignedLength = this.activeDossiersService.all.length - configArray.map(v => v.value).reduce((acc, val) => acc + val, 0);
configArray.push({
value: notAssignedLength,
label: this._translateService.instant('edit-dossier-dialog.general-info.form.dossier-status.placeholder'),
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-status/dossiers-listing-status.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-status/dossiers-listing-status.component.html
deleted file mode 100644
index 3df7ebc04..000000000
--- a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-status/dossiers-listing-status.component.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
{{ currentState.name }}
-
-
-
-
-
-
-
{{ 'edit-dossier-dialog.general-info.form.dossier-status.placeholder' | translate }}
-
-
-
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.html
index 606997852..1adaefc28 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.html
+++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.html
@@ -21,7 +21,7 @@
-
+
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.ts
index 0151c3320..209e5bebf 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.ts
+++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.ts
@@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
import { Dossier, DossierStats } from '@red/domain';
-import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
+import { DossierStatsService } from '../../../../../../services/dossiers/dossier-stats.service';
import { BehaviorSubject, Observable } from 'rxjs';
import { switchMap } from 'rxjs/operators';
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/config.service.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/config.service.ts
index 19ad1c220..4cf52fe54 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/config.service.ts
+++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/config.service.ts
@@ -5,12 +5,12 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { TranslateService } from '@ngx-translate/core';
import { UserPreferenceService } from '@services/user-preference.service';
import { UserService } from '@services/user.service';
-import { workflowFileStatusTranslations } from '../../translations/file-status-translations';
+import { workflowFileStatusTranslations } from '../../../../translations/file-status-translations';
import { dossierMemberChecker, dossierStateChecker, dossierTemplateChecker, RedactionFilterSorter } from '@utils/index';
import { workloadTranslations } from '../../translations/workload-translations';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
-import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
-import { DossierStateService } from '../../../../services/entity-services/dossier-state.service';
+import { DossierStatsService } from '../../../../services/dossiers/dossier-stats.service';
+import { DossierStateService } from '@services/entity-services/dossier-state.service';
@Injectable()
export class ConfigService {
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/dossiers-listing.module.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/dossiers-listing.module.ts
index 29af016b3..d36444a4d 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/dossiers-listing.module.ts
+++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/dossiers-listing.module.ts
@@ -7,12 +7,10 @@ import { RouterModule, Routes } from '@angular/router';
import { DossiersListingActionsComponent } from './components/dossiers-listing-actions/dossiers-listing-actions.component';
import { SharedModule } from '@shared/shared.module';
import { DossiersListingDetailsComponent } from './components/dossiers-listing-details/dossiers-listing-details.component';
-import { DossiersListingDossierNameComponent } from './components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component';
import { ConfigService } from './config.service';
import { TableItemComponent } from './components/table-item/table-item.component';
import { SharedDossiersModule } from '../../shared/shared-dossiers.module';
import { DossierWorkloadColumnComponent } from './components/dossier-workload-column/dossier-workload-column.component';
-import { DossiersListingStatusComponent } from './components/dossiers-listing-status/dossiers-listing-status.component';
import { DossierDocumentsStatusComponent } from './components/dossier-documents-status/dossier-documents-status.component';
const routes: Routes = [
@@ -29,10 +27,8 @@ const routes: Routes = [
DossiersListingScreenComponent,
DossiersListingActionsComponent,
DossiersListingDetailsComponent,
- DossiersListingDossierNameComponent,
DossierWorkloadColumnComponent,
TableItemComponent,
- DossiersListingStatusComponent,
DossierDocumentsStatusComponent,
],
providers: [ConfigService],
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.ts
index d70a2ae00..1d78c6fe4 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.ts
+++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.ts
@@ -8,7 +8,7 @@ import { DossiersDialogService } from '../../../services/dossiers-dialog.service
import { DefaultListingServicesTmp, EntitiesService, ListingComponent, OnAttach, TableComponent } from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { ConfigService } from '../config.service';
-import { DossiersService } from '@services/entity-services/dossiers.service';
+import { ActiveDossiersService } from '../../../../../services/dossiers/active-dossiers.service';
import { FilesService } from '@services/entity-services/files.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { tap } from 'rxjs/operators';
@@ -18,7 +18,7 @@ import { tap } from 'rxjs/operators';
styleUrls: ['./dossiers-listing-screen.component.scss'],
providers: [
...DefaultListingServicesTmp,
- { provide: EntitiesService, useExisting: DossiersService },
+ { provide: EntitiesService, useExisting: ActiveDossiersService },
{ provide: ListingComponent, useExisting: forwardRef(() => DossiersListingScreenComponent) },
],
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -40,7 +40,7 @@ export class DossiersListingScreenComponent extends ListingComponent
im
protected readonly _injector: Injector,
private readonly _userService: UserService,
readonly permissionsService: PermissionsService,
- private readonly _dossiersService: DossiersService,
+ private readonly _activeDossiersService: ActiveDossiersService,
private readonly _dialogService: DossiersDialogService,
private readonly _translateChartService: TranslateChartService,
private readonly _configService: ConfigService,
@@ -55,7 +55,7 @@ export class DossiersListingScreenComponent extends ListingComponent im
}
ngOnInit(): void {
- this.addSubscription = this._dossiersService.all$.pipe(tap(() => this._computeAllFilters())).subscribe();
+ this.addSubscription = this._activeDossiersService.all$.pipe(tap(() => this._computeAllFilters())).subscribe();
}
ngOnAttach(): void {
diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/annotation-actions/annotation-actions.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/annotation-actions/annotation-actions.component.ts
index 1edf49830..6d83d104c 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/annotation-actions/annotation-actions.component.ts
+++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/annotation-actions/annotation-actions.component.ts
@@ -72,6 +72,10 @@ export class AnnotationActionsComponent implements OnChanges {
return this.annotations?.length === 1 && this.annotations?.[0].resizing;
}
+ get scrollableParentView(): ScrollableParentView {
+ return ScrollableParentViews.ANNOTATIONS_LIST;
+ }
+
async ngOnChanges(): Promise {
await this._setPermissions();
}
@@ -115,10 +119,6 @@ export class AnnotationActionsComponent implements OnChanges {
this.annotationActionsService.cancelResize($event, this.viewer, this.annotations[0], this.annotationsChanged);
}
- get scrollableParentView(): ScrollableParentView {
- return ScrollableParentViews.ANNOTATIONS_LIST;
- }
-
private async _setPermissions() {
const dossier = await this._state.dossier;
this.annotationPermissions = AnnotationPermissions.forUser(
diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/document-info/document-info.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/document-info/document-info.component.ts
index 323443061..2e4dc612d 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/document-info/document-info.component.ts
+++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/document-info/document-info.component.ts
@@ -3,7 +3,7 @@ import { DossiersDialogService } from '../../../../services/dossiers-dialog.serv
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DocumentInfoService } from '../../services/document-info.service';
import { combineLatest, Observable, switchMap } from 'rxjs';
-import { PermissionsService } from '../../../../../../services/permissions.service';
+import { PermissionsService } from '@services/permissions.service';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { map } from 'rxjs/operators';
import { File } from '@red/domain';
diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.html b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.html
index ba8892922..cbfbcaa6b 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.html
+++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.html
@@ -40,7 +40,7 @@
-
+
diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/page-exclusion/page-exclusion.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/page-exclusion/page-exclusion.component.ts
index ef40421ca..c0d1a0e23 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/page-exclusion/page-exclusion.component.ts
+++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/page-exclusion/page-exclusion.component.ts
@@ -67,7 +67,7 @@ export class PageExclusionComponent {
endPage,
};
});
- const excludePages$ = this._reanalysisService.excludePages({ pageRanges }, this._state.dossierId, this._state.fileId);
+ const excludePages$ = this._reanalysisService.excludePages({ pageRanges }, file.dossierId, file);
await firstValueFrom(excludePages$);
this._inputComponent.reset();
} catch (e) {
@@ -78,12 +78,13 @@ export class PageExclusionComponent {
async includePagesRange(range: IPageRange): Promise
{
this._loadingService.start();
+ const file = await this._state.file;
const includePages$ = this._reanalysisService.includePages(
{
pageRanges: [range],
},
- this._state.dossierId,
- this._state.fileId,
+ file.dossierId,
+ file,
);
await firstValueFrom(includePages$);
this._inputComponent.reset();
diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/user-management/user-management.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/user-management/user-management.component.ts
index 075571ce1..7da635d51 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/user-management/user-management.component.ts
+++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/user-management/user-management.component.ts
@@ -3,7 +3,7 @@ import { Dossier, File, StatusBarConfigs, User } from '@red/domain';
import { List, LoadingService, Toaster } from '@iqser/common-ui';
import { PermissionsService } from '@services/permissions.service';
import { FileAssignService } from '../../../../shared/services/file-assign.service';
-import { workflowFileStatusTranslations } from '../../../../translations/file-status-translations';
+import { workflowFileStatusTranslations } from '../../../../../../translations/file-status-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { UserService } from '@services/user.service';
import { FilesService } from '@services/entity-services/files.service';
@@ -11,7 +11,7 @@ import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject, combineLatest, firstValueFrom, Observable, switchMap } from 'rxjs';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { distinctUntilChanged, map } from 'rxjs/operators';
-import { DossiersService } from '@services/entity-services/dossiers.service';
+import { ActiveDossiersService } from '../../../../../../services/dossiers/active-dossiers.service';
@Component({
selector: 'redaction-user-management',
@@ -42,9 +42,9 @@ export class UserManagementComponent {
readonly loadingService: LoadingService,
readonly translateService: TranslateService,
readonly stateService: FilePreviewStateService,
- private readonly _dossiersService: DossiersService,
+ private readonly _activeDossiersService: ActiveDossiersService,
) {
- this._dossier$ = this.stateService.file$.pipe(switchMap(file => this._dossiersService.getEntityChanged$(file.dossierId)));
+ this._dossier$ = this.stateService.file$.pipe(switchMap(file => this._activeDossiersService.getEntityChanged$(file.dossierId)));
this.statusBarConfig$ = this.stateService.file$.pipe(map(file => [{ length: 1, color: file.workflowStatus }]));
this.assignTooltip$ = this.stateService.file$.pipe(
map(file =>
@@ -96,15 +96,15 @@ export class UserManagementComponent {
const assigneeId = typeof user === 'string' ? user : user?.id;
const reviewerName = this.userService.getNameForId(assigneeId);
- const { dossierId, fileId, filename } = file;
+ const { dossierId, filename } = file;
this.loadingService.start();
if (!assigneeId) {
- await firstValueFrom(this.filesService.setUnassigned([fileId], dossierId));
+ await firstValueFrom(this.filesService.setUnassigned([file], dossierId));
} else if (file.isNew || file.isUnderReview) {
- await firstValueFrom(this.filesService.setReviewerFor([fileId], dossierId, assigneeId));
+ await firstValueFrom(this.filesService.setReviewerFor([file], dossierId, assigneeId));
} else if (file.isUnderApproval) {
- await firstValueFrom(this.filesService.setUnderApprovalFor([fileId], dossierId, assigneeId));
+ await firstValueFrom(this.filesService.setUnderApprovalFor([file], dossierId, assigneeId));
}
this.loadingService.stop();
diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/dialogs/accept-recommendation-dialog/accept-recommendation-dialog.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/dialogs/accept-recommendation-dialog/accept-recommendation-dialog.component.ts
index 7b513bd5b..23486c7f9 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/dialogs/accept-recommendation-dialog/accept-recommendation-dialog.component.ts
+++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/dialogs/accept-recommendation-dialog/accept-recommendation-dialog.component.ts
@@ -3,7 +3,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { PermissionsService } from '@services/permissions.service';
import { Dictionary, Dossier } from '@red/domain';
-import { DossiersService } from '@services/entity-services/dossiers.service';
+import { ActiveDossiersService } from '../../../../../../services/dossiers/active-dossiers.service';
import { BaseDialogComponent } from '@iqser/common-ui';
import { DictionaryService } from '@shared/services/dictionary.service';
import { ManualAnnotationService } from '../../../../services/manual-annotation.service';
@@ -33,14 +33,14 @@ export class AcceptRecommendationDialogComponent extends BaseDialogComponent imp
private readonly _formBuilder: FormBuilder,
private readonly _manualAnnotationService: ManualAnnotationService,
private readonly _permissionsService: PermissionsService,
- private readonly _dossiersService: DossiersService,
+ private readonly _activeDossiersService: ActiveDossiersService,
private readonly _dictionaryService: DictionaryService,
protected readonly _injector: Injector,
protected readonly _dialogRef: MatDialogRef,
@Inject(MAT_DIALOG_DATA) readonly data: AcceptRecommendationData,
) {
super(_injector, _dialogRef);
- this._dossier = this._dossiersService.find(this.data.dossierId);
+ this._dossier = this._activeDossiersService.find(this.data.dossierId);
this.isDocumentAdmin = this._permissionsService.isApprover(this._dossier);
this.form = this._getForm();
this.initialFormValue = this.form.getRawValue();
diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-providers.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-providers.ts
index 1ce08b6a8..4cb277023 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-providers.ts
+++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-providers.ts
@@ -10,6 +10,9 @@ import { FilePreviewStateService } from './services/file-preview-state.service';
import { PdfViewerDataService } from '../../services/pdf-viewer-data.service';
import { AnnotationReferencesService } from './services/annotation-references.service';
import { FilterService } from '@iqser/common-ui';
+import { ManualAnnotationService } from '../../services/manual-annotation.service';
+import { AnnotationProcessingService } from '../../services/annotation-processing.service';
+import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider';
import { PageRotationService } from './services/page-rotation.service';
import { PdfViewer } from './services/pdf-viewer.service';
@@ -28,4 +31,7 @@ export const filePreviewScreenProviders = [
AnnotationReferencesService,
PageRotationService,
PdfViewer,
+ ManualAnnotationService,
+ AnnotationProcessingService,
+ dossiersServiceProvider,
];
diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts
index 70d08c0ea..6af1c52dc 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts
+++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts
@@ -34,7 +34,6 @@ import { clearStamps, stampPDFPage } from '@utils/page-stamper';
import { TranslateService } from '@ngx-translate/core';
import { handleFilterDelta } from '@utils/filter-utils';
import { FilesService } from '@services/entity-services/files.service';
-import { DossiersService } from '@services/entity-services/dossiers.service';
import { FileManagementService } from '@services/entity-services/file-management.service';
import { catchError, map, switchMap, tap } from 'rxjs/operators';
import { FilesMapService } from '@services/entity-services/files-map.service';
@@ -50,6 +49,7 @@ import { FilePreviewStateService } from './services/file-preview-state.service';
import { FileDataModel } from '../../../../models/file/file-data.model';
import { filePreviewScreenProviders } from './file-preview-providers';
import { ManualAnnotationService } from '../../services/manual-annotation.service';
+import { DossiersService } from '../../../../services/dossiers/dossiers.service';
import { PageRotationService } from './services/page-rotation.service';
import { ComponentCanDeactivate } from '../../../../guards/can-deactivate.guard';
import Annotation = Core.Annotations.Annotation;
@@ -254,7 +254,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
const file = await this.stateService.file;
if (file?.analysisRequired && !file.excludedFromAutomaticAnalysis) {
- const reanalyzeFiles = this._reanalysisService.reanalyzeFilesForDossier([this.fileId], this.dossierId, { force: true });
+ const reanalyzeFiles = this._reanalysisService.reanalyzeFilesForDossier([file], this.dossierId, { force: true });
await firstValueFrom(reanalyzeFiles);
}
@@ -449,7 +449,8 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
async annotationsChangedByReviewAction(annotation?: AnnotationWrapper) {
this.multiSelectService.deactivate();
- const fileReloaded = await firstValueFrom(this._filesService.reload(this.dossierId, this.fileId));
+ const file = await this.stateService.file;
+ const fileReloaded = await firstValueFrom(this._filesService.reload(this.dossierId, file));
if (!fileReloaded) {
await this._reloadAnnotationsForPage(annotation?.pageNumber ?? this.activeViewerPage);
}
@@ -574,7 +575,10 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
private _subscribeToFileUpdates(): void {
this.addActiveScreenSubscription = timer(0, 5000)
- .pipe(switchMap(() => this._filesService.reload(this.dossierId, this.fileId)))
+ .pipe(
+ switchMap(() => this.stateService.file$),
+ switchMap(file => this._filesService.reload(this.dossierId, file)),
+ )
.subscribe();
this.addActiveScreenSubscription = this._dossiersService
diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/annotation-actions.service.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/annotation-actions.service.ts
index 29b31a0e8..9678c87ae 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/annotation-actions.service.ts
+++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/annotation-actions.service.ts
@@ -14,7 +14,7 @@ import { Dossier, IAddRedactionRequest, ILegalBasisChangeRequest, IRectangle, IR
import { toPosition } from '../../../utils/pdf-calculation.utils';
import { AnnotationDrawService } from './annotation-draw.service';
import { translateQuads } from '@utils/pdf-coordinates';
-import { DossiersService } from '@services/entity-services/dossiers.service';
+import { ActiveDossiersService } from '../../../../../services/dossiers/active-dossiers.service';
import {
AcceptRecommendationData,
AcceptRecommendationDialogComponent,
@@ -38,12 +38,12 @@ export class AnnotationActionsService {
private readonly _dialogService: DossiersDialogService,
private readonly _dialog: MatDialog,
private readonly _annotationDrawService: AnnotationDrawService,
- private readonly _dossiersService: DossiersService,
+ private readonly _activeDossiersService: ActiveDossiersService,
private readonly _screenStateService: FilePreviewStateService,
) {}
private get _dossier(): Dossier {
- return this._dossiersService.find(this._screenStateService.dossierId);
+ return this._activeDossiersService.find(this._screenStateService.dossierId);
}
acceptSuggestion($event: MouseEvent, annotations: AnnotationWrapper[], annotationsChanged: EventEmitter) {
diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/annotation-draw.service.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/annotation-draw.service.ts
index 5f3a32f1f..9fb5591fa 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/annotation-draw.service.ts
+++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/annotation-draw.service.ts
@@ -3,7 +3,6 @@ import { Core, WebViewerInstance } from '@pdftron/webviewer';
import { hexToRgb } from '@utils/functions';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { UserPreferenceService } from '@services/user-preference.service';
-import { DossiersService } from '@services/entity-services/dossiers.service';
import { RedactionLogService } from '../../../services/redaction-log.service';
import { environment } from '@environments/environment';
@@ -11,6 +10,7 @@ import { IRectangle, ISectionGrid, ISectionRectangle } from '@red/domain';
import { SkippedService } from './skipped.service';
import { firstValueFrom } from 'rxjs';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
+import { DossiersService } from '../../../../../services/dossiers/dossiers.service';
import Annotation = Core.Annotations.Annotation;
@Injectable()
diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/file-preview-state.service.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/file-preview-state.service.ts
index 5c43e1fc6..ea78bb349 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/file-preview-state.service.ts
+++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/file-preview-state.service.ts
@@ -1,15 +1,16 @@
-import { Injectable } from '@angular/core';
+import { Injectable, Injector } from '@angular/core';
import { BehaviorSubject, firstValueFrom, from, Observable, pairwise, switchMap } from 'rxjs';
import { FileDataModel } from '@models/file/file-data.model';
import { Dossier, File } from '@red/domain';
-import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActivatedRoute } from '@angular/router';
import { FilesMapService } from '@services/entity-services/files-map.service';
-import { PermissionsService } from '../../../../../services/permissions.service';
+import { PermissionsService } from '@services/permissions.service';
import { boolFactory } from '@iqser/common-ui';
import { filter, startWith } from 'rxjs/operators';
import { FileManagementService } from '@services/entity-services/file-management.service';
import { DOSSIER_ID, FILE_ID } from '@utils/constants';
+import { DossiersService } from '../../../../../services/dossiers/dossiers.service';
+import { dossiersServiceResolver } from '@services/entity-services/dossiers.service.provider';
import { wipeFilesCache } from '../../../../../../../../../libs/red-cache/src';
@Injectable()
@@ -28,19 +29,19 @@ export class FilePreviewStateService {
readonly #fileData$ = new BehaviorSubject(undefined);
constructor(
- dossiersService: DossiersService,
- filesMapService: FilesMapService,
- permissionsService: PermissionsService,
- activatedRoute: ActivatedRoute,
private readonly _fileManagementService: FileManagementService,
+ private readonly _injector: Injector,
+ private readonly _route: ActivatedRoute,
+ private readonly _filesMapService: FilesMapService,
+ private readonly _permissionsService: PermissionsService,
) {
- this.fileId = activatedRoute.snapshot.paramMap.get(FILE_ID);
- this.dossierId = activatedRoute.snapshot.paramMap.get(DOSSIER_ID);
- this.dossierTemplateId = dossiersService.find(this.dossierId).dossierTemplateId;
+ this.fileId = _route.snapshot.paramMap.get(FILE_ID);
+ this.dossierId = _route.snapshot.paramMap.get(DOSSIER_ID);
+ this.dossierTemplateId = this._dossiersService.find(this.dossierId).dossierTemplateId;
- this.dossier$ = dossiersService.getEntityChanged$(this.dossierId);
- this.file$ = filesMapService.watch$(this.dossierId, this.fileId);
- [this.isReadonly$, this.isWritable$] = boolFactory(this.file$, file => !permissionsService.canPerformAnnotationActions(file));
+ this.dossier$ = this._dossiersService.getEntityChanged$(this.dossierId);
+ this.file$ = _filesMapService.watch$(this.dossierId, this.fileId);
+ [this.isReadonly$, this.isWritable$] = boolFactory(this.file$, file => !_permissionsService.canPerformAnnotationActions(file));
this.fileData$ = this.#fileData$.asObservable().pipe(filter(value => !!value));
this.blob$ = this.#blob$;
@@ -66,6 +67,10 @@ export class FilePreviewStateService {
return firstValueFrom(this.blob$);
}
+ private get _dossiersService(): DossiersService {
+ return dossiersServiceResolver(this._injector);
+ }
+
get #blob$() {
return this.file$.pipe(
startWith(undefined),
diff --git a/apps/red-ui/src/app/modules/dossier/services/manual-annotation.service.ts b/apps/red-ui/src/app/modules/dossier/services/manual-annotation.service.ts
index fbf1e8c9b..6c14dc6cd 100644
--- a/apps/red-ui/src/app/modules/dossier/services/manual-annotation.service.ts
+++ b/apps/red-ui/src/app/modules/dossier/services/manual-annotation.service.ts
@@ -17,7 +17,7 @@ import { AnnotationActionMode } from '../models/annotation-action-mode.model';
import { annotationActionsTranslations } from '../translations/annotation-actions-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
-import { DossiersService } from '@services/entity-services/dossiers.service';
+import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { Observable } from 'rxjs';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
import { ManualRedactionEntryType } from '@models/file/manual-redaction-entry.wrapper';
@@ -32,7 +32,7 @@ export class ManualAnnotationService extends GenericService
private readonly _dictionariesMapService: DictionariesMapService,
private readonly _toaster: Toaster,
private readonly _permissionsService: PermissionsService,
- private readonly _dossiersService: DossiersService,
+ private readonly _activeDossiersService: ActiveDossiersService,
protected readonly _injector: Injector,
) {
super(_injector, 'manualRedaction');
@@ -372,7 +372,7 @@ export class ManualAnnotationService extends GenericService
}
private _dossier(dossierId: string): Dossier {
- return this._dossiersService.find(dossierId);
+ return this._activeDossiersService.find(dossierId);
}
private _getMessage(mode: AnnotationActionMode, modifyDictionary?: boolean, error = false, isConflict = false) {
diff --git a/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts
index e32acc196..a59492f02 100644
--- a/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts
+++ b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts
@@ -28,7 +28,7 @@ import { UserService } from '@services/user.service';
import { UserPreferenceService } from '@services/user-preference.service';
import { LongPressEvent } from '@shared/directives/long-press.directive';
import { FileAssignService } from '../../services/file-assign.service';
-import { DossiersService } from '@services/entity-services/dossiers.service';
+import { ActiveDossiersService } from '../../../../../services/dossiers/active-dossiers.service';
import { FileManagementService } from '@services/entity-services/file-management.service';
import { FilesService } from '@services/entity-services/files.service';
import { ReanalysisService, ReanalyzeQueryParams } from '@services/reanalysis.service';
@@ -75,6 +75,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
showUnderApproval = false;
showApprove = false;
canToggleAnalysis = false;
+ showToggleAnalysis = false;
showStatusBar = false;
showOpenDocument = false;
showReanalyseFilePreview = false;
@@ -95,7 +96,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
@Optional() private readonly _documentInfoService: DocumentInfoService,
@Optional() private readonly _pageRotationService: PageRotationService,
private readonly _permissionsService: PermissionsService,
- private readonly _dossiersService: DossiersService,
+ private readonly _activeDossiersService: ActiveDossiersService,
private readonly _dialogService: DossiersDialogService,
private readonly _fileAssignService: FileAssignService,
private readonly _loadingService: LoadingService,
@@ -129,28 +130,28 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
return [
{
type: ActionTypes.circleBtn,
- action: $event => this._openDeleteFileDialog($event),
+ action: ($event: MouseEvent) => this._openDeleteFileDialog($event),
tooltip: _('dossier-overview.delete.action'),
icon: 'iqser:trash',
show: this.showDelete,
},
{
type: ActionTypes.circleBtn,
- action: $event => this._assign($event),
+ action: ($event: MouseEvent) => this._assign($event),
tooltip: this.assignTooltip,
icon: 'red:assign',
show: this.showAssign,
},
{
type: ActionTypes.circleBtn,
- action: $event => this._assignToMe($event),
+ action: ($event: MouseEvent) => this._assignToMe($event),
tooltip: _('dossier-overview.assign-me'),
icon: 'red:assign-me',
show: this.showAssignToSelf,
},
{
type: ActionTypes.circleBtn,
- action: $event => this._triggerImportRedactions($event),
+ action: ($event: MouseEvent) => this._triggerImportRedactions($event),
tooltip: _('dossier-overview.import-redactions'),
icon: 'red:import_redactions',
show: this.showImportRedactions,
@@ -180,21 +181,21 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
},
{
type: ActionTypes.circleBtn,
- action: $event => this._setFileUnderApproval($event),
+ action: ($event: MouseEvent) => this._setFileUnderApproval($event),
tooltip: _('dossier-overview.under-approval'),
icon: 'red:ready-for-approval',
show: this.showUnderApproval,
},
{
type: ActionTypes.circleBtn,
- action: $event => this._setFileUnderReview($event),
+ action: ($event: MouseEvent) => this._setFileUnderReview($event),
tooltip: _('dossier-overview.under-review'),
icon: 'red:undo',
show: this.showUnderReview,
},
{
type: ActionTypes.circleBtn,
- action: $event => this.setFileApproved($event),
+ action: ($event: MouseEvent) => this.setFileApproved($event),
tooltip: this.file.canBeApproved ? _('dossier-overview.approve') : _('dossier-overview.approve-disabled'),
icon: 'red:approved',
disabled: !this.file.canBeApproved,
@@ -202,14 +203,14 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
},
{
type: ActionTypes.circleBtn,
- action: $event => this._toggleAutomaticAnalysis($event),
+ action: ($event: MouseEvent) => this._toggleAutomaticAnalysis($event),
tooltip: _('dossier-overview.stop-auto-analysis'),
icon: 'red:disable-analysis',
show: this.canDisableAutoAnalysis,
},
{
type: ActionTypes.circleBtn,
- action: $event => this._reanalyseFile($event),
+ action: ($event: MouseEvent) => this._reanalyseFile($event),
tooltip: _('file-preview.reanalyse-notification'),
tooltipClass: 'warn small',
icon: 'iqser:refresh',
@@ -217,7 +218,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
},
{
type: ActionTypes.circleBtn,
- action: $event => this._toggleAutomaticAnalysis($event),
+ action: ($event: MouseEvent) => this._toggleAutomaticAnalysis($event),
tooltip: _('dossier-overview.start-auto-analysis'),
buttonType: this.isFilePreview ? CircleButtonTypes.warn : CircleButtonTypes.default,
icon: 'red:enable-analysis',
@@ -225,21 +226,21 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
},
{
type: ActionTypes.circleBtn,
- action: $event => this._setFileUnderApproval($event),
+ action: ($event: MouseEvent) => this._setFileUnderApproval($event),
tooltip: _('dossier-overview.under-approval'),
icon: 'red:undo',
show: this.showUndoApproval,
},
{
type: ActionTypes.circleBtn,
- action: $event => this._ocrFile($event),
+ action: ($event: MouseEvent) => this._ocrFile($event),
tooltip: _('dossier-overview.ocr-file'),
icon: 'iqser:ocr',
show: this.showOCR,
},
{
type: ActionTypes.circleBtn,
- action: $event => this._reanalyseFile($event),
+ action: ($event: MouseEvent) => this._reanalyseFile($event),
tooltip: _('dossier-overview.reanalyse.action'),
icon: 'iqser:refresh',
show: this.showReanalyseDossierOverview,
@@ -251,13 +252,13 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
tooltip: this.toggleTooltip,
class: { 'mr-24': this.isDossierOverviewList },
checked: !this.file.excluded,
- show: true,
+ show: this.showToggleAnalysis,
},
].filter(btn => btn.show);
}
ngOnInit() {
- this.addSubscription = this._dossiersService
+ this.addSubscription = this._activeDossiersService
.getEntityChanged$(this.file.dossierId)
.pipe(tap(() => this._setup()))
.subscribe();
@@ -332,8 +333,8 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
async () => {
this._loadingService.start();
try {
- const dossier = this._dossiersService.find(this.file.dossierId);
- await firstValueFrom(this._fileManagementService.delete([this.file.fileId], this.file.dossierId));
+ const dossier = this._activeDossiersService.find(this.file.dossierId);
+ await firstValueFrom(this._fileManagementService.delete([this.file], this.file.dossierId));
await this._router.navigate([dossier.routerLink]);
} catch (error) {
this._toaster.error(_('error.http.generic'), { params: error });
@@ -362,7 +363,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
force: true,
triggeredByUser: true,
};
- await firstValueFrom(this._reanalysisService.reanalyzeFilesForDossier([this.file.fileId], this.file.dossierId, params));
+ await firstValueFrom(this._reanalysisService.reanalyzeFilesForDossier([this.file], this.file.dossierId, params));
}
private async _toggleAutomaticAnalysis($event: MouseEvent) {
@@ -383,7 +384,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
await firstValueFrom(this._pageRotationService.showConfirmationDialogIfHasRotations());
}
this._loadingService.start();
- await firstValueFrom(this._reanalysisService.ocrFiles([this.file.fileId], this.file.dossierId));
+ await firstValueFrom(this._reanalysisService.ocrFiles([this.file], this.file.dossierId));
this._loadingService.stop();
}
@@ -393,7 +394,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
private async _toggleAnalysis() {
this._loadingService.start();
- await firstValueFrom(this._reanalysisService.toggleAnalysis(this.file.dossierId, [this.file.fileId], !this.file.excluded));
+ await firstValueFrom(this._reanalysisService.toggleAnalysis(this.file.dossierId, [this.file], !this.file.excluded));
this._loadingService.stop();
}
@@ -415,8 +416,9 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
this.showApprove = this._permissionsService.isReadyForApproval(this.file) && !this.isDossierOverviewWorkflow;
this.canToggleAnalysis = this._permissionsService.canToggleAnalysis(this.file);
+ this.showToggleAnalysis = this._permissionsService.showToggleAnalysis(this.file);
this.showDelete = this._permissionsService.canDeleteFile(this.file);
- this.showOCR = this.file.canBeOCRed;
+ this.showOCR = this._permissionsService.canOcrFile(this.file);
this.canReanalyse = this._permissionsService.canReanalyseFile(this.file);
this.canDisableAutoAnalysis = this._permissionsService.canDisableAutoAnalysis([this.file]);
this.canEnableAutoAnalysis = this._permissionsService.canEnableAutoAnalysis([this.file]);
@@ -442,7 +444,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
private async _setFileApproved() {
this._loadingService.start();
- await firstValueFrom(this._filesService.setApprovedFor([this.file.id], this.file.dossierId));
+ await firstValueFrom(this._filesService.setApprovedFor([this.file], this.file.dossierId));
this._loadingService.stop();
}
}
diff --git a/apps/red-ui/src/app/modules/dossier/shared/services/file-assign.service.ts b/apps/red-ui/src/app/modules/dossier/shared/services/file-assign.service.ts
index 0694e0588..49c61d10e 100644
--- a/apps/red-ui/src/app/modules/dossier/shared/services/file-assign.service.ts
+++ b/apps/red-ui/src/app/modules/dossier/shared/services/file-assign.service.ts
@@ -5,7 +5,7 @@ import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { FilesService } from '@services/entity-services/files.service';
import { ConfirmationDialogInput, LoadingService, Toaster } from '@iqser/common-ui';
-import { DossiersService } from '@services/entity-services/dossiers.service';
+import { ActiveDossiersService } from '../../../../services/dossiers/active-dossiers.service';
import { firstValueFrom, Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@@ -15,7 +15,7 @@ export class FileAssignService {
private readonly _dialogService: DossiersDialogService,
private readonly _userService: UserService,
private readonly _filesService: FilesService,
- private readonly _dossiersService: DossiersService,
+ private readonly _activeDossiersService: ActiveDossiersService,
private readonly _loadingService: LoadingService,
private readonly _toaster: Toaster,
) {}
@@ -53,7 +53,7 @@ export class FileAssignService {
$event?.stopPropagation();
const currentUserId = this._userService.currentUser.id;
- const currentDossier = this._dossiersService.find(file.dossierId);
+ const currentDossier = this._activeDossiersService.find(file.dossierId);
const eligibleUsersIds = this._getUserIds(mode, currentDossier);
if (file.isNew) {
@@ -81,28 +81,11 @@ export class FileAssignService {
this._loadingService.start();
try {
if (!userId) {
- await firstValueFrom(
- this._filesService.setUnassigned(
- files.map(f => f.fileId),
- files[0].dossierId,
- ),
- );
+ await firstValueFrom(this._filesService.setUnassigned(files, files[0].dossierId));
} else if (mode === 'reviewer') {
- await firstValueFrom(
- this._filesService.setReviewerFor(
- files.map(f => f.fileId),
- files[0].dossierId,
- userId,
- ),
- );
+ await firstValueFrom(this._filesService.setReviewerFor(files, files[0].dossierId, userId));
} else {
- await firstValueFrom(
- this._filesService.setUnderApprovalFor(
- files.map(f => f.fileId),
- files[0].dossierId,
- userId,
- ),
- );
+ await firstValueFrom(this._filesService.setUnderApprovalFor(files, files[0].dossierId, userId));
}
} catch (error) {
this._toaster.error(_('error.http.generic'), { params: error });
@@ -117,11 +100,7 @@ export class FileAssignService {
private _assignReviewerToCurrentUser(files: File[]): Observable {
this._loadingService.start();
return this._filesService
- .setReviewerFor(
- files.map(f => f.fileId),
- files[0].dossierId,
- this._userService.currentUser.id,
- )
+ .setReviewerFor(files, files[0].dossierId, this._userService.currentUser.id)
.pipe(tap(() => this._loadingService.stop()));
}
}
diff --git a/apps/red-ui/src/app/modules/dossier/shared/shared-dossiers.module.ts b/apps/red-ui/src/app/modules/dossier/shared/shared-dossiers.module.ts
index b7cded317..4a59416c5 100644
--- a/apps/red-ui/src/app/modules/dossier/shared/shared-dossiers.module.ts
+++ b/apps/red-ui/src/app/modules/dossier/shared/shared-dossiers.module.ts
@@ -2,17 +2,40 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FileAssignService } from './services/file-assign.service';
import { FileActionsComponent } from './components/file-actions/file-actions.component';
-import { IqserIconsModule } from '@iqser/common-ui';
import { SharedModule } from '@shared/shared.module';
import { RedactionImportService } from './services/redaction-import.service';
+import { DossiersDialogService } from '../services/dossiers-dialog.service';
+import { DocumentInfoDialogComponent } from '../dialogs/document-info-dialog/document-info-dialog.component';
+import { EditDossierDialogComponent } from '../dialogs/edit-dossier-dialog/edit-dossier-dialog.component';
+import { AddDossierDialogComponent } from '../dialogs/add-dossier-dialog/add-dossier-dialog.component';
+import { AssignReviewerApproverDialogComponent } from '../dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component';
+import { EditDossierGeneralInfoComponent } from '../dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component';
+import { EditDossierDownloadPackageComponent } from '../dialogs/edit-dossier-dialog/download-package/edit-dossier-download-package.component';
+import { EditDossierDictionaryComponent } from '../dialogs/edit-dossier-dialog/dictionary/edit-dossier-dictionary.component';
+import { EditDossierAttributesComponent } from '../dialogs/edit-dossier-dialog/attributes/edit-dossier-attributes.component';
+import { EditDossierTeamComponent } from '../dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component';
+import { EditDossierDeletedDocumentsComponent } from '../dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component';
import { DateColumnComponent } from './components/date-column/date-column.component';
-const components = [FileActionsComponent, DateColumnComponent];
+const components = [
+ FileActionsComponent,
+ DocumentInfoDialogComponent,
+ EditDossierGeneralInfoComponent,
+ EditDossierDownloadPackageComponent,
+ EditDossierDictionaryComponent,
+ EditDossierAttributesComponent,
+ EditDossierTeamComponent,
+ EditDossierDeletedDocumentsComponent,
+ FileActionsComponent,
+ DateColumnComponent,
+];
+const dialogs = [EditDossierDialogComponent, AddDossierDialogComponent, AssignReviewerApproverDialogComponent];
+const services = [DossiersDialogService, FileAssignService, RedactionImportService];
@NgModule({
- declarations: [...components],
- exports: [...components],
- providers: [FileAssignService, RedactionImportService],
- imports: [CommonModule, IqserIconsModule, SharedModule],
+ declarations: [...components, ...dialogs],
+ exports: [...components, ...dialogs],
+ providers: [...services],
+ imports: [CommonModule, SharedModule],
})
export class SharedDossiersModule {}
diff --git a/apps/red-ui/src/app/modules/icons/icons.module.ts b/apps/red-ui/src/app/modules/icons/icons.module.ts
index 3513cf42d..bb2e7eff9 100644
--- a/apps/red-ui/src/app/modules/icons/icons.module.ts
+++ b/apps/red-ui/src/app/modules/icons/icons.module.ts
@@ -13,6 +13,7 @@ export class IconsModule {
const icons = [
'ai',
'approved',
+ 'archive',
'arrow-up',
'assign',
'assign-me',
diff --git a/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.html b/apps/red-ui/src/app/modules/search/search-screen/search-screen.component.html
similarity index 93%
rename from apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.html
rename to apps/red-ui/src/app/modules/search/search-screen/search-screen.component.html
index 5c44f4aca..b63970902 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.html
+++ b/apps/red-ui/src/app/modules/search/search-screen/search-screen.component.html
@@ -74,8 +74,11 @@
>
- {{ item.dossierName }}
+
+
+
+ {{ item.dossierName }}
+
diff --git a/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.scss b/apps/red-ui/src/app/modules/search/search-screen/search-screen.component.scss
similarity index 100%
rename from apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.scss
rename to apps/red-ui/src/app/modules/search/search-screen/search-screen.component.scss
diff --git a/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.ts b/apps/red-ui/src/app/modules/search/search-screen/search-screen.component.ts
similarity index 68%
rename from apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.ts
rename to apps/red-ui/src/app/modules/search/search-screen/search-screen.component.ts
index 71a3b1244..0fae5de35 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.ts
+++ b/apps/red-ui/src/app/modules/search/search-screen/search-screen.component.ts
@@ -14,13 +14,14 @@ import { combineLatest, Observable } from 'rxjs';
import { debounceTime, map, startWith, switchMap, tap } from 'rxjs/operators';
import { ActivatedRoute, Router } from '@angular/router';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
-import { workflowFileStatusTranslations } from '../../translations/file-status-translations';
+import { workflowFileStatusTranslations } from '../../../translations/file-status-translations';
import { TranslateService } from '@ngx-translate/core';
import { RouterHistoryService } from '@services/router-history.service';
-import { DossiersService } from '@services/entity-services/dossiers.service';
-import { Dossier, IMatchedDocument, ISearchListItem, ISearchResponse } from '@red/domain';
+import { Dossier, DossierStatuses, IMatchedDocument, ISearchListItem, ISearchResponse } from '@red/domain';
import { FilesMapService } from '@services/entity-services/files-map.service';
import { PlatformSearchService } from '@services/entity-services/platform-search.service';
+import { ActiveDossiersService } from '../../../services/dossiers/active-dossiers.service';
+import { ArchivedDossiersService } from '../../../services/dossiers/archived-dossiers.service';
@Component({
templateUrl: './search-screen.component.html',
@@ -43,8 +44,14 @@ export class SearchScreenComponent extends ListingComponent imp
readonly searchResults$ = combineLatest([this._queryChanged, this._filtersChanged$]).pipe(
tap(() => this._loadingService.start()),
- tap(([query, dossierIds]) => this._updateNavigation(query, dossierIds)),
- switchMap(([query, dossierIds]) => this._platformSearchService.search({ query, dossierIds })),
+ tap(([query, [dossierIds, onlyActive]]) => this._updateNavigation(query, dossierIds, onlyActive)),
+ switchMap(([query, [dossierIds, onlyActive]]) =>
+ this._platformSearchService.search({
+ query,
+ dossierIds,
+ dossierStatus: onlyActive ? [] : [DossierStatuses.ACTIVE, DossierStatuses.ARCHIVED],
+ }),
+ ),
map(searchResult => this._toMatchedDocuments(searchResult)),
map(docs => this._toListItems(docs)),
tap(result => this.entitiesService.setEntities(result)),
@@ -56,7 +63,8 @@ export class SearchScreenComponent extends ListingComponent imp
protected readonly _injector: Injector,
private readonly _activatedRoute: ActivatedRoute,
private readonly _loadingService: LoadingService,
- private readonly _dossiersService: DossiersService,
+ private readonly _activeDossiersService: ActiveDossiersService,
+ private readonly _archivedDossiersService: ArchivedDossiersService,
readonly routerHistoryService: RouterHistoryService,
private readonly _translateService: TranslateService,
private readonly _filesMapService: FilesMapService,
@@ -71,21 +79,28 @@ export class SearchScreenComponent extends ListingComponent imp
const checked = dossierIds.includes(id);
return new NestedFilter({ id, label: dossierName, checked });
};
+ const allDossiers = [...this._activeDossiersService.all, ...this._archivedDossiersService.all];
const dossierNameFilter: IFilterGroup = {
slug: 'dossiers',
label: this._translateService.instant('search-screen.filters.by-dossier'),
filterceptionPlaceholder: this._translateService.instant('search-screen.filters.search-placeholder'),
icon: 'red:folder',
- filters: this._dossiersService.all.map(dossierToFilter),
+ filters: allDossiers.map(dossierToFilter),
checker: keyChecker('dossierId'),
};
this.filterService.addFilterGroups([dossierNameFilter]);
+ const onlyActiveLabel = this._translateService.instant('search-screen.filters.only-active');
+ this.filterService.addSingleFilter({ id: 'onlyActiveDossiers', label: onlyActiveLabel, checked: this._routeOnlyActive });
}
private get _routeDossierIds(): string[] {
return this._activatedRoute.snapshot.queryParamMap.get('dossierIds').split(',');
}
+ private get _routeOnlyActive(): boolean {
+ return this._activatedRoute.snapshot.queryParamMap.get('onlyActive') === 'true';
+ }
+
private get _routeQuery(): string {
return this._activatedRoute.snapshot.queryParamMap.get('query');
}
@@ -98,10 +113,15 @@ export class SearchScreenComponent extends ListingComponent imp
);
}
- private get _filtersChanged$(): Observable {
- return this.filterService.filterGroups$.pipe(
- map(groups => groups[0].filters.filter(v => v.checked).map(v => v.id)),
- startWith(this._routeDossierIds),
+ private get _filtersChanged$(): Observable<[string[], boolean]> {
+ const onlyActiveDossiers$ = this.filterService.getSingleFilter('onlyActiveDossiers').pipe(map(f => !!f.checked));
+ const filterGroups$ = this.filterService.filterGroups$;
+ return combineLatest([filterGroups$, onlyActiveDossiers$]).pipe(
+ map(([groups, onlyActive]) => {
+ const dossierIds: string[] = groups[0].filters.filter(v => v.checked).map(v => v.id);
+ return [dossierIds, onlyActive];
+ }),
+ startWith<[string[], boolean]>([this._routeDossierIds, this._routeOnlyActive]),
);
}
@@ -110,8 +130,8 @@ export class SearchScreenComponent extends ListingComponent imp
this.searchService.searchValue = newQuery ?? '';
}
- private _updateNavigation(query?: string, dossierIds?: string[]): Promise {
- const queryParams = { query, dossierIds: dossierIds.join(',') };
+ private _updateNavigation(query?: string, dossierIds?: string[], onlyActive?: boolean): Promise {
+ const queryParams = { query, dossierIds: dossierIds.join(','), onlyActive };
return this._router.navigate([], { queryParams, replaceUrl: true });
}
@@ -123,24 +143,29 @@ export class SearchScreenComponent extends ListingComponent imp
return matchedDocuments.map(document => this._toListItem(document)).filter(value => value);
}
- private _toListItem({ dossierId, fileId, unmatchedTerms, highlights, score }: IMatchedDocument): ISearchListItem {
+ private _toListItem({ dossierId, fileId, unmatchedTerms, highlights, score, dossierStatus }: IMatchedDocument): ISearchListItem {
const file = this._filesMapService.get(dossierId, fileId);
if (!file) {
return undefined;
}
+ const dossier = (dossierStatus === DossierStatuses.ARCHIVED ? this._archivedDossiersService : this._activeDossiersService).find(
+ dossierId,
+ );
+
return {
id: fileId,
dossierId,
+ dossierStatus,
unmatched: unmatchedTerms || null,
highlights,
status: file.workflowStatus,
assignee: file.assignee,
numberOfPages: file.numberOfPages,
- dossierName: this._dossiersService.find(dossierId).dossierName,
+ dossierName: dossier.dossierName,
filename: file.filename,
searchKey: score.toString(),
- routerLink: `/main/dossiers/${dossierId}/file/${fileId}`,
+ routerLink: file.routerLink,
};
}
}
diff --git a/apps/red-ui/src/app/modules/search/search.module.ts b/apps/red-ui/src/app/modules/search/search.module.ts
new file mode 100644
index 000000000..7a79b48ab
--- /dev/null
+++ b/apps/red-ui/src/app/modules/search/search.module.ts
@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { SearchScreenComponent } from './search-screen/search-screen.component';
+import { RouterModule } from '@angular/router';
+import { CommonUiModule } from '@iqser/common-ui';
+import { SharedModule } from '../shared/shared.module';
+
+const routes = [{ path: '', component: SearchScreenComponent }];
+
+@NgModule({
+ declarations: [SearchScreenComponent],
+ imports: [CommonModule, RouterModule.forChild(routes), CommonUiModule, SharedModule],
+})
+export class SearchModule {}
diff --git a/apps/red-ui/src/app/modules/shared/components/buttons/file-download-btn/file-download-btn.component.ts b/apps/red-ui/src/app/modules/shared/components/buttons/file-download-btn/file-download-btn.component.ts
index 8009960da..0b595cdf7 100644
--- a/apps/red-ui/src/app/modules/shared/components/buttons/file-download-btn/file-download-btn.component.ts
+++ b/apps/red-ui/src/app/modules/shared/components/buttons/file-download-btn/file-download-btn.component.ts
@@ -38,7 +38,7 @@ export class FileDownloadBtnComponent implements OnChanges {
async downloadFiles($event: MouseEvent) {
$event.stopPropagation();
const dossierId = this.files[0].dossierId;
- const filesIds = this.files.map(f => f.fileId);
+ const filesIds = this.files.map(f => f.id);
await firstValueFrom(this._fileDownloadService.downloadFiles(filesIds, dossierId));
this._toaster.info(_('download-status.queued'));
}
diff --git a/apps/red-ui/src/app/modules/shared/components/dictionary-manager/dictionary-manager.component.html b/apps/red-ui/src/app/modules/shared/components/dictionary-manager/dictionary-manager.component.html
index 8b54d5c02..8050b4264 100644
--- a/apps/red-ui/src/app/modules/shared/components/dictionary-manager/dictionary-manager.component.html
+++ b/apps/red-ui/src/app/modules/shared/components/dictionary-manager/dictionary-manager.component.html
@@ -51,7 +51,7 @@
-
-
+
{{ dossierStats.numberOfFiles }}
diff --git a/apps/red-ui/src/app/modules/shared/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component.scss b/apps/red-ui/src/app/modules/shared/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component.ts b/apps/red-ui/src/app/modules/shared/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component.ts
similarity index 75%
rename from apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component.ts
rename to apps/red-ui/src/app/modules/shared/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component.ts
index 30a26f8b5..fab9ad4de 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component.ts
+++ b/apps/red-ui/src/app/modules/shared/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component.ts
@@ -1,8 +1,6 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { Dossier, DossierStats } from '@red/domain';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
-import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
-import { DossiersService } from '@services/entity-services/dossiers.service';
import * as moment from 'moment';
const DUE_DATE_WARN_DAYS = 14;
@@ -17,11 +15,7 @@ export class DossiersListingDossierNameComponent {
@Input() dossier: Dossier;
@Input() dossierStats: DossierStats;
- constructor(
- private readonly _dossierTemplatesService: DossierTemplatesService,
- private readonly _dossierStatsService: DossierStatsService,
- private readonly _dossiersService: DossiersService,
- ) {}
+ constructor(private readonly _dossierTemplatesService: DossierTemplatesService) {}
get approachingDueDate(): boolean {
return this._dueDateDaysDiff >= 0 && this._dueDateDaysDiff <= DUE_DATE_WARN_DAYS;
diff --git a/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.ts b/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.ts
index b2f3ae75c..c3cf688a2 100644
--- a/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.ts
+++ b/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.ts
@@ -63,7 +63,7 @@ export class ExpandableFileActionsComponent implements OnChanges {
private async _downloadFiles($event: MouseEvent, files: File[]) {
$event.stopPropagation();
const dossierId = files[0].dossierId;
- const filesIds = files.map(f => f.fileId);
+ const filesIds = files.map(f => f.id);
await firstValueFrom(this._fileDownloadService.downloadFiles(filesIds, dossierId));
this._toaster.info(_('download-status.queued'));
}
diff --git a/apps/red-ui/src/app/modules/shared/pipes/date.pipe.ts b/apps/red-ui/src/app/modules/shared/pipes/date.pipe.ts
index d1ec3f3a2..10734a943 100644
--- a/apps/red-ui/src/app/modules/shared/pipes/date.pipe.ts
+++ b/apps/red-ui/src/app/modules/shared/pipes/date.pipe.ts
@@ -5,7 +5,7 @@ import { DatePipe as BaseDatePipe } from '@angular/common';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { getLeftDateTime } from '@iqser/common-ui';
-const MONTH_NAMES = {
+const MONTH_NAMES: Record
= {
0: _('months.jan'),
1: _('months.feb'),
2: _('months.mar'),
@@ -29,16 +29,18 @@ export class DatePipe extends BaseDatePipe implements PipeTransform {
}
transform(value: null | undefined, format?: string, timezone?: string, locale?: string): null;
- transform(value: Date | string | number | null | undefined, format?: string, timezone?: string, locale?: string): string | null;
- transform(value: any, format?: string, timezone?: string, locale?: string): string {
- if (format === 'timeFromNow') {
- return this._getTimeFromNow(value);
- }
- if (format === 'sophisticatedDate') {
- return this._getSophisticatedDate(value);
- }
- if (format === 'exactDate') {
- return this._getExactDate(value);
+ transform(value: Date | string | number, format?: string, timezone?: string, locale?: string): string;
+ transform(value: Date | string | number | null | undefined, format?: string, timezone?: string, locale?: string): string | null {
+ if (typeof value === 'string') {
+ if (format === 'timeFromNow') {
+ return this._getTimeFromNow(value);
+ }
+ if (format === 'sophisticatedDate') {
+ return this._getSophisticatedDate(value);
+ }
+ if (format === 'exactDate') {
+ return this._getExactDate(value);
+ }
}
return super.transform(value, format, timezone, locale);
}
diff --git a/apps/red-ui/src/app/modules/shared/shared.module.ts b/apps/red-ui/src/app/modules/shared/shared.module.ts
index 939fd0eb7..a185ccf62 100644
--- a/apps/red-ui/src/app/modules/shared/shared.module.ts
+++ b/apps/red-ui/src/app/modules/shared/shared.module.ts
@@ -27,6 +27,8 @@ import { TeamMembersComponent } from './components/team-members/team-members.com
import { EditorComponent } from './components/editor/editor.component';
import { ExpandableFileActionsComponent } from './components/expandable-file-actions/expandable-file-actions.component';
import { ProcessingIndicatorComponent } from '@shared/components/processing-indicator/processing-indicator.component';
+import { DossierStatusComponent } from '@shared/components/dossier-status/dossier-status.component';
+import { DossiersListingDossierNameComponent } from '@shared/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component';
const buttons = [FileDownloadBtnComponent, UserButtonComponent];
@@ -43,6 +45,8 @@ const components = [
TeamMembersComponent,
ExpandableFileActionsComponent,
ProcessingIndicatorComponent,
+ DossierStatusComponent,
+ DossiersListingDossierNameComponent,
...buttons,
];
diff --git a/apps/red-ui/src/app/modules/upload-download/file-drop/file-drop.component.ts b/apps/red-ui/src/app/modules/upload-download/file-drop/file-drop.component.ts
index ca0b84888..29b4dc609 100644
--- a/apps/red-ui/src/app/modules/upload-download/file-drop/file-drop.component.ts
+++ b/apps/red-ui/src/app/modules/upload-download/file-drop/file-drop.component.ts
@@ -2,7 +2,7 @@ import { ChangeDetectorRef, Component, HostListener, Inject } from '@angular/cor
import { FileUploadService } from '../services/file-upload.service';
import { OverlayRef } from '@angular/cdk/overlay';
import { StatusOverlayService } from '../services/status-overlay.service';
-import { DossiersService } from '@services/entity-services/dossiers.service';
+import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { handleFileDrop } from '@utils/file-drop-utils';
import { FileUploadModel } from '@upload-download/model/file-upload.model';
import { DOSSIER_ID } from '../../../tokens';
@@ -15,7 +15,7 @@ export class FileDropComponent {
constructor(
private readonly _dialogRef: OverlayRef,
private readonly _fileUploadService: FileUploadService,
- private readonly _dossiersService: DossiersService,
+ private readonly _activeDossiersService: ActiveDossiersService,
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _statusOverlayService: StatusOverlayService,
@Inject(DOSSIER_ID) private readonly _dossierId: string,
@@ -34,7 +34,7 @@ export class FileDropComponent {
@HostListener('drop', ['$event'])
onDrop(event: DragEvent) {
- const dossier = this._dossiersService.find(this._dossierId);
+ const dossier = this._activeDossiersService.find(this._dossierId);
handleFileDrop(event, dossier, this.uploadFiles.bind(this));
}
diff --git a/apps/red-ui/src/app/modules/upload-download/services/file-upload.service.ts b/apps/red-ui/src/app/modules/upload-download/services/file-upload.service.ts
index 5494c0e81..674e28b8a 100644
--- a/apps/red-ui/src/app/modules/upload-download/services/file-upload.service.ts
+++ b/apps/red-ui/src/app/modules/upload-download/services/file-upload.service.ts
@@ -42,7 +42,7 @@ export class FileUploadService extends GenericService impleme
super(_injector, 'upload');
const fileFetch$ = this._fetchFiles$.pipe(
throttleTime(250),
- switchMap(dossierId => this._filesService.loadAll(dossierId)),
+ switchMap(dossierId => this._filesService.loadAll(dossierId, 'dossiers')),
);
this._subscriptions.add(fileFetch$.subscribe());
const interval$ = interval(2500).pipe(tap(() => this._handleUploads()));
diff --git a/apps/red-ui/src/app/services/breadcrumbs.service.ts b/apps/red-ui/src/app/services/breadcrumbs.service.ts
index a6a082738..16f90f59d 100644
--- a/apps/red-ui/src/app/services/breadcrumbs.service.ts
+++ b/apps/red-ui/src/app/services/breadcrumbs.service.ts
@@ -1,21 +1,28 @@
-import { Injectable } from '@angular/core';
+import { Injectable, Injector } from '@angular/core';
import { List } from '@iqser/common-ui';
import { ActivatedRouteSnapshot, IsActiveMatchOptions, NavigationEnd, Router } from '@angular/router';
import { BehaviorSubject, Observable, of } from 'rxjs';
import { filter, pluck } from 'rxjs/operators';
import { FilesMapService } from '@services/entity-services/files-map.service';
-import { DossiersService } from '@services/entity-services/dossiers.service';
import { TranslateService } from '@ngx-translate/core';
import { BreadcrumbTypes } from '@red/domain';
import { DOSSIER_ID, FILE_ID } from '@utils/constants';
+import { DossiersService } from '@services/dossiers/dossiers.service';
+import { dossiersServiceResolver } from '@services/entity-services/dossiers.service.provider';
export type RouterLinkActiveOptions = { exact: boolean } | IsActiveMatchOptions;
+export type BreadcrumbDisplayType = 'text' | 'dropdown';
export interface Breadcrumb {
readonly name$: Observable;
- readonly routerLink?: string[];
- readonly routerLinkActiveOptions?: RouterLinkActiveOptions | undefined;
- readonly clamp?: boolean;
+ readonly type: BreadcrumbDisplayType;
+ readonly options: {
+ readonly routerLink?: string[];
+ readonly routerLinkActiveOptions?: RouterLinkActiveOptions | undefined;
+ readonly clamp?: boolean;
+ readonly options?: Breadcrumb[];
+ readonly activeOption?: Breadcrumb;
+ };
}
export type Breadcrumbs = List;
@@ -28,10 +35,10 @@ export class BreadcrumbsService {
private readonly _store$ = new BehaviorSubject([]);
constructor(
+ private readonly _injector: Injector,
private readonly _router: Router,
private readonly _translateService: TranslateService,
private readonly _filesMapService: FilesMapService,
- private readonly _dossiersService: DossiersService,
) {
this.breadcrumbs$ = this._store$.asObservable();
@@ -46,6 +53,32 @@ export class BreadcrumbsService {
return this._store$.value;
}
+ private get _dossiersService(): DossiersService {
+ return dossiersServiceResolver(this._injector);
+ }
+
+ private get _mainBreadcrumb(): Breadcrumb {
+ return {
+ name$: of(this._translateService.instant('top-bar.navigation-items.dossiers')),
+ type: 'text' as BreadcrumbDisplayType,
+ options: {
+ routerLink: ['/main', 'dossiers'],
+ routerLinkActiveOptions: { exact: true },
+ },
+ };
+ }
+
+ private get _archiveBreadcrumb(): Breadcrumb {
+ return {
+ name$: of(this._translateService.instant('top-bar.navigation-items.archived-dossiers')),
+ type: 'text' as BreadcrumbDisplayType,
+ options: {
+ routerLink: ['/main', 'archive'],
+ routerLinkActiveOptions: { exact: true },
+ },
+ };
+ }
+
private _append(breadcrumb: Breadcrumb) {
this._store$.next([...this._store$.value, breadcrumb]);
}
@@ -60,10 +93,26 @@ export class BreadcrumbsService {
return;
}
- for (const breadcrumb of route.data.breadcrumbs || []) {
+ const breadcrumbs = route.data.breadcrumbs || [];
+
+ if (breadcrumbs.length === 1) {
+ if (breadcrumbs[0] === BreadcrumbTypes.main) {
+ this._addMainDropdownBreadcrumb('active');
+ return;
+ }
+ if (breadcrumbs[0] === BreadcrumbTypes.archive) {
+ this._addMainDropdownBreadcrumb('archived');
+ return;
+ }
+ }
+
+ for (const breadcrumb of breadcrumbs) {
switch (breadcrumb) {
case BreadcrumbTypes.main:
- this._addMainBreadcrumb();
+ this._append(this._mainBreadcrumb);
+ break;
+ case BreadcrumbTypes.archive:
+ this._append(this._archiveBreadcrumb);
break;
case BreadcrumbTypes.dossier:
this._addDossierBreadcrumb(route);
@@ -75,31 +124,46 @@ export class BreadcrumbsService {
}
}
- private _addMainBreadcrumb(): void {
+ private _addMainDropdownBreadcrumb(type: 'active' | 'archived'): void {
+ const activeDossiers: Breadcrumb = this._mainBreadcrumb;
+ const archivedDossiers: Breadcrumb = this._archiveBreadcrumb;
+ const activeOption = type === 'active' ? activeDossiers : archivedDossiers;
+
this._append({
- name$: of(this._translateService.instant('top-bar.navigation-items.dossiers')),
- routerLink: ['/main', 'dossiers'],
- routerLinkActiveOptions: { exact: true },
+ name$: activeOption.name$,
+ type: 'dropdown' as BreadcrumbDisplayType,
+ options: {
+ options: [activeDossiers, archivedDossiers],
+ activeOption,
+ },
});
}
private _addDossierBreadcrumb(route: ActivatedRouteSnapshot): void {
+ const dossiersService = this._dossiersService;
const dossierId = route.paramMap.get(DOSSIER_ID);
this._append({
- name$: this._dossiersService.getEntityChanged$(dossierId).pipe(pluck('dossierName')),
- routerLink: ['/main', 'dossiers', dossierId],
- routerLinkActiveOptions: { exact: true },
- clamp: true,
+ name$: dossiersService.getEntityChanged$(dossierId).pipe(pluck('dossierName')),
+ type: 'text' as BreadcrumbDisplayType,
+ options: {
+ routerLink: ['/main', dossiersService.routerPath, dossierId],
+ routerLinkActiveOptions: { exact: true },
+ clamp: true,
+ },
});
}
private _addFileBreadcrumb(route: ActivatedRouteSnapshot): void {
const dossierId = route.paramMap.get(DOSSIER_ID);
const fileId = route.paramMap.get(FILE_ID);
+ const dossiersService = this._dossiersService;
this._append({
name$: this._filesMapService.watch$(dossierId, fileId).pipe(pluck('filename')),
- routerLink: ['/main', 'dossiers', dossierId, 'file', fileId],
- clamp: true,
+ type: 'text' as BreadcrumbDisplayType,
+ options: {
+ routerLink: ['/main', dossiersService.routerPath, dossierId, 'file', fileId],
+ clamp: true,
+ },
});
}
}
diff --git a/apps/red-ui/src/app/services/dossiers/active-dossiers.service.ts b/apps/red-ui/src/app/services/dossiers/active-dossiers.service.ts
new file mode 100644
index 000000000..af72b2eb5
--- /dev/null
+++ b/apps/red-ui/src/app/services/dossiers/active-dossiers.service.ts
@@ -0,0 +1,30 @@
+import { Injectable, Injector } from '@angular/core';
+import { switchMap, tap } from 'rxjs/operators';
+import { timer } from 'rxjs';
+import { CHANGED_CHECK_INTERVAL } from '../../utils/constants';
+import { DossiersService } from './dossiers.service';
+
+export interface IDossiersStats {
+ totalPeople: number;
+ totalAnalyzedPages: number;
+}
+
+@Injectable({
+ providedIn: 'root',
+})
+export class ActiveDossiersService extends DossiersService {
+ constructor(protected readonly _injector: Injector) {
+ super(_injector, 'dossier', 'dossiers');
+
+ timer(CHANGED_CHECK_INTERVAL, CHANGED_CHECK_INTERVAL)
+ .pipe(
+ switchMap(() => this.loadOnlyChanged()),
+ tap(changes => this._emitFileChanges(changes)),
+ )
+ .subscribe();
+ }
+
+ getCountWithState(dossierStatusId: string): number {
+ return this.all.filter(dossier => dossier.dossierStatusId === dossierStatusId).length;
+ }
+}
diff --git a/apps/red-ui/src/app/services/dossiers/archived-dossiers.service.ts b/apps/red-ui/src/app/services/dossiers/archived-dossiers.service.ts
new file mode 100644
index 000000000..75ae0b05c
--- /dev/null
+++ b/apps/red-ui/src/app/services/dossiers/archived-dossiers.service.ts
@@ -0,0 +1,39 @@
+import { Injectable, Injector } from '@angular/core';
+import { Dossier } from '@red/domain';
+import { catchError, tap } from 'rxjs/operators';
+import { Observable, of } from 'rxjs';
+import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
+import { ActiveDossiersService } from './active-dossiers.service';
+import { DossiersService } from './dossiers.service';
+import { FilesMapService } from '../entity-services/files-map.service';
+
+@Injectable({ providedIn: 'root' })
+export class ArchivedDossiersService extends DossiersService {
+ constructor(
+ protected readonly _injector: Injector,
+ private readonly _activeDossiersService: ActiveDossiersService,
+ private readonly _filesMapService: FilesMapService,
+ ) {
+ super(_injector, 'archived-dossiers', 'archive');
+ }
+
+ archive(dossiers: Dossier[]): Observable {
+ const showArchiveFailedToast = () => {
+ this._toaster.error(_('dossier-listing.archive.archive-failed'), { params: dossiers });
+ return of({});
+ };
+
+ const archivedDossiersIds = dossiers.map(d => d.dossierId);
+
+ return this._post(archivedDossiersIds, `${this._defaultModelPath}/archive`).pipe(
+ tap(() => this.#removeFromActiveDossiers(archivedDossiersIds)),
+ catchError(showArchiveFailedToast),
+ );
+ }
+
+ #removeFromActiveDossiers(archivedDossiersIds: string[]): void {
+ const remainingEntities = this._activeDossiersService.all.filter(d => !archivedDossiersIds.includes(d.dossierId));
+ this._activeDossiersService.setEntities(remainingEntities);
+ this._filesMapService.delete(archivedDossiersIds);
+ }
+}
diff --git a/apps/red-ui/src/app/services/entity-services/dossier-stats.service.ts b/apps/red-ui/src/app/services/dossiers/dossier-stats.service.ts
similarity index 94%
rename from apps/red-ui/src/app/services/entity-services/dossier-stats.service.ts
rename to apps/red-ui/src/app/services/dossiers/dossier-stats.service.ts
index 52085cd92..aa244511b 100644
--- a/apps/red-ui/src/app/services/entity-services/dossier-stats.service.ts
+++ b/apps/red-ui/src/app/services/dossiers/dossier-stats.service.ts
@@ -3,7 +3,7 @@ import { StatsService } from '@iqser/common-ui';
import { DossierStats, IDossierStats } from '@red/domain';
import { DOSSIER_ID } from '@utils/constants';
import { Observable, of } from 'rxjs';
-import { UserService } from '@services/user.service';
+import { UserService } from '../user.service';
@Injectable({
providedIn: 'root',
diff --git a/apps/red-ui/src/app/services/entity-services/dossiers.service.ts b/apps/red-ui/src/app/services/dossiers/dossiers.service.ts
similarity index 52%
rename from apps/red-ui/src/app/services/entity-services/dossiers.service.ts
rename to apps/red-ui/src/app/services/dossiers/dossiers.service.ts
index 6c007257b..ad2c166b4 100644
--- a/apps/red-ui/src/app/services/entity-services/dossiers.service.ts
+++ b/apps/red-ui/src/app/services/dossiers/dossiers.service.ts
@@ -1,69 +1,42 @@
-import { Injectable, Injector } from '@angular/core';
import { EntitiesService, List, mapEach, QueryParam, RequiredParam, shareLast, Toaster, Validate } from '@iqser/common-ui';
-import { Dossier, DossierStats, IDossier, IDossierRequest } from '@red/domain';
+import { Dossier, DossierStats, IChangesDetails, IDossier, IDossierChanges, IDossierRequest } from '@red/domain';
+import { combineLatest, EMPTY, forkJoin, Observable, of, Subject, throwError } from 'rxjs';
import { catchError, filter, map, mapTo, pluck, switchMap, tap } from 'rxjs/operators';
-import { combineLatest, firstValueFrom, forkJoin, Observable, of, Subject, throwError, timer } from 'rxjs';
-import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
+import { Injector } from '@angular/core';
+import { DossierStateService } from '../entity-services/dossier-state.service';
+import { DossierStatsService } from './dossier-stats.service';
+import { IDossiersStats } from './active-dossiers.service';
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
-import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
-import { CHANGED_CHECK_INTERVAL } from '@utils/constants';
-import { DossierStateService } from '@services/entity-services/dossier-state.service';
-
-export interface IDossiersStats {
- totalPeople: number;
- totalAnalyzedPages: number;
-}
-
-interface DossierChange {
- readonly dossierChanges: boolean;
- readonly dossierId: string;
- readonly fileChanges: boolean;
-}
-
-type DossierChanges = readonly DossierChange[];
-
-interface ChangesDetails {
- readonly dossierChanges: DossierChanges;
-}
+import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
const DOSSIER_EXISTS_MSG = _('add-dossier-dialog.errors.dossier-already-exists');
const GENERIC_MSG = _('add-dossier-dialog.errors.generic');
-@Injectable({
- providedIn: 'root',
-})
-export class DossiersService extends EntitiesService {
- readonly generalStats$ = this.all$.pipe(switchMap(entities => this.#generalStats$(entities)));
+export abstract class DossiersService extends EntitiesService {
readonly dossierFileChanges$ = new Subject();
+ readonly generalStats$ = this.all$.pipe(switchMap(entities => this.#generalStats$(entities)));
+ protected readonly _dossierStatsService = this._injector.get(DossierStatsService);
+ protected readonly _dossierStateService = this._injector.get(DossierStateService);
+ protected readonly _toaster = this._injector.get(Toaster);
- constructor(
- private readonly _toaster: Toaster,
- protected readonly _injector: Injector,
- private readonly _dossierStatsService: DossierStatsService,
- private readonly _dossierStateService: DossierStateService,
- ) {
- super(_injector, Dossier, 'dossier');
-
- timer(CHANGED_CHECK_INTERVAL, CHANGED_CHECK_INTERVAL)
- .pipe(
- switchMap(() => this.loadOnlyChanged()),
- tap(changes => this.#emitFileChanges(changes)),
- )
- .subscribe();
+ protected constructor(protected readonly _injector: Injector, protected readonly _path: string, readonly routerPath: string) {
+ super(_injector, Dossier, _path);
}
- loadAll(): Observable {
- const dossierIds = (dossiers: Dossier[]) => dossiers.map(d => d.id);
- return this.getAll().pipe(
- mapEach(entity => new Dossier(entity)),
- /* Load stats before updating entities */
- switchMap(dossiers => this._dossierStatsService.getFor(dossierIds(dossiers)).pipe(mapTo(dossiers))),
- switchMap(dossiers => this._dossierStateService.loadAllForAllTemplates().pipe(mapTo(dossiers))),
- tap(dossiers => this.setEntities(dossiers)),
+ @Validate()
+ createOrUpdate(@RequiredParam() dossier: IDossierRequest): Observable {
+ const showToast = (error: HttpErrorResponse) => {
+ this._toaster.error(error.status === HttpStatusCode.Conflict ? DOSSIER_EXISTS_MSG : GENERIC_MSG);
+ return EMPTY;
+ };
+
+ return this._post(dossier, 'dossier').pipe(
+ switchMap(newDossier => this.loadAll().pipe(map(() => this.find(newDossier.dossierId)))),
+ catchError(showToast),
);
}
- loadOnlyChanged(): Observable {
+ loadOnlyChanged(): Observable {
const removeIfNotFound = (id: string) =>
catchError((error: HttpErrorResponse) => {
if (error.status === HttpStatusCode.NotFound) {
@@ -73,78 +46,47 @@ export class DossiersService extends EntitiesService {
return throwError(() => error);
});
- const load = (changes: DossierChanges) =>
+ const load = (changes: IDossierChanges) =>
changes.map(change => this._load(change.dossierId).pipe(removeIfNotFound(change.dossierId)));
return this.hasChangesDetails$().pipe(
- pluck('dossierChanges'),
switchMap(dossierChanges => forkJoin(load(dossierChanges)).pipe(mapTo(dossierChanges))),
tap(() => this._updateLastChanged()),
);
}
- hasChangesDetails$(): Observable {
+ loadAll(): Observable {
+ const dossierIds = (dossiers: Dossier[]) => dossiers.map(d => d.id);
+ return this.getAll().pipe(
+ mapEach(entity => new Dossier(entity, this.routerPath)),
+ /* Load stats before updating entities */
+ switchMap(dossiers => this._dossierStatsService.getFor(dossierIds(dossiers)).pipe(mapTo(dossiers))),
+ switchMap(dossiers => this._dossierStateService.loadAllForAllTemplates().pipe(mapTo(dossiers))),
+ tap(dossiers => this.setEntities(dossiers)),
+ );
+ }
+
+ hasChangesDetails$(): Observable {
const body = { value: this._lastCheckedForChanges.get('root') ?? '0' };
- return this._post(body, `${this._defaultModelPath}/changes/details`).pipe(
+ return this._post(body, `${this._defaultModelPath}/changes/details`).pipe(
filter(changes => changes.dossierChanges.length > 0),
+ pluck('dossierChanges'),
);
}
- @Validate()
- createOrUpdate(@RequiredParam() dossier: IDossierRequest): Observable {
- const showToast = (error: HttpErrorResponse) => {
- this._toaster.error(error.status === HttpStatusCode.Conflict ? DOSSIER_EXISTS_MSG : GENERIC_MSG);
- return of(undefined);
- };
-
- return this._post(dossier).pipe(
- switchMap(newDossier => this.loadAll().pipe(map(() => this.find(newDossier.dossierId)))),
- catchError(showToast),
- );
+ protected _emitFileChanges(dossierChanges: IDossierChanges): void {
+ dossierChanges.filter(change => change.fileChanges).forEach(change => this.dossierFileChanges$.next(change.dossierId));
}
- getDeleted(): Promise {
- return firstValueFrom(this.getAll('deleted-dossiers'));
- }
-
- delete(dossier: Dossier): Observable {
- const updateDossiers = () => {
- this.setEntities(this.all.filter(d => d.dossierId !== dossier.dossierId));
- };
- const showToast = () => {
- this._toaster.error(_('dossier-listing.delete.delete-failed'), { params: dossier });
- return of({});
- };
- return super.delete(dossier.dossierId).pipe(tap(updateDossiers), catchError(showToast));
- }
-
- @Validate()
- restore(@RequiredParam() dossierIds: List): Promise {
- return firstValueFrom(this._post(dossierIds, 'deleted-dossiers/restore'));
- }
-
- @Validate()
- hardDelete(@RequiredParam() dossierIds: List): Promise {
- const body = dossierIds.map(id => ({ key: 'dossierId', value: id }));
- return firstValueFrom(super.delete(body, 'deleted-dossiers/hard-delete', body));
- }
-
- getCountWithState(dossierStatusId: string): number {
- return this.all.filter(dossier => dossier.dossierStatusId === dossierStatusId).length;
- }
-
- private _load(id: string, queryParams?: List): Observable {
+ private _load(id: string): Observable {
+ const queryParams: List = [{ key: 'includeArchived', value: this._path === 'archived-dossiers' }];
return super._getOne([id], this._defaultModelPath, queryParams).pipe(
- map(entity => new Dossier(entity)),
+ map(entity => new Dossier(entity, 'dossiers')),
tap(dossier => this.replace(dossier)),
switchMap(dossier => this._dossierStatsService.getFor([dossier.dossierId])),
);
}
- #emitFileChanges(dossierChanges: DossierChanges): void {
- dossierChanges.filter(change => change.fileChanges).forEach(change => this.dossierFileChanges$.next(change.dossierId));
- }
-
#computeStats(entities: List): IDossiersStats {
let totalAnalyzedPages = 0;
const totalPeople = new Set();
diff --git a/apps/red-ui/src/app/services/entity-services/dossier-state.service.ts b/apps/red-ui/src/app/services/entity-services/dossier-state.service.ts
index e3c3fb7bd..cfe8f536a 100644
--- a/apps/red-ui/src/app/services/entity-services/dossier-state.service.ts
+++ b/apps/red-ui/src/app/services/entity-services/dossier-state.service.ts
@@ -14,7 +14,7 @@ export class DossierStateService extends EntitiesService(body, this._defaultModelPath);
}
@@ -27,7 +27,7 @@ export class DossierStateService extends EntitiesService template.dossierTemplateId),
mapEach(id => this.loadAllForTemplate(id)),
- switchMap(all => forkJoin(all).pipe(defaultIfEmpty([]))),
+ switchMap(all => forkJoin(all).pipe(defaultIfEmpty([] as DossierState[][]))),
map(value => value.flatMap(item => item)),
tap(value => this.setEntities(value)),
);
diff --git a/apps/red-ui/src/app/services/entity-services/dossiers.service.provider.ts b/apps/red-ui/src/app/services/entity-services/dossiers.service.provider.ts
new file mode 100644
index 000000000..0b176f0d7
--- /dev/null
+++ b/apps/red-ui/src/app/services/entity-services/dossiers.service.provider.ts
@@ -0,0 +1,15 @@
+import { ActivatedRoute } from '@angular/router';
+import { Injector, ProviderToken } from '@angular/core';
+import { DossiersService } from '../dossiers/dossiers.service';
+
+export const dossiersServiceResolver = (injector: Injector) => {
+ const route = injector.get(ActivatedRoute);
+ const token: ProviderToken = (route.firstChild || route).snapshot.data.dossiersService;
+ return injector.get(token);
+};
+
+export const dossiersServiceProvider = {
+ provide: DossiersService,
+ useFactory: dossiersServiceResolver,
+ deps: [Injector],
+};
diff --git a/apps/red-ui/src/app/services/entity-services/file-attributes.service.ts b/apps/red-ui/src/app/services/entity-services/file-attributes.service.ts
index 15e982731..bc1046d9a 100644
--- a/apps/red-ui/src/app/services/entity-services/file-attributes.service.ts
+++ b/apps/red-ui/src/app/services/entity-services/file-attributes.service.ts
@@ -16,10 +16,6 @@ export class FileAttributesService extends EntitiesService {
}
@Validate()
- delete(@RequiredParam() fileIds: List, @RequiredParam() dossierId: string) {
- return super._post(fileIds, `delete/${dossierId}`).pipe(switchMap(() => this._filesService.loadAll(dossierId)));
+ delete(@RequiredParam() files: List, @RequiredParam() dossierId: string) {
+ const fileIds = files.map(f => f.id);
+ const routerPath: string = files[0].routerPath;
+ return super._post(fileIds, `delete/${dossierId}`).pipe(switchMap(() => this._filesService.loadAll(dossierId, routerPath)));
}
@Validate()
@@ -33,8 +35,10 @@ export class FileManagementService extends GenericService {
}
@Validate()
- restore(@RequiredParam() body: List, @RequiredParam() dossierId: string) {
- return this._post(body, `delete/restore/${dossierId}`).pipe(switchMap(() => this._filesService.loadAll(dossierId)));
+ restore(@RequiredParam() files: List, @RequiredParam() dossierId: string) {
+ const fileIds = files.map(f => f.id);
+ const routerPath: string = files[0].routerPath;
+ return this._post(fileIds, `delete/restore/${dossierId}`).pipe(switchMap(() => this._filesService.loadAll(dossierId, routerPath)));
}
@Validate()
diff --git a/apps/red-ui/src/app/services/entity-services/files-map.service.ts b/apps/red-ui/src/app/services/entity-services/files-map.service.ts
index d71f717d1..bfe6587aa 100644
--- a/apps/red-ui/src/app/services/entity-services/files-map.service.ts
+++ b/apps/red-ui/src/app/services/entity-services/files-map.service.ts
@@ -12,7 +12,11 @@ export class FilesMapService extends EntitiesMapService {
replaceFiles(files: File[], property: keyof IFile, generateValue: Function) {
const newFiles = files.map(
file =>
- new File({ ...file, [property]: generateValue(file[property]), lastUpdated: new Date().toISOString() }, file.reviewerName),
+ new File(
+ { ...file, [property]: generateValue(file[property]), lastUpdated: new Date().toISOString() },
+ file.reviewerName,
+ file.routerPath,
+ ),
);
this.replace(newFiles);
}
diff --git a/apps/red-ui/src/app/services/entity-services/files.service.ts b/apps/red-ui/src/app/services/entity-services/files.service.ts
index 8a8109f46..6d76d9d1f 100644
--- a/apps/red-ui/src/app/services/entity-services/files.service.ts
+++ b/apps/red-ui/src/app/services/entity-services/files.service.ts
@@ -1,11 +1,11 @@
import { Injectable, Injector } from '@angular/core';
-import { EntitiesService, List, mapEach, RequiredParam, Validate } from '@iqser/common-ui';
+import { EntitiesService, IRouterPath, List, mapEach, RequiredParam, Validate } from '@iqser/common-ui';
import { File, IFile } from '@red/domain';
import { Observable } from 'rxjs';
import { UserService } from '../user.service';
import { FilesMapService } from '@services/entity-services/files-map.service';
import { map, mapTo, switchMap, tap } from 'rxjs/operators';
-import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
+import { DossierStatsService } from '@services/dossiers/dossier-stats.service';
@Injectable({
providedIn: 'root',
@@ -21,40 +21,50 @@ export class FilesService extends EntitiesService {
}
/** Reload dossier files + stats. */
- loadAll(dossierId: string) {
- const files$ = this.getFor(dossierId).pipe(mapEach(file => new File(file, this._userService.getNameForId(file.assignee))));
+ loadAll(dossierId: string, routerPath: string) {
+ const files$ = this.getFor(dossierId).pipe(
+ mapEach(file => new File(file, this._userService.getNameForId(file.assignee), routerPath)),
+ );
const loadStats$ = files$.pipe(switchMap(files => this._dossierStatsService.getFor([dossierId]).pipe(mapTo(files))));
return loadStats$.pipe(tap(files => this._filesMapService.set(dossierId, files)));
}
- reload(dossierId: string, fileId: string): Observable {
- return super._getOne([dossierId, fileId]).pipe(
- map(file => new File(file, this._userService.getNameForId(file.assignee))),
- switchMap(file => this._dossierStatsService.getFor([dossierId]).pipe(mapTo(file))),
- map(file => this._filesMapService.replace([file])),
+ reload(dossierId: string, file: File): Observable {
+ return super._getOne([dossierId, file.id]).pipe(
+ map(_file => new File(_file, this._userService.getNameForId(_file.assignee), file.routerPath)),
+ switchMap(_file => this._dossierStatsService.getFor([dossierId]).pipe(mapTo(_file))),
+ map(_file => this._filesMapService.replace([_file])),
);
}
@Validate()
- setUnassigned(@RequiredParam() fileIds: List, @RequiredParam() dossierId: string) {
+ setUnassigned(@RequiredParam() files: List, @RequiredParam() dossierId: string) {
const url = `${this._defaultModelPath}/set-assignee/${dossierId}/bulk`;
- return this._post(fileIds, url).pipe(switchMap(() => this.loadAll(dossierId)));
+ const fileIds = files.map(f => f.id);
+ const routerPath: string = files[0].routerPath;
+ return this._post(fileIds, url).pipe(switchMap(() => this.loadAll(dossierId, routerPath)));
}
@Validate()
- setUnderApprovalFor(@RequiredParam() fileIds: List, @RequiredParam() dossierId: string, assigneeId: string) {
+ setUnderApprovalFor(@RequiredParam() files: List, @RequiredParam() dossierId: string, assigneeId: string) {
const url = `${this._defaultModelPath}/under-approval/${dossierId}/bulk`;
- return this._post(fileIds, url, [{ key: 'assigneeId', value: assigneeId }]).pipe(switchMap(() => this.loadAll(dossierId)));
+ const fileIds = files.map(f => f.id);
+ const routerPath: string = files[0].routerPath;
+ return this._post(fileIds, url, [{ key: 'assigneeId', value: assigneeId }]).pipe(
+ switchMap(() => this.loadAll(dossierId, routerPath)),
+ );
}
/**
* Assigns a reviewer for a list of files.
*/
@Validate()
- setReviewerFor(@RequiredParam() filesIds: List, @RequiredParam() dossierId: string, assigneeId: string) {
+ setReviewerFor(@RequiredParam() files: List, @RequiredParam() dossierId: string, assigneeId: string) {
const url = `${this._defaultModelPath}/under-review/${dossierId}/bulk`;
- return this._post(filesIds, url, [{ key: 'assigneeId', value: assigneeId }]).pipe(
- switchMap(() => this.loadAll(dossierId)),
+ const fileIds = files.map(f => f.id);
+ const routerPath: string = files[0].routerPath;
+ return this._post(fileIds, url, [{ key: 'assigneeId', value: assigneeId }]).pipe(
+ switchMap(() => this.loadAll(dossierId, routerPath)),
);
}
@@ -62,9 +72,11 @@ export class FilesService extends EntitiesService {
* Sets the status APPROVED for a list of files.
*/
@Validate()
- setApprovedFor(@RequiredParam() filesIds: List, @RequiredParam() dossierId: string) {
- return this._post(filesIds, `${this._defaultModelPath}/approved/${dossierId}/bulk`).pipe(
- switchMap(() => this.loadAll(dossierId)),
+ setApprovedFor(@RequiredParam() files: List, @RequiredParam() dossierId: string) {
+ const fileIds = files.map(f => f.id);
+ const routerPath: string = files[0].routerPath;
+ return this._post(fileIds, `${this._defaultModelPath}/approved/${dossierId}/bulk`).pipe(
+ switchMap(() => this.loadAll(dossierId, routerPath)),
);
}
@@ -72,9 +84,11 @@ export class FilesService extends EntitiesService {
* Sets the status UNDER_REVIEW for a list of files.
*/
@Validate()
- setUnderReviewFor(@RequiredParam() filesIds: List, @RequiredParam() dossierId: string) {
- return this._post(filesIds, `${this._defaultModelPath}/under-review/${dossierId}/bulk`).pipe(
- switchMap(() => this.loadAll(dossierId)),
+ setUnderReviewFor(@RequiredParam() files: List, @RequiredParam() dossierId: string) {
+ const fileIds = files.map(f => f.id);
+ const routerPath: string = files[0].routerPath;
+ return this._post(fileIds, `${this._defaultModelPath}/under-review/${dossierId}/bulk`).pipe(
+ switchMap(() => this.loadAll(dossierId, routerPath)),
);
}
diff --git a/apps/red-ui/src/app/services/entity-services/platform-search.service.ts b/apps/red-ui/src/app/services/entity-services/platform-search.service.ts
index 000d563c1..abda973f3 100644
--- a/apps/red-ui/src/app/services/entity-services/platform-search.service.ts
+++ b/apps/red-ui/src/app/services/entity-services/platform-search.service.ts
@@ -1,24 +1,27 @@
import { Injectable, Injector } from '@angular/core';
import { GenericService } from '@iqser/common-ui';
-import { IMatchedDocument, ISearchInput, ISearchRequest, ISearchResponse } from '@red/domain';
+import { Dossier, IMatchedDocument, ISearchInput, ISearchRequest, ISearchResponse } from '@red/domain';
import { Observable, of, zip } from 'rxjs';
import { mapTo, switchMap } from 'rxjs/operators';
-import { DossiersService } from './dossiers.service';
+import { ActiveDossiersService } from '../dossiers/active-dossiers.service';
import { FilesMapService } from './files-map.service';
import { FilesService } from './files.service';
+import { ArchivedDossiersService } from '../dossiers/archived-dossiers.service';
+import { DossiersService } from '../dossiers/dossiers.service';
@Injectable({ providedIn: 'root' })
export class PlatformSearchService extends GenericService {
constructor(
protected readonly _injector: Injector,
private readonly _filesService: FilesService,
- private readonly _dossiersService: DossiersService,
+ private readonly _activeDossiersService: ActiveDossiersService,
+ private readonly _archivedDossiersService: ArchivedDossiersService,
private readonly _filesMapService: FilesMapService,
) {
super(_injector, 'search-v2');
}
- search({ dossierIds, query }: ISearchInput): Observable {
+ search({ dossierIds, query, dossierStatus }: ISearchInput): Observable {
if (!query) {
return of({
matchedDocuments: [],
@@ -32,21 +35,26 @@ export class PlatformSearchService extends GenericService {
page: 0,
returnSections: false,
pageSize: 300,
+ dossierStatus,
};
return this._post(body).pipe(switchMap(searchValue => this._loadMissingFiles$(searchValue)));
}
- private _loadMissingFiles$(searchResponse: ISearchResponse): Observable {
- const documentsOfActiveDossiers = searchResponse.matchedDocuments.filter(document => this._dossiersService.has(document.dossierId));
+ private _dossiersWithMissingFiles(searchResponse: ISearchResponse, service: DossiersService): Dossier[] {
+ const documentsOfType = searchResponse.matchedDocuments.filter(document => service.has(document.dossierId));
const fileNotLoaded = ({ dossierId, fileId }: IMatchedDocument) => !this._filesMapService.get(dossierId, fileId);
- const dossiersWithNotLoadedFiles = documentsOfActiveDossiers.filter(fileNotLoaded).map(document => document.dossierId);
-
- const dossierIds = Array.from(new Set(dossiersWithNotLoadedFiles));
- return dossierIds.length ? this._loadFilesFor$(dossierIds).pipe(mapTo(searchResponse)) : of(searchResponse);
+ const dossiersWithNotLoadedFiles = documentsOfType.filter(fileNotLoaded).map(document => document.dossierId);
+ return Array.from(new Set(dossiersWithNotLoadedFiles)).map(dossierId => service.find(dossierId));
}
- private _loadFilesFor$(dossierIds: string[]) {
- return zip(...dossierIds.map(dossierId => this._filesService.loadAll(dossierId)));
+ private _loadMissingFiles$(searchResponse: ISearchResponse): Observable {
+ const services = [this._activeDossiersService, this._archivedDossiersService];
+ const dossiers = services.map(service => this._dossiersWithMissingFiles(searchResponse, service)).flat();
+ return dossiers.length ? this._loadFilesFor$(dossiers).pipe(mapTo(searchResponse)) : of(searchResponse);
+ }
+
+ private _loadFilesFor$(dossiers: Dossier[]) {
+ return zip(...dossiers.map(dossier => this._filesService.loadAll(dossier.id, dossier.routerPath)));
}
}
diff --git a/apps/red-ui/src/app/services/entity-services/trash-dossiers.service.ts b/apps/red-ui/src/app/services/entity-services/trash-dossiers.service.ts
new file mode 100644
index 000000000..158ff48d5
--- /dev/null
+++ b/apps/red-ui/src/app/services/entity-services/trash-dossiers.service.ts
@@ -0,0 +1,83 @@
+import { Injectable, Injector } from '@angular/core';
+import { EntitiesService, mapEach, QueryParam, RequiredParam, Toaster, Validate } from '@iqser/common-ui';
+import { Dossier, IDossier, TrashDossier } from '@red/domain';
+import { catchError, switchMap, tap } from 'rxjs/operators';
+import { firstValueFrom, Observable, of } from 'rxjs';
+import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
+import * as moment from 'moment';
+import { ConfigService } from '../config.service';
+import { PermissionsService } from '../permissions.service';
+import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
+
+export interface IDossiersStats {
+ totalPeople: number;
+ totalAnalyzedPages: number;
+}
+
+@Injectable({
+ providedIn: 'root',
+})
+export class TrashDossiersService extends EntitiesService {
+ constructor(
+ protected readonly _injector: Injector,
+ private readonly _toaster: Toaster,
+ private readonly _configService: ConfigService,
+ private readonly _permissionsService: PermissionsService,
+ private readonly _activeDossiersService: ActiveDossiersService,
+ ) {
+ super(_injector, TrashDossier, 'dossier');
+ }
+
+ loadAll(): Observable {
+ return this.#getDeleted().pipe(
+ mapEach(
+ dossier => new TrashDossier(dossier, this.#getRestoreDate(dossier), this._permissionsService.canDeleteDossier(dossier)),
+ ),
+ tap(dossiers => this.setEntities(dossiers)),
+ );
+ }
+
+ delete(dossier: Dossier): Observable {
+ const showToast = () => {
+ this._toaster.error(_('dossier-listing.delete.delete-failed'), { params: dossier });
+ return of({});
+ };
+ return super.delete(dossier.dossierId).pipe(
+ switchMap(() => this._activeDossiersService.loadAll()),
+ catchError(showToast),
+ );
+ }
+
+ @Validate()
+ restore(@RequiredParam() dossierIds: string[]): Promise {
+ return firstValueFrom(
+ this._post(dossierIds, 'deleted-dossiers/restore').pipe(
+ switchMap(() => this._activeDossiersService.loadAll()),
+ tap(() => this.#removeDossiers(dossierIds)),
+ ),
+ );
+ }
+
+ @Validate()
+ hardDelete(@RequiredParam() dossierIds: string[]): Promise {
+ const body = dossierIds.map(id => ({ key: 'dossierId', value: id }));
+ return firstValueFrom(
+ super.delete(body, 'deleted-dossiers/hard-delete', body).pipe(
+ switchMap(() => this._activeDossiersService.loadAll()),
+ tap(() => this.#removeDossiers(dossierIds)),
+ ),
+ );
+ }
+
+ #getRestoreDate(dossier: IDossier): string {
+ return moment(dossier.softDeletedTime).add(this._configService.values.DELETE_RETENTION_HOURS, 'hours').toISOString();
+ }
+
+ #getDeleted(): Observable {
+ return this.getAll('deleted-dossiers');
+ }
+
+ #removeDossiers(dossierIds: string[]): void {
+ this.setEntities(this.all.filter(dossier => !dossierIds.includes(dossier.id)));
+ }
+}
diff --git a/apps/red-ui/src/app/services/notifications.service.ts b/apps/red-ui/src/app/services/notifications.service.ts
index 9d798fcb5..f47cce9c5 100644
--- a/apps/red-ui/src/app/services/notifications.service.ts
+++ b/apps/red-ui/src/app/services/notifications.service.ts
@@ -7,7 +7,7 @@ import { INotification, Notification, NotificationTypes } from '@red/domain';
import { map, switchMap } from 'rxjs/operators';
import { notificationsTranslations } from '../translations/notifications-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
-import { DossiersService } from '@services/entity-services/dossiers.service';
+import { ActiveDossiersService } from './dossiers/active-dossiers.service';
import { UserService } from '@services/user.service';
import { FilesMapService } from '@services/entity-services/files-map.service';
@@ -18,7 +18,7 @@ export class NotificationsService extends GenericService {
constructor(
protected readonly _injector: Injector,
private readonly _translateService: TranslateService,
- private readonly _dossiersService: DossiersService,
+ private readonly _activeDossiersService: ActiveDossiersService,
private readonly _userService: UserService,
private readonly _filesMapService: FilesMapService,
) {
@@ -67,7 +67,7 @@ export class NotificationsService extends GenericService {
private _translate(notification: INotification, translation: string): string {
const fileId = notification.target.fileId;
const dossierId = notification.target.dossierId;
- const dossier = this._dossiersService.find(dossierId);
+ const dossier = this._activeDossiersService.find(dossierId);
const files = this._filesMapService.get(dossierId);
const file = files?.find(f => f.fileId === fileId);
diff --git a/apps/red-ui/src/app/services/permissions.service.ts b/apps/red-ui/src/app/services/permissions.service.ts
index 366aaf617..befafdb3f 100644
--- a/apps/red-ui/src/app/services/permissions.service.ts
+++ b/apps/red-ui/src/app/services/permissions.service.ts
@@ -1,19 +1,24 @@
-import { Injectable } from '@angular/core';
+import { Injectable, Injector } from '@angular/core';
import { UserService } from './user.service';
import { Dossier, File, IComment, IDossier } from '@red/domain';
-import { DossiersService } from './entity-services/dossiers.service';
+import { DossiersService } from '@services/dossiers/dossiers.service';
+import { ActivatedRoute } from '@angular/router';
+import { dossiersServiceResolver } from '@services/entity-services/dossiers.service.provider';
import { FilesMapService } from '@services/entity-services/files-map.service';
-@Injectable({
- providedIn: 'root',
-})
+@Injectable({ providedIn: 'root' })
export class PermissionsService {
constructor(
private readonly _userService: UserService,
- private readonly _dossiersService: DossiersService,
+ private readonly _route: ActivatedRoute,
+ private readonly _injector: Injector,
private readonly _filesMapService: FilesMapService,
) {}
+ private get _dossiersService(): DossiersService {
+ return dossiersServiceResolver(this._injector);
+ }
+
isReviewerOrApprover(file: File): boolean {
const dossier = this._getDossier(file);
return this.isFileAssignee(file) || this.isApprover(dossier);
@@ -23,9 +28,20 @@ export class PermissionsService {
return this.isApprover(dossier) && !!this._filesMapService.get(dossier.dossierId).find(f => f.analysisRequired);
}
+ canUploadFiles(dossier: Dossier): boolean {
+ return dossier.isActive;
+ }
+
+ canDownloadCsvReport(dossier: Dossier): boolean {
+ return dossier.isActive;
+ }
+
canEditFileAttributes(file: File): boolean {
const dossier = this._getDossier(file);
- return ((file.isUnderReview || file.isNew) && this.isDossierMember(dossier)) || (file.isUnderApproval && this.isApprover(dossier));
+ return (
+ this._isActive(file) &&
+ (((file.isUnderReview || file.isNew) && this.isDossierMember(dossier)) || (file.isUnderApproval && this.isApprover(dossier)))
+ );
}
canToggleAnalysis(file: File | File[]): boolean {
@@ -34,6 +50,11 @@ export class PermissionsService {
return sameState && files.reduce((acc, _file) => this._canToggleAnalysis(_file) && acc, true);
}
+ showToggleAnalysis(file: File | File[]): boolean {
+ const files = file instanceof File ? [file] : file;
+ return this._isActive(files[0]);
+ }
+
canReanalyseFile(file: File | File[]): boolean {
const files = file instanceof File ? [file] : file;
return files.reduce((acc, _file) => this._canReanalyseFile(_file) && acc, true);
@@ -57,6 +78,16 @@ export class PermissionsService {
return files.reduce((acc, _file) => this._canDeleteFile(_file, dossier) && acc, true);
}
+ canHardDeleteOrRestore(dossier: Dossier): boolean {
+ return dossier.isActive;
+ }
+
+ canOcrFile(file: File | File[]): boolean {
+ const files = file instanceof File ? [file] : file;
+ const dossier = this._getDossier(files[0]);
+ return files.reduce((acc, _file) => this._canOcrFile(_file, dossier) && acc, true);
+ }
+
canAssignToSelf(file: File | File[]): boolean {
const files = file instanceof File ? [file] : file;
const dossier = this._getDossier(files[0]);
@@ -113,7 +144,13 @@ export class PermissionsService {
// TODO: Remove '?', after we make sure file is loaded before page
canPerformAnnotationActions(file: File): boolean {
- return !file.isOcrProcessing && !file.excluded && (file?.isUnderReview || file?.isUnderApproval) && this.isFileAssignee(file);
+ return (
+ this._isActive(file) &&
+ !file.isOcrProcessing &&
+ !file.excluded &&
+ (file?.isUnderReview || file?.isUnderApproval) &&
+ this.isFileAssignee(file)
+ );
}
canUndoApproval(file: File | File[]): boolean {
@@ -138,10 +175,30 @@ export class PermissionsService {
return dossier.ownerId === this._userService.currentUser.id;
}
+ canArchiveDossier(dossier: Dossier): boolean {
+ return dossier.isActive && dossier.ownerId === this._userService.currentUser.id;
+ }
+
canEditDossier(dossier: Dossier, user = this._userService.currentUser): boolean {
return user.isManager && !!dossier?.ownerId;
}
+ canEditDossierDictionary(dossier: Dossier, user = this._userService.currentUser): boolean {
+ return dossier.isActive && this.canEditDossier(dossier, user);
+ }
+
+ canEditDossierDictionaryDisplayName(dossier: Dossier, user = this._userService.currentUser): boolean {
+ return dossier.isActive && this.isOwner(dossier, user);
+ }
+
+ canEditDossierDictionaryAddAction(dossier: Dossier, user = this._userService.currentUser): boolean {
+ return dossier.isActive && this.isOwner(dossier, user);
+ }
+
+ canEditDossierAttributes(dossier: Dossier, user = this._userService.currentUser): boolean {
+ return dossier.isActive && this.isOwner(dossier, user);
+ }
+
isAdmin(user = this._userService.currentUser): boolean {
return user.isAdmin;
}
@@ -160,54 +217,70 @@ export class PermissionsService {
}
canImportRedactions(file: File) {
- return (this.isFileAssignee(file) || this.isApprover(this._getDossier(file))) && !file.isApproved;
+ return this._isActive(file) && (this.isFileAssignee(file) || this.isApprover(this._getDossier(file))) && !file.isApproved;
+ }
+
+ private _canOcrFile(file: File, dossier: Dossier): boolean {
+ return dossier.isActive && file.canBeOCRed;
}
private _canToggleAnalysis(file: File): boolean {
- return this.isFileAssignee(file) && (file.isNew || file.isUnderReview || file.isUnderApproval);
+ return this._isActive(file) && this.isFileAssignee(file) && (file.isNew || file.isUnderReview || file.isUnderApproval);
}
// https://jira.iqser.com/browse/RED-2787
private _canDeleteFile(file: File, dossier: Dossier): boolean {
return (
- file.isNew ||
- (file.isUnderReview && !file.assignee && this.isDossierMember(dossier)) ||
- (file.isUnderApproval && !file.assignee && this.isApprover(dossier)) ||
- (file.assignee && !file.isApproved && (this.isFileAssignee(file) || this.isOwner(dossier)))
+ dossier.isActive &&
+ (file.isNew ||
+ (file.isUnderReview && !file.assignee && this.isDossierMember(dossier)) ||
+ (file.isUnderApproval && !file.assignee && this.isApprover(dossier)) ||
+ (file.assignee && !file.isApproved && (this.isFileAssignee(file) || this.isOwner(dossier))))
);
}
private _canReanalyseFile(file: File): boolean {
- return this.isReviewerOrApprover(file) && file.analysisRequired;
+ return this._isActive(file) && this.isReviewerOrApprover(file) && file.analysisRequired;
}
private _canEnableAutoAnalysis(file: File): boolean {
- return file.excludedFromAutomaticAnalysis && file.assignee === this._userService.currentUser.id && !file.isApproved;
+ return (
+ this._isActive(file) &&
+ file.excludedFromAutomaticAnalysis &&
+ file.assignee === this._userService.currentUser.id &&
+ !file.isApproved
+ );
}
private _canDisableAutoAnalysis(file: File): boolean {
- return !file.excludedFromAutomaticAnalysis && file.assignee === this._userService.currentUser.id && !file.isApproved;
+ return (
+ this._isActive(file) &&
+ !file.excludedFromAutomaticAnalysis &&
+ file.assignee === this._userService.currentUser.id &&
+ !file.isApproved
+ );
}
private _canAssignToSelf(file: File, dossier: Dossier): boolean {
- const precondition = this.isDossierMember(dossier) && !this.isFileAssignee(file) && !file.isError && !file.isProcessing;
+ const precondition =
+ this._isActive(file) && this.isDossierMember(dossier) && !this.isFileAssignee(file) && !file.isError && !file.isProcessing;
return precondition && (file.isNew || file.isUnderReview || (file.isUnderApproval && this.isApprover(dossier)));
}
private _canSetUnderApproval(file: File): boolean {
- return file.isUnderReview && this.isReviewerOrApprover(file);
+ return this._isActive(file) && file.isUnderReview && this.isReviewerOrApprover(file);
}
private _canUndoApproval(file: File, dossier: Dossier): boolean {
- return file.isApproved && this.isApprover(dossier);
+ return this._isActive(file) && file.isApproved && this.isApprover(dossier);
}
private _canBeApproved(file: File): boolean {
- return file.canBeApproved;
+ return this._isActive(file) && file.canBeApproved;
}
private _canAssignUser(file: File, dossier: Dossier) {
- const precondition = !file.isProcessing && !file.isError && !file.isApproved && this.isApprover(dossier);
+ const precondition = this._isActive(file) && !file.isProcessing && !file.isError && !file.isApproved && this.isApprover(dossier);
if (precondition) {
if ((file.isNew || file.isUnderReview) && dossier.hasReviewers) {
@@ -221,14 +294,20 @@ export class PermissionsService {
}
private _canUnassignUser(file: File, dossier: Dossier) {
- return (file.isUnderReview || file.isUnderApproval) && (this.isFileAssignee(file) || this.isApprover(dossier));
+ return (
+ this._isActive(file) && (file.isUnderReview || file.isUnderApproval) && (this.isFileAssignee(file) || this.isApprover(dossier))
+ );
}
private _canSetUnderReview(file: File): boolean {
- return file.isUnderApproval;
+ return this._isActive(file) && file.isUnderApproval;
}
private _getDossier(file: File): Dossier {
return this._dossiersService.find(file.dossierId);
}
+
+ private _isActive(file: File): boolean {
+ return this._getDossier(file).isActive;
+ }
}
diff --git a/apps/red-ui/src/app/services/reanalysis.service.ts b/apps/red-ui/src/app/services/reanalysis.service.ts
index 03d002626..d58a2e406 100644
--- a/apps/red-ui/src/app/services/reanalysis.service.ts
+++ b/apps/red-ui/src/app/services/reanalysis.service.ts
@@ -1,6 +1,6 @@
import { Injectable, Injector } from '@angular/core';
-import { GenericService, List, QueryParam, RequiredParam, Toaster, Validate } from '@iqser/common-ui';
-import { File, IPageExclusionRequest } from '@red/domain';
+import { GenericService, IRouterPath, List, QueryParam, RequiredParam, Toaster, Validate } from '@iqser/common-ui';
+import { Dossier, File, IPageExclusionRequest } from '@red/domain';
import { catchError, switchMap, tap } from 'rxjs/operators';
import { FilesService } from './entity-services/files.service';
import { FilesMapService } from './entity-services/files-map.service';
@@ -26,17 +26,19 @@ export class ReanalysisService extends GenericService {
}
@Validate()
- excludePages(@RequiredParam() body: IPageExclusionRequest, @RequiredParam() dossierId: string, @RequiredParam() fileId: string) {
- return this._post(body, `exclude-pages/${dossierId}/${fileId}`).pipe(switchMap(() => this._filesService.reload(dossierId, fileId)));
+ excludePages(@RequiredParam() body: IPageExclusionRequest, @RequiredParam() dossierId: string, @RequiredParam() file: File) {
+ return this._post(body, `exclude-pages/${dossierId}/${file.id}`).pipe(switchMap(() => this._filesService.reload(dossierId, file)));
}
@Validate()
- includePages(@RequiredParam() body: IPageExclusionRequest, @RequiredParam() dossierId: string, @RequiredParam() fileId: string) {
- return this._post(body, `include-pages/${dossierId}/${fileId}`).pipe(switchMap(() => this._filesService.reload(dossierId, fileId)));
+ includePages(@RequiredParam() body: IPageExclusionRequest, @RequiredParam() dossierId: string, @RequiredParam() file: File) {
+ return this._post(body, `include-pages/${dossierId}/${file.id}`).pipe(switchMap(() => this._filesService.reload(dossierId, file)));
}
@Validate()
- reanalyzeFilesForDossier(@RequiredParam() fileIds: List, @RequiredParam() dossierId: string, params?: ReanalyzeQueryParams) {
+ reanalyzeFilesForDossier(@RequiredParam() files: List, @RequiredParam() dossierId: string, params?: ReanalyzeQueryParams) {
+ const fileIds = files.map(f => f.id);
+ const routerPath: string = files[0].routerPath;
const queryParams: QueryParam[] = [];
if (params?.force) {
queryParams.push({ key: 'force', value: true });
@@ -45,18 +47,22 @@ export class ReanalysisService extends GenericService {
queryParams.push({ key: 'triggeredByUser', value: true });
}
- return this._post(fileIds, `reanalyze/${dossierId}/bulk`, queryParams).pipe(switchMap(() => this._filesService.loadAll(dossierId)));
+ return this._post(fileIds, `reanalyze/${dossierId}/bulk`, queryParams).pipe(
+ switchMap(() => this._filesService.loadAll(dossierId, routerPath)),
+ );
}
@Validate()
- toggleAnalysis(@RequiredParam() dossierId: string, @RequiredParam() fileIds: string[], excluded?: boolean) {
+ toggleAnalysis(@RequiredParam() dossierId: string, @RequiredParam() files: List, excluded?: boolean) {
+ const fileIds = files.map(f => f.id);
+ const routerPath: string = files[0].routerPath;
const queryParams: QueryParam[] = [];
if (excluded) {
queryParams.push({ key: 'excluded', value: excluded });
}
return this._post(fileIds, `toggle-analysis/${dossierId}/bulk`, queryParams).pipe(
- switchMap(() => this._filesService.loadAll(dossierId)),
+ switchMap(() => this._filesService.loadAll(dossierId, routerPath)),
);
}
@@ -80,17 +86,24 @@ export class ReanalysisService extends GenericService {
}
@Validate()
- ocrFiles(@RequiredParam() fileIds: List, @RequiredParam() dossierId: string) {
- return this._post(fileIds, `ocr/reanalyze/${dossierId}/bulk`).pipe(switchMap(() => this._filesService.loadAll(dossierId)));
+ ocrFiles(@RequiredParam() files: List, @RequiredParam() dossierId: string) {
+ const fileIds = files.map(f => f.id);
+ const routerPath: string = files[0].routerPath;
+ return this._post(fileIds, `ocr/reanalyze/${dossierId}/bulk`).pipe(
+ switchMap(() => this._filesService.loadAll(dossierId, routerPath)),
+ );
}
@Validate()
- reanalyzeDossier(@RequiredParam() dossierId: string, force?: boolean) {
+ reanalyzeDossier(@RequiredParam() dossier: Dossier, force?: boolean) {
+ const { dossierId, routerPath } = dossier;
const queryParams: QueryParam[] = [];
if (force) {
queryParams.push({ key: 'force', value: force });
}
- return this._post({}, `reanalyze/${dossierId}`, queryParams).pipe(switchMap(() => this._filesService.loadAll(dossierId)));
+ return this._post({}, `reanalyze/${dossierId}`, queryParams).pipe(
+ switchMap(() => this._filesService.loadAll(dossierId, routerPath)),
+ );
}
}
diff --git a/apps/red-ui/src/app/services/router-history.service.ts b/apps/red-ui/src/app/services/router-history.service.ts
index bbd98d1c2..5e87a8b82 100644
--- a/apps/red-ui/src/app/services/router-history.service.ts
+++ b/apps/red-ui/src/app/services/router-history.service.ts
@@ -10,7 +10,7 @@ export class RouterHistoryService {
constructor(private readonly _router: Router) {
this._router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe((event: NavigationEnd) => {
- if (event.url.startsWith('/main/dossiers') && !event.url.includes('/search')) {
+ if (event.url.startsWith('/main/dossiers')) {
this._lastDossiersScreen = event.url;
}
});
diff --git a/apps/red-ui/src/app/tokens.ts b/apps/red-ui/src/app/tokens.ts
index ca56f808f..ce8f6a595 100644
--- a/apps/red-ui/src/app/tokens.ts
+++ b/apps/red-ui/src/app/tokens.ts
@@ -2,3 +2,6 @@ import { InjectionToken } from '@angular/core';
export const BASE_HREF: InjectionToken = new InjectionToken('BASE_HREF');
export const DOSSIER_ID: InjectionToken = new InjectionToken('DOSSIER_ID');
+
+export const ACTIVE_DOSSIERS_SERVICE = new InjectionToken('Active dossiers service');
+export const ARCHIVED_DOSSIERS_SERVICE = new InjectionToken('Archived dossiers service');
diff --git a/apps/red-ui/src/app/modules/dossier/translations/file-status-translations.ts b/apps/red-ui/src/app/translations/file-status-translations.ts
similarity index 100%
rename from apps/red-ui/src/app/modules/dossier/translations/file-status-translations.ts
rename to apps/red-ui/src/app/translations/file-status-translations.ts
diff --git a/apps/red-ui/src/assets/i18n/de.json b/apps/red-ui/src/assets/i18n/de.json
index c647382a9..2c978b185 100644
--- a/apps/red-ui/src/assets/i18n/de.json
+++ b/apps/red-ui/src/assets/i18n/de.json
@@ -424,16 +424,16 @@
"warning": ""
},
"confirm-delete-file-attribute": {
- "cancel": "{type, select, single{Attribut} bulk{Attribute} other{}} behalten",
- "delete": "{type, select, single{Attribut} bulk{Attribute} other{}} löschen",
+ "cancel": "{count, plural, one{Attribut} other{Attribute}} behalten",
+ "delete": "{count, plural, one{Attribut} other{Attribute}} löschen",
+ "file-impacted-documents": "Alle Dokumente {count, plural, one{ist} other{sind}} betroffen",
"dossier-impacted-documents": "",
- "dossier-lost-details": "",
- "file-impacted-documents": "Alle Dokumente {type, select, single{ist} bulk{sind} other{}} betroffen",
"file-lost-details": "Alle in die Dokumente eingegebenen Daten gehen verloren",
- "impacted-report": "{count}",
- "title": "{type, select, single{{name}} bulk{Datei-Attribute} other{}} löschen",
+ "dossier-lost-details": "",
+ "title": "{count, plural, one{{name}} other{Datei-Attribute}} löschen",
"toast-error": "Bitte bestätigen Sie, dass Ihnen die Konsequenzen dieser Aktion bewusst sind!",
- "warning": "Achtung: Diese Aktion kann nicht rückgängig gemacht werden!"
+ "warning": "Achtung: Diese Aktion kann nicht rückgängig gemacht werden!",
+ "impacted-report": "{reportsCount}"
},
"confirm-delete-users": {
"cancel": "{usersCount, plural, one{Benutzer} other{Benutzer}} behalten",
@@ -1730,6 +1730,7 @@
},
"filters": {
"by-dossier": "Nach Dossier filtern",
+ "only-active": "",
"search-placeholder": "Dossiername..."
},
"missing": "Fehlt",
@@ -1739,7 +1740,7 @@
"table-header": "{length} {length, plural, one{Suchergebnis} other{Suchergebnisse}}"
},
"search": {
- "entire-platform": "ganze Plattform",
+ "active-dossiers": "ganze Plattform",
"placeholder": "Nach Dokumenten oder Dokumenteninhalt suchen",
"this-dossier": "in diesem Dossier"
},
diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json
index b9efdb33b..cbccfdcba 100644
--- a/apps/red-ui/src/assets/i18n/en.json
+++ b/apps/red-ui/src/assets/i18n/en.json
@@ -315,6 +315,23 @@
"text-highlight": "Highlight"
},
"annotations": "Annotations",
+ "archived-dossiers-listing": {
+ "no-data": {
+ "title": "No archived dossiers."
+ },
+ "no-match": {
+ "title": "No archived dossiers match your current filters."
+ },
+ "table-col-names": {
+ "dossier-status": "Dossier Status",
+ "last-modified": "Archived Time",
+ "name": "Name",
+ "owner": "Owner"
+ },
+ "table-header": {
+ "title": "{length} Archived {length, plural, one{Dossier} other{Dossiers}}"
+ }
+ },
"assign-dossier-owner": {
"dialog": {
"approvers": "Approvers",
@@ -411,6 +428,17 @@
}
},
"configurations": "Configurations",
+ "confirm-archive-dossier": {
+ "archive": "Archive Dossier",
+ "cancel": "Cancel",
+ "checkbox": {
+ "documents": "All documents will be archived and cannot be put back to active"
+ },
+ "details": "Restoring an archived dossier is not possible anymore, once it got archived.",
+ "title": "Archive {dossierName}",
+ "toast-error": "Please confirm that you understand the ramifications of your action!",
+ "warning": "Are you sure you want to archive the dossier?"
+ },
"confirm-delete-dossier-state": {
"cancel": "Cancel",
"delete": "Delete",
@@ -424,14 +452,14 @@
"warning": "The {name} status is assigned to {count} {count, plural, one{Dossier} other{Dossiers}}."
},
"confirm-delete-file-attribute": {
- "cancel": "Keep {type, select, single{Attribute} bulk{Attributes} other{}}",
- "delete": "Delete {type, select, single{Attribute} bulk{Attributes} other{}}",
+ "cancel": "Keep {count, plural, one{Attribute} other{Attributes}}",
+ "delete": "Delete {count, plural, one{Attribute} other{Attributes}}",
"dossier-impacted-documents": "All dossiers based on this template will be affected",
"dossier-lost-details": "All values for this attribute will be lost",
- "file-impacted-documents": "All documents {type, select, single{it is} bulk{they are} other{}} used on will be impacted",
+ "file-impacted-documents": "All documents {count, plural, one{it is} other{they are}} used on will be impacted",
"file-lost-details": "All inputted details on the documents will be lost",
- "impacted-report": "{count} reports use the placeholder for this attribute and need to be adjusted",
- "title": "Delete {type, select, single{{name}} bulk{File Attributes} other{}}",
+ "impacted-report": "{reportsCount} reports use the placeholder for this attribute and need to be adjusted",
+ "title": "Delete {count, plural, one{{name}} other{File Attributes}}",
"toast-error": "Please confirm that you understand the ramifications of your action!",
"warning": "Warning: this cannot be undone!"
},
@@ -692,6 +720,11 @@
},
"dossier-listing": {
"add-new": "New Dossier",
+ "archive": {
+ "action": "Archive Dossier",
+ "archive-failed": "Failed to archive dossier {dossierName}!",
+ "archive-succeeded": "Successfully archived dossier {dossierName}."
+ },
"delete": {
"action": "Delete Dossier",
"delete-failed": "Failed to delete dossier: {dossierName}"
@@ -802,7 +835,7 @@
},
"no-data": {
"action": "Upload Document",
- "title": "There are no documents yet."
+ "title": "There are no documents in this dossier."
},
"no-match": {
"title": "No documents match your current filters."
@@ -1226,6 +1259,7 @@
"reanalyse-notification": "This document was not processed with the latest rule/dictionary set. Analyze now to get updated annotations.",
"redacted": "Preview",
"redacted-tooltip": "Redaction preview shows only redactions. Consider this a preview for the final redacted version. This view is only available if the file has no pending changes & doesn't require a reanalysis",
+ "reset-filters": "",
"standard": "Standard",
"standard-tooltip": "Standard Workload view shows all hints, redactions, recommendations & suggestions. This view allows editing.",
"tabs": {
@@ -1601,6 +1635,7 @@
"processing": "Processing"
},
"readonly": "Read only",
+ "readonly-archived": "Read only (archived)",
"recategorize-image-dialog": {
"actions": {
"cancel": "Cancel",
@@ -1730,6 +1765,7 @@
},
"filters": {
"by-dossier": "Filter by Dossier",
+ "only-active": "Active dossiers only",
"search-placeholder": "Dossier name..."
},
"missing": "Missing",
@@ -1739,7 +1775,8 @@
"table-header": "{length} search {length, plural, one{result} other{results}}"
},
"search": {
- "entire-platform": "across all dossiers",
+ "active-dossiers": "documents in active dossiers",
+ "all-dossiers": "all documents",
"placeholder": "Search documents...",
"this-dossier": "in this dossier"
},
@@ -1768,6 +1805,7 @@
},
"top-bar": {
"navigation-items": {
+ "archived-dossiers": "Archived Dossiers",
"back": "Back",
"dossiers": "Active Dossiers",
"my-account": {
diff --git a/apps/red-ui/src/assets/icons/general/archive.svg b/apps/red-ui/src/assets/icons/general/archive.svg
new file mode 100644
index 000000000..0d4414f55
--- /dev/null
+++ b/apps/red-ui/src/assets/icons/general/archive.svg
@@ -0,0 +1,9 @@
+
+
diff --git a/libs/red-domain/src/index.ts b/libs/red-domain/src/index.ts
index 2858caf86..8220a575e 100644
--- a/libs/red-domain/src/index.ts
+++ b/libs/red-domain/src/index.ts
@@ -20,4 +20,5 @@ export * from './lib/signature';
export * from './lib/legal-basis';
export * from './lib/dossier-stats';
export * from './lib/dossier-state';
+export * from './lib/trash-dossier';
export * from './lib/text-highlight';
diff --git a/libs/red-domain/src/lib/dossiers/dossier-changes.ts b/libs/red-domain/src/lib/dossiers/dossier-changes.ts
new file mode 100644
index 000000000..a63406f4c
--- /dev/null
+++ b/libs/red-domain/src/lib/dossiers/dossier-changes.ts
@@ -0,0 +1,11 @@
+export interface IDossierChange {
+ readonly dossierChanges: boolean;
+ readonly dossierId: string;
+ readonly fileChanges: boolean;
+}
+
+export type IDossierChanges = readonly IDossierChange[];
+
+export interface IChangesDetails {
+ readonly dossierChanges: IDossierChanges;
+}
diff --git a/libs/red-domain/src/lib/dossiers/dossier.model.ts b/libs/red-domain/src/lib/dossiers/dossier.model.ts
index e2a79d550..fba166c8a 100644
--- a/libs/red-domain/src/lib/dossiers/dossier.model.ts
+++ b/libs/red-domain/src/lib/dossiers/dossier.model.ts
@@ -1,9 +1,9 @@
-import { IListable, List } from '@iqser/common-ui';
+import { IListable, IRouterPath, List } from '@iqser/common-ui';
import { IDossier } from './dossier';
-import { DossierStatus } from './types';
+import { DossierStatus, DossierStatuses } from './types';
import { DownloadFileType } from '../shared';
-export class Dossier implements IDossier, IListable {
+export class Dossier implements IDossier, IListable, IRouterPath {
readonly dossierId: string;
readonly dossierTemplateId: string;
readonly ownerId: string;
@@ -22,9 +22,10 @@ export class Dossier implements IDossier, IListable {
readonly status: DossierStatus;
readonly watermarkEnabled: boolean;
readonly watermarkPreviewEnabled: boolean;
+ readonly archivedTime: string;
readonly hasReviewers: boolean;
- constructor(dossier: IDossier) {
+ constructor(dossier: IDossier, readonly routerPath: string) {
this.dossierId = dossier.dossierId;
this.approverIds = dossier.approverIds;
this.date = dossier.date;
@@ -43,6 +44,7 @@ export class Dossier implements IDossier, IListable {
this.status = dossier.status;
this.watermarkEnabled = dossier.watermarkEnabled;
this.watermarkPreviewEnabled = dossier.watermarkPreviewEnabled;
+ this.archivedTime = dossier.archivedTime;
this.hasReviewers = !!this.memberIds && this.memberIds.length > 1;
}
@@ -51,13 +53,17 @@ export class Dossier implements IDossier, IListable {
}
get routerLink(): string {
- return `/main/dossiers/${this.dossierId}`;
+ return `/main/${this.routerPath}/${this.dossierId}`;
}
get searchKey(): string {
return this.dossierName;
}
+ get isActive(): boolean {
+ return this.status === DossierStatuses.ACTIVE;
+ }
+
hasMember(memberId: string): boolean {
return !!this.memberIds && this.memberIds.indexOf(memberId) >= 0;
}
diff --git a/libs/red-domain/src/lib/dossiers/dossier.ts b/libs/red-domain/src/lib/dossiers/dossier.ts
index 1ff219033..59689a5d9 100644
--- a/libs/red-domain/src/lib/dossiers/dossier.ts
+++ b/libs/red-domain/src/lib/dossiers/dossier.ts
@@ -21,4 +21,5 @@ export interface IDossier {
readonly status: DossierStatus;
readonly watermarkEnabled: boolean;
readonly watermarkPreviewEnabled: boolean;
+ readonly archivedTime: string;
}
diff --git a/libs/red-domain/src/lib/dossiers/index.ts b/libs/red-domain/src/lib/dossiers/index.ts
index 5f42d97f7..994f4274c 100644
--- a/libs/red-domain/src/lib/dossiers/index.ts
+++ b/libs/red-domain/src/lib/dossiers/index.ts
@@ -2,3 +2,4 @@ export * from './dossier';
export * from './dossier.request';
export * from './dossier.model';
export * from './types';
+export * from './dossier-changes';
diff --git a/libs/red-domain/src/lib/dossiers/types.ts b/libs/red-domain/src/lib/dossiers/types.ts
index 34a2be57c..f667390d4 100644
--- a/libs/red-domain/src/lib/dossiers/types.ts
+++ b/libs/red-domain/src/lib/dossiers/types.ts
@@ -1,6 +1,7 @@
export const DossierStatuses = {
ACTIVE: 'ACTIVE',
DELETED: 'DELETED',
+ ARCHIVED: 'ARCHIVED',
} as const;
export type DossierStatus = keyof typeof DossierStatuses;
diff --git a/libs/red-domain/src/lib/files/file.model.ts b/libs/red-domain/src/lib/files/file.model.ts
index ef369ad6f..60b2911f1 100644
--- a/libs/red-domain/src/lib/files/file.model.ts
+++ b/libs/red-domain/src/lib/files/file.model.ts
@@ -1,10 +1,10 @@
-import { Entity } from '@iqser/common-ui';
+import { Entity, IRouterPath } from '@iqser/common-ui';
import { StatusSorter } from '../shared';
import { isProcessingStatuses, ProcessingFileStatus, ProcessingFileStatuses, WorkflowFileStatus, WorkflowFileStatuses } from './types';
import { IFile } from './file';
import { FileAttributes } from '../file-attributes';
-export class File extends Entity implements IFile {
+export class File extends Entity implements IFile, IRouterPath {
readonly added?: string;
readonly allManualRedactionsApplied: boolean;
readonly analysisDuration?: number;
@@ -59,7 +59,7 @@ export class File extends Entity implements IFile {
readonly canBeOpened: boolean;
readonly canBeOCRed: boolean;
- constructor(file: IFile, readonly reviewerName: string) {
+ constructor(file: IFile, readonly reviewerName: string, readonly routerPath: string) {
super(file);
this.added = file.added;
this.allManualRedactionsApplied = !!file.allManualRedactionsApplied;
@@ -129,6 +129,6 @@ export class File extends Entity implements IFile {
}
get routerLink(): string | undefined {
- return this.canBeOpened ? `/main/dossiers/${this.dossierId}/file/${this.fileId}` : undefined;
+ return this.canBeOpened ? `/main/${this.routerPath}/${this.dossierId}/file/${this.fileId}` : undefined;
}
}
diff --git a/libs/red-domain/src/lib/search/matched-document.ts b/libs/red-domain/src/lib/search/matched-document.ts
index e6b8b5f3b..65d41ddce 100644
--- a/libs/red-domain/src/lib/search/matched-document.ts
+++ b/libs/red-domain/src/lib/search/matched-document.ts
@@ -1,10 +1,12 @@
import { IMatchedSection } from './matched-section';
import { List } from '@iqser/common-ui';
+import { DossierStatus } from '../dossiers';
export interface IMatchedDocument {
containsAllMatchedSections?: boolean;
dossierId?: string;
dossierTemplateId?: string;
+ dossierStatus?: DossierStatus;
fileId?: string;
highlights?: { [key: string]: List };
matchedSections?: List;
diff --git a/libs/red-domain/src/lib/search/search-input.ts b/libs/red-domain/src/lib/search/search-input.ts
index 597b6391f..dc0786951 100644
--- a/libs/red-domain/src/lib/search/search-input.ts
+++ b/libs/red-domain/src/lib/search/search-input.ts
@@ -1,6 +1,8 @@
import { List } from '@iqser/common-ui';
+import { DossierStatus } from '../dossiers';
export interface ISearchInput {
readonly query: string;
readonly dossierIds?: List;
+ readonly dossierStatus?: List;
}
diff --git a/libs/red-domain/src/lib/search/search-list-item.ts b/libs/red-domain/src/lib/search/search-list-item.ts
index 1b5006a56..0091366b8 100644
--- a/libs/red-domain/src/lib/search/search-list-item.ts
+++ b/libs/red-domain/src/lib/search/search-list-item.ts
@@ -1,7 +1,9 @@
import { IListable, List } from '@iqser/common-ui';
+import { DossierStatus } from '../dossiers';
export interface ISearchListItem extends IListable {
readonly dossierId: string;
+ readonly dossierStatus: DossierStatus;
readonly filename: string;
readonly assignee: string;
readonly unmatched: List | null;
diff --git a/libs/red-domain/src/lib/search/search.request.ts b/libs/red-domain/src/lib/search/search.request.ts
index 104e9b097..e9ae5c23e 100644
--- a/libs/red-domain/src/lib/search/search.request.ts
+++ b/libs/red-domain/src/lib/search/search.request.ts
@@ -1,11 +1,13 @@
import { List } from '@iqser/common-ui';
+import { DossierStatus } from '../dossiers';
export interface ISearchRequest {
- dossierIds?: List;
- dossierTemplateIds?: List;
- fileId?: string;
- page?: number;
- pageSize?: number;
- queryString?: string;
- returnSections?: boolean;
+ readonly dossierIds?: List;
+ readonly dossierTemplateIds?: List;
+ readonly dossierStatus?: List;
+ readonly fileId?: string;
+ readonly page?: number;
+ readonly pageSize?: number;
+ readonly queryString?: string;
+ readonly returnSections?: boolean;
}
diff --git a/libs/red-domain/src/lib/shared/breadcrumb-types.ts b/libs/red-domain/src/lib/shared/breadcrumb-types.ts
index 3366c25a2..74ed8ae69 100644
--- a/libs/red-domain/src/lib/shared/breadcrumb-types.ts
+++ b/libs/red-domain/src/lib/shared/breadcrumb-types.ts
@@ -1,7 +1,8 @@
-export type BreadcrumbType = 'main' | 'dossier' | 'file';
+export type BreadcrumbType = 'main' | 'dossier' | 'file' | 'archive';
export const BreadcrumbTypes = {
main: 'main' as BreadcrumbType,
dossier: 'dossier' as BreadcrumbType,
file: 'file' as BreadcrumbType,
+ archive: 'archive' as BreadcrumbType,
};
diff --git a/libs/red-domain/src/lib/trash-dossier/index.ts b/libs/red-domain/src/lib/trash-dossier/index.ts
new file mode 100644
index 000000000..a90f3227f
--- /dev/null
+++ b/libs/red-domain/src/lib/trash-dossier/index.ts
@@ -0,0 +1 @@
+export * from './trash-dossier.model';
diff --git a/libs/red-domain/src/lib/trash-dossier/trash-dossier.model.ts b/libs/red-domain/src/lib/trash-dossier/trash-dossier.model.ts
new file mode 100644
index 000000000..aca8adf9d
--- /dev/null
+++ b/libs/red-domain/src/lib/trash-dossier/trash-dossier.model.ts
@@ -0,0 +1,68 @@
+import { getLeftDateTime, List } from '@iqser/common-ui';
+import { DownloadFileType } from '../shared';
+import { DossierStatus, IDossier } from '../dossiers';
+
+export class TrashDossier implements IDossier {
+ readonly dossierId: string;
+ readonly dossierTemplateId: string;
+ readonly ownerId: string;
+ readonly memberIds: List;
+ readonly approverIds: List;
+ readonly reportTemplateIds: List;
+ readonly dossierName: string;
+ readonly dossierStatusId: string;
+ readonly date: string;
+ readonly dueDate?: string;
+ readonly description?: string;
+ readonly downloadFileTypes?: List;
+ readonly hardDeletedTime?: string;
+ readonly softDeletedTime?: string;
+ readonly startDate?: string;
+ readonly status: DossierStatus;
+ readonly watermarkEnabled: boolean;
+ readonly watermarkPreviewEnabled: boolean;
+ readonly archivedTime: string;
+ readonly hasReviewers: boolean;
+ readonly canRestore: boolean;
+
+ constructor(dossier: IDossier, readonly restoreDate: string, readonly canHardDelete: boolean) {
+ this.dossierId = dossier.dossierId;
+ this.approverIds = dossier.approverIds;
+ this.date = dossier.date;
+ this.description = dossier.description;
+ this.dossierName = dossier.dossierName;
+ this.dossierStatusId = dossier.dossierStatusId;
+ this.dossierTemplateId = dossier.dossierTemplateId;
+ this.downloadFileTypes = dossier.downloadFileTypes;
+ this.dueDate = dossier.dueDate;
+ this.hardDeletedTime = dossier.hardDeletedTime;
+ this.memberIds = dossier.memberIds;
+ this.ownerId = dossier.ownerId;
+ this.reportTemplateIds = dossier.reportTemplateIds;
+ this.softDeletedTime = dossier.softDeletedTime;
+ this.startDate = dossier.startDate;
+ this.status = dossier.status;
+ this.watermarkEnabled = dossier.watermarkEnabled;
+ this.watermarkPreviewEnabled = dossier.watermarkPreviewEnabled;
+ this.archivedTime = dossier.archivedTime;
+ this.hasReviewers = !!this.memberIds && this.memberIds.length > 1;
+
+ this.canRestore = this.#canRestoreDossier(restoreDate);
+ // Because of migrations, for some this is not set
+ this.softDeletedTime = dossier.softDeletedTime || '-';
+ }
+
+ get id(): string {
+ return this.dossierId;
+ }
+
+ get searchKey(): string {
+ return this.dossierName;
+ }
+
+ #canRestoreDossier(restoreDate: string): boolean {
+ const { daysLeft, hoursLeft, minutesLeft } = getLeftDateTime(restoreDate);
+
+ return daysLeft >= 0 && hoursLeft >= 0 && minutesLeft > 0;
+ }
+}