fixed doughnut charts

This commit is contained in:
Edi Cziszter 2022-02-11 14:59:49 +02:00
parent 2b3638a97d
commit 860cc91abc
8 changed files with 143 additions and 112 deletions

View File

@ -22,7 +22,6 @@ import { DICTIONARY_TYPE, DOSSIER_TEMPLATE_ID } from '@utils/constants';
import { DossierTemplateExistsGuard } from '../../guards/dossier-template-exists.guard';
import { DictionaryExistsGuard } from '../../guards/dictionary-exists.guard';
import { DossierStatesListingScreenComponent } from './screens/dossier-states-listing/dossier-states-listing-screen.component';
import { DossiersGuard } from '../../guards/dossiers.guard';
const routes: Routes = [
{ path: '', redirectTo: 'dossier-templates', pathMatch: 'full' },
@ -121,7 +120,7 @@ const routes: Routes = [
component: DossierStatesListingScreenComponent,
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard, DossiersGuard],
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard],
},
},
{

View File

@ -26,7 +26,7 @@
</div>
<div class="dialog-actions">
<button (click)="dialogRef.close(replaceDossierStatusId)" [disabled]="!replaceDossierStatusId" mat-flat-button>
<button (click)="dialogRef.close(replaceDossierStatusId)" color="primary" [disabled]="!replaceDossierStatusId" mat-flat-button>
{{ 'confirm-delete-dossier-state.delete-replace' | translate }}
</button>
<div (click)="dialogRef.close()" [translate]="'confirm-delete-dossier-state.cancel'" class="all-caps-label cancel"></div>

View File

@ -1,94 +1,98 @@
<section>
<div class="page-header">
<redaction-dossier-template-breadcrumbs class="flex-1"></redaction-dossier-template-breadcrumbs>
<ng-container *ngIf="dossierStateService.all">
<section>
<div class="page-header">
<redaction-dossier-template-breadcrumbs class="flex-1"></redaction-dossier-template-breadcrumbs>
<div class="actions flex-1">
<redaction-dossier-template-actions></redaction-dossier-template-actions>
<div class="actions flex-1">
<redaction-dossier-template-actions></redaction-dossier-template-actions>
<iqser-circle-button
[routerLink]="['../..']"
[tooltip]="'common.close' | translate"
icon="iqser:close"
tooltipPosition="below"
></iqser-circle-button>
</div>
</div>
<div class="content-inner">
<div class="overlay-shadow"></div>
<redaction-admin-side-nav type="dossierTemplates"></redaction-admin-side-nav>
<div class="content-container">
<iqser-table
[headerTemplate]="headerTemplate"
[itemSize]="80"
[noDataText]="'dossier-states-listing.no-data.title' | translate"
[noMatchText]="'dossier-states-listing.no-match.title' | translate"
[selectionEnabled]="true"
[tableColumnConfigs]="tableColumnConfigs"
emptyColumnWidth="1fr"
noDataIcon="red:attribute"
></iqser-table>
</div>
<div class="right-container">
<redaction-simple-doughnut-chart
[config]="chartData"
[radius]="80"
[strokeWidth]="15"
[subtitle]="'dossier-states-listing.chart.dossier-states' | translate"
[totalType]="'simpleLabel'"
></redaction-simple-doughnut-chart>
</div>
</div>
</section>
<ng-template #headerTemplate>
<div class="table-header-actions">
<iqser-input-with-action
[(value)]="searchService.searchValue"
[placeholder]="'dossier-states-listing.search' | translate"
></iqser-input-with-action>
<iqser-icon-button
(action)="openAddEditStateDialog($event)"
*ngIf="currentUser.isAdmin"
[label]="'dossier-states-listing.add-new' | translate"
[type]="iconButtonTypes.primary"
icon="iqser:plus"
></iqser-icon-button>
</div>
</ng-template>
<ng-template #tableItemTemplate let-entity="entity">
<div *ngIf="cast(entity) as state">
<div class="cell">
<div class="flex-align-items-center">
<div [style.background-color]="state.description" class="dossier-state-square"></div>
<div class="state-name">{{ state.name }}</div>
</div>
</div>
<div class="cell small-label">
<span>{{ state.dossierCount }}</span>
</div>
<div class="cell">
<div *ngIf="currentUser.isAdmin" class="action-buttons">
<iqser-circle-button
(action)="openAddEditStateDialog($event, state)"
[tooltip]="'dossier-states-listing.action.edit' | translate"
[type]="circleButtonTypes.dark"
icon="iqser:edit"
></iqser-circle-button>
<iqser-circle-button
(action)="openConfirmDeleteStateDialog($event, state)"
[tooltip]="'dossier-states-listing.action.delete' | translate"
[type]="circleButtonTypes.dark"
icon="iqser:trash"
[routerLink]="['../..']"
[tooltip]="'common.close' | translate"
icon="iqser:close"
tooltipPosition="below"
></iqser-circle-button>
</div>
</div>
</div>
</ng-template>
<div class="content-inner">
<div class="overlay-shadow"></div>
<redaction-admin-side-nav type="dossierTemplates"></redaction-admin-side-nav>
<div class="content-container">
<iqser-table
[headerTemplate]="headerTemplate"
[itemSize]="80"
[noDataText]="'dossier-states-listing.no-data.title' | translate"
[noMatchText]="'dossier-states-listing.no-match.title' | translate"
[selectionEnabled]="true"
[tableColumnConfigs]="tableColumnConfigs"
emptyColumnWidth="1fr"
noDataIcon="red:attribute"
></iqser-table>
</div>
<div class="right-container">
<ng-container *ngIf="chartData">
<redaction-simple-doughnut-chart
[config]="chartData"
[radius]="80"
[strokeWidth]="15"
[subtitle]="'dossier-states-listing.chart.dossier-states' | translate"
[totalType]="'simpleLabel'"
></redaction-simple-doughnut-chart>
</ng-container>
</div>
</div>
</section>
<ng-template #headerTemplate>
<div class="table-header-actions">
<iqser-input-with-action
[(value)]="searchService.searchValue"
[placeholder]="'dossier-states-listing.search' | translate"
></iqser-input-with-action>
<iqser-icon-button
(action)="openAddEditStateDialog($event)"
*ngIf="currentUser.isAdmin"
[label]="'dossier-states-listing.add-new' | translate"
[type]="iconButtonTypes.primary"
icon="iqser:plus"
></iqser-icon-button>
</div>
</ng-template>
<ng-template #tableItemTemplate let-entity="entity">
<div *ngIf="cast(entity) as state">
<div class="cell">
<div class="flex-align-items-center">
<div [style.background-color]="state.description" class="dossier-state-square"></div>
<div class="state-name">{{ state.name }}</div>
</div>
</div>
<div class="cell small-label">
<span>{{ state.dossierCount }}</span>
</div>
<div class="cell">
<div *ngIf="currentUser.isAdmin" class="action-buttons">
<iqser-circle-button
(action)="openAddEditStateDialog($event, state)"
[tooltip]="'dossier-states-listing.action.edit' | translate"
[type]="circleButtonTypes.dark"
icon="iqser:edit"
></iqser-circle-button>
<iqser-circle-button
(action)="openConfirmDeleteStateDialog($event, state)"
[tooltip]="'dossier-states-listing.action.delete' | translate"
[type]="circleButtonTypes.dark"
icon="iqser:trash"
></iqser-circle-button>
</div>
</div>
</div>
</ng-template>
</ng-container>

View File

@ -44,7 +44,7 @@ export class DossierStatesListingScreenComponent extends ListingComponent<Dossie
protected readonly _injector: Injector,
private readonly _loadingService: LoadingService,
private readonly _dossiersService: DossiersService,
private readonly _dossierStateService: DossierStateService,
readonly dossierStateService: DossierStateService,
private readonly _dossierTemplatesService: DossierTemplatesService,
private readonly _dialogService: AdminDialogService,
private readonly _userService: UserService,
@ -77,38 +77,34 @@ export class DossierStatesListingScreenComponent extends ListingComponent<Dossie
};
this._dialogService.openDialog('deleteDossierState', $event, data, async (value: string) => {
if (value) {
await firstValueFrom(this._dossierStateService.deleteAndReplace(dossierState.dossierStatusId, value));
await firstValueFrom(this.dossierStateService.deleteAndReplace(dossierState.dossierStatusId, value));
}
await firstValueFrom(this._dossierStateService.loadAllForAllTemplates());
await this._appStateService.refreshDossierTemplate(templateId);
await this._loadData();
});
}
private async _createNewDossierStateAndRefreshView(newValue: IDossierState): Promise<void> {
await firstValueFrom(this._dossierStateService.setDossierState(newValue)).catch(error => {
await firstValueFrom(this.dossierStateService.setDossierState(newValue)).catch(error => {
if (error.status === HttpStatusCode.Conflict) {
this._toaster.error(_('dossier-states-listing.error.conflict'));
} else {
this._toaster.error(_('dossier-states-listing.error.generic'));
}
});
await firstValueFrom(this._dossierStateService.loadAllForAllTemplates());
await this._appStateService.refreshDossierTemplate(this._dossierTemplatesService.activeDossierTemplateId);
await this._loadData();
}
private async _loadData() {
private async _loadData(): Promise<void> {
this._loadingService.start();
await firstValueFrom(this._dossiersService.loadAll());
try {
const templateId = this._dossierTemplatesService.activeDossierTemplateId;
const dossierStates = this._dossierStateService.all.filter(d => d.dossierTemplateId === templateId);
const dossiers = this._dossiersService.all;
this._dossierStateService.all.forEach(
state => (state.dossierCount = dossiers.filter(dossier => dossier.dossierStatusId === state.dossierStatusId).length),
);
const dossierStates = this.dossierStateService.all.filter(d => d.dossierTemplateId === templateId);
this._setStatesCount();
this.chartData = this._loadChartData();
this.entitiesService.setEntities(dossierStates || []);
} catch (e) {}
@ -117,10 +113,16 @@ export class DossierStatesListingScreenComponent extends ListingComponent<Dossie
private _loadChartData(): DoughnutChartConfig[] {
const config: DoughnutChartConfig[] = [];
this._dossierStateService.all.forEach(state => {
this.dossierStateService.all.forEach(state => {
config.push({ value: state.dossierCount, label: state.name, key: state.name, color: state.description });
});
return config;
}
private _setStatesCount(): void {
this.dossierStateService.all.forEach(
state => (state.dossierCount = this._dossiersService.getCountWithState(state.dossierStatusId)),
);
}
}

View File

@ -57,7 +57,9 @@
<label translate="edit-dossier-dialog.general-info.form.dossier-status.label"></label>
<mat-select
[placeholder]="
currentStatus ? currentStatus.name : ('edit-dossier-dialog.general-info.form.dossier-status.label' | translate)
currentStatus
? currentStatus.name
: ('edit-dossier-dialog.general-info.form.dossier-status.placeholder' | translate)
"
formControlName="dossierStatusId"
>

View File

@ -7,8 +7,9 @@ import { Dossier, DossierStats, FileCountPerWorkflowStatus, StatusSorter } from
import { workflowFileStatusTranslations } from '../../../../translations/file-status-translations';
import { TranslateChartService } from '@services/translate-chart.service';
import { filter, map, switchMap } from 'rxjs/operators';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
import { DossierStateService } from '../../../../../../services/entity-services/dossier-state.service';
import { TranslateService } from '@ngx-translate/core';
@Component({
selector: 'redaction-dossiers-listing-details',
@ -25,6 +26,8 @@ export class DossiersListingDetailsComponent {
readonly dossiersService: DossiersService,
private readonly _dossierStatsMap: DossierStatsService,
private readonly _translateChartService: TranslateChartService,
private readonly _dossierStateService: DossierStateService,
private readonly _translateService: TranslateService,
) {
this.documentsChartData$ = this.dossiersService.all$.pipe(
mapEach(dossier => _dossierStatsMap.watch$(dossier.dossierId)),
@ -37,12 +40,19 @@ export class DossiersListingDetailsComponent {
}
private async _toDossierChartData(dossiers: Dossier[]): Promise<DoughnutChartConfig[]> {
const config: DoughnutChartConfig[] = [];
this._dossierStateService.all.forEach(state => {
state.dossierCount = this.dossiersService.getCountWithState(state.dossierStatusId);
config.push({ value: state.dossierCount, label: state.name, color: state.description });
});
const notAssignedLength = this.dossiersService.all.length - config.map(v => v.value).reduce((acc, val) => acc + val, 0);
config.push({
value: notAssignedLength,
label: this._translateService.instant('edit-dossier-dialog.general-info.form.dossier-status.placeholder'),
color: '#D8DAE0',
});
// TODO: deleted dossiers count should come with stats
// const deletedDossiers = await this.dossiersService.getDeleted();
return [
{ value: dossiers.length, color: 'ACTIVE', label: _('active') },
// { value: deletedDossiers.length, color: 'DELETED', label: _('archived') },
];
return config;
}
private _toChartData(stats: DossierStats[]) {

View File

@ -51,7 +51,7 @@ export class SimpleDoughnutChartComponent implements OnChanges, OnInit {
}
get displayedDataTotal() {
return this.totalType === 'count' ? this.config.length : this.dataTotal;
return this.totalType === 'sum' ? this.dataTotal : this.config.length;
}
ngOnInit() {

View File

@ -62,7 +62,17 @@ export class DossiersService extends EntitiesService<Dossier, IDossier> {
}
loadAllIfChanged(): Observable<ChangesDetails> {
return this.hasChangesDetails$().pipe(switchMap(changes => this.loadAll().pipe(mapTo(changes))));
return this.hasChangesDetails$().pipe(
catchError(err => {
console.log('aaa', err);
return of(err);
}),
switchMap(changes => this.loadAll().pipe(mapTo(changes))),
catchError(err => {
console.log('bbb', err);
return of(err);
}),
);
}
hasChangesDetails$(): Observable<ChangesDetails> {
@ -111,6 +121,10 @@ export class DossiersService extends EntitiesService<Dossier, IDossier> {
return firstValueFrom(super.delete(body, 'deleted-dossiers/hard-delete', body));
}
getCountWithState(dossierStatusId: string): number {
return this.all.filter(dossier => dossier.dossierStatusId === dossierStatusId).length;
}
private _emitFileChanges(changes: ChangesDetails): void {
changes.dossierChanges.filter(change => change.fileChanges).forEach(change => this.dossierFileChanges$.next(change.dossierId));
}