Pull request #20: Permissions
Merge in RED/ui from permissions to master * commit 'd23f5264c1ffe7b99a25721c82315c83ae4636f6': Hide buttons depending on user role
This commit is contained in:
commit
da2e8b9335
@ -59,6 +59,7 @@ import { AssignOwnerDialogComponent } from './dialogs/assign-owner-dialog/assign
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatNativeDateModule } from '@angular/material/core';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { ProjectMemberGuard } from './auth/project-member-guard.service';
|
||||
|
||||
export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
|
||||
@ -131,7 +132,12 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||
component: ProjectOverviewScreenComponent,
|
||||
canActivate: [CompositeRouteGuard],
|
||||
data: {
|
||||
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
|
||||
routeGuards: [
|
||||
AuthGuard,
|
||||
RedRoleGuard,
|
||||
ProjectMemberGuard,
|
||||
AppStateGuard
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -139,7 +145,12 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||
component: FilePreviewScreenComponent,
|
||||
canActivate: [CompositeRouteGuard],
|
||||
data: {
|
||||
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
|
||||
routeGuards: [
|
||||
AuthGuard,
|
||||
RedRoleGuard,
|
||||
ProjectMemberGuard,
|
||||
AppStateGuard
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
53
apps/red-ui/src/app/auth/project-member-guard.service.ts
Normal file
53
apps/red-ui/src/app/auth/project-member-guard.service.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
|
||||
import { UserService } from '../user/user.service';
|
||||
import { AppLoadStateService } from '../utils/app-load-state.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { AppStateService } from '../state/app-state.service';
|
||||
import { NotificationService, NotificationType } from '../notification/notification.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ProjectMemberGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly _router: Router,
|
||||
private readonly _appLoadStateService: AppLoadStateService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _userService: UserService,
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _translateService: TranslateService
|
||||
) {}
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
||||
return new Observable((obs) => {
|
||||
if (this._userService.isManager(this._userService.user)) {
|
||||
obs.next(true);
|
||||
obs.complete();
|
||||
return;
|
||||
}
|
||||
|
||||
const projectId = route.params.projectId;
|
||||
this._appStateService.loadAllProjects().then(() => {
|
||||
const isProjectMember = this._appStateService.allProjects
|
||||
.find((pw) => pw.project.projectId === projectId)
|
||||
?.project.memberIds.includes(this._userService.user.id);
|
||||
if (!isProjectMember) {
|
||||
this._router.navigate(['ui', 'projects']);
|
||||
this._appLoadStateService.pushLoadingEvent(false);
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant('project-member-guard.access-denied.label'),
|
||||
null,
|
||||
NotificationType.ERROR
|
||||
);
|
||||
obs.next(false);
|
||||
obs.complete();
|
||||
} else {
|
||||
obs.next(true);
|
||||
obs.complete();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -231,7 +231,7 @@ export class DialogService {
|
||||
return ref;
|
||||
}
|
||||
|
||||
public openAssignFileOwnerDialog(
|
||||
public openAssignFileReviewerDialog(
|
||||
$event: MouseEvent,
|
||||
file: FileStatus,
|
||||
cb?: Function
|
||||
|
||||
@ -12,13 +12,21 @@
|
||||
|
||||
<div class="flex-1 actions-container">
|
||||
<div class="actions-row">
|
||||
<button mat-icon-button (click)="openDeleteFileDialog($event)">
|
||||
<button
|
||||
mat-icon-button
|
||||
(click)="openDeleteFileDialog($event)"
|
||||
*ngIf="userService.isManager(this.user) || appStateService.isActiveProjectOwner"
|
||||
>
|
||||
<mat-icon svgIcon="red:trash"></mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button>
|
||||
<mat-icon svgIcon="red:report"></mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="openAssignFileOwnerDialog($event)">
|
||||
<button
|
||||
mat-icon-button
|
||||
(click)="openAssignFileReviewerDialog($event)"
|
||||
*ngIf="appStateService.isActiveProjectMember"
|
||||
>
|
||||
<mat-icon svgIcon="red:assign"></mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="reanalyseFile($event)">
|
||||
@ -30,6 +38,7 @@
|
||||
</div>
|
||||
|
||||
<button
|
||||
*ngIf="userService.isManager(user) || appStateService.isActiveProjectOwner"
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
class="arrow-button"
|
||||
|
||||
@ -1,33 +1,20 @@
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
NgZone,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
EventEmitter
|
||||
} from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import {
|
||||
DictionaryControllerService,
|
||||
ManualRedactionEntry,
|
||||
ReanalysisControllerService
|
||||
} from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '../../../state/app-state.service';
|
||||
import { Annotations, WebViewerInstance } from '@pdftron/webviewer';
|
||||
import { PdfViewerComponent } from '../pdf-viewer/pdf-viewer.component';
|
||||
import { AnnotationUtils } from '../../../utils/annotation-utils';
|
||||
import { UserService } from '../../../user/user.service';
|
||||
import { debounce } from '../../../utils/debounce';
|
||||
import {ChangeDetectorRef, Component, ElementRef, HostListener, NgZone, OnInit, ViewChild} from '@angular/core';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {ManualRedactionEntry, ReanalysisControllerService} from '@redaction/red-ui-http';
|
||||
import {AppStateService} from '../../../state/app-state.service';
|
||||
import {Annotations, WebViewerInstance} from '@pdftron/webviewer';
|
||||
import {PdfViewerComponent} from '../pdf-viewer/pdf-viewer.component';
|
||||
import {AnnotationUtils} from '../../../utils/annotation-utils';
|
||||
import {UserService} from '../../../user/user.service';
|
||||
import {debounce} from '../../../utils/debounce';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
import { AnnotationFilters } from '../../../utils/types';
|
||||
import { FiltersService } from '../service/filters.service';
|
||||
import { FileDownloadService } from '../service/file-download.service';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { FileType } from '../model/file-type';
|
||||
import { DialogService } from '../../../dialogs/dialog.service';
|
||||
import { MatDialogRef, MatDialogState } from '@angular/material/dialog';
|
||||
import {AnnotationFilters} from '../../../utils/types';
|
||||
import {FiltersService} from '../service/filters.service';
|
||||
import {FileDownloadService} from '../service/file-download.service';
|
||||
import {saveAs} from 'file-saver';
|
||||
import {FileType} from '../model/file-type';
|
||||
import {DialogService} from '../../../dialogs/dialog.service';
|
||||
import {MatDialogRef, MatDialogState} from '@angular/material/dialog';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-file-preview-screen',
|
||||
@ -57,12 +44,11 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
public readonly appStateService: AppStateService,
|
||||
public readonly userService: UserService,
|
||||
private readonly _changeDetectorRef: ChangeDetectorRef,
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _dialogService: DialogService,
|
||||
private readonly _router: Router,
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService,
|
||||
private readonly _userService: UserService,
|
||||
private readonly _fileDownloadService: FileDownloadService,
|
||||
private readonly _reanalysisControllerService: ReanalysisControllerService,
|
||||
private readonly _filtersService: FiltersService,
|
||||
@ -76,7 +62,7 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
}
|
||||
|
||||
public get user() {
|
||||
return this._userService.user;
|
||||
return this.userService.user;
|
||||
}
|
||||
|
||||
public filterKeys(key?: string) {
|
||||
@ -145,9 +131,9 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
);
|
||||
}
|
||||
|
||||
public openAssignFileOwnerDialog($event: MouseEvent) {
|
||||
public openAssignFileReviewerDialog($event: MouseEvent) {
|
||||
const file = this.appStateService.getFileById(this.projectId, this.fileId);
|
||||
this._dialogRef = this._dialogService.openAssignFileOwnerDialog($event, file);
|
||||
this._dialogRef = this._dialogService.openAssignFileReviewerDialog($event, file);
|
||||
}
|
||||
|
||||
public get activeViewer() {
|
||||
|
||||
@ -22,11 +22,12 @@
|
||||
</div>
|
||||
<button
|
||||
(click)="openAddProjectDialog()"
|
||||
*ngIf="userService.isManager(user)"
|
||||
class="add-project-btn"
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
class="add-project-btn"
|
||||
>
|
||||
<mat-icon svgIcon="red:plus"> </mat-icon>
|
||||
<mat-icon svgIcon="red:plus"></mat-icon>
|
||||
<span translate="project-listing.add-new.label"></span>
|
||||
</button>
|
||||
</div>
|
||||
@ -73,18 +74,19 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="no-data"
|
||||
*ngIf="appStateService.allProjects?.length === 0"
|
||||
class="no-data"
|
||||
translate="project-listing.no-projects.label"
|
||||
></div>
|
||||
|
||||
<div
|
||||
class="table-item pointer"
|
||||
[routerLink]="'/ui/projects/' + pw.project.projectId"
|
||||
*ngFor="
|
||||
let pw of appStateService.allProjects
|
||||
| sortBy: sortingOption.order:sortingOption.column
|
||||
"
|
||||
[routerLink]="[canOpenProject(pw) ? '/ui/projects/' + pw.project.projectId : []]"
|
||||
class="table-item"
|
||||
[class.pointer]="canOpenProject(pw)"
|
||||
>
|
||||
<div>
|
||||
<div class="table-item-title table-item-title--large">
|
||||
@ -122,26 +124,28 @@
|
||||
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
mat-icon-button
|
||||
color="accent"
|
||||
(click)="openDeleteProjectDialog($event, pw.project)"
|
||||
[matTooltip]="'project-listing.delete.action.label' | translate"
|
||||
*ngIf="userService.isManager(user)"
|
||||
color="accent"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon svgIcon="red:trash"></mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
color="accent"
|
||||
(click)="downloadRedactionReport($event, pw.project)"
|
||||
[matTooltip]="'project-listing.report.action.label' | translate"
|
||||
color="accent"
|
||||
>
|
||||
<mat-icon svgIcon="red:report"></mat-icon>
|
||||
</button>
|
||||
<button
|
||||
color="accent"
|
||||
(click)="openAssignProjectOwnerDialog($event, pw.project)"
|
||||
mat-icon-button
|
||||
[matTooltip]="'project-listing.assign.action.label' | translate"
|
||||
*ngIf="userService.isManager(user)"
|
||||
color="accent"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon svgIcon="red:assign"></mat-icon>
|
||||
</button>
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FileUploadControllerService, Project } from '@redaction/red-ui-http';
|
||||
import { AppStateService, ProjectWrapper } from '../../state/app-state.service';
|
||||
import { UserService } from '../../user/user.service';
|
||||
import { DoughnutChartConfig } from '../../components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import { SortingOption } from '../../utils/types';
|
||||
import { groupBy } from '../../utils/functions';
|
||||
import { DialogService } from '../../dialogs/dialog.service';
|
||||
import { download } from '../../utils/file-download-utils';
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {Project} from '@redaction/red-ui-http';
|
||||
import {AppStateService, ProjectWrapper} from '../../state/app-state.service';
|
||||
import {UserService} from '../../user/user.service';
|
||||
import {DoughnutChartConfig} from '../../components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import {SortingOption} from '../../utils/types';
|
||||
import {groupBy} from '../../utils/functions';
|
||||
import {DialogService} from '../../dialogs/dialog.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-project-listing-screen',
|
||||
@ -28,8 +27,7 @@ export class ProjectListingScreenComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
public readonly appStateService: AppStateService,
|
||||
private readonly _fileUploadControllerService: FileUploadControllerService,
|
||||
private readonly _userService: UserService,
|
||||
public readonly userService: UserService,
|
||||
private readonly _dialogService: DialogService
|
||||
) {}
|
||||
|
||||
@ -54,7 +52,7 @@ export class ProjectListingScreenComponent implements OnInit {
|
||||
}
|
||||
|
||||
public get user() {
|
||||
return this._userService.user;
|
||||
return this.userService.user;
|
||||
}
|
||||
|
||||
public get totalPages() {
|
||||
@ -84,6 +82,10 @@ export class ProjectListingScreenComponent implements OnInit {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public canOpenProject(pw: ProjectWrapper): boolean {
|
||||
return this.userService.isManager(this.user) || pw.project.memberIds.includes(this.user.id);
|
||||
}
|
||||
|
||||
public openAddProjectDialog(): void {
|
||||
this._dialogService.openAddProjectDialog(() => {
|
||||
this._calculateData();
|
||||
|
||||
@ -194,6 +194,9 @@
|
||||
<button
|
||||
(click)="openDeleteFileDialog($event, fileStatus)"
|
||||
color="accent"
|
||||
*ngIf="
|
||||
userService.isManager(user) || appStateService.isActiveProjectOwner
|
||||
"
|
||||
mat-icon-button
|
||||
[matTooltip]="'project-overview.delete.action.label' | translate"
|
||||
>
|
||||
@ -208,8 +211,9 @@
|
||||
<mat-icon svgIcon="red:report"></mat-icon>
|
||||
</button>
|
||||
<button
|
||||
(click)="openAssignFileOwnerDialog($event, fileStatus)"
|
||||
(click)="openAssignFileReviewerDialog($event, fileStatus)"
|
||||
color="accent"
|
||||
*ngIf="appStateService.isActiveProjectMember"
|
||||
mat-icon-button
|
||||
[matTooltip]="'project-overview.assign.action.label' | translate"
|
||||
>
|
||||
@ -231,10 +235,18 @@
|
||||
|
||||
<div class="project-details-container right-fixed-container">
|
||||
<div class="actions-row">
|
||||
<button mat-icon-button (click)="openDeleteProjectDialog($event)">
|
||||
<button
|
||||
mat-icon-button
|
||||
*ngIf="userService.isManager(user)"
|
||||
(click)="openDeleteProjectDialog($event)"
|
||||
>
|
||||
<mat-icon svgIcon="red:trash"></mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="openEditProjectDialog($event)">
|
||||
<button
|
||||
mat-icon-button
|
||||
*ngIf="userService.isManager(user)"
|
||||
(click)="openEditProjectDialog($event)"
|
||||
>
|
||||
<mat-icon svgIcon="red:edit"></mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="downloadRedactionReport($event)">
|
||||
@ -292,7 +304,11 @@
|
||||
<div class="member" *ngIf="overflowCount">
|
||||
<div class="oval large white-dark">+{{ overflowCount }}</div>
|
||||
</div>
|
||||
<div class="member pointer" (click)="openAssignProjectMembersDialog()">
|
||||
<div
|
||||
class="member pointer"
|
||||
(click)="openAssignProjectMembersDialog()"
|
||||
*ngIf="userService.isManager(user)"
|
||||
>
|
||||
<div class="oval red-white large">+</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,235 +1,234 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {
|
||||
FileStatus,
|
||||
FileUploadControllerService,
|
||||
ReanalysisControllerService,
|
||||
StatusControllerService
|
||||
FileStatus,
|
||||
FileUploadControllerService,
|
||||
ReanalysisControllerService,
|
||||
StatusControllerService
|
||||
} from '@redaction/red-ui-http';
|
||||
import { NotificationService } from '../../notification/notification.service';
|
||||
import { AppStateService } from '../../state/app-state.service';
|
||||
import { FileDropOverlayService } from '../../upload/file-drop/service/file-drop-overlay.service';
|
||||
import { FileUploadModel } from '../../upload/model/file-upload.model';
|
||||
import { FileUploadService } from '../../upload/file-upload.service';
|
||||
import { UploadStatusOverlayService } from '../../upload/upload-status-dialog/service/upload-status-overlay.service';
|
||||
import { UserService } from '../../user/user.service';
|
||||
import { SortingOption } from '../../utils/types';
|
||||
import { DoughnutChartConfig } from '../../components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import { groupBy } from '../../utils/functions';
|
||||
import { DialogService } from '../../dialogs/dialog.service';
|
||||
import { download } from '../../utils/file-download-utils';
|
||||
import {NotificationService} from '../../notification/notification.service';
|
||||
import {AppStateService} from '../../state/app-state.service';
|
||||
import {FileDropOverlayService} from '../../upload/file-drop/service/file-drop-overlay.service';
|
||||
import {FileUploadModel} from '../../upload/model/file-upload.model';
|
||||
import {FileUploadService} from '../../upload/file-upload.service';
|
||||
import {UploadStatusOverlayService} from '../../upload/upload-status-dialog/service/upload-status-overlay.service';
|
||||
import {UserService} from '../../user/user.service';
|
||||
import {SortingOption} from '../../utils/types';
|
||||
import {DoughnutChartConfig} from '../../components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import {groupBy} from '../../utils/functions';
|
||||
import {DialogService} from '../../dialogs/dialog.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-project-overview-screen',
|
||||
templateUrl: './project-overview-screen.component.html',
|
||||
styleUrls: ['./project-overview-screen.component.scss']
|
||||
selector: 'redaction-project-overview-screen',
|
||||
templateUrl: './project-overview-screen.component.html',
|
||||
styleUrls: ['./project-overview-screen.component.scss']
|
||||
})
|
||||
export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
|
||||
private _selectedFileIds: string[] = [];
|
||||
private _selectedFileIds: string[] = [];
|
||||
|
||||
public sortingOptions: SortingOption[] = [
|
||||
{ label: 'project-overview.sorting.recent.label', order: 'desc', column: 'lastUpdated' },
|
||||
{ label: 'project-overview.sorting.oldest.label', order: 'asc', column: 'lastUpdated' },
|
||||
{
|
||||
label: 'project-overview.sorting.alphabetically.label',
|
||||
order: 'asc',
|
||||
column: 'filename'
|
||||
},
|
||||
{
|
||||
label: 'project-overview.sorting.number-of-pages.label',
|
||||
order: 'asc',
|
||||
column: 'numberOfPages'
|
||||
},
|
||||
{
|
||||
label: 'project-overview.sorting.number-of-analyses.label',
|
||||
order: 'desc',
|
||||
column: 'numberOfAnalyses'
|
||||
}
|
||||
];
|
||||
public sortingOption: SortingOption = this.sortingOptions[0];
|
||||
public documentsChartData: DoughnutChartConfig[] = [];
|
||||
|
||||
constructor(
|
||||
public readonly appStateService: AppStateService,
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _fileUploadControllerService: FileUploadControllerService,
|
||||
private readonly _statusControllerService: StatusControllerService,
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _dialogService: DialogService,
|
||||
private readonly _fileUploadService: FileUploadService,
|
||||
private readonly _uploadStatusOverlayService: UploadStatusOverlayService,
|
||||
private readonly _reanalysisControllerService: ReanalysisControllerService,
|
||||
private readonly _router: Router,
|
||||
private readonly _userService: UserService,
|
||||
private readonly _fileDropOverlayService: FileDropOverlayService
|
||||
) {
|
||||
this._activatedRoute.params.subscribe((params) => {
|
||||
this.appStateService.activateProject(params.projectId);
|
||||
});
|
||||
|
||||
this.appStateService.fileStatusChanged.subscribe(() => {
|
||||
this._calculateChartConfig();
|
||||
});
|
||||
public sortingOptions: SortingOption[] = [
|
||||
{label: 'project-overview.sorting.recent.label', order: 'desc', column: 'lastUpdated'},
|
||||
{label: 'project-overview.sorting.oldest.label', order: 'asc', column: 'lastUpdated'},
|
||||
{
|
||||
label: 'project-overview.sorting.alphabetically.label',
|
||||
order: 'asc',
|
||||
column: 'filename'
|
||||
},
|
||||
{
|
||||
label: 'project-overview.sorting.number-of-pages.label',
|
||||
order: 'asc',
|
||||
column: 'numberOfPages'
|
||||
},
|
||||
{
|
||||
label: 'project-overview.sorting.number-of-analyses.label',
|
||||
order: 'desc',
|
||||
column: 'numberOfAnalyses'
|
||||
}
|
||||
];
|
||||
public sortingOption: SortingOption = this.sortingOptions[0];
|
||||
public documentsChartData: DoughnutChartConfig[] = [];
|
||||
|
||||
ngOnInit(): void {
|
||||
this._fileDropOverlayService.initFileDropHandling();
|
||||
constructor(
|
||||
public readonly appStateService: AppStateService,
|
||||
public readonly userService: UserService,
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _fileUploadControllerService: FileUploadControllerService,
|
||||
private readonly _statusControllerService: StatusControllerService,
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _dialogService: DialogService,
|
||||
private readonly _fileUploadService: FileUploadService,
|
||||
private readonly _uploadStatusOverlayService: UploadStatusOverlayService,
|
||||
private readonly _reanalysisControllerService: ReanalysisControllerService,
|
||||
private readonly _router: Router,
|
||||
private readonly _fileDropOverlayService: FileDropOverlayService
|
||||
) {
|
||||
this._activatedRoute.params.subscribe((params) => {
|
||||
this.appStateService.activateProject(params.projectId);
|
||||
});
|
||||
|
||||
this.appStateService.fileStatusChanged.subscribe(() => {
|
||||
this._calculateChartConfig();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this._fileDropOverlayService.initFileDropHandling();
|
||||
this._calculateChartConfig();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._fileDropOverlayService.cleanupFileDropHandling();
|
||||
}
|
||||
|
||||
public get activeProject() {
|
||||
return this.appStateService.activeProject.project;
|
||||
}
|
||||
|
||||
public get user() {
|
||||
return this.userService.user;
|
||||
}
|
||||
|
||||
public get displayMembers() {
|
||||
return this.activeProject.memberIds.slice(0, 6);
|
||||
}
|
||||
|
||||
public get overflowCount() {
|
||||
return this.activeProject.memberIds.length > 6
|
||||
? this.activeProject.memberIds.length - 6
|
||||
: 0;
|
||||
}
|
||||
|
||||
private _reloadProjects() {
|
||||
this.appStateService.loadAllProjects().then(() => {
|
||||
this._calculateChartConfig();
|
||||
});
|
||||
}
|
||||
|
||||
private _calculateChartConfig() {
|
||||
const groups = groupBy(this.appStateService.activeProject.files, 'status');
|
||||
this.documentsChartData = [];
|
||||
for (const key of Object.keys(groups)) {
|
||||
this.documentsChartData.push({value: groups[key].length, color: key, label: key});
|
||||
}
|
||||
}
|
||||
|
||||
public toggleFileSelected($event: MouseEvent, file: FileStatus) {
|
||||
$event.stopPropagation();
|
||||
const idx = this._selectedFileIds.indexOf(file.fileId);
|
||||
if (idx === -1) {
|
||||
this._selectedFileIds.push(file.fileId);
|
||||
} else {
|
||||
this._selectedFileIds.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public toggleSelectAll() {
|
||||
if (this.areAllFilesSelected()) {
|
||||
this._selectedFileIds = [];
|
||||
} else {
|
||||
this._selectedFileIds = this.appStateService.activeProject.files.map(
|
||||
(file) => file.fileId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public areAllFilesSelected() {
|
||||
return (
|
||||
this.appStateService.activeProject.files.length !== 0 &&
|
||||
this._selectedFileIds.length === this.appStateService.activeProject.files.length
|
||||
);
|
||||
}
|
||||
|
||||
public isFileSelected(file: FileStatus) {
|
||||
return this._selectedFileIds.indexOf(file.fileId) !== -1;
|
||||
}
|
||||
|
||||
public openDeleteFileDialog($event: MouseEvent, fileStatus: FileStatus) {
|
||||
this._dialogService.openDeleteFileDialog(
|
||||
$event,
|
||||
fileStatus.projectId,
|
||||
fileStatus.fileId,
|
||||
() => {
|
||||
this._calculateChartConfig();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
downloadFileRedactionReport($event: MouseEvent, file: FileStatus) {
|
||||
$event.stopPropagation();
|
||||
this.appStateService.downloadFileRedactionReport(file);
|
||||
}
|
||||
|
||||
downloadRedactionReport($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
this.appStateService.downloadRedactionReport();
|
||||
}
|
||||
|
||||
public openEditProjectDialog($event: MouseEvent) {
|
||||
this._dialogService.openEditProjectDialog(
|
||||
$event,
|
||||
this.appStateService.activeProject.project
|
||||
);
|
||||
}
|
||||
|
||||
public openDeleteProjectDialog($event: MouseEvent) {
|
||||
this._dialogService.openDeleteProjectDialog(
|
||||
$event,
|
||||
this.appStateService.activeProject.project,
|
||||
() => {
|
||||
this._router.navigate(['/ui/projects']);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public openAssignProjectMembersDialog() {
|
||||
this._dialogService.openAssignProjectMembersAndOwnerDialog(null, this.activeProject, () => {
|
||||
this._reloadProjects();
|
||||
});
|
||||
}
|
||||
|
||||
public openAssignFileReviewerDialog($event: MouseEvent, file: FileStatus) {
|
||||
this._dialogService.openAssignFileReviewerDialog($event, file, () => {
|
||||
this._reloadProjects();
|
||||
});
|
||||
}
|
||||
|
||||
public reanalyseFile($event: MouseEvent, fileStatus: FileStatus) {
|
||||
$event.stopPropagation();
|
||||
this._reanalysisControllerService
|
||||
.reanalyzeFile(this.appStateService.activeProject.project.projectId, fileStatus.fileId)
|
||||
.subscribe(() => {
|
||||
this._reloadProjects();
|
||||
});
|
||||
}
|
||||
|
||||
public fileId(index, item) {
|
||||
return item.fileId;
|
||||
}
|
||||
|
||||
public uploadFiles(files: FileList | File[]) {
|
||||
const uploadFiles: FileUploadModel[] = [];
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
uploadFiles.push({
|
||||
file: file,
|
||||
progress: 0,
|
||||
completed: false,
|
||||
error: null
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._fileDropOverlayService.cleanupFileDropHandling();
|
||||
}
|
||||
this._fileUploadService.uploadFiles(uploadFiles);
|
||||
this._uploadStatusOverlayService.openStatusOverlay();
|
||||
}
|
||||
|
||||
public get activeProject() {
|
||||
return this.appStateService.activeProject.project;
|
||||
}
|
||||
public canOpenFile(fileStatus: FileStatus): boolean {
|
||||
// TODO check correct condition for this
|
||||
return fileStatus === 'PROCESSING' || fileStatus === 'REVIEWED' || true;
|
||||
}
|
||||
|
||||
public get user() {
|
||||
return this._userService.user;
|
||||
}
|
||||
|
||||
public get displayMembers() {
|
||||
return this.activeProject.memberIds.slice(0, 6);
|
||||
}
|
||||
|
||||
public get overflowCount() {
|
||||
return this.activeProject.memberIds.length > 6
|
||||
? this.activeProject.memberIds.length - 6
|
||||
: 0;
|
||||
}
|
||||
|
||||
private _getFileStatus() {
|
||||
this.appStateService.reloadActiveProjectFiles().then(() => {
|
||||
this._calculateChartConfig();
|
||||
});
|
||||
}
|
||||
|
||||
private _calculateChartConfig() {
|
||||
const groups = groupBy(this.appStateService.activeProject.files, 'status');
|
||||
this.documentsChartData = [];
|
||||
for (const key of Object.keys(groups)) {
|
||||
this.documentsChartData.push({ value: groups[key].length, color: key, label: key });
|
||||
}
|
||||
}
|
||||
|
||||
public toggleFileSelected($event: MouseEvent, file: FileStatus) {
|
||||
$event.stopPropagation();
|
||||
const idx = this._selectedFileIds.indexOf(file.fileId);
|
||||
if (idx === -1) {
|
||||
this._selectedFileIds.push(file.fileId);
|
||||
} else {
|
||||
this._selectedFileIds.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public toggleSelectAll() {
|
||||
if (this.areAllFilesSelected()) {
|
||||
this._selectedFileIds = [];
|
||||
} else {
|
||||
this._selectedFileIds = this.appStateService.activeProject.files.map(
|
||||
(file) => file.fileId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public areAllFilesSelected() {
|
||||
return (
|
||||
this.appStateService.activeProject.files.length !== 0 &&
|
||||
this._selectedFileIds.length === this.appStateService.activeProject.files.length
|
||||
);
|
||||
}
|
||||
|
||||
public isFileSelected(file: FileStatus) {
|
||||
return this._selectedFileIds.indexOf(file.fileId) !== -1;
|
||||
}
|
||||
|
||||
public openDeleteFileDialog($event: MouseEvent, fileStatus: FileStatus) {
|
||||
this._dialogService.openDeleteFileDialog(
|
||||
$event,
|
||||
fileStatus.projectId,
|
||||
fileStatus.fileId,
|
||||
() => {
|
||||
this._calculateChartConfig();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
downloadFileRedactionReport($event: MouseEvent, file: FileStatus) {
|
||||
$event.stopPropagation();
|
||||
this.appStateService.downloadFileRedactionReport(file);
|
||||
}
|
||||
|
||||
downloadRedactionReport($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
this.appStateService.downloadRedactionReport();
|
||||
}
|
||||
|
||||
public openEditProjectDialog($event: MouseEvent) {
|
||||
this._dialogService.openEditProjectDialog(
|
||||
$event,
|
||||
this.appStateService.activeProject.project
|
||||
);
|
||||
}
|
||||
|
||||
public openDeleteProjectDialog($event: MouseEvent) {
|
||||
this._dialogService.openDeleteProjectDialog(
|
||||
$event,
|
||||
this.appStateService.activeProject.project,
|
||||
() => {
|
||||
this._router.navigate(['/ui/projects']);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public openAssignProjectMembersDialog() {
|
||||
this._dialogService.openAssignProjectMembersAndOwnerDialog(null, this.activeProject, () => {
|
||||
this._getFileStatus();
|
||||
});
|
||||
}
|
||||
|
||||
public openAssignFileOwnerDialog($event: MouseEvent, file: FileStatus) {
|
||||
this._dialogService.openAssignFileOwnerDialog($event, file, () => {
|
||||
this._getFileStatus();
|
||||
});
|
||||
}
|
||||
|
||||
public reanalyseFile($event: MouseEvent, fileStatus: FileStatus) {
|
||||
$event.stopPropagation();
|
||||
this._reanalysisControllerService
|
||||
.reanalyzeFile(this.appStateService.activeProject.project.projectId, fileStatus.fileId)
|
||||
.subscribe(() => {
|
||||
this._getFileStatus();
|
||||
});
|
||||
}
|
||||
|
||||
public fileId(index, item) {
|
||||
return item.fileId;
|
||||
}
|
||||
|
||||
public uploadFiles(files: FileList | File[]) {
|
||||
const uploadFiles: FileUploadModel[] = [];
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
uploadFiles.push({
|
||||
file: file,
|
||||
progress: 0,
|
||||
completed: false,
|
||||
error: null
|
||||
});
|
||||
}
|
||||
|
||||
this._fileUploadService.uploadFiles(uploadFiles);
|
||||
this._uploadStatusOverlayService.openStatusOverlay();
|
||||
}
|
||||
|
||||
public canOpenFile(fileStatus: FileStatus): boolean {
|
||||
// TODO check correct condition for this
|
||||
return fileStatus === 'PROCESSING' || fileStatus === 'REVIEWED' || true;
|
||||
}
|
||||
|
||||
public toggleSortByAddedOn() {
|
||||
const sortedByRecent: boolean = this.sortingOption === this.sortingOptions[0];
|
||||
this.sortingOption = sortedByRecent ? this.sortingOptions[1] : this.sortingOptions[0];
|
||||
}
|
||||
public toggleSortByAddedOn() {
|
||||
const sortedByRecent: boolean = this.sortingOption === this.sortingOptions[0];
|
||||
this.sortingOption = sortedByRecent ? this.sortingOptions[1] : this.sortingOptions[0];
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,9 +90,7 @@ export class AppStateService {
|
||||
}
|
||||
|
||||
get isActiveProjectMember() {
|
||||
return (
|
||||
this._appState.activeProject?.project?.memberIds?.indexOf(this._userService.userId) >= 0
|
||||
);
|
||||
return this._appState.activeProject?.project?.memberIds?.includes(this._userService.userId);
|
||||
}
|
||||
|
||||
get dictionaryData() {
|
||||
|
||||
@ -99,11 +99,11 @@ export class UserService {
|
||||
: undefined;
|
||||
}
|
||||
|
||||
isManager(user: User) {
|
||||
isManager(user: User): boolean {
|
||||
return user.roles.indexOf('RED_MANAGER') >= 0;
|
||||
}
|
||||
|
||||
isUser(user: User) {
|
||||
isUser(user: User): boolean {
|
||||
return user.roles.indexOf('RED_USER') >= 0;
|
||||
}
|
||||
|
||||
|
||||
@ -527,6 +527,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"project-member-guard": {
|
||||
"access-denied": {
|
||||
"label": "You are not allowed to access that page."
|
||||
}
|
||||
},
|
||||
"unassigned": "Unassigned",
|
||||
"under-review": "Under review",
|
||||
"under-approval": "Under approval",
|
||||
|
||||
0
package-lock.json
generated
0
package-lock.json
generated
Loading…
x
Reference in New Issue
Block a user