Pull request #17: Ui updates
Merge in RED/ui from ui-updates to master * commit '13802f4082163edec4de000980cab1a1758492a3': Select files in project overview Added legend Fixed assign members Moved all dialogs into dialog service Removed all dialogs from project listing Moved some dialogs into a service Moved dialogs in separate directory Display filename in top bar Center logo in menu bar Show annotation comment - not very nice
This commit is contained in:
commit
b77b58c74a
@ -20,7 +20,7 @@ import { languageInitializer } from './i18n/language.initializer';
|
||||
import { LanguageService } from './i18n/language.service';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { IconsModule } from './icons/icons.module';
|
||||
import { AddEditProjectDialogComponent } from './screens/project-listing-screen/add-edit-project-dialog/add-edit-project-dialog.component';
|
||||
import { AddEditProjectDialogComponent } from './dialogs/add-edit-project-dialog/add-edit-project-dialog.component';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
@ -33,11 +33,11 @@ import { NgpSortModule } from 'ngp-sort-pipe';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { FileDetailsDialogComponent } from './screens/file/file-preview-screen/file-details-dialog/file-details-dialog.component';
|
||||
import { FileDetailsDialogComponent } from './dialogs/file-details-dialog/file-details-dialog.component';
|
||||
import { ToastrModule } from 'ngx-toastr';
|
||||
import { ServiceWorkerModule } from '@angular/service-worker';
|
||||
import { environment } from '../environments/environment';
|
||||
import { ProjectDetailsDialogComponent } from './screens/project-overview-screen/project-details-dialog/project-details-dialog.component';
|
||||
import { ProjectDetailsDialogComponent } from './dialogs/project-details-dialog/project-details-dialog.component';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { FileUploadModule } from './upload/file-upload.module';
|
||||
import { FullPageLoadingIndicatorComponent } from './utils/full-page-loading-indicator/full-page-loading-indicator.component';
|
||||
@ -48,15 +48,16 @@ import { LogoComponent } from './logo/logo.component';
|
||||
import { CompositeRouteGuard } from './utils/composite-route.guard';
|
||||
import { AppStateGuard } from './state/app-state.guard';
|
||||
import { SimpleDoughnutChartComponent } from './components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import { ManualRedactionDialogComponent } from './screens/file/manual-redaction-dialog/manual-redaction-dialog.component';
|
||||
import { ManualRedactionDialogComponent } from './dialogs/manual-redaction-dialog/manual-redaction-dialog.component';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||
import { AnnotationIconComponent } from './components/annotation-icon/annotation-icon.component';
|
||||
import { AuthGuard } from './auth/auth.guard';
|
||||
import { AuthErrorComponent } from './screens/auth-error/auth-error.component';
|
||||
import { RedRoleGuard } from './auth/red-role.guard';
|
||||
import { AssignProjectMembersDialogComponent } from './dialogs/assign-project-members-dialog/assign-project-members-dialog.component';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { AssignOwnerDialogComponent } from './components/project-members-dialog/assign-owner-dialog.component';
|
||||
import { AssignOwnerDialogComponent } from './dialogs/assign-owner-dialog/assign-owner-dialog.component';
|
||||
|
||||
export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
|
||||
@ -74,6 +75,7 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||
PdfViewerComponent,
|
||||
FileDetailsDialogComponent,
|
||||
ProjectDetailsDialogComponent,
|
||||
AssignProjectMembersDialogComponent,
|
||||
AssignOwnerDialogComponent,
|
||||
FullPageLoadingIndicatorComponent,
|
||||
InitialsAvatarComponent,
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
@import "../../../assets/styles/red-variables";
|
||||
|
||||
.owner {
|
||||
background-color: rgba($primary, 0.1);
|
||||
}
|
||||
@ -1,91 +0,0 @@
|
||||
import {Component, Inject} from '@angular/core';
|
||||
import {FileStatus, Project, ProjectControllerService, 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 '../../user/user.service';
|
||||
import {NotificationService} from '../../notification/notification.service';
|
||||
import {FormBuilder, FormGroup, Validators} from "@angular/forms";
|
||||
|
||||
class DialogData {
|
||||
type: 'file' | 'project';
|
||||
project?: Project;
|
||||
file?: FileStatus;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-project-details-dialog',
|
||||
templateUrl: './assign-owner-dialog.component.html',
|
||||
styleUrls: ['./assign-owner-dialog.component.scss']
|
||||
})
|
||||
export class AssignOwnerDialogComponent {
|
||||
|
||||
usersForm: FormGroup;
|
||||
|
||||
constructor(public readonly userService: UserService,
|
||||
private readonly _projectControllerService: ProjectControllerService,
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _statusControllerService: StatusControllerService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
public dialogRef: MatDialogRef<AssignOwnerDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: DialogData) {
|
||||
this._loadData();
|
||||
}
|
||||
|
||||
|
||||
private _loadData() {
|
||||
if (this.data.type === 'project') {
|
||||
const project = this.data.project;
|
||||
|
||||
this.usersForm = this._formBuilder.group({
|
||||
singleUser: [project?.ownerId, Validators.required],
|
||||
userList: [project?.memberIds]
|
||||
});
|
||||
}
|
||||
|
||||
if (this.data.type === 'file') {
|
||||
const file = this.data.file;
|
||||
this.usersForm = this._formBuilder.group({
|
||||
singleUser: [file?.currentReviewer],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async saveUsers() {
|
||||
if (this.data.type === 'project') {
|
||||
|
||||
const ownerId = this.usersForm.get('singleUser').value;
|
||||
const memberIds = this.usersForm.get('userList').value;
|
||||
const project = this.data.project;
|
||||
|
||||
await this._projectControllerService.addMembersToProject({memberIds: memberIds}, project.projectId).toPromise();
|
||||
await this._projectControllerService.assignProjectOwner(project.projectId, ownerId).toPromise();
|
||||
|
||||
const updatedProject = await this._projectControllerService.getProject(project.projectId).toPromise();
|
||||
|
||||
const toRemoveMembers = updatedProject.memberIds.filter(m => memberIds.indexOf(m) < 0);
|
||||
if (toRemoveMembers.length > 0) {
|
||||
await this._projectControllerService.deleteMembersToProject({memberIds: toRemoveMembers}, project.projectId).toPromise();
|
||||
}
|
||||
|
||||
project.ownerId = ownerId;
|
||||
project.memberIds = [...new Set([ownerId, ...memberIds])];
|
||||
|
||||
this._notificationService.showToastNotification('Successfully assigned ' + this.userService.getNameForId(ownerId) + ' to project: ' + project.projectName);
|
||||
//this._notificationService.showToastNotification('Failed: ' + error.error.message, null, NotificationType.ERROR);
|
||||
}
|
||||
|
||||
if (this.data.type === 'file') {
|
||||
|
||||
const reviewerId = this.usersForm.get('singleUser').value;
|
||||
|
||||
await this._statusControllerService.assignProjectOwner1(this._appStateService.activeProjectId, this.data.file.fileId, reviewerId).toPromise();
|
||||
this.data.file.currentReviewer = reviewerId;
|
||||
this._notificationService.showToastNotification('Successfully assigned ' + this.userService.getNameForId(reviewerId) + ' to file: ' + this.data.file.filename);
|
||||
|
||||
}
|
||||
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
}
|
||||
@ -2,7 +2,7 @@ import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
import {Project} from "@redaction/red-ui-http";
|
||||
import {FormBuilder, FormGroup, Validators} from "@angular/forms";
|
||||
import {AppStateService} from "../../../state/app-state.service";
|
||||
import {AppStateService} from "../../state/app-state.service";
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-add-edit-project-dialog',
|
||||
@ -32,7 +32,7 @@ export class AddEditProjectDialogComponent implements OnInit {
|
||||
const project: Project = this._formToObject();
|
||||
project.projectId = this.project?.projectId;
|
||||
await this._appStateService.addOrUpdateProject(project);
|
||||
this.dialogRef.close();
|
||||
this.dialogRef.close(true);
|
||||
}
|
||||
|
||||
private _formToObject(): Project {
|
||||
@ -0,0 +1,93 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { FileStatus, Project, ProjectControllerService, 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 '../../user/user.service';
|
||||
import { NotificationService, NotificationType } from '../../notification/notification.service';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
|
||||
class DialogData {
|
||||
type: 'file' | 'project';
|
||||
project?: Project;
|
||||
file?: FileStatus;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-project-details-dialog',
|
||||
templateUrl: './assign-owner-dialog.component.html',
|
||||
styleUrls: ['./assign-owner-dialog.component.scss']
|
||||
})
|
||||
export class AssignOwnerDialogComponent {
|
||||
|
||||
usersForm: FormGroup;
|
||||
|
||||
constructor(public readonly userService: UserService,
|
||||
private readonly _projectControllerService: ProjectControllerService,
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _statusControllerService: StatusControllerService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
public dialogRef: MatDialogRef<AssignOwnerDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: DialogData) {
|
||||
this._loadData();
|
||||
}
|
||||
|
||||
|
||||
private _loadData() {
|
||||
if (this.data.type === 'project') {
|
||||
const project = this.data.project;
|
||||
this.usersForm = this._formBuilder.group({
|
||||
singleUser: [project?.ownerId, Validators.required],
|
||||
userList: [project?.memberIds]
|
||||
});
|
||||
}
|
||||
|
||||
if (this.data.type === 'file') {
|
||||
const file = this.data.file;
|
||||
this.usersForm = this._formBuilder.group({
|
||||
singleUser: [file?.currentReviewer]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async saveUsers() {
|
||||
try {
|
||||
if (this.data.type === 'project') {
|
||||
|
||||
const ownerId = this.usersForm.get('singleUser').value;
|
||||
const memberIds = this.usersForm.get('userList').value;
|
||||
const project = this.data.project;
|
||||
await this._projectControllerService.addMembersToProject({ memberIds: memberIds }, project.projectId).toPromise();
|
||||
await this._projectControllerService.assignProjectOwner(project.projectId, ownerId).toPromise();
|
||||
|
||||
const updatedProject = await this._projectControllerService.getProject(project.projectId).toPromise();
|
||||
|
||||
const toRemoveMembers = updatedProject.memberIds.filter(m => memberIds.indexOf(m) < 0);
|
||||
if (toRemoveMembers.length > 0) {
|
||||
await this._projectControllerService.deleteMembersToProject({ memberIds: toRemoveMembers }, project.projectId).toPromise();
|
||||
}
|
||||
|
||||
project.ownerId = ownerId;
|
||||
project.memberIds = [...new Set([ownerId, ...memberIds])];
|
||||
|
||||
this._notificationService.showToastNotification('Successfully assigned ' + this.userService.getNameForId(ownerId) + ' to project: ' + project.projectName);
|
||||
|
||||
}
|
||||
|
||||
if (this.data.type === 'file') {
|
||||
|
||||
const reviewerId = this.usersForm.get('singleUser').value;
|
||||
|
||||
await this._statusControllerService.assignProjectOwner1(this._appStateService.activeProjectId, this.data.file.fileId, reviewerId).toPromise();
|
||||
this.data.file.currentReviewer = reviewerId;
|
||||
this._notificationService.showToastNotification('Successfully assigned ' + this.userService.getNameForId(reviewerId) + ' to file: ' + this.data.file.filename);
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
this._notificationService.showToastNotification('Failed: ' + error.error.message, null, NotificationType.ERROR);
|
||||
}
|
||||
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
<section class="dialog">
|
||||
<div [translate]="'project-members.dialog.title.label'"
|
||||
class="dialog-header heading-l">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="dialog-content">
|
||||
<mat-selection-list class="list-50vh" color="primary"
|
||||
[(ngModel)]="memberIds"
|
||||
(selectionChange)="selectionChange($event)">
|
||||
<ng-container *ngFor="let user of userService.allUsers">
|
||||
<mat-list-option *ngIf="userService.isManager(user) || userService.isUser(user)" [value]="user.userId" checkboxPosition="before">
|
||||
{{ userService.getNameForId(user.userId) }}
|
||||
</mat-list-option>
|
||||
</ng-container>
|
||||
</mat-selection-list>
|
||||
</div>
|
||||
|
||||
<button (click)="dialogRef.close()" class="dialog-close" mat-icon-button>
|
||||
<mat-icon svgIcon="red:close"></mat-icon>
|
||||
</button>
|
||||
</section>
|
||||
@ -0,0 +1,55 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { Project, ProjectControllerService } from '@redaction/red-ui-http';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { AppStateService } from '../../state/app-state.service';
|
||||
import { UserService } from '../../user/user.service';
|
||||
import { MatSelectionListChange } from '@angular/material/list';
|
||||
import { NotificationService, NotificationType } from '../../notification/notification.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-project-details-dialog',
|
||||
templateUrl: './assign-project-members-dialog.component.html',
|
||||
styleUrls: ['./assign-project-members-dialog.component.scss']
|
||||
})
|
||||
export class AssignProjectMembersDialogComponent {
|
||||
public memberIds: string[];
|
||||
|
||||
constructor(private readonly _projectControllerService: ProjectControllerService,
|
||||
private readonly _notificationService: NotificationService,
|
||||
public readonly userService: UserService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
public dialogRef: MatDialogRef<AssignProjectMembersDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public _project: Project) {
|
||||
this._loadMembers();
|
||||
}
|
||||
|
||||
private _loadMembers() {
|
||||
this.memberIds = [...this._project.memberIds];
|
||||
}
|
||||
|
||||
private _reloadProject() {
|
||||
this._appStateService.addOrUpdateProject(this._project).then(() => {
|
||||
this._loadMembers();
|
||||
});
|
||||
}
|
||||
|
||||
public selectionChange($event: MatSelectionListChange) {
|
||||
const userId = $event.option.value;
|
||||
const selected = $event.option.selected;
|
||||
const userName = this.userService.getNameForId(userId);
|
||||
|
||||
if (selected) {
|
||||
this._projectControllerService.addMembersToProject({ memberIds: [userId] }, this._project.projectId).subscribe(() => {
|
||||
this._notificationService.showToastNotification('Successfully assigned ' + userName + ' to project: ' + this._project.projectName);
|
||||
}, error => {
|
||||
this._notificationService.showToastNotification('Failed: ' + error.error.message, null, NotificationType.ERROR);
|
||||
}).add(() => this._reloadProject());
|
||||
} else {
|
||||
this._projectControllerService.deleteMembersToProject({ memberIds: [userId] }, this._project.projectId).subscribe(() => {
|
||||
this._notificationService.showToastNotification('Successfully removed ' + userName + ' from project: ' + this._project.projectName);
|
||||
}, error => {
|
||||
this._notificationService.showToastNotification('Failed: ' + error.error.message, null, NotificationType.ERROR);
|
||||
}).add(() => this._reloadProject());
|
||||
}
|
||||
}
|
||||
}
|
||||
178
apps/red-ui/src/app/dialogs/dialog.service.ts
Normal file
178
apps/red-ui/src/app/dialogs/dialog.service.ts
Normal file
@ -0,0 +1,178 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { FileDetailsDialogComponent } from './file-details-dialog/file-details-dialog.component';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import {
|
||||
FileStatus,
|
||||
FileUploadControllerService,
|
||||
ManualRedactionControllerService,
|
||||
ManualRedactionEntry,
|
||||
Project
|
||||
} from '@redaction/red-ui-http';
|
||||
import { ConfirmationDialogComponent } from '../common/confirmation-dialog/confirmation-dialog.component';
|
||||
import { NotificationService, NotificationType } from '../notification/notification.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { AppStateService, ProjectWrapper } from '../state/app-state.service';
|
||||
import { AddEditProjectDialogComponent } from './add-edit-project-dialog/add-edit-project-dialog.component';
|
||||
import { AssignOwnerDialogComponent } from './assign-owner-dialog/assign-owner-dialog.component';
|
||||
import { ProjectDetailsDialogComponent } from './project-details-dialog/project-details-dialog.component';
|
||||
import { AssignProjectMembersDialogComponent } from './assign-project-members-dialog/assign-project-members-dialog.component';
|
||||
import { ManualRedactionDialogComponent } from './manual-redaction-dialog/manual-redaction-dialog.component';
|
||||
import { Annotations } from '@pdftron/webviewer';
|
||||
|
||||
const dialogConfig = {
|
||||
width: '600px',
|
||||
maxWidth: '90vw',
|
||||
autoFocus: false
|
||||
};
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class DialogService {
|
||||
|
||||
constructor(private readonly _dialog: MatDialog,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _fileUploadControllerService: FileUploadControllerService,
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _manualRedactionControllerService: ManualRedactionControllerService) {
|
||||
|
||||
}
|
||||
|
||||
public openFileDetailsDialog($event: MouseEvent, file: FileStatus) {
|
||||
$event.stopPropagation();
|
||||
this._dialog.open(FileDetailsDialogComponent, {
|
||||
...dialogConfig,
|
||||
data: file
|
||||
});
|
||||
}
|
||||
|
||||
public openDeleteFileDialog($event: MouseEvent, projectId: string, fileId: string, cb?: Function) {
|
||||
$event.stopPropagation();
|
||||
this._dialog.open(ConfirmationDialogComponent, dialogConfig).afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
const file = this._appStateService.getFileById(projectId, fileId);
|
||||
this._fileUploadControllerService.deleteFile(file.projectId, file.fileId).subscribe(async () => {
|
||||
await this._appStateService.reloadActiveProjectFiles();
|
||||
if (cb) cb();
|
||||
}, () => {
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant('delete-file-error.label', file),
|
||||
null,
|
||||
NotificationType.ERROR);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public openManualRedactionDialog($event: ManualRedactionEntry) {
|
||||
this._dialog.open(ManualRedactionDialogComponent, {
|
||||
...dialogConfig,
|
||||
autoFocus: true,
|
||||
data: $event
|
||||
});
|
||||
}
|
||||
|
||||
public acceptSuggestionAnnotation($event: MouseEvent, annotation: Annotations.Annotation, projectId: string, fileId: string) {
|
||||
$event.stopPropagation();
|
||||
|
||||
const parts = annotation.Id.split(':');
|
||||
const annotationId = parts[parts.length - 1];
|
||||
|
||||
this._dialog.open(ConfirmationDialogComponent, dialogConfig)
|
||||
.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this._manualRedactionControllerService.approveRequest(projectId, fileId, annotationId).subscribe(() => {
|
||||
this._notificationService.showToastNotification(this._translateService.instant('manual-redaction.confirm-annotation.success.label'), null, NotificationType.SUCCESS);
|
||||
}, (err) => {
|
||||
this._notificationService.showToastNotification(this._translateService.instant('manual-redaction.confirm-annotation.failed.label', err), null, NotificationType.ERROR);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public suggestRemoveAnnotation($event: MouseEvent, annotation: Annotations.Annotation, projectId: string, fileId: string) {
|
||||
$event.stopPropagation();
|
||||
|
||||
const parts = annotation.Id.split(':');
|
||||
const annotationId = parts[parts.length - 1];
|
||||
|
||||
this._dialog.open(ConfirmationDialogComponent, {
|
||||
width: '400px',
|
||||
maxWidth: '90vw'
|
||||
}).afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this._manualRedactionControllerService.undo(projectId, fileId, annotationId).subscribe(ok => {
|
||||
this._notificationService.showToastNotification(this._translateService.instant('manual-redaction.remove-annotation.success.label'), null, NotificationType.SUCCESS);
|
||||
}, (err) => {
|
||||
this._notificationService.showToastNotification(this._translateService.instant('manual-redaction.remove-annotation.failed.label', err), null, NotificationType.ERROR);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public openEditProjectDialog($event: MouseEvent, project: Project) {
|
||||
$event.stopPropagation();
|
||||
this._dialog.open(AddEditProjectDialogComponent, {
|
||||
...dialogConfig,
|
||||
autoFocus: true,
|
||||
data: project
|
||||
});
|
||||
}
|
||||
|
||||
public openDeleteProjectDialog($event: MouseEvent, project: Project, cb?: Function) {
|
||||
$event.stopPropagation();
|
||||
this._dialog.open(ConfirmationDialogComponent, dialogConfig)
|
||||
.afterClosed().subscribe(async result => {
|
||||
if (result) {
|
||||
await this._appStateService.deleteProject(project);
|
||||
if (cb) cb();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public openAssignProjectOwnerDialog($event: MouseEvent, project: Project) {
|
||||
$event.stopPropagation();
|
||||
this._dialog.open(AssignOwnerDialogComponent, {
|
||||
...dialogConfig,
|
||||
data: { type: 'project', project: project }
|
||||
});
|
||||
}
|
||||
|
||||
public openAssignFileOwnerDialog($event: MouseEvent, file: FileStatus, cb?: Function) {
|
||||
$event.stopPropagation();
|
||||
this._dialog.open(AssignOwnerDialogComponent, {
|
||||
...dialogConfig,
|
||||
data: { type: 'file', file: file }
|
||||
}).afterClosed().subscribe(() => {
|
||||
if (cb) cb();
|
||||
});
|
||||
}
|
||||
|
||||
public openProjectDetailsDialog($event: MouseEvent, project: ProjectWrapper) {
|
||||
$event.stopPropagation();
|
||||
this._dialog.open(ProjectDetailsDialogComponent, {
|
||||
...dialogConfig,
|
||||
data: project
|
||||
});
|
||||
}
|
||||
|
||||
public openAddProjectDialog(cb?: Function): void {
|
||||
this._dialog.open(AddEditProjectDialogComponent, {
|
||||
...dialogConfig,
|
||||
autoFocus: true
|
||||
}).afterClosed().subscribe(result => {
|
||||
if (result && cb) cb();
|
||||
});
|
||||
}
|
||||
|
||||
public openAssignProjectMembersDialog(project: Project, cb?: Function) {
|
||||
this._dialog.open(AssignProjectMembersDialogComponent, {
|
||||
...dialogConfig,
|
||||
data: project
|
||||
}).afterClosed().subscribe(result => {
|
||||
if (result && cb) cb();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
@import "../../../assets/styles/red-variables";
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {FileStatus, FileUploadControllerService} from "@redaction/red-ui-http";
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
import {download} from "../../../../utils/file-download-utils";
|
||||
import {download} from "../../utils/file-download-utils";
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-file-details-dialog',
|
||||
@ -1,6 +1,6 @@
|
||||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {FormBuilder, FormGroup, Validators} from "@angular/forms";
|
||||
import {AppStateService} from "../../../state/app-state.service";
|
||||
import {AppStateService} from "../../state/app-state.service";
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
import {
|
||||
AddRedactionRequest,
|
||||
@ -8,11 +8,11 @@ import {
|
||||
ManualRedactionControllerService,
|
||||
TypeValue
|
||||
} from "@redaction/red-ui-http";
|
||||
import {NotificationService, NotificationType} from "../../../notification/notification.service";
|
||||
import {NotificationService, NotificationType} from "../../notification/notification.service";
|
||||
import {TranslateService} from "@ngx-translate/core";
|
||||
import {map} from "rxjs/operators";
|
||||
import {Observable} from "rxjs";
|
||||
import {UserService} from "../../../user/user.service";
|
||||
import {UserService} from "../../user/user.service";
|
||||
|
||||
|
||||
@Component({
|
||||
@ -1,8 +1,8 @@
|
||||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {FileUploadControllerService, ReanalysisControllerService} from "@redaction/red-ui-http";
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
import {download} from "../../../utils/file-download-utils";
|
||||
import {ProjectWrapper} from "../../../state/app-state.service";
|
||||
import {download} from "../../utils/file-download-utils";
|
||||
import {ProjectWrapper} from "../../state/app-state.service";
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-project-details-dialog',
|
||||
@ -1,6 +1,6 @@
|
||||
<div class="red-top-bar">
|
||||
<div class="top-bar-row">
|
||||
<div class="menu left visible-lt-lg">
|
||||
<div class="menu visible-lt-lg">
|
||||
<button [matMenuTriggerFor]="menuNav" mat-flat-button>
|
||||
<mat-icon svgIcon="red:menu"></mat-icon>
|
||||
</button>
|
||||
@ -16,29 +16,29 @@
|
||||
mat-menu-item>{{appStateService.activeFile.filename}}</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
<div class="menu left visible-lg">
|
||||
<div class="menu flex-2 visible-lg">
|
||||
<a class="breadcrumb" routerLink="/ui/projects"
|
||||
translate="top-bar.navigation-items.projects.label"></a>
|
||||
<div *ngIf="appStateService.activeProject" class="breadcrumb">
|
||||
<mat-icon svgIcon="red:double-chevron-right"></mat-icon>
|
||||
<mat-icon>arrow_right</mat-icon>
|
||||
</div>
|
||||
<a *ngIf="appStateService.activeProject" class="breadcrumb"
|
||||
[routerLink]="'/ui/projects/'+appStateService.activeProjectId">
|
||||
{{appStateService.activeProject.project.projectName}}
|
||||
</a>
|
||||
<div *ngIf="appStateService.activeFile" class="breadcrumb">
|
||||
<mat-icon svgIcon="red:double-chevron-right"></mat-icon>
|
||||
<mat-icon>arrow_right</mat-icon>
|
||||
</div>
|
||||
<a *ngIf="appStateService.activeFile" class="breadcrumb"
|
||||
[routerLink]="'/ui/projects/'+appStateService.activeProjectId+'/file/'+appStateService.activeFile.fileId">
|
||||
{{appStateService.activeFile.filename}}
|
||||
</a>
|
||||
</div>
|
||||
<div class="center">
|
||||
<div class="center flex-1">
|
||||
<redaction-logo></redaction-logo>
|
||||
<div class="app-name" translate="app-name.label"></div>
|
||||
</div>
|
||||
<div class="menu right">
|
||||
<div class="menu right flex-2">
|
||||
<button [matMenuTriggerFor]="menu" mat-button>
|
||||
<redaction-initials-avatar color="red-white" size="small" [username]="user?.name" [withName]="true"></redaction-initials-avatar>
|
||||
<mat-icon>arrow_drop_down</mat-icon>
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
@import "../../../../../assets/styles/red-variables";
|
||||
|
||||
@ -1,9 +1,36 @@
|
||||
<section [class.hidden]="!viewReady">
|
||||
<div class="page-header">
|
||||
<mat-slide-toggle color="primary"
|
||||
[(ngModel)]="redactedView"
|
||||
translate="file-preview.view-toggle.label"></mat-slide-toggle>
|
||||
<div>
|
||||
<div class="flex-1">
|
||||
<mat-slide-toggle color="primary"
|
||||
labelPosition="after"
|
||||
[(ngModel)]="redactedView">
|
||||
{{ "file-preview.view-toggle.label" | translate }}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 filename page-title">
|
||||
{{ appStateService.activeFile.filename }}
|
||||
</div>
|
||||
|
||||
<div class="flex-1 actions-container">
|
||||
<div class="actions-row">
|
||||
<button mat-icon-button (click)="openDeleteFileDialog($event)">
|
||||
<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)">
|
||||
<mat-icon svgIcon="red:assign"></mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="reanalyseFile($event)">
|
||||
<mat-icon svgIcon="red:analyse"></mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="openFileDetailsDialog($event)">
|
||||
<mat-icon svgIcon="red:info"></mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button color="primary" mat-flat-button
|
||||
[matMenuTriggerFor]="downloadMenu">
|
||||
<span translate="file-preview.download.label"></span>
|
||||
@ -32,12 +59,12 @@
|
||||
[fileStatus]="appStateService.activeFile"
|
||||
(fileReady)="fileReady('ANNOTATED')"
|
||||
(pageChanged)="viewerPageChanged($event)"
|
||||
(manualAnnotationRequested)="handleManualAnnotationRequest($event)"
|
||||
(manualAnnotationRequested)="openManualRedactionDialog($event)"
|
||||
(annotationSelected)="handleAnnotationSelected($event)"
|
||||
(annotationsAdded)="handleAnnotationsAdded($event)"></redaction-pdf-viewer>
|
||||
<redaction-pdf-viewer [class.visible]="activeViewer === 'REDACTED'" [fileId]="fileId" fileType="REDACTED"
|
||||
(pageChanged)="viewerPageChanged($event)"
|
||||
(manualAnnotationRequested)="handleManualAnnotationRequest($event)"
|
||||
(manualAnnotationRequested)="openManualRedactionDialog($event)"
|
||||
(fileReady)="fileReady('REDACTED')"></redaction-pdf-viewer>
|
||||
</div>
|
||||
|
||||
@ -124,6 +151,9 @@
|
||||
<div><strong><span translate="dictionary"></span>: </strong>{{getDictionary(annotation)}}</div>
|
||||
<div *ngIf="annotation.getContents()"><strong><span translate="content"></span>:
|
||||
</strong>{{annotation.getContents()}}</div>
|
||||
<div *ngIf="annotation.Mi[0]?.eC">
|
||||
<strong><span translate="comment"></span>:</strong> {{ annotation.Mi[0].eC }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-number">
|
||||
@ -144,9 +174,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button (click)="showDetailsDialog($event)" aria-label="details" class="details-button" color="primary" mat-fab>
|
||||
<mat-icon svgIcon="red:info"></mat-icon>
|
||||
</button>
|
||||
</section>
|
||||
|
||||
<redaction-full-page-loading-indicator [displayed]="!viewReady"></redaction-full-page-loading-indicator>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
@import "../../../../assets/styles/red-variables.scss";
|
||||
@import "../../../../assets/styles/red-variables";
|
||||
@import "../../../../assets/styles/red-mixins";
|
||||
|
||||
redaction-pdf-viewer {
|
||||
position: absolute;
|
||||
@ -11,6 +12,18 @@ redaction-pdf-viewer {
|
||||
}
|
||||
}
|
||||
|
||||
.actions-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.filename {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.right-fixed-container {
|
||||
padding: 0;
|
||||
width: $right-container-width;
|
||||
@ -92,6 +105,7 @@ redaction-pdf-viewer {
|
||||
position: relative;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
border-left: 2px solid transparent;
|
||||
|
||||
redaction-annotation-icon {
|
||||
margin-top: 6px;
|
||||
|
||||
@ -1,23 +1,11 @@
|
||||
import { ChangeDetectorRef, Component, ElementRef, NgZone, OnInit, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import {
|
||||
AddRedactionRequest,
|
||||
FileUploadControllerService,
|
||||
ManualRedactionControllerService,
|
||||
ManualRedactionEntry,
|
||||
ProjectControllerService,
|
||||
StatusControllerService
|
||||
} from '@redaction/red-ui-http';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import {NotificationService, NotificationType} from '../../../notification/notification.service';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ManualRedactionEntry, ReanalysisControllerService } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '../../../state/app-state.service';
|
||||
import { FileDetailsDialogComponent } from './file-details-dialog/file-details-dialog.component';
|
||||
import { ViewerSyncService } from '../service/viewer-sync.service';
|
||||
import { Annotations } from '@pdftron/webviewer';
|
||||
import { PdfViewerComponent } from '../pdf-viewer/pdf-viewer.component';
|
||||
import { AnnotationUtils } from '../../../utils/annotation-utils';
|
||||
import { ManualRedactionDialogComponent } from '../manual-redaction-dialog/manual-redaction-dialog.component';
|
||||
import { UserService } from '../../../user/user.service';
|
||||
import { debounce } from '../../../utils/debounce';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
@ -26,7 +14,7 @@ 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 { ConfirmationDialogComponent } from '../../../common/confirmation-dialog/confirmation-dialog.component';
|
||||
import { DialogService } from '../../../dialogs/dialog.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-file-preview-screen',
|
||||
@ -54,17 +42,12 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
public readonly appStateService: AppStateService,
|
||||
private readonly _changeDetectorRef: ChangeDetectorRef,
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _statusControllerService: StatusControllerService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _viewerSyncService: ViewerSyncService,
|
||||
private readonly _dialog: MatDialog,
|
||||
private readonly _dialogService: DialogService,
|
||||
private readonly _router: Router,
|
||||
private readonly _manualRedactionControllerService: ManualRedactionControllerService,
|
||||
private readonly _userService: UserService,
|
||||
private readonly _fileDownloadService: FileDownloadService,
|
||||
private readonly _fileUploadControllerService: FileUploadControllerService,
|
||||
private readonly _projectControllerService: ProjectControllerService,
|
||||
private readonly _reanalysisControllerService: ReanalysisControllerService,
|
||||
private readonly _filtersService: FiltersService,
|
||||
private ngZone: NgZone) {
|
||||
this._activatedRoute.params.subscribe(params => {
|
||||
@ -102,15 +85,28 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
this._viewerSyncService.activateViewer('ANNOTATED');
|
||||
}
|
||||
|
||||
public showDetailsDialog($event: MouseEvent) {
|
||||
public openFileDetailsDialog($event: MouseEvent) {
|
||||
this._dialogService.openFileDetailsDialog($event, this.appStateService.activeFile);
|
||||
}
|
||||
|
||||
public reanalyseFile($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
this._dialog.open(FileDetailsDialogComponent, {
|
||||
width: '600px',
|
||||
maxWidth: '90vw',
|
||||
data: this.appStateService.activeFile
|
||||
this._reanalysisControllerService.reanalyzeFile(this.appStateService.activeProject.project.projectId, this.fileId).subscribe(async () => {
|
||||
await this.appStateService.reloadActiveProjectFiles();
|
||||
});
|
||||
}
|
||||
|
||||
public openDeleteFileDialog($event: MouseEvent) {
|
||||
this._dialogService.openDeleteFileDialog($event, this.projectId, this.fileId, () => {
|
||||
this._router.navigate([`/ui/projects/${this.projectId}`]);
|
||||
});
|
||||
}
|
||||
|
||||
public openAssignFileOwnerDialog($event: MouseEvent) {
|
||||
const file = this.appStateService.getFileById(this.projectId, this.fileId);
|
||||
this._dialogService.openAssignFileOwnerDialog($event, file);
|
||||
}
|
||||
|
||||
public fileReady(viewer: string) {
|
||||
this._readyViewers.push(viewer);
|
||||
this._changeDetectorRef.detectChanges();
|
||||
@ -165,13 +161,9 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
this._viewerComponent.navigateToPage(pageNumber);
|
||||
}
|
||||
|
||||
handleManualAnnotationRequest($event: ManualRedactionEntry) {
|
||||
public openManualRedactionDialog($event: ManualRedactionEntry) {
|
||||
this.ngZone.run(() => {
|
||||
this._dialog.open(ManualRedactionDialogComponent, {
|
||||
width: '600px',
|
||||
maxWidth: '90vw',
|
||||
data: $event
|
||||
});
|
||||
this._dialogService.openManualRedactionDialog($event);
|
||||
});
|
||||
}
|
||||
|
||||
@ -213,59 +205,24 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
getType(annotation: Annotations.Annotation): string {
|
||||
public getType(annotation: Annotations.Annotation): string {
|
||||
return AnnotationUtils.getType(annotation);
|
||||
}
|
||||
|
||||
getDictionary(annotation: Annotations.Annotation): string {
|
||||
public getDictionary(annotation: Annotations.Annotation): string {
|
||||
return AnnotationUtils.getDictionary(annotation);
|
||||
}
|
||||
|
||||
acceptSuggestionAnnotation($event: MouseEvent, annotation: Annotations.Annotation) {
|
||||
$event.stopPropagation();
|
||||
public acceptSuggestionAnnotation($event: MouseEvent, annotation: Annotations.Annotation) {
|
||||
this.ngZone.run(() => {
|
||||
const dialogRef = this._dialog.open(ConfirmationDialogComponent, {
|
||||
width: '400px',
|
||||
maxWidth: '90vw'
|
||||
});
|
||||
|
||||
const parts = annotation.Id.split(':');
|
||||
const annotationId = parts[parts.length - 1];
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this._manualRedactionControllerService.approveRequest(this.appStateService.activeProjectId, this.appStateService.activeFile.fileId, annotationId).subscribe(ok => {
|
||||
this._notificationService.showToastNotification(this._translateService.instant('manual-redaction.confirm-annotation.success.label'), null, NotificationType.SUCCESS);
|
||||
}, (err) => {
|
||||
this._notificationService.showToastNotification(this._translateService.instant('manual-redaction.confirm-annotation.failed.label', err), null, NotificationType.ERROR);
|
||||
});
|
||||
}
|
||||
});
|
||||
this._dialogService.acceptSuggestionAnnotation($event, annotation, this.projectId, this.fileId);
|
||||
});
|
||||
}
|
||||
|
||||
suggestRemoveAnnotation($event: MouseEvent, annotation: Annotations.Annotation) {
|
||||
$event.stopPropagation();
|
||||
public suggestRemoveAnnotation($event: MouseEvent, annotation: Annotations.Annotation) {
|
||||
this.ngZone.run(() => {
|
||||
const dialogRef = this._dialog.open(ConfirmationDialogComponent, {
|
||||
width: '400px',
|
||||
maxWidth: '90vw'
|
||||
});
|
||||
|
||||
const parts = annotation.Id.split(':');
|
||||
const annotationId = parts[parts.length - 1];
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this._manualRedactionControllerService.undo(this.appStateService.activeProjectId, this.appStateService.activeFile.fileId, annotationId).subscribe(ok => {
|
||||
this._notificationService.showToastNotification(this._translateService.instant('manual-redaction.remove-annotation.success.label'), null, NotificationType.SUCCESS);
|
||||
}, (err) => {
|
||||
this._notificationService.showToastNotification(this._translateService.instant('manual-redaction.remove-annotation.failed.label', err), null, NotificationType.ERROR);
|
||||
});
|
||||
}
|
||||
});
|
||||
this._dialogService.suggestRemoveAnnotation($event, annotation, this.projectId, this.fileId);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public downloadFile(type: FileType | string) {
|
||||
@ -312,8 +269,7 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
this.expandedFilters[key] = value;
|
||||
}
|
||||
|
||||
isManuallyAddedAnnotation(annotation: Annotations.Annotation) {
|
||||
public isManuallyAddedAnnotation(annotation: Annotations.Annotation) {
|
||||
return annotation.Id.indexOf('request:') >= 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -9,15 +9,13 @@ import {
|
||||
Output,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import {AppConfigKey, AppConfigService} from '../../../app-config/app-config.service';
|
||||
import {FileStatus, ManualRedactionEntry, Rectangle} from '@redaction/red-ui-http';
|
||||
import WebViewer, {Annotations, WebViewerInstance} from '@pdftron/webviewer';
|
||||
import {TranslateService} from '@ngx-translate/core';
|
||||
import {ViewerSyncService} from '../service/viewer-sync.service';
|
||||
import {MatDialog} from "@angular/material/dialog";
|
||||
import {FileDownloadService} from "../service/file-download.service";
|
||||
import {FileType} from "../model/file-type";
|
||||
import {AppStateService} from "../../../state/app-state.service";
|
||||
import { AppConfigKey, AppConfigService } from '../../../app-config/app-config.service';
|
||||
import { FileStatus, ManualRedactionEntry, Rectangle } from '@redaction/red-ui-http';
|
||||
import WebViewer, { Annotations, WebViewerInstance } from '@pdftron/webviewer';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { ViewerSyncService } from '../service/viewer-sync.service';
|
||||
import { FileDownloadService } from '../service/file-download.service';
|
||||
import { FileType } from '../model/file-type';
|
||||
|
||||
|
||||
@Component({
|
||||
@ -36,18 +34,15 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
@Output() manualAnnotationRequested = new EventEmitter<ManualRedactionEntry>();
|
||||
@Output() pageChanged = new EventEmitter<number>();
|
||||
|
||||
@ViewChild('viewer', {static: true}) viewer: ElementRef;
|
||||
@ViewChild('viewer', { static: true }) viewer: ElementRef;
|
||||
wvInstance: WebViewerInstance;
|
||||
|
||||
_fileData: Blob;
|
||||
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _viewerSyncService: ViewerSyncService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _fileDownloadService: FileDownloadService,
|
||||
private readonly _dialog: MatDialog,
|
||||
private readonly _appConfigService: AppConfigService) {
|
||||
constructor(private readonly _viewerSyncService: ViewerSyncService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _fileDownloadService: FileDownloadService,
|
||||
private readonly _appConfigService: AppConfigService) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
@ -61,10 +56,10 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this._fileDownloadService.loadFile(this.fileType, this.fileId, (data) => {
|
||||
this._fileData = data
|
||||
this._fileData = data;
|
||||
}, () => this._fileData).subscribe(() => {
|
||||
this._loadViewer(this._fileData);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
private _loadViewer(pdfBlob: any) {
|
||||
@ -98,7 +93,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
});
|
||||
|
||||
instance.docViewer.on('documentLoaded', this.wvDocumentLoadedHandler);
|
||||
instance.loadDocument(pdfBlob, {filename: this.fileStatus ? this.fileStatus.filename : 'document.pdf'});
|
||||
instance.loadDocument(pdfBlob, { filename: this.fileStatus ? this.fileStatus.filename : 'document.pdf' });
|
||||
});
|
||||
}
|
||||
|
||||
@ -128,7 +123,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
onClick: () => {
|
||||
const selectedQuads = this.wvInstance.docViewer.getSelectedTextQuads();
|
||||
const text = this.wvInstance.docViewer.getSelectedText();
|
||||
const entry: ManualRedactionEntry = {positions: []};
|
||||
const entry: ManualRedactionEntry = { positions: [] };
|
||||
for (const key of Object.keys(selectedQuads)) {
|
||||
for (const quad of selectedQuads[key]) {
|
||||
entry.positions.push(this.toPosition(parseInt(key, 10), quad));
|
||||
@ -152,7 +147,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
},
|
||||
height: height,
|
||||
width: selectedQuad.x3 - selectedQuad.x4
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private _configureHeader() {
|
||||
|
||||
@ -69,7 +69,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<redaction-initials-avatar [username]="_userService.getNameForId(pw.project.ownerId)"
|
||||
<redaction-initials-avatar [username]="getOwnerName(pw)"
|
||||
color="lightgray-red"
|
||||
withName="true"
|
||||
></redaction-initials-avatar>
|
||||
@ -85,13 +85,13 @@
|
||||
[matTooltip]="'project-listing.delete.action.label'|translate">
|
||||
<mat-icon svgIcon="red:trash"></mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button color="accent" (click)="openDetailsDialog($event,pw)"
|
||||
<button mat-icon-button color="accent" (click)="openProjectDetailsDialog($event,pw)"
|
||||
[matTooltip]="'project-listing.report.action.label'|translate">
|
||||
<mat-icon svgIcon="red:report"></mat-icon>
|
||||
</button>
|
||||
<!-- <button mat-icon-button (click)="editProject($event,pw.project)">-->
|
||||
<!-- <mat-icon svgIcon="red:edit"></mat-icon>-->
|
||||
<!-- </button>-->
|
||||
<!-- <button mat-icon-button (click)="editProject($event,pw.project)">-->
|
||||
<!-- <mat-icon svgIcon="red:edit"></mat-icon>-->
|
||||
<!-- </button>-->
|
||||
<button color="accent" (click)="openAssignProjectOwnerDialog($event,pw.project)" mat-icon-button
|
||||
[matTooltip]="'project-listing.assign.action.label'|translate">
|
||||
<mat-icon svgIcon="red:assign"></mat-icon>
|
||||
|
||||
@ -11,18 +11,20 @@
|
||||
.left-container {
|
||||
width: calc(100vw - #{$right-container-width} - 130px);
|
||||
|
||||
.table-item:hover {
|
||||
.stats-bar {
|
||||
display: none;
|
||||
.table-item {
|
||||
&:hover {
|
||||
.stats-bar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.stats-subtitle {
|
||||
margin-top: 6px;
|
||||
}
|
||||
.stats-subtitle {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.stats-bar, .action-buttons {
|
||||
width: 160px;
|
||||
.stats-bar, .action-buttons {
|
||||
width: 160px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,17 +1,11 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Project, ProjectControllerService } from '@redaction/red-ui-http';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { AddEditProjectDialogComponent } from './add-edit-project-dialog/add-edit-project-dialog.component';
|
||||
import { ConfirmationDialogComponent } from '../../common/confirmation-dialog/confirmation-dialog.component';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { NotificationService } from '../../notification/notification.service';
|
||||
import { Project } from '@redaction/red-ui-http';
|
||||
import { AppStateService, ProjectWrapper } from '../../state/app-state.service';
|
||||
import { UserService } from '../../user/user.service';
|
||||
import { ProjectDetailsDialogComponent } from '../project-overview-screen/project-details-dialog/project-details-dialog.component';
|
||||
import { DoughnutChartConfig } from '../../components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import { SortingOption } from '../../utils/types';
|
||||
import { groupBy } from '../../utils/functions';
|
||||
import { AssignOwnerDialogComponent } from '../../components/project-members-dialog/assign-owner-dialog.component';
|
||||
import { DialogService } from '../../dialogs/dialog.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-project-listing-screen',
|
||||
@ -30,14 +24,16 @@ export class ProjectListingScreenComponent implements OnInit {
|
||||
constructor(
|
||||
public readonly appStateService: AppStateService,
|
||||
private readonly _userService: UserService,
|
||||
private readonly _projectControllerService: ProjectControllerService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _dialog: MatDialog) {
|
||||
private readonly _dialogService: DialogService
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.appStateService.reset();
|
||||
this._calculateData();
|
||||
}
|
||||
|
||||
private _calculateData() {
|
||||
this.projectsChartData = [
|
||||
{ value: this.activeProjects, color: 'ACTIVE', label: 'active' },
|
||||
{ value: this.inactiveProjects, color: 'DELETED', label: 'archived' }
|
||||
@ -47,7 +43,6 @@ export class ProjectListingScreenComponent implements OnInit {
|
||||
for (const key of Object.keys(groups)) {
|
||||
this.documentsChartData.push({ value: groups[key].length, color: key, label: key });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public get user() {
|
||||
@ -70,6 +65,10 @@ export class ProjectListingScreenComponent implements OnInit {
|
||||
return this.appStateService.allProjects.length - this.activeProjects;
|
||||
}
|
||||
|
||||
public getOwnerName(pw: ProjectWrapper) {
|
||||
return this._userService.getNameForId(pw.project.ownerId);
|
||||
}
|
||||
|
||||
public documentCount(project: ProjectWrapper) {
|
||||
return project.files.length;
|
||||
}
|
||||
@ -78,43 +77,23 @@ export class ProjectListingScreenComponent implements OnInit {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public openAddProjectDialog(project?: Project): void {
|
||||
this._dialog.open(AddEditProjectDialogComponent, {
|
||||
width: '400px',
|
||||
maxWidth: '90vw',
|
||||
data: project
|
||||
public openAddProjectDialog(): void {
|
||||
this._dialogService.openAddProjectDialog(() => {
|
||||
this._calculateData();
|
||||
});
|
||||
}
|
||||
|
||||
public openDeleteProjectDialog($event: MouseEvent, project: Project) {
|
||||
$event.stopPropagation();
|
||||
const dialogRef = this._dialog.open(ConfirmationDialogComponent, {
|
||||
width: '400px',
|
||||
maxWidth: '90vw'
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.appStateService.deleteProject(project);
|
||||
}
|
||||
this._dialogService.openDeleteProjectDialog($event, project, () => {
|
||||
this._calculateData();
|
||||
});
|
||||
}
|
||||
|
||||
public openDetailsDialog($event: MouseEvent, project: ProjectWrapper) {
|
||||
$event.stopPropagation();
|
||||
this._dialog.open(ProjectDetailsDialogComponent, {
|
||||
width: '600px',
|
||||
maxWidth: '90vw',
|
||||
data: project
|
||||
});
|
||||
public openProjectDetailsDialog($event: MouseEvent, project: ProjectWrapper) {
|
||||
this._dialogService.openProjectDetailsDialog($event, project);
|
||||
}
|
||||
|
||||
public openAssignProjectOwnerDialog($event: MouseEvent, project: Project) {
|
||||
$event.stopPropagation();
|
||||
this._dialog.open(AssignOwnerDialogComponent, {
|
||||
width: '400px',
|
||||
maxWidth: '90vw',
|
||||
data: { type: 'project', projectId: project.projectId }
|
||||
});
|
||||
this._dialogService.openAssignProjectOwnerDialog($event, project);
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,9 +33,12 @@
|
||||
<div class="flex red-content-inner">
|
||||
<div class="left-container">
|
||||
<div class="table-header">
|
||||
<div class="select-all-container">
|
||||
<div class="select-oval" [class.active]="areAllFilesSelected()" (click)="toggleSelectAll()"></div>
|
||||
<span class="all-caps-label">
|
||||
{{'project-overview.table-header.title.label'| translate:{length: appStateService.activeProject?.files.length || 0} }}
|
||||
{{'project-overview.table-header.title.label'| translate:{ length: appStateService.activeProject?.files.length || 0 } }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div translate="project-overview.table-header.bulk-select.label"></div>
|
||||
<mat-form-field appearance="none" class="red-select">
|
||||
@ -49,6 +52,7 @@
|
||||
</div>
|
||||
|
||||
<div class="table-col-names">
|
||||
<div class="select-oval placeholder"></div>
|
||||
<div class="flex-6 small-label min-width" translate="project-overview.table-col-names.name.label"></div>
|
||||
<div class="flex-4 small-label min-width" translate="project-overview.table-col-names.added-on.label"></div>
|
||||
<div class="flex-2 small-label min-width" translate="project-overview.table-col-names.needs-work.label"></div>
|
||||
@ -61,6 +65,11 @@
|
||||
[class.pointer]="canOpenFile(fileStatus.status)"
|
||||
*ngFor="let fileStatus of appStateService.activeProject.files | sortBy: sortingOption.order:sortingOption.column; trackBy:fileId"
|
||||
[routerLink]="canOpenFile(fileStatus.status) ? ['/ui/projects/'+activeProject.projectId+'/file/'+fileStatus.fileId] : []">
|
||||
|
||||
<div class="select-oval"
|
||||
[class.active]="isFileSelected(fileStatus)"
|
||||
(click)="toggleFileSelected($event, fileStatus)"></div>
|
||||
|
||||
<div class="flex-6 table-item-title min-width" [matTooltip]="'['+fileStatus.status+'] '+fileStatus.filename ">
|
||||
{{ fileStatus.filename }}
|
||||
</div>
|
||||
@ -96,7 +105,7 @@
|
||||
[matTooltip]="'project-overview.report.action.label'|translate">
|
||||
<mat-icon svgIcon="red:report"></mat-icon>
|
||||
</button>
|
||||
<button (click)="assignPeopleToFile($event,fileStatus)" color="accent" mat-icon-button
|
||||
<button (click)="openAssignFileOwnerDialog($event,fileStatus)" color="accent" mat-icon-button
|
||||
[matTooltip]="'project-overview.assign.action.label'|translate">
|
||||
<mat-icon svgIcon="red:assign"></mat-icon>
|
||||
</button>
|
||||
@ -168,7 +177,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-20">
|
||||
<div class="mt-32">
|
||||
<redaction-simple-doughnut-chart [config]="documentsChartData"
|
||||
[strokeWidth]="15"
|
||||
[radius]="70"
|
||||
@ -176,5 +185,20 @@
|
||||
direction="row"
|
||||
></redaction-simple-doughnut-chart>
|
||||
</div>
|
||||
|
||||
<div class="mt-32 legend">
|
||||
<div>
|
||||
<redaction-annotation-icon type="hint"></redaction-annotation-icon>
|
||||
{{ 'project-overview.legend.contains-hints.label' | translate }}
|
||||
</div>
|
||||
<div>
|
||||
<redaction-annotation-icon type="redaction"></redaction-annotation-icon>
|
||||
{{ 'project-overview.legend.contains-redactions.label' | translate }}
|
||||
</div>
|
||||
<div>
|
||||
<redaction-annotation-icon type="suggestion"></redaction-annotation-icon>
|
||||
{{ 'project-overview.legend.contains-suggestions.label' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -13,6 +13,35 @@
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.select-all-container {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
|
||||
.select-oval {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.select-oval {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid $grey-5;
|
||||
background-color: $white;
|
||||
padding: 0;
|
||||
margin-left: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
background-color: $primary;
|
||||
}
|
||||
|
||||
&.placeholder {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.table-item {
|
||||
.needs-work {
|
||||
display: flex;
|
||||
@ -41,3 +70,19 @@
|
||||
gap: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.legend {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.mt-32 {
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
@ -1,28 +1,17 @@
|
||||
import {ChangeDetectorRef, Component, OnDestroy, OnInit} from '@angular/core';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {
|
||||
FileStatus,
|
||||
FileUploadControllerService,
|
||||
ProjectControllerService,
|
||||
ReanalysisControllerService,
|
||||
StatusControllerService
|
||||
} from '@redaction/red-ui-http';
|
||||
import {NotificationService, NotificationType} from '../../notification/notification.service';
|
||||
import {TranslateService} from '@ngx-translate/core';
|
||||
import {ConfirmationDialogComponent} from '../../common/confirmation-dialog/confirmation-dialog.component';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {AppStateService} from '../../state/app-state.service';
|
||||
import {ProjectDetailsDialogComponent} from './project-details-dialog/project-details-dialog.component';
|
||||
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 {AddEditProjectDialogComponent} from '../project-listing-screen/add-edit-project-dialog/add-edit-project-dialog.component';
|
||||
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 {AssignOwnerDialogComponent} from "../../components/project-members-dialog/assign-owner-dialog.component";
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { FileStatus, 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';
|
||||
|
||||
|
||||
@Component({
|
||||
@ -32,31 +21,28 @@ import {AssignOwnerDialogComponent} from "../../components/project-members-dialo
|
||||
})
|
||||
export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
|
||||
private _fileStatusInterval;
|
||||
private _selectedFileIds: string[] = [];
|
||||
|
||||
public sortingOptions: SortingOption[] = [
|
||||
{label: 'project-overview.sorting.recent.label', order: 'desc', 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'}
|
||||
{ label: 'project-overview.sorting.recent.label', order: 'desc', 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 _changeDetectorRef: ChangeDetectorRef,
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _statusControllerService: StatusControllerService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _dialog: MatDialog,
|
||||
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,
|
||||
private readonly _fileUploadControllerService: FileUploadControllerService,
|
||||
private readonly _projectControllerService: ProjectControllerService) {
|
||||
private readonly _fileDropOverlayService: FileDropOverlayService) {
|
||||
this._activatedRoute.params.subscribe(params => {
|
||||
this.appStateService.activateProject(params.projectId);
|
||||
});
|
||||
@ -91,7 +77,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public get displayMembers() {
|
||||
return this.members.slice(0, 6)
|
||||
return this.members.slice(0, 6);
|
||||
}
|
||||
|
||||
public get overflowCount() {
|
||||
@ -112,72 +98,66 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
|
||||
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});
|
||||
this.documentsChartData.push({ value: groups[key].length, color: key, label: key });
|
||||
}
|
||||
}
|
||||
|
||||
public openDeleteFileDialog($event: MouseEvent, fileStatus: FileStatus) {
|
||||
public toggleFileSelected($event: MouseEvent, file: FileStatus) {
|
||||
$event.stopPropagation();
|
||||
const dialogRef = this._dialog.open(ConfirmationDialogComponent, {
|
||||
width: '400px',
|
||||
maxWidth: '90vw',
|
||||
autoFocus: false
|
||||
});
|
||||
const idx = this._selectedFileIds.indexOf(file.fileId);
|
||||
if (idx === -1) {
|
||||
this._selectedFileIds.push(file.fileId);
|
||||
} else {
|
||||
this._selectedFileIds.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this._fileUploadControllerService.deleteFile(fileStatus.projectId, fileStatus.fileId).subscribe(() => {
|
||||
this._getFileStatus();
|
||||
}, () => {
|
||||
this._notificationService.showToastNotification(this._translateService.instant('project-overview.delete-file-error.label', fileStatus), null, NotificationType.ERROR);
|
||||
});
|
||||
}
|
||||
public toggleSelectAll() {
|
||||
if (this.areAllFilesSelected()) {
|
||||
this._selectedFileIds = [];
|
||||
} else {
|
||||
this._selectedFileIds = this.appStateService.activeProject.files.map(file => file.fileId);
|
||||
}
|
||||
}
|
||||
|
||||
public areAllFilesSelected() {
|
||||
return 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();
|
||||
});
|
||||
}
|
||||
|
||||
public openDetailsDialog($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
this._dialog.open(ProjectDetailsDialogComponent, {
|
||||
width: '600px',
|
||||
maxWidth: '90vw',
|
||||
data: this.appStateService.activeProject
|
||||
});
|
||||
this._dialogService.openProjectDetailsDialog($event, this.appStateService.activeProject);
|
||||
}
|
||||
|
||||
public openEditProjectDialog($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
this._dialog.open(AddEditProjectDialogComponent, {
|
||||
width: '400px',
|
||||
maxWidth: '90vw',
|
||||
data: this.appStateService.activeProject.project
|
||||
});
|
||||
this._dialogService.openEditProjectDialog($event, this.appStateService.activeProject.project);
|
||||
}
|
||||
|
||||
public openDeleteProjectDialog($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
const dialogRef = this._dialog.open(ConfirmationDialogComponent, {
|
||||
width: '400px',
|
||||
maxWidth: '90vw'
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.appStateService.deleteProject(this.appStateService.activeProject.project);
|
||||
this.ngOnDestroy();
|
||||
this._router.navigate(['/ui/projects']);
|
||||
}
|
||||
this._dialogService.openDeleteProjectDialog($event, this.appStateService.activeProject.project, () => {
|
||||
this._router.navigate(['/ui/projects']);
|
||||
});
|
||||
}
|
||||
|
||||
public openAssignProjectMembersDialog() {
|
||||
this._dialog.open(AssignOwnerDialogComponent, {
|
||||
width: '400px',
|
||||
maxWidth: '90vw',
|
||||
autoFocus: false,
|
||||
data: {type: 'project', project: this.appStateService.activeProject.project }
|
||||
}).afterClosed().subscribe( ()=>{
|
||||
this._dialogService.openAssignProjectMembersDialog(this.activeProject, () => {
|
||||
this._getFileStatus();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
public openAssignFileOwnerDialog($event: MouseEvent, file: FileStatus) {
|
||||
this._dialogService.openAssignFileOwnerDialog($event, file, () => {
|
||||
this._getFileStatus();
|
||||
});
|
||||
}
|
||||
|
||||
public reanalyseFile($event: MouseEvent, fileStatus: FileStatus) {
|
||||
@ -212,17 +192,6 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
|
||||
return fileStatus === 'PROCESSING' || fileStatus === 'REVIEWED' || true;
|
||||
}
|
||||
|
||||
public assignPeopleToFile($event: MouseEvent, fileStatus: FileStatus) {
|
||||
$event.stopPropagation();
|
||||
this._dialog.open(AssignOwnerDialogComponent, {
|
||||
width: '400px',
|
||||
maxWidth: '90vw',
|
||||
data: {type: 'file', file: fileStatus}
|
||||
}).afterClosed().subscribe( ()=>{
|
||||
this._getFileStatus();
|
||||
})
|
||||
}
|
||||
|
||||
public getFileOwnerUsername(fileStatus: FileStatus) {
|
||||
return this._userService.getNameForId(fileStatus.currentReviewer);
|
||||
}
|
||||
|
||||
@ -109,6 +109,10 @@ export class AppStateService {
|
||||
return this.allProjects.find(project => project.project.projectId === id);
|
||||
}
|
||||
|
||||
public getFileById(projectId: string, fileId: string) {
|
||||
return this.getProjectById(projectId).files.find(file => file.fileId === fileId);
|
||||
}
|
||||
|
||||
async loadAllProjects() {
|
||||
const projects = await this._projectControllerService.getProjects().toPromise();
|
||||
if (projects) {
|
||||
@ -124,7 +128,7 @@ export class AppStateService {
|
||||
|
||||
async getFiles(projectId: string) {
|
||||
const files = await this._statusControllerService.getProjectStatus(projectId).toPromise();
|
||||
const project = this._appState.projects.find(p => p.project.projectId === projectId);
|
||||
const project = this.getProjectById(projectId);
|
||||
project.files = files;
|
||||
this._computeStats();
|
||||
return files;
|
||||
@ -152,7 +156,7 @@ export class AppStateService {
|
||||
}
|
||||
|
||||
deleteProject(project: Project) {
|
||||
this._projectControllerService.deleteProject(project.projectId).subscribe(() => {
|
||||
return this._projectControllerService.deleteProject(project.projectId).toPromise().then(() => {
|
||||
const index = this._appState.projects.findIndex(p => p.project.projectId === project.projectId);
|
||||
this._appState.projects.splice(index, 1);
|
||||
this._appState.projects = [...this._appState.projects];
|
||||
|
||||
@ -372,6 +372,17 @@
|
||||
},
|
||||
"no-project": {
|
||||
"label": "Requested project: {{projectId}} does not exist! <a href='/ui/projects'>Back to Project Listing. <a/>"
|
||||
},
|
||||
"legend": {
|
||||
"contains-hints": {
|
||||
"label": "Contains hints "
|
||||
},
|
||||
"contains-redactions": {
|
||||
"label": "Contains redactions "
|
||||
},
|
||||
"contains-suggestions": {
|
||||
"label": "Contains suggestions for redaction "
|
||||
}
|
||||
}
|
||||
},
|
||||
"file-preview": {
|
||||
@ -457,13 +468,12 @@
|
||||
"label": "Unassigned"
|
||||
}
|
||||
},
|
||||
|
||||
"assign-file-owner":{
|
||||
"assign-file-owner": {
|
||||
"dialog": {
|
||||
"single-user": {
|
||||
"label": "Reviewer"
|
||||
},
|
||||
"title":{
|
||||
"title": {
|
||||
"label": "Manage File Reviewer"
|
||||
},
|
||||
"save": {
|
||||
@ -471,8 +481,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"assign-project-owner":{
|
||||
"assign-project-owner": {
|
||||
"dialog": {
|
||||
"single-user": {
|
||||
"label": "Owner"
|
||||
@ -480,7 +489,7 @@
|
||||
"multi-user": {
|
||||
"label": "Members"
|
||||
},
|
||||
"title":{
|
||||
"title": {
|
||||
"label": "Manage Project Owner and Members"
|
||||
},
|
||||
"save": {
|
||||
@ -488,7 +497,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"unassigned": "Unassigned",
|
||||
"under-review": "Under review",
|
||||
"under-approval": "Under approval",
|
||||
|
||||
@ -1,14 +1,7 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg id="Capa_1" style="enable-background:new 0 0 512 512;" version="1.1" viewBox="0 0 512 512"
|
||||
x="0px"
|
||||
xml:space="preserve" xmlns="http://www.w3.org/2000/svg" y="0px">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M437.02,74.98C388.667,26.629,324.38,0,256,0S123.333,26.629,74.98,74.98C26.629,123.333,0,187.62,0,256
|
||||
s26.629,132.667,74.98,181.02C123.333,485.371,187.62,512,256,512s132.667-26.629,181.02-74.98
|
||||
C485.371,388.667,512,324.38,512,256S485.371,123.333,437.02,74.98z M256,70c30.327,0,55,24.673,55,55c0,30.327-24.673,55-55,55
|
||||
c-30.327,0-55-24.673-55-55C201,94.673,225.673,70,256,70z M326,420H186v-30h30V240h-30v-30h110v180h30V420z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>info</title>
|
||||
<g id="info" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M50,0 C77.5,0 100,22.5 100,50 C100,77.5 77.5,100 50,100 C22.5,100 0,77.5 0,50 C0,22.5 22.5,0 50,0 Z M50,10 C28,10 10,28 10,50 C10,72 28,90 50,90 C72,90 90,72 90,50 C90,28 72,10 50,10 Z M50,38.25 C57.5,38.25 59.3203,42.6758 58.4805,48.1328 L55,70.7498 L55.0318998,70.7491065 C55.3367202,70.7391667 57.826087,70.5867565 62.5,68.2498 C62.5,68.2498 60,78.2498 50,78.2498 C42.5,78.2498 40.6797,73.824 41.5195,68.367 L45,45.75 L44.9681002,45.7506935 C44.6632798,45.7606333 42.173913,45.9130435 37.5,48.25 C37.5,48.25 40,38.25 50,38.25 Z M52.5,19.75 C56.6445,19.75 60,23.1094 60,27.25 C60,31.3906 56.6445,34.75 52.5,34.75 C48.3555,34.75 45,31.3906 45,27.25 C45,23.1094 48.3555,19.75 52.5,19.75 Z" id="Combined-Shape" fill="#283241" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 790 B After Width: | Height: | Size: 1.1 KiB |
@ -119,12 +119,6 @@ html, body {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.details-button {
|
||||
position: fixed !important;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
opacity: 1;
|
||||
font-family: Inconsolata, monospace, monospace;
|
||||
@ -165,6 +159,11 @@ html, body {
|
||||
.menu {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
|
||||
&.right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,17 +172,15 @@ html, body {
|
||||
color: $accent;
|
||||
font-weight: 600;
|
||||
@include line-clamp(1);
|
||||
max-width: 320px;
|
||||
|
||||
mat-icon {
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
min-width: 25px;
|
||||
|
||||
&:last-child {
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
.mat-icon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
|
||||
@ -19,11 +19,11 @@
|
||||
.table-col-names {
|
||||
display: flex;
|
||||
text-transform: uppercase;
|
||||
border-bottom: 1px solid rgba(226, 228, 233, 0.9);
|
||||
|
||||
> div {
|
||||
padding: 8px 16px;
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid rgba(226, 228, 233, 0.9);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -29,6 +29,13 @@ a {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.all-caps-label {
|
||||
text-transform: uppercase;
|
||||
opacity: 0.7;
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
mat-slide-toggle {
|
||||
display: flex !important;
|
||||
flex-direction: row-reverse;
|
||||
gap: 8px;
|
||||
|
||||
.mat-slide-toggle-bar {
|
||||
height: 16px !important;
|
||||
width: 30px !important;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user