fixed lint

This commit is contained in:
Timo Bejan 2020-11-06 14:01:46 +02:00
parent b8a35461c2
commit 9d407514b8
17 changed files with 702 additions and 577 deletions

View File

@ -71,6 +71,8 @@ import { TableColNameComponent } from './components/table-col-name/table-col-nam
import { ProjectDetailsComponent } from './screens/project-overview-screen/project-details/project-details.component';
import { PageIndicatorComponent } from './screens/file/page-indicator/page-indicator.component';
import { NeedsWorkBadgeComponent } from './screens/common/needs-work-badge/needs-work-badge.component';
import { ProjectOverviewEmptyComponent } from './screens/empty-states/project-overview-empty/project-overview-empty.component';
import { ProjectListingEmptyComponent } from './screens/empty-states/project-listing-empty/project-listing-empty.component';
export function HttpLoaderFactory(httpClient: HttpClient) {
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
@ -107,7 +109,9 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
TableColNameComponent,
ProjectDetailsComponent,
PageIndicatorComponent,
NeedsWorkBadgeComponent
NeedsWorkBadgeComponent,
ProjectOverviewEmptyComponent,
ProjectListingEmptyComponent
],
imports: [
BrowserModule,

View File

@ -0,0 +1,13 @@
<div class="empty-state-container">
<div class="heading-xl" translate="project-listing.no-projects"></div>
<button
(click)="addProjectRequest.emit()"
*ngIf="userService.isManager()"
class="add-project-btn"
color="primary"
mat-flat-button
>
<mat-icon svgIcon="red:plus"></mat-icon>
<span translate="project-listing.add-new"></span>
</button>
</div>

View File

@ -0,0 +1,13 @@
import { Component, EventEmitter, Output } from '@angular/core';
import { UserService } from '../../../user/user.service';
@Component({
selector: 'redaction-project-listing-empty',
templateUrl: './project-listing-empty.component.html',
styleUrls: ['./project-listing-empty.component.scss']
})
export class ProjectListingEmptyComponent {
@Output() addProjectRequest = new EventEmitter();
constructor(public userService: UserService) {}
}

View File

@ -0,0 +1,34 @@
<div class="empty-state-container">
<div
*ngIf="!appStateService.activeProject"
[innerHTML]="
'project-overview.no-project'
| translate: { projectId: appStateService.activeProjectId }
"
class="heading-xl"
></div>
<div
*ngIf="appStateService.activeProject"
class="heading-xl"
translate="project-overview.no-files"
></div>
<div *ngIf="appStateService.activeProject">
<button (click)="fileInput.click()" color="primary" mat-flat-button>
<mat-icon svgIcon="red:upload"></mat-icon>
<span translate="project-overview.upload-files-btn"></span>
</button>
<input
#fileInput
(change)="uploadFiles.emit($event.target.files)"
class="file-upload-input"
multiple="true"
type="file"
/>
<button [routerLink]="['/ui/projects/']" mat-icon-button class="close-btn">
<mat-icon svgIcon="red:close"></mat-icon>
</button>
</div>
</div>

View File

@ -0,0 +1,9 @@
.file-upload-input {
display: none;
}
.close-btn {
position: absolute;
top: 10px;
right: 14px;
}

View File

@ -0,0 +1,13 @@
import { Component, EventEmitter, Output } from '@angular/core';
import { AppStateService } from '../../../state/app-state.service';
@Component({
selector: 'redaction-project-overview-empty',
templateUrl: './project-overview-empty.component.html',
styleUrls: ['./project-overview-empty.component.scss']
})
export class ProjectOverviewEmptyComponent {
@Output() uploadFiles = new EventEmitter();
constructor(public appStateService: AppStateService) {}
}

View File

@ -1,266 +1,279 @@
<div class="page-header">
<div class="filters flex-row">
<div translate="filters.filter-by"></div>
<redaction-filter
[filters]="statusFilters"
[filterLabel]="'filters.status'"
[hasArrow]="false"
[icon]="'red:status'"
(filtersChanged)="filtersChanged()"
></redaction-filter>
<redaction-filter
[filters]="peopleFilters"
[filterLabel]="'filters.people'"
[hasArrow]="false"
[icon]="'red:user'"
(filtersChanged)="filtersChanged()"
></redaction-filter>
<redaction-filter
[filters]="dueDateFilters"
[filterLabel]="'filters.due-date'"
[hasArrow]="false"
[icon]="'red:lightning'"
(filtersChanged)="filtersChanged()"
></redaction-filter>
<redaction-filter
(filtersChanged)="filtersChanged()"
[filterLabel]="'filters.needs-work'"
[filters]="needsWorkFilters"
[hasArrow]="false"
[icon]="'red:needs-work'"
></redaction-filter>
</div>
<button
(click)="openAddProjectDialog()"
*ngIf="userService.isManager(user)"
class="add-project-btn"
color="primary"
mat-flat-button
>
<mat-icon svgIcon="red:plus"></mat-icon>
<span translate="project-listing.add-new"></span>
</button>
</div>
<div class="flex red-content-inner">
<div class="left-container">
<div class="table-header">
<div class="select-all-container">
<div
*ngIf="bulkSelectActive"
class="select-oval"
[class.active]="areAllProjectsSelected()"
(click)="toggleSelectAll()"
></div>
<span class="all-caps-label">
{{
'project-listing.table-header.title'
| translate: { length: displayedProjects.length || 0 }
}}
</span>
</div>
<div class="actions">
<div
translate="project-listing.table-header.bulk-select"
class="pointer"
[class.active]="bulkSelectActive"
(click)="toggleBulkSelect()"
></div>
<redaction-sorting
#sortingComponent
(optionChanged)="sortingOptionChanged($event)"
type="project-listing"
></redaction-sorting>
</div>
<section *ngIf="appStateService.hasProjects">
<div class="page-header">
<div class="filters flex-row">
<div translate="filters.filter-by"></div>
<redaction-filter
[filters]="statusFilters"
[filterLabel]="'filters.status'"
[hasArrow]="false"
[icon]="'red:status'"
(filtersChanged)="filtersChanged()"
></redaction-filter>
<redaction-filter
[filters]="peopleFilters"
[filterLabel]="'filters.people'"
[hasArrow]="false"
[icon]="'red:user'"
(filtersChanged)="filtersChanged()"
></redaction-filter>
<redaction-filter
[filters]="dueDateFilters"
[filterLabel]="'filters.due-date'"
[hasArrow]="false"
[icon]="'red:lightning'"
(filtersChanged)="filtersChanged()"
></redaction-filter>
<redaction-filter
(filtersChanged)="filtersChanged()"
[filterLabel]="'filters.needs-work'"
[filters]="needsWorkFilters"
[hasArrow]="false"
[icon]="'red:needs-work'"
></redaction-filter>
</div>
<button
(click)="openAddProjectDialog()"
*ngIf="userService.isManager(user)"
class="add-project-btn"
color="primary"
mat-flat-button
>
<mat-icon svgIcon="red:plus"></mat-icon>
<span translate="project-listing.add-new"></span>
</button>
</div>
<div class="grid-container" [class.bulk-select]="bulkSelectActive">
<div class="select-oval-placeholder" *ngIf="bulkSelectActive"></div>
<redaction-table-col-name
label="project-listing.table-col-names.name"
column="project.projectName"
[activeSortingOption]="sortingOption"
[withSort]="true"
(toggleSort)="sortingComponent.toggleSort($event)"
></redaction-table-col-name>
<redaction-table-col-name
label="project-listing.table-col-names.needs-work"
></redaction-table-col-name>
<redaction-table-col-name
label="project-listing.table-col-names.owner"
></redaction-table-col-name>
<redaction-table-col-name
label="project-listing.table-col-names.status"
class="flex-end"
></redaction-table-col-name>
<div
*ngIf="displayedProjects?.length === 0"
class="no-data heading-l"
translate="project-listing.no-projects-match"
></div>
<div
*ngFor="
let pw of displayedProjects | sortBy: sortingOption.order:sortingOption.column
"
[routerLink]="[canOpenProject(pw) ? '/ui/projects/' + pw.project.projectId : []]"
class="table-item"
[class.pointer]="canOpenProject(pw)"
>
<div class="pr-0" *ngIf="bulkSelectActive">
<div class="flex red-content-inner">
<div class="left-container">
<div class="table-header">
<div class="select-all-container">
<div
*ngIf="bulkSelectActive"
class="select-oval"
[class.active]="isProjectSelected(pw)"
(click)="toggleProjectSelected($event, pw)"
[class.active]="areAllProjectsSelected()"
(click)="toggleSelectAll()"
></div>
<span class="all-caps-label">
{{
'project-listing.table-header.title'
| translate: { length: displayedProjects.length || 0 }
}}
</span>
</div>
<div>
<div class="table-item-title">
{{ pw.project.projectName }}
<div class="actions">
<div
translate="project-listing.table-header.bulk-select"
class="pointer"
[class.active]="bulkSelectActive"
(click)="toggleBulkSelect()"
></div>
<redaction-sorting
#sortingComponent
[initialOption]="sortingOption"
(optionChanged)="sortingOptionChanged($event)"
type="project-listing"
></redaction-sorting>
</div>
</div>
<div class="grid-container" [class.bulk-select]="bulkSelectActive">
<div class="select-oval-placeholder" *ngIf="bulkSelectActive"></div>
<redaction-table-col-name
label="project-listing.table-col-names.name"
column="project.projectName"
[activeSortingOption]="sortingOption"
[withSort]="true"
(toggleSort)="sortingComponent.toggleSort($event)"
></redaction-table-col-name>
<redaction-table-col-name
label="project-listing.table-col-names.needs-work"
></redaction-table-col-name>
<redaction-table-col-name
label="project-listing.table-col-names.owner"
></redaction-table-col-name>
<redaction-table-col-name
label="project-listing.table-col-names.status"
class="flex-end"
></redaction-table-col-name>
<div
*ngIf="displayedProjects?.length === 0"
class="no-data heading-l"
translate="project-listing.no-projects-match"
></div>
<div
*ngFor="
let pw of displayedProjects
| sortBy: sortingOption.order:sortingOption.column
"
[routerLink]="[
canOpenProject(pw) ? '/ui/projects/' + pw.project.projectId : []
]"
class="table-item"
[class.pointer]="canOpenProject(pw)"
>
<div class="pr-0" *ngIf="bulkSelectActive">
<div
class="select-oval"
[class.active]="isProjectSelected(pw)"
(click)="toggleProjectSelected($event, pw)"
></div>
</div>
<div class="small-label stats-subtitle">
<div>
<mat-icon svgIcon="red:document"></mat-icon>
{{ documentCount(pw) }}
<div>
<div class="table-item-title">
{{ pw.project.projectName }}
</div>
<div>
<mat-icon svgIcon="red:pages"></mat-icon>
{{ pw.totalNumberOfPages }}
</div>
<div>
<mat-icon svgIcon="red:user"></mat-icon>
{{ userCount(pw) }}
</div>
<div>
<mat-icon svgIcon="red:calendar"></mat-icon>
{{ pw.project.date | date: 'mediumDate' }}
</div>
<div *ngIf="pw.project.dueDate">
<mat-icon svgIcon="red:lightning"></mat-icon>
{{ pw.project.dueDate | date: 'mediumDate' }}
<div class="small-label stats-subtitle">
<div>
<mat-icon svgIcon="red:document"></mat-icon>
{{ documentCount(pw) }}
</div>
<div>
<mat-icon svgIcon="red:pages"></mat-icon>
{{ pw.totalNumberOfPages }}
</div>
<div>
<mat-icon svgIcon="red:user"></mat-icon>
{{ userCount(pw) }}
</div>
<div>
<mat-icon svgIcon="red:calendar"></mat-icon>
{{ pw.project.date | date: 'mediumDate' }}
</div>
<div *ngIf="pw.project.dueDate">
<mat-icon svgIcon="red:lightning"></mat-icon>
{{ pw.project.dueDate | date: 'mediumDate' }}
</div>
</div>
</div>
</div>
<div>
<redaction-needs-work-badge
[needsWorkInput]="pw"
[displayed]="true"
></redaction-needs-work-badge>
</div>
<div>
<redaction-initials-avatar
[userId]="pw.project.ownerId"
withName="true"
></redaction-initials-avatar>
</div>
<div class="status-container">
<redaction-status-bar
[config]="getProjectStatusConfig(pw)"
></redaction-status-bar>
<div>
<redaction-needs-work-badge
[needsWorkInput]="pw"
[displayed]="true"
></redaction-needs-work-badge>
</div>
<div>
<redaction-initials-avatar
[userId]="pw.project.ownerId"
withName="true"
></redaction-initials-avatar>
</div>
<div class="status-container">
<redaction-status-bar
[config]="getProjectStatusConfig(pw)"
></redaction-status-bar>
<div class="action-buttons">
<button
(click)="openDeleteProjectDialog($event, pw.project)"
[matTooltip]="'project-listing.delete.action' | translate"
[matTooltipPosition]="'above'"
*ngIf="userService.isManager(user)"
color="accent"
mat-icon-button
>
<mat-icon svgIcon="red:trash"></mat-icon>
</button>
<div class="action-buttons">
<button
(click)="openDeleteProjectDialog($event, pw.project)"
[matTooltip]="'project-listing.delete.action' | translate"
[matTooltipPosition]="'above'"
*ngIf="userService.isManager(user)"
color="accent"
mat-icon-button
>
<mat-icon svgIcon="red:trash"></mat-icon>
</button>
<button
(click)="openEditProjectDialog($event, pw.project)"
[matTooltip]="'project-listing.edit.action' | translate"
[matTooltipPosition]="'above'"
*ngIf="userService.isManager(user)"
color="accent"
mat-icon-button
>
<mat-icon svgIcon="red:edit"></mat-icon>
</button>
<button
(click)="openEditProjectDialog($event, pw.project)"
[matTooltip]="'project-listing.edit.action' | translate"
[matTooltipPosition]="'above'"
*ngIf="userService.isManager(user)"
color="accent"
mat-icon-button
>
<mat-icon svgIcon="red:edit"></mat-icon>
</button>
<button
mat-icon-button
(click)="downloadRedactionReport($event, pw.project)"
*ngIf="
appStateService.isManagerAndOwner(user, pw.project) && pw.hasFiles
"
[matTooltip]="'project-listing.report.action' | translate"
[matTooltipPosition]="'above'"
color="accent"
>
<mat-icon svgIcon="red:report"></mat-icon>
</button>
<button
(click)="openAssignProjectOwnerDialog($event, pw.project)"
[matTooltip]="'project-listing.assign.action' | translate"
[matTooltipPosition]="'above'"
*ngIf="userService.isManager(user)"
color="accent"
mat-icon-button
>
<mat-icon svgIcon="red:assign"></mat-icon>
</button>
<button
color="accent"
*ngIf="
appStateService.isManagerAndOwner(user, pw.project) && pw.hasFiles
"
(click)="reanalyseProject($event, pw.project)"
mat-icon-button
[matTooltip]="'project-listing.reanalyse.action' | translate"
[matTooltipPosition]="'above'"
>
<mat-icon svgIcon="red:refresh"></mat-icon>
</button>
<button
mat-icon-button
(click)="downloadRedactionReport($event, pw.project)"
*ngIf="
appStateService.isManagerAndOwner(user, pw.project) &&
pw.hasFiles
"
[matTooltip]="'project-listing.report.action' | translate"
[matTooltipPosition]="'above'"
color="accent"
>
<mat-icon svgIcon="red:report"></mat-icon>
</button>
<button
(click)="openAssignProjectOwnerDialog($event, pw.project)"
[matTooltip]="'project-listing.assign.action' | translate"
[matTooltipPosition]="'above'"
*ngIf="userService.isManager(user)"
color="accent"
mat-icon-button
>
<mat-icon svgIcon="red:assign"></mat-icon>
</button>
<button
color="accent"
*ngIf="
appStateService.isManagerAndOwner(user, pw.project) &&
pw.hasFiles
"
(click)="reanalyseProject($event, pw.project)"
mat-icon-button
[matTooltip]="'project-listing.reanalyse.action' | translate"
[matTooltipPosition]="'above'"
>
<mat-icon svgIcon="red:refresh"></mat-icon>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="right-fixed-container">
<div>
<redaction-simple-doughnut-chart
[config]="projectsChartData"
[strokeWidth]="15"
[subtitle]="'project-listing.stats.charts.projects'"
></redaction-simple-doughnut-chart>
<div class="right-fixed-container">
<div>
<redaction-simple-doughnut-chart
[config]="projectsChartData"
[strokeWidth]="15"
[subtitle]="'project-listing.stats.charts.projects'"
></redaction-simple-doughnut-chart>
<div class="project-stats-container">
<div class="project-stats-item">
<mat-icon svgIcon="red:document"></mat-icon>
<div>
<div class="heading">{{ totalPages }}</div>
<div translate="project-listing.stats.analyzed-pages"></div>
<div class="project-stats-container">
<div class="project-stats-item">
<mat-icon svgIcon="red:document"></mat-icon>
<div>
<div class="heading">{{ totalPages }}</div>
<div translate="project-listing.stats.analyzed-pages"></div>
</div>
</div>
</div>
<div class="project-stats-item">
<mat-icon svgIcon="red:user"></mat-icon>
<div>
<div class="heading">{{ totalPeople }}</div>
<div translate="project-listing.stats.total-people"></div>
<div class="project-stats-item">
<mat-icon svgIcon="red:user"></mat-icon>
<div>
<div class="heading">{{ totalPeople }}</div>
<div translate="project-listing.stats.total-people"></div>
</div>
</div>
</div>
</div>
</div>
<div>
<redaction-simple-doughnut-chart
[config]="documentsChartData"
[strokeWidth]="15"
[subtitle]="'project-listing.stats.charts.total-documents'"
></redaction-simple-doughnut-chart>
<div>
<redaction-simple-doughnut-chart
[config]="documentsChartData"
[strokeWidth]="15"
[subtitle]="'project-listing.stats.charts.total-documents'"
></redaction-simple-doughnut-chart>
</div>
</div>
</div>
</div>
</section>
<redaction-project-listing-empty
*ngIf="!appStateService.hasProjects"
(addProjectRequest)="openAddProjectDialog()"
></redaction-project-listing-empty>

