Re-do project details in overview

This commit is contained in:
Adina Țeudan 2020-11-02 18:35:06 +02:00
parent 4ccb8f8ec6
commit 63f813b2f9
14 changed files with 308 additions and 217 deletions

View File

@ -68,6 +68,7 @@ import { FilterComponent } from './common/filter/filter.component';
import { AppInfoComponent } from './screens/app-info/app-info.component';
import { SortingComponent } from './components/sorting/sorting.component';
import { TableColNameComponent } from './components/table-col-name/table-col-name.component';
import { ProjectDetailsComponent } from './screens/project-overview-screen/project-details/project-details.component';
export function HttpLoaderFactory(httpClient: HttpClient) {
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
@ -101,7 +102,8 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
FilterComponent,
AppInfoComponent,
SortingComponent,
TableColNameComponent
TableColNameComponent,
ProjectDetailsComponent
],
imports: [
BrowserModule,

View File

@ -9,7 +9,7 @@
}
.left-container {
width: calc(100vw - #{$right-container-width} - 130px);
width: calc(100vw - #{$right-container-width} - 90px);
.grid-container {
grid-template-columns: 2fr 1fr auto;
@ -37,7 +37,7 @@
.right-fixed-container {
display: flex;
width: 470px;
width: 430px;
padding-top: 50px;
> div {

View File

@ -0,0 +1,127 @@
<div class="actions-row">
<button
(click)="openDeleteProjectDialog($event)"
*ngIf="userService.isManager(user)"
mat-icon-button
>
<mat-icon svgIcon="red:trash"></mat-icon>
</button>
<button
(click)="openEditProjectDialog($event)"
*ngIf="userService.isManager(user)"
mat-icon-button
>
<mat-icon svgIcon="red:edit"></mat-icon>
</button>
<button (click)="downloadRedactionReport($event)" mat-icon-button>
<mat-icon svgIcon="red:report"></mat-icon>
</button>
</div>
<div class="heading-xl mt-16">
{{ appStateService.activeProject.project.projectName }}
</div>
<div class="owner flex-row mt-16">
<redaction-initials-avatar
[userId]="appStateService.activeProject.project.ownerId"
size="large"
withName="true"
></redaction-initials-avatar>
</div>
<div class="project-team mt-16">
<div class="flex mt-20 members-container">
<div *ngFor="let userId of displayMembers" class="member">
<redaction-initials-avatar [userId]="userId" size="large"></redaction-initials-avatar>
</div>
<div *ngIf="overflowCount" class="member">
<div class="oval large white-dark">+{{ overflowCount }}</div>
</div>
<div
(click)="openAssignProjectMembersDialog()"
*ngIf="userService.isManager(user)"
class="member pointer"
>
<div class="oval red-white large">+</div>
</div>
</div>
</div>
<div class="mt-24">
<redaction-simple-doughnut-chart
[config]="documentsChartData"
[radius]="70"
[strokeWidth]="15"
[subtitle]="'project-overview.project-details.charts.total-documents.label'"
direction="row"
></redaction-simple-doughnut-chart>
</div>
<div class="mt-24 legend">
<div>
<redaction-annotation-icon
[typeValue]="appStateService.getDictionaryTypeValue('hint')"
></redaction-annotation-icon>
{{ 'project-overview.legend.contains-hints.label' | translate }}
</div>
<div>
<redaction-annotation-icon
[typeValue]="appStateService.getDictionaryTypeValue('redaction')"
></redaction-annotation-icon>
{{ 'project-overview.legend.contains-redactions.label' | translate }}
</div>
<div>
<redaction-annotation-icon
[typeValue]="appStateService.getDictionaryTypeValue('request')"
></redaction-annotation-icon>
{{ 'project-overview.legend.contains-suggestions.label' | translate }}
</div>
</div>
<div class="mt-32 small-label stats-subtitle">
<div>
<mat-icon svgIcon="red:document"></mat-icon>
<span>{{
'project-overview.project-details.stats.documents.label'
| translate: { count: appStateService.activeProject.files.length }
}}</span>
</div>
<div>
<mat-icon svgIcon="red:user"></mat-icon>
<span>{{
'project-overview.project-details.stats.people.label'
| translate: { count: appStateService.activeProject.project.memberIds.length }
}}</span>
</div>
<div>
<mat-icon svgIcon="red:pages"></mat-icon>
<span>{{
'project-overview.project-details.stats.analysed-pages.label'
| translate: { count: appStateService.activeProject.totalNumberOfPages }
}}</span>
</div>
<div>
<mat-icon svgIcon="red:calendar"></mat-icon>
<span
>{{
'project-overview.project-details.stats.created-on.label'
| translate
: { date: appStateService.activeProject.project.date | date: 'd MMM. yyyy' }
}}
</span>
</div>
<div *ngIf="appStateService.activeProject.project.dueDate">
<mat-icon svgIcon="red:lightning"></mat-icon>
<span>{{
'project-overview.project-details.stats.due-date.label'
| translate
: { date: appStateService.activeProject.project.dueDate | date: 'd MMM. yyyy' }
}}</span>
</div>
</div>
<div class="mt-32">
<div class="heading" translate="project-overview.project-details.description.label"></div>
<div class="mt-8">{{ appStateService.activeProject.project.description }}</div>
</div>

View File

@ -0,0 +1,37 @@
.members-container {
gap: 5px;
}
.legend {
display: flex;
flex-direction: column;
gap: 8px;
> div {
display: flex;
gap: 8px;
align-items: center;
}
}
.stats-subtitle {
gap: 0;
flex-direction: column;
align-items: flex-start;
}
.mt-8 {
margin-top: 8px;
}
.mt-16 {
margin-top: 16px;
}
.mt-24 {
margin-top: 24px;
}
.mt-32 {
margin-top: 32px;
}

View File

@ -0,0 +1,82 @@
import { Component, OnInit, Output, EventEmitter } from '@angular/core';
import { AppStateService } from '../../../state/app-state.service';
import { UserService } from '../../../user/user.service';
import { groupBy } from '../../../utils/functions';
import { DoughnutChartConfig } from '../../../components/simple-doughnut-chart/simple-doughnut-chart.component';
import { DialogService } from '../../../dialogs/dialog.service';
import { Router } from '@angular/router';
@Component({
selector: 'redaction-project-details',
templateUrl: './project-details.component.html',
styleUrls: ['./project-details.component.scss']
})
export class ProjectDetailsComponent implements OnInit {
public documentsChartData: DoughnutChartConfig[] = [];
@Output() public reloadProjects = new EventEmitter<void>();
constructor(
public readonly appStateService: AppStateService,
public readonly userService: UserService,
private readonly _dialogService: DialogService,
private readonly _router: Router
) {}
ngOnInit(): void {}
public get user() {
return this.userService.user;
}
public get displayMembers() {
return this.appStateService.activeProject.project.memberIds.slice(0, 6);
}
public get overflowCount() {
return this.appStateService.activeProject.project.memberIds.length > 6
? this.appStateService.activeProject.project.memberIds.length - 6
: 0;
}
public openEditProjectDialog($event: MouseEvent) {
this._dialogService.openEditProjectDialog(
$event,
this.appStateService.activeProject.project
);
}
public openDeleteProjectDialog($event: MouseEvent) {
this._dialogService.openDeleteProjectDialog(
$event,
this.appStateService.activeProject.project,
() => {
this._router.navigate(['/ui/projects']);
}
);
}
public openAssignProjectMembersDialog() {
this._dialogService.openAssignProjectMembersAndOwnerDialog(
null,
this.appStateService.activeProject.project,
() => {
this.reloadProjects.emit();
}
);
}
public downloadRedactionReport($event: MouseEvent) {
$event.stopPropagation();
this.appStateService.downloadRedactionReport();
}
public calculateChartConfig() {
if (this.appStateService.activeProject) {
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 });
}
}
}
}

View File

@ -1,7 +1,8 @@
<div
*ngIf="!appStateService.activeProject"
[innerHTML]="
'project-overview.no-project.label' | translate: { projectId: activeProject.projectId }
'project-overview.no-project.label'
| translate: { projectId: appStateService.activeProject.project.projectId }
"
class="heading-l"
></div>
@ -141,7 +142,12 @@
"
[routerLink]="
canOpenFile(fileStatus)
? ['/ui/projects/' + activeProject.projectId + '/file/' + fileStatus.fileId]
? [
'/ui/projects/' +
appStateService.activeProject.project.projectId +
'/file/' +
fileStatus.fileId
]
: []
"
>
@ -277,120 +283,10 @@
</div>
</div>
<div class="project-details-container right-fixed-container">
<div class="actions-row">
<button
mat-icon-button
*ngIf="userService.isManager(user)"
(click)="openDeleteProjectDialog($event)"
>
<mat-icon svgIcon="red:trash"></mat-icon>
</button>
<button
mat-icon-button
*ngIf="userService.isManager(user)"
(click)="openEditProjectDialog($event)"
>
<mat-icon svgIcon="red:edit"></mat-icon>
</button>
<button mat-icon-button (click)="downloadRedactionReport($event)">
<mat-icon svgIcon="red:report"></mat-icon>
</button>
</div>
<div class="small-label stats-subtitle mt-20">
<div>
<mat-icon svgIcon="red:document"></mat-icon>
{{ appStateService.activeProject.files.length }}
</div>
<div>
<mat-icon svgIcon="red:pages"></mat-icon>
{{ appStateService.activeProject.totalNumberOfPages }}
</div>
<div>
<mat-icon svgIcon="red:user"></mat-icon>
{{ appStateService.activeProject.project.memberIds.length }}
</div>
<div>
<mat-icon svgIcon="red:calendar"></mat-icon>
{{ appStateService.activeProject.project.date | date: 'd MMM. yyyy' }}
</div>
<div *ngIf="appStateService.activeProject.project.dueDate">
<mat-icon svgIcon="red:lightning"></mat-icon>
{{ appStateService.activeProject.project.dueDate | date: 'mediumDate' }}
</div>
</div>
<div class="heading-xl mt-20">
{{ appStateService.activeProject.project.projectName }}
</div>
<div class="owner flex-row mt-20">
<redaction-initials-avatar
[userId]="activeProject.ownerId"
size="large"
withName="true"
></redaction-initials-avatar>
</div>
<div class="mt-20">
{{ appStateService.activeProject.project.description }}
</div>
<div class="project-team mt-20">
<div
class="all-caps-label"
translate="project-overview.project-details.project-team.label"
></div>
<div class="flex mt-20 members-container">
<div *ngFor="let userId of displayMembers" class="member">
<redaction-initials-avatar
[userId]="userId"
size="large"
></redaction-initials-avatar>
</div>
<div class="member" *ngIf="overflowCount">
<div class="oval large white-dark">+{{ overflowCount }}</div>
</div>
<div
class="member pointer"
(click)="openAssignProjectMembersDialog()"
*ngIf="userService.isManager(user)"
>
<div class="oval red-white large">+</div>
</div>
</div>
</div>
<div class="mt-32">
<redaction-simple-doughnut-chart
[config]="documentsChartData"
[strokeWidth]="15"
[radius]="70"
[subtitle]="'project-overview.project-details.charts.total-documents.label'"
direction="row"
></redaction-simple-doughnut-chart>
</div>
<div class="mt-32 legend">
<div>
<redaction-annotation-icon
[typeValue]="appStateService.getDictionaryTypeValue('hint')"
></redaction-annotation-icon>
{{ 'project-overview.legend.contains-hints.label' | translate }}
</div>
<div>
<redaction-annotation-icon
[typeValue]="appStateService.getDictionaryTypeValue('redaction')"
></redaction-annotation-icon>
{{ 'project-overview.legend.contains-redactions.label' | translate }}
</div>
<div>
<redaction-annotation-icon
[typeValue]="appStateService.getDictionaryTypeValue('request')"
></redaction-annotation-icon>
{{ 'project-overview.legend.contains-suggestions.label' | translate }}
</div>
</div>
<div class="right-fixed-container">
<redaction-project-details
#projectDetailsComponent
(reloadProjects)="reloadProjects()"
></redaction-project-details>
</div>
</div>

View File

@ -1,4 +1,5 @@
@import '../../../assets/styles/red-variables';
@import '../../../assets/styles/red-mixins';
.file-upload-input {
display: none;
@ -57,24 +58,7 @@
}
}
.project-details-container {
.members-container {
gap: 5px;
}
}
.legend {
display: flex;
flex-direction: column;
gap: 8px;
> div {
display: flex;
gap: 8px;
align-items: center;
}
}
.mt-32 {
margin-top: 32px;
.right-fixed-container {
overflow-y: scroll;
@include no-scroll-bar();
}

View File

@ -13,14 +13,14 @@ 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 { DoughnutChartConfig } from '../../components/simple-doughnut-chart/simple-doughnut-chart.component';
import { groupBy, humanize } from '../../utils/functions';
import { humanize } from '../../utils/functions';
import { DialogService } from '../../dialogs/dialog.service';
import { TranslateService } from '@ngx-translate/core';
import { FileActionService } from '../file/service/file-action.service';
import { FilterModel } from '../../common/filter/model/filter.model';
import * as moment from 'moment';
import { SortingComponent, SortingOption } from '../../components/sorting/sorting.component';
import { ProjectDetailsComponent } from './project-details/project-details.component';
@Component({
selector: 'redaction-project-overview-screen',
@ -29,8 +29,6 @@ import { SortingComponent, SortingOption } from '../../components/sorting/sortin
})
export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
private _selectedFileIds: string[] = [];
public documentsChartData: DoughnutChartConfig[] = [];
public bulkSelectActive = false;
public statusFilters: FilterModel[];
@ -39,6 +37,9 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
public displayedFiles: FileStatus[] = [];
@ViewChild('projectDetailsComponent', { static: true })
private _projectDetailsComponent: ProjectDetailsComponent;
@ViewChild('sortingComponent', { static: true }) public sortingComponent: SortingComponent;
public sortingOption: SortingOption = { column: 'added', order: 'desc' };
@ -78,24 +79,10 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
this._fileDropOverlayService.cleanupFileDropHandling();
}
public get activeProject() {
return this.appStateService.activeProject.project;
}
public get user() {
return this.userService.user;
}
public get displayMembers() {
return this.activeProject.memberIds.slice(0, 6);
}
public get overflowCount() {
return this.activeProject.memberIds.length > 6
? this.activeProject.memberIds.length - 6
: 0;
}
public isPending(fileStatus: FileStatus) {
return fileStatus.status === FileStatus.StatusEnum.UNPROCESSED;
}
@ -138,9 +125,11 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
),
action: () =>
this._reanalysisControllerService
.reanalyzeProject(this.activeProject.projectId)
.reanalyzeProject(
this.appStateService.activeProject.project.projectId
)
.toPromise()
.then(() => this._reloadProjects())
.then(() => this.reloadProjects())
},
{
title: this._translateService.instant(
@ -152,7 +141,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
);
}
private _reloadProjects() {
public reloadProjects() {
this.appStateService.loadAllProjects().then(() => {
this._calculateData();
});
@ -161,17 +150,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
private _calculateData(): void {
this._computeAllFilters();
this._filterFiles();
this._calculateChartConfig();
}
private _calculateChartConfig() {
if (this.appStateService.activeProject) {
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._projectDetailsComponent.calculateChartConfig();
}
public toggleFileSelected($event: MouseEvent, file: FileStatus) {
@ -221,37 +200,9 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
this.appStateService.downloadFileRedactionReport(file);
}
downloadRedactionReport($event: MouseEvent) {
$event.stopPropagation();
this.appStateService.downloadRedactionReport();
}
public openEditProjectDialog($event: MouseEvent) {
this._dialogService.openEditProjectDialog(
$event,
this.appStateService.activeProject.project
);
}
public openDeleteProjectDialog($event: MouseEvent) {
this._dialogService.openDeleteProjectDialog(
$event,
this.appStateService.activeProject.project,
() => {
this._router.navigate(['/ui/projects']);
}
);
}
public openAssignProjectMembersDialog() {
this._dialogService.openAssignProjectMembersAndOwnerDialog(null, this.activeProject, () => {
this._reloadProjects();
});
}
public assignReviewer($event: MouseEvent, file: FileStatus) {
$event.stopPropagation();
this._fileActionService.assignProjectReviewer(file, () => this._reloadProjects());
this._fileActionService.assignProjectReviewer(file, () => this.reloadProjects());
}
public reanalyseFile($event: MouseEvent, fileStatus: FileStatus) {
@ -259,7 +210,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
this._reanalysisControllerService
.reanalyzeFile(this.appStateService.activeProject.project.projectId, fileStatus.fileId)
.subscribe(() => {
this._reloadProjects();
this.reloadProjects();
});
}

View File

@ -397,9 +397,6 @@
}
},
"project-details": {
"project-team": {
"label": "Projektteam"
},
"charts": {
"total-documents": {
"label": "Dokumente insgesamt"

View File

@ -411,13 +411,30 @@
}
},
"project-details": {
"project-team": {
"label": "Review Team"
},
"charts": {
"total-documents": {
"label": "Total Documents"
}
},
"stats": {
"documents": {
"label": "{{count}} documents"
},
"analysed-pages": {
"label": "{{count}} analysed pages"
},
"people": {
"label": "{{count}} people"
},
"created-on": {
"label": "Created on {{date}}"
},
"due-date": {
"label": "Due {{date}}"
}
},
"description": {
"label": "Description"
}
},
"header": {

View File

@ -55,18 +55,17 @@
.stats-subtitle {
display: flex;
gap: 12px;
> div {
display: flex;
justify-content: center;
align-items: center;
}
gap: 12px;
mat-icon {
width: 10px;
margin-right: 4px;
mat-icon {
width: 10px;
margin-right: 4px;
}
}
}

View File

@ -33,7 +33,7 @@ body {
.right-fixed-container {
border-left: 1px solid $grey-4;
background: $white;
height: 100%;
height: calc(100vh - 110px - 2 * #{$right-container-padding});
width: $right-container-inside-width;
padding: $right-container-padding;
position: fixed;

View File

@ -28,7 +28,7 @@ $dark: $black;
$separator: rgba(226, 228, 233, 0.9);
$right-container-inside-width: 340px;
$right-container-padding: 16px;
$right-container-padding: 24px;
$right-container-width: calc(
#{$right-container-inside-width} + 2 *#{$right-container-padding} + 1px
);

View File

@ -192,7 +192,6 @@
}
},
"project-details": {
"project-team": { "label": "Projektteam" },
"charts": { "total-documents": { "label": "Dokumente insgesamt" } }
},
"header": { "label": "Projektübersicht" },