RED-3586, RED-3593: Redo dossier states
This commit is contained in:
parent
61ecb52969
commit
460701a17b
@ -1,7 +1,7 @@
|
||||
<section class="dialog">
|
||||
<div
|
||||
[translateParams]="{
|
||||
type: data.dossierState ? 'edit' : 'create',
|
||||
type: type,
|
||||
name: data.dossierState?.name
|
||||
}"
|
||||
[translate]="'add-edit-dossier-state.title'"
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
import { ChangeDetectionStrategy, Component, Inject, Injector } from '@angular/core';
|
||||
import { BaseDialogComponent } from '@iqser/common-ui';
|
||||
import { BaseDialogComponent, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { IDossierState } from '@red/domain';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { DossierStatesService } from '@services/entity-services/dossier-states.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
|
||||
interface DialogData {
|
||||
readonly dossierState: IDossierState;
|
||||
@ -18,6 +21,9 @@ interface DialogData {
|
||||
export class AddEditDossierStateDialogComponent extends BaseDialogComponent {
|
||||
constructor(
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _toaster: Toaster,
|
||||
private readonly _dossierStateService: DossierStatesService,
|
||||
protected readonly _injector: Injector,
|
||||
protected readonly _dialogRef: MatDialogRef<AddEditDossierStateDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) readonly data: DialogData,
|
||||
@ -27,13 +33,23 @@ export class AddEditDossierStateDialogComponent extends BaseDialogComponent {
|
||||
this.initialFormValue = this.form.getRawValue();
|
||||
}
|
||||
|
||||
save(): void {
|
||||
get type(): 'edit' | 'create' {
|
||||
return this.data.dossierState ? 'edit' : 'create';
|
||||
}
|
||||
|
||||
async save(): Promise<void> {
|
||||
const dossierState: IDossierState = {
|
||||
dossierStatusId: this.data.dossierState?.dossierStatusId,
|
||||
dossierTemplateId: this.data.dossierTemplateId,
|
||||
...this.form.getRawValue(),
|
||||
};
|
||||
this._dialogRef.close(dossierState);
|
||||
this._loadingService.start();
|
||||
try {
|
||||
await firstValueFrom(this._dossierStateService.createOrUpdate(dossierState));
|
||||
this._toaster.success(_('add-edit-dossier-state.success'), { params: { type: this.type } });
|
||||
this._dialogRef.close();
|
||||
} catch (e) {}
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
#getForm(): FormGroup {
|
||||
|
||||
@ -12,12 +12,12 @@
|
||||
<form [formGroup]="form">
|
||||
<div class="flex">
|
||||
<div class="iqser-input-group w-300">
|
||||
<label translate="confirm-delete-dossier-state.form.status"></label>
|
||||
<label translate="confirm-delete-dossier-state.form.state"></label>
|
||||
<mat-select
|
||||
[placeholder]="'confirm-delete-dossier-state.form.status-placeholder' | translate"
|
||||
[placeholder]="'confirm-delete-dossier-state.form.state-placeholder' | translate"
|
||||
formControlName="replaceDossierStatusId"
|
||||
>
|
||||
<mat-option>{{ 'confirm-delete-dossier-state.form.status-placeholder' | translate }}</mat-option>
|
||||
<mat-option>{{ 'confirm-delete-dossier-state.form.state-placeholder' | translate }}</mat-option>
|
||||
<mat-option *ngFor="let state of data.otherStates" [value]="state.dossierStatusId">
|
||||
{{ state.name }}
|
||||
</mat-option>
|
||||
@ -29,10 +29,10 @@
|
||||
</div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
<button (click)="dialogRef.close(afterCloseValue)" color="primary" mat-flat-button>
|
||||
<button (click)="save()" color="primary" mat-flat-button>
|
||||
{{ label | translate }}
|
||||
</button>
|
||||
<div (click)="dialogRef.close()" [translate]="'confirm-delete-dossier-state.cancel'" class="all-caps-label cancel"></div>
|
||||
<div [translate]="'confirm-delete-dossier-state.cancel'" class="all-caps-label cancel" mat-dialog-close></div>
|
||||
</div>
|
||||
|
||||
<iqser-circle-button class="dialog-close" icon="iqser:close" mat-dialog-close></iqser-circle-button>
|
||||
|
||||
@ -1,12 +1,18 @@
|
||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
||||
import { IDossierState } from '@red/domain';
|
||||
import { DossierState } from '@red/domain';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { firstValueFrom, forkJoin } from 'rxjs';
|
||||
import { DossierStatesService } from '@services/entity-services/dossier-states.service';
|
||||
import { LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
||||
import { ArchivedDossiersService } from '@services/dossiers/archived-dossiers.service';
|
||||
import { take } from 'rxjs/operators';
|
||||
|
||||
interface DialogData {
|
||||
readonly toBeDeletedState: IDossierState;
|
||||
readonly otherStates: IDossierState[];
|
||||
readonly toBeDeletedState: DossierState;
|
||||
readonly otherStates: DossierState[];
|
||||
readonly dossierCount: number;
|
||||
}
|
||||
|
||||
@ -21,7 +27,12 @@ export class ConfirmDeleteDossierStateDialogComponent {
|
||||
|
||||
constructor(
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
readonly dialogRef: MatDialogRef<ConfirmDeleteDossierStateDialogComponent>,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _toaster: Toaster,
|
||||
private readonly _dossierStateService: DossierStatesService,
|
||||
private readonly _dialogRef: MatDialogRef<ConfirmDeleteDossierStateDialogComponent>,
|
||||
private readonly _activeDossiersService: ActiveDossiersService,
|
||||
private readonly _archivedDossiersService: ArchivedDossiersService,
|
||||
@Inject(MAT_DIALOG_DATA) readonly data: DialogData,
|
||||
) {
|
||||
this.form = this.#getForm();
|
||||
@ -42,8 +53,15 @@ export class ConfirmDeleteDossierStateDialogComponent {
|
||||
return this.replaceDossierStatusId ? _('confirm-delete-dossier-state.delete-replace') : _('confirm-delete-dossier-state.delete');
|
||||
}
|
||||
|
||||
get afterCloseValue(): string | true {
|
||||
return this.replaceDossierStatusId ?? true;
|
||||
async save(): Promise<void> {
|
||||
this._loadingService.start();
|
||||
await firstValueFrom(this._dossierStateService.deleteState(this.data.toBeDeletedState, this.replaceDossierStatusId));
|
||||
await firstValueFrom(
|
||||
forkJoin([this._activeDossiersService.loadAll().pipe(take(1)), this._archivedDossiersService.loadAll().pipe(take(1))]),
|
||||
);
|
||||
this._toaster.success(_('confirm-delete-dossier-state.success'));
|
||||
this._dialogRef.close();
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
#getForm(): FormGroup {
|
||||
|
||||
@ -1,101 +1,98 @@
|
||||
<ng-container *ngIf="dossierStateService.all">
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<redaction-dossier-template-breadcrumbs class="flex-1"></redaction-dossier-template-breadcrumbs>
|
||||
<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"
|
||||
[tableColumnConfigs]="tableColumnConfigs"
|
||||
emptyColumnWidth="1fr"
|
||||
noDataIcon="red:attribute"
|
||||
></iqser-table>
|
||||
</div>
|
||||
|
||||
<div class="right-container">
|
||||
<redaction-simple-doughnut-chart
|
||||
*ngIf="chartData$ | async as chartData"
|
||||
[config]="chartData"
|
||||
[radius]="80"
|
||||
[strokeWidth]="15"
|
||||
[subtitle]="'dossier-states-listing.chart.dossier-states' | translate: { count: chartData.length }"
|
||||
[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="permissionsService.canPerformDossierStatesActions"
|
||||
[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.color" class="dossier-state-square"></div>
|
||||
<div class="state-name">{{ state.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cell small-label">
|
||||
<span>{{ state.rank }}</span>
|
||||
</div>
|
||||
|
||||
<div class="cell small-label">
|
||||
<span>{{ state.dossierCount }}</span>
|
||||
</div>
|
||||
|
||||
<div class="cell">
|
||||
<div *ngIf="permissionsService.canPerformDossierStatesActions" class="action-buttons">
|
||||
<iqser-circle-button
|
||||
[routerLink]="['../..']"
|
||||
[tooltip]="'common.close' | translate"
|
||||
icon="iqser:close"
|
||||
tooltipPosition="below"
|
||||
(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 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
|
||||
*ngIf="chartData"
|
||||
[config]="chartData"
|
||||
[radius]="80"
|
||||
[strokeWidth]="15"
|
||||
[subtitle]="'dossier-states-listing.chart.dossier-states' | translate: { count: chartData.length }"
|
||||
[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.color" class="dossier-state-square"></div>
|
||||
<div class="state-name">{{ state.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cell small-label">
|
||||
<span>{{ state.rank }}</span>
|
||||
</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>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
.dossier-state-square {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
min-width: 16px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
|
||||
@ -4,22 +4,19 @@ import {
|
||||
DefaultListingServices,
|
||||
IconButtonTypes,
|
||||
ListingComponent,
|
||||
LoadingService,
|
||||
SortingOrders,
|
||||
TableColumnConfig,
|
||||
Toaster,
|
||||
} from '@iqser/common-ui';
|
||||
import { DossierState, IDossierState } from '@red/domain';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
||||
import { DossierStateService } from '@services/entity-services/dossier-state.service';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { firstValueFrom, map, Observable } from 'rxjs';
|
||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { HttpStatusCode } from '@angular/common/http';
|
||||
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
||||
import { DossierStatesMapService } from '@services/entity-services/dossier-states-map.service';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { DossierStatesService } from '@services/entity-services/dossier-states.service';
|
||||
|
||||
@Component({
|
||||
templateUrl: './dossier-states-listing-screen.component.html',
|
||||
@ -33,29 +30,29 @@ import { DossierTemplatesService } from '@services/entity-services/dossier-templ
|
||||
export class DossierStatesListingScreenComponent extends ListingComponent<DossierState> implements OnInit, OnDestroy {
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly currentUser = this._userService.currentUser;
|
||||
readonly tableHeaderLabel = _('dossier-states-listing.table-header.title');
|
||||
readonly tableColumnConfigs: TableColumnConfig<DossierState>[] = [
|
||||
{ label: _('dossier-states-listing.table-col-names.name'), sortByKey: 'name' },
|
||||
{ label: _('dossier-states-listing.table-col-names.rank'), sortByKey: 'rank' },
|
||||
{ label: _('dossier-states-listing.table-col-names.dossiers-count') },
|
||||
];
|
||||
chartData: DoughnutChartConfig[];
|
||||
chartData$: Observable<DoughnutChartConfig[]>;
|
||||
readonly #dossierTemplateId: string;
|
||||
|
||||
constructor(
|
||||
protected readonly _injector: Injector,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _activeDossiersService: ActiveDossiersService,
|
||||
readonly dossierStateService: DossierStateService,
|
||||
private readonly _dialogService: AdminDialogService,
|
||||
private readonly _userService: UserService,
|
||||
private readonly _toaster: Toaster,
|
||||
private readonly _route: ActivatedRoute,
|
||||
private readonly _dossierTemplatesService: DossierTemplatesService,
|
||||
private readonly _dossierStatesMapService: DossierStatesMapService,
|
||||
private readonly _dossierStatesService: DossierStatesService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
) {
|
||||
super(_injector);
|
||||
this.#dossierTemplateId = _route.snapshot.paramMap.get('dossierTemplateId');
|
||||
this.chartData$ = this._dossierStatesMapService.get$(this.#dossierTemplateId).pipe(
|
||||
tap(states => this.entitiesService.setEntities(states)),
|
||||
map(states => this.#chartData(states)),
|
||||
);
|
||||
}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
@ -63,7 +60,7 @@ export class DossierStatesListingScreenComponent extends ListingComponent<Dossie
|
||||
column: 'rank',
|
||||
order: SortingOrders.asc,
|
||||
});
|
||||
await this.#loadData();
|
||||
await firstValueFrom(this._dossierStatesService.loadAllForTemplate(this.#dossierTemplateId));
|
||||
}
|
||||
|
||||
openAddEditStateDialog($event: MouseEvent, dossierState?: IDossierState) {
|
||||
@ -71,66 +68,24 @@ export class DossierStatesListingScreenComponent extends ListingComponent<Dossie
|
||||
dossierState,
|
||||
dossierTemplateId: this.#dossierTemplateId,
|
||||
};
|
||||
this._dialogService.openDialog('addEditDossierState', $event, data, async (newValue: IDossierState) => {
|
||||
await this.#createNewDossierStateAndRefreshView(newValue);
|
||||
});
|
||||
this._dialogService.openDialog('addEditDossierState', $event, data);
|
||||
}
|
||||
|
||||
openConfirmDeleteStateDialog($event: MouseEvent, dossierState: IDossierState) {
|
||||
const templateId = this.#dossierTemplateId;
|
||||
openConfirmDeleteStateDialog($event: MouseEvent, dossierState: DossierState) {
|
||||
const data = {
|
||||
toBeDeletedState: dossierState,
|
||||
otherStates: this.entitiesService.all.filter(state => state.dossierStatusId !== dossierState.dossierStatusId),
|
||||
otherStates: this.entitiesService.all.filter(state => state.id !== dossierState.id),
|
||||
dossierCount: dossierState.dossierCount,
|
||||
};
|
||||
this._dialogService.openDialog('deleteDossierState', $event, data, async (value: string | true) => {
|
||||
if (value) {
|
||||
if (typeof value === 'string') {
|
||||
await firstValueFrom(this.dossierStateService.deleteAndReplace(dossierState.dossierStatusId, value));
|
||||
} else {
|
||||
await firstValueFrom(this.dossierStateService.delete(dossierState.dossierStatusId));
|
||||
}
|
||||
}
|
||||
|
||||
await firstValueFrom(this._dossierTemplatesService.refreshDossierTemplate(templateId));
|
||||
await this.#loadData();
|
||||
});
|
||||
this._dialogService.openDialog('deleteDossierState', $event, data);
|
||||
}
|
||||
|
||||
async #createNewDossierStateAndRefreshView(newValue: IDossierState): Promise<void> {
|
||||
this._loadingService.start();
|
||||
await firstValueFrom(this.dossierStateService.updateDossierState(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._dossierTemplatesService.refreshDossierTemplate(this.#dossierTemplateId));
|
||||
await this.#loadData();
|
||||
}
|
||||
|
||||
async #loadData(): Promise<void> {
|
||||
this._loadingService.start();
|
||||
// TODO: Move this in service; dossiers states service should be a mapping service
|
||||
await firstValueFrom(this._activeDossiersService.loadAll());
|
||||
|
||||
try {
|
||||
const dossierStates = this.dossierStateService.all.filter(d => d.dossierTemplateId === this.#dossierTemplateId);
|
||||
this.#setStatesCount(dossierStates);
|
||||
this.chartData = dossierStates.map(state => ({
|
||||
value: state.dossierCount,
|
||||
label: state.name,
|
||||
key: state.name,
|
||||
color: state.color,
|
||||
}));
|
||||
|
||||
this.entitiesService.setEntities(dossierStates || []);
|
||||
} catch (e) {}
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
#setStatesCount(dossierStates: DossierState[]): void {
|
||||
dossierStates.forEach(state => (state.dossierCount = this._activeDossiersService.getCountWithState(state.dossierStatusId)));
|
||||
#chartData(states: DossierState[]): DoughnutChartConfig[] {
|
||||
return states.map(state => ({
|
||||
value: state.dossierCount,
|
||||
label: state.name,
|
||||
key: state.name,
|
||||
color: state.color,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,5 +9,5 @@
|
||||
</div>
|
||||
|
||||
<div class="cell">
|
||||
<redaction-dossier-status [dossier]="dossier"></redaction-dossier-status>
|
||||
<redaction-dossier-state [dossier]="dossier"></redaction-dossier-state>
|
||||
</div>
|
||||
|
||||
@ -10,7 +10,7 @@ export class ConfigService {
|
||||
{ label: _('archived-dossiers-listing.table-col-names.name'), sortByKey: 'searchKey', width: '2fr' },
|
||||
{ label: _('archived-dossiers-listing.table-col-names.last-modified'), sortByKey: 'archivedTime' },
|
||||
{ label: _('archived-dossiers-listing.table-col-names.owner'), class: 'user-column' },
|
||||
{ label: _('archived-dossiers-listing.table-col-names.dossier-status'), class: 'flex-end', width: '2fr' },
|
||||
{ label: _('archived-dossiers-listing.table-col-names.dossier-state'), class: 'flex-end', width: '2fr' },
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
|
||||
<div class="flex fields-container">
|
||||
<div class="iqser-input-group w-300">
|
||||
<label translate="edit-dossier-dialog.general-info.form.dossier-status.label"></label>
|
||||
<label translate="edit-dossier-dialog.general-info.form.dossier-state.label"></label>
|
||||
<mat-select [placeholder]="statusPlaceholder" formControlName="dossierStatusId">
|
||||
<mat-option *ngFor="let stateId of states" [value]="stateId">
|
||||
<div class="flex-align-items-center">
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import * as moment from 'moment';
|
||||
import { Dossier, DossierState, IDossierRequest, IDossierTemplate } from '@red/domain';
|
||||
import { Dossier, IDossierRequest, IDossierTemplate } from '@red/domain';
|
||||
import { EditDossierSaveResult, EditDossierSectionInterface } from '../edit-dossier-section.interface';
|
||||
import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
@ -13,12 +13,12 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
||||
import { DossierStatsService } from '@services/dossiers/dossier-stats.service';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { DossierStateService } from '@services/entity-services/dossier-state.service';
|
||||
import { DOSSIER_TEMPLATE_ID } from '@utils/constants';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { DossiersService } from '@services/dossiers/dossiers.service';
|
||||
import { TrashDossiersService } from '@services/entity-services/trash-dossiers.service';
|
||||
import { ArchivedDossiersService } from '@services/dossiers/archived-dossiers.service';
|
||||
import { DossierStatesMapService } from '@services/entity-services/dossier-states-map.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-edit-dossier-general-info',
|
||||
@ -36,11 +36,10 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
|
||||
hasDueDate: boolean;
|
||||
dossierTemplates: IDossierTemplate[];
|
||||
states: string[];
|
||||
currentStatus: DossierState;
|
||||
|
||||
constructor(
|
||||
readonly permissionsService: PermissionsService,
|
||||
private readonly _dossierStateService: DossierStateService,
|
||||
private readonly _dossierStatesMapService: DossierStatesMapService,
|
||||
private readonly _dossierTemplatesService: DossierTemplatesService,
|
||||
private readonly _dossiersService: DossiersService,
|
||||
private readonly _trashDossiersService: TrashDossiersService,
|
||||
@ -80,20 +79,22 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
|
||||
return this.hasDueDate && this.form.get('dueDate').value === null;
|
||||
}
|
||||
|
||||
get #statusPlaceholder(): string {
|
||||
return this._translateService.instant(
|
||||
this.states.length === 1
|
||||
? 'edit-dossier-dialog.general-info.form.dossier-state.no-state-placeholder'
|
||||
: 'edit-dossier-dialog.general-info.form.dossier-state.placeholder',
|
||||
);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.states = [
|
||||
null,
|
||||
...this._dossierStateService.all.filter(s => s.dossierTemplateId === this.dossier.dossierTemplateId).map(s => s.id),
|
||||
];
|
||||
this.states = [null, ...this._dossierStatesMapService.get(this.dossier.dossierTemplateId).map(s => s.id)];
|
||||
this.statusPlaceholder = this.#statusPlaceholder;
|
||||
this.#filterInvalidDossierTemplates();
|
||||
this.form = this.#getForm();
|
||||
if (!this.permissionsService.canEditDossier(this.dossier)) {
|
||||
this.form.disable();
|
||||
}
|
||||
if (this.dossier.dossierStatusId) {
|
||||
this.currentStatus = this._dossierStateService.find(this.dossier.dossierStatusId);
|
||||
}
|
||||
this.hasDueDate = !!this.dossier.dueDate;
|
||||
}
|
||||
|
||||
@ -168,6 +169,17 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
|
||||
});
|
||||
}
|
||||
|
||||
getStateName(stateId: string): string {
|
||||
return (
|
||||
this._dossierStatesMapService.get(this.dossier.dossierTemplateId, stateId)?.name ||
|
||||
this._translateService.instant('edit-dossier-dialog.general-info.form.dossier-state.placeholder')
|
||||
);
|
||||
}
|
||||
|
||||
getStateColor(stateId: string): string {
|
||||
return this._dossierStatesMapService.get(this.dossier.dossierTemplateId, stateId).color;
|
||||
}
|
||||
|
||||
#getForm(): FormGroup {
|
||||
const formFieldWithArchivedCheck = value => ({ value, disabled: !this.dossier.isActive });
|
||||
return this._formBuilder.group({
|
||||
@ -185,25 +197,6 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
|
||||
});
|
||||
}
|
||||
|
||||
get #statusPlaceholder(): string {
|
||||
return this._translateService.instant(
|
||||
this.states.length === 1
|
||||
? 'edit-dossier-dialog.general-info.form.dossier-status.no-status-placeholder'
|
||||
: 'edit-dossier-dialog.general-info.form.dossier-status.placeholder',
|
||||
);
|
||||
}
|
||||
|
||||
getStateName(stateId: string): string {
|
||||
return (
|
||||
this._dossierStateService.find(stateId)?.name ||
|
||||
this._translateService.instant('edit-dossier-dialog.general-info.form.dossier-status.placeholder')
|
||||
);
|
||||
}
|
||||
|
||||
getStateColor(stateId: string): string {
|
||||
return this._dossierStateService.find(stateId).color;
|
||||
}
|
||||
|
||||
#filterInvalidDossierTemplates() {
|
||||
this.dossierTemplates = this._dossierTemplatesService.all.filter(r => {
|
||||
if (this.dossier?.dossierTemplateId === r.dossierTemplateId) {
|
||||
|
||||
@ -8,8 +8,8 @@ import { workflowFileStatusTranslations } from '../../../../../../translations/f
|
||||
import { TranslateChartService } from '@services/translate-chart.service';
|
||||
import { filter, map, switchMap } from 'rxjs/operators';
|
||||
import { DossierStatsService } from '@services/dossiers/dossier-stats.service';
|
||||
import { DossierStateService } from '@services/entity-services/dossier-state.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { DossierStatesMapService } from '@services/entity-services/dossier-states-map.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-dossiers-listing-details',
|
||||
@ -26,7 +26,7 @@ export class DossiersListingDetailsComponent {
|
||||
readonly activeDossiersService: ActiveDossiersService,
|
||||
private readonly _dossierStatsMap: DossierStatsService,
|
||||
private readonly _translateChartService: TranslateChartService,
|
||||
private readonly _dossierStateService: DossierStateService,
|
||||
private readonly _dossierStatesMapService: DossierStatesMapService,
|
||||
private readonly _translateService: TranslateService,
|
||||
) {
|
||||
this.documentsChartData$ = this.activeDossiersService.all$.pipe(
|
||||
@ -40,24 +40,12 @@ export class DossiersListingDetailsComponent {
|
||||
}
|
||||
|
||||
private _toDossierChartData(): DoughnutChartConfig[] {
|
||||
this._dossierStateService.all.forEach(
|
||||
state => (state.dossierCount = this.activeDossiersService.getCountWithState(state.dossierStatusId)),
|
||||
);
|
||||
const configArray: DoughnutChartConfig[] = [
|
||||
...this._dossierStateService.all
|
||||
.reduce((acc, { color, dossierCount, name }) => {
|
||||
const key = name + '-' + color;
|
||||
const item = acc.get(key) ?? Object.assign({}, { value: 0, label: name, color: color });
|
||||
|
||||
return acc.set(key, { ...item, value: item.value + dossierCount });
|
||||
}, new Map<string, DoughnutChartConfig>())
|
||||
.values(),
|
||||
];
|
||||
|
||||
const notAssignedLength = this.activeDossiersService.all.length - configArray.map(v => v.value).reduce((acc, val) => acc + val, 0);
|
||||
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: notAssignedLength,
|
||||
label: this._translateService.instant('edit-dossier-dialog.general-info.form.dossier-status.placeholder'),
|
||||
value: undefinedStateLength,
|
||||
label: this._translateService.instant('edit-dossier-dialog.general-info.form.dossier-state.placeholder'),
|
||||
color: '#E2E4E9',
|
||||
});
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
</div>
|
||||
|
||||
<div class="cell">
|
||||
<redaction-dossier-status [dossier]="dossier"></redaction-dossier-status>
|
||||
<redaction-dossier-state [dossier]="dossier"></redaction-dossier-state>
|
||||
|
||||
<redaction-dossiers-listing-actions [dossier]="dossier" [stats]="stats"></redaction-dossiers-listing-actions>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Injectable, TemplateRef } from '@angular/core';
|
||||
import { ButtonConfig, IFilterGroup, INestedFilter, keyChecker, NestedFilter, TableColumnConfig } from '@iqser/common-ui';
|
||||
import { Dossier, StatusSorter, User } from '@red/domain';
|
||||
import { Dossier, StatusSorter, User, WorkflowFileStatus } from '@red/domain';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
@ -10,7 +10,7 @@ import { dossierMemberChecker, dossierStateChecker, dossierTemplateChecker, Reda
|
||||
import { workloadTranslations } from '../../translations/workload-translations';
|
||||
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
||||
import { DossierStatsService } from '@services/dossiers/dossier-stats.service';
|
||||
import { DossierStateService } from '@services/entity-services/dossier-state.service';
|
||||
import { DossierStatesMapService } from '@services/entity-services/dossier-states-map.service';
|
||||
|
||||
@Injectable()
|
||||
export class ConfigService {
|
||||
@ -20,7 +20,7 @@ export class ConfigService {
|
||||
private readonly _userService: UserService,
|
||||
private readonly _dossierTemplatesService: DossierTemplatesService,
|
||||
private readonly _dossierStatsService: DossierStatsService,
|
||||
private readonly _dossierStateService: DossierStateService,
|
||||
private readonly _dossierStatesMapService: DossierStatesMapService,
|
||||
) {}
|
||||
|
||||
get tableConfig(): TableColumnConfig<Dossier>[] {
|
||||
@ -30,7 +30,7 @@ export class ConfigService {
|
||||
{ label: _('dossier-listing.table-col-names.needs-work') },
|
||||
{ label: _('dossier-listing.table-col-names.owner'), class: 'user-column' },
|
||||
{ label: _('dossier-listing.table-col-names.documents-status'), class: 'flex-end', width: 'auto' },
|
||||
{ label: _('dossier-listing.table-col-names.dossier-status'), class: 'flex-end' },
|
||||
{ label: _('dossier-listing.table-col-names.dossier-state'), class: 'flex-end' },
|
||||
];
|
||||
}
|
||||
|
||||
@ -66,6 +66,8 @@ export class ConfigService {
|
||||
const allDistinctDossierTemplates = new Set<string>();
|
||||
const allDistinctDossierStates = new Set<string>();
|
||||
|
||||
const stateToTemplateMap = new Map<string, string>();
|
||||
|
||||
const filterGroups: IFilterGroup[] = [];
|
||||
|
||||
entities?.forEach(entry => {
|
||||
@ -73,6 +75,7 @@ export class ConfigService {
|
||||
allDistinctDossierTemplates.add(entry.dossierTemplateId);
|
||||
if (entry.dossierStatusId) {
|
||||
allDistinctDossierStates.add(entry.dossierStatusId);
|
||||
stateToTemplateMap.set(entry.dossierStatusId, entry.dossierTemplateId);
|
||||
}
|
||||
const stats = this._dossierStatsService.get(entry.dossierId);
|
||||
|
||||
@ -100,13 +103,13 @@ export class ConfigService {
|
||||
id =>
|
||||
new NestedFilter({
|
||||
id: id,
|
||||
label: this._dossierStateService.find(id).name,
|
||||
label: this._dossierStatesMapService.get(stateToTemplateMap.get(id), id).name,
|
||||
}),
|
||||
);
|
||||
|
||||
filterGroups.push({
|
||||
slug: 'dossierStatesFilters',
|
||||
label: this._translateService.instant('filters.dossier-status'),
|
||||
label: this._translateService.instant('filters.dossier-state'),
|
||||
icon: 'red:status',
|
||||
hide: dossierStatesFilters.length <= 1,
|
||||
filters: dossierStatesFilters,
|
||||
@ -114,7 +117,7 @@ export class ConfigService {
|
||||
});
|
||||
|
||||
const statusFilters = [...allDistinctFileStatus].map(
|
||||
status =>
|
||||
(status: WorkflowFileStatus) =>
|
||||
new NestedFilter({
|
||||
id: status,
|
||||
label: this._translateService.instant(workflowFileStatusTranslations[status]),
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
<div class="flex-align-items-center dossier-state-container">
|
||||
<div class="dossier-state-text">
|
||||
{{ (dossierState$ | async)?.name || ('edit-dossier-dialog.general-info.form.dossier-state.placeholder' | translate) }}
|
||||
</div>
|
||||
<redaction-small-chip [color]="(dossierState$ | async)?.color || '#E2E4E9'"></redaction-small-chip>
|
||||
</div>
|
||||
@ -1,6 +1,6 @@
|
||||
@use 'variables';
|
||||
|
||||
.dossier-status-container {
|
||||
.dossier-state-container {
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
}
|
||||
@ -9,7 +9,7 @@ redaction-small-chip {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.dossier-status-text {
|
||||
.dossier-state-text {
|
||||
font-size: 13px;
|
||||
line-height: 16px;
|
||||
color: variables.$grey-1;
|
||||
@ -0,0 +1,21 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
|
||||
import { Dossier, DossierState } from '@red/domain';
|
||||
import { DossierStatesMapService } from '@services/entity-services/dossier-states-map.service';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-dossier-state [dossier]',
|
||||
templateUrl: './dossier-state.component.html',
|
||||
styleUrls: ['./dossier-state.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class DossierStateComponent implements OnChanges {
|
||||
@Input() dossier: Dossier;
|
||||
dossierState$: Observable<DossierState>;
|
||||
|
||||
constructor(private readonly _dossierStatesMapService: DossierStatesMapService) {}
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.dossierState$ = this._dossierStatesMapService.watch$(this.dossier.dossierTemplateId, this.dossier.dossierStatusId);
|
||||
}
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
<div class="flex-align-items-center dossier-status-container">
|
||||
<div class="dossier-status-text">
|
||||
{{ currentState?.name || ('edit-dossier-dialog.general-info.form.dossier-status.placeholder' | translate) }}
|
||||
</div>
|
||||
<redaction-small-chip [color]="currentState?.color || '#E2E4E9'"></redaction-small-chip>
|
||||
</div>
|
||||
@ -1,28 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnChanges, OnInit } from '@angular/core';
|
||||
import { Dossier, DossierState } from '@red/domain';
|
||||
import { DossierStateService } from '@services/entity-services/dossier-state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-dossier-status [dossier]',
|
||||
templateUrl: './dossier-status.component.html',
|
||||
styleUrls: ['./dossier-status.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class DossierStatusComponent implements OnInit, OnChanges {
|
||||
@Input() dossier: Dossier;
|
||||
currentState: DossierState;
|
||||
|
||||
constructor(private readonly _dossierStateService: DossierStateService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.#setState();
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.#setState();
|
||||
}
|
||||
|
||||
#setState(): void {
|
||||
this.currentState = this._dossierStateService.find(this.dossier.dossierStatusId);
|
||||
}
|
||||
}
|
||||
@ -27,7 +27,7 @@ import { TeamMembersComponent } from './components/team-members/team-members.com
|
||||
import { EditorComponent } from './components/editor/editor.component';
|
||||
import { ExpandableFileActionsComponent } from './components/expandable-file-actions/expandable-file-actions.component';
|
||||
import { ProcessingIndicatorComponent } from '@shared/components/processing-indicator/processing-indicator.component';
|
||||
import { DossierStatusComponent } from '@shared/components/dossier-status/dossier-status.component';
|
||||
import { DossierStateComponent } from '@shared/components/dossier-state/dossier-state.component';
|
||||
import { DossiersListingDossierNameComponent } from '@shared/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component';
|
||||
|
||||
const buttons = [FileDownloadBtnComponent, UserButtonComponent];
|
||||
@ -45,7 +45,7 @@ const components = [
|
||||
TeamMembersComponent,
|
||||
ExpandableFileActionsComponent,
|
||||
ProcessingIndicatorComponent,
|
||||
DossierStatusComponent,
|
||||
DossierStateComponent,
|
||||
DossiersListingDossierNameComponent,
|
||||
|
||||
...buttons,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { switchMap, tap } from 'rxjs/operators';
|
||||
import { timer } from 'rxjs';
|
||||
import { CHANGED_CHECK_INTERVAL } from '../../utils/constants';
|
||||
import { CHANGED_CHECK_INTERVAL } from '@utils/constants';
|
||||
import { DossiersService } from './dossiers.service';
|
||||
|
||||
export interface IDossiersStats {
|
||||
|
||||
@ -3,20 +3,20 @@ import { Dossier, DossierStats, IChangesDetails, IDossier, IDossierChanges, IDos
|
||||
import { combineLatest, EMPTY, forkJoin, Observable, of, Subject, throwError } from 'rxjs';
|
||||
import { catchError, filter, map, mapTo, pluck, switchMap, tap } from 'rxjs/operators';
|
||||
import { Injector } from '@angular/core';
|
||||
import { DossierStateService } from '../entity-services/dossier-state.service';
|
||||
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';
|
||||
|
||||
const DOSSIER_EXISTS_MSG = _('add-dossier-dialog.errors.dossier-already-exists');
|
||||
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(DossierStateService);
|
||||
protected readonly _dossierStateService = this._injector.get(DossierStatesService);
|
||||
protected readonly _toaster = this._injector.get(Toaster);
|
||||
|
||||
protected constructor(protected readonly _injector: Injector, protected readonly _path: string, readonly routerPath: string) {
|
||||
@ -26,7 +26,7 @@ export abstract class DossiersService extends EntitiesService<Dossier, IDossier>
|
||||
@Validate()
|
||||
createOrUpdate(@RequiredParam() dossier: IDossierRequest): Observable<Dossier> {
|
||||
const showToast = (error: HttpErrorResponse) => {
|
||||
this._toaster.error(error.status === HttpStatusCode.Conflict ? DOSSIER_EXISTS_MSG : GENERIC_MSG);
|
||||
this._toaster.error(error.status === HttpStatusCode.Conflict ? CONFLICT_MSG : GENERIC_MSG);
|
||||
return EMPTY;
|
||||
};
|
||||
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { EntitiesService, mapEach, RequiredParam, Validate } from '@iqser/common-ui';
|
||||
import { DossierState, IDossierState } from '@red/domain';
|
||||
import { forkJoin, Observable, switchMap } from 'rxjs';
|
||||
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
||||
import { defaultIfEmpty, map, tap } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class DossierStateService extends EntitiesService<DossierState, IDossierState> {
|
||||
constructor(protected readonly _injector: Injector, private readonly _dossierTemplatesService: DossierTemplatesService) {
|
||||
super(_injector, DossierState, 'dossier-status');
|
||||
}
|
||||
|
||||
@Validate()
|
||||
updateDossierState(@RequiredParam() body: IDossierState) {
|
||||
return this._post<unknown>(body, this._defaultModelPath);
|
||||
}
|
||||
|
||||
@Validate()
|
||||
loadAllForTemplate(@RequiredParam() templateId: string) {
|
||||
return this.loadAll(`${this._defaultModelPath}/dossier-template/${templateId}`);
|
||||
}
|
||||
|
||||
loadAllForAllTemplates(): Observable<DossierState[]> {
|
||||
return this._dossierTemplatesService.all$.pipe(
|
||||
mapEach(template => template.dossierTemplateId),
|
||||
mapEach(id => this.loadAllForTemplate(id)),
|
||||
switchMap(all => forkJoin(all).pipe(defaultIfEmpty([] as DossierState[][]))),
|
||||
map(value => value.flatMap(item => item)),
|
||||
tap(value => this.setEntities(value)),
|
||||
);
|
||||
}
|
||||
|
||||
@Validate()
|
||||
deleteAndReplace(@RequiredParam() dossierStatusId: string, @RequiredParam() replaceDossierStatusId: string) {
|
||||
const url = `${this._defaultModelPath}/${dossierStatusId}?replaceDossierStatusId=${replaceDossierStatusId}`;
|
||||
return this.delete({}, url);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { DossierState, IDossierState } from '@red/domain';
|
||||
import { EntitiesMapService } from '@iqser/common-ui';
|
||||
import { DOSSIER_TEMPLATE_ID } from '@utils/constants';
|
||||
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import { flatMap } from 'lodash';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class DossierStatesMapService extends EntitiesMapService<DossierState, IDossierState> {
|
||||
constructor() {
|
||||
super(DOSSIER_TEMPLATE_ID);
|
||||
}
|
||||
|
||||
get stats(): DoughnutChartConfig[] {
|
||||
const allStates = flatMap(Array.from(this._map.values()).map(obs => obs.value));
|
||||
return Array.from(
|
||||
allStates
|
||||
.reduce((acc, { color, name, dossierCount }) => {
|
||||
const key = name + '-' + color;
|
||||
const item = acc.get(key) ?? Object.assign({}, { value: 0, label: name, color: color });
|
||||
return acc.set(key, { ...item, value: item.value + dossierCount });
|
||||
}, new Map<string, DoughnutChartConfig>())
|
||||
.values(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
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 { catchError, defaultIfEmpty, tap } from 'rxjs/operators';
|
||||
import { DossierStatesMapService } from '@services/entity-services/dossier-states-map.service';
|
||||
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
|
||||
const CONFLICT_MSG = _('dossier-states-listing.error.conflict');
|
||||
const GENERIC_MSG = _('dossier-states-listing.error.generic');
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class DossierStatesService extends EntitiesService<DossierState, IDossierState> {
|
||||
constructor(
|
||||
protected readonly _injector: Injector,
|
||||
private readonly _toaster: Toaster,
|
||||
private readonly _dossierTemplatesService: DossierTemplatesService,
|
||||
private readonly _dossierStatesMapService: DossierStatesMapService,
|
||||
) {
|
||||
super(_injector, DossierState, 'dossier-status');
|
||||
}
|
||||
|
||||
@Validate()
|
||||
createOrUpdate(@RequiredParam() state: IDossierState): Observable<DossierState[]> {
|
||||
const showToast = (error: HttpErrorResponse) => {
|
||||
this._toaster.error(error.status === HttpStatusCode.Conflict ? CONFLICT_MSG : GENERIC_MSG);
|
||||
return EMPTY;
|
||||
};
|
||||
return this._post<unknown>(state, this._defaultModelPath).pipe(
|
||||
catchError(showToast),
|
||||
switchMap(() => this.loadAllForTemplate(state.dossierTemplateId)),
|
||||
);
|
||||
}
|
||||
|
||||
@Validate()
|
||||
loadAllForTemplate(@RequiredParam() templateId: string) {
|
||||
return this.loadAll(`${this._defaultModelPath}/dossier-template/${templateId}`).pipe(
|
||||
tap(states => this._dossierStatesMapService.set(templateId, states)),
|
||||
);
|
||||
}
|
||||
|
||||
loadAllForAllTemplates(): Observable<DossierState[][]> {
|
||||
return this._dossierTemplatesService.all$.pipe(
|
||||
mapEach(template => template.dossierTemplateId),
|
||||
mapEach(id => this.loadAllForTemplate(id)),
|
||||
switchMap(all => forkJoin(all).pipe(defaultIfEmpty([] as DossierState[][]))),
|
||||
);
|
||||
}
|
||||
|
||||
deleteState(dossierState: IDossierState, replaceDossierStatusId?: string): Observable<unknown> {
|
||||
const queryParams = replaceDossierStatusId ? [{ key: 'replaceDossierStatusId', value: replaceDossierStatusId }] : null;
|
||||
return super
|
||||
.delete(dossierState.dossierStatusId, this._defaultModelPath, queryParams)
|
||||
.pipe(switchMap(() => this.loadAllForTemplate(dossierState.dossierTemplateId)));
|
||||
}
|
||||
}
|
||||
@ -19,6 +19,10 @@ export class PermissionsService {
|
||||
return dossiersServiceResolver(this._injector);
|
||||
}
|
||||
|
||||
canPerformDossierStatesActions(user = this._userService.currentUser): boolean {
|
||||
return user.isAdmin;
|
||||
}
|
||||
|
||||
isReviewerOrApprover(file: File): boolean {
|
||||
const dossier = this._getDossier(file);
|
||||
return this.isFileAssignee(file) || this.isApprover(dossier);
|
||||
|
||||
@ -84,6 +84,7 @@
|
||||
"rank": ""
|
||||
},
|
||||
"save": "",
|
||||
"success": "",
|
||||
"title": ""
|
||||
},
|
||||
"add-edit-dossier-template": {
|
||||
@ -327,7 +328,7 @@
|
||||
"title": ""
|
||||
},
|
||||
"table-col-names": {
|
||||
"dossier-status": "",
|
||||
"dossier-state": "",
|
||||
"last-modified": "",
|
||||
"name": "",
|
||||
"owner": ""
|
||||
@ -460,9 +461,10 @@
|
||||
"delete": "",
|
||||
"delete-replace": "",
|
||||
"form": {
|
||||
"status": "",
|
||||
"status-placeholder": ""
|
||||
"state": "",
|
||||
"state-placeholder": ""
|
||||
},
|
||||
"success": "",
|
||||
"suggestion": "",
|
||||
"title": "",
|
||||
"warning": ""
|
||||
@ -769,7 +771,7 @@
|
||||
},
|
||||
"table-col-names": {
|
||||
"documents-status": "",
|
||||
"dossier-status": "",
|
||||
"dossier-state": "",
|
||||
"name": "Name",
|
||||
"needs-work": "Arbeitsvorrat",
|
||||
"owner": "Besitzer"
|
||||
@ -1065,9 +1067,9 @@
|
||||
"label": "Beschreibung",
|
||||
"placeholder": "Beschreibung eingeben"
|
||||
},
|
||||
"dossier-status": {
|
||||
"dossier-state": {
|
||||
"label": "",
|
||||
"no-status-placeholder": "",
|
||||
"no-state-placeholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"due-date": "Termin",
|
||||
@ -1359,7 +1361,7 @@
|
||||
"filters": {
|
||||
"assigned-people": "Beauftragt",
|
||||
"documents-status": "",
|
||||
"dossier-status": "",
|
||||
"dossier-state": "",
|
||||
"dossier-templates": "Regelsätze",
|
||||
"empty": "Leer",
|
||||
"filter-by": "Filter:",
|
||||
|
||||
@ -83,8 +83,9 @@
|
||||
"name-placeholder": "Enter Name",
|
||||
"rank": "Rank"
|
||||
},
|
||||
"save": "Save Status",
|
||||
"title": "{type, select, edit{Edit {name}} create{Create} other{}} Dossier Status"
|
||||
"save": "Save State",
|
||||
"success": "Successfully {type, select, edit{updated} create{created} other{}} the dossier state!",
|
||||
"title": "{type, select, edit{Edit {name}} create{Create} other{}} Dossier State"
|
||||
},
|
||||
"add-edit-dossier-template": {
|
||||
"error": {
|
||||
@ -327,7 +328,7 @@
|
||||
"title": "No archived dossiers match your current filters."
|
||||
},
|
||||
"table-col-names": {
|
||||
"dossier-status": "Dossier Status",
|
||||
"dossier-state": "Dossier State",
|
||||
"last-modified": "Archived Time",
|
||||
"name": "Name",
|
||||
"owner": "Owner"
|
||||
@ -460,12 +461,13 @@
|
||||
"delete": "Delete",
|
||||
"delete-replace": "Delete and Replace",
|
||||
"form": {
|
||||
"status": "Replace Status",
|
||||
"status-placeholder": "Choose another status"
|
||||
"state": "Replace State",
|
||||
"state-placeholder": "Choose another state"
|
||||
},
|
||||
"suggestion": "Would you like to replace the states of the Dossiers with another status?",
|
||||
"title": "Delete Dossier Status",
|
||||
"warning": "The {name} status is assigned to {count} {count, plural, one{Dossier} other{Dossiers}}."
|
||||
"success": "Successfully deleted state!",
|
||||
"suggestion": "Would you like to replace the dossiers' states with another state?",
|
||||
"title": "Delete Dossier State",
|
||||
"warning": "The {name} state is assigned to {count} {count, plural, one{dossier} other{dossiers}}."
|
||||
},
|
||||
"confirm-delete-users": {
|
||||
"cancel": "Keep {usersCount, plural, one{User} other{Users}}",
|
||||
@ -769,7 +771,7 @@
|
||||
},
|
||||
"table-col-names": {
|
||||
"documents-status": "Documents Status",
|
||||
"dossier-status": "Dossier Status",
|
||||
"dossier-state": "Dossier State",
|
||||
"name": "Name",
|
||||
"needs-work": "Workload",
|
||||
"owner": "Owner"
|
||||
@ -881,16 +883,16 @@
|
||||
"dossier-states": "Dossier States",
|
||||
"dossier-states-listing": {
|
||||
"action": {
|
||||
"delete": "Delete Status",
|
||||
"edit": "Edit Status"
|
||||
"delete": "Delete State",
|
||||
"edit": "Edit State"
|
||||
},
|
||||
"add-new": "New Status",
|
||||
"add-new": "New State",
|
||||
"chart": {
|
||||
"dossier-states": "{count, plural, one{Dossier State} other{Dossier States}}"
|
||||
},
|
||||
"error": {
|
||||
"conflict": "Dossier State with this name already exists!",
|
||||
"generic": "Failed to add Dossier State"
|
||||
"conflict": "Dossier state with this name already exists!",
|
||||
"generic": "Failed to save dossier state!"
|
||||
},
|
||||
"no-data": {
|
||||
"title": "There are no dossier states."
|
||||
@ -1065,9 +1067,9 @@
|
||||
"label": "Description",
|
||||
"placeholder": "Enter Description"
|
||||
},
|
||||
"dossier-status": {
|
||||
"label": "Dossier Status",
|
||||
"no-status-placeholder": "This dossier template has no states",
|
||||
"dossier-state": {
|
||||
"label": "Dossier State",
|
||||
"no-state-placeholder": "This dossier template has no states",
|
||||
"placeholder": "Undefined"
|
||||
},
|
||||
"due-date": "Due Date",
|
||||
@ -1359,7 +1361,7 @@
|
||||
"filters": {
|
||||
"assigned-people": "Assignee(s)",
|
||||
"documents-status": "Documents Status",
|
||||
"dossier-status": "Dossier Status",
|
||||
"dossier-state": "Dossier State",
|
||||
"dossier-templates": "Dossier Templates",
|
||||
"empty": "Empty",
|
||||
"filter-by": "Filter:",
|
||||
|
||||
@ -1,23 +1,25 @@
|
||||
import { IListable } from '@iqser/common-ui';
|
||||
import { Entity } from '@iqser/common-ui';
|
||||
import { IDossierState } from './dossier-state';
|
||||
|
||||
export class DossierState implements IDossierState, IListable {
|
||||
export class DossierState extends Entity<IDossierState> {
|
||||
readonly description: string;
|
||||
readonly dossierStatusId: string;
|
||||
readonly dossierTemplateId: string;
|
||||
readonly name: string;
|
||||
readonly color: string;
|
||||
readonly rank?: number;
|
||||
dossierCount?: number;
|
||||
readonly routerLink = undefined;
|
||||
readonly dossierCount: number;
|
||||
|
||||
constructor(dossierState: IDossierState) {
|
||||
super(dossierState);
|
||||
this.description = dossierState.description;
|
||||
this.dossierStatusId = dossierState.dossierStatusId;
|
||||
this.dossierTemplateId = dossierState.dossierTemplateId;
|
||||
this.name = dossierState.name;
|
||||
this.color = dossierState.color;
|
||||
this.dossierCount = dossierState.dossierCount;
|
||||
this.rank = dossierState.rank;
|
||||
this.dossierCount = dossierState.dossierCount || 0;
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user