added needs work for project overview, extracted component

This commit is contained in:
Timo Bejan 2020-11-06 12:26:26 +02:00
parent a3bc58c269
commit 6241ef254a
13 changed files with 125 additions and 85 deletions

View File

@ -70,6 +70,7 @@ import { SortingComponent } from './components/sorting/sorting.component';
import { TableColNameComponent } from './components/table-col-name/table-col-name.component'; import { TableColNameComponent } from './components/table-col-name/table-col-name.component';
import { ProjectDetailsComponent } from './screens/project-overview-screen/project-details/project-details.component'; import { ProjectDetailsComponent } from './screens/project-overview-screen/project-details/project-details.component';
import { PageIndicatorComponent } from './screens/file/page-indicator/page-indicator.component'; import { PageIndicatorComponent } from './screens/file/page-indicator/page-indicator.component';
import { NeedsWorkBadgeComponent } from './screens/common/needs-work-badge/needs-work-badge.component';
export function HttpLoaderFactory(httpClient: HttpClient) { export function HttpLoaderFactory(httpClient: HttpClient) {
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json'); return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
@ -105,7 +106,8 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
SortingComponent, SortingComponent,
TableColNameComponent, TableColNameComponent,
ProjectDetailsComponent, ProjectDetailsComponent,
PageIndicatorComponent PageIndicatorComponent,
NeedsWorkBadgeComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,

View File

@ -0,0 +1,14 @@
<div *ngIf="displayed" class="needs-work">
<redaction-annotation-icon
*ngIf="needsWorkInput.hasRedactions"
[typeValue]="appStateService.getDictionaryTypeValue('redaction')"
></redaction-annotation-icon>
<redaction-annotation-icon
*ngIf="needsWorkInput.hasHints"
[typeValue]="appStateService.getDictionaryTypeValue('hint')"
></redaction-annotation-icon>
<redaction-annotation-icon
*ngIf="needsWorkInput.hasRequests"
[typeValue]="appStateService.getDictionaryTypeValue('request')"
></redaction-annotation-icon>
</div>

View File

@ -0,0 +1,7 @@
.needs-work {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
gap: 4px;
}

View File

@ -0,0 +1,22 @@
import { Component, Input, OnInit } from '@angular/core';
import { AppStateService } from '../../../state/app-state.service';
export interface NeedsWorkInput {
hasHints?: boolean;
hasRedactions?: boolean;
hasRequests?: boolean;
}
@Component({
selector: 'redaction-needs-work-badge',
templateUrl: './needs-work-badge.component.html',
styleUrls: ['./needs-work-badge.component.scss']
})
export class NeedsWorkBadgeComponent implements OnInit {
@Input() displayed: boolean;
@Input() needsWorkInput: NeedsWorkInput;
constructor(public appStateService: AppStateService) {}
ngOnInit(): void {}
}

View File

@ -22,20 +22,13 @@
[icon]="'red:lightning'" [icon]="'red:lightning'"
(filtersChanged)="filtersChanged()" (filtersChanged)="filtersChanged()"
></redaction-filter> ></redaction-filter>
<!-- <redaction-filter--> <redaction-filter
<!-- [filters]="addedDateFilters"--> (filtersChanged)="filtersChanged()"
<!-- [filterLabel]="'filters.created-on'"--> [filterLabel]="'filters.needs-work'"
<!-- [hasArrow]="false"--> [filters]="needsWorkFilters"
<!-- [icon]="'red:calendar'"--> [hasArrow]="false"
<!-- (filtersChanged)="filtersChanged()"--> [icon]="'red:needs-work'"
<!-- ></redaction-filter>--> ></redaction-filter>
<!-- <button mat-button translate="filters.project">-->
<!-- <mat-icon svgIcon="red:folder"></mat-icon>-->
<!-- </button>-->
<!-- <button mat-button translate="filters.document">-->
<!-- <mat-icon svgIcon="red:document"></mat-icon>-->
<!-- </button>-->
</div> </div>
<button <button
(click)="openAddProjectDialog()" (click)="openAddProjectDialog()"
@ -94,6 +87,10 @@
(toggleSort)="sortingComponent.toggleSort($event)" (toggleSort)="sortingComponent.toggleSort($event)"
></redaction-table-col-name> ></redaction-table-col-name>
<redaction-table-col-name
label="project-listing.table-col-names.needs-work"
></redaction-table-col-name>
<redaction-table-col-name <redaction-table-col-name
label="project-listing.table-col-names.owner" label="project-listing.table-col-names.owner"
></redaction-table-col-name> ></redaction-table-col-name>
@ -152,6 +149,12 @@
</div> </div>
</div> </div>
</div> </div>
<div>
<redaction-needs-work-badge
[needsWorkInput]="pw"
[displayed]="true"
></redaction-needs-work-badge>
</div>
<div> <div>
<redaction-initials-avatar <redaction-initials-avatar
[userId]="pw.project.ownerId" [userId]="pw.project.ownerId"

View File

@ -11,10 +11,10 @@
width: calc(100vw - #{$right-container-width} - 90px); width: calc(100vw - #{$right-container-width} - 90px);
.grid-container { .grid-container {
grid-template-columns: 2fr 1fr auto; grid-template-columns: 2fr 1fr 1fr auto;
&.bulk-select { &.bulk-select {
grid-template-columns: auto 2fr 1fr auto; grid-template-columns: auto 2fr 1fr 1fr auto;
} }
} }

View File

@ -10,11 +10,13 @@ import * as moment from 'moment';
import { SortingComponent, SortingOption } from '../../components/sorting/sorting.component'; import { SortingComponent, SortingOption } from '../../components/sorting/sorting.component';
import { import {
addedDateChecker, addedDateChecker,
annotationFilterChecker,
dueDateChecker, dueDateChecker,
getFilteredEntities, getFilteredEntities,
projectMemberChecker, projectMemberChecker,
projectStatusChecker projectStatusChecker
} from '../../common/filter/utils/filter-utils'; } from '../../common/filter/utils/filter-utils';
import { TranslateService } from '@ngx-translate/core';
@Component({ @Component({
selector: 'redaction-project-listing-screen', selector: 'redaction-project-listing-screen',
@ -32,8 +34,7 @@ export class ProjectListingScreenComponent implements OnInit {
public statusFilters: FilterModel[]; public statusFilters: FilterModel[];
public dueDateFilters: FilterModel[]; public dueDateFilters: FilterModel[];
public peopleFilters: FilterModel[]; public peopleFilters: FilterModel[];
public addedDateFilters: FilterModel[]; public needsWorkFilters: FilterModel[];
public displayedProjects: ProjectWrapper[] = []; public displayedProjects: ProjectWrapper[] = [];
@ViewChild('sortingComponent', { static: true }) public sortingComponent: SortingComponent; @ViewChild('sortingComponent', { static: true }) public sortingComponent: SortingComponent;
@ -43,7 +44,8 @@ export class ProjectListingScreenComponent implements OnInit {
public readonly appStateService: AppStateService, public readonly appStateService: AppStateService,
public readonly userService: UserService, public readonly userService: UserService,
private readonly _changeDetectorRef: ChangeDetectorRef, private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _dialogService: DialogService private readonly _dialogService: DialogService,
private readonly _translateService: TranslateService
) {} ) {}
public ngOnInit(): void { public ngOnInit(): void {
@ -158,7 +160,7 @@ export class ProjectListingScreenComponent implements OnInit {
const allDistinctFileStatus = new Set<string>(); const allDistinctFileStatus = new Set<string>();
const allDistinctPeople = new Set<string>(); const allDistinctPeople = new Set<string>();
const allDistinctDueDates = new Set<string>(); const allDistinctDueDates = new Set<string>();
const allDistinctAddedDates = new Set<string>(); const allDistinctNeedsWork = new Set<string>();
this.appStateService.allProjects.forEach((entry) => { this.appStateService.allProjects.forEach((entry) => {
// all people // all people
entry.project.memberIds.forEach((memberId) => allDistinctPeople.add(memberId)); entry.project.memberIds.forEach((memberId) => allDistinctPeople.add(memberId));
@ -166,12 +168,17 @@ export class ProjectListingScreenComponent implements OnInit {
if (entry.dueDate) { if (entry.dueDate) {
allDistinctDueDates.add(moment(entry.dueDate).format('DD/MM/YYYY')); allDistinctDueDates.add(moment(entry.dueDate).format('DD/MM/YYYY'));
} }
// added date
allDistinctAddedDates.add(moment(entry.projectDate).format('DD/MM/YYYY'));
// file statuses // file statuses
entry.files.forEach((file) => { entry.files.forEach((file) => {
allDistinctFileStatus.add(file.status); allDistinctFileStatus.add(file.status);
}); });
// Needs work
entry.files.forEach((file) => {
if (file.hasHints) allDistinctNeedsWork.add('hints');
if (file.hasRedactions) allDistinctNeedsWork.add('redactions');
if (file.hasRequests) allDistinctNeedsWork.add('requests');
});
}); });
this.statusFilters = []; this.statusFilters = [];
@ -198,11 +205,11 @@ export class ProjectListingScreenComponent implements OnInit {
}); });
}); });
this.addedDateFilters = []; this.needsWorkFilters = [];
allDistinctAddedDates.forEach((date) => { allDistinctNeedsWork.forEach((type) => {
this.addedDateFilters.push({ this.needsWorkFilters.push({
key: date, key: type,
label: date label: this._translateService.instant('filter.' + type)
}); });
}); });
} }
@ -216,7 +223,7 @@ export class ProjectListingScreenComponent implements OnInit {
{ values: this.statusFilters, checker: projectStatusChecker }, { values: this.statusFilters, checker: projectStatusChecker },
{ values: this.peopleFilters, checker: projectMemberChecker }, { values: this.peopleFilters, checker: projectMemberChecker },
{ values: this.dueDateFilters, checker: dueDateChecker }, { values: this.dueDateFilters, checker: dueDateChecker },
{ values: this.addedDateFilters, checker: addedDateChecker } { values: this.needsWorkFilters, checker: annotationFilterChecker, matchAll: true }
]; ];
this.displayedProjects = getFilteredEntities(this.appStateService.allProjects, filters); this.displayedProjects = getFilteredEntities(this.appStateService.allProjects, filters);

View File

@ -27,16 +27,6 @@
[hasArrow]="false" [hasArrow]="false"
[icon]="'red:user'" [icon]="'red:user'"
></redaction-filter> ></redaction-filter>
<!-- <button mat-button translate="filters.due-date">-->
<!-- <mat-icon svgIcon="red:lightning"></mat-icon>-->
<!-- </button>-->
<redaction-filter
(filtersChanged)="filtersChanged()"
[filterLabel]="'filters.created-on'"
[filters]="addedDateFilters"
[hasArrow]="false"
[icon]="'red:calendar'"
></redaction-filter>
<redaction-filter <redaction-filter
(filtersChanged)="filtersChanged()" (filtersChanged)="filtersChanged()"
[filterLabel]="'filters.needs-work'" [filterLabel]="'filters.needs-work'"
@ -44,12 +34,6 @@
[hasArrow]="false" [hasArrow]="false"
[icon]="'red:needs-work'" [icon]="'red:needs-work'"
></redaction-filter> ></redaction-filter>
<!-- <button mat-button translate="filters.project">-->
<!-- <mat-icon svgIcon="red:folder"></mat-icon>-->
<!-- </button>-->
<!-- <button mat-button translate="filters.document">-->
<!-- <mat-icon svgIcon="red:document"></mat-icon>-->
<!-- </button>-->
</div> </div>
<div> <div>
@ -210,21 +194,12 @@
</div> </div>
</div> </div>
<div *ngIf="!isError(fileStatus)" class="needs-work"> <div>
<redaction-annotation-icon <redaction-needs-work-badge
*ngIf="fileStatus.hasRedactions" [needsWorkInput]="fileStatus"
[typeValue]="appStateService.getDictionaryTypeValue('redaction')" [displayed]="!isError(fileStatus)"
></redaction-annotation-icon> ></redaction-needs-work-badge>
<redaction-annotation-icon
*ngIf="fileStatus.hasHints"
[typeValue]="appStateService.getDictionaryTypeValue('hint')"
></redaction-annotation-icon>
<redaction-annotation-icon
*ngIf="fileStatus.hasRequests"
[typeValue]="appStateService.getDictionaryTypeValue('request')"
></redaction-annotation-icon>
</div> </div>
<div *ngIf="!isError(fileStatus)" class="assigned-to"> <div *ngIf="!isError(fileStatus)" class="assigned-to">
<redaction-initials-avatar <redaction-initials-avatar
[userId]="fileStatus.currentReviewer" [userId]="fileStatus.currentReviewer"

View File

@ -37,14 +37,6 @@
max-width: 25vw; max-width: 25vw;
} }
.needs-work {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
gap: 4px;
}
.pages { .pages {
display: flex; display: flex;
flex-direction: row; flex-direction: row;

View File

@ -40,7 +40,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
public statusFilters: FilterModel[]; public statusFilters: FilterModel[];
public peopleFilters: FilterModel[]; public peopleFilters: FilterModel[];
public addedDateFilters: FilterModel[]; // public addedDateFilters: FilterModel[];
public needsWorkFilters: FilterModel[]; public needsWorkFilters: FilterModel[];
public displayedFiles: FileStatusWrapper[] = []; public displayedFiles: FileStatusWrapper[] = [];
@ -305,14 +305,6 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
}); });
}); });
this.addedDateFilters = [];
allDistinctAddedDates.forEach((date) => {
this.addedDateFilters.push({
key: date,
label: date
});
});
this.needsWorkFilters = []; this.needsWorkFilters = [];
allDistinctNeedsWork.forEach((type) => { allDistinctNeedsWork.forEach((type) => {
this.needsWorkFilters.push({ this.needsWorkFilters.push({
@ -330,7 +322,6 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
const filters = [ const filters = [
{ values: this.statusFilters, checker: keyChecker('status') }, { values: this.statusFilters, checker: keyChecker('status') },
{ values: this.peopleFilters, checker: keyChecker('currentReviewer') }, { values: this.peopleFilters, checker: keyChecker('currentReviewer') },
{ values: this.addedDateFilters, checker: fileAddedFilterChecker },
{ values: this.needsWorkFilters, checker: annotationFilterChecker, matchAll: true } { values: this.needsWorkFilters, checker: annotationFilterChecker, matchAll: true }
]; ];
this.displayedFiles = getFilteredEntities( this.displayedFiles = getFilteredEntities(

View File

@ -1,7 +1,6 @@
import { EventEmitter, Injectable } from '@angular/core'; import { EventEmitter, Injectable } from '@angular/core';
import { import {
DictionaryControllerService, DictionaryControllerService,
FileStatus,
FileUploadControllerService, FileUploadControllerService,
Project, Project,
ProjectControllerService, ProjectControllerService,
@ -9,7 +8,6 @@ import {
StatusControllerService, StatusControllerService,
TypeValue, TypeValue,
VersionsControllerService, VersionsControllerService,
ViewedPages,
ViewedPagesControllerService ViewedPagesControllerService
} from '@redaction/red-ui-http'; } from '@redaction/red-ui-http';
import { NotificationService, NotificationType } from '../notification/notification.service'; import { NotificationService, NotificationType } from '../notification/notification.service';
@ -38,11 +36,24 @@ export interface AppState {
export class ProjectWrapper { export class ProjectWrapper {
totalNumberOfPages?: number; totalNumberOfPages?: number;
hasStatus(status: string) { hasHints?: boolean;
return this.files.find((f) => f.status === status); hasRedactions?: boolean;
hasRequests?: boolean;
private _files: FileStatusWrapper[];
constructor(public project: Project, files: FileStatusWrapper[]) {
this._files = files ? files : [];
this._recomputeFileStatus();
} }
constructor(public project: Project, public files: FileStatusWrapper[]) {} set files(files: FileStatusWrapper[]) {
this._files = files ? files : [];
this._recomputeFileStatus();
}
get files() {
return this._files;
}
get projectDate() { get projectDate() {
return this.project.date; return this.project.date;
@ -52,6 +63,14 @@ export class ProjectWrapper {
return this.project.dueDate; return this.project.dueDate;
} }
get hasFiles() {
return this._files.length > 0;
}
hasStatus(status: string) {
return this._files.find((f) => f.status === status);
}
hasMember(key: string) { hasMember(key: string) {
return this.project.memberIds.indexOf(key) >= 0; return this.project.memberIds.indexOf(key) >= 0;
} }
@ -64,8 +83,15 @@ export class ProjectWrapper {
return moment(this.projectDate).format('DD/MM/YYYY') === key; return moment(this.projectDate).format('DD/MM/YYYY') === key;
} }
get hasFiles() { private _recomputeFileStatus() {
return this.files?.length > 0; this.hasHints = false;
this.hasRedactions = false;
this.hasRequests = false;
this._files.forEach((f) => {
this.hasHints = this.hasHints || f.hasHints;
this.hasRedactions = this.hasRedactions || f.hasRedactions;
this.hasRequests = this.hasRequests || f.hasRequests;
});
} }
} }

View File

@ -90,7 +90,7 @@
"created-on": "Created On", "created-on": "Created On",
"project": "Project", "project": "Project",
"document": "Document", "document": "Document",
"needs-work": "Needs Work" "needs-work": "Analysed"
}, },
"project-listing": { "project-listing": {
"report": { "report": {
@ -109,6 +109,7 @@
}, },
"table-col-names": { "table-col-names": {
"name": "Document", "name": "Document",
"needs-work": "Analysed",
"owner": "Owner", "owner": "Owner",
"status": "Status" "status": "Status"
}, },