remove dossier stats from dossier

This commit is contained in:
Dan Percic 2021-11-14 14:42:09 +02:00
parent 4f8fa98e65
commit 9ad4e2400a
21 changed files with 142 additions and 77 deletions

View File

@ -1,4 +1,4 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { UserService } from '@services/user.service'; import { UserService } from '@services/user.service';
import { Toaster } from '@iqser/common-ui'; import { Toaster } from '@iqser/common-ui';
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@ -9,6 +9,7 @@ import { Dossier, IDossier, IDossierRequest } from '@red/domain';
selector: 'redaction-team-members-manager', selector: 'redaction-team-members-manager',
templateUrl: './team-members-manager.component.html', templateUrl: './team-members-manager.component.html',
styleUrls: ['./team-members-manager.component.scss'], styleUrls: ['./team-members-manager.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class TeamMembersManagerComponent implements OnInit { export class TeamMembersManagerComponent implements OnInit {
teamForm: FormGroup; teamForm: FormGroup;

View File

@ -12,6 +12,7 @@ import { ConfirmationDialogInput, IconButtonTypes, TitleColors, Toaster } from '
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DossiersService } from '@services/entity-services/dossiers.service'; import { DossiersService } from '@services/entity-services/dossiers.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service'; import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
@Component({ @Component({
selector: 'redaction-edit-dossier-general-info', selector: 'redaction-edit-dossier-general-info',
@ -32,6 +33,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
readonly permissionsService: PermissionsService, readonly permissionsService: PermissionsService,
private readonly _dossierTemplatesService: DossierTemplatesService, private readonly _dossierTemplatesService: DossierTemplatesService,
private readonly _dossiersService: DossiersService, private readonly _dossiersService: DossiersService,
private readonly _dossierStatsService: DossierStatsService,
private readonly _formBuilder: FormBuilder, private readonly _formBuilder: FormBuilder,
private readonly _dialogService: DossiersDialogService, private readonly _dialogService: DossiersDialogService,
private readonly _router: Router, private readonly _router: Router,
@ -71,7 +73,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
dossierTemplateId: [ dossierTemplateId: [
{ {
value: this.dossier.dossierTemplateId, value: this.dossier.dossierTemplateId,
disabled: this.dossier.stats.hasFiles, disabled: this._dossierStatsService.get(this.dossier.dossierId).hasFiles,
}, },
Validators.required, Validators.required,
], ],

View File

@ -1,4 +1,4 @@
import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import { Dossier } from '@red/domain'; import { Dossier } from '@red/domain';
import { EditDossierSectionInterface } from '../edit-dossier-section.interface'; import { EditDossierSectionInterface } from '../edit-dossier-section.interface';
import { TeamMembersManagerComponent } from '../../../components/team-members-manager/team-members-manager.component'; import { TeamMembersManagerComponent } from '../../../components/team-members-manager/team-members-manager.component';
@ -8,6 +8,7 @@ import { UserService } from '@services/user.service';
selector: 'redaction-edit-dossier-team-members', selector: 'redaction-edit-dossier-team-members',
templateUrl: './edit-dossier-team-members.component.html', templateUrl: './edit-dossier-team-members.component.html',
styleUrls: ['./edit-dossier-team-members.component.scss'], styleUrls: ['./edit-dossier-team-members.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class EditDossierTeamMembersComponent implements EditDossierSectionInterface { export class EditDossierTeamMembersComponent implements EditDossierSectionInterface {
readonly currentUser = this._userService.currentUser; readonly currentUser = this._userService.currentUser;

View File

@ -1,4 +1,4 @@
<ng-container *ngIf="dossier.stats$ | async as stats"> <ng-container *ngIf="dossierStats$ | async as stats">
<div> <div>
<mat-icon svgIcon="iqser:document"></mat-icon> <mat-icon svgIcon="iqser:document"></mat-icon>
<span>{{ 'dossier-overview.dossier-details.stats.documents' | translate: { count: stats.numberOfFiles } }}</span> <span>{{ 'dossier-overview.dossier-details.stats.documents' | translate: { count: stats.numberOfFiles } }}</span>

View File

@ -1,11 +1,12 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Dossier, DossierAttributeWithValue, DossierTemplate } from '@red/domain'; import { Dossier, DossierAttributeWithValue, DossierStats, DossierTemplate } from '@red/domain';
import { DossiersDialogService } from '../../../../services/dossiers-dialog.service'; import { DossiersDialogService } from '../../../../services/dossiers-dialog.service';
import { DossiersService } from '@services/entity-services/dossiers.service'; import { DossiersService } from '@services/entity-services/dossiers.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service'; import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { FilesService } from '@services/entity-services/files.service'; import { FilesService } from '@services/entity-services/files.service';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { distinctUntilChanged, map } from 'rxjs/operators'; import { distinctUntilChanged, map } from 'rxjs/operators';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
@Component({ @Component({
selector: 'redaction-dossier-details-stats', selector: 'redaction-dossier-details-stats',
@ -15,6 +16,7 @@ import { distinctUntilChanged, map } from 'rxjs/operators';
export class DossierDetailsStatsComponent implements OnInit { export class DossierDetailsStatsComponent implements OnInit {
attributesExpanded = false; attributesExpanded = false;
deletedFilesCount$: Observable<number>; deletedFilesCount$: Observable<number>;
dossierStats$: Observable<DossierStats>;
@Input() @Input()
dossierAttributes: DossierAttributeWithValue[]; dossierAttributes: DossierAttributeWithValue[];
@Input() @Input()
@ -26,10 +28,12 @@ export class DossierDetailsStatsComponent implements OnInit {
private readonly _dossierTemplatesService: DossierTemplatesService, private readonly _dossierTemplatesService: DossierTemplatesService,
private readonly _dialogService: DossiersDialogService, private readonly _dialogService: DossiersDialogService,
private readonly _filesService: FilesService, private readonly _filesService: FilesService,
private readonly _dossierStatsService: DossierStatsService,
readonly dossiersService: DossiersService, readonly dossiersService: DossiersService,
) {} ) {}
ngOnInit() { ngOnInit() {
this.dossierStats$ = this._dossierStatsService.watch$(this.dossier.dossierId);
this.deletedFilesCount$ = this._filesService.getDeletedFilesFor(this.dossier.id).pipe( this.deletedFilesCount$ = this._filesService.getDeletedFilesFor(this.dossier.id).pipe(
map(files => files.length), map(files => files.length),
distinctUntilChanged(), distinctUntilChanged(),

View File

@ -38,7 +38,7 @@
></redaction-team-members> ></redaction-team-members>
</div> </div>
<ng-container *ngIf="dossier.stats$ | async as stats"> <ng-container *ngIf="dossierStats$ | async as stats">
<div *ngIf="stats.hasFiles" class="mt-24"> <div *ngIf="stats.hasFiles" class="mt-24">
<redaction-simple-doughnut-chart <redaction-simple-doughnut-chart
[config]="calculateChartConfig(stats.fileCountPerWorkflowStatus)" [config]="calculateChartConfig(stats.fileCountPerWorkflowStatus)"

View File

@ -3,13 +3,23 @@ import { AppStateService } from '@state/app-state.service';
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component'; import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
import { TranslateChartService } from '@services/translate-chart.service'; import { TranslateChartService } from '@services/translate-chart.service';
import { UserService } from '@services/user.service'; import { UserService } from '@services/user.service';
import { FilterService, Toaster } from '@iqser/common-ui'; import { FilterService, shareLast, Toaster } from '@iqser/common-ui';
import { fileStatusTranslations } from '../../../../translations/file-status-translations'; import { fileStatusTranslations } from '../../../../translations/file-status-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { Dossier, DossierAttributeWithValue, FileCountPerWorkflowStatus, IDossierRequest, StatusSorter, User } from '@red/domain'; import {
Dossier,
DossierAttributeWithValue,
DossierStats,
FileCountPerWorkflowStatus,
IDossierRequest,
StatusSorter,
User,
} from '@red/domain';
import { DossiersService } from '@services/entity-services/dossiers.service'; import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
import { pluck, switchMap } from 'rxjs/operators';
@Component({ @Component({
selector: 'redaction-dossier-details', selector: 'redaction-dossier-details',
@ -30,6 +40,7 @@ export class DossierDetailsComponent {
readonly currentUser = this._userService.currentUser; readonly currentUser = this._userService.currentUser;
readonly dossierId: string; readonly dossierId: string;
readonly dossier$: Observable<Dossier>; readonly dossier$: Observable<Dossier>;
readonly dossierStats$: Observable<DossierStats>;
constructor( constructor(
readonly appStateService: AppStateService, readonly appStateService: AppStateService,
@ -38,11 +49,16 @@ export class DossierDetailsComponent {
readonly filterService: FilterService, readonly filterService: FilterService,
private readonly _changeDetectorRef: ChangeDetectorRef, private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _userService: UserService, private readonly _userService: UserService,
private readonly _dossierStatsService: DossierStatsService,
private readonly _toaster: Toaster, private readonly _toaster: Toaster,
activatedRoute: ActivatedRoute, activatedRoute: ActivatedRoute,
) { ) {
this.dossierId = activatedRoute.snapshot.paramMap.get('dossierId'); this.dossierId = activatedRoute.snapshot.paramMap.get('dossierId');
this.dossier$ = this.dossiersService.getEntityChanged$(this.dossierId); this.dossier$ = this.dossiersService.getEntityChanged$(this.dossierId).pipe(shareLast());
this.dossierStats$ = this.dossier$.pipe(
pluck('dossierId'),
switchMap(dossierId => this._dossierStatsService.watch$(dossierId)),
);
} }
get managers() { get managers() {

View File

@ -1,18 +1,20 @@
<div *ngIf="dossier.stats$ | async as stats" class="needs-work"> <div class="needs-work">
<redaction-annotation-icon <redaction-annotation-icon
*ngIf="stats.hasRedactionsFilePresent" *ngIf="dossierStats.hasRedactionsFilePresent"
[color]="redactionColor" [color]="redactionColor"
label="R" label="R"
type="square" type="square"
></redaction-annotation-icon> ></redaction-annotation-icon>
<redaction-annotation-icon <redaction-annotation-icon
*ngIf="stats.hasHintsNoRedactionsFilePresent" *ngIf="dossierStats.hasHintsNoRedactionsFilePresent"
[color]="hintColor" [color]="hintColor"
label="H" label="H"
type="circle" type="circle"
></redaction-annotation-icon> ></redaction-annotation-icon>
<redaction-annotation-icon <redaction-annotation-icon
*ngIf="stats.hasSuggestionsFilePresent" *ngIf="dossierStats.hasSuggestionsFilePresent"
[color]="suggestionColor" [color]="suggestionColor"
label="S" label="S"
type="rhombus" type="rhombus"

View File

@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { AppStateService } from '@state/app-state.service'; import { AppStateService } from '@state/app-state.service';
import { Dossier } from '@red/domain'; import { Dossier, DossierStats } from '@red/domain';
@Component({ @Component({
selector: 'redaction-dossier-workload-column', selector: 'redaction-dossier-workload-column',
@ -10,6 +10,7 @@ import { Dossier } from '@red/domain';
}) })
export class DossierWorkloadColumnComponent { export class DossierWorkloadColumnComponent {
@Input() dossier: Dossier; @Input() dossier: Dossier;
@Input() dossierStats: DossierStats;
constructor(private readonly _appStateService: AppStateService) {} constructor(private readonly _appStateService: AppStateService) {}

View File

@ -1,4 +1,4 @@
<iqser-status-bar *ngIf="dossier.stats$ | async as stats" [configs]="statusConfig(stats)"></iqser-status-bar> <iqser-status-bar *ngIf="dossierStats$ | async as stats" [configs]="statusConfig(stats)"></iqser-status-bar>
<div (longPress)="forceReanalysisAction($event)" class="action-buttons" redactionLongPress> <div (longPress)="forceReanalysisAction($event)" class="action-buttons" redactionLongPress>
<iqser-circle-button <iqser-circle-button

View File

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { PermissionsService } from '@services/permissions.service'; import { PermissionsService } from '@services/permissions.service';
import { CircleButtonTypes, StatusBarConfig } from '@iqser/common-ui'; import { CircleButtonTypes, StatusBarConfig } from '@iqser/common-ui';
import { UserService } from '@services/user.service'; import { UserService } from '@services/user.service';
@ -10,6 +10,8 @@ import { UserPreferenceService } from '@services/user-preference.service';
import { FilesMapService } from '@services/entity-services/files-map.service'; import { FilesMapService } from '@services/entity-services/files-map.service';
import { ReanalysisService } from '@services/reanalysis.service'; import { ReanalysisService } from '@services/reanalysis.service';
import { switchMapTo, tap } from 'rxjs/operators'; import { switchMapTo, tap } from 'rxjs/operators';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
import { Observable } from 'rxjs';
@Component({ @Component({
selector: 'redaction-dossiers-listing-actions', selector: 'redaction-dossiers-listing-actions',
@ -17,7 +19,7 @@ import { switchMapTo, tap } from 'rxjs/operators';
styleUrls: ['./dossiers-listing-actions.component.scss'], styleUrls: ['./dossiers-listing-actions.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class DossiersListingActionsComponent { export class DossiersListingActionsComponent implements OnInit {
readonly circleButtonTypes = CircleButtonTypes; readonly circleButtonTypes = CircleButtonTypes;
readonly currentUser = this._userService.currentUser; readonly currentUser = this._userService.currentUser;
@ -25,6 +27,7 @@ export class DossiersListingActionsComponent {
@Input() dossier: Dossier; @Input() dossier: Dossier;
@Output() readonly actionPerformed = new EventEmitter<Dossier | undefined>(); @Output() readonly actionPerformed = new EventEmitter<Dossier | undefined>();
dossierStats$: Observable<DossierStats>;
constructor( constructor(
readonly appStateService: AppStateService, readonly appStateService: AppStateService,
@ -33,9 +36,14 @@ export class DossiersListingActionsComponent {
readonly permissionsService: PermissionsService, readonly permissionsService: PermissionsService,
readonly filesMapService: FilesMapService, readonly filesMapService: FilesMapService,
private readonly _dialogService: DossiersDialogService, private readonly _dialogService: DossiersDialogService,
private readonly _dossierStatsService: DossierStatsService,
private readonly _userPreferenceService: UserPreferenceService, private readonly _userPreferenceService: UserPreferenceService,
) {} ) {}
ngOnInit() {
this.dossierStats$ = this._dossierStatsService.watch$(this.dossier.dossierId);
}
statusConfig(stats: DossierStats): readonly StatusBarConfig<string>[] { statusConfig(stats: DossierStats): readonly StatusBarConfig<string>[] {
return Object.keys(stats.fileCountPerWorkflowStatus) return Object.keys(stats.fileCountPerWorkflowStatus)
.sort(StatusSorter.byStatus) .sort(StatusSorter.byStatus)

View File

@ -8,6 +8,7 @@ import { fileStatusTranslations } from '../../../../translations/file-status-tra
import { TranslateChartService } from '@services/translate-chart.service'; import { TranslateChartService } from '@services/translate-chart.service';
import { filter, map, switchMap } from 'rxjs/operators'; import { filter, map, switchMap } from 'rxjs/operators';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
@Component({ @Component({
selector: 'redaction-dossiers-listing-details', selector: 'redaction-dossiers-listing-details',
@ -22,10 +23,11 @@ export class DossiersListingDetailsComponent {
constructor( constructor(
readonly filterService: FilterService, readonly filterService: FilterService,
readonly dossiersService: DossiersService, readonly dossiersService: DossiersService,
private readonly _dossierStatsMap: DossierStatsService,
private readonly _translateChartService: TranslateChartService, private readonly _translateChartService: TranslateChartService,
) { ) {
this.documentsChartData$ = this.dossiersService.all$.pipe( this.documentsChartData$ = this.dossiersService.all$.pipe(
mapEach(dossier => dossier.stats$), mapEach(dossier => _dossierStatsMap.watch$(dossier.dossierId)),
switchMap(stats$ => combineLatest(stats$)), switchMap(stats$ => combineLatest(stats$)),
filter(stats => !stats.some(s => s === undefined)), filter(stats => !stats.some(s => s === undefined)),
map(stats => this._toChartData(stats)), map(stats => this._toChartData(stats)),

View File

@ -9,15 +9,15 @@
</div> </div>
</div> </div>
<div *ngIf="dossier.stats$ | async as stats" class="small-label stats-subtitle"> <div class="small-label stats-subtitle">
<div> <div>
<mat-icon svgIcon="iqser:document"></mat-icon> <mat-icon svgIcon="iqser:document"></mat-icon>
{{ stats.numberOfFiles }} {{ dossierStats.numberOfFiles }}
</div> </div>
<div> <div>
<mat-icon svgIcon="iqser:pages"></mat-icon> <mat-icon svgIcon="iqser:pages"></mat-icon>
{{ stats.numberOfPages }} {{ dossierStats.numberOfPages }}
</div> </div>
<div> <div>

View File

@ -1,6 +1,8 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { Dossier } from '@red/domain'; import { Dossier, DossierStats } from '@red/domain';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service'; import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
import { DossiersService } from '@services/entity-services/dossiers.service';
@Component({ @Component({
selector: 'redaction-dossiers-listing-dossier-name', selector: 'redaction-dossiers-listing-dossier-name',
@ -10,8 +12,13 @@ import { DossierTemplatesService } from '@services/entity-services/dossier-templ
}) })
export class DossiersListingDossierNameComponent { export class DossiersListingDossierNameComponent {
@Input() dossier: Dossier; @Input() dossier: Dossier;
@Input() dossierStats: DossierStats;
constructor(private readonly _dossierTemplatesService: DossierTemplatesService) {} constructor(
private readonly _dossierTemplatesService: DossierTemplatesService,
private readonly _dossierStatsService: DossierStatsService,
private readonly _dossiersService: DossiersService,
) {}
getDossierTemplateNameFor(dossierTemplateId: string): string { getDossierTemplateNameFor(dossierTemplateId: string): string {
return this._dossierTemplatesService.find(dossierTemplateId).name; return this._dossierTemplatesService.find(dossierTemplateId).name;

View File

@ -1,12 +1,17 @@
<div class="cell"> <ng-container *ngIf="dossierStatsService.watch$(dossier.dossierId) | async as stats">
<redaction-dossiers-listing-dossier-name [dossier]="dossier"></redaction-dossiers-listing-dossier-name> <div class="cell">
</div> <redaction-dossiers-listing-dossier-name [dossierStats]="stats" [dossier]="dossier"></redaction-dossiers-listing-dossier-name>
<div class="cell"> </div>
<redaction-dossier-workload-column [dossier]="dossier"></redaction-dossier-workload-column>
</div> <div class="cell">
<redaction-dossier-workload-column [dossierStats]="stats" [dossier]="dossier"></redaction-dossier-workload-column>
</div>
</ng-container>
<div class="cell user-column"> <div class="cell user-column">
<redaction-initials-avatar [user]="dossier.ownerId" [withName]="true"></redaction-initials-avatar> <redaction-initials-avatar [user]="dossier.ownerId" [withName]="true"></redaction-initials-avatar>
</div> </div>
<div class="cell status-container"> <div class="cell status-container">
<redaction-dossiers-listing-actions (actionPerformed)="calculateData.emit()" [dossier]="dossier"></redaction-dossiers-listing-actions> <redaction-dossiers-listing-actions (actionPerformed)="calculateData.emit()" [dossier]="dossier"></redaction-dossiers-listing-actions>
</div> </div>

View File

@ -1,13 +1,17 @@
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { Dossier } from '@red/domain'; import { Dossier } from '@red/domain';
import { Required } from '@iqser/common-ui'; import { Required } from '@iqser/common-ui';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
@Component({ @Component({
selector: 'redaction-table-item', selector: 'redaction-table-item',
templateUrl: './table-item.component.html', templateUrl: './table-item.component.html',
styleUrls: ['./table-item.component.scss'], styleUrls: ['./table-item.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class TableItemComponent { export class TableItemComponent {
@Input() @Required() dossier!: Dossier; @Input() @Required() dossier!: Dossier;
@Output() readonly calculateData = new EventEmitter(); @Output() readonly calculateData = new EventEmitter();
constructor(readonly dossierStatsService: DossierStatsService) {}
} }

View File

@ -10,6 +10,7 @@ import { dossierMemberChecker, dossierTemplateChecker, RedactionFilterSorter } f
import { workloadTranslations } from '../../translations/workload-translations'; import { workloadTranslations } from '../../translations/workload-translations';
import { AppStateService } from '@state/app-state.service'; import { AppStateService } from '@state/app-state.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service'; import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
@Injectable() @Injectable()
export class ConfigService { export class ConfigService {
@ -19,6 +20,7 @@ export class ConfigService {
private readonly _userService: UserService, private readonly _userService: UserService,
private readonly _appStateService: AppStateService, private readonly _appStateService: AppStateService,
private readonly _dossierTemplatesService: DossierTemplatesService, private readonly _dossierTemplatesService: DossierTemplatesService,
private readonly _dossierStatsService: DossierStatsService,
) {} ) {}
get tableConfig(): TableColumnConfig<Dossier>[] { get tableConfig(): TableColumnConfig<Dossier>[] {
@ -85,23 +87,24 @@ export class ConfigService {
entities?.forEach(entry => { entities?.forEach(entry => {
entry.memberIds.forEach(f => allDistinctPeople.add(f)); entry.memberIds.forEach(f => allDistinctPeople.add(f));
allDistinctDossierTemplates.add(entry.dossierTemplateId); allDistinctDossierTemplates.add(entry.dossierTemplateId);
const stats = this._dossierStatsService.get(entry.dossierId);
if (!entry.stats) { if (!stats) {
return; return;
} }
Object.keys(entry.stats?.fileCountPerWorkflowStatus).forEach(status => allDistinctFileStatus.add(status)); Object.keys(stats?.fileCountPerWorkflowStatus).forEach(status => allDistinctFileStatus.add(status));
if (entry.stats.hasHintsNoRedactionsFilePresent) { if (stats.hasHintsNoRedactionsFilePresent) {
allDistinctNeedsWork.add('hint'); allDistinctNeedsWork.add('hint');
} }
if (entry.stats.hasRedactionsFilePresent) { if (stats.hasRedactionsFilePresent) {
allDistinctNeedsWork.add('redaction'); allDistinctNeedsWork.add('redaction');
} }
if (entry.stats.hasSuggestionsFilePresent) { if (stats.hasSuggestionsFilePresent) {
allDistinctNeedsWork.add('suggestion'); allDistinctNeedsWork.add('suggestion');
} }
if (entry.stats.hasNoFlagsFilePresent) { if (stats.hasNoFlagsFilePresent) {
allDistinctNeedsWork.add('none'); allDistinctNeedsWork.add('none');
} }
}); });
@ -119,7 +122,7 @@ export class ConfigService {
label: this._translateService.instant('filters.status'), label: this._translateService.instant('filters.status'),
icon: 'red:status', icon: 'red:status',
filters: statusFilters.sort((a, b) => StatusSorter[a.id] - StatusSorter[b.id]), filters: statusFilters.sort((a, b) => StatusSorter[a.id] - StatusSorter[b.id]),
checker: this._dossierStatusChecker, checker: (dossier: Dossier, filter: INestedFilter) => this._dossierStatusChecker(dossier, filter),
}); });
const peopleFilters = [...allDistinctPeople].map( const peopleFilters = [...allDistinctPeople].map(
@ -152,7 +155,7 @@ export class ConfigService {
icon: 'red:needs-work', icon: 'red:needs-work',
filterTemplate: needsWorkFilterTemplate, filterTemplate: needsWorkFilterTemplate,
filters: needsWorkFilters.sort((a, b) => RedactionFilterSorter[a.id] - RedactionFilterSorter[b.id]), filters: needsWorkFilters.sort((a, b) => RedactionFilterSorter[a.id] - RedactionFilterSorter[b.id]),
checker: this._annotationFilterChecker, checker: (dossier: Dossier, filter: INestedFilter) => this._annotationFilterChecker(dossier, filter),
matchAll: true, matchAll: true,
}); });
@ -199,24 +202,28 @@ export class ConfigService {
return filterGroups; return filterGroups;
} }
private _dossierStatusChecker = (dossier: Dossier, filter: INestedFilter) => dossier.stats.fileCountPerWorkflowStatus[filter.id]; private _dossierStatusChecker = (dossier: Dossier, filter: INestedFilter) => {
const stats = this._dossierStatsService.get(dossier.dossierId);
return stats?.fileCountPerWorkflowStatus[filter.id];
};
private _annotationFilterChecker = (dossier: Dossier, filter: INestedFilter) => { private _annotationFilterChecker = (dossier: Dossier, filter: INestedFilter) => {
const stats = this._dossierStatsService.get(dossier.dossierId);
switch (filter.id) { switch (filter.id) {
// case 'analysis': { // case 'analysis': {
// return stats.reanalysisRequired; // return stats.reanalysisRequired;
// } // }
case 'suggestion': { case 'suggestion': {
return dossier.stats.hasSuggestionsFilePresent; return stats.hasSuggestionsFilePresent;
} }
case 'redaction': { case 'redaction': {
return dossier.stats.hasRedactionsFilePresent; return stats.hasRedactionsFilePresent;
} }
case 'hint': { case 'hint': {
return dossier.stats.hasHintsNoRedactionsFilePresent; return stats.hasHintsNoRedactionsFilePresent;
} }
case 'none': { case 'none': {
return dossier.stats.hasNoFlagsFilePresent; return stats.hasNoFlagsFilePresent;
} }
} }
}; };

View File

@ -1,26 +1,44 @@
import { Injectable, Injector } from '@angular/core'; import { Injectable } from '@angular/core';
import { GenericService, mapEach, RequiredParam, Validate } from '@iqser/common-ui'; import { HeadersConfiguration, mapEach, RequiredParam, Validate } from '@iqser/common-ui';
import { Observable } from 'rxjs'; import { BehaviorSubject, Observable } from 'rxjs';
import { DossierStats, IDossierStats } from '@red/domain'; import { DossierStats, IDossierStats } from '@red/domain';
import { HttpClient } from '@angular/common/http';
import { tap } from 'rxjs/operators';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class DossierStatsService extends GenericService<IDossierStats> { export class DossierStatsService {
constructor(protected readonly _injector: Injector) { private readonly _map = new Map<string, BehaviorSubject<DossierStats>>();
super(_injector, 'dossier-stats');
} constructor(private readonly _http: HttpClient) {}
@Validate() @Validate()
getFor(@RequiredParam() dossierIds: string[]): Observable<DossierStats[]> { getFor(@RequiredParam() dossierIds: string[]): Observable<DossierStats[]> {
return this._post<IDossierStats[]>(dossierIds).pipe(mapEach(entity => new DossierStats(entity))); const request = this._http.post<IDossierStats[]>(`/${encodeURI('dossier-stats')}`, dossierIds, {
headers: HeadersConfiguration.getHeaders(),
observe: 'body',
});
return request.pipe(
mapEach(entity => new DossierStats(entity)),
tap(entities => entities.forEach(entity => this.set(entity))),
);
} }
// @Validate() get(key: string): DossierStats {
// loadFor(@RequiredParam() dossierId: string): Observable<DossierStats> { return this._map.get(key)?.value;
// return this._getOne<IDossierStats>([dossierId]).pipe( }
// map((entity: IDossierStats) => new DossierStats(entity)),
// tap((entity: DossierStats) => this.replace(entity)), set(stats: DossierStats): void {
// ); if (!this._map.has(stats.dossierId)) {
// } this._map.set(stats.dossierId, new BehaviorSubject<DossierStats>(stats));
} else {
this._map.get(stats.dossierId).next(stats);
}
}
watch$(key: string): Observable<DossierStats> {
return this._map.get(key)?.asObservable();
}
} }

View File

@ -94,7 +94,7 @@ export class DossiersService extends EntitiesService<Dossier, IDossier> {
const stats$ = dossier$.pipe(switchMap(updatedDossier => this._dossierStatsService.getFor([updatedDossier.dossierId]))); const stats$ = dossier$.pipe(switchMap(updatedDossier => this._dossierStatsService.getFor([updatedDossier.dossierId])));
return combineLatest([dossier$, stats$]).pipe( return combineLatest([dossier$, stats$]).pipe(
map(([updatedDossier, stats]) => new Dossier(updatedDossier, stats[0])), map(([updatedDossier]) => new Dossier(updatedDossier)),
tap(newDossier => this.replace(newDossier)), tap(newDossier => this.replace(newDossier)),
catchError(showToast), catchError(showToast),
); );
@ -132,7 +132,7 @@ export class DossiersService extends EntitiesService<Dossier, IDossier> {
entities.forEach(dossier => { entities.forEach(dossier => {
dossier.memberIds?.forEach(m => totalPeople.add(m)); dossier.memberIds?.forEach(m => totalPeople.add(m));
totalAnalyzedPages += dossier.stats.numberOfPages; totalAnalyzedPages += this._dossierStatsService.get(dossier.dossierId).numberOfPages;
}); });
return { return {
@ -142,7 +142,7 @@ export class DossiersService extends EntitiesService<Dossier, IDossier> {
} }
private _generalStats$(entities: List<Dossier>): Observable<IDossiersStats> { private _generalStats$(entities: List<Dossier>): Observable<IDossiersStats> {
const stats$ = entities.map(entity => entity.stats$); const stats$ = entities.map(entity => this._dossierStatsService.watch$(entity.dossierId));
return combineLatest(stats$).pipe( return combineLatest(stats$).pipe(
filter(stats => stats.every(s => !!s)), filter(stats => stats.every(s => !!s)),
map(() => this._computeStats(entities)), map(() => this._computeStats(entities)),

View File

@ -127,13 +127,12 @@ export class AppStateService {
} }
const dossierIds = dossiers.map(dossier => dossier.dossierId); const dossierIds = dossiers.map(dossier => dossier.dossierId);
const dossierStats = await this._dossierStatsService.getFor(dossierIds).toPromise(); await this._dossierStatsService.getFor(dossierIds).toPromise();
const mappedDossiers$ = dossiers.map(async p => { const mappedDossiers$ = dossiers.map(async p => {
const oldDossier = this._dossiersService.find(p.dossierId); const oldDossier = this._dossiersService.find(p.dossierId);
const type = oldDossier?.type ?? (await this._getDictionaryFor(p)); const type = oldDossier?.type ?? (await this._getDictionaryFor(p));
const stats = dossierStats.find(s => s.dossierId === p.dossierId); this._dossiersService.replace(new Dossier(p, type));
this._dossiersService.replace(new Dossier(p, stats, type));
}); });
return Promise.all(mappedDossiers$); return Promise.all(mappedDossiers$);
} }

View File

@ -3,8 +3,6 @@ import { IDossier } from './dossier';
import { DossierStatus } from './types'; import { DossierStatus } from './types';
import { DownloadFileType } from '../shared'; import { DownloadFileType } from '../shared';
import { IDictionary } from '../dictionaries'; import { IDictionary } from '../dictionaries';
import { BehaviorSubject, Observable } from 'rxjs';
import { DossierStats } from '../dossier-stats';
export class Dossier implements IDossier, IListable { export class Dossier implements IDossier, IListable {
readonly dossierId: string; readonly dossierId: string;
@ -25,10 +23,7 @@ export class Dossier implements IDossier, IListable {
readonly watermarkEnabled: boolean; readonly watermarkEnabled: boolean;
readonly hasReviewers: boolean; readonly hasReviewers: boolean;
readonly stats$: Observable<DossierStats>; constructor(dossier: IDossier, public type?: IDictionary) {
private readonly _stats$: BehaviorSubject<DossierStats>;
constructor(dossier: IDossier, stats: DossierStats, public type?: IDictionary) {
this.dossierId = dossier.dossierId; this.dossierId = dossier.dossierId;
this.approverIds = dossier.approverIds; this.approverIds = dossier.approverIds;
this.date = dossier.date; this.date = dossier.date;
@ -46,9 +41,6 @@ export class Dossier implements IDossier, IListable {
this.status = dossier.status; this.status = dossier.status;
this.watermarkEnabled = dossier.watermarkEnabled; this.watermarkEnabled = dossier.watermarkEnabled;
this.hasReviewers = !!this.memberIds && this.memberIds.length > 1; this.hasReviewers = !!this.memberIds && this.memberIds.length > 1;
this._stats$ = new BehaviorSubject<DossierStats>(stats);
this.stats$ = this._stats$.asObservable();
} }
get id(): string { get id(): string {
@ -63,10 +55,6 @@ export class Dossier implements IDossier, IListable {
return this.dossierName; return this.dossierName;
} }
get stats(): DossierStats {
return this._stats$.getValue();
}
hasMember(memberId: string): boolean { hasMember(memberId: string): boolean {
return !!this.memberIds && this.memberIds.indexOf(memberId) >= 0; return !!this.memberIds && this.memberIds.indexOf(memberId) >= 0;
} }