From 0a05eb9a6c18c0f65dd55a1916fb411952b776c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adina=20=C8=9Aeudan?= Date: Tue, 22 Feb 2022 22:31:31 +0200 Subject: [PATCH] Archived dossier overview WIP --- .../src/app/guards/dossier-files-guard.ts | 12 +-- .../modules/archive/archive-routing.module.ts | 14 ++++ .../src/app/modules/archive/archive.module.ts | 3 +- .../edit-dossier-dictionary.component.ts | 4 +- ...edit-dossier-download-package.component.ts | 6 +- .../edit-dossier-dialog.component.ts | 6 +- .../edit-dossier-team.component.ts | 6 +- .../edit-dossier-general-info.component.ts | 8 +- .../dossier/dossiers-routing.module.ts | 3 + .../app/modules/dossier/dossiers.module.ts | 34 +------- .../dossier-details.component.ts | 12 +-- .../file-workload/file-workload.component.ts | 10 +-- .../dossier-overview/config.service.ts | 6 +- .../dossier-overview.module.ts | 3 - .../dossier-overview-screen.component.ts | 30 ++++--- .../file-preview-providers.ts | 4 + .../dossier/shared/shared-dossiers.module.ts | 39 +++++++-- .../src/app/services/breadcrumbs.service.ts | 19 +++-- .../active-dossiers.service.ts | 84 ++----------------- .../archived-dossiers.service.ts | 18 ++-- .../dossiers.service.provider.ts | 15 ++++ .../entity-services/dossiers.service.ts | 83 ++++++++++++++++-- .../src/app/services/permissions.service.ts | 22 +++-- .../src/lib/dossiers/dossier.model.ts | 4 +- 24 files changed, 247 insertions(+), 198 deletions(-) create mode 100644 apps/red-ui/src/app/services/entity-services/dossiers.service.provider.ts diff --git a/apps/red-ui/src/app/guards/dossier-files-guard.ts b/apps/red-ui/src/app/guards/dossier-files-guard.ts index d8ecf32fc..3594a6897 100644 --- a/apps/red-ui/src/app/guards/dossier-files-guard.ts +++ b/apps/red-ui/src/app/guards/dossier-files-guard.ts @@ -1,15 +1,15 @@ -import { Injectable } from '@angular/core'; +import { Injectable, Injector, ProviderToken } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router'; -import { ActiveDossiersService } from '@services/entity-services/active-dossiers.service'; import { FilesMapService } from '@services/entity-services/files-map.service'; import { FilesService } from '@services/entity-services/files.service'; import { firstValueFrom } from 'rxjs'; import { DOSSIER_ID } from '@utils/constants'; +import { DossiersService } from '@services/entity-services/dossiers.service'; @Injectable({ providedIn: 'root' }) export class DossierFilesGuard implements CanActivate { constructor( - private readonly _dossiersService: ActiveDossiersService, + private readonly _injector: Injector, private readonly _filesMapService: FilesMapService, private readonly _filesService: FilesService, private readonly _router: Router, @@ -17,9 +17,11 @@ export class DossierFilesGuard implements CanActivate { async canActivate(route: ActivatedRouteSnapshot): Promise { const dossierId = route.paramMap.get(DOSSIER_ID); + const token: ProviderToken = route.data.dossiersService; + const dossiersService: DossiersService = this._injector.get(token); - if (!this._dossiersService.has(dossierId)) { - await this._router.navigate(['/main', 'dossiers']); + if (!dossiersService.has(dossierId)) { + await this._router.navigate(['/main', dossiersService.routerPath]); return false; } diff --git a/apps/red-ui/src/app/modules/archive/archive-routing.module.ts b/apps/red-ui/src/app/modules/archive/archive-routing.module.ts index 2f9d6f7f3..36e88ed79 100644 --- a/apps/red-ui/src/app/modules/archive/archive-routing.module.ts +++ b/apps/red-ui/src/app/modules/archive/archive-routing.module.ts @@ -2,6 +2,10 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { BreadcrumbTypes } from '@red/domain'; import { ArchivedDossiersScreenComponent } from './screens/archived-dossiers-screen/archived-dossiers-screen.component'; +import { DOSSIER_ID } from '@utils/constants'; +import { CompositeRouteGuard } from '@iqser/common-ui'; +import { ARCHIVED_DOSSIERS_SERVICE } from '../../tokens'; +import { DossierFilesGuard } from '../../guards/dossier-files-guard'; const routes: Routes = [ { @@ -10,6 +14,16 @@ const routes: Routes = [ component: ArchivedDossiersScreenComponent, data: { breadcrumbs: [BreadcrumbTypes.archive] }, }, + { + path: `:${DOSSIER_ID}`, + canActivate: [CompositeRouteGuard], + data: { + routeGuards: [DossierFilesGuard], + breadcrumbs: [BreadcrumbTypes.archive, BreadcrumbTypes.dossier], + dossiersService: ARCHIVED_DOSSIERS_SERVICE, + }, + loadChildren: () => import('../dossier/screens/dossier-overview/dossier-overview.module').then(m => m.DossierOverviewModule), + }, ]; @NgModule({ diff --git a/apps/red-ui/src/app/modules/archive/archive.module.ts b/apps/red-ui/src/app/modules/archive/archive.module.ts index 30b524bb7..a49f1e1b3 100644 --- a/apps/red-ui/src/app/modules/archive/archive.module.ts +++ b/apps/red-ui/src/app/modules/archive/archive.module.ts @@ -2,7 +2,6 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ArchivedDossiersScreenComponent } from './screens/archived-dossiers-screen/archived-dossiers-screen.component'; import { ArchiveRoutingModule } from './archive-routing.module'; -import { CommonUiModule } from '@iqser/common-ui'; import { TableItemComponent } from './components/table-item/table-item.component'; import { ConfigService } from './services/config.service'; import { SharedModule } from '../shared/shared.module'; @@ -12,7 +11,7 @@ const screens = [ArchivedDossiersScreenComponent]; @NgModule({ declarations: [...components, ...screens], - imports: [CommonModule, ArchiveRoutingModule, SharedModule, CommonUiModule], + imports: [CommonModule, ArchiveRoutingModule, SharedModule], providers: [ConfigService], }) export class ArchiveModule {} diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/dictionary/edit-dossier-dictionary.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/dictionary/edit-dossier-dictionary.component.ts index 68a0f725f..298bc00af 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/dictionary/edit-dossier-dictionary.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/dictionary/edit-dossier-dictionary.component.ts @@ -5,10 +5,10 @@ import { PermissionsService } from '@services/permissions.service'; import { DictionaryManagerComponent } from '@shared/components/dictionary-manager/dictionary-manager.component'; import { DictionaryService } from '@shared/services/dictionary.service'; import { CircleButtonTypes, LoadingService, Toaster } from '@iqser/common-ui'; -import { ActiveDossiersService } from '@services/entity-services/active-dossiers.service'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { FormBuilder, FormGroup } from '@angular/forms'; import { firstValueFrom } from 'rxjs'; +import { DossiersService } from '@services/entity-services/dossiers.service'; @Component({ selector: 'redaction-edit-dossier-dictionary', @@ -27,7 +27,7 @@ export class EditDossierDictionaryComponent implements EditDossierSectionInterfa @ViewChild(DictionaryManagerComponent, { static: false }) private readonly _dictionaryManager: DictionaryManagerComponent; constructor( - private readonly _activeDossiersService: ActiveDossiersService, + private readonly _dossiersService: DossiersService, private readonly _dictionaryService: DictionaryService, private readonly _permissionsService: PermissionsService, private readonly _loadingService: LoadingService, diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/download-package/edit-dossier-download-package.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/download-package/edit-dossier-download-package.component.ts index cdbae3661..ba0ff34b2 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/download-package/edit-dossier-download-package.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/download-package/edit-dossier-download-package.component.ts @@ -3,10 +3,10 @@ import { Dossier, DownloadFileType, IReportTemplate } from '@red/domain'; import { FormBuilder, FormGroup } from '@angular/forms'; import { EditDossierSaveResult, EditDossierSectionInterface } from '../edit-dossier-section.interface'; import { downloadTypesTranslations } from '../../../../../translations/download-types-translations'; -import { ActiveDossiersService } from '@services/entity-services/active-dossiers.service'; import { ReportTemplateService } from '@services/report-template.service'; import { PermissionsService } from '@services/permissions.service'; import { firstValueFrom } from 'rxjs'; +import { DossiersService } from '@services/entity-services/dossiers.service'; @Component({ selector: 'redaction-edit-dossier-download-package', @@ -26,7 +26,7 @@ export class EditDossierDownloadPackageComponent implements OnInit, EditDossierS @Input() dossier: Dossier; constructor( - private readonly _activeDossiersService: ActiveDossiersService, + private readonly _dossiersService: DossiersService, private readonly _reportTemplateController: ReportTemplateService, private readonly _formBuilder: FormBuilder, private readonly _permissionsService: PermissionsService, @@ -88,7 +88,7 @@ export class EditDossierDownloadPackageComponent implements OnInit, EditDossierS reportTemplateIds: this.form.get('reportTemplateIds').value, }; - const updatedDossier = await firstValueFrom(this._activeDossiersService.createOrUpdate(dossier)); + const updatedDossier = await firstValueFrom(this._dossiersService.createOrUpdate(dossier)); return { success: !!updatedDossier }; } diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.ts index be9d8491e..4322e548d 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.ts @@ -10,18 +10,20 @@ import { EditDossierAttributesComponent } from './attributes/edit-dossier-attrib import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { EditDossierDeletedDocumentsComponent } from './deleted-documents/edit-dossier-deleted-documents.component'; -import { ActiveDossiersService } from '@services/entity-services/active-dossiers.service'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; import { EditDossierTeamComponent } from './edit-dossier-team/edit-dossier-team.component'; import { PermissionsService } from '@services/permissions.service'; import { UserService } from '@services/user.service'; +import { DossiersService } from '@services/entity-services/dossiers.service'; +import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider'; type Section = 'dossierInfo' | 'downloadPackage' | 'dossierDictionary' | 'members' | 'dossierAttributes' | 'deletedDocuments'; @Component({ templateUrl: './edit-dossier-dialog.component.html', styleUrls: ['./edit-dossier-dialog.component.scss'], + providers: [dossiersServiceProvider], }) export class EditDossierDialogComponent extends BaseDialogComponent implements AfterViewInit { readonly navItems: { key: Section; title?: string; sideNavTitle?: string }[]; @@ -40,7 +42,7 @@ export class EditDossierDialogComponent extends BaseDialogComponent implements A constructor( private readonly _toaster: Toaster, - private readonly _activeDossiersService: ActiveDossiersService, + private readonly _activeDossiersService: DossiersService, private readonly _changeRef: ChangeDetectorRef, private readonly _loadingService: LoadingService, private readonly _permissionsService: PermissionsService, diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.ts index c1943c05f..f51df9547 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.ts @@ -1,12 +1,12 @@ import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit } from '@angular/core'; import { UserService } from '@services/user.service'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { ActiveDossiersService } from '@services/entity-services/active-dossiers.service'; import { Dossier, IDossierRequest } from '@red/domain'; import { AutoUnsubscribe } from '@iqser/common-ui'; import { EditDossierSaveResult, EditDossierSectionInterface } from '../edit-dossier-section.interface'; import { BehaviorSubject, firstValueFrom } from 'rxjs'; import { PermissionsService } from '@services/permissions.service'; +import { DossiersService } from '@services/entity-services/dossiers.service'; @Component({ selector: 'redaction-edit-dossier-team', @@ -28,7 +28,7 @@ export class EditDossierTeamComponent extends AutoUnsubscribe implements EditDos constructor( readonly userService: UserService, private readonly _formBuilder: FormBuilder, - private readonly _activeDossiersService: ActiveDossiersService, + private readonly _dossiersService: DossiersService, private readonly _permissionsService: PermissionsService, ) { super(); @@ -91,7 +91,7 @@ export class EditDossierTeamComponent extends AutoUnsubscribe implements EditDos ownerId: this.selectedOwnerId, } as IDossierRequest; - const updatedDossier = await firstValueFrom(this._activeDossiersService.createOrUpdate(dossier)); + const updatedDossier = await firstValueFrom(this._dossiersService.createOrUpdate(dossier)); return { success: !!updatedDossier }; } diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.ts index 9b3f48eee..7bf60d961 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.ts @@ -10,13 +10,13 @@ import { MatDialogRef } from '@angular/material/dialog'; import { EditDossierDialogComponent } from '../edit-dossier-dialog.component'; import { ConfirmationDialogInput, IconButtonTypes, TitleColors, Toaster } from '@iqser/common-ui'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; -import { ActiveDossiersService } from '@services/entity-services/active-dossiers.service'; import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service'; import { DossierStatsService } from '@services/entity-services/dossier-stats.service'; import { firstValueFrom } from 'rxjs'; import { DossierStateService } from '@services/entity-services/dossier-state.service'; import { DOSSIER_TEMPLATE_ID } from '@utils/constants'; import { TranslateService } from '@ngx-translate/core'; +import { DossiersService } from '@services/entity-services/dossiers.service'; @Component({ selector: 'redaction-edit-dossier-general-info', @@ -38,7 +38,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti readonly permissionsService: PermissionsService, private readonly _dossierStateService: DossierStateService, private readonly _dossierTemplatesService: DossierTemplatesService, - private readonly _activeDossiersService: ActiveDossiersService, + private readonly _dossiersService: DossiersService, private readonly _dossierStatsService: DossierStatsService, private readonly _formBuilder: FormBuilder, private readonly _dialogService: DossiersDialogService, @@ -120,7 +120,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti dossierStatusId: this.form.get('dossierStatusId').value, } as IDossierRequest; - const updatedDossier = await firstValueFrom(this._activeDossiersService.createOrUpdate(dossier)); + const updatedDossier = await firstValueFrom(this._dossiersService.createOrUpdate(dossier)); return { success: !!updatedDossier }; } @@ -139,7 +139,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti }, }); this._dialogService.openDialog('confirm', null, data, async () => { - await firstValueFrom(this._activeDossiersService.delete(this.dossier)); + await firstValueFrom(this._dossiersService.delete(this.dossier)); this._editDossierDialogRef.close(); this._router.navigate(['main', 'dossiers']).then(() => this.#notifyDossierDeleted()); }); diff --git a/apps/red-ui/src/app/modules/dossier/dossiers-routing.module.ts b/apps/red-ui/src/app/modules/dossier/dossiers-routing.module.ts index 58f455fb1..74723fcf3 100644 --- a/apps/red-ui/src/app/modules/dossier/dossiers-routing.module.ts +++ b/apps/red-ui/src/app/modules/dossier/dossiers-routing.module.ts @@ -6,6 +6,7 @@ import { DossierFilesGuard } from '@guards/dossier-files-guard'; import { CompositeRouteGuard } from '@iqser/common-ui'; import { BreadcrumbTypes } from '@red/domain'; import { DOSSIER_ID, FILE_ID } from '@utils/constants'; +import { ACTIVE_DOSSIERS_SERVICE } from '../../tokens'; const routes: Routes = [ { @@ -18,6 +19,7 @@ const routes: Routes = [ data: { routeGuards: [DossierFilesGuard], breadcrumbs: [BreadcrumbTypes.main, BreadcrumbTypes.dossier], + dossiersService: ACTIVE_DOSSIERS_SERVICE, }, loadChildren: () => import('./screens/dossier-overview/dossier-overview.module').then(m => m.DossierOverviewModule), }, @@ -27,6 +29,7 @@ const routes: Routes = [ data: { routeGuards: [DossierFilesGuard, FilePreviewGuard], breadcrumbs: [BreadcrumbTypes.main, BreadcrumbTypes.dossier, BreadcrumbTypes.file], + dossiersService: ACTIVE_DOSSIERS_SERVICE, }, loadChildren: () => import('./screens/file-preview-screen/file-preview.module').then(m => m.FilePreviewModule), }, diff --git a/apps/red-ui/src/app/modules/dossier/dossiers.module.ts b/apps/red-ui/src/app/modules/dossier/dossiers.module.ts index 2ff709a8a..d11543139 100644 --- a/apps/red-ui/src/app/modules/dossier/dossiers.module.ts +++ b/apps/red-ui/src/app/modules/dossier/dossiers.module.ts @@ -1,65 +1,33 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -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 { ManualAnnotationDialogComponent } from './dialogs/manual-redaction-dialog/manual-annotation-dialog.component'; import { ForceAnnotationDialogComponent } from './dialogs/force-redaction-dialog/force-annotation-dialog.component'; import { RemoveAnnotationsDialogComponent } from './dialogs/remove-annotations-dialog/remove-annotations-dialog.component'; -import { DocumentInfoDialogComponent } from './dialogs/document-info-dialog/document-info-dialog.component'; import { SharedModule } from '@shared/shared.module'; import { DossiersRoutingModule } from './dossiers-routing.module'; import { FileUploadDownloadModule } from '@upload-download/file-upload-download.module'; -import { DossiersDialogService } from './services/dossiers-dialog.service'; -import { ManualAnnotationService } from './services/manual-annotation.service'; -import { AnnotationProcessingService } from './services/annotation-processing.service'; -import { EditDossierDialogComponent } from './dialogs/edit-dossier-dialog/edit-dossier-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 { ChangeLegalBasisDialogComponent } from './dialogs/change-legal-basis-dialog/change-legal-basis-dialog.component'; import { RecategorizeImageDialogComponent } from './dialogs/recategorize-image-dialog/recategorize-image-dialog.component'; -import { EditDossierAttributesComponent } from './dialogs/edit-dossier-dialog/attributes/edit-dossier-attributes.component'; import { SearchScreenComponent } from './screens/search-screen/search-screen.component'; -import { EditDossierDeletedDocumentsComponent } from './dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component'; import { OverlayModule } from '@angular/cdk/overlay'; import { SharedDossiersModule } from './shared/shared-dossiers.module'; import { ResizeAnnotationDialogComponent } from './dialogs/resize-annotation-dialog/resize-annotation-dialog.component'; -import { EditDossierTeamComponent } from './dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component'; -import { ConfirmArchiveDossierDialogComponent } from './dialogs/confirm-archive-dossier-dialog/confirm-archive-dossier-dialog.component'; const screens = [SearchScreenComponent]; const dialogs = [ - AddDossierDialogComponent, - EditDossierDialogComponent, - ConfirmArchiveDossierDialogComponent, ManualAnnotationDialogComponent, ForceAnnotationDialogComponent, RemoveAnnotationsDialogComponent, ResizeAnnotationDialogComponent, - DocumentInfoDialogComponent, - AssignReviewerApproverDialogComponent, ChangeLegalBasisDialogComponent, RecategorizeImageDialogComponent, ]; -const components = [ - EditDossierGeneralInfoComponent, - EditDossierDownloadPackageComponent, - EditDossierDictionaryComponent, - EditDossierAttributesComponent, - EditDossierTeamComponent, - EditDossierDeletedDocumentsComponent, - - ...screens, - ...dialogs, -]; - -const services = [DossiersDialogService, ManualAnnotationService, AnnotationProcessingService]; +const components = [...screens, ...dialogs]; @NgModule({ declarations: [...components], - providers: [...services], imports: [CommonModule, SharedModule, SharedDossiersModule, FileUploadDownloadModule, DossiersRoutingModule, OverlayModule], }) export class DossiersModule {} diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details/dossier-details.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details/dossier-details.component.ts index b6ede1e8d..15e995f4f 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details/dossier-details.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details/dossier-details.component.ts @@ -6,7 +6,6 @@ import { FilterService, ProgressBarConfigModel, shareLast, Toaster } from '@iqse import { workflowFileStatusTranslations } from '../../../../translations/file-status-translations'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { Dossier, DossierAttributeWithValue, DossierStats, IDossierRequest, StatusSorter, User } from '@red/domain'; -import { ActiveDossiersService } from '@services/entity-services/active-dossiers.service'; import { ActivatedRoute } from '@angular/router'; import { firstValueFrom, Observable } from 'rxjs'; import { DossierStatsService } from '@services/entity-services/dossier-stats.service'; @@ -14,6 +13,7 @@ import { map, pluck, switchMap } from 'rxjs/operators'; import { DossiersDialogService } from '../../../../services/dossiers-dialog.service'; import { FilesService } from '@services/entity-services/files.service'; import { DOSSIER_ID } from '@utils/constants'; +import { DossiersService } from '@services/entity-services/dossiers.service'; @Component({ selector: 'redaction-dossier-details', @@ -37,19 +37,19 @@ export class DossierDetailsComponent { statusConfig$: Observable; constructor( - readonly activeDossiersService: ActiveDossiersService, readonly translateChartService: TranslateChartService, readonly filterService: FilterService, + private readonly _dossiersService: DossiersService, private readonly _changeDetectorRef: ChangeDetectorRef, private readonly _userService: UserService, private readonly _filesService: FilesService, private readonly _dossierStatsService: DossierStatsService, private readonly _toaster: Toaster, private readonly _dialogService: DossiersDialogService, - activatedRoute: ActivatedRoute, + private readonly _activatedRoute: ActivatedRoute, ) { - this.dossierId = activatedRoute.snapshot.paramMap.get(DOSSIER_ID); - this.dossier$ = this.activeDossiersService.getEntityChanged$(this.dossierId).pipe(shareLast()); + this.dossierId = _activatedRoute.snapshot.paramMap.get(DOSSIER_ID); + this.dossier$ = this._dossiersService.getEntityChanged$(this.dossierId).pipe(shareLast()); this.dossierStats$ = this.dossier$.pipe( pluck('dossierId'), switchMap(dossierId => this._dossierStatsService.watch$(dossierId)), @@ -65,7 +65,7 @@ export class DossierDetailsComponent { async assignOwner(user: User | string, dossier: Dossier) { const owner = typeof user === 'string' ? this._userService.find(user) : user; const dossierRequest: IDossierRequest = { ...dossier, ownerId: owner.id }; - await firstValueFrom(this.activeDossiersService.createOrUpdate(dossierRequest)); + await firstValueFrom(this._dossiersService.createOrUpdate(dossierRequest)); const ownerName = this._userService.getNameForId(owner.id); const dossierName = dossier.dossierName; diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/table-item/file-workload/file-workload.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/table-item/file-workload/file-workload.component.ts index 021efffce..0bc097f52 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/table-item/file-workload/file-workload.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/table-item/file-workload/file-workload.component.ts @@ -1,8 +1,8 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { File } from '@red/domain'; -import { ActiveDossiersService } from '@services/entity-services/active-dossiers.service'; import { UserService } from '@services/user.service'; import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service'; +import { DossiersService } from '@services/entity-services/dossiers.service'; @Component({ selector: 'redaction-file-workload', @@ -16,7 +16,7 @@ export class FileWorkloadComponent { constructor( readonly userService: UserService, private readonly _dictionariesMapService: DictionariesMapService, - private readonly _activeDossiersService: ActiveDossiersService, + private readonly _dossiersService: DossiersService, ) {} get suggestionColor() { @@ -44,9 +44,7 @@ export class FileWorkloadComponent { } private _getDictionaryColor(type: string) { - return this._dictionariesMapService.getDictionaryColor( - type, - this._activeDossiersService.find(this.file.dossierId).dossierTemplateId, - ); + const dossierTemplateId = this._dossiersService.find(this.file.dossierId).dossierTemplateId; + return this._dictionariesMapService.getDictionaryColor(type, dossierTemplateId); } } diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/config.service.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/config.service.ts index ee750234f..5d5ad670c 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/config.service.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/config.service.ts @@ -164,7 +164,7 @@ export class ConfigService { checkedRequiredFilters: () => NestedFilter[], checkedNotRequiredFilters: () => NestedFilter[], ) { - const allDistinctWorkflowFileStatuses = new Set(); + const allDistinctWorkflowFileStatuses = new Set(); const allDistinctPeople = new Set(); const allDistinctAddedDates = new Set(); const allDistinctNeedsWork = new Set(); @@ -328,7 +328,9 @@ export class ConfigService { } _recentlyModifiedChecker = (file: File) => - moment(file.lastUpdated).add(this._appConfigService.values.RECENT_PERIOD_IN_HOURS, 'hours').isAfter(moment()); + moment(file.lastUpdated) + .add(this._appConfigService.values.RECENT_PERIOD_IN_HOURS as number, 'hours') + .isAfter(moment()); _assignedToMeChecker = (file: File) => file.assignee === this._userService.currentUser.id; diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/dossier-overview.module.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/dossier-overview.module.ts index 0b9582d02..752e28458 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/dossier-overview.module.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/dossier-overview.module.ts @@ -9,7 +9,6 @@ import { DossierOverviewBulkActionsComponent } from './components/bulk-actions/d import { DossierDetailsComponent } from './components/dossier-details/dossier-details.component'; import { DossierDetailsStatsComponent } from './components/dossier-details-stats/dossier-details-stats.component'; import { TableItemComponent } from './components/table-item/table-item.component'; -import { ConfigService } from './config.service'; import { SharedDossiersModule } from '../../shared/shared-dossiers.module'; import { FileWorkloadComponent } from './components/table-item/file-workload/file-workload.component'; import { FileStatsComponent } from './components/file-stats/file-stats.component'; @@ -18,7 +17,6 @@ import { ScreenHeaderComponent } from './components/screen-header/screen-header. import { ViewModeSelectionComponent } from './components/view-mode-selection/view-mode-selection.component'; import { FileNameColumnComponent } from './components/table-item/file-name-column/file-name-column.component'; import { DateColumnComponent } from './components/table-item/date-column/date-column.component'; -import { BulkActionsService } from './services/bulk-actions.service'; const routes: Routes = [ { @@ -46,7 +44,6 @@ const routes: Routes = [ FileNameColumnComponent, DateColumnComponent, ], - providers: [ConfigService, BulkActionsService], imports: [RouterModule.forChild(routes), CommonModule, SharedModule, SharedDossiersModule, IqserIconsModule, TranslateModule], }) export class DossierOverviewModule {} diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.ts index c62cee103..a8e088dab 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.ts @@ -38,18 +38,26 @@ import { PermissionsService } from '@services/permissions.service'; import { ActivatedRoute, Router } from '@angular/router'; import { FileAttributesService } from '@services/entity-services/file-attributes.service'; import { ConfigService } from '../config.service'; -import { ActiveDossiersService } from '@services/entity-services/active-dossiers.service'; import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service'; import { LongPressEvent } from '@shared/directives/long-press.directive'; import { UserPreferenceService } from '@services/user-preference.service'; import { FilesMapService } from '@services/entity-services/files-map.service'; import { FilesService } from '@services/entity-services/files.service'; import { DOSSIER_ID } from '@utils/constants'; +import { BulkActionsService } from '../services/bulk-actions.service'; +import { DossiersService } from '@services/entity-services/dossiers.service'; +import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider'; @Component({ templateUrl: './dossier-overview-screen.component.html', styleUrls: ['./dossier-overview-screen.component.scss'], - providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => DossierOverviewScreenComponent) }], + providers: [ + ...DefaultListingServices, + ConfigService, + BulkActionsService, + { provide: ListingComponent, useExisting: forwardRef(() => DossierOverviewScreenComponent) }, + dossiersServiceProvider, + ], changeDetection: ChangeDetectionStrategy.OnPush, }) export class DossierOverviewScreenComponent extends ListingComponent implements OnInit, OnDestroy, OnAttach { @@ -77,8 +85,8 @@ export class DossierOverviewScreenComponent extends ListingComponent imple constructor( protected readonly _injector: Injector, private readonly _router: Router, + private readonly _dossiersService: DossiersService, private readonly _loadingService: LoadingService, - private readonly _activeDossiersService: ActiveDossiersService, private readonly _dossierTemplatesService: DossierTemplatesService, private readonly _fileUploadService: FileUploadService, private readonly _filesService: FilesService, @@ -89,16 +97,16 @@ export class DossierOverviewScreenComponent extends ListingComponent imple private readonly _userPreferenceService: UserPreferenceService, private readonly _fileMapService: FilesMapService, private readonly _errorService: ErrorService, + private readonly _route: ActivatedRoute, readonly permissionsService: PermissionsService, readonly configService: ConfigService, - readonly activatedRoute: ActivatedRoute, ) { super(_injector); - this.dossierId = activatedRoute.snapshot.paramMap.get(DOSSIER_ID); - this.dossier$ = this._activeDossiersService + this.dossierId = _route.snapshot.paramMap.get(DOSSIER_ID); + this.dossier$ = this._dossiersService .getEntityChanged$(this.dossierId) .pipe(tap(dossier => (this.dossierTemplateId = dossier.dossierTemplateId))); - this.currentDossier = this._activeDossiersService.find(this.dossierId); + this.currentDossier = this._dossiersService.find(this.dossierId); this._updateFileAttributes(); } @@ -133,7 +141,7 @@ export class DossierOverviewScreenComponent extends ListingComponent imple this._computeAllFilters(); }); - this.addSubscription = this._activeDossiersService.dossierFileChanges$ + this.addSubscription = this._dossiersService.dossierFileChanges$ .pipe( filter(dossierId => dossierId === this.dossierId), switchMap(dossierId => this._filesService.loadAll(dossierId)), @@ -176,7 +184,7 @@ export class DossierOverviewScreenComponent extends ListingComponent imple @HostListener('drop', ['$event']) onDrop(event: DragEvent): void { - const currentDossier = this._activeDossiersService.find(this.dossierId); + const currentDossier = this._dossiersService.find(this.dossierId); handleFileDrop(event, currentDossier, this._uploadFiles.bind(this)); } @@ -192,7 +200,7 @@ export class DossierOverviewScreenComponent extends ListingComponent imple } private _setRemovableSubscriptions(): void { - this.addActiveScreenSubscription = this._activeDossiersService + this.addActiveScreenSubscription = this._dossiersService .getEntityDeleted$(this.dossierId) .pipe(tap(() => this._handleDeletedDossier())) .subscribe(); @@ -214,7 +222,7 @@ export class DossierOverviewScreenComponent extends ListingComponent imple } private _loadEntitiesFromState() { - this.currentDossier = this._activeDossiersService.find(this.dossierId); + this.currentDossier = this._dossiersService.find(this.dossierId); this._computeAllFilters(); } 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 037a36118..a6de718d8 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,8 @@ 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'; export const filePreviewScreenProviders = [ FilterService, @@ -24,4 +26,6 @@ export const filePreviewScreenProviders = [ FilePreviewStateService, PdfViewerDataService, AnnotationReferencesService, + ManualAnnotationService, + AnnotationProcessingService, ]; 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 085e242e1..1e48f5758 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,16 +2,43 @@ 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 { ConfirmArchiveDossierDialogComponent } from '../dialogs/confirm-archive-dossier-dialog/confirm-archive-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'; -const components = [FileActionsComponent]; +const components = [ + FileActionsComponent, + DocumentInfoDialogComponent, + EditDossierGeneralInfoComponent, + EditDossierDownloadPackageComponent, + EditDossierDictionaryComponent, + EditDossierAttributesComponent, + EditDossierTeamComponent, + EditDossierDeletedDocumentsComponent, +]; +const dialogs = [ + EditDossierDialogComponent, + AddDossierDialogComponent, + ConfirmArchiveDossierDialogComponent, + 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/services/breadcrumbs.service.ts b/apps/red-ui/src/app/services/breadcrumbs.service.ts index 2602d59e5..6679e7255 100644 --- a/apps/red-ui/src/app/services/breadcrumbs.service.ts +++ b/apps/red-ui/src/app/services/breadcrumbs.service.ts @@ -1,13 +1,14 @@ -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 { ActiveDossiersService } from './entity-services/active-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/entity-services/dossiers.service'; +import { dossiersServiceResolver } from '@services/entity-services/dossiers.service.provider'; export type RouterLinkActiveOptions = { exact: boolean } | IsActiveMatchOptions; export type BreadcrumbDisplayType = 'text' | 'dropdown'; @@ -34,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 _activeDossiersService: ActiveDossiersService, ) { this.breadcrumbs$ = this._store$.asObservable(); @@ -52,6 +53,10 @@ 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')), @@ -135,12 +140,13 @@ export class BreadcrumbsService { } private _addDossierBreadcrumb(route: ActivatedRouteSnapshot): void { + const dossiersService = this._dossiersService; const dossierId = route.paramMap.get(DOSSIER_ID); this._append({ - name$: this._activeDossiersService.getEntityChanged$(dossierId).pipe(pluck('dossierName')), + name$: dossiersService.getEntityChanged$(dossierId).pipe(pluck('dossierName')), type: 'text' as BreadcrumbDisplayType, options: { - routerLink: ['/main', 'dossiers', dossierId], + routerLink: ['/main', dossiersService.routerPath, dossierId], routerLinkActiveOptions: { exact: true }, clamp: true, }, @@ -150,11 +156,12 @@ export class BreadcrumbsService { 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')), type: 'text' as BreadcrumbDisplayType, options: { - routerLink: ['/main', 'dossiers', dossierId, 'file', fileId], + routerLink: ['/main', dossiersService.routerPath, dossierId, 'file', fileId], clamp: true, }, }); diff --git a/apps/red-ui/src/app/services/entity-services/active-dossiers.service.ts b/apps/red-ui/src/app/services/entity-services/active-dossiers.service.ts index 0b22e665d..7f842c2c5 100644 --- a/apps/red-ui/src/app/services/entity-services/active-dossiers.service.ts +++ b/apps/red-ui/src/app/services/entity-services/active-dossiers.service.ts @@ -1,10 +1,9 @@ import { Injectable, Injector } from '@angular/core'; -import { List, QueryParam, RequiredParam, Toaster, Validate } from '@iqser/common-ui'; -import { Dossier, DossierStats, IDossier, IDossierRequest } from '@red/domain'; -import { catchError, filter, map, mapTo, pluck, switchMap, tap } from 'rxjs/operators'; -import { firstValueFrom, forkJoin, Observable, of, Subject, throwError, timer } from 'rxjs'; +import { List, QueryParam, RequiredParam, Validate } from '@iqser/common-ui'; +import { Dossier, IDossier } from '@red/domain'; +import { catchError, switchMap, tap } from 'rxjs/operators'; +import { firstValueFrom, Observable, of, timer } from 'rxjs'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; -import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http'; import { CHANGED_CHECK_INTERVAL } from '@utils/constants'; import { DossiersService } from '@services/entity-services/dossiers.service'; @@ -13,78 +12,21 @@ export interface IDossiersStats { totalAnalyzedPages: number; } -interface DossierChange { - readonly dossierChanges: boolean; - readonly dossierId: string; - readonly fileChanges: boolean; -} - -type DossierChanges = readonly DossierChange[]; - -interface ChangesDetails { - readonly dossierChanges: DossierChanges; -} - -const DOSSIER_EXISTS_MSG = _('add-dossier-dialog.errors.dossier-already-exists'); -const GENERIC_MSG = _('add-dossier-dialog.errors.generic'); - @Injectable({ providedIn: 'root', }) export class ActiveDossiersService extends DossiersService { - readonly dossierFileChanges$ = new Subject(); - - constructor(private readonly _toaster: Toaster, protected readonly _injector: Injector) { - super(_injector, 'dossier'); + 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)), + tap(changes => this._emitFileChanges(changes)), ) .subscribe(); } - loadOnlyChanged(): Observable { - const removeIfNotFound = (id: string) => - catchError((error: HttpErrorResponse) => { - if (error.status === HttpStatusCode.NotFound) { - this.remove(id); - return of([]); - } - return throwError(() => error); - }); - - const load = (changes: DossierChanges) => - 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 { - const body = { value: this._lastCheckedForChanges.get('root') ?? '0' }; - return this._post(body, `${this._defaultModelPath}/changes/details`).pipe( - filter(changes => changes.dossierChanges.length > 0), - ); - } - - @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), - ); - } - getDeleted(): Promise { return firstValueFrom(this.getAll('deleted-dossiers')); } @@ -118,16 +60,4 @@ export class ActiveDossiersService extends DossiersService { #removeDossiers(dossiers: Dossier[]): void { this.setEntities(this.all.filter(dossier => !dossiers.find(d => dossier.id === d.id))); } - - private _load(id: string, queryParams?: List): Observable { - return super._getOne([id], this._defaultModelPath, queryParams).pipe( - map(entity => new Dossier(entity)), - 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)); - } } diff --git a/apps/red-ui/src/app/services/entity-services/archived-dossiers.service.ts b/apps/red-ui/src/app/services/entity-services/archived-dossiers.service.ts index 50709d769..dc41a4462 100644 --- a/apps/red-ui/src/app/services/entity-services/archived-dossiers.service.ts +++ b/apps/red-ui/src/app/services/entity-services/archived-dossiers.service.ts @@ -1,5 +1,4 @@ import { Injectable, Injector } from '@angular/core'; -import { Toaster } from '@iqser/common-ui'; import { Dossier } from '@red/domain'; import { catchError, tap } from 'rxjs/operators'; import { Observable, of } from 'rxjs'; @@ -9,12 +8,8 @@ import { DossiersService } from '@services/entity-services/dossiers.service'; @Injectable({ providedIn: 'root' }) export class ArchivedDossiersService extends DossiersService { - constructor( - protected readonly _injector: Injector, - private readonly _toaster: Toaster, - private readonly _activeDossiersService: ActiveDossiersService, - ) { - super(_injector, 'archived-dossiers'); + constructor(protected readonly _injector: Injector, private readonly _activeDossiersService: ActiveDossiersService) { + super(_injector, 'archived-dossiers', 'archive'); } archive(dossiers: Dossier[]): Observable { @@ -26,14 +21,13 @@ export class ArchivedDossiersService extends DossiersService { dossiers.map(d => d.id), `${this._defaultModelPath}/archive`, ).pipe( - tap(() => this.#removeDossiers(dossiers)), + tap(() => this.#removeFromActiveDossiers(dossiers)), catchError(showToast), ); } - #removeDossiers(dossiers: Dossier[]): void { - this._activeDossiersService.setEntities( - this._activeDossiersService.all.filter(dossier => !dossiers.find(d => dossier.id === d.id)), - ); + #removeFromActiveDossiers(dossiers: Dossier[]): void { + const remainingEntities = this._activeDossiersService.all.filter(dossier => !dossiers.find(d => dossier.id === d.id)); + this._activeDossiersService.setEntities(remainingEntities); } } 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..20f342608 --- /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.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/dossiers.service.ts b/apps/red-ui/src/app/services/entity-services/dossiers.service.ts index cd2c67a19..cf383c3dc 100644 --- a/apps/red-ui/src/app/services/entity-services/dossiers.service.ts +++ b/apps/red-ui/src/app/services/entity-services/dossiers.service.ts @@ -1,25 +1,84 @@ -import { EntitiesService, List, mapEach, shareLast } from '@iqser/common-ui'; -import { Dossier, IDossier } from '@red/domain'; -import { combineLatest, Observable } from 'rxjs'; -import { filter, map, mapTo, switchMap, tap } from 'rxjs/operators'; +import { EntitiesService, List, mapEach, QueryParam, RequiredParam, shareLast, Toaster, Validate } from '@iqser/common-ui'; +import { Dossier, DossierStats, IDossier, IDossierRequest } from '@red/domain'; +import { combineLatest, forkJoin, Observable, of, Subject, throwError } from 'rxjs'; +import { catchError, filter, map, mapTo, pluck, switchMap, tap } from 'rxjs/operators'; import { Injector } from '@angular/core'; import { DossierStateService } from './dossier-state.service'; import { DossierStatsService } from './dossier-stats.service'; import { IDossiersStats } from '@services/entity-services/active-dossiers.service'; +import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http'; +import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; + +interface DossierChange { + readonly dossierChanges: boolean; + readonly dossierId: string; + readonly fileChanges: boolean; +} + +type DossierChanges = readonly DossierChange[]; + +interface ChangesDetails { + readonly dossierChanges: DossierChanges; +} + +const DOSSIER_EXISTS_MSG = _('add-dossier-dialog.errors.dossier-already-exists'); +const GENERIC_MSG = _('add-dossier-dialog.errors.generic'); 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); - protected constructor(protected readonly _injector: Injector, protected readonly _path: string) { + protected constructor(protected readonly _injector: Injector, protected readonly _path: string, readonly routerPath: string) { super(_injector, Dossier, _path); } + @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, 'dossier').pipe( + switchMap(newDossier => this.loadAll().pipe(map(() => this.find(newDossier.dossierId)))), + catchError(showToast), + ); + } + + loadOnlyChanged(): Observable { + const removeIfNotFound = (id: string) => + catchError((error: HttpErrorResponse) => { + if (error.status === HttpStatusCode.NotFound) { + this.remove(id); + return of([]); + } + return throwError(() => error); + }); + + const load = (changes: DossierChanges) => + 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 { + const body = { value: this._lastCheckedForChanges.get('root') ?? '0' }; + return this._post(body, `${this._defaultModelPath}/changes/details`).pipe( + filter(changes => changes.dossierChanges.length > 0), + ); + } + loadAll(): Observable { const dossierIds = (dossiers: Dossier[]) => dossiers.map(d => d.id); return this.getAll().pipe( - mapEach(entity => new Dossier(entity)), + 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))), @@ -27,6 +86,18 @@ export abstract class DossiersService extends EntitiesService ); } + protected _emitFileChanges(dossierChanges: DossierChanges): void { + dossierChanges.filter(change => change.fileChanges).forEach(change => this.dossierFileChanges$.next(change.dossierId)); + } + + private _load(id: string, queryParams?: List): Observable { + return super._getOne([id], this._defaultModelPath, queryParams).pipe( + map(entity => new Dossier(entity, 'dossiers')), + tap(dossier => this.replace(dossier)), + switchMap(dossier => this._dossierStatsService.getFor([dossier.dossierId])), + ); + } + #computeStats(entities: List): IDossiersStats { let totalAnalyzedPages = 0; const totalPeople = new Set(); diff --git a/apps/red-ui/src/app/services/permissions.service.ts b/apps/red-ui/src/app/services/permissions.service.ts index 257e618c1..3298ccb55 100644 --- a/apps/red-ui/src/app/services/permissions.service.ts +++ b/apps/red-ui/src/app/services/permissions.service.ts @@ -1,13 +1,21 @@ -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 { ActiveDossiersService } from './entity-services/active-dossiers.service'; +import { DossiersService } from '@services/entity-services/dossiers.service'; +import { ActivatedRoute } from '@angular/router'; +import { dossiersServiceResolver } from '@services/entity-services/dossiers.service.provider'; -@Injectable({ - providedIn: 'root', -}) +@Injectable({ providedIn: 'root' }) export class PermissionsService { - constructor(private readonly _userService: UserService, private readonly _activeDossiersService: ActiveDossiersService) {} + constructor( + private readonly _userService: UserService, + private readonly _route: ActivatedRoute, + private readonly _injector: Injector, + ) {} + + private get _dossiersService(): DossiersService { + return dossiersServiceResolver(this._injector); + } isReviewerOrApprover(file: File): boolean { const dossier = this._getDossier(file); @@ -228,6 +236,6 @@ export class PermissionsService { } private _getDossier(file: File): Dossier { - return this._activeDossiersService.find(file.dossierId); + return this._dossiersService.find(file.dossierId); } } diff --git a/libs/red-domain/src/lib/dossiers/dossier.model.ts b/libs/red-domain/src/lib/dossiers/dossier.model.ts index b95caf827..e6304b02c 100644 --- a/libs/red-domain/src/lib/dossiers/dossier.model.ts +++ b/libs/red-domain/src/lib/dossiers/dossier.model.ts @@ -25,7 +25,7 @@ export class Dossier implements IDossier, IListable { 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; @@ -53,7 +53,7 @@ export class Dossier implements IDossier, IListable { } get routerLink(): string { - return `/main/dossiers/${this.dossierId}`; + return `/main/${this.routerPath}/${this.dossierId}`; } get searchKey(): string {