RED-3796: Use dossier template stats endpoint

This commit is contained in:
Adina Țeudan 2022-05-04 19:03:39 +03:00
parent 06c9b2bbc7
commit 30dbee52d2
64 changed files with 348 additions and 249 deletions

View File

@ -0,0 +1,14 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate } from '@angular/router';
import { firstValueFrom } from 'rxjs';
import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service';
@Injectable({ providedIn: 'root' })
export class DashboardGuard implements CanActivate {
constructor(private readonly _dashboardStatsService: DashboardStatsService) {}
async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
await firstValueFrom(this._dashboardStatsService.loadAll());
return true;
}
}

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { DOSSIER_TEMPLATE_ID } from '@utils/constants';
@Injectable({ providedIn: 'root' })

View File

@ -1,15 +1,11 @@
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { firstValueFrom } from 'rxjs';
import { DictionaryService } from '@services/entity-services/dictionary.service';
@Injectable({ providedIn: 'root' })
export class DossierTemplatesGuard implements CanActivate {
constructor(
private readonly _dossierTemplatesService: DossierTemplatesService,
private readonly _dictionaryService: DictionaryService,
) {}
constructor(private readonly _dossierTemplatesService: DossierTemplatesService) {}
async canActivate(): Promise<boolean> {
await firstValueFrom(this._dossierTemplatesService.loadAll());

View File

@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
import { DOSSIER_TEMPLATE_ID, ENTITY_TYPE } from '@utils/constants';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
@Injectable({ providedIn: 'root' })
export class EntityExistsGuard implements CanActivate {

View File

@ -6,7 +6,7 @@ import { AdminDialogService } from '../services/admin-dialog.service';
import { DictionaryService } from '@services/entity-services/dictionary.service';
import { UserService } from '@services/user.service';
import { LoadingService } from '@iqser/common-ui';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
import { map } from 'rxjs/operators';
import { PermissionsService } from '@services/permissions.service';

View File

@ -1,5 +1,5 @@
import { Component, Input } from '@angular/core';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { Observable, of } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { ActivatedRoute } from '@angular/router';

View File

@ -23,7 +23,7 @@ export class AddEditDossierStateDialogComponent extends BaseDialogComponent {
private readonly _formBuilder: FormBuilder,
private readonly _loadingService: LoadingService,
private readonly _toaster: Toaster,
private readonly _dossierStateService: DossierStatesService,
private readonly _dossierStatesService: DossierStatesService,
protected readonly _injector: Injector,
protected readonly _dialogRef: MatDialogRef<AddEditDossierStateDialogComponent>,
@Inject(MAT_DIALOG_DATA) readonly data: DialogData,
@ -45,7 +45,7 @@ export class AddEditDossierStateDialogComponent extends BaseDialogComponent {
};
this._loadingService.start();
try {
await firstValueFrom(this._dossierStateService.createOrUpdate(dossierState));
await firstValueFrom(this._dossierStatesService.createOrUpdate(dossierState));
this._toaster.success(_('add-edit-dossier-state.success'), { params: { type: this.type } });
this._dialogRef.close();
} catch (e) {}

View File

@ -3,7 +3,7 @@ import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/fo
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { applyIntervalConstraints } from '@utils/date-inputs-utils';
import { downloadTypesTranslations } from '../../../../translations/download-types-translations';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { BaseDialogComponent, LoadingService, Toaster } from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DossierTemplate, DownloadFileType, IDossierTemplate } from '@red/domain';

View File

@ -1,6 +1,6 @@
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { DossierTemplate } from '@red/domain';
import { LoadingService, Toaster } from '@iqser/common-ui';
import { firstValueFrom } from 'rxjs';

View File

@ -29,7 +29,7 @@ export class ConfirmDeleteDossierStateDialogComponent {
private readonly _formBuilder: FormBuilder,
private readonly _loadingService: LoadingService,
private readonly _toaster: Toaster,
private readonly _dossierStateService: DossierStatesService,
private readonly _dossierStatesService: DossierStatesService,
private readonly _dialogRef: MatDialogRef<ConfirmDeleteDossierStateDialogComponent>,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _archivedDossiersService: ArchivedDossiersService,
@ -55,7 +55,7 @@ export class ConfirmDeleteDossierStateDialogComponent {
async save(): Promise<void> {
this._loadingService.start();
await firstValueFrom(this._dossierStateService.deleteState(this.data.toBeDeletedState, this.replaceDossierStatusId));
await firstValueFrom(this._dossierStatesService.deleteState(this.data.toBeDeletedState, this.replaceDossierStatusId));
await firstValueFrom(
forkJoin([this._activeDossiersService.loadAll().pipe(take(1)), this._archivedDossiersService.loadAll().pipe(take(1))]),
);

View File

@ -4,7 +4,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { FileAttributeEncodingTypes, IFileAttributesConfig } from '@red/domain';
import { fileAttributeEncodingTypesTranslations } from '../../translations/file-attribute-encoding-types-translations';
import { BaseDialogComponent, Toaster } from '@iqser/common-ui';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { firstValueFrom } from 'rxjs';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { FileAttributesService } from '@services/entity-services/file-attributes.service';

View File

@ -13,7 +13,7 @@ import { defaultColorsTranslations } from '../../translations/default-colors-tra
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { UserService } from '@services/user.service';
import { DictionaryService } from '@services/entity-services/dictionary.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { firstValueFrom } from 'rxjs';
import { ActivatedRoute } from '@angular/router';
import { DOSSIER_TEMPLATE_ID } from '@utils/constants';

View File

@ -15,7 +15,7 @@ import {
import { UserService } from '@services/user.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { RouterHistoryService } from '@services/router-history.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { firstValueFrom } from 'rxjs';
@Component({

View File

@ -28,7 +28,7 @@ import { HttpStatusCode } from '@angular/common/http';
import { firstValueFrom } from 'rxjs';
import { ReportTemplateService } from '../../../../services/report-template.service';
import { ActivatedRoute } from '@angular/router';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { DOSSIER_TEMPLATE_ID } from '@utils/constants';
@Component({

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs';
import { DossierTemplate, DossierTemplateStats } from '@red/domain';

View File

@ -3,7 +3,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Justification } from '@red/domain';
import { JustificationsService } from '@services/entity-services/justifications.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { BaseDialogComponent, LoadingService } from '@iqser/common-ui';
import { firstValueFrom } from 'rxjs';

View File

@ -10,7 +10,7 @@ import {
} from '@iqser/common-ui';
import { AddEditJustificationDialogComponent } from './add-edit-justification-dialog/add-edit-justification-dialog.component';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { JustificationsService } from '@services/entity-services/justifications.service';
import { Justification } from '@red/domain';
import { firstValueFrom } from 'rxjs';

View File

@ -10,7 +10,7 @@ import {
import { removeBraces } from '@utils/functions';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { AdminDialogService } from '../../../services/admin-dialog.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { ReportTemplateService } from '@services/report-template.service';
import { BehaviorSubject, firstValueFrom } from 'rxjs';
import { ActivatedRoute } from '@angular/router';

View File

@ -18,7 +18,7 @@ import { TrashItem } from '@red/domain';
import { TrashService } from '@services/entity-services/trash.service';
import { FilesService } from '@services/entity-services/files.service';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
@Component({
templateUrl: './trash-screen.component.html',

View File

@ -3,7 +3,7 @@ import { ActivatedRoute, Router } from '@angular/router';
import { AdminDialogService } from '../../../services/admin-dialog.service';
import { CircleButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
import { UserService } from '@services/user.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { firstValueFrom } from 'rxjs';
import { DictionaryService } from '@services/entity-services/dictionary.service';
import { DOSSIER_TEMPLATE_ID } from '@utils/constants';
@ -37,6 +37,7 @@ export class DossierTemplateActionsComponent implements OnInit {
openEditDossierTemplateDialog($event: MouseEvent) {
this._dialogService.openDialog('addEditDossierTemplate', $event, this.dossierTemplateId);
}
openCloneDossierTemplateDialog($event: MouseEvent) {
this._dialogService.openDialog('cloneDossierTemplate', $event, this.dossierTemplateId);
}

View File

@ -5,7 +5,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { dossierMemberChecker, dossierTemplateChecker } from '@utils/index';
import { UserService } from '@services/user.service';
import { TranslateService } from '@ngx-translate/core';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
@Injectable()
export class ConfigService {

View File

@ -1,13 +1,32 @@
<div *ngIf="dossierTemplate$ | async as dossierTemplate" [class.empty]="empty" class="dialog">
<ng-container *ngIf="!empty">
<a [routerLink]="dossierTemplate.dossiersRouterLink" class="heading">{{ dossierTemplate.name }}</a>
<div>
<!-- <pre>{{ stats$ | async | json }}</pre>-->
<div *ngIf="stats as dossierTemplate" [class.empty]="dossierTemplate.isEmpty" class="dialog">
<ng-container *ngIf="!dossierTemplate.isEmpty; else empty">
<div class="flex-1">
<a [routerLink]="['..', dossierTemplate.dossierTemplateId]" class="heading">{{ dossierTemplate.name }}</a>
{{ dossierTemplate.id }}
</div>
<div class="flex-2">
<redaction-simple-doughnut-chart
[config]="translateChartService.translateDossierStates(dossierTemplate.dossiersChartData, dossierTemplate.id)"
[radius]="70"
[strokeWidth]="15"
[subtitle]="'dossier-template-charts.active-dossiers' | translate: { count: dossierTemplate.numberOfActiveDossiers }"
direction="row"
totalType="sum"
></redaction-simple-doughnut-chart>
</div>
<div class="flex-2">
<redaction-simple-doughnut-chart
[config]="translateChartService.translateWorkflowStatus(dossierTemplate.documentsChartData)"
[radius]="70"
[strokeWidth]="15"
[subtitle]="'dossier-template-charts.total-documents' | translate"
direction="row"
totalType="sum"
></redaction-simple-doughnut-chart>
</div>
<div>stats2</div>
</ng-container>
<div *ngIf="empty" class="empty">
<ng-template #empty>
<div class="text-muted">
<div class="heading mb-8">
{{ dossierTemplate.name }}
@ -22,5 +41,5 @@
[type]="iconButtonTypes.primary"
icon="iqser:plus"
></iqser-icon-button>
</div>
</ng-template>
</div>

View File

@ -1,6 +1,7 @@
.dialog {
flex-direction: row;
max-width: unset;
min-height: unset;
margin: 0 0 16px 0;
.heading {
@ -11,22 +12,20 @@
}
}
> div {
&.empty {
justify-content: space-between;
align-items: center;
padding: 24px;
}
&:not(.empty) > div {
padding: 24px;
display: flex;
flex-direction: column;
flex: 1;
&:not(:first-child) {
align-items: center;
justify-content: center;
border-left: 1px solid var(--iqser-separator);
}
&.empty {
flex-direction: row;
justify-content: space-between;
align-items: center;
}
}
}

View File

@ -1,49 +1,28 @@
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplate, DossierTemplateStats } from '@red/domain';
import { BehaviorSubject, Observable } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { DossierTemplateStatsService } from '@services/entity-services/dossier-template-stats.service';
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { DashboardStats } from '@red/domain';
import { IconButtonTypes } from '@iqser/common-ui';
import { DossiersDialogService } from '../../../dossier/services/dossiers-dialog.service';
import { Router } from '@angular/router';
import { DossiersDialogService } from '../../../dossier/shared/services/dossiers-dialog.service';
import { TranslateService } from '@ngx-translate/core';
import { TranslateChartService } from '@services/translate-chart.service';
@Component({
selector: 'redaction-template-stats [dossierTemplateId]',
selector: 'redaction-template-stats [stats]',
templateUrl: './template-stats.component.html',
styleUrls: ['./template-stats.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TemplateStatsComponent implements OnChanges {
export class TemplateStatsComponent {
readonly iconButtonTypes = IconButtonTypes;
@Input() dossierTemplateId: string;
readonly dossierTemplate$: Observable<DossierTemplate>;
readonly stats$: Observable<DossierTemplateStats>;
readonly #ngOnChanges$ = new BehaviorSubject<string>(undefined);
@Input() stats: DashboardStats;
constructor(
private readonly _dossierTemplatesService: DossierTemplatesService,
private readonly _dossierTemplateStatsService: DossierTemplateStatsService,
private readonly _dialogService: DossiersDialogService,
private readonly _router: Router,
) {
this.dossierTemplate$ = this.#ngOnChanges$.pipe(switchMap(id => this._dossierTemplatesService.getEntityChanged$(id)));
this.stats$ = this.#ngOnChanges$.pipe(switchMap(id => this._dossierTemplateStatsService.watch$(id)));
}
get empty(): boolean {
return false;
}
ngOnChanges() {
if (this.dossierTemplateId) {
this.#ngOnChanges$.next(this.dossierTemplateId);
}
}
private readonly _translateService: TranslateService,
readonly translateChartService: TranslateChartService,
) {}
newDossier(): void {
this._dialogService.openDialog('addDossier', null, { dossierTemplateId: this.dossierTemplateId });
this._dialogService.openDialog('addDossier', null, { dossierTemplateId: this.stats.dossierTemplateId });
}
}

View File

@ -9,8 +9,5 @@
<div class="mb-32" translate="dashboard.greeting.subtitle"></div>
<redaction-template-stats
*ngFor="let dossierTemplate of dossierTemplatesService.all$ | async"
[dossierTemplateId]="dossierTemplate.id"
></redaction-template-stats>
<redaction-template-stats *ngFor="let dossierTemplate of stats$ | async" [stats]="dossierTemplate"></redaction-template-stats>
</div>

View File

@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { UserService } from '../../../services/user.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service';
@Component({
selector: 'redaction-dashboard-screen',
@ -10,6 +10,7 @@ import { DossierTemplatesService } from '@services/entity-services/dossier-templ
})
export class DashboardScreenComponent {
readonly currentUser = this._userService.currentUser;
readonly stats$ = this._dashboardStatsService.all$;
constructor(private readonly _userService: UserService, readonly dossierTemplatesService: DossierTemplatesService) {}
constructor(private readonly _userService: UserService, private readonly _dashboardStatsService: DashboardStatsService) {}
}

View File

@ -4,10 +4,11 @@ import { DashboardScreenComponent } from './dashboard-screen/dashboard-screen.co
import { RouterModule } from '@angular/router';
import { SharedModule } from '../shared/shared.module';
import { TemplateStatsComponent } from './components/template-stats/template-stats.component';
import { DossierTemplatesGuard } from '../../guards/dossier-templates.guard';
import { CompositeRouteGuard } from '@iqser/common-ui';
import { SharedDossiersModule } from '../dossier/shared/shared-dossiers.module';
import { BreadcrumbTypes } from '@red/domain';
import { DossierTemplatesGuard } from '../../guards/dossier-templates.guard';
import { DashboardGuard } from '../../guards/dashboard-guard.service';
const routes = [
{
@ -15,8 +16,8 @@ const routes = [
component: DashboardScreenComponent,
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [DossierTemplatesGuard],
breadcrumbs: [BreadcrumbTypes.dashboard],
routeGuards: [DossierTemplatesGuard, DashboardGuard],
},
},
];

View File

@ -1,10 +1,10 @@
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
import { Dossier, DossierAttributeWithValue, DossierStats } from '@red/domain';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { FilesService } from '@services/entity-services/files.service';
import { firstValueFrom, Observable } from 'rxjs';
import { DossierStatsService } from '@services/dossiers/dossier-stats.service';
import { DossiersDialogService } from '../../../dossier/services/dossiers-dialog.service';
import { DossiersDialogService } from '../../../dossier/shared/services/dossiers-dialog.service';
@Component({
selector: 'redaction-dossier-details-stats',

View File

@ -10,7 +10,7 @@ import { ActivatedRoute } from '@angular/router';
import { firstValueFrom, Observable } from 'rxjs';
import { DossierStatsService } from '@services/dossiers/dossier-stats.service';
import { map, pluck, switchMap } from 'rxjs/operators';
import { DossiersDialogService } from '../../../dossier/services/dossiers-dialog.service';
import { DossiersDialogService } from '../../../dossier/shared/services/dossiers-dialog.service';
import { FilesService } from '@services/entity-services/files.service';
import { DOSSIER_ID } from '@utils/constants';
import { DossiersService } from '@services/dossiers/dossiers.service';
@ -80,7 +80,7 @@ export class DossierDetailsComponent {
key: status,
}));
documentsChartData.sort((a, b) => StatusSorter.byStatus(a.key, b.key));
return this.translateChartService.translateStatus(documentsChartData);
return this.translateChartService.translateLabels(documentsChartData);
}
#calculateStatusConfig(stats: DossierStats): ProgressBarConfigModel[] {

View File

@ -17,7 +17,7 @@ import { PermissionsService } from '@services/permissions.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { TranslateService } from '@ngx-translate/core';
import { UserService } from '@services/user.service';
import { DossiersDialogService } from '../dossier/services/dossiers-dialog.service';
import { DossiersDialogService } from '../dossier/shared/services/dossiers-dialog.service';
import { annotationFilterChecker, RedactionFilterSorter } from '../../utils';
import { workloadTranslations } from '../dossier/translations/workload-translations';
import { ConfigService as AppConfigService } from '@services/config.service';

View File

@ -28,7 +28,7 @@ import { PermissionsService } from '@services/permissions.service';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
import { ConfigService } from '../config.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { UserPreferenceService } from '@services/user-preference.service';
import { FilesMapService } from '@services/entity-services/files-map.service';
import { FilesService } from '@services/entity-services/files.service';

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { Dossier, File } from '@red/domain';
import { DossiersDialogService } from '../../dossier/services/dossiers-dialog.service';
import { DossiersDialogService } from '../../dossier/shared/services/dossiers-dialog.service';
import { ConfirmationDialogInput, LoadingService } from '@iqser/common-ui';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { FilesService } from '@services/entity-services/files.service';

View File

@ -5,13 +5,13 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { downloadTypesTranslations } from '../../../../translations/download-types-translations';
import { BaseDialogComponent, IconButtonTypes, LoadingService, SaveOptions } from '@iqser/common-ui';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { ReportTemplateService } from '@services/report-template.service';
import { firstValueFrom } from 'rxjs';
import { DOSSIER_TEMPLATE_ID } from '@utils/constants';
import dayjs from 'dayjs';
import { Router } from '@angular/router';
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { DossiersDialogService } from '../../shared/services/dossiers-dialog.service';
interface DialogData {
readonly dossierTemplateId?: string;

View File

@ -2,14 +2,14 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Dossier, IDossierRequest, IDossierTemplate } from '@red/domain';
import { EditDossierSaveResult, EditDossierSectionInterface } from '../edit-dossier-section.interface';
import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
import { DossiersDialogService } from '../../../shared/services/dossiers-dialog.service';
import { PermissionsService } from '@services/permissions.service';
import { Router } from '@angular/router';
import { MatDialogRef } from '@angular/material/dialog';
import { EditDossierDialogComponent } from '../edit-dossier-dialog.component';
import { ConfirmationDialogInput, ConfirmOptions, IconButtonTypes, LoadingService, TitleColors, Toaster } from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { DossierStatsService } from '@services/dossiers/dossier-stats.service';
import { firstValueFrom } from 'rxjs';
import { DOSSIER_TEMPLATE_ID } from '@utils/constants';

View File

@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, Input, OnChanges, Optional, ViewChild } from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
import { Action, ActionTypes, Dossier, File } from '@red/domain';
import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import {
CircleButtonType,
CircleButtonTypes,
@ -24,7 +24,6 @@ import { ExcludedPagesService } from '../../../../file-preview/services/excluded
import { DocumentInfoService } from '../../../../file-preview/services/document-info.service';
import { ExpandableFileActionsComponent } from '@shared/components/expandable-file-actions/expandable-file-actions.component';
import { firstValueFrom, Observable } from 'rxjs';
import { RedactionImportService } from '../../services/redaction-import.service';
import { PageRotationService } from '../../../../file-preview/services/page-rotation.service';
@Component({

View File

@ -1,10 +1,10 @@
import { Injectable } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { AddDossierDialogComponent } from '../dialogs/add-dossier-dialog/add-dossier-dialog.component';
import { EditDossierDialogComponent } from '../dialogs/edit-dossier-dialog/edit-dossier-dialog.component';
import { AssignReviewerApproverDialogComponent } from '../dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component';
import { AddDossierDialogComponent } from '../../dialogs/add-dossier-dialog/add-dossier-dialog.component';
import { EditDossierDialogComponent } from '../../dialogs/edit-dossier-dialog/edit-dossier-dialog.component';
import { AssignReviewerApproverDialogComponent } from '../../dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component';
import { ConfirmationDialogComponent, DialogConfig, DialogService, largeDialogConfig } from '@iqser/common-ui';
import { ImportRedactionsDialogComponent } from '../../file-preview/dialogs/import-redactions-dialog/import-redactions-dialog';
import { ImportRedactionsDialogComponent } from '../../../file-preview/dialogs/import-redactions-dialog/import-redactions-dialog';
type DialogType = 'confirm' | 'editDossier' | 'addDossier' | 'assignFile' | 'importRedactions';

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { UserService } from '@services/user.service';
import { Dossier, File } from '@red/domain';
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { DossiersDialogService } from './dossiers-dialog.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { FilesService } from '@services/entity-services/files.service';
import { ConfirmationDialogInput, LoadingService, Toaster } from '@iqser/common-ui';

View File

@ -4,7 +4,7 @@ import { FileAssignService } from './services/file-assign.service';
import { FileActionsComponent } from './components/file-actions/file-actions.component';
import { SharedModule } from '@shared/shared.module';
import { RedactionImportService } from './services/redaction-import.service';
import { DossiersDialogService } from '../services/dossiers-dialog.service';
import { DossiersDialogService } from './services/dossiers-dialog.service';
import { EditDossierDialogComponent } from '../dialogs/edit-dossier-dialog/edit-dossier-dialog.component';
import { AddDossierDialogComponent } from '../dialogs/add-dossier-dialog/add-dossier-dialog.component';
import { AssignReviewerApproverDialogComponent } from '../dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component';

View File

@ -3,7 +3,7 @@ import { PermissionsService } from '@services/permissions.service';
import { CircleButtonTypes, List, ScrollableParentView, ScrollableParentViews, StatusBarConfig } from '@iqser/common-ui';
import { UserService } from '@services/user.service';
import { Dossier, DossierStats, File } from '@red/domain';
import { DossiersDialogService } from '../../../dossier/services/dossiers-dialog.service';
import { DossiersDialogService } from '../../../dossier/shared/services/dossiers-dialog.service';
import { LongPressEvent } from '@shared/directives/long-press.directive';
import { UserPreferenceService } from '@services/user-preference.service';
import { FilesMapService } from '@services/entity-services/files-map.service';

View File

@ -1,25 +1,24 @@
<div>
<div *ngIf="stats$ | async as stats">
<redaction-simple-doughnut-chart
*ngIf="dossiersChartData$ | async as config"
[config]="config"
[config]="dossiersChartData$ | async"
[radius]="80"
[strokeWidth]="15"
[subtitle]="'dossier-listing.stats.charts.dossiers' | translate: { count: activeDossiersService.all.length }"
[subtitle]="'dossier-template-charts.active-dossiers' | translate: { count: stats.numberOfActiveDossiers }"
></redaction-simple-doughnut-chart>
<div *ngIf="activeDossiersService.generalStats$ | async as stats" class="dossier-stats-container">
<div class="dossier-stats-container">
<div class="dossier-stats-item">
<mat-icon svgIcon="red:needs-work"></mat-icon>
<div>
<div class="heading">{{ stats.totalAnalyzedPages | number }}</div>
<div [translateParams]="{ count: stats.totalAnalyzedPages }" [translate]="'dossier-listing.stats.analyzed-pages'"></div>
<div class="heading">{{ stats.numberOfPages | number }}</div>
<div [translateParams]="{ count: stats.numberOfPages }" [translate]="'dossier-listing.stats.analyzed-pages'"></div>
</div>
</div>
<div class="dossier-stats-item">
<mat-icon svgIcon="red:user"></mat-icon>
<div>
<div class="heading">{{ stats.totalPeople }}</div>
<div class="heading">{{ stats.numberOfPeople }}</div>
<div translate="dossier-listing.stats.total-people"></div>
</div>
</div>
@ -28,10 +27,9 @@
<div class="right-chart">
<redaction-simple-doughnut-chart
*ngIf="documentsChartData$ | async as config"
[config]="config"
[config]="documentsChartData$ | async"
[radius]="80"
[strokeWidth]="15"
[subtitle]="'dossier-listing.stats.charts.total-documents' | translate"
[subtitle]="'dossier-template-charts.total-documents' | translate"
></redaction-simple-doughnut-chart>
</div>

View File

@ -1,15 +1,12 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
import { FilterService, mapEach } from '@iqser/common-ui';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { combineLatest, Observable } from 'rxjs';
import { DossierStats, FileCountPerWorkflowStatus, StatusSorter } from '@red/domain';
import { workflowFileStatusTranslations } from '../../../../translations/file-status-translations';
import { Observable } from 'rxjs';
import { TranslateChartService } from '@services/translate-chart.service';
import { filter, map, switchMap } from 'rxjs/operators';
import { DossierStatsService } from '@services/dossiers/dossier-stats.service';
import { TranslateService } from '@ngx-translate/core';
import { DossierStatesMapService } from '@services/entity-services/dossier-states-map.service';
import { map } from 'rxjs/operators';
import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service';
import { ActivatedRoute } from '@angular/router';
import { DOSSIER_TEMPLATE_ID } from '@utils/constants';
import { DashboardStats } from '@red/domain';
@Component({
selector: 'redaction-dossiers-listing-details',
@ -18,59 +15,20 @@ import { DossierStatesMapService } from '@services/entity-services/dossier-state
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DossiersListingDetailsComponent {
readonly stats$: Observable<DashboardStats>;
readonly documentsChartData$: Observable<DoughnutChartConfig[]>;
readonly dossiersChartData$: Observable<DoughnutChartConfig[]>;
constructor(
readonly filterService: FilterService,
readonly activeDossiersService: ActiveDossiersService,
private readonly _dossierStatsMap: DossierStatsService,
private readonly _dashboardStatsService: DashboardStatsService,
private readonly _translateChartService: TranslateChartService,
private readonly _dossierStatesMapService: DossierStatesMapService,
private readonly _translateService: TranslateService,
private readonly _route: ActivatedRoute,
) {
this.documentsChartData$ = this.activeDossiersService.all$.pipe(
mapEach(dossier => _dossierStatsMap.watch$(dossier.dossierId)),
switchMap(stats$ => combineLatest(stats$)),
filter(stats => !stats.some(s => s === undefined)),
map(stats => this._toChartData(stats)),
const dossierTemplateId: string = this._route.snapshot.paramMap.get(DOSSIER_TEMPLATE_ID);
this.stats$ = this._dashboardStatsService.getEntityChanged$(dossierTemplateId);
this.dossiersChartData$ = this.stats$.pipe(
map(s => this._translateChartService.translateDossierStates(s.dossiersChartData, dossierTemplateId)),
);
this.dossiersChartData$ = this.activeDossiersService.all$.pipe(map(() => this._toDossierChartData()));
}
private _toDossierChartData(): DoughnutChartConfig[] {
const configArray: DoughnutChartConfig[] = this._dossierStatesMapService.stats;
const undefinedStateLength =
this.activeDossiersService.all.length - configArray.map(v => v.value).reduce((acc, val) => acc + val, 0);
configArray.push({
value: undefinedStateLength,
label: this._translateService.instant('edit-dossier-dialog.general-info.form.dossier-state.placeholder'),
color: '#E2E4E9',
});
return configArray;
}
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: workflowFileStatusTranslations[status],
key: status,
} as DoughnutChartConfig),
);
documentsChartData.sort((a, b) => StatusSorter.byStatus(a.key, b.key));
return this._translateChartService.translateStatus(documentsChartData);
this.documentsChartData$ = this.stats$.pipe(map(s => this._translateChartService.translateWorkflowStatus(s.documentsChartData)));
}
}

View File

@ -10,7 +10,7 @@ import { dossierMemberChecker, dossierStateChecker, RedactionFilterSorter } from
import { workloadTranslations } from '../dossier/translations/workload-translations';
import { DossierStatsService } from '@services/dossiers/dossier-stats.service';
import { DossierStatesMapService } from '@services/entity-services/dossier-states-map.service';
import { DossiersDialogService } from '../dossier/services/dossiers-dialog.service';
import { DossiersDialogService } from '../dossier/shared/services/dossiers-dialog.service';
@Injectable()
export class ConfigService {

View File

@ -6,9 +6,10 @@ import { DefaultListingServicesTmp, EntitiesService, ListingComponent, OnAttach,
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { ConfigService } from '../config.service';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { tap } from 'rxjs/operators';
import { DossiersDialogService } from '../../dossier/services/dossiers-dialog.service';
import { DossiersDialogService } from '../../dossier/shared/services/dossiers-dialog.service';
import { Router } from '@angular/router';
@Component({
templateUrl: './dossiers-listing-screen.component.html',
@ -40,8 +41,10 @@ export class DossiersListingScreenComponent extends ListingComponent<Dossier> im
private readonly _configService: ConfigService,
private readonly _dossierTemplatesService: DossierTemplatesService,
private readonly _dialogService: DossiersDialogService,
private readonly _router: Router,
) {
super(_injector);
this._router.routeReuseStrategy.shouldReuseRoute = () => false;
}
get defaultDossierTemplateId(): string {

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { DocumentInfoService } from '../../services/document-info.service';
import { combineLatest, Observable, switchMap } from 'rxjs';
import { PermissionsService } from '@services/permissions.service';

View File

@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { BehaviorSubject, merge, Observable } from 'rxjs';
import { shareLast } from '@iqser/common-ui';
import { filter, map, startWith, tap, withLatestFrom } from 'rxjs/operators';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
import { FilesMapService } from '@services/entity-services/files-map.service';
import { File, IFileAttributeConfig } from '@red/domain';

View File

@ -6,7 +6,7 @@ import { Dictionary, DICTIONARY_TYPE_KEY_MAP, DictionaryType, Dossier, DossierTe
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DictionaryService } from '@services/entity-services/dictionary.service';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { EditorComponent } from '@shared/components/editor/editor.component';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration;

View File

@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { Dossier, DossierStats } from '@red/domain';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { List } from '@iqser/common-ui';
import dayjs from 'dayjs';

View File

@ -47,6 +47,7 @@
> div {
border-radius: 4px;
padding: 3px 8px;
width: 100%;
&:not(:last-child) {
margin-bottom: 8px;

View File

@ -1,4 +1,4 @@
import { Component, Input, OnChanges, OnInit } from '@angular/core';
import { Component, Input, OnChanges, OnInit, Optional } from '@angular/core';
import { Color } from '@red/domain';
import { FilterService, INestedFilter } from '@iqser/common-ui';
import { Observable, of } from 'rxjs';
@ -30,17 +30,20 @@ export class SimpleDoughnutChartComponent implements OnChanges, OnInit {
filtersEnabled: boolean;
chartData: any[] = [];
perimeter: number;
cx = 0;
cy = 0;
size = 0;
filters$: Observable<INestedFilter[]>;
constructor(readonly filterService: FilterService) {
this.filterService.filterGroups$.subscribe(() => {
this.filtersEnabled = !!this.filterService.filterGroups.find(g => g.slug === this.filterKey);
});
constructor(@Optional() readonly filterService: FilterService) {
if (filterService) {
this.filterService.filterGroups$.subscribe(() => {
this.filtersEnabled = !!this.filterService.filterGroups.find(g => g.slug === this.filterKey);
});
} else {
this.filtersEnabled = false;
}
}
get circumference(): number {
@ -56,7 +59,7 @@ export class SimpleDoughnutChartComponent implements OnChanges, OnInit {
}
ngOnInit() {
this.filters$ = this.filterService.getFilterModels$(this.filterKey) ?? of([]);
this.filters$ = (this.filtersEnabled && this.filterService.getFilterModels$(this.filterKey)) ?? of([]);
}
ngOnChanges(): void {
@ -67,7 +70,7 @@ export class SimpleDoughnutChartComponent implements OnChanges, OnInit {
}
filterChecked$(key: string): Observable<boolean> {
return this.filters$.pipe(map(all => all?.find(e => e.id === key)?.checked));
return this.filtersEnabled ? this.filters$.pipe(map(all => all?.find(e => e.id === key)?.checked)) : of(false);
}
calculateChartData() {

View File

@ -1,7 +1,7 @@
import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import { CircleButtonTypes, List } from '@iqser/common-ui';
import { UserService } from '@services/user.service';
import { DossiersDialogService } from '../../../dossier/services/dossiers-dialog.service';
import { DossiersDialogService } from '../../../dossier/shared/services/dossiers-dialog.service';
@Component({
selector: 'redaction-team-members',

View File

@ -10,7 +10,7 @@ import { DOSSIER_ID, DOSSIER_TEMPLATE_ID, DOSSIERS_ARCHIVE, FILE_ID } from '@uti
import { DossiersService } from '@services/dossiers/dossiers.service';
import { dossiersServiceResolver } from '@services/entity-services/dossiers.service.provider';
import { FeaturesService } from '@services/features.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
export type RouterLinkActiveOptions = { exact: boolean } | IsActiveMatchOptions;
export type BreadcrumbDisplayType = 'text' | 'dropdown';

View File

@ -0,0 +1,29 @@
import { EntitiesService, mapEach } from '@iqser/common-ui';
import { DashboardStats, IDashboardStats } from '@red/domain';
import { Injectable, Injector } from '@angular/core';
import { NGXLogger } from 'ngx-logger';
import { Observable } from 'rxjs';
import { map, switchMap, tap } from 'rxjs/operators';
import { DossierStatesService } from '@services/entity-services/dossier-states.service';
@Injectable({
providedIn: 'root',
})
export class DashboardStatsService extends EntitiesService<DashboardStats, IDashboardStats> {
constructor(
protected readonly _injector: Injector,
private readonly _dossierStatesService: DossierStatesService,
private readonly _logger: NGXLogger,
) {
super(_injector, DashboardStats, 'dossier-template/stats');
}
loadAll(): Observable<DashboardStats[]> {
return this.getAll(this._defaultModelPath).pipe(
mapEach(entity => new DashboardStats(entity)),
switchMap(entities => this._dossierStatesService.loadAllForAllTemplates().pipe(map(() => entities))),
tap(entities => entities.sort((a, b) => (a.numberOfActiveDossiers > 0 && b.numberOfActiveDossiers === 0 ? -1 : 1))),
tap(entities => this.setEntities(entities)),
);
}
}

View File

@ -2,12 +2,12 @@ import { EntitiesService, List, mapEach, RequiredParam, Toaster, Validate } from
import { DossierTemplate, IDossierTemplate } from '@red/domain';
import { Injectable, Injector } from '@angular/core';
import { forkJoin, Observable, of } from 'rxjs';
import { FileAttributesService } from './file-attributes.service';
import { FileAttributesService } from '../entity-services/file-attributes.service';
import { catchError, mapTo, switchMap, tap } from 'rxjs/operators';
import { DossierTemplateStatsService } from '@services/entity-services/dossier-template-stats.service';
import { DossierTemplateStatsService } from '../entity-services/dossier-template-stats.service';
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DictionaryService } from '@services/entity-services/dictionary.service';
import { DictionaryService } from '../entity-services/dictionary.service';
const DOSSIER_TEMPLATE_CONFLICT_MSG = _('dossier-templates-listing.error.conflict');
const GENERIC_MSG = _('dossier-templates-listing.error.generic');

View File

@ -4,11 +4,6 @@ import { timer } from 'rxjs';
import { CHANGED_CHECK_INTERVAL, DOSSIERS_ROUTE } from '@utils/constants';
import { DossiersService } from './dossiers.service';
export interface IDossiersStats {
totalPeople: number;
totalAnalyzedPages: number;
}
@Injectable({
providedIn: 'root',
})
@ -23,8 +18,4 @@ export class ActiveDossiersService extends DossiersService {
)
.subscribe();
}
getCountWithState(dossierStatusId: string): number {
return this.all.filter(dossier => dossier.dossierStatusId === dossierStatusId).length;
}
}

View File

@ -1,23 +1,21 @@
import { EntitiesService, List, mapEach, QueryParam, RequiredParam, shareLast, Toaster, Validate } from '@iqser/common-ui';
import { EntitiesService, List, mapEach, QueryParam, RequiredParam, Toaster, Validate } from '@iqser/common-ui';
import { Dossier, DossierStats, IDossier, IDossierChanges, IDossierRequest } from '@red/domain';
import { combineLatest, forkJoin, Observable, of, Subject, throwError } from 'rxjs';
import { forkJoin, Observable, of, Subject, throwError } from 'rxjs';
import { catchError, filter, map, switchMap, tap } from 'rxjs/operators';
import { Injector } from '@angular/core';
import { DossierStatesService } from '../entity-services/dossier-states.service';
import { DossierStatsService } from './dossier-stats.service';
import { IDossiersStats } from './active-dossiers.service';
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { NGXLogger } from 'ngx-logger';
import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service';
const CONFLICT_MSG = _('add-dossier-dialog.errors.dossier-already-exists');
const GENERIC_MSG = _('add-dossier-dialog.errors.generic');
export abstract class DossiersService extends EntitiesService<Dossier, IDossier> {
readonly dossierFileChanges$ = new Subject<string>();
readonly generalStats$ = this.all$.pipe(switchMap(entities => this.#generalStats$(entities)));
protected readonly _dossierStatsService = this._injector.get(DossierStatsService);
protected readonly _dossierStateService = this._injector.get(DossierStatesService);
protected readonly _dashboardStatsService = this._injector.get(DashboardStatsService);
protected readonly _toaster = this._injector.get(Toaster);
protected readonly _logger = this._injector.get(NGXLogger);
@ -64,7 +62,7 @@ export abstract class DossiersService extends EntitiesService<Dossier, IDossier>
mapEach(entity => new Dossier(entity)),
/* Load stats before updating entities */
switchMap(dossiers => this._dossierStatsService.getFor(dossierIds(dossiers)).pipe(map(() => dossiers))),
switchMap(dossiers => this._dossierStateService.loadAllForAllTemplates().pipe(map(() => dossiers))),
switchMap(dossiers => this._dashboardStatsService.loadAll().pipe(map(() => dossiers))),
tap(dossiers => this.setEntities(dossiers)),
);
}
@ -86,28 +84,4 @@ export abstract class DossiersService extends EntitiesService<Dossier, IDossier>
switchMap(dossier => this._dossierStatsService.getFor([dossier.dossierId])),
);
}
#computeStats(entities: List<Dossier>): IDossiersStats {
let totalAnalyzedPages = 0;
const totalPeople = new Set<string>();
entities.forEach(dossier => {
dossier.memberIds?.forEach(m => totalPeople.add(m));
totalAnalyzedPages += this._dossierStatsService.get(dossier.dossierId).numberOfPages;
});
return {
totalPeople: totalPeople.size,
totalAnalyzedPages,
};
}
#generalStats$(entities: List<Dossier>): Observable<IDossiersStats> {
const stats$ = entities.map(entity => this._dossierStatsService.watch$(entity.dossierId));
return combineLatest(stats$).pipe(
filter(stats => stats.every(s => !!s)),
map(() => this.#computeStats(entities)),
shareLast(),
);
}
}

View File

@ -2,7 +2,7 @@ import { Injectable, Injector } from '@angular/core';
import { EntitiesService, mapEach, RequiredParam, Toaster, Validate } from '@iqser/common-ui';
import { DossierState, IDossierState } from '@red/domain';
import { EMPTY, forkJoin, Observable, switchMap } from 'rxjs';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { catchError, defaultIfEmpty, tap } from 'rxjs/operators';
import { DossierStatesMapService } from '@services/entity-services/dossier-states-map.service';
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';

View File

@ -12,11 +12,6 @@ import { flatMap } from 'lodash-es';
import { DossierStatsService } from '@services/dossiers/dossier-stats.service';
import { FilesService } from '@services/entity-services/files.service';
export interface IDossiersStats {
totalPeople: number;
totalAnalyzedPages: number;
}
@Injectable({
providedIn: 'root',
})

View File

@ -2,17 +2,34 @@ import { Injectable } from '@angular/core';
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
import { TranslateService } from '@ngx-translate/core';
import { rolesTranslations } from '../translations/roles-translations';
import { workflowFileStatusTranslations } from '../translations/file-status-translations';
import { DossierStatesMapService } from './entity-services/dossier-states-map.service';
@Injectable({
providedIn: 'root',
})
export class TranslateChartService {
constructor(private readonly _translateService: TranslateService) {}
constructor(private readonly _translateService: TranslateService, private readonly _dossierStatesMapService: DossierStatesMapService) {}
translateStatus(config: DoughnutChartConfig[]): DoughnutChartConfig[] {
translateLabels(config: DoughnutChartConfig[]): DoughnutChartConfig[] {
return config.map(val => ({ ...val, label: this._translateService.instant(val.label) }));
}
translateDossierStates(config: DoughnutChartConfig[], dossierTemplateId: string): DoughnutChartConfig[] {
return config.map(val => {
if (!val.key) {
return { ...val, label: this._translateService.instant(val.label) };
} else {
const dossierState = this._dossierStatesMapService.get(dossierTemplateId, val.key);
return { ...val, key: null, label: dossierState.name, color: dossierState.color };
}
});
}
translateWorkflowStatus(config: DoughnutChartConfig[]): DoughnutChartConfig[] {
return config.map(val => ({ ...val, label: this._translateService.instant(workflowFileStatusTranslations[val.label] as string) }));
}
translateRoles(config: DoughnutChartConfig[]): DoughnutChartConfig[] {
return config.map(val => ({
...val,

View File

@ -775,10 +775,6 @@
},
"stats": {
"analyzed-pages": "Seiten",
"charts": {
"dossiers": "Dossiers",
"total-documents": "Anzahl der Dokumente"
},
"total-people": "Anzahl der Benutzer"
},
"table-col-names": {
@ -793,6 +789,10 @@
"title": "{length} {length, plural, one{aktives Dossier} other{aktive Dossiers}}"
}
},
"dossier-template-charts": {
"active-dossiers": "Aktive Dossiers",
"total-documents": "Anzahl der Dokumente"
},
"dossier-overview": {
"approve": "Genehmigen",
"approve-disabled": "Das Dokument kann erst genehmigt werden, wenn eine Analyse auf Basis der aktuellen Wörterbücher durchgeführt wurde und die Vorschläge bearbeitet wurden.",

View File

@ -775,10 +775,6 @@
},
"stats": {
"analyzed-pages": "{count, plural, one{Page} other{Pages}}",
"charts": {
"dossiers": "{count, plural, one{Dossier} other{Dossiers}}",
"total-documents": "Total Documents"
},
"total-people": "Total users"
},
"table-col-names": {
@ -793,6 +789,10 @@
"title": "{length} active {length, plural, one{Dossier} other{Dossiers}}"
}
},
"dossier-template-charts": {
"active-dossiers": "Active {count, plural, one{Dossier} other{Dossiers}}",
"total-documents": "Total Documents"
},
"dossier-overview": {
"approve": "Approve",
"approve-disabled": "File can only be approved once it has been analysed with the latest dictionaries and all suggestions have been processed.",

@ -1 +1 @@
Subproject commit f06c007bec1a7da5c257e68d6df8c806419ceb2b
Subproject commit 5444b09e647672383a0b39d23400a98d7efb6536

View File

@ -0,0 +1,89 @@
import { IDashboardStats, ProcessingFileStatus, StatusSorter, WorkflowFileStatus } from '@red/domain';
import { IListable } from '@iqser/common-ui';
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
export class DashboardStats implements IListable, IDashboardStats {
readonly dossierCountByStatus: [
{
count: number;
statusId: string;
},
];
readonly dossierTemplateId: string;
readonly dossiersInTemplate: [string];
readonly fileCountPerProcessingStatus: [
{
readonly count: number;
readonly processingStatus: ProcessingFileStatus;
},
];
readonly fileCountPerWorkflowStatus: [
{
readonly count: number;
readonly workflowStatus: WorkflowFileStatus;
},
];
readonly name: string;
readonly numberOfActiveDossiers: number;
readonly numberOfActiveFiles: number;
readonly numberOfArchivedDossiers: number;
readonly numberOfDeletedDossiers: number;
readonly numberOfExcludedPages: number;
readonly numberOfPages: number;
readonly numberOfPeople: number;
readonly numberOfSoftDeletedFiles: number;
readonly dossiersChartData: DoughnutChartConfig[];
readonly documentsChartData: DoughnutChartConfig[];
constructor(stats: IDashboardStats) {
this.dossierCountByStatus = stats.dossierCountByStatus;
this.dossierTemplateId = stats.dossierTemplateId;
this.dossiersInTemplate = stats.dossiersInTemplate;
this.fileCountPerProcessingStatus = stats.fileCountPerProcessingStatus;
this.fileCountPerWorkflowStatus = stats.fileCountPerWorkflowStatus;
this.name = stats.name;
this.numberOfActiveDossiers = stats.numberOfActiveDossiers;
this.numberOfActiveFiles = stats.numberOfActiveFiles;
this.numberOfArchivedDossiers = stats.numberOfArchivedDossiers;
this.numberOfDeletedDossiers = stats.numberOfDeletedDossiers;
this.numberOfExcludedPages = stats.numberOfExcludedPages;
this.numberOfPages = stats.numberOfPages;
this.numberOfPeople = stats.numberOfPeople;
this.numberOfSoftDeletedFiles = stats.numberOfSoftDeletedFiles;
this.dossiersChartData = this._dossiersChartData;
this.documentsChartData = this._documentsChartData;
}
get isEmpty(): boolean {
return this.numberOfActiveDossiers === 0;
}
get id(): string {
return this.dossierTemplateId;
}
get searchKey(): string {
return this.name;
}
private get _dossiersChartData(): DoughnutChartConfig[] {
return this.dossierCountByStatus.map(d => ({
value: d.count,
color: '#e2e4e9',
label: 'edit-dossier-dialog.general-info.form.dossier-state.placeholder',
key: d.statusId,
}));
}
private get _documentsChartData(): DoughnutChartConfig[] {
const configArray: DoughnutChartConfig[] = this.fileCountPerWorkflowStatus.map(d => ({
value: d.count,
color: d.workflowStatus,
label: d.workflowStatus,
key: d.workflowStatus,
}));
configArray.sort((a: DoughnutChartConfig, b) => StatusSorter.byStatus(a.label, b.label));
return configArray;
}
}

View File

@ -0,0 +1,33 @@
import { ProcessingFileStatus, WorkflowFileStatus } from '@red/domain';
export interface IDashboardStats {
readonly dossierCountByStatus: [
{
count: number;
statusId: string;
},
];
readonly dossierTemplateId: string;
readonly dossiersInTemplate: [string];
readonly fileCountPerProcessingStatus: [
{
readonly count: number;
readonly processingStatus: ProcessingFileStatus;
},
];
readonly fileCountPerWorkflowStatus: [
{
readonly count: number;
readonly workflowStatus: WorkflowFileStatus;
},
];
readonly name: string;
readonly numberOfActiveDossiers: number;
readonly numberOfActiveFiles: number;
readonly numberOfArchivedDossiers: number;
readonly numberOfDeletedDossiers: number;
readonly numberOfExcludedPages: number;
readonly numberOfPages: number;
readonly numberOfPeople: number;
readonly numberOfSoftDeletedFiles: number;
}

View File

@ -2,4 +2,6 @@ export * from './dossier-template';
export * from './dossier-template.model';
export * from './dossier-template-stats';
export * from './dossier-template-stats.model';
export * from './dashboard-stats';
export * from './dashboard-stats.model';
export * from './types';