diff --git a/.editorconfig b/.editorconfig index 49601a81f..347f00f8a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -17,6 +17,7 @@ trim_trailing_whitespace = false [*.ts] ij_typescript_use_double_quotes = false ij_typescript_enforce_trailing_comma = keep +ij_typescript_spaces_within_imports = true [{*.json, .prettierrc, .eslintrc}] indent_size = 2 diff --git a/apps/red-ui/src/app/components/base-screen/base-screen.component.html b/apps/red-ui/src/app/components/base-screen/base-screen.component.html index 2c927f27c..6d51a9cf3 100644 --- a/apps/red-ui/src/app/components/base-screen/base-screen.component.html +++ b/apps/red-ui/src/app/components/base-screen/base-screen.component.html @@ -18,7 +18,7 @@ translate="top-bar.navigation-items.dossiers" > - + this._search(query), }, ]; + readonly activeDossier$ = this.dossiersService.activeDossierId$.pipe(switchMap(id => this.dossiersService.getEntityChanged$(id))); private readonly _navigationStart$ = this._router.events.pipe( filter(isNavigationStart), map((event: NavigationStart) => event.url), diff --git a/apps/red-ui/src/app/modules/dossier/components/annotation-actions/annotation-actions.component.ts b/apps/red-ui/src/app/modules/dossier/components/annotation-actions/annotation-actions.component.ts index e0aa2c6ec..a9d14fb73 100644 --- a/apps/red-ui/src/app/modules/dossier/components/annotation-actions/annotation-actions.component.ts +++ b/apps/red-ui/src/app/modules/dossier/components/annotation-actions/annotation-actions.component.ts @@ -6,6 +6,8 @@ import { AnnotationPermissions } from '@models/file/annotation.permissions'; import { AnnotationActionsService } from '../../services/annotation-actions.service'; import { WebViewerInstance } from '@pdftron/webviewer'; import { UserService } from '@services/user.service'; +import { ActivatedRoute } from '@angular/router'; +import { DossiersService } from '@services/entity-services/dossiers.service'; export const AnnotationButtonTypes = { dark: 'dark', @@ -27,13 +29,18 @@ export class AnnotationActionsComponent implements OnInit { @Input() alwaysVisible: boolean; @Output() annotationsChanged = new EventEmitter(); annotationPermissions: AnnotationPermissions; + readonly dossierId: string; constructor( readonly appStateService: AppStateService, readonly annotationActionsService: AnnotationActionsService, private readonly _permissionsService: PermissionsService, private readonly _userService: UserService, - ) {} + private readonly _dossiersService: DossiersService, + activatedRoute: ActivatedRoute, + ) { + this.dossierId = activatedRoute.snapshot.paramMap.get('dossierId'); + } private _annotations: AnnotationWrapper[]; @@ -94,14 +101,6 @@ export class AnnotationActionsComponent implements OnInit { this.annotationActionsService.updateHiddenAnnotation(this.annotations, this.viewerAnnotations, false); } - private _setPermissions() { - this.annotationPermissions = AnnotationPermissions.forUser( - this._permissionsService.isApprover(), - this._userService.currentUser, - this.annotations, - ); - } - resize($event: MouseEvent) { this.annotationActionsService.resize($event, this.viewer, this.annotations[0]); } @@ -113,4 +112,13 @@ export class AnnotationActionsComponent implements OnInit { cancelResize($event: MouseEvent) { this.annotationActionsService.cancelResize($event, this.viewer, this.annotations[0], this.annotationsChanged); } + + private _setPermissions() { + const dossier = this._dossiersService.find(this.dossierId); + this.annotationPermissions = AnnotationPermissions.forUser( + this._permissionsService.isApprover(dossier), + this._userService.currentUser, + this.annotations, + ); + } } diff --git a/apps/red-ui/src/app/modules/dossier/components/comments/comments.component.html b/apps/red-ui/src/app/modules/dossier/components/comments/comments.component.html index 190e5a2c7..8686a4b2e 100644 --- a/apps/red-ui/src/app/modules/dossier/components/comments/comments.component.html +++ b/apps/red-ui/src/app/modules/dossier/components/comments/comments.component.html @@ -1,32 +1,34 @@ -
-
-
- {{ comment.user | name }} - {{ comment.date | date: 'sophisticatedDate' }} + +
+
+
+ {{ comment.user | name }} + {{ comment.date | date: 'sophisticatedDate' }} +
+ +
+ +
-
- -
+
{{ comment.text }}
-
{{ comment.text }}
-
- - + +
diff --git a/apps/red-ui/src/app/modules/dossier/components/comments/comments.component.ts b/apps/red-ui/src/app/modules/dossier/components/comments/comments.component.ts index 0412ddced..1aa3c123c 100644 --- a/apps/red-ui/src/app/modules/dossier/components/comments/comments.component.ts +++ b/apps/red-ui/src/app/modules/dossier/components/comments/comments.component.ts @@ -1,10 +1,13 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, Input, ViewChild } from '@angular/core'; -import { IComment } from '@red/domain'; +import { File, IComment } from '@red/domain'; import { ManualAnnotationService } from '../../services/manual-annotation.service'; import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { UserService } from '@services/user.service'; import { PermissionsService } from '@services/permissions.service'; import { InputWithActionComponent, trackBy } from '@iqser/common-ui'; +import { FilesMapService } from '@services/entity-services/files-map.service'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs'; @Component({ selector: 'redaction-comments', @@ -15,6 +18,7 @@ import { InputWithActionComponent, trackBy } from '@iqser/common-ui'; export class CommentsComponent { @Input() annotation: AnnotationWrapper; readonly trackBy = trackBy(); + readonly file$: Observable; @HostBinding('class.hidden') private _hidden = true; @ViewChild(InputWithActionComponent) private readonly _input: InputWithActionComponent; @@ -23,7 +27,13 @@ export class CommentsComponent { private readonly _userService: UserService, private readonly _manualAnnotationService: ManualAnnotationService, private readonly _changeDetectorRef: ChangeDetectorRef, - ) {} + readonly filesMapService: FilesMapService, + activatedRoute: ActivatedRoute, + ) { + const fileId = activatedRoute.snapshot.paramMap.get('fileId'); + const dossierId = activatedRoute.snapshot.paramMap.get('dossierId'); + this.file$ = filesMapService.watch$(dossierId, fileId); + } addComment(value: string): void { if (!value) { diff --git a/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.html b/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.html index 02872ba00..580c995a8 100644 --- a/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.html +++ b/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.html @@ -26,8 +26,8 @@
-
-
+
+
@@ -87,14 +87,15 @@ *ngFor="let pageNumber of displayedPages" [activeSelection]="pageHasSelection(pageNumber)" [active]="pageNumber === activeViewerPage" + [file]="file" [number]="pageNumber" [showDottedIcon]="hasOnlyManualRedactionsAndNotExcluded(pageNumber)" - [viewedPages]="fileData?.viewedPages" + [viewedPages]="viewedPages" >
diff --git a/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.ts b/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.ts index 4dfe8b247..e99fd1d58 100644 --- a/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.ts +++ b/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.ts @@ -1,14 +1,25 @@ -import { ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Input, Output, TemplateRef, ViewChild } from '@angular/core'; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ElementRef, + EventEmitter, + HostListener, + Input, + Output, + TemplateRef, + ViewChild, +} from '@angular/core'; import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationProcessingService } from '../../services/annotation-processing.service'; import { MatDialogRef, MatDialogState } from '@angular/material/dialog'; import scrollIntoView from 'scroll-into-view-if-needed'; import { CircleButtonTypes, Debounce, FilterService, IconButtonTypes, INestedFilter, IqserEventTarget } from '@iqser/common-ui'; -import { FileDataModel } from '@models/file/file-data.model'; import { PermissionsService } from '@services/permissions.service'; import { WebViewerInstance } from '@pdftron/webviewer'; import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; +import { File, IViewedPage } from '@red/domain'; const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape']; const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']; @@ -17,6 +28,7 @@ const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']; selector: 'redaction-file-workload', templateUrl: './file-workload.component.html', styleUrls: ['./file-workload.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class FileWorkloadComponent { readonly iconButtonTypes = IconButtonTypes; @@ -27,7 +39,8 @@ export class FileWorkloadComponent { @Input() activeViewerPage: number; @Input() shouldDeselectAnnotationsOnPageChange: boolean; @Input() dialogRef: MatDialogRef; - @Input() fileData: FileDataModel; + @Input() viewedPages: IViewedPage[]; + @Input() file: File; @Input() hideSkipped: boolean; @Input() excludePages: boolean; @Input() annotationActionsTemplate: TemplateRef; @@ -78,20 +91,16 @@ export class FileWorkloadComponent { } } - get isProcessing(): boolean { - return this.fileData?.file?.isProcessing; - } - get activeAnnotations(): AnnotationWrapper[] | undefined { return this.displayedAnnotations.get(this.activeViewerPage); } get isReadOnly(): boolean { - return !this._permissionsService.canPerformAnnotationActions(); + return !this._permissionsService.canPerformAnnotationActions(this.file); } get currentPageIsExcluded(): boolean { - return this.fileData?.file?.excludedPages?.includes(this.activeViewerPage); + return this.file?.excludedPages?.includes(this.activeViewerPage); } private get _firstSelectedAnnotation() { @@ -120,7 +129,7 @@ export class FileWorkloadComponent { hasOnlyManualRedactionsAndNotExcluded(pageNumber: number): boolean { const hasOnlyManualRedactions = this.displayedAnnotations.get(pageNumber).every(annotation => annotation.manual); - return hasOnlyManualRedactions && this.fileData.file.excludedPages.includes(pageNumber); + return hasOnlyManualRedactions && this.file.excludedPages.includes(pageNumber); } pageHasSelection(page: number) { @@ -171,7 +180,7 @@ export class FileWorkloadComponent { this._navigatePages($event); } - this._changeDetectorRef.detectChanges(); + this._changeDetectorRef.markForCheck(); } scrollAnnotations(): void { @@ -212,7 +221,7 @@ export class FileWorkloadComponent { } scrollQuickNavLast(): void { - this.selectPage.emit(this.fileData.file.numberOfPages); + this.selectPage.emit(this.file.numberOfPages); } pageSelectedByClick($event: number): void { diff --git a/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.html b/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.html index e1923ce1f..623b48ca9 100644 --- a/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.html +++ b/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.html @@ -1,4 +1,4 @@ -
+
{{ range.startPage }} -{{ range.endPage }} @@ -29,7 +29,7 @@
diff --git a/apps/red-ui/src/app/modules/dossier/components/page-indicator/page-indicator.component.ts b/apps/red-ui/src/app/modules/dossier/components/page-indicator/page-indicator.component.ts index 743b86fd5..cc63a24e3 100644 --- a/apps/red-ui/src/app/modules/dossier/components/page-indicator/page-indicator.component.ts +++ b/apps/red-ui/src/app/modules/dossier/components/page-indicator/page-indicator.component.ts @@ -1,21 +1,11 @@ -import { - ChangeDetectionStrategy, - Component, - EventEmitter, - Input, - OnChanges, - OnDestroy, - OnInit, - Output, - SimpleChanges, -} from '@angular/core'; -import { AppStateService } from '@state/app-state.service'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, OnDestroy, Output } from '@angular/core'; import { PermissionsService } from '@services/permissions.service'; import { ConfigService } from '@services/config.service'; import { DossiersService } from '@services/entity-services/dossiers.service'; import { ViewedPagesService } from '../../shared/services/viewed-pages.service'; -import { IViewedPage } from '@red/domain'; +import { File, IViewedPage } from '@red/domain'; import { AutoUnsubscribe } from '@iqser/common-ui'; +import { FilesMapService } from '@services/entity-services/files-map.service'; @Component({ selector: 'redaction-page-indicator', @@ -23,8 +13,13 @@ import { AutoUnsubscribe } from '@iqser/common-ui'; styleUrls: ['./page-indicator.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class PageIndicatorComponent extends AutoUnsubscribe implements OnChanges, OnInit, OnDestroy { - @Input() active: boolean; +export class PageIndicatorComponent extends AutoUnsubscribe implements OnDestroy, OnChanges { + @Input() + file: File; + + @Input() + active = false; + @Input() showDottedIcon = false; @Input() number: number; @Input() viewedPages: IViewedPage[]; @@ -33,11 +28,10 @@ export class PageIndicatorComponent extends AutoUnsubscribe implements OnChanges @Output() readonly pageSelected = new EventEmitter(); pageReadTimeout: number = null; - canMarkPagesAsViewed: boolean; constructor( private readonly _viewedPagesService: ViewedPagesService, - private readonly _appStateService: AppStateService, + private readonly _filesMapService: FilesMapService, private readonly _dossiersService: DossiersService, private readonly _configService: ConfigService, private readonly _permissionService: PermissionsService, @@ -58,23 +52,12 @@ export class PageIndicatorComponent extends AutoUnsubscribe implements OnChanges } } - ngOnInit(): void { - this.addSubscription = this._appStateService.fileChanged$.subscribe(() => { - if (this.canMarkPagesAsViewed !== this._permissionService.canMarkPagesAsViewed()) { - this.canMarkPagesAsViewed = this._permissionService.canMarkPagesAsViewed(); - this._handlePageRead(); - } - }); - } - - ngOnChanges(changes: SimpleChanges): void { - if (changes.active) { - this._handlePageRead(); - } + ngOnChanges() { + this.handlePageRead(); } async toggleReadState() { - if (this.canMarkPagesAsViewed) { + if (this._permissionService.canMarkPagesAsViewed(this.file)) { if (this.read) { await this._markPageUnread(); } else { @@ -83,8 +66,8 @@ export class PageIndicatorComponent extends AutoUnsubscribe implements OnChanges } } - private _handlePageRead() { - if (!this.canMarkPagesAsViewed) { + handlePageRead() { + if (!this._permissionService.canMarkPagesAsViewed(this.file)) { return; } @@ -119,20 +102,16 @@ export class PageIndicatorComponent extends AutoUnsubscribe implements OnChanges // } private async _markPageRead() { - await this._viewedPagesService - .addPage({ page: this.number }, this._dossiersService.activeDossierId, this._appStateService.activeFileId) - .toPromise(); + await this._viewedPagesService.addPage({ page: this.number }, this.file.dossierId, this.file.fileId).toPromise(); if (this.activePage) { this.activePage.hasChanges = false; } else { - this.viewedPages?.push({ page: this.number, fileId: this._appStateService.activeFileId }); + this.viewedPages?.push({ page: this.number, fileId: this.file.fileId }); } } private async _markPageUnread() { - await this._viewedPagesService - .removePage(this._dossiersService.activeDossierId, this._appStateService.activeFileId, this.number) - .toPromise(); + await this._viewedPagesService.removePage(this.file.dossierId, this.file.fileId, this.number).toPromise(); this.viewedPages?.splice( this.viewedPages?.findIndex(p => p.page === this.number), 1, diff --git a/apps/red-ui/src/app/modules/dossier/components/team-members-manager/team-members-manager.component.html b/apps/red-ui/src/app/modules/dossier/components/team-members-manager/team-members-manager.component.html index c2fa279d7..e48a1fc4a 100644 --- a/apps/red-ui/src/app/modules/dossier/components/team-members-manager/team-members-manager.component.html +++ b/apps/red-ui/src/app/modules/dossier/components/team-members-manager/team-members-manager.component.html @@ -1,4 +1,4 @@ -
+
{{ 'assign-dossier-owner.dialog.single-user' | translate }} @@ -28,15 +28,19 @@ [canAdd]="false" [canRemove]="true" [largeSpacing]="true" - [memberIds]="selectedReviewersList" + [memberIds]="selectedReviewers$ | async" [perLine]="13" [unremovableMembers]="[selectedOwnerId]" > -

+    

 
     ();
+    @Output() readonly updateDossier = new EventEmitter();
 
     readonly ownersSelectOptions = this.userService.managerUsers.map(m => m.id);
-    selectedReviewersList: string[] = [];
     membersSelectOptions: string[] = [];
-    changed = false;
+    readonly selectedReviewers$ = new BehaviorSubject([]);
 
     constructor(
         readonly userService: UserService,
-        private readonly _toaster: Toaster,
         private readonly _formBuilder: FormBuilder,
         private readonly _dossiersService: DossiersService,
-    ) {}
+    ) {
+        super();
+    }
 
     get selectedOwnerId(): string {
-        return this.teamForm.get('owner').value;
+        return this.form.get('owner').value;
     }
 
     get selectedApproversList(): string[] {
-        return this.teamForm.get('approvers').value;
+        return this.form.get('approvers').value;
     }
 
     get selectedMembersList(): string[] {
-        return this.teamForm.get('members').value;
+        return this.form.get('members').value;
     }
 
     get valid(): boolean {
-        return this.teamForm.valid;
+        return this.form.valid;
+    }
+
+    get disabled() {
+        return !this.userService.currentUser.isManager;
+    }
+
+    get changed() {
+        if (this.dossier.ownerId !== this.selectedOwnerId) {
+            return true;
+        }
+
+        const initialMembers = [...this.dossier.memberIds].sort();
+        const currentMembers = this.selectedMembersList.sort();
+
+        const initialApprovers = [...this.dossier.approverIds].sort();
+        const currentApprovers = this.selectedApproversList.sort();
+        return this._compareLists(initialMembers, currentMembers) || this._compareLists(initialApprovers, currentApprovers);
     }
 
     isOwner(userId: string): boolean {
         return userId === this.selectedOwnerId;
     }
 
-    async saveMembers() {
+    async save() {
         const dossier = {
             ...this.dossier,
             memberIds: this.selectedMembersList,
@@ -59,8 +79,7 @@ export class TeamMembersManagerComponent implements OnInit {
 
         const result = await this._dossiersService.createOrUpdate(dossier).toPromise();
         if (result) {
-            this.save.emit(result);
-            this._updateChanged();
+            this.updateDossier.emit(result);
         }
     }
 
@@ -112,39 +131,25 @@ export class TeamMembersManagerComponent implements OnInit {
         this._loadData();
     }
 
-    setMembersSelectOptions(): void {
+    setMembersSelectOptions(value = this.searchQuery): void {
         this.membersSelectOptions = this.userService.eligibleUsers
-            .filter(user => this.userService.getNameForId(user.id).toLowerCase().includes(this.searchQuery.toLowerCase()))
+            .filter(user => this.userService.getNameForId(user.id).toLowerCase().includes(value.toLowerCase()))
             .filter(user => this.selectedOwnerId !== user.id)
             .map(user => user.id);
     }
 
-    private _updateChanged() {
-        if (this.dossier.ownerId !== this.selectedOwnerId) {
-            this.changed = true;
-            return;
-        }
-
-        const initialMembers = [...this.dossier.memberIds].sort();
-        const currentMembers = this.selectedMembersList.sort();
-
-        const initialApprovers = [...this.dossier.approverIds].sort();
-        const currentApprovers = this.selectedApproversList.sort();
-
-        this.changed = this._compareLists(initialMembers, currentMembers) || this._compareLists(initialApprovers, currentApprovers);
-    }
-
     private _setSelectedReviewersList() {
-        this.selectedReviewersList = this.selectedMembersList.filter(m => this.selectedApproversList.indexOf(m) === -1);
+        const selectedReviewers = this.selectedMembersList.filter(m => this.selectedApproversList.indexOf(m) === -1);
+        this.selectedReviewers$.next(selectedReviewers);
     }
 
     private _loadData() {
-        this.teamForm = this._formBuilder.group({
+        this.form = this._formBuilder.group({
             owner: [this.dossier?.ownerId, Validators.required],
             approvers: [[...this.dossier?.approverIds]],
             members: [[...this.dossier?.memberIds]],
         });
-        this.teamForm.get('owner').valueChanges.subscribe(owner => {
+        this.addSubscription = this.form.get('owner').valueChanges.subscribe(owner => {
             if (!this.isApprover(owner)) {
                 this.toggleApprover(owner);
             }
@@ -157,7 +162,6 @@ export class TeamMembersManagerComponent implements OnInit {
     private _updateLists() {
         this._setSelectedReviewersList();
         this.setMembersSelectOptions();
-        this._updateChanged();
     }
 
     private _compareLists(l1: string[], l2: string[]) {
diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.html b/apps/red-ui/src/app/modules/dossier/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.html
index 8c7d61eb1..1e851ab46 100644
--- a/apps/red-ui/src/app/modules/dossier/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.html
+++ b/apps/red-ui/src/app/modules/dossier/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.html
@@ -7,13 +7,13 @@
         class="dialog-header heading-l"
     >
- +
{{ 'assign-owner.dialog.label' | translate: { type: data.mode } }} - - + + {{ userId | name: { defaultValue: 'initials-avatar.unassigned' | translate } }} @@ -22,7 +22,7 @@
- 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 8f12cc5b9..05978ad9a 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 @@ -4,7 +4,7 @@ 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 { Dossier, File } from '@red/domain'; +import { File } from '@red/domain'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { FilesService } from '@services/entity-services/files.service'; import { DossiersService } from '@services/entity-services/dossiers.service'; @@ -12,7 +12,6 @@ import { PermissionsService } from '@services/permissions.service'; class DialogData { mode: 'approver' | 'reviewer'; - dossier?: Dossier; files?: File[]; ignoreChanged?: boolean; } @@ -22,8 +21,7 @@ class DialogData { styleUrls: ['./assign-reviewer-approver-dialog.component.scss'], }) export class AssignReviewerApproverDialogComponent { - usersForm: FormGroup; - searchForm: FormGroup; + form: FormGroup; constructor( readonly userService: UserService, @@ -39,15 +37,14 @@ export class AssignReviewerApproverDialogComponent { this._loadData(); } - get selectedSingleUser(): string { - return this.usersForm.get('singleUser').value; + get selectedUser(): string { + return this.form.get('user').value; } - get singleUsersSelectOptions() { + get userOptions() { const unassignUser = this._canUnassignFiles ? [undefined] : []; - return this.data.mode === 'approver' - ? [...this._dossiersService.activeDossier.approverIds, ...unassignUser] - : [...this._dossiersService.activeDossier.memberIds, ...unassignUser]; + const dossier = this._dossiersService.activeDossier; + return this.data.mode === 'approver' ? [...dossier.approverIds, ...unassignUser] : [...dossier.memberIds, ...unassignUser]; } get changed(): boolean { @@ -56,7 +53,7 @@ export class AssignReviewerApproverDialogComponent { } for (const file of this.data.files) { - if (file.currentReviewer !== this.selectedSingleUser) { + if (file.currentReviewer !== this.selectedUser) { return true; } } @@ -69,19 +66,17 @@ export class AssignReviewerApproverDialogComponent { } isOwner(userId: string): boolean { - return userId === this.selectedSingleUser; + return userId === this.selectedUser; } async save() { try { - const selectedUser = this.selectedSingleUser; - if (this.data.mode === 'reviewer') { await this._filesService .setReviewerFor( this.data.files.map(f => f.fileId), this._dossiersService.activeDossierId, - selectedUser, + this.selectedUser, ) .toPromise(); } else { @@ -89,7 +84,7 @@ export class AssignReviewerApproverDialogComponent { .setUnderApprovalFor( this.data.files.map(f => f.fileId), this._dossiersService.activeDossierId, - selectedUser, + this.selectedUser, ) .toPromise(); } @@ -111,13 +106,13 @@ export class AssignReviewerApproverDialogComponent { uniqueReviewers.add(file.currentReviewer); } } - let singleUser: string = uniqueReviewers.size === 1 ? uniqueReviewers.values().next().value : this.userService.currentUser.id; + let user: string = uniqueReviewers.size === 1 ? uniqueReviewers.values().next().value : this.userService.currentUser.id; - singleUser = this.singleUsersSelectOptions.indexOf(singleUser) >= 0 ? singleUser : this.singleUsersSelectOptions[0]; + user = this.userOptions.indexOf(user) >= 0 ? user : this.userOptions[0]; - this.usersForm = this._formBuilder.group({ + this.form = this._formBuilder.group({ // Allow a null reviewer if a previous reviewer exists (= it's not the first assignment) & current user is allowed to unassign - singleUser: [singleUser, this._canUnassignFiles && !singleUser ? Validators.required : null], + user: [user, this._canUnassignFiles && !user ? Validators.required : null], }); } } diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/change-legal-basis-dialog/change-legal-basis-dialog.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/change-legal-basis-dialog/change-legal-basis-dialog.component.ts index e9fc4bce4..c789b445d 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/change-legal-basis-dialog/change-legal-basis-dialog.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/change-legal-basis-dialog/change-legal-basis-dialog.component.ts @@ -1,9 +1,7 @@ import { Component, Inject, OnInit } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { AppStateService } from '@state/app-state.service'; import { PermissionsService } from '@services/permissions.service'; import { DossiersService } from '@services/entity-services/dossiers.service'; import { JustificationsService } from '@services/entity-services/justifications.service'; @@ -15,7 +13,6 @@ export interface LegalBasisOption { } @Component({ - selector: 'redaction-change-legal-basis-dialog', templateUrl: './change-legal-basis-dialog.component.html', styleUrls: ['./change-legal-basis-dialog.component.scss'], }) @@ -25,14 +22,12 @@ export class ChangeLegalBasisDialogComponent implements OnInit { legalOptions: LegalBasisOption[] = []; constructor( - private readonly _translateService: TranslateService, private readonly _justificationsService: JustificationsService, - private readonly _appStateService: AppStateService, private readonly _dossiersService: DossiersService, private readonly _permissionsService: PermissionsService, private readonly _formBuilder: FormBuilder, - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public annotations: AnnotationWrapper[], + readonly dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) readonly annotations: AnnotationWrapper[], ) {} get changed(): boolean { @@ -40,19 +35,18 @@ export class ChangeLegalBasisDialogComponent implements OnInit { } async ngOnInit() { - this.isDocumentAdmin = this._permissionsService.isApprover(); + const dossier = this._dossiersService.activeDossier; + this.isDocumentAdmin = this._permissionsService.isApprover(dossier); this.legalBasisForm = this._formBuilder.group({ reason: [null, Validators.required], comment: this.isDocumentAdmin ? [null] : [null, Validators.required], }); - const data = await this._justificationsService - .getForDossierTemplate(this._dossiersService.activeDossier.dossierTemplateId) - .toPromise(); + const data = await this._justificationsService.getForDossierTemplate(dossier.dossierTemplateId).toPromise(); this.legalOptions = data - .map(lbm => ({ + .map(lbm => ({ legalBasis: lbm.reason, description: lbm.description, label: lbm.name, diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.html b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.html index 6dfc4cd8b..33432d956 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.html +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.html @@ -1,4 +1,4 @@ -
+
{{ 'edit-dossier-dialog.header' | translate: { dossierName: dossier.dossierName } }}
@@ -13,6 +13,7 @@ class="item" >
+
@@ -20,37 +21,37 @@
- + > @@ -69,7 +70,8 @@ (click)="save(true)" [disabled]="activeComponent?.disabled || !activeComponent?.changed" [label]="'edit-dossier-dialog.actions.save-and-close' | translate" - [type]="iconButtonTypes.dark"> + [type]="iconButtonTypes.dark" + >
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 80d5ce762..b0faec9d2 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 @@ -6,13 +6,14 @@ import { EditDossierDownloadPackageComponent } from './download-package/edit-dos import { EditDossierSectionInterface } from './edit-dossier-section.interface'; import { IconButtonTypes, Toaster } from '@iqser/common-ui'; import { EditDossierDictionaryComponent } from './dictionary/edit-dossier-dictionary.component'; -import { EditDossierTeamMembersComponent } from './team-members/edit-dossier-team-members.component'; import { EditDossierAttributesComponent } from './attributes/edit-dossier-attributes.component'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { EditDossierDeletedDocumentsComponent } from './deleted-documents/edit-dossier-deleted-documents.component'; import { AppStateService } from '@state/app-state.service'; import { DossiersService } from '@services/entity-services/dossiers.service'; +import { TeamMembersManagerComponent } from '../../components/team-members-manager/team-members-manager.component'; +import { Observable } from 'rxjs'; type Section = 'dossierInfo' | 'downloadPackage' | 'dossierDictionary' | 'members' | 'dossierAttributes' | 'deletedDocuments'; @@ -24,12 +25,12 @@ export class EditDossierDialogComponent { readonly navItems: { key: Section; title?: string; sideNavTitle?: string }[]; readonly iconButtonTypes = IconButtonTypes; activeNav: Section; - dossier: Dossier; + readonly dossier$: Observable; @ViewChild(EditDossierGeneralInfoComponent) generalInfoComponent: EditDossierGeneralInfoComponent; @ViewChild(EditDossierDownloadPackageComponent) downloadPackageComponent: EditDossierDownloadPackageComponent; @ViewChild(EditDossierDictionaryComponent) dictionaryComponent: EditDossierDictionaryComponent; - @ViewChild(EditDossierTeamMembersComponent) membersComponent: EditDossierTeamMembersComponent; + @ViewChild(TeamMembersManagerComponent) membersComponent: TeamMembersManagerComponent; @ViewChild(EditDossierAttributesComponent) attributesComponent: EditDossierAttributesComponent; @ViewChild(EditDossierDeletedDocumentsComponent) deletedDocumentsComponent: EditDossierDeletedDocumentsComponent; @@ -41,7 +42,7 @@ export class EditDossierDialogComponent { private readonly _dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) private readonly _data: { - dossier: Dossier; + dossierId: string; afterSave: Function; section?: Section; }, @@ -77,7 +78,7 @@ export class EditDossierDialogComponent { }, ]; - this.dossier = _data.dossier; + this.dossier$ = this._dossiersService.getEntityChanged$(_data.dossierId); this.activeNav = _data.section || 'dossierInfo'; } @@ -108,10 +109,8 @@ export class EditDossierDialogComponent { return !['deletedDocuments'].includes(this.activeNav); } - updatedDossier() { - this._toaster.success(_('edit-dossier-dialog.change-successful'), { params: { dossierName: this.dossier.dossierName } }); - this.dossier = this._dossiersService.find(this.dossier.id); - this._changeRef.detectChanges(); + updatedDossier(dossier: Dossier) { + this._toaster.success(_('edit-dossier-dialog.change-successful'), { params: { dossierName: dossier.dossierName } }); this.afterSave(); } 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 fb680d5fe..56c85330d 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 @@ -12,6 +12,7 @@ import { ConfirmationDialogInput, IconButtonTypes, TitleColors, Toaster } from ' import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { DossiersService } from '@services/entity-services/dossiers.service'; import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service'; +import { DossierStatsService } from '@services/entity-services/dossier-stats.service'; @Component({ selector: 'redaction-edit-dossier-general-info', @@ -32,6 +33,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti readonly permissionsService: PermissionsService, private readonly _dossierTemplatesService: DossierTemplatesService, private readonly _dossiersService: DossiersService, + private readonly _dossierStatsService: DossierStatsService, private readonly _formBuilder: FormBuilder, private readonly _dialogService: DossiersDialogService, private readonly _router: Router, @@ -71,7 +73,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti dossierTemplateId: [ { value: this.dossier.dossierTemplateId, - disabled: this.dossier.hasFiles, + disabled: this._dossierStatsService.get(this.dossier.dossierId).hasFiles, }, Validators.required, ], diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/team-members/edit-dossier-team-members.component.html b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/team-members/edit-dossier-team-members.component.html deleted file mode 100644 index cb8de7e94..000000000 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/team-members/edit-dossier-team-members.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/team-members/edit-dossier-team-members.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/team-members/edit-dossier-team-members.component.ts deleted file mode 100644 index cf0521c84..000000000 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/team-members/edit-dossier-team-members.component.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; -import { Dossier } from '@red/domain'; -import { EditDossierSectionInterface } from '../edit-dossier-section.interface'; -import { TeamMembersManagerComponent } from '../../../components/team-members-manager/team-members-manager.component'; -import { UserService } from '@services/user.service'; - -@Component({ - selector: 'redaction-edit-dossier-team-members', - templateUrl: './edit-dossier-team-members.component.html', - styleUrls: ['./edit-dossier-team-members.component.scss'], -}) -export class EditDossierTeamMembersComponent implements EditDossierSectionInterface { - readonly currentUser = this._userService.currentUser; - - @Input() dossier: Dossier; - @Output() readonly updateDossier = new EventEmitter(); - - @ViewChild(TeamMembersManagerComponent) managerComponent: TeamMembersManagerComponent; - - constructor(private readonly _userService: UserService) {} - - get changed() { - return this.managerComponent.changed; - } - - get disabled() { - return !this.currentUser.isManager; - } - - async save() { - await this.managerComponent.saveMembers(); - } - - revert() { - this.managerComponent.revert(); - } -} 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 507ab21b6..d615c8c7a 100644 --- a/apps/red-ui/src/app/modules/dossier/dossiers.module.ts +++ b/apps/red-ui/src/app/modules/dossier/dossiers.module.ts @@ -27,7 +27,6 @@ import { EditDossierDialogComponent } from './dialogs/edit-dossier-dialog/edit-d 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 { EditDossierTeamMembersComponent } from './dialogs/edit-dossier-dialog/team-members/edit-dossier-team-members.component'; import { TeamMembersManagerComponent } from './components/team-members-manager/team-members-manager.component'; import { ChangeLegalBasisDialogComponent } from './dialogs/change-legal-basis-dialog/change-legal-basis-dialog.component'; import { PageExclusionComponent } from './components/page-exclusion/page-exclusion.component'; @@ -68,7 +67,6 @@ const components = [ EditDossierGeneralInfoComponent, EditDossierDownloadPackageComponent, EditDossierDictionaryComponent, - EditDossierTeamMembersComponent, EditDossierAttributesComponent, TeamMembersManagerComponent, PageExclusionComponent, diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.html index b343383f7..691762c9e 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.html @@ -1,4 +1,4 @@ - + , + readonly listingService: ListingService, private readonly _userPreferenceService: UserPreferenceService, ) {} get selectedFiles(): File[] { - return this._listingService.selected; - } - - get areAllFilesSelected() { - return this.dossier.files.length !== 0 && this.selectedFiles.length === this.dossier.files.length; - } - - get areSomeFilesSelected() { - return this.selectedFiles.length > 0; + return this.listingService.selected; } get allSelectedFilesCanBeAssignedIntoSameState() { - if (this.areSomeFilesSelected) { - const allFilesAreUnderReviewOrUnassigned = this.selectedFiles.reduce( - (acc, file) => acc && (file.isUnderReview || file.isUnassigned), - true, - ); - const allFilesAreUnderApproval = this.selectedFiles.reduce((acc, file) => acc && file.isUnderApproval, true); - return allFilesAreUnderReviewOrUnassigned || allFilesAreUnderApproval; - } - return false; + const allFilesAreUnderReviewOrUnassigned = this.selectedFiles.reduce( + (acc, file) => acc && (file.isUnderReview || file.isUnassigned), + true, + ); + const allFilesAreUnderApproval = this.selectedFiles.reduce((acc, file) => acc && file.isUnderApproval, true); + return allFilesAreUnderReviewOrUnassigned || allFilesAreUnderApproval; } get canAssignToSelf() { @@ -81,31 +71,27 @@ export class DossierOverviewBulkActionsComponent { } get canDelete() { - return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canDeleteFile(file), true); + return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canDeleteFile(file, this.dossier), true); } get canReanalyse() { - return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canReanalyseFile(file), true); + return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canReanalyseFile(file, this.dossier), true); } get canOcr() { return this.selectedFiles.reduce((acc, file) => acc && file.canBeOCRed, true); } - get files() { - return this.selectedFiles.map(file => file.status); - } - get canSetToUnderReview() { - return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canSetUnderReview(file), true); + return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canSetUnderReview(file, this.dossier), true); } get canSetToUnderApproval() { - return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canSetUnderApproval(file), true); + return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canSetUnderApproval(file, this.dossier), true); } get isReadyForApproval() { - return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.isReadyForApproval(file), true); + return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.isReadyForApproval(file, this.dossier), true); } get canApprove() { diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.html index 0abb33ec4..0dd0c8949 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.html @@ -1,76 +1,72 @@ - +
- {{ 'dossier-overview.dossier-details.stats.documents' | translate: { count: dossier.files.length } }} + {{ 'dossier-overview.dossier-details.stats.documents' | translate: { count: stats.numberOfFiles } }}
+ +
+ + {{ 'dossier-overview.dossier-details.stats.processing-documents' | translate: { count: stats.numberOfProcessingFiles } }} +
+
{{ 'dossier-overview.dossier-details.stats.people' | translate: { count: dossier.memberIds.length } }}
+
- {{ - 'dossier-overview.dossier-details.stats.analysed-pages' | translate: { count: dossier.totalNumberOfPages | number } - }} + {{ 'dossier-overview.dossier-details.stats.analysed-pages' | translate: { count: stats.numberOfPages | number } }}
-
- - {{ - 'dossier-overview.dossier-details.stats.created-on' - | translate - : { - date: dossier.date | date: 'd MMM. yyyy' - } - }} - -
-
- - {{ - 'dossier-overview.dossier-details.stats.due-date' - | translate - : { - date: dossier.dueDate | date: 'd MMM. yyyy' - } - }} -
-
- - {{ dossierTemplate(dossier).name }} -
- -