diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.ts index ad3464181..7bbbd4ff5 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.ts @@ -1,13 +1,13 @@ import { Component, Inject } from '@angular/core'; -import { StatusControllerService } from '@redaction/red-ui-http'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { AppStateService } from '@state/app-state.service'; import { UserService } from '@services/user.service'; import { Toaster } from '@iqser/common-ui'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { File } from '@models/file/file'; -import { Dossier } from '../../../../state/model/dossier'; +import { Dossier } from '@state/model/dossier'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; +import { FilesService } from '../../services/files.service'; class DialogData { mode: 'approver' | 'reviewer'; @@ -28,8 +28,8 @@ export class AssignReviewerApproverDialogComponent { readonly userService: UserService, private readonly _toaster: Toaster, private readonly _formBuilder: FormBuilder, - private readonly _statusControllerService: StatusControllerService, private readonly _appStateService: AppStateService, + private readonly _filesService: FilesService, private readonly _dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) readonly data: DialogData ) { @@ -69,16 +69,17 @@ export class AssignReviewerApproverDialogComponent { const selectedUser = this.selectedSingleUser; if (this.data.mode === 'reviewer') { - await this._statusControllerService - .setFileReviewerForList( + console.log('assign reviewer'); + await this._filesService + .setReviewerFor( this.data.files.map(f => f.fileId), this._appStateService.activeDossierId, selectedUser ) .toPromise(); } else { - await this._statusControllerService - .setStatusUnderApprovalForList( + await this._filesService + .setUnderApprovalFor( this.data.files.map(f => f.fileId), selectedUser, this._appStateService.activeDossierId diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.ts index 6e588c51b..920cff75e 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.ts @@ -10,7 +10,7 @@ import { SortingOrders, TableColumnConfig } from '@iqser/common-ui'; -import { FileManagementControllerService, IFile, StatusControllerService } from '@redaction/red-ui-http'; +import { FileManagementControllerService, IFile } from '@redaction/red-ui-http'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import * as moment from 'moment'; import { ConfigService } from '@services/config.service'; @@ -20,6 +20,7 @@ import { distinctUntilChanged, map } from 'rxjs/operators'; import { ConfirmationDialogInput, TitleColors } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component'; import { DossiersDialogService } from '../../../services/dossiers-dialog.service'; import { AppStateService } from '@state/app-state.service'; +import { FilesService } from '../../../services/files.service'; interface FileListItem extends IFile, IListable { readonly canRestore: boolean; @@ -52,9 +53,9 @@ export class EditDossierDeletedDocumentsComponent extends ListingComponent implements } private _toListItem({ dossierId, fileId, unmatchedTerms, highlights }: MatchedDocument): ListItem { - const fileWrapper = this._appStateService.getFileById(dossierId, fileId); - if (!fileWrapper) { + const file = this._appStateService.getFileById(dossierId, fileId); + if (!file) { return undefined; } @@ -160,11 +160,11 @@ export class SearchScreenComponent extends ListingComponent implements dossierId, unmatched: unmatchedTerms || null, highlights, - status: fileWrapper.status, - numberOfPages: fileWrapper.numberOfPages, + status: file.status, + numberOfPages: file.numberOfPages, dossierName: this._appStateService.getDossierById(dossierId).dossierName, - filename: fileWrapper.filename, - searchKey: fileWrapper.filename, + filename: file.filename, + searchKey: file.filename, routerLink: `/main/dossiers/${dossierId}/file/${fileId}` }; } diff --git a/apps/red-ui/src/app/modules/dossier/services/dossiers.service.ts b/apps/red-ui/src/app/modules/dossier/services/dossiers.service.ts index 220ccb14c..f0d4113db 100644 --- a/apps/red-ui/src/app/modules/dossier/services/dossiers.service.ts +++ b/apps/red-ui/src/app/modules/dossier/services/dossiers.service.ts @@ -1,45 +1,61 @@ -import { Injectable, Injector } from "@angular/core"; -import { IDossier } from "@redaction/red-ui-http"; -import { EntitiesService, FilterService, SearchService } from "@iqser/common-ui"; -import { Dossier } from "@state/model/dossier"; +import { Injectable, Injector } from '@angular/core'; +import { IDossier } from '@redaction/red-ui-http'; +import { EntitiesService, List, QueryParam } from '@iqser/common-ui'; +import { Dossier } from '@state/model/dossier'; +import { map } from 'rxjs/operators'; +import { TEMPORARY_INJECTOR } from './injector'; +import { Observable } from 'rxjs'; -/** - * This should be removed when refactoring is done - * @param injector - * @constructor - */ -const TEMPORARY_INJECTOR = injector => - Injector.create({ - providers: [ - { provide: FilterService, useClass: FilterService }, - { provide: SearchService, useClass: SearchService } - ], - parent: injector - }); +export interface IDossiersStats { + totalPeople: number; + totalAnalyzedPages: number; +} @Injectable({ providedIn: 'root' }) export class DossiersService extends EntitiesService { + readonly stats$ = this.all$.pipe(map(entities => this._computeStats(entities))); + constructor(protected readonly _injector: Injector) { super(TEMPORARY_INJECTOR(_injector), 'dossier'); } + get(): Observable; + get(dossierId: string): Observable; + get(dossierId?: string): Observable { + return dossierId ? super._getOne([dossierId]) : super.getAll(); + } + createOrUpdate(dossier: IDossier): Promise { - return this.post(dossier).toPromise(); + return this._post(dossier).toPromise(); } getDeleted(): Promise { - return this.getAll('deleted-dossiers').toPromise(); + return this.getAll('deleted-dossiers').toPromise(); } - restore(dossierIds: Array): Promise { - const body = dossierIds.map<[string, string]>(id => ['dossierId', id]); - return this.post(body, 'deleted-dossiers/restore').toPromise(); + restore(dossierIds: List): Promise { + return this._post(dossierIds, 'deleted-dossiers/restore').toPromise(); } - hardDelete(dossierIds: Array): Promise { - const body = dossierIds.map<[string, string]>(id => ['dossierId', id]); + hardDelete(dossierIds: List): Promise { + const body = dossierIds.map(id => ({ key: 'dossierId', value: id })); return this.delete(body, 'deleted-dossiers/hard-delete').toPromise(); } + + private _computeStats(entities: List): IDossiersStats { + let totalAnalyzedPages = 0; + const totalPeople = new Set(); + + entities.forEach(dossier => { + dossier.memberIds?.forEach(m => totalPeople.add(m)); + totalAnalyzedPages += dossier.totalNumberOfPages; + }); + + return { + totalPeople: totalPeople.size, + totalAnalyzedPages + }; + } } diff --git a/apps/red-ui/src/app/modules/dossier/services/file-action.service.ts b/apps/red-ui/src/app/modules/dossier/services/file-action.service.ts index c147d2ddc..58bce9fc0 100644 --- a/apps/red-ui/src/app/modules/dossier/services/file-action.service.ts +++ b/apps/red-ui/src/app/modules/dossier/services/file-action.service.ts @@ -1,12 +1,13 @@ import { Injectable } from '@angular/core'; import { AppStateService } from '@state/app-state.service'; import { UserService } from '@services/user.service'; -import { ReanalysisControllerService, StatusControllerService } from '@redaction/red-ui-http'; +import { ReanalysisControllerService } from '@redaction/red-ui-http'; import { File } from '@models/file/file'; import { PermissionsService } from '@services/permissions.service'; import { DossiersDialogService } from './dossiers-dialog.service'; import { ConfirmationDialogInput } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; +import { FilesService } from './files.service'; import { Observable } from 'rxjs'; @Injectable() @@ -15,7 +16,7 @@ export class FileActionService { private readonly _dialogService: DossiersDialogService, private readonly _permissionsService: PermissionsService, private readonly _userService: UserService, - private readonly _statusControllerService: StatusControllerService, + private readonly _fileService: FilesService, private readonly _reanalysisControllerService: ReanalysisControllerService, private readonly _appStateService: AppStateService ) {} @@ -54,7 +55,7 @@ export class FileActionService { approverId = this._appStateService.activeDossier.approverIds[0]; } - return this._statusControllerService.setStatusUnderApprovalForList( + return this._fileService.setUnderApprovalFor( files.map(f => f.fileId), approverId, this._appStateService.activeDossierId @@ -62,14 +63,14 @@ export class FileActionService { } setFilesApproved(files: File[]) { - return this._statusControllerService.setStatusApprovedForList( + return this._fileService.setApprovedFor( files.map(f => f.fileId), this._appStateService.activeDossierId ); } setFilesUnderReview(files: File[]) { - return this._statusControllerService.setStatusUnderReviewForList( + return this._fileService.setUnderReviewFor( files.map(f => f.fileId), this._appStateService.activeDossierId ); @@ -98,8 +99,8 @@ export class FileActionService { } private async _assignReviewerToCurrentUser(files: File[], callback?: Function) { - await this._statusControllerService - .setFileReviewerForList( + await this._fileService + .setReviewerFor( files.map(f => f.fileId), this._appStateService.activeDossierId, this._userService.currentUser.id diff --git a/apps/red-ui/src/app/modules/dossier/services/files.service.ts b/apps/red-ui/src/app/modules/dossier/services/files.service.ts new file mode 100644 index 000000000..5b1f481f5 --- /dev/null +++ b/apps/red-ui/src/app/modules/dossier/services/files.service.ts @@ -0,0 +1,79 @@ +import { Injectable, Injector } from '@angular/core'; +import { EntitiesService, List, RequiredParam, Validate } from '@iqser/common-ui'; +import { IFile } from '@redaction/red-ui-http'; +import { File } from '@models/file/file'; +import { TEMPORARY_INJECTOR } from './injector'; +import { Observable } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class FilesService extends EntitiesService { + constructor(protected readonly _injector: Injector) { + super(TEMPORARY_INJECTOR(_injector), 'status'); + } + + /** + * Gets the status for all files. + */ + get(): Observable; + /** + * Gets the status for a file from a dossier. + */ + get(dossierId: string, fileId: string): Observable; + get(dossierId?: string, fileId?: string) { + if (dossierId && fileId) { + return super._getOne([dossierId, fileId]); + } + + return super.getAll(); + } + + getFor(dossierId: string): Observable; + getFor(dossierIds: List): Observable>; + getFor(args: string | List) { + if (typeof args === 'string') { + return super.getAll(`${this._defaultModelPath}/${args}`); + } + + return this._post>(args); + } + + @Validate() + setUnderApprovalFor(@RequiredParam() body: List, @RequiredParam() approverId: string, @RequiredParam() dossierId: string) { + const url = `${this._defaultModelPath}/underapproval/${dossierId}/bulk`; + return this._post(body, url, [{ key: 'approverId', value: approverId }]); + } + + /** + * Assigns a reviewer for a list of files. + */ + @Validate() + setReviewerFor(@RequiredParam() filesIds: List, @RequiredParam() dossierId: string, @RequiredParam() reviewerId: string) { + return this._post(filesIds, `${this._defaultModelPath}/${dossierId}/bulk/${reviewerId}`); + } + + /** + * 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`); + } + + /** + * Sets the status UNDER_REVIEW for a list of files. + */ + @Validate() + setUnderReviewFor(@RequiredParam() filesIds: List, @RequiredParam() dossierId: string) { + return this._post(filesIds, `${this._defaultModelPath}/underreview/${dossierId}/bulk`); + } + + /** + * Gets the deleted files for a dossier. + */ + @Validate() + getDeletedFilesFor(@RequiredParam() dossierId: string): Observable { + return this.getAll(`${this._defaultModelPath}/softdeleted/${dossierId}`); + } +} diff --git a/apps/red-ui/src/app/modules/dossier/services/injector.ts b/apps/red-ui/src/app/modules/dossier/services/injector.ts new file mode 100644 index 000000000..3100fb6e4 --- /dev/null +++ b/apps/red-ui/src/app/modules/dossier/services/injector.ts @@ -0,0 +1,16 @@ +import { Injector } from "@angular/core"; +import { FilterService, SearchService } from "@iqser/common-ui"; + +/** + * This should be removed when refactoring is done + * @param injector + * @constructor + */ +export const TEMPORARY_INJECTOR = injector => + Injector.create({ + providers: [ + { provide: FilterService, useClass: FilterService }, + { provide: SearchService, useClass: SearchService } + ], + parent: injector + }); diff --git a/apps/red-ui/src/app/state/app-state.service.ts b/apps/red-ui/src/app/state/app-state.service.ts index 174af45ab..ae4cf7b6f 100644 --- a/apps/red-ui/src/app/state/app-state.service.ts +++ b/apps/red-ui/src/app/state/app-state.service.ts @@ -6,8 +6,7 @@ import { FileAttributesControllerService, IDossier, IFile, - ReanalysisControllerService, - StatusControllerService + ReanalysisControllerService } from '@redaction/red-ui-http'; import { Toaster } from '@iqser/common-ui'; import { TranslateService } from '@ngx-translate/core'; @@ -23,6 +22,7 @@ import { DossierTemplate } from '@models/file/dossier-template'; import { DossiersService } from '../modules/dossier/services/dossiers.service'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { UserPreferenceService } from '@services/user-preference.service'; +import { FilesService } from '../modules/dossier/services/files.service'; export interface AppState { dossiers: Dossier[]; @@ -32,7 +32,6 @@ export interface AppState { activeDossierTemplateId: string; activeDictionaryType: string; totalAnalysedPages?: number; - totalDocuments?: number; totalPeople?: number; } @@ -51,12 +50,12 @@ export class AppStateService { private readonly _router: Router, private readonly _userService: UserService, private readonly _dossiersService: DossiersService, + private readonly _filesService: FilesService, private readonly _toaster: Toaster, private readonly _reanalysisControllerService: ReanalysisControllerService, private readonly _translateService: TranslateService, private readonly _dictionaryControllerService: DictionaryControllerService, private readonly _dossierTemplateControllerService: DossierTemplateControllerService, - private readonly _statusControllerService: StatusControllerService, private readonly _fileAttributesService: FileAttributesControllerService, private readonly _userPreferenceService: UserPreferenceService ) { @@ -222,7 +221,7 @@ export class AppStateService { } const mappedDossiers = dossiers.map(p => new Dossier(p, this._getExistingFiles(p.dossierId))); - const fileData = await this._statusControllerService.getFileStatusForDossiers(mappedDossiers.map(p => p.id)).toPromise(); + const fileData = await this._filesService.getFor(mappedDossiers.map(p => p.id)).toPromise(); for (const dossierId of Object.keys(fileData)) { const dossier = mappedDossiers.find(p => p.id === dossierId); @@ -239,7 +238,7 @@ export class AppStateService { } const oldProcessedDate = this.activeFile.lastProcessed; const dossierTemplateId = this.activeFile.dossierTemplateId; - const iFile = await this._statusControllerService.getFileStatus(this.activeDossierId, this.activeFileId).toPromise(); + const iFile = await this._filesService.get(this.activeDossierId, this.activeFileId).toPromise(); iFile.dossierTemplateId = dossierTemplateId; const activeFile = new File( @@ -258,7 +257,7 @@ export class AppStateService { } async getFiles(dossier: Dossier = this.activeDossier, emitEvents = true) { - const files = await this._statusControllerService.getDossierStatus(dossier.id).toPromise(); + const files = await this._filesService.getFor(dossier.id).toPromise(); return this._processFiles(dossier, files, emitEvents); } @@ -725,17 +724,14 @@ export class AppStateService { private _computeStats() { let totalAnalysedPages = 0; - let totalDocuments = 0; const totalPeople = new Set(); this.allDossiers.forEach(d => { - totalDocuments += d.files.length; d.memberIds?.forEach(m => totalPeople.add(m)); totalAnalysedPages += d.totalNumberOfPages; }); this._appState.totalPeople = totalPeople.size; this._appState.totalAnalysedPages = totalAnalysedPages; - this._appState.totalDocuments = totalDocuments; } } diff --git a/libs/red-ui-http/src/lib/api.module.ts b/libs/red-ui-http/src/lib/api.module.ts index f5c180e43..5e558f413 100644 --- a/libs/red-ui-http/src/lib/api.module.ts +++ b/libs/red-ui-http/src/lib/api.module.ts @@ -20,7 +20,6 @@ import { RedactionLogControllerService } from './api/redactionLogController.serv import { ReportTemplateControllerService } from './api/reportTemplateController.service'; import { RulesControllerService } from './api/rulesController.service'; import { SmtpConfigurationControllerService } from './api/smtpConfigurationController.service'; -import { StatusControllerService } from './api/statusController.service'; import { UploadControllerService } from './api/uploadController.service'; import { UserControllerService } from './api/userController.service'; import { UserPreferenceControllerService } from './api/userPreferenceController.service'; @@ -53,7 +52,6 @@ import { NotificationControllerService } from './api/notificationController.serv ReportTemplateControllerService, RulesControllerService, SmtpConfigurationControllerService, - StatusControllerService, UploadControllerService, UserControllerService, UserPreferenceControllerService, diff --git a/libs/red-ui-http/src/lib/api/api.ts b/libs/red-ui-http/src/lib/api/api.ts index 434e92b65..61cf9c815 100644 --- a/libs/red-ui-http/src/lib/api/api.ts +++ b/libs/red-ui-http/src/lib/api/api.ts @@ -10,7 +10,6 @@ import { ReanalysisControllerService } from './reanalysisController.service'; import { RedactionLogControllerService } from './redactionLogController.service'; import { DossierTemplateControllerService } from './dossierTemplateController.service'; import { RulesControllerService } from './rulesController.service'; -import { StatusControllerService } from './statusController.service'; import { UserControllerService } from './userController.service'; import { UserPreferenceControllerService } from './userPreferenceController.service'; import { VersionsControllerService } from './versionsController.service'; @@ -98,7 +97,6 @@ export const APIS = [ RedactionLogControllerService, DossierTemplateControllerService, RulesControllerService, - StatusControllerService, UserControllerService, UserPreferenceControllerService, VersionsControllerService,