View File

@ -37,7 +37,6 @@ export class ProjectListingScreenComponent implements OnInit {
public needsWorkFilters: FilterModel[];
public displayedProjects: ProjectWrapper[] = [];
@ViewChild('sortingComponent', { static: true }) public sortingComponent: SortingComponent;
public sortingOption: SortingOption = { column: 'projectDate', order: 'desc' };
constructor(
@ -49,7 +48,6 @@ export class ProjectListingScreenComponent implements OnInit {
) {}
public ngOnInit(): void {
this.sortingComponent.setOption(this.sortingOption);
this.appStateService.reset();
this._calculateData();
this.appStateService.fileStatusChanged.subscribe(() => {

View File

@ -1,4 +1,4 @@
import { Component, OnInit, Output, EventEmitter } from '@angular/core';
import { Component, OnInit, Output, EventEmitter, ChangeDetectorRef } from '@angular/core';
import { AppStateService } from '../../../state/app-state.service';
import { UserService } from '../../../user/user.service';
import { groupBy } from '../../../utils/functions';
@ -18,11 +18,17 @@ export class ProjectDetailsComponent implements OnInit {
constructor(
public readonly appStateService: AppStateService,
public readonly userService: UserService,
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _dialogService: DialogService,
private readonly _router: Router
) {}
ngOnInit(): void {}
ngOnInit(): void {
this.calculateChartConfig();
this.appStateService.fileStatusChanged.subscribe((event) => {
this.calculateChartConfig();
});
}
public get user() {
return this.userService.user;
@ -77,6 +83,7 @@ export class ProjectDetailsComponent implements OnInit {
for (const key of Object.keys(groups)) {
this.documentsChartData.push({ value: groups[key].length, color: key, label: key });
}
this._changeDetectorRef.detectChanges();
}
}

View File

@ -1,339 +1,338 @@
<div
*ngIf="!appStateService.activeProject"
[innerHTML]="
'project-overview.no-project'
| translate: { projectId: appStateService.activeProject.project.projectId }
"
class="heading-l"
></div>
<div *ngIf="appStateService.activeProject" class="page-header">
<div *ngIf="!appStateService.activeProject.hasFiles">
<div class="no-data heading-l" translate="project-overview.no-files"></div>
</div>
<div class="filters flex-row" *ngIf="appStateService.activeProject.hasFiles">
<div translate="filters.filter-by"></div>
<redaction-filter
(filtersChanged)="filtersChanged()"
[filterLabel]="'filters.status'"
[filters]="statusFilters"
[hasArrow]="false"
[icon]="'red:status'"
></redaction-filter>
<redaction-filter
(filtersChanged)="filtersChanged()"
[filterLabel]="'filters.people'"
[filters]="peopleFilters"
[hasArrow]="false"
[icon]="'red:user'"
></redaction-filter>
<redaction-filter
(filtersChanged)="filtersChanged()"
[filterLabel]="'filters.needs-work'"
[filters]="needsWorkFilters"
[hasArrow]="false"
[icon]="'red:needs-work'"
></redaction-filter>
</div>
<div>
<button (click)="fileInput.click()" color="primary" class="custom-mini-fab" mat-mini-fab>
<mat-icon svgIcon="red:upload"></mat-icon>
</button>
<button [routerLink]="['/ui/projects/']" mat-icon-button>
<mat-icon svgIcon="red:close"></mat-icon>
</button>
<input
#fileInput
(change)="uploadFiles($event.target.files)"
class="file-upload-input"
multiple="true"
type="file"
/>
</div>
</div>
<div class="flex red-content-inner">
<div class="left-container" *ngIf="appStateService.activeProject.hasFiles">
<div class="table-header">
<div class="select-all-container">
<div
(click)="toggleSelectAll()"
*ngIf="bulkSelectActive"
[class.active]="areAllFilesSelected()"
class="select-oval"
></div>
<span class="all-caps-label">
{{
'project-overview.table-header.title'
| translate: { length: displayedFiles.length || 0 }
}}
</span>
</div>
<div class="actions">
<div
(click)="toggleBulkSelect()"
[class.active]="bulkSelectActive"
class="pointer"
translate="project-overview.table-header.bulk-select"
></div>
<redaction-sorting
#sortingComponent
[initialOption]="sortingOption"
(optionChanged)="sortingOptionChanged($event)"
type="project-overview"
></redaction-sorting>
</div>
<redaction-project-overview-empty
(uploadFiles)="uploadFiles($event)"
*ngIf="!appStateService.activeProject?.hasFiles"
></redaction-project-overview-empty>
<section *ngIf="appStateService.activeProject?.hasFiles">
<div *ngIf="appStateService.activeProject" class="page-header">
<div class="filters flex-row">
<div translate="filters.filter-by"></div>
<redaction-filter
(filtersChanged)="filtersChanged()"
[filterLabel]="'filters.status'"
[filters]="statusFilters"
[hasArrow]="false"
[icon]="'red:status'"
></redaction-filter>
<redaction-filter
(filtersChanged)="filtersChanged()"
[filterLabel]="'filters.people'"
[filters]="peopleFilters"
[hasArrow]="false"
[icon]="'red:user'"
></redaction-filter>
<redaction-filter
(filtersChanged)="filtersChanged()"
[filterLabel]="'filters.needs-work'"
[filters]="needsWorkFilters"
[hasArrow]="false"
[icon]="'red:needs-work'"
></redaction-filter>
</div>
<div [class.bulk-select]="bulkSelectActive" class="grid-container">
<!-- Table column names-->
<div *ngIf="bulkSelectActive" class="select-oval-placeholder"></div>
<redaction-table-col-name
(toggleSort)="sortingComponent.toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
column="filename"
label="project-overview.table-col-names.name"
></redaction-table-col-name>
<redaction-table-col-name
(toggleSort)="sortingComponent.toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
column="added"
label="project-overview.table-col-names.added-on"
></redaction-table-col-name>
<redaction-table-col-name
label="project-overview.table-col-names.needs-work"
></redaction-table-col-name>
<redaction-table-col-name
(toggleSort)="sortingComponent.toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
column="reviewerName"
label="project-overview.table-col-names.assigned-to"
></redaction-table-col-name>
<redaction-table-col-name
(toggleSort)="sortingComponent.toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
column="pages"
label="project-overview.table-col-names.pages"
></redaction-table-col-name>
<redaction-table-col-name
(toggleSort)="sortingComponent.toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
class="flex-end"
column="status"
label="project-overview.table-col-names.status"
></redaction-table-col-name>
<div
*ngIf="displayedFiles?.length === 0"
class="no-data heading-l"
translate="project-overview.no-files-match"
></div>
<div
*ngFor="
let fileStatus of displayedFiles
| sortBy: sortingOption.order:sortingOption.column
"
[class.pointer]="canOpenFile(fileStatus)"
[routerLink]="
canOpenFile(fileStatus)
? [
'/ui/projects/' +
appStateService.activeProject.project.projectId +
'/file/' +
fileStatus.fileId
]
: []
"
class="table-item"
<div>
<button
(click)="fileInput.click()"
color="primary"
class="custom-mini-fab"
mat-mini-fab
>
<div *ngIf="bulkSelectActive" class="pr-0">
<mat-icon svgIcon="red:upload"></mat-icon>
</button>
<button [routerLink]="['/ui/projects/']" mat-icon-button>
<mat-icon svgIcon="red:close"></mat-icon>
</button>
<input
#fileInput
(change)="uploadFiles($event.target.files)"
class="file-upload-input"
multiple="true"
type="file"
/>
</div>
</div>
<div class="flex red-content-inner">
<div class="left-container">
<div class="table-header">
<div class="select-all-container">
<div
(click)="toggleFileSelected($event, fileStatus)"
[class.active]="isFileSelected(fileStatus)"
(click)="toggleSelectAll()"
*ngIf="bulkSelectActive"
[class.active]="areAllFilesSelected()"
class="select-oval"
></div>
<span class="all-caps-label">
{{
'project-overview.table-header.title'
| translate: { length: displayedFiles.length || 0 }
}}
</span>
</div>
<div class="actions">
<div
(click)="toggleBulkSelect()"
[class.active]="bulkSelectActive"
class="pointer"
translate="project-overview.table-header.bulk-select"
></div>
<redaction-sorting
#sortingComponent
[initialOption]="sortingOption"
(optionChanged)="sortingOptionChanged($event)"
type="project-overview"
></redaction-sorting>
</div>
</div>
<div [class.bulk-select]="bulkSelectActive" class="grid-container">
<!-- Table column names-->
<div *ngIf="bulkSelectActive" class="select-oval-placeholder"></div>
<redaction-table-col-name
(toggleSort)="sortingComponent.toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
column="filename"
label="project-overview.table-col-names.name"
></redaction-table-col-name>
<redaction-table-col-name
(toggleSort)="sortingComponent.toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
column="added"
label="project-overview.table-col-names.added-on"
></redaction-table-col-name>
<redaction-table-col-name
label="project-overview.table-col-names.needs-work"
></redaction-table-col-name>
<redaction-table-col-name
(toggleSort)="sortingComponent.toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
column="reviewerName"
label="project-overview.table-col-names.assigned-to"
></redaction-table-col-name>
<redaction-table-col-name
(toggleSort)="sortingComponent.toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
column="pages"
label="project-overview.table-col-names.pages"
></redaction-table-col-name>
<redaction-table-col-name
(toggleSort)="sortingComponent.toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
class="flex-end"
column="status"
label="project-overview.table-col-names.status"
></redaction-table-col-name>
<div
[matTooltipPosition]="'above'"
[matTooltip]="'[' + fileStatus.status + '] ' + fileStatus.filename"
*ngIf="displayedFiles?.length === 0"
class="no-data heading-l"
translate="project-overview.no-files-match"
></div>
<div
*ngFor="
let fileStatus of displayedFiles
| sortBy: sortingOption.order:sortingOption.column
"
[class.pointer]="canOpenFile(fileStatus)"
[routerLink]="
canOpenFile(fileStatus)
? [
'/ui/projects/' +
appStateService.activeProject.project.projectId +
'/file/' +
fileStatus.fileId
]
: []
"
class="table-item"
>
<div class="filename-wrapper">
<div *ngIf="bulkSelectActive" class="pr-0">
<div
[class.disabled]="isPending(fileStatus) || isProcessing(fileStatus)"
[class.error]="isError(fileStatus)"
class="table-item-title"
>
{{ fileStatus.filename }}
(click)="toggleFileSelected($event, fileStatus)"
[class.active]="isFileSelected(fileStatus)"
class="select-oval"
></div>
</div>
<div
[matTooltipPosition]="'above'"
[matTooltip]="'[' + fileStatus.status + '] ' + fileStatus.filename"
>
<div class="filename-wrapper">
<div
[class.disabled]="isPending(fileStatus) || isProcessing(fileStatus)"
[class.error]="isError(fileStatus)"
class="table-item-title"
>
{{ fileStatus.filename }}
</div>
<span
*ngIf="appStateService.fileNotUpToDateWithDictionary(fileStatus)"
class="pill"
translate="project-overview.new-rule.label"
></span>
</div>
<span
*ngIf="appStateService.fileNotUpToDateWithDictionary(fileStatus)"
class="pill"
translate="project-overview.new-rule.label"
></span>
</div>
</div>
<div>
<div [class.error]="isError(fileStatus)" class="small-label">
{{ fileStatus.added | date: 'd MMM. yyyy, hh:mm a' }}
<div>
<div [class.error]="isError(fileStatus)" class="small-label">
{{ fileStatus.added | date: 'd MMM. yyyy, hh:mm a' }}
</div>
</div>
</div>
<div>
<redaction-needs-work-badge
[needsWorkInput]="fileStatus"
[displayed]="!isError(fileStatus)"
></redaction-needs-work-badge>
</div>
<div *ngIf="!isError(fileStatus)" class="assigned-to">
<redaction-initials-avatar
[userId]="fileStatus.currentReviewer"
withName="true"
></redaction-initials-avatar>
</div>
<div>
<redaction-needs-work-badge
[needsWorkInput]="fileStatus"
[displayed]="!isError(fileStatus)"
></redaction-needs-work-badge>
</div>
<div *ngIf="!isError(fileStatus)" class="assigned-to">
<redaction-initials-avatar
[userId]="fileStatus.currentReviewer"
withName="true"
></redaction-initials-avatar>
</div>
<div *ngIf="!isError(fileStatus)" class="pages">
<mat-icon svgIcon="red:pages"></mat-icon>
{{ fileStatus.numberOfPages }}
</div>
<div *ngIf="!isError(fileStatus)" class="pages">
<mat-icon svgIcon="red:pages"></mat-icon>
{{ fileStatus.numberOfPages }}
</div>
<div [class.extend-cols]="isError(fileStatus)" class="status-container">
<div
*ngIf="isError(fileStatus)"
class="small-label error"
translate="project-overview.file-listing.file-entry.file-error"
></div>
<div
*ngIf="isPending(fileStatus)"
class="small-label"
translate="project-overview.file-listing.file-entry.file-pending"
></div>
<div
*ngIf="isProcessing(fileStatus)"
class="small-label"
translate="processing"
></div>
<redaction-status-bar
*ngIf="
!isPending(fileStatus) &&
!isProcessing(fileStatus) &&
!isError(fileStatus)
"
[config]="[
{
color: fileStatus.status,
length: 1
}
]"
></redaction-status-bar>
<div [class.extend-cols]="isError(fileStatus)" class="status-container">
<div
*ngIf="isError(fileStatus)"
class="small-label error"
translate="project-overview.file-listing.file-entry.file-error"
></div>
<div
*ngIf="isPending(fileStatus)"
class="small-label"
translate="project-overview.file-listing.file-entry.file-pending"
></div>
<div
*ngIf="isProcessing(fileStatus)"
class="small-label"
translate="processing"
></div>
<redaction-status-bar
*ngIf="
!isPending(fileStatus) &&
!isProcessing(fileStatus) &&
!isError(fileStatus)
"
[config]="[
{
color: fileStatus.status,
length: 1
}
]"
></redaction-status-bar>
<div class="action-buttons">
<button
(click)="openDeleteFileDialog($event, fileStatus)"
*ngIf="
userService.isManager(user) ||
appStateService.isActiveProjectOwnerAndManager
"
[matTooltip]="'project-overview.delete.action' | translate"
[matTooltipPosition]="'above'"
color="accent"
mat-icon-button
>
<mat-icon svgIcon="red:trash"></mat-icon>
</button>
<button
(click)="downloadFileRedactionReport($event, fileStatus)"
[matTooltip]="'project-overview.report.action' | translate"
[matTooltipPosition]="'above'"
color="accent"
mat-icon-button
>
<mat-icon svgIcon="red:report"></mat-icon>
</button>
<button
(click)="assignReviewer($event, fileStatus)"
*ngIf="
appStateService.isActiveProjectMember &&
!isApprovedOrUnderApproval(fileStatus)
"
[matTooltip]="'project-overview.assign.action' | translate"
[matTooltipPosition]="'above'"
color="accent"
mat-icon-button
>
<mat-icon svgIcon="red:assign"></mat-icon>
</button>
<button
(click)="reanalyseFile($event, fileStatus)"
*ngIf="!isApprovedOrUnderApproval(fileStatus)"
[matTooltip]="'project-overview.reanalyse.action' | translate"
[matTooltipPosition]="'above'"
color="accent"
mat-icon-button
>
<mat-icon svgIcon="red:refresh"></mat-icon>
</button>
<button
(click)="requestApprovalOrApproveFile($event, fileStatus)"
*ngIf="
canApprove(fileStatus) &&
appStateService.isActiveProjectOwnerAndManager
"
[matTooltip]="
(fileStatus.status === 'UNDER_APPROVAL'
? 'project-overview.approve'
: 'project-overview.under-approval'
) | translate
"
[matTooltipPosition]="'above'"
color="accent"
mat-icon-button
>
<mat-icon svgIcon="red:check-alt"></mat-icon>
</button>
<button
(click)="undoApproveOrUnderApproval($event, fileStatus)"
*ngIf="
isApprovedOrUnderApproval(fileStatus) &&
appStateService.isActiveProjectOwnerAndManager
"
[matTooltip]="
(fileStatus.status === 'APPROVED'
? 'project-overview.under-approval'
: 'project-overview.under-review'
) | translate
"
[matTooltipPosition]="'above'"
color="accent"
mat-icon-button
>
<mat-icon svgIcon="red:undo"></mat-icon>
</button>
<div class="action-buttons">
<button
(click)="openDeleteFileDialog($event, fileStatus)"
*ngIf="
userService.isManager(user) ||
appStateService.isActiveProjectOwnerAndManager
"
[matTooltip]="'project-overview.delete.action' | translate"
[matTooltipPosition]="'above'"
color="accent"
mat-icon-button
>
<mat-icon svgIcon="red:trash"></mat-icon>
</button>
<button
(click)="downloadFileRedactionReport($event, fileStatus)"
[matTooltip]="'project-overview.report.action' | translate"
[matTooltipPosition]="'above'"
color="accent"
mat-icon-button
>
<mat-icon svgIcon="red:report"></mat-icon>
</button>
<button
(click)="assignReviewer($event, fileStatus)"
*ngIf="
appStateService.isActiveProjectMember &&
!isApprovedOrUnderApproval(fileStatus)
"
[matTooltip]="'project-overview.assign.action' | translate"
[matTooltipPosition]="'above'"
color="accent"
mat-icon-button
>
<mat-icon svgIcon="red:assign"></mat-icon>
</button>
<button
(click)="reanalyseFile($event, fileStatus)"
*ngIf="!isApprovedOrUnderApproval(fileStatus)"
[matTooltip]="'project-overview.reanalyse.action' | translate"
[matTooltipPosition]="'above'"
color="accent"
mat-icon-button
>
<mat-icon svgIcon="red:refresh"></mat-icon>
</button>
<button
(click)="requestApprovalOrApproveFile($event, fileStatus)"
*ngIf="
canApprove(fileStatus) &&
appStateService.isActiveProjectOwnerAndManager
"
[matTooltip]="
(fileStatus.status === 'UNDER_APPROVAL'
? 'project-overview.approve'
: 'project-overview.under-approval'
) | translate
"
[matTooltipPosition]="'above'"
color="accent"
mat-icon-button
>
<mat-icon svgIcon="red:check-alt"></mat-icon>
</button>
<button
(click)="undoApproveOrUnderApproval($event, fileStatus)"
*ngIf="
isApprovedOrUnderApproval(fileStatus) &&
appStateService.isActiveProjectOwnerAndManager
"
[matTooltip]="
(fileStatus.status === 'APPROVED'
? 'project-overview.under-approval'
: 'project-overview.under-review'
) | translate
"
[matTooltipPosition]="'above'"
color="accent"
mat-icon-button
>
<mat-icon svgIcon="red:undo"></mat-icon>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="right-fixed-container">
<redaction-project-details
#projectDetailsComponent
(reloadProjects)="reloadProjects()"
></redaction-project-details>
<div class="right-fixed-container">
<redaction-project-details
#projectDetailsComponent
(reloadProjects)="reloadProjects()"
></redaction-project-details>
</div>
</div>
</div>
</section>

View File

@ -1,10 +1,6 @@
@import '../../../assets/styles/red-variables';
@import '../../../assets/styles/red-mixins';
.file-upload-input {
display: none;
}
.actions {
.active {
font-weight: 600;

View File

@ -19,12 +19,11 @@ 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 { SortingOption } from '../../components/sorting/sorting.component';
import { ProjectDetailsComponent } from './project-details/project-details.component';
import { FileStatusWrapper } from '../file/model/file-status.wrapper';
import {
annotationFilterChecker,
fileAddedFilterChecker,
getFilteredEntities,
keyChecker
} from '../../common/filter/utils/filter-utils';
@ -40,12 +39,11 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
public statusFilters: FilterModel[];
public peopleFilters: FilterModel[];
// public addedDateFilters: FilterModel[];
public needsWorkFilters: FilterModel[];
public displayedFiles: FileStatusWrapper[] = [];
@ViewChild('projectDetailsComponent', { static: true })
@ViewChild('projectDetailsComponent', { static: false })
private _projectDetailsComponent: ProjectDetailsComponent;
public sortingOption: SortingOption = { column: 'added', order: 'desc' };
@ -161,7 +159,8 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
private _calculateData(): void {
this._computeAllFilters();
this._filterFiles();
this._projectDetailsComponent.calculateChartConfig();
console.log('calculate parent');
this._projectDetailsComponent?.calculateChartConfig();
this._changeDetectorRef.detectChanges();
}

View File

@ -215,6 +215,10 @@ export class AppStateService {
return this._appState.projects;
}
get hasProjects() {
return this.allProjects?.length > 0;
}
get activeFile(): FileStatusWrapper {
return this._appState.activeFile;
}
@ -281,6 +285,9 @@ export class AppStateService {
.toPromise();
const oldFiles = [...project.files];
const fileStatusChangedEvent = [];
const fileReanalysedEvent = [];
for (const file of files) {
let found = false;
for (const oldFile of oldFiles) {
@ -291,10 +298,10 @@ export class AppStateService {
file,
this._userService.getNameForId(file.currentReviewer)
);
this.fileStatusChanged.emit(fileStatusWrapper);
fileStatusChangedEvent.push(fileStatusWrapper);
if (oldFile.lastProcessed !== file.lastProcessed) {
this.fileReanalysed.emit(fileStatusWrapper);
fileReanalysedEvent.push(fileStatusWrapper);
}
}
found = true;
@ -303,12 +310,11 @@ export class AppStateService {
}
// emit for new file
if (!found) {
this.fileStatusChanged.emit(
new FileStatusWrapper(
file,
this._userService.getNameForId(file.currentReviewer)
)
const fsw = new FileStatusWrapper(
file,
this._userService.getNameForId(file.currentReviewer)
);
fileStatusChangedEvent.push(fsw);
}
}
@ -317,6 +323,10 @@ export class AppStateService {
);
this._computeStats();
fileReanalysedEvent.forEach((file) => this.fileReanalysed.emit(file));
fileStatusChangedEvent.forEach((file) => this.fileStatusChanged.emit(file));
return files;
}

View File

@ -105,11 +105,17 @@ export class UserService {
: undefined;
}
isManager(user: User): boolean {
isManager(user?: User): boolean {
if (!user) {
user = this.user;
}
return user.roles.indexOf('RED_MANAGER') >= 0;
}
isUser(user: User): boolean {
isUser(user?: User): boolean {
if (!user) {
user = this.user;
}
return user.roles.indexOf('RED_USER') >= 0;
}

View File

@ -142,7 +142,7 @@
"delete-failed": "Failed to delete project: {{projectName}}"
},
"add-new": "New Project",
"no-projects": "You currently have no projects. You can start your work by creating a new one!",
"no-projects": "You currently have no projects.",
"no-projects-match": "No Projects match your current filters"
},
"file-details": {
@ -172,6 +172,7 @@
"no-files": "Project is empty!",
"no-files-match": "No File match your current filters",
"upload-files": "Drop files anywhere for upload!",
"upload-files-btn": "Upload Files",
"new-rule": {
"label": "Outdated",
"toast": {

View File

@ -195,3 +195,13 @@ body {
.pr-0 {
padding-right: 0 !important;
}
.empty-state-container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 12px;
height: calc(100vh - 61px);
position: relative;
}