Assign project owner

This commit is contained in:
Adina Țeudan 2020-10-21 02:26:23 +03:00
parent ee037c63ac
commit 6bebc5409d
13 changed files with 176 additions and 85 deletions

View File

@ -55,8 +55,9 @@ import { AnnotationIconComponent } from './components/annotation-icon/annotation
import { AuthGuard } from './auth/auth.guard';
import { AuthErrorComponent } from './screens/auth-error/auth-error.component';
import { RedRoleGuard } from './auth/red-role.guard';
import { ProjectMembersDialogComponent } from './screens/project-overview-screen/project-members-dialog/project-members-dialog.component';
import { AssignProjectMembersDialogComponent } from './screens/project-overview-screen/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';
export function HttpLoaderFactory(httpClient: HttpClient) {
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
@ -74,7 +75,8 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
PdfViewerComponent,
FileDetailsDialogComponent,
ProjectDetailsDialogComponent,
ProjectMembersDialogComponent,
AssignProjectMembersDialogComponent,
AssignOwnerDialogComponent,
FullPageLoadingIndicatorComponent,
InitialsAvatarComponent,
StatusBarComponent,

View File

@ -0,0 +1,21 @@
<section class="dialog">
<div [translate]="'assign-' + data.type + '-owner.dialog.title.label'"
class="dialog-header heading-l">
</div>
<div class="dialog-content">
<mat-list class="list-50vh">
<ng-container *ngFor="let user of userService.allUsers">
<mat-list-item class="pointer" [class.owner]="isOwner(user)"
(click)="assignOwner(user)"
*ngIf="userService.isManager(user)">
{{ userService.getNameForId(user.userId) }}
</mat-list-item>
</ng-container>
</mat-list>
</div>
<button (click)="dialogRef.close()" class="dialog-close" mat-icon-button>
<mat-icon svgIcon="red:close"></mat-icon>
</button>
</section>

View File

@ -0,0 +1,5 @@
@import "../../../assets/styles/red-variables";
.owner {
background-color: rgba($primary, 0.1);
}

View File

@ -0,0 +1,59 @@
import { Component, Inject } from '@angular/core';
import { Project, ProjectControllerService, User } 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';
class DialogData {
type: 'file' | 'project';
projectId?: string;
fileId?: string;
}
@Component({
selector: 'redaction-project-details-dialog',
templateUrl: './assign-owner-dialog.component.html',
styleUrls: ['./assign-owner-dialog.component.scss']
})
export class AssignOwnerDialogComponent {
private project: Project;
public memberIds: string[];
constructor(private readonly _projectControllerService: ProjectControllerService,
private readonly _notificationService: NotificationService,
public readonly userService: UserService,
private readonly _appStateService: AppStateService,
public dialogRef: MatDialogRef<AssignOwnerDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: DialogData) {
this._loadData();
}
public isOwner(user: User) {
return this.data.type === 'project' ? this.project.ownerId === user.userId : false;
}
public assignOwner(user: User) {
if (this.data.type === 'project') {
this._projectControllerService.assignProjectOwner(this.project.projectId, user.userId).subscribe(() => {
this._notificationService.showToastNotification('Successfully assigned ' + this.userService.getNameForId(user.userId) + ' to project: ' + this.project.projectName);
}, error => {
this._notificationService.showToastNotification('Failed: ' + error.error.message, null, NotificationType.ERROR);
}).add(() => this._reloadProject());
} else if (this.data.type === 'file') {
console.log('not implemented yet');
}
}
private _loadData() {
if (this.data.type === 'project') {
this.project = this._appStateService.getProjectById(this.data.projectId).project;
}
}
private _reloadProject() {
this._appStateService.addOrUpdateProject(this.project).then(() => {
this._loadData();
});
}
}

View File

@ -69,7 +69,7 @@
</div>
</div>
<div class="flex-1">
<redaction-initials-avatar [username]="user.name"
<redaction-initials-avatar [username]="_userService.getNameForId(pw.project.ownerId)"
color="lightgray-red"
withName="true"
></redaction-initials-avatar>
@ -81,18 +81,18 @@
</div>
<div class="action-buttons">
<button mat-icon-button color="accent" (click)="deleteProject($event,pw.project)"
<button mat-icon-button color="accent" (click)="openDeleteProjectDialog($event,pw.project)"
[matTooltip]="'project-listing.delete.action.label'|translate">
<mat-icon svgIcon="red:trash"></mat-icon>
</button>
<button mat-icon-button color="accent" (click)="showDetailsDialog($event,pw)"
<button mat-icon-button color="accent" (click)="openDetailsDialog($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 color="accent" (click)="assignProjectPeople($event,pw.project)" 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>
</button>

View File

@ -1,16 +1,17 @@
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 {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 { 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 { 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';
@Component({
selector: 'redaction-project-listing-screen',
@ -21,8 +22,8 @@ export class ProjectListingScreenComponent implements OnInit {
public projectsChartData: DoughnutChartConfig [] = [];
public documentsChartData: DoughnutChartConfig [] = [];
public sortingOptions: SortingOption[] = [
{label: 'project-listing.sorting.recent.label', order: 'desc', column: 'projectDate'},
{label: 'project-listing.sorting.alphabetically.label', order: 'asc', column: 'project.projectName'}
{ label: 'project-listing.sorting.recent.label', order: 'desc', column: 'projectDate' },
{ label: 'project-listing.sorting.alphabetically.label', order: 'asc', column: 'project.projectName' }
];
public sortingOption: SortingOption = this.sortingOptions[0];
@ -35,25 +36,49 @@ export class ProjectListingScreenComponent implements OnInit {
private readonly _dialog: MatDialog) {
}
get user() {
return this._userService.user;
}
ngOnInit(): void {
this.appStateService.reset();
this.projectsChartData = [
{value: this.activeProjects, color: 'ACTIVE', label: 'active'},
{value: this.inactiveProjects, color: 'DELETED', label: 'archived'}
{ value: this.activeProjects, color: 'ACTIVE', label: 'active' },
{ value: this.inactiveProjects, color: 'DELETED', label: 'archived' }
];
const groups = groupBy(this.appStateService.aggregatedFiles, '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 });
}
}
openAddProjectDialog(project?: Project): void {
public get user() {
return this._userService.user;
}
public get totalPages() {
return this.appStateService.totalAnalysedPages;
}
public get totalPeople() {
return this.appStateService.totalPeople;
}
public get activeProjects() {
return this.appStateService.allProjects.reduce((i, p) => i + (p.project.status === Project.StatusEnum.ACTIVE ? 1 : 0), 0);
}
public get inactiveProjects() {
return this.appStateService.allProjects.length - this.activeProjects;
}
public documentCount(project: ProjectWrapper) {
return project.files.length;
}
public userCount(project: ProjectWrapper) {
return 1;
}
public openAddProjectDialog(project?: Project): void {
this._dialog.open(AddEditProjectDialogComponent, {
width: '400px',
maxWidth: '90vw',
@ -61,12 +86,7 @@ export class ProjectListingScreenComponent implements OnInit {
});
}
editProject($event: MouseEvent, project: Project) {
$event.stopPropagation();
this.openAddProjectDialog(project);
}
deleteProject($event: MouseEvent, project: Project) {
public openDeleteProjectDialog($event: MouseEvent, project: Project) {
$event.stopPropagation();
const dialogRef = this._dialog.open(ConfirmationDialogComponent, {
width: '400px',
@ -80,7 +100,7 @@ export class ProjectListingScreenComponent implements OnInit {
});
}
showDetailsDialog($event: MouseEvent, project: ProjectWrapper) {
public openDetailsDialog($event: MouseEvent, project: ProjectWrapper) {
$event.stopPropagation();
this._dialog.open(ProjectDetailsDialogComponent, {
width: '600px',
@ -89,34 +109,12 @@ export class ProjectListingScreenComponent implements OnInit {
});
}
get totalPages() {
return this.appStateService.totalAnalysedPages;
}
get totalPeople(){
return this.appStateService.totalPeople;
}
documentCount(project: ProjectWrapper) {
return project.files.length;
}
userCount(project: ProjectWrapper) {
return 1;
}
get activeProjects() {
return this.appStateService.allProjects.reduce((i, p) => i + (p.project.status === Project.StatusEnum.ACTIVE ? 1 : 0), 0);
}
get inactiveProjects() {
return this.appStateService.allProjects.length - this.activeProjects;
}
assignProjectPeople($event: MouseEvent, project: Project) {
public openAssignProjectOwnerDialog($event: MouseEvent, project: Project) {
$event.stopPropagation();
this._projectControllerService.assignProjectOwner(project.projectId, this._userService.user.id).subscribe(() => {
this._notificationService.showToastNotification("Successfully assigned " + this.user.name + " to project: " + project.projectName);
})
this._dialog.open(AssignOwnerDialogComponent, {
width: '400px',
maxWidth: '90vw',
data: { type: 'project', projectId: project.projectId }
});
}
}

View File

@ -5,10 +5,12 @@
</div>
<div class="dialog-content">
<mat-selection-list [(ngModel)]="memberIds" (selectionChange)="selectionChange($event)" color="primary">
<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">
<p matLine>{{ userService.getNameForId(user.userId) }}</p>
{{ userService.getNameForId(user.userId) }}
</mat-list-option>
</ng-container>
</mat-selection-list>

View File

@ -8,10 +8,10 @@ import { NotificationService, NotificationType } from '../../../notification/not
@Component({
selector: 'redaction-project-details-dialog',
templateUrl: './project-members-dialog.component.html',
styleUrls: ['./project-members-dialog.component.scss']
templateUrl: './assign-project-members-dialog.component.html',
styleUrls: ['./assign-project-members-dialog.component.scss']
})
export class ProjectMembersDialogComponent {
export class AssignProjectMembersDialogComponent {
private _project: Project;
public memberIds: string[];
@ -19,8 +19,7 @@ export class ProjectMembersDialogComponent {
private readonly _notificationService: NotificationService,
public readonly userService: UserService,
private readonly _appStateService: AppStateService,
public dialogRef: MatDialogRef<ProjectMembersDialogComponent>,
@Inject(MAT_DIALOG_DATA) public projectDetails: ProjectWrapper) {
public dialogRef: MatDialogRef<AssignProjectMembersDialogComponent>) {
this._loadProject();
}

View File

@ -1,4 +0,0 @@
mat-selection-list {
overflow-y: scroll;
max-height: 50vh;
}

View File

@ -1,4 +1,4 @@
import { ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import {
FileStatus,
@ -21,8 +21,8 @@ import { AddEditProjectDialogComponent } from '../project-listing-screen/add-edi
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 { ProjectMembersDialogComponent } from './project-members-dialog/project-members-dialog.component';
import { groupBy } from '../../utils/functions';
import { AssignProjectMembersDialogComponent } from './project-members-dialog/assign-project-members-dialog.component';
@Component({
@ -86,8 +86,8 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
return this._userService.user;
}
public get members() {
return this.activeProject.memberIds.map(m => this._userService.getName(this._userService.getUserById(m)))
public get members() {
return this.activeProject.memberIds.map(m => this._userService.getName(this._userService.getUserById(m)));
}
public get ownerName() {
@ -104,7 +104,7 @@ 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 });
}
}
@ -162,7 +162,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
}
public openAssignProjectMembersDialog() {
this._dialog.open(ProjectMembersDialogComponent, {
this._dialog.open(AssignProjectMembersDialogComponent, {
width: '400px',
maxWidth: '90vw',
autoFocus: false
@ -196,15 +196,15 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
this._uploadStatusOverlayService.openStatusOverlay();
}
public canOpenFile(fileStatus: FileStatus): boolean {
public canOpenFile(fileStatus: FileStatus): boolean {
// TODO check correct condition for this
return fileStatus === 'PROCESSING' || fileStatus === 'REVIEWED' || true;
}
public assignPeopleToFile($event: MouseEvent, fileStatus: FileStatus) {
$event.stopPropagation();
this._statusControllerService.assignProjectOwner1(this.appStateService.activeProjectId, fileStatus.fileId, this.user.id).subscribe(()=>{
this._notificationService.showToastNotification("Successfully assigned " + this.user.name + " to file: " + fileStatus.filename);
this._statusControllerService.assignProjectOwner1(this.appStateService.activeProjectId, fileStatus.fileId, this.user.id).subscribe(() => {
this._notificationService.showToastNotification('Successfully assigned ' + this.user.name + ' to file: ' + fileStatus.filename);
});
}

View File

@ -82,6 +82,10 @@ export class AppStateService {
return this._appState.totalDocuments;
}
public getProjectById(id: string) {
return this.allProjects.find(project => project.project.projectId === id);
}
async loadAllProjects() {
const projects = await this._projectControllerService.getProjects().toPromise();
if (projects) {

View File

@ -6,3 +6,8 @@
font-size: 13px !important;
line-height: 16px !important;
}
.list-50vh {
overflow-y: scroll;
max-height: 50vh;
}