Pull request #350: RED-3331

Merge in RED/ui from RED-3331 to master

* commit 'b7fce7525d0c1e924f1a92ab867c4f70f1a2ebd8':
  refactoired statusPlaceholder getter
  disabled states select if template has no states
  small improvements
  improvements in dossier-states-listing
  back to data
  fixed rebase stuff and chart pluralization
  added delete only functionality
  added separator between doughnut charts
  fixed doughnut charts
  fixed chart legend labels
  added states chart
  fixed loadstates when loading dossiers bugs
  load all states when loading dossiers and templates
  wip edit dossier status
  finished states screen, update dossier table
  wip states screen
  added dossier status model and service
This commit is contained in:
Eduard Cziszter 2022-02-15 12:06:37 +01:00
commit 3505dba7f3
43 changed files with 900 additions and 103 deletions

View File

@ -21,6 +21,7 @@ import { DossierTemplatesGuard } from '../../guards/dossier-templates.guard';
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';
const routes: Routes = [
{ path: '', redirectTo: 'dossier-templates', pathMatch: 'full' },
@ -114,6 +115,14 @@ const routes: Routes = [
routeGuards: [AuthGuard, RedRoleGuard],
},
},
{
path: 'dossier-states',
component: DossierStatesListingScreenComponent,
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [AuthGuard, RedRoleGuard],
},
},
{
path: 'default-colors',
component: DefaultColorsScreenComponent,

View File

@ -62,6 +62,7 @@ export class AdminSideNavComponent implements OnInit {
{ screen: 'watermark', label: _('watermark') },
{ screen: 'file-attributes', label: _('file-attributes') },
{ screen: 'dossier-attributes', label: _('dossier-attributes') },
{ screen: 'dossier-states', label: _('dossier-states') },
{ screen: 'reports', label: _('reports') },
{ screen: 'justifications', label: _('justifications') },
],

View File

@ -46,6 +46,10 @@ import { SmtpFormComponent } from './screens/general-config/smtp-form/smtp-form.
import { FileAttributesConfigurationsDialogComponent } from './dialogs/file-attributes-configurations-dialog/file-attributes-configurations-dialog.component';
import { SharedAdminModule } from './shared/shared-admin.module';
import { BaseDossierTemplateScreenComponent } from './base-dossier-templates-screen/base-dossier-template-screen.component';
import { DossierStatesListingScreenComponent } from './screens/dossier-states-listing/dossier-states-listing-screen.component';
import { AddEditDossierStateDialogComponent } from './dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component';
import { A11yModule } from '@angular/cdk/a11y';
import { ConfirmDeleteDossierStateDialogComponent } from './dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component';
const dialogs = [
AddEditDossierTemplateDialogComponent,
@ -95,8 +99,22 @@ const components = [
];
@NgModule({
declarations: [...components],
declarations: [
...components,
DossierStatesListingScreenComponent,
AddEditDossierStateDialogComponent,
ConfirmDeleteDossierStateDialogComponent,
],
providers: [AdminDialogService, AuditService, DigitalSignatureService, LicenseReportService, RulesService, SmtpConfigService],
imports: [CommonModule, SharedModule, AdminRoutingModule, SharedAdminModule, NgxChartsModule, ColorPickerModule, MonacoEditorModule],
imports: [
CommonModule,
SharedModule,
AdminRoutingModule,
SharedAdminModule,
NgxChartsModule,
ColorPickerModule,
MonacoEditorModule,
A11yModule,
],
})
export class AdminModule {}

View File

@ -0,0 +1,55 @@
<section class="dialog">
<div
[translateParams]="{
type: data.dossierState ? 'edit' : 'create',
name: data.dossierState?.name
}"
[translate]="'add-edit-dossier-state.title'"
class="dialog-header heading-l"
></div>
<form [formGroup]="form">
<div class="dialog-content flex">
<div class="iqser-input-group required w-300">
<label translate="add-edit-dossier-state.form.name"></label>
<input
[placeholder]="'add-edit-dossier-state.form.name-placeholder' | translate"
formControlName="name"
name="name"
type="text"
/>
</div>
<div class="iqser-input-group required">
<label translate="add-edit-dossier-state.form.color"></label>
<input
[placeholder]="'add-edit-dossier-state.form.color-placeholder' | translate"
class="hex-color-input"
formControlName="color"
name="color"
type="text"
/>
<div
(colorPickerChange)="form.get('color').setValue($event)"
[colorPicker]="form.get('color').value"
[cpOutputFormat]="'hex'"
[style.background]="form.get('color').value"
class="input-icon"
>
<mat-icon
*ngIf="!form.get('color').value || form.get('color').value?.length === 0"
svgIcon="red:color-picker"
></mat-icon>
</div>
</div>
</div>
<div class="dialog-actions">
<button (click)="save()" [disabled]="disabled" color="primary" mat-flat-button type="button">
{{ 'add-edit-dossier-state.save' | translate }}
</button>
</div>
</form>
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close"></iqser-circle-button>
</section>

View File

@ -0,0 +1,5 @@
.iqser-input-group:nth-child(2) {
width: fit-content;
margin-top: 0;
margin-left: 16px;
}

View File

@ -0,0 +1,45 @@
import { ChangeDetectionStrategy, Component, Inject, Injector } from '@angular/core';
import { BaseDialogComponent } from '../../../../../../../../libs/common-ui/src';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { IDossierState } from '@red/domain';
interface DialogData {
readonly dossierState: IDossierState;
readonly dossierTemplateId: string;
}
@Component({
selector: 'redaction-add-edit-dossier-state-dialog',
templateUrl: './add-edit-dossier-state-dialog.component.html',
styleUrls: ['./add-edit-dossier-state-dialog.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AddEditDossierStateDialogComponent extends BaseDialogComponent {
constructor(
private readonly _formBuilder: FormBuilder,
protected readonly _injector: Injector,
protected readonly _dialogRef: MatDialogRef<AddEditDossierStateDialogComponent>,
@Inject(MAT_DIALOG_DATA) readonly data: DialogData,
) {
super(_injector, _dialogRef);
this.form = this.#getForm();
this.initialFormValue = this.form.getRawValue();
}
save(): void {
const dossierState: IDossierState = {
dossierStatusId: this.data.dossierState?.dossierStatusId,
dossierTemplateId: this.data.dossierTemplateId,
...this.form.getRawValue(),
};
this._dialogRef.close(dossierState);
}
#getForm(): FormGroup {
return this._formBuilder.group({
name: [this.data.dossierState?.name, Validators.required],
color: [this.data.dossierState?.color, Validators.required],
});
}
}

View File

@ -0,0 +1,39 @@
<section class="dialog">
<div class="dialog-header heading-l">
{{ 'confirm-delete-dossier-state.title' | translate }}
</div>
<div class="dialog-content">
<div class="heading">{{ 'confirm-delete-dossier-state.warning' | translate: translateArgs }}</div>
<ng-container *ngIf="data.dossierCount !== 0">
<div class="replacement-suggestion">{{ 'confirm-delete-dossier-state.suggestion' | translate }}</div>
<form [formGroup]="form">
<div class="flex">
<div class="iqser-input-group w-300">
<label translate="confirm-delete-dossier-state.form.status"></label>
<mat-select
[placeholder]="'confirm-delete-dossier-state.form.status-placeholder' | translate"
formControlName="replaceDossierStatusId"
>
<mat-option>{{ 'confirm-delete-dossier-state.form.status-placeholder' | translate }}</mat-option>
<mat-option *ngFor="let state of data.otherStates" [value]="state.dossierStatusId">
{{ state.name }}
</mat-option>
</mat-select>
</div>
</div>
</form>
</ng-container>
</div>
<div class="dialog-actions">
<button (click)="dialogRef.close(afterCloseValue)" 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>
<iqser-circle-button class="dialog-close" icon="iqser:close" mat-dialog-close></iqser-circle-button>
</section>

View File

@ -0,0 +1,16 @@
@use 'variables';
.replacement-suggestion {
font-size: 13px;
line-height: 18px;
color: variables.$grey-1;
margin-bottom: 24px;
}
.dialog-header {
color: variables.$primary;
}
.heading {
margin-bottom: 8px;
}

View File

@ -0,0 +1,53 @@
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
import { IDossierState } from '@red/domain';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { FormBuilder, FormGroup } from '@angular/forms';
interface DialogData {
readonly toBeDeletedState: IDossierState;
readonly otherStates: IDossierState[];
readonly dossierCount: number;
}
@Component({
selector: 'redaction-confirm-delete-dossier-state-dialog',
templateUrl: './confirm-delete-dossier-state-dialog.component.html',
styleUrls: ['./confirm-delete-dossier-state-dialog.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ConfirmDeleteDossierStateDialogComponent {
readonly form: FormGroup;
constructor(
private readonly _formBuilder: FormBuilder,
readonly dialogRef: MatDialogRef<ConfirmDeleteDossierStateDialogComponent>,
@Inject(MAT_DIALOG_DATA) readonly data: DialogData,
) {
this.form = this.#getForm();
}
get translateArgs() {
return {
name: this.data.toBeDeletedState.name,
count: this.data.dossierCount,
};
}
get replaceDossierStatusId(): string {
return this.form.get('replaceDossierStatusId').value;
}
get label(): string {
return this.replaceDossierStatusId ? 'confirm-delete-dossier-state.delete-replace' : 'confirm-delete-dossier-state.delete';
}
get afterCloseValue(): string | true {
return this.replaceDossierStatusId ?? true;
}
#getForm(): FormGroup {
return this._formBuilder.group({
replaceDossierStatusId: [null],
});
}
}

View File

@ -0,0 +1,97 @@
<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>
<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
*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.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

@ -0,0 +1,18 @@
@use 'variables';
.dossier-state-square {
height: 16px;
width: 16px;
margin-right: 16px;
}
.state-name {
font-size: 16px;
font-weight: 600;
line-height: 20px;
color: variables.$grey-1;
}
.right-container {
padding: 50px 26px 0;
}

View File

@ -0,0 +1,126 @@
import { ChangeDetectionStrategy, Component, forwardRef, Injector, OnDestroy, OnInit } from '@angular/core';
import {
CircleButtonTypes,
DefaultListingServices,
IconButtonTypes,
ListingComponent,
LoadingService,
TableColumnConfig,
Toaster,
} from '../../../../../../../../libs/common-ui/src';
import { DossierState, IDossierState } from '@red/domain';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DossiersService } from '../../../../services/entity-services/dossiers.service';
import { DossierStateService } from '../../../../services/entity-services/dossier-state.service';
import { firstValueFrom } 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';
@Component({
templateUrl: './dossier-states-listing-screen.component.html',
styleUrls: ['./dossier-states-listing-screen.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
...DefaultListingServices,
{ provide: ListingComponent, useExisting: forwardRef(() => DossierStatesListingScreenComponent) },
],
})
export class DossierStatesListingScreenComponent extends ListingComponent<DossierState> implements OnInit, OnDestroy {
readonly iconButtonTypes = IconButtonTypes;
readonly circleButtonTypes = CircleButtonTypes;
readonly #dossierTemplateId: string;
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: 'searchKey' },
{ label: _('dossier-states-listing.table-col-names.dossiers-count'), sortByKey: 'dossierCount' },
];
chartData: DoughnutChartConfig[];
constructor(
protected readonly _injector: Injector,
private readonly _loadingService: LoadingService,
private readonly _dossiersService: DossiersService,
readonly dossierStateService: DossierStateService,
private readonly _dialogService: AdminDialogService,
private readonly _userService: UserService,
private readonly _toaster: Toaster,
private readonly _route: ActivatedRoute,
private readonly _dossierTemplatesService: DossierTemplatesService,
) {
super(_injector);
this.#dossierTemplateId = _route.snapshot.paramMap.get('dossierTemplateId');
}
ngOnInit(): Promise<void> {
return this.#loadData();
}
openAddEditStateDialog($event: MouseEvent, dossierState?: IDossierState) {
const data = {
dossierState,
dossierTemplateId: this.#dossierTemplateId,
};
this._dialogService.openDialog('addEditDossierState', $event, data, async (newValue: IDossierState) => {
await this.#createNewDossierStateAndRefreshView(newValue);
});
}
openConfirmDeleteStateDialog($event: MouseEvent, dossierState: IDossierState) {
const templateId = this.#dossierTemplateId;
const data = {
toBeDeletedState: dossierState,
otherStates: this.entitiesService.all.filter(state => state.dossierStatusId !== dossierState.dossierStatusId),
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 this._dossierTemplatesService.refreshDossierTemplate(templateId);
await this.#loadData();
});
}
async #createNewDossierStateAndRefreshView(newValue: IDossierState): Promise<void> {
this._loadingService.start();
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 this._dossierTemplatesService.refreshDossierTemplate(this.#dossierTemplateId);
await this.#loadData();
}
async #loadData(): Promise<void> {
this._loadingService.start();
await firstValueFrom(this._dossiersService.loadAll());
try {
const dossierStates = this.dossierStateService.all.filter(d => d.dossierTemplateId === this.#dossierTemplateId);
this.#setStatesCount(dossierStates);
this.chartData = this.dossierStateService.all.map(state => {
return { 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._dossiersService.getCountWithState(state.dossierStatusId)));
}
}

View File

@ -13,6 +13,8 @@ import { AddEditDossierAttributeDialogComponent } from '../dialogs/add-edit-doss
import { ConfirmationDialogComponent, DialogConfig, DialogService, largeDialogConfig } from '@iqser/common-ui';
import { UploadDictionaryDialogComponent } from '../dialogs/upload-dictionary-dialog/upload-dictionary-dialog.component';
import { FileAttributesConfigurationsDialogComponent } from '../dialogs/file-attributes-configurations-dialog/file-attributes-configurations-dialog.component';
import { AddEditDossierStateDialogComponent } from '../dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component';
import { ConfirmDeleteDossierStateDialogComponent } from '../dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component';
type DialogType =
| 'confirm'
@ -27,7 +29,9 @@ type DialogType =
| 'smtpAuthConfig'
| 'addEditDossierTemplate'
| 'addEditDossierAttribute'
| 'uploadDictionary';
| 'uploadDictionary'
| 'addEditDossierState'
| 'deleteDossierState';
@Injectable()
export class AdminDialogService extends DialogService<DialogType> {
@ -82,6 +86,12 @@ export class AdminDialogService extends DialogService<DialogType> {
uploadDictionary: {
component: UploadDictionaryDialogComponent,
},
addEditDossierState: {
component: AddEditDossierStateDialogComponent,
},
deleteDossierState: {
component: ConfirmDeleteDossierStateDialogComponent,
},
};
constructor(protected readonly _dialog: MatDialog) {

View File

@ -1,71 +1,89 @@
<form [formGroup]="form">
<div class="iqser-input-group required w-300">
<label translate="edit-dossier-dialog.general-info.form.name.label"></label>
<input
[placeholder]="'edit-dossier-dialog.general-info.form.name.placeholder' | translate"
formControlName="dossierName"
name="dossierName"
type="text"
/>
</div>
<div class="flex">
<div class="flex fields-container">
<div class="iqser-input-group required w-300">
<label translate="edit-dossier-dialog.general-info.form.name.label"></label>
<input
[placeholder]="'edit-dossier-dialog.general-info.form.name.placeholder' | translate"
formControlName="dossierName"
name="dossierName"
type="text"
/>
</div>
<div class="iqser-input-group required w-400">
<mat-form-field floatLabel="always">
<mat-label>{{ 'edit-dossier-dialog.general-info.form.template' | translate }}</mat-label>
<mat-select formControlName="dossierTemplateId" style="width: 100%">
<mat-option
*ngFor="let dossierTemplate of dossierTemplates"
[matTooltip]="dossierTemplate.description ? dossierTemplate.description : dossierTemplate.name"
[value]="dossierTemplate.dossierTemplateId"
matTooltipPosition="after"
<div class="iqser-input-group required w-400">
<mat-form-field floatLabel="always">
<mat-label>{{ 'edit-dossier-dialog.general-info.form.template' | translate }}</mat-label>
<mat-select formControlName="dossierTemplateId" style="width: 100%">
<mat-option
*ngFor="let dossierTemplate of dossierTemplates"
[matTooltip]="dossierTemplate.description ? dossierTemplate.description : dossierTemplate.name"
[value]="dossierTemplate.dossierTemplateId"
matTooltipPosition="after"
>
{{ dossierTemplate.name }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="iqser-input-group w-400">
<label translate="edit-dossier-dialog.general-info.form.description.label"></label>
<textarea
[placeholder]="'edit-dossier-dialog.general-info.form.description.placeholder' | translate"
formControlName="description"
iqserHasScrollbar
name="description"
rows="5"
type="text"
></textarea>
</div>
<div>
<mat-checkbox class="watermark" color="primary" formControlName="watermarkEnabled">
{{ 'edit-dossier-dialog.general-info.form.watermark' | translate }}
</mat-checkbox>
</div>
<div>
<mat-checkbox class="watermark-preview" color="primary" formControlName="watermarkPreviewEnabled">
{{ 'edit-dossier-dialog.general-info.form.watermark-preview' | translate }}
</mat-checkbox>
</div>
</div>
<div class="flex fields-container">
<div class="iqser-input-group w-300">
<label translate="edit-dossier-dialog.general-info.form.dossier-status.label"></label>
<mat-select [placeholder]="statusPlaceholder" [disabled]="states.length === 0" formControlName="dossierStatusId">
<mat-option *ngFor="let state of states" [value]="state.dossierStatusId">
<div class="flex-align-items-center">
<redaction-small-chip [color]="state.color"></redaction-small-chip>
<div>{{ state.name }}</div>
</div>
</mat-option>
</mat-select>
</div>
<div class="due-date">
<mat-checkbox
(change)="hasDueDate = !hasDueDate"
[checked]="hasDueDate"
[disabled]="!permissionsService.canEditDossier(dossier)"
class="filter-menu-checkbox"
color="primary"
>
{{ dossierTemplate.name }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
{{ 'edit-dossier-dialog.general-info.form.due-date' | translate }}
</mat-checkbox>
<div class="iqser-input-group w-400">
<label translate="edit-dossier-dialog.general-info.form.description.label"></label>
<textarea
[placeholder]="'edit-dossier-dialog.general-info.form.description.placeholder' | translate"
formControlName="description"
iqserHasScrollbar
name="description"
rows="5"
type="text"
></textarea>
</div>
<div>
<mat-checkbox class="watermark" color="primary" formControlName="watermarkEnabled">
{{ 'edit-dossier-dialog.general-info.form.watermark' | translate }}
</mat-checkbox>
</div>
<div>
<mat-checkbox class="watermark-preview" color="primary" formControlName="watermarkPreviewEnabled">
{{ 'edit-dossier-dialog.general-info.form.watermark-preview' | translate }}
</mat-checkbox>
</div>
<div class="due-date">
<mat-checkbox
(change)="hasDueDate = !hasDueDate"
[checked]="hasDueDate"
[disabled]="!permissionsService.canEditDossier(dossier)"
class="filter-menu-checkbox"
color="primary"
>
{{ 'edit-dossier-dialog.general-info.form.due-date' | translate }}
</mat-checkbox>
<div *ngIf="hasDueDate" class="iqser-input-group datepicker-wrapper">
<input [matDatepicker]="picker" formControlName="dueDate" placeholder="dd/mm/yy" />
<mat-datepicker-toggle [for]="picker" matSuffix>
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
</mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
<div *ngIf="hasDueDate" class="iqser-input-group datepicker-wrapper">
<input [matDatepicker]="picker" formControlName="dueDate" placeholder="dd/mm/yy" />
<mat-datepicker-toggle [for]="picker" matSuffix>
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
</mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</div>
</div>
</div>
</div>

View File

@ -23,3 +23,15 @@
border-top: none;
padding: 0;
}
.fields-container {
flex-direction: column;
&:first-child {
margin-right: 40px;
}
}
redaction-small-chip {
margin-right: 8px;
}

View File

@ -1,7 +1,7 @@
import { Component, Input, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import * as moment from 'moment';
import { Dossier, IDossierRequest, IDossierTemplate } from '@red/domain';
import { Dossier, DossierState, 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';
@ -14,7 +14,9 @@ import { DossiersService } from '@services/entity-services/dossiers.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierStatsService } from '@services/entity-services/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';
@Component({
selector: 'redaction-edit-dossier-general-info',
@ -29,9 +31,12 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
form: FormGroup;
hasDueDate: boolean;
dossierTemplates: IDossierTemplate[];
states: DossierState[];
currentStatus: DossierState;
constructor(
readonly permissionsService: PermissionsService,
private readonly _dossierStateService: DossierStateService,
private readonly _dossierTemplatesService: DossierTemplatesService,
private readonly _dossiersService: DossiersService,
private readonly _dossierStatsService: DossierStatsService,
@ -40,6 +45,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
private readonly _router: Router,
private readonly _editDossierDialogRef: MatDialogRef<EditDossierDialogComponent>,
private readonly _toaster: Toaster,
private readonly _translateService: TranslateService,
) {}
get changed(): boolean {
@ -67,12 +73,26 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
return this.hasDueDate && this.form.get('dueDate').value === null;
}
get statusPlaceholder(): string {
if (this.states.length === 0) {
return this._translateService.instant('edit-dossier-dialog.general-info.form.dossier-status.no-status-placeholder');
}
return (
this.currentStatus?.name ?? this._translateService.instant('edit-dossier-dialog.general-info.form.dossier-status.placeholder')
);
}
ngOnInit() {
this._filterInvalidDossierTemplates();
this.form = this._getForm();
this.#filterInvalidDossierTemplates();
this.form = this.#getForm();
if (!this.permissionsService.canEditDossier(this.dossier)) {
this.form.disable();
}
this.states = this._dossierStateService.all.filter(s => s.dossierTemplateId === this.dossier.dossierTemplateId);
if (this.dossier.dossierStatusId) {
this.currentStatus = this._dossierStateService.all.find(s => s.dossierStatusId === this.dossier.dossierStatusId);
}
this.hasDueDate = !!this.dossier.dueDate;
}
@ -80,6 +100,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
this.form.reset({
dossierName: this.dossier.dossierName,
dossierTemplateId: this.dossier.dossierTemplateId,
dossierStatusId: this.dossier.dossierStatusId,
description: this.dossier.description,
watermarkEnabled: this.dossier.watermarkEnabled,
watermarkPreviewEnabled: this.dossier.watermarkPreviewEnabled,
@ -96,6 +117,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
watermarkPreviewEnabled: this.form.get('watermarkPreviewEnabled').value,
dueDate: this.hasDueDate ? this.form.get('dueDate').value : undefined,
dossierTemplateId: this.form.get(DOSSIER_TEMPLATE_ID).value,
dossierStatusId: this.form.get('dossierStatusId').value,
} as IDossierRequest;
try {
await firstValueFrom(this._dossiersService.createOrUpdate(dossier));
@ -122,11 +144,11 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
this._dialogService.openDialog('confirm', null, data, async () => {
await firstValueFrom(this._dossiersService.delete(this.dossier));
this._editDossierDialogRef.close();
this._router.navigate(['main', 'dossiers']).then(() => this._notifyDossierDeleted());
this._router.navigate(['main', 'dossiers']).then(() => this.#notifyDossierDeleted());
});
}
private _getForm(): FormGroup {
#getForm(): FormGroup {
return this._formBuilder.group({
dossierName: [this.dossier.dossierName, Validators.required],
dossierTemplateId: [
@ -136,6 +158,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
},
Validators.required,
],
dossierStatusId: [this.dossier.dossierStatusId],
description: [this.dossier.description],
dueDate: [this.dossier.dueDate],
watermarkEnabled: [this.dossier.watermarkEnabled],
@ -143,11 +166,11 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
});
}
private _notifyDossierDeleted() {
#notifyDossierDeleted() {
this._toaster.success(_('edit-dossier-dialog.delete-successful'), { params: { dossierName: this.dossier.dossierName } });
}
private _filterInvalidDossierTemplates() {
#filterInvalidDossierTemplates() {
this.dossierTemplates = this._dossierTemplatesService.all.filter(r => {
if (this.dossier?.dossierTemplateId === r.dossierTemplateId) {
return true;

View File

@ -0,0 +1 @@
<iqser-status-bar *ngIf="stats" [configs]="statusBarConfig"></iqser-status-bar>

View File

@ -0,0 +1,24 @@
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
import { DossierStats, StatusSorter } from '../../../../../../../../../../libs/red-domain/src';
import { List, StatusBarConfig } from '../../../../../../../../../../libs/common-ui/src';
@Component({
selector: 'redaction-dossier-documents-status',
templateUrl: './dossier-documents-status.component.html',
styleUrls: ['./dossier-documents-status.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DossierDocumentsStatusComponent implements OnChanges {
@Input() stats: DossierStats;
statusBarConfig: List<StatusBarConfig<string>>;
private get _statusConfig(): List<StatusBarConfig<string>> {
const { fileCountPerWorkflowStatus } = this.stats;
const statuses = Object.keys(fileCountPerWorkflowStatus).sort(StatusSorter.byStatus);
return statuses.map(status => ({ length: fileCountPerWorkflowStatus[status], color: status }));
}
ngOnChanges(): void {
this.statusBarConfig = this._statusConfig;
}
}

View File

@ -1,5 +1,3 @@
<iqser-status-bar *ngIf="stats" [configs]="statusBarConfig"></iqser-status-bar>
<div (longPress)="forceReanalysisAction($event)" class="action-buttons" redactionLongPress>
<iqser-circle-button
(action)="openEditDossierDialog($event, dossier.dossierId)"

View File

@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/c
import { PermissionsService } from '@services/permissions.service';
import { CircleButtonTypes, List, StatusBarConfig } from '@iqser/common-ui';
import { UserService } from '@services/user.service';
import { Dossier, DossierStats, File, StatusSorter } from '@red/domain';
import { Dossier, DossierStats, File } from '@red/domain';
import { DossiersDialogService } from '../../../../services/dossiers-dialog.service';
import { LongPressEvent } from '@shared/directives/long-press.directive';
import { UserPreferenceService } from '@services/user-preference.service';
@ -39,14 +39,7 @@ export class DossiersListingActionsComponent implements OnChanges {
private readonly _userPreferenceService: UserPreferenceService,
) {}
private get _statusConfig(): List<StatusBarConfig<string>> {
const { fileCountPerWorkflowStatus } = this.stats;
const statuses = Object.keys(fileCountPerWorkflowStatus).sort(StatusSorter.byStatus);
return statuses.map(status => ({ length: fileCountPerWorkflowStatus[status], color: status }));
}
ngOnChanges() {
this.statusBarConfig = this._statusConfig;
this.files = this.filesMapService.get(this.dossier.dossierId);
this.displayReanalyseBtn = this.permissionsService.displayReanalyseBtn(this.dossier) && this.analysisForced;
}

View File

@ -26,7 +26,7 @@
</div>
</div>
<div>
<div class="right-chart">
<redaction-simple-doughnut-chart
*ngIf="documentsChartData$ | async as config"
[config]="config"

View File

@ -29,3 +29,7 @@
}
}
}
.right-chart {
border-left: 1px solid rgba(226, 228, 233, 0.9);
}

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.color });
});
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: '#E2E4E9',
});
// 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

@ -0,0 +1,13 @@
<ng-container *ngIf="dossier.dossierStatusId">
<div class="flex-align-items-center dossier-status-container">
<div class="dossier-status-text">{{ currentState.name }}</div>
<redaction-small-chip [color]="currentState.color"></redaction-small-chip>
</div>
</ng-container>
<ng-container *ngIf="!dossier.dossierStatusId">
<div class="flex-align-items-center dossier-status-container">
<div class="dossier-status-text">{{ 'edit-dossier-dialog.general-info.form.dossier-status.placeholder' | translate }}</div>
<redaction-small-chip [color]="'#E2E4E9'"></redaction-small-chip>
</div>
</ng-container>

View File

@ -0,0 +1,16 @@
@use 'variables';
.dossier-status-container {
justify-content: flex-end;
width: 100%;
}
redaction-small-chip {
margin-left: 8px;
}
.dossier-status-text {
font-size: 13px;
line-height: 16px;
color: variables.$grey-1;
}

View File

@ -0,0 +1,31 @@
import { ChangeDetectionStrategy, Component, Input, OnChanges, OnInit } from '@angular/core';
import { Dossier } from '../../../../../../../../../../libs/red-domain/src';
import { DossierStateService } from '../../../../../../services/entity-services/dossier-state.service';
import { DossierState } from '@red/domain';
@Component({
selector: 'redaction-dossiers-listing-status',
templateUrl: './dossiers-listing-status.component.html',
styleUrls: ['./dossiers-listing-status.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DossiersListingStatusComponent implements OnInit, OnChanges {
@Input() dossier: Dossier;
currentState: DossierState;
constructor(private readonly _dossierStateService: DossierStateService) {}
ngOnInit(): void {
this.#setState();
}
ngOnChanges(): void {
this.#setState();
}
#setState(): void {
if (this.dossier.dossierStatusId) {
this.currentState = this._dossierStateService.all.find(s => s.dossierStatusId === this.dossier.dossierStatusId);
}
}
}

View File

@ -12,6 +12,12 @@
</div>
<div class="cell status-container">
<redaction-dossier-documents-status [stats]="stats"></redaction-dossier-documents-status>
</div>
<div class="cell">
<redaction-dossiers-listing-status [dossier]="dossier"></redaction-dossiers-listing-status>
<redaction-dossiers-listing-actions [dossier]="dossier" [stats]="stats"></redaction-dossiers-listing-actions>
</div>
</ng-container>

View File

@ -14,15 +14,15 @@ export class TableItemComponent implements OnChanges {
@Input() dossier!: Dossier;
readonly stats$: Observable<DossierStats>;
private readonly _ngOnChanges$ = new BehaviorSubject<string>(undefined);
readonly #ngOnChanges$ = new BehaviorSubject<string>(undefined);
constructor(readonly dossierStatsService: DossierStatsService) {
this.stats$ = this._ngOnChanges$.pipe(switchMap(dossierId => this.dossierStatsService.watch$(dossierId)));
this.stats$ = this.#ngOnChanges$.pipe(switchMap(dossierId => this.dossierStatsService.watch$(dossierId)));
}
ngOnChanges() {
if (this.dossier) {
this._ngOnChanges$.next(this.dossier.dossierId);
this.#ngOnChanges$.next(this.dossier.dossierId);
}
}
}

View File

@ -26,7 +26,8 @@ export class ConfigService {
{ label: _('dossier-listing.table-col-names.name'), sortByKey: 'searchKey', width: '2fr' },
{ label: _('dossier-listing.table-col-names.needs-work') },
{ label: _('dossier-listing.table-col-names.owner'), class: 'user-column' },
{ label: _('dossier-listing.table-col-names.status'), class: 'flex-end', width: 'auto' },
{ label: _('dossier-listing.table-col-names.documents-status'), class: 'flex-end', width: 'auto' },
{ label: _('dossier-listing.table-col-names.dossier-status'), class: 'flex-end' },
];
}

View File

@ -12,6 +12,8 @@ import { ConfigService } from './config.service';
import { TableItemComponent } from './components/table-item/table-item.component';
import { SharedDossiersModule } from '../../shared/shared-dossiers.module';
import { DossierWorkloadColumnComponent } from './components/dossier-workload-column/dossier-workload-column.component';
import { DossiersListingStatusComponent } from './components/dossiers-listing-status/dossiers-listing-status.component';
import { DossierDocumentsStatusComponent } from './components/dossier-documents-status/dossier-documents-status.component';
const routes: Routes = [
{
@ -30,6 +32,8 @@ const routes: Routes = [
DossiersListingDossierNameComponent,
DossierWorkloadColumnComponent,
TableItemComponent,
DossiersListingStatusComponent,
DossierDocumentsStatusComponent,
],
providers: [ConfigService],
imports: [RouterModule.forChild(routes), CommonModule, SharedModule, SharedDossiersModule, IqserIconsModule, TranslateModule],

View File

@ -23,7 +23,7 @@ export class SimpleDoughnutChartComponent implements OnChanges, OnInit {
@Input() radius = 85;
@Input() strokeWidth = 20;
@Input() direction: 'row' | 'column' = 'column';
@Input() totalType: 'sum' | 'count' = 'sum';
@Input() totalType: 'sum' | 'count' | 'simpleLabel' = 'sum';
@Input() counterText: string;
@Input() filterKey = 'statusFilters';
filtersEnabled: boolean;
@ -96,7 +96,11 @@ export class SimpleDoughnutChartComponent implements OnChanges, OnInit {
}
getLabel({ label, value }: DoughnutChartConfig): string {
return this.totalType === 'sum' ? `${value} ${label}` : `${label} (${value} ${this.counterText})`;
return this.totalType === 'simpleLabel'
? `${label}`
: this.totalType === 'sum'
? `${value} ${label}`
: `${label} (${value} ${this.counterText})`;
}
selectValue(key: string): void {

View File

@ -0,0 +1,41 @@
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 { 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()
setDossierState(@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)),
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);
}
}

View File

@ -7,6 +7,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
import { CHANGED_CHECK_INTERVAL } from '@utils/constants';
import { DossierStateService } from '@services/entity-services/dossier-state.service';
export interface IDossiersStats {
totalPeople: number;
@ -37,6 +38,7 @@ export class DossiersService extends EntitiesService<Dossier, IDossier> {
private readonly _toaster: Toaster,
protected readonly _injector: Injector,
private readonly _dossierStatsService: DossierStatsService,
private readonly _dossierStateService: DossierStateService,
) {
super(_injector, Dossier, 'dossier');
@ -54,6 +56,7 @@ export class DossiersService extends EntitiesService<Dossier, IDossier> {
mapEach(entity => new Dossier(entity)),
/* Load stats before updating entities */
switchMap(dossiers => this._dossierStatsService.getFor(dossierIds(dossiers)).pipe(mapTo(dossiers))),
switchMap(dossiers => this._dossierStateService.loadAllForAllTemplates().pipe(mapTo(dossiers))),
tap(dossiers => this.setEntities(dossiers)),
);
}
@ -108,6 +111,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));
}

View File

@ -605,6 +605,7 @@
"title": "Datei-Attribute anlegen"
},
"dossier": "Dossier",
"dossier-states": "",
"dossier-attribute-types": {
"date": "Datum",
"image": "Bild",

View File

@ -8,7 +8,6 @@
"all": "All",
"none": "None"
},
"active": "Active",
"add-dossier-dialog": {
"actions": {
"save": "Save",
@ -72,6 +71,16 @@
"save": "Save Attribute",
"title": "{type, select, edit{Edit {name}} create{Add New} other{}} Dossier Attribute"
},
"add-edit-dossier-state": {
"form": {
"color": "Hex Color",
"color-placeholder": "#",
"name": "Status Name",
"name-placeholder": "Enter Name"
},
"save": "Save Status",
"title": "{type, select, edit{Edit {name}} create{Create} other{}} Dossier Status"
},
"add-edit-dossier-template": {
"error": {
"conflict": "Failed to create dossier template: a dossier template with the same name already exists.",
@ -399,6 +408,18 @@
}
},
"configurations": "Configurations",
"confirm-delete-dossier-state": {
"cancel": "Cancel",
"delete-replace": "Delete and Replace",
"delete": "Delete only",
"form": {
"status": "Replace Status",
"status-placeholder": "Choose another status"
},
"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}}."
},
"confirm-delete-file-attribute": {
"cancel": "Keep {type, select, single{Attribute} bulk{Attributes} other{}}",
"delete": "Delete {type, select, single{Attribute} bulk{Attributes} other{}}",
@ -696,10 +717,11 @@
"total-people": "Total users"
},
"table-col-names": {
"documents-status": "Documents Status",
"dossier-status": "Dossier Status",
"name": "Name",
"needs-work": "Workload",
"owner": "Owner",
"status": "Status"
"owner": "Owner"
},
"table-header": {
"title": "{length} active {length, plural, one{Dossier} other{Dossiers}}"
@ -803,6 +825,35 @@
"under-review": "Under Review",
"upload-files": "Drag & drop files anywhere..."
},
"dossier-states": "Dossier States",
"dossier-states-listing": {
"action": {
"delete": "Delete Status",
"edit": "Edit Status"
},
"add-new": "New Status",
"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"
},
"no-data": {
"title": "There are no dossier states."
},
"no-match": {
"title": "No dossier states match your current filters."
},
"search": "Search...",
"table-col-names": {
"dossiers-count": "Dossiers Count",
"name": "Name"
},
"table-header": {
"title": "{length} dossier {length, plural, one{state} other{states}}"
}
},
"dossier-template-info": "Info",
"dossier-template-info-screen": {
"created-by": "Created by",
@ -957,6 +1008,11 @@
"label": "Description",
"placeholder": "Enter Description"
},
"dossier-status": {
"label": "Dossier Status",
"no-status-placeholder": "This dossier template has no states",
"placeholder": "Undefined"
},
"due-date": "Due Date",
"name": {
"label": "Dossier Name",
@ -1633,8 +1689,8 @@
"no-time-left": "Time to restore already passed"
},
"toggle-auto-analysis-message": {
"success": "{toggleOperation} automatic processing.",
"error": "Something went wrong."
"error": "Something went wrong.",
"success": "{toggleOperation} automatic processing."
},
"top-bar": {
"navigation-items": {

@ -1 +1 @@
Subproject commit 77e22758239cf564965e82579e662416a6349010
Subproject commit f54374859ec75ea73921b9e6a934bd3747f9721d

View File

@ -19,3 +19,4 @@ export * from './lib/configuration';
export * from './lib/signature';
export * from './lib/legal-basis';
export * from './lib/dossier-stats';
export * from './lib/dossier-state';

View File

@ -0,0 +1,28 @@
import { IListable } from '@iqser/common-ui';
import { IDossierState } from './dossier-state';
export class DossierState implements IDossierState, IListable {
readonly description: string;
readonly dossierStatusId: string;
readonly dossierTemplateId: string;
readonly name: string;
readonly color: string;
dossierCount?: number;
constructor(dossierState: IDossierState) {
this.description = dossierState.description;
this.dossierStatusId = dossierState.dossierStatusId;
this.dossierTemplateId = dossierState.dossierTemplateId;
this.name = dossierState.name;
this.color = dossierState.color;
this.dossierCount = dossierState.dossierCount;
}
get id(): string {
return this.dossierStatusId;
}
get searchKey(): string {
return this.name;
}
}

View File

@ -0,0 +1,8 @@
export interface IDossierState {
description: string;
dossierStatusId: string;
dossierTemplateId: string;
name: string;
color: string;
dossierCount?: number;
}

View File

@ -0,0 +1,2 @@
export * from './dossier-state';
export * from './dossier-state.model';

View File

@ -11,6 +11,7 @@ export class Dossier implements IDossier, IListable {
readonly approverIds: List;
readonly reportTemplateIds: List;
readonly dossierName: string;
readonly dossierStatusId: string;
readonly date: string;
readonly dueDate?: string;
readonly description?: string;
@ -29,6 +30,7 @@ export class Dossier implements IDossier, IListable {
this.date = dossier.date;
this.description = dossier.description;
this.dossierName = dossier.dossierName;
this.dossierStatusId = dossier.dossierStatusId;
this.dossierTemplateId = dossier.dossierTemplateId;
this.downloadFileTypes = dossier.downloadFileTypes;
this.dueDate = dossier.dueDate;

View File

@ -7,6 +7,7 @@ export interface IDossier {
readonly date: string;
readonly description?: string;
readonly dossierId: string;
readonly dossierStatusId: string;
readonly dossierName: string;
readonly dossierTemplateId: string;
readonly downloadFileTypes?: List<DownloadFileType>;