move chart computations to dossier listing details component

This commit is contained in:
Dan Percic 2021-11-12 18:09:06 +02:00
parent 9bd98a0c46
commit 9ba4120aa0
9 changed files with 77 additions and 88 deletions

View File

@ -41,7 +41,7 @@
<ng-container *ngIf="dossier.stats$ | async as stats">
<div *ngIf="stats.hasFiles" class="mt-24">
<redaction-simple-doughnut-chart
[config]="calculateChartConfig(dossier)"
[config]="calculateChartConfig(stats.fileCountPerWorkflowStatus)"
[radius]="63"
[strokeWidth]="15"
[subtitle]="'dossier-overview.dossier-details.charts.documents-in-dossier' | translate"

View File

@ -1,13 +1,12 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
import { AppStateService } from '@state/app-state.service';
import { groupBy } from '@utils/index';
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
import { TranslateChartService } from '@services/translate-chart.service';
import { UserService } from '@services/user.service';
import { FilterService, Toaster } from '@iqser/common-ui';
import { fileStatusTranslations } from '../../../../translations/file-status-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { Dossier, DossierAttributeWithValue, IDossierRequest, StatusSorter, User } from '@red/domain';
import { Dossier, DossierAttributeWithValue, FileCountPerWorkflowStatus, IDossierRequest, StatusSorter, User } from '@red/domain';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs';
@ -44,20 +43,15 @@ export class DossierDetailsComponent {
) {
this.dossierId = activatedRoute.snapshot.paramMap.get('dossierId');
this.dossier$ = this.dossiersService.getEntityChanged$(this.dossierId);
// this.documentsChartData$ = this.dossier$.pipe(
// switchMap(dossier => dossier.stats$),
// map(stats => this._calculateChartConfig(stats.fileCountPerWorkflowStatus)),
// );
}
get managers() {
return this._userService.managerUsers;
return this._userService.managerUsers.map(manager => manager.id);
}
calculateChartConfig(dossier: Dossier): DoughnutChartConfig[] {
const groups = groupBy(dossier?.files, 'status');
const documentsChartData: DoughnutChartConfig[] = Object.keys(groups).map(status => ({
value: groups[status].length,
calculateChartConfig(filesCount: FileCountPerWorkflowStatus): DoughnutChartConfig[] {
const documentsChartData: DoughnutChartConfig[] = Object.keys(filesCount).map(status => ({
value: filesCount[status],
color: status,
label: fileStatusTranslations[status],
key: status,

View File

@ -1,6 +1,7 @@
<div>
<redaction-simple-doughnut-chart
[config]="dossiersChartData"
*ngIf="dossiersChartData$ | async as config"
[config]="config"
[radius]="80"
[strokeWidth]="15"
[subtitle]="'dossier-listing.stats.charts.dossiers' | translate"
@ -24,9 +25,11 @@
</div>
</div>
</div>
<div>
<redaction-simple-doughnut-chart
[config]="documentsChartData"
*ngIf="documentsChartData$ | async as config"
[config]="config"
[radius]="80"
[strokeWidth]="15"
[subtitle]="'dossier-listing.stats.charts.total-documents' | translate"

View File

@ -1,7 +1,13 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
import { FilterService } from '@iqser/common-ui';
import { FilterService, mapEach } from '@iqser/common-ui';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { combineLatest, Observable } from 'rxjs';
import { Dossier, DossierStats, DossierStatuses, FileCountPerWorkflowStatus, StatusSorter } from '@red/domain';
import { fileStatusTranslations } from '../../../../translations/file-status-translations';
import { TranslateChartService } from '@services/translate-chart.service';
import { filter, map, switchMap } from 'rxjs/operators';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@Component({
selector: 'redaction-dossiers-listing-details',
@ -10,8 +16,53 @@ import { DossiersService } from '@services/entity-services/dossiers.service';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DossiersListingDetailsComponent {
@Input() dossiersChartData: DoughnutChartConfig[];
@Input() documentsChartData: DoughnutChartConfig[];
readonly documentsChartData$: Observable<DoughnutChartConfig[]>;
readonly dossiersChartData$: Observable<DoughnutChartConfig[]>;
constructor(readonly filterService: FilterService, readonly dossiersService: DossiersService) {}
constructor(
readonly filterService: FilterService,
readonly dossiersService: DossiersService,
private readonly _translateChartService: TranslateChartService,
) {
this.documentsChartData$ = this.dossiersService.all$.pipe(
mapEach(dossier => dossier.stats$),
switchMap(stats$ => combineLatest(stats$)),
filter(stats => !stats.some(s => s === undefined)),
map(stats => this._toChartData(stats)),
);
this.dossiersChartData$ = this.dossiersService.all$.pipe(map(dossiers => this._toDossierChartData(dossiers)));
}
private _toDossierChartData(dossiers: Dossier[]): DoughnutChartConfig[] {
const activeDossiersCount = dossiers.filter(p => p.status === DossierStatuses.ACTIVE).length;
const inactiveDossiersCount = (dossiers.length = activeDossiersCount);
return [
{ value: activeDossiersCount, color: 'ACTIVE', label: _('active') },
{ value: inactiveDossiersCount, color: 'DELETED', label: _('archived') },
];
}
private _toChartData(stats: DossierStats[]) {
const chartData: FileCountPerWorkflowStatus = {};
stats.forEach(stat => {
const statuses: FileCountPerWorkflowStatus = stat.fileCountPerWorkflowStatus;
Object.keys(statuses).forEach(status => {
chartData[status] = chartData[status] ? (chartData[status] as number) + (statuses[status] as number) : statuses[status];
});
});
const documentsChartData = Object.keys(chartData).map(
status =>
({
value: chartData[status],
color: status,
label: fileStatusTranslations[status],
key: status,
} as DoughnutChartConfig),
);
documentsChartData.sort((a, b) => StatusSorter.byStatus(a.key, b.key));
return this._translateChartService.translateStatus(documentsChartData);
}
}

View File

@ -18,11 +18,7 @@
</div>
<div class="right-container" iqserHasScrollbar>
<redaction-dossiers-listing-details
*ngIf="(entitiesService.noData$ | async) === false"
[documentsChartData]="documentsChartData"
[dossiersChartData]="dossiersChartData"
></redaction-dossiers-listing-details>
<redaction-dossiers-listing-details *ngIf="(entitiesService.noData$ | async) === false"></redaction-dossiers-listing-details>
</div>
</div>
</section>
@ -32,5 +28,5 @@
</ng-template>
<ng-template #tableItemTemplate let-dossier="entity">
<redaction-table-item (calculateData)="calculateData()" [dossier]="dossier"></redaction-table-item>
<redaction-table-item (calculateData)="computeAllFilters()" [dossier]="dossier"></redaction-table-item>
</ng-template>

View File

@ -10,18 +10,15 @@ import {
ViewChild,
} from '@angular/core';
import { AppStateService } from '@state/app-state.service';
import { Dossier, DossierStatuses, StatusSorter } from '@red/domain';
import { Dossier } from '@red/domain';
import { UserService } from '@services/user.service';
import { PermissionsService } from '@services/permissions.service';
import { TranslateChartService } from '@services/translate-chart.service';
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
import { timer } from 'rxjs';
import { tap } from 'rxjs/operators';
import { Router } from '@angular/router';
import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
import { groupBy } from '@utils/index';
import { DefaultListingServicesTmp, EntitiesService, ListingComponent, OnAttach, OnDetach, TableComponent } from '@iqser/common-ui';
import { fileStatusTranslations } from '../../../translations/file-status-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { ConfigService } from '../config.service';
import { DossiersService } from '@services/entity-services/dossiers.service';
@ -44,8 +41,6 @@ export class DossiersListingScreenComponent
readonly tableColumnConfigs = this._configService.tableConfig;
readonly tableHeaderLabel = _('dossier-listing.table-header.title');
readonly buttonConfigs = this._configService.buttonsConfig(() => this.openAddDossierDialog());
dossiersChartData: DoughnutChartConfig[] = [];
documentsChartData: DoughnutChartConfig[] = [];
private _lastScrolledIndex: number;
@ViewChild('needsWorkFilterTemplate', {
read: TemplateRef,
@ -66,23 +61,14 @@ export class DossiersListingScreenComponent
private readonly _configService: ConfigService,
) {
super(_injector);
this._appStateService.reset();
}
private get _activeDossiersCount(): number {
return this.entitiesService.all.filter(p => p.status === DossierStatuses.ACTIVE).length;
}
private get _inactiveDossiersCount(): number {
return this.entitiesService.all.length - this._activeDossiersCount;
}
ngOnInit(): void {
this.calculateData();
this.computeAllFilters();
this.addSubscription = timer(0, 20000).subscribe(async () => {
await this._appStateService.loadAllDossiers();
this.calculateData();
this.computeAllFilters();
});
}
@ -115,29 +101,7 @@ export class DossiersListingScreenComponent
});
}
calculateData(): void {
this._computeAllFilters();
this.dossiersChartData = [
{ value: this._activeDossiersCount, color: 'ACTIVE', label: _('active') },
{ value: this._inactiveDossiersCount, color: 'DELETED', label: _('archived') },
];
const groups = groupBy(this._dossiersService.allFiles, 'status');
this.documentsChartData = [];
for (const status of Object.keys(groups)) {
this.documentsChartData.push({
value: groups[status].length,
color: status,
label: fileStatusTranslations[status],
key: status,
});
}
this.documentsChartData.sort((a, b) => StatusSorter.byStatus(a.key, b.key));
this.documentsChartData = this._translateChartService.translateStatus(this.documentsChartData);
}
private _computeAllFilters() {
computeAllFilters() {
const filterGroups = this._configService.filterGroups(this.entitiesService.all, this._needsWorkFilterTemplate);
this.filterService.addFilterGroups(filterGroups);
}

View File

@ -109,7 +109,7 @@ export class DossiersService extends EntitiesService<Dossier, IDossier> {
const stats$ = dossier$.pipe(switchMap(updatedDossier => this._dossierStatsService.getFor([updatedDossier.dossierId])));
return combineLatest([dossier$, stats$]).pipe(
map(([updatedDossier, stats]) => new Dossier(updatedDossier, stats[0], this.find(updatedDossier.dossierId)?.files ?? [])),
map(([updatedDossier, stats]) => new Dossier(updatedDossier, stats[0], [])),
tap(newDossier => this.replace(newDossier)),
catchError(showToast),
);

View File

@ -120,7 +120,7 @@ export class AppStateService {
return data ? data : this._dictionaryData[dossierTemplateId]['default'];
}
async loadAllDossiers(emitEvents = true) {
async loadAllDossiers() {
const dossiers = await this._dossiersService.get().toPromise();
if (!dossiers) {
return;
@ -133,17 +133,9 @@ export class AppStateService {
const oldDossier = this._dossiersService.find(p.dossierId);
const type = oldDossier?.type ?? (await this._getDictionaryFor(p));
const stats = dossierStats.find(s => s.dossierId === p.dossierId);
return new Dossier(p, stats, oldDossier?.files ?? [], type);
this._dossiersService.replace(new Dossier(p, stats, [], type));
});
const mappedDossiers = await Promise.all(mappedDossiers$);
const fileData = await this._filesService.getFor(mappedDossiers.map(p => p.id)).toPromise();
for (const dossierId of Object.keys(fileData)) {
const dossier = mappedDossiers.find(p => p.id === dossierId);
if (dossier) {
this._processFiles(dossier, fileData[dossierId], emitEvents);
}
}
return Promise.all(mappedDossiers$);
}
async reloadActiveFile() {
@ -160,10 +152,6 @@ export class AppStateService {
this._userService.getNameForId(iFile.currentReviewer),
this._fileAttributesService.getFileAttributeConfig(activeDossier.dossierTemplateId),
);
const files = activeDossier.files.filter(file => file.fileId !== activeFile.fileId);
files.push(activeFile);
const newDossier = new Dossier(activeDossier, activeDossier.stats, files, activeDossier.type);
this._dossiersService.replace(newDossier);
if (activeFile.lastProcessed !== oldProcessedDate) {
this.fileReanalysed$.next(activeFile);
@ -514,9 +502,6 @@ export class AppStateService {
}
}
const newDossier = new Dossier(dossier, dossier.stats, newFiles, dossier.type);
this._dossiersService.replace(newDossier);
return newFiles;
}
}

View File

@ -68,10 +68,6 @@ export class Dossier implements IDossier, IListable {
return this._stats$.getValue();
}
hasStatus(status: string): boolean {
return !!this.files.find(f => f.status === status);
}
hasMember(memberId: string): boolean {
return !!this.memberIds && this.memberIds.indexOf(memberId) >= 0;
}