wip states screen

This commit is contained in:
Edi Cziszter 2022-02-02 14:13:27 +02:00
parent 9e94ecb3b6
commit 4c67355bb4
21 changed files with 396 additions and 42 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, AppStateGuard],
},
},
{
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,9 @@ 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';
const dialogs = [
AddEditDossierTemplateDialogComponent,
@ -95,8 +98,17 @@ const components = [
];
@NgModule({
declarations: [...components],
declarations: [...components, DossierStatesListingScreenComponent, AddEditDossierStateDialogComponent],
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.description"></label>
<input
[placeholder]="'add-edit-dossier-state.form.description-placeholder' | translate"
class="hex-color-input"
formControlName="description"
name="description"
type="text"
/>
<div
(colorPickerChange)="form.get('description').setValue($event)"
[colorPicker]="form.get('description').value"
[cpOutputFormat]="'hex'"
[style.background]="form.get('description').value"
class="input-icon"
>
<mat-icon
*ngIf="!form.get('description').value || form.get('description').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 '../../../../../../../../libs/red-domain/src/lib/dossier-state';
interface DialogData {
dossierState: IDossierState;
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) public data: DialogData,
) {
super(_injector, _dialogRef);
this.form = this._getForm(data.dossierState);
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);
}
private _getForm(dossierState: IDossierState): FormGroup {
return this._formBuilder.group({
name: [dossierState?.name, Validators.required],
description: [dossierState?.description, Validators.required],
});
}
}

View File

@ -0,0 +1,78 @@
<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>
</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 class="dossier-state-square" [style.background-color]="state.description"></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>
</div>
</div>
</div>
</ng-template>

View File

@ -0,0 +1,14 @@
@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;
}

View File

@ -0,0 +1,84 @@
import { ChangeDetectionStrategy, Component, forwardRef, Injector, OnDestroy, OnInit } from '@angular/core';
import {
CircleButtonTypes,
DefaultListingServices,
IconButtonTypes,
ListingComponent,
LoadingService,
TableColumnConfig,
} from '../../../../../../../../libs/common-ui/src';
import { DossierState, IDossierState } from '../../../../../../../../libs/red-domain/src/lib/dossier-state';
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 { DossierTemplatesService } from '../../../../services/entity-services/dossier-templates.service';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { UserService } from '../../../../services/user.service';
import { AppStateService } from '../../../../state/app-state.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 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' },
];
constructor(
protected readonly _injector: Injector,
private readonly _loadingService: LoadingService,
private readonly _dossiersService: DossiersService,
private readonly _dossierStateService: DossierStateService,
private readonly _dossierTemplatesService: DossierTemplatesService,
private readonly _dialogService: AdminDialogService,
private readonly _userService: UserService,
private readonly _appStateService: AppStateService,
) {
super(_injector);
}
async ngOnInit(): Promise<void> {
await this._loadData();
}
openAddEditStateDialog($event: MouseEvent, dossierState?: IDossierState) {
const data = {
dossierState,
dossierTemplateId: this._dossierTemplatesService.activeDossierTemplateId,
};
this._dialogService.openDialog('addEditDossierState', $event, data, async (newValue: IDossierState) => {
await firstValueFrom(this._dossierStateService.setDossierState(newValue));
await this._appStateService.refreshDossierTemplate(this._dossierTemplatesService.activeDossierTemplateId);
await this._loadData();
});
}
private async _loadData() {
this._loadingService.start();
try {
const templateId = this._dossierTemplatesService.activeDossierTemplateId;
const dossierStates = await firstValueFrom(this._dossierStateService.loadAll(`dossier-status/dossier-template/${templateId}`));
const dossiers = this._dossiersService.all;
dossierStates.forEach(state => {
state.dossierCount = dossiers.filter(
dossier => dossier.dossierStatusId === state.dossierStatusId && dossier.dossierTemplateId === templateId,
).length;
});
this.entitiesService.setEntities(dossierStates || []);
} catch (e) {}
this._loadingService.stop();
}
}

View File

@ -13,6 +13,7 @@ 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';
type DialogType =
| 'confirm'
@ -27,7 +28,9 @@ type DialogType =
| 'smtpAuthConfig'
| 'addEditDossierTemplate'
| 'addEditDossierAttribute'
| 'uploadDictionary';
| 'addEditJustification'
| 'uploadDictionary'
| 'addEditDossierState';
@Injectable()
export class AdminDialogService extends DialogService<DialogType> {
@ -82,6 +85,9 @@ export class AdminDialogService extends DialogService<DialogType> {
uploadDictionary: {
component: UploadDictionaryDialogComponent,
},
addEditDossierState: {
component: AddEditDossierStateDialogComponent,
},
};
constructor(protected readonly _dialog: MatDialog) {

View File

@ -0,0 +1,17 @@
import { Injectable, Injector } from '@angular/core';
import { EntitiesService, RequiredParam, Validate } from '../../../../../../libs/common-ui/src';
import { DossierState, IDossierState } from '../../../../../../libs/red-domain/src/lib/dossier-state';
@Injectable({
providedIn: 'root',
})
export class DossierStateService extends EntitiesService<DossierState, IDossierState> {
constructor(protected readonly _injector: Injector) {
super(_injector, DossierState, 'dossier-status');
}
@Validate()
setDossierState(@RequiredParam() body: IDossierState) {
return this._post<unknown>(body, this._defaultModelPath);
}
}

View File

@ -1,12 +0,0 @@
import { Injectable, Injector } from '@angular/core';
import { EntitiesService } from '../../../../../../libs/common-ui/src';
import { DossierStatus, IDossierStatus } from '../../../../../../libs/red-domain/src/lib/dossier-status';
@Injectable({
providedIn: 'root',
})
export class DossierStatusService extends EntitiesService<DossierStatus, IDossierStatus> {
constructor(protected readonly _injector: Injector) {
super(_injector, DossierStatus, 'dossier-status');
}
}

View File

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

View File

@ -88,6 +88,16 @@
"save": "Save Dossier Template",
"title": "{type, select, edit{Edit {name}} create{Create} other{}} Dossier Template"
},
"add-edit-dossier-state": {
"form": {
"name": "Status Name",
"name-placeholder": "Enter Name",
"description": "Hex Color",
"description-placeholder": "#"
},
"save": "Save Status",
"title": "{type, select, edit{Edit {name}} create{Create} other{}} Dossier Status"
},
"add-edit-file-attribute": {
"form": {
"column-header": "CSV Column Header",
@ -612,6 +622,29 @@
"title": "Introduce File Attributes"
},
"dossier": "Dossier",
"dossier-states": "Dossier States",
"dossier-states-listing": {
"action": {
"delete": "Delete Status",
"edit": "Edit Status"
},
"search": "Search...",
"table-col-names": {
"name": "Name",
"dossiers-count": "Dossiers Count"
},
"add-new": "New Status",
"table-header": {
"title": "{length} dossier {length, plural, one{state} other{states}}"
},
"no-data": {
"action": "New State",
"title": "There are no dossier states."
},
"no-match": {
"title": "No dossier states match your current filters."
}
},
"dossier-attribute-types": {
"date": "Date",
"image": "Image",

View File

@ -0,0 +1,26 @@
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;
dossierCount?: number;
constructor(dossierState: IDossierState) {
this.description = dossierState.description;
this.dossierStatusId = dossierState.dossierStatusId;
this.dossierTemplateId = dossierState.dossierTemplateId;
this.name = dossierState.name;
this.dossierCount = dossierState.dossierCount;
}
get id(): string {
return this.dossierStatusId;
}
get searchKey(): string {
return this.name;
}
}

View File

@ -1,6 +1,7 @@
export interface IDossierStatus {
export interface IDossierState {
description: string;
dossierStatusId: string;
dossierTemplateId: string;
name: string;
dossierCount?: number;
}

View File

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

View File

@ -1,24 +0,0 @@
import { IDossierStatus } from './dossier-status';
import { IListable } from '@iqser/common-ui';
export class DossierStatus implements IDossierStatus, IListable {
readonly description: string;
readonly dossierStatusId: string;
readonly dossierTemplateId: string;
readonly name: string;
constructor(dossierStatus: IDossierStatus) {
this.description = dossierStatus.description;
this.dossierStatusId = dossierStatus.dossierStatusId;
this.dossierTemplateId = dossierStatus.dossierTemplateId;
this.name = dossierStatus.name;
}
get id(): string {
return this.dossierStatusId;
}
get searchKey(): string {
return this.name;
}
}

View File

@ -1,2 +0,0 @@
export * from './dossier-status';
export * from './dossier-status.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>;