RED-3800 Audit Info Dialog

This commit is contained in:
Timo Bejan 2022-12-14 12:37:22 +02:00
parent 8fb0c84c64
commit f8fe1799f5
14 changed files with 148 additions and 2 deletions

View File

@ -46,6 +46,7 @@ import { ConfigureCertificateDialogComponent } from './dialogs/configure-digital
import { PkcsSignatureConfigurationComponent } from './dialogs/configure-digital-signature-dialog/form/pkcs-signature-configuration/pkcs-signature-configuration.component'; import { PkcsSignatureConfigurationComponent } from './dialogs/configure-digital-signature-dialog/form/pkcs-signature-configuration/pkcs-signature-configuration.component';
import { KmsSignatureConfigurationComponent } from './dialogs/configure-digital-signature-dialog/form/kms-signature-configuration/kms-signature-configuration.component'; import { KmsSignatureConfigurationComponent } from './dialogs/configure-digital-signature-dialog/form/kms-signature-configuration/kms-signature-configuration.component';
import { import {
HumanizeCamelCasePipe,
HumanizePipe, HumanizePipe,
IqserButtonsModule, IqserButtonsModule,
IqserEmptyStatesModule, IqserEmptyStatesModule,
@ -59,6 +60,8 @@ import {
IqserUsersModule, IqserUsersModule,
} from '@iqser/common-ui'; } from '@iqser/common-ui';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { OverlayModule } from '@angular/cdk/overlay';
import { AuditInfoDialog } from './dialogs/audit-info-dialog/audit-info-dialog.component';
const dialogs = [ const dialogs = [
AddEditCloneDossierTemplateDialogComponent, AddEditCloneDossierTemplateDialogComponent,
@ -74,6 +77,7 @@ const dialogs = [
AddEditDossierStateDialogComponent, AddEditDossierStateDialogComponent,
ConfirmDeleteDossierStateDialogComponent, ConfirmDeleteDossierStateDialogComponent,
ConfigureCertificateDialogComponent, ConfigureCertificateDialogComponent,
AuditInfoDialog,
]; ];
const screens = [ const screens = [
@ -129,6 +133,8 @@ const components = [
IqserSharedModule, IqserSharedModule,
IqserHelpModeModule, IqserHelpModeModule,
IqserPermissionsModule, IqserPermissionsModule,
OverlayModule,
HumanizeCamelCasePipe,
], ],
}) })
export class AdminModule {} export class AdminModule {}

View File

@ -0,0 +1,17 @@
<section class="dialog">
<div [translate]="'audit-screen.audit-info-dialog.title'" class="dialog-header heading-l"></div>
<div class="dialog-content">
<div class="table output-data">
<div class="table-header">Key</div>
<div class="table-header">Value</div>
<ng-container *ngFor="let entry of data.auditEntry.details | keyvalue: originalOrder">
<div class="bold">{{ entry.key | humanize }}</div>
<div>{{ entry.value }}</div>
</ng-container>
</div>
</div>
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close"></iqser-circle-button>
</section>

View File

@ -0,0 +1,27 @@
.table {
display: grid;
grid-template-columns: repeat(2, 1fr);
> div {
padding: 8px 10px;
}
.bold {
font-weight: 600;
}
.value-content {
.value {
}
.actions {
}
}
.table-header {
margin: 10px 0;
border-bottom: 1px solid var(--iqser-separator);
background-color: var(--iqser-grey-2);
font-weight: 600;
}
}

View File

@ -0,0 +1,32 @@
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
import { BaseDialogComponent } from '@iqser/common-ui';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { IAudit } from '@red/domain';
import { DossierStatesService } from '@services/entity-services/dossier-states.service';
import { KeyValue } from '@angular/common';
interface DialogData {
readonly auditEntry: IAudit;
}
@Component({
selector: 'redaction-audit-info-dialog',
templateUrl: './audit-info-dialog.component.html',
styleUrls: ['./audit-info-dialog.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AuditInfoDialogComponent extends BaseDialogComponent {
readonly originalOrder = (a: KeyValue<string, string>, b: KeyValue<string, string>): number => 0;
constructor(
private readonly _dossierStatesService: DossierStatesService,
protected readonly _dialogRef: MatDialogRef<AuditInfoDialogComponent>,
@Inject(MAT_DIALOG_DATA) readonly data: DialogData,
) {
super(_dialogRef, false);
}
async save(): Promise<void> {
return;
}
}

View File

@ -11,6 +11,7 @@
[noDataText]="'audit-screen.no-data.title' | translate" [noDataText]="'audit-screen.no-data.title' | translate"
[tableColumnConfigs]="tableColumnConfigs" [tableColumnConfigs]="tableColumnConfigs"
[totalSize]="logs?.totalHits || 0" [totalSize]="logs?.totalHits || 0"
[emptyColumnWidth]="'2fr'"
></iqser-table> ></iqser-table>
<ng-template #headerTemplate> <ng-template #headerTemplate>
@ -94,5 +95,19 @@
</div> </div>
<div class="cell">{{ (translations[log.category] | translate) || log.category | humanize }}</div> <div class="cell">{{ (translations[log.category] | translate) || log.category | humanize }}</div>
<div class="cell">
<div class="action-buttons">
<iqser-circle-button
*ngIf="log.hasDetails"
(action)="openAuditDetails($event, log)"
cdkOverlayOrigin
#trigger="cdkOverlayOrigin"
[tooltip]="'audit-screen.action.info' | translate"
icon="red:info"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
</div>
</div>
</div> </div>
</ng-template> </ng-template>

View File

@ -17,3 +17,9 @@ form {
font-size: 16px; font-size: 16px;
opacity: 0.7; opacity: 0.7;
} }
.item-info {
background: var(--iqser-light);
border: 1px solid var(--iqser-grey-1);
padding: 20px;
}

View File

@ -2,6 +2,7 @@ import { Component, inject, OnDestroy, OnInit } from '@angular/core';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { applyIntervalConstraints } from '@utils/date-inputs-utils'; import { applyIntervalConstraints } from '@utils/date-inputs-utils';
import { import {
CircleButtonTypes,
getCurrentUser, getCurrentUser,
IqserPermissionsService, IqserPermissionsService,
ListingComponent, ListingComponent,
@ -17,6 +18,7 @@ import { firstValueFrom } from 'rxjs';
import { Dayjs } from 'dayjs'; import { Dayjs } from 'dayjs';
import { RouterHistoryService } from '@services/router-history.service'; import { RouterHistoryService } from '@services/router-history.service';
import { ROLES } from '@users/roles'; import { ROLES } from '@users/roles';
import { AdminDialogService } from '../../services/admin-dialog.service';
const PAGE_SIZE = 50; const PAGE_SIZE = 50;
@ -26,6 +28,7 @@ const PAGE_SIZE = 50;
providers: listingProvidersFactory(AuditScreenComponent), providers: listingProvidersFactory(AuditScreenComponent),
}) })
export class AuditScreenComponent extends ListingComponent<Audit> implements OnInit, OnDestroy { export class AuditScreenComponent extends ListingComponent<Audit> implements OnInit, OnDestroy {
readonly circleButtonTypes = CircleButtonTypes;
readonly ALL_CATEGORIES = 'allCategories'; readonly ALL_CATEGORIES = 'allCategories';
readonly ALL_USERS = _('audit-screen.all-users'); readonly ALL_USERS = _('audit-screen.all-users');
readonly translations = auditCategoriesTranslations; readonly translations = auditCategoriesTranslations;
@ -38,6 +41,7 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnI
categories: string[] = []; categories: string[] = [];
userIds: Set<string>; userIds: Set<string>;
logs: IAuditResponse; logs: IAuditResponse;
readonly tableColumnConfigs: TableColumnConfig<Audit>[] = [ readonly tableColumnConfigs: TableColumnConfig<Audit>[] = [
{ label: _('audit-screen.table-col-names.message') }, { label: _('audit-screen.table-col-names.message') },
{ label: _('audit-screen.table-col-names.date') }, { label: _('audit-screen.table-col-names.date') },
@ -52,6 +56,7 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnI
private readonly _formBuilder: UntypedFormBuilder, private readonly _formBuilder: UntypedFormBuilder,
private readonly _loadingService: LoadingService, private readonly _loadingService: LoadingService,
private readonly _auditService: AuditService, private readonly _auditService: AuditService,
private readonly _dialogService: AdminDialogService,
) { ) {
super(); super();
} }
@ -130,4 +135,8 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnI
} }
this._loadingService.stop(); this._loadingService.stop();
} }
openAuditDetails($event: MouseEvent, log: Audit) {
this._dialogService.openDialog('auditInfo', $event, { auditEntry: log });
}
} }

View File

@ -29,6 +29,7 @@ import { UserService } from '@users/user.service';
import { IDossierAttributeConfig, IFileAttributeConfig, IReportTemplate } from '@red/domain'; import { IDossierAttributeConfig, IFileAttributeConfig, IReportTemplate } from '@red/domain';
import { ReportTemplateService } from '@services/report-template.service'; import { ReportTemplateService } from '@services/report-template.service';
import { ConfigureCertificateDialogComponent } from '../dialogs/configure-digital-signature-dialog/configure-certificate-dialog.component'; import { ConfigureCertificateDialogComponent } from '../dialogs/configure-digital-signature-dialog/configure-certificate-dialog.component';
import { AuditInfoDialog } from '../dialogs/audit-info-dialog/audit-info-dialog.component';
type DialogType = type DialogType =
| 'confirm' | 'confirm'
@ -40,6 +41,7 @@ type DialogType =
| 'addEditUser' | 'addEditUser'
| 'smtpAuthConfig' | 'smtpAuthConfig'
| 'addEditCloneDossierTemplate' | 'addEditCloneDossierTemplate'
| 'auditInfo'
| 'addEditDossierAttribute' | 'addEditDossierAttribute'
| 'uploadDictionary' | 'uploadDictionary'
| 'addEditDossierState' | 'addEditDossierState'
@ -101,6 +103,9 @@ export class AdminDialogService extends DialogService<DialogType> {
component: ConfigureCertificateDialogComponent, component: ConfigureCertificateDialogComponent,
dialogConfig: { disableClose: false, maxHeight: '100vh' }, dialogConfig: { disableClose: false, maxHeight: '100vh' },
}, },
auditInfo: {
component: AuditInfoDialog,
},
}; };
constructor( constructor(

View File

@ -402,7 +402,13 @@
}, },
"audit": "Aktivitätenprotokoll", "audit": "Aktivitätenprotokoll",
"audit-screen": { "audit-screen": {
"action": {
"info": "Info"
},
"all-users": "Alle Benutzer", "all-users": "Alle Benutzer",
"audit-info-dialog": {
"title": "Audit Info"
},
"categories": { "categories": {
"all-categories": "Alle Bereiche", "all-categories": "Alle Bereiche",
"audit": "Aktivitätenprotokoll", "audit": "Aktivitätenprotokoll",
@ -1961,6 +1967,7 @@
"save": "", "save": "",
"undo": "" "undo": ""
}, },
"annotations": "",
"title": "" "title": ""
}, },
"rules-screen": { "rules-screen": {

View File

@ -402,7 +402,13 @@
}, },
"audit": "Audit", "audit": "Audit",
"audit-screen": { "audit-screen": {
"action": {
"info": "Info"
},
"all-users": "All Users", "all-users": "All Users",
"audit-info-dialog": {
"title": "Audit Info"
},
"categories": { "categories": {
"all-categories": "All Categories", "all-categories": "All Categories",
"audit": "Audit", "audit": "Audit",
@ -1961,6 +1967,7 @@
"save": "Save", "save": "Save",
"undo": "Undo" "undo": "Undo"
}, },
"annotations": "",
"title": "Structured Component Management" "title": "Structured Component Management"
}, },
"rules-screen": { "rules-screen": {

View File

@ -402,7 +402,13 @@
}, },
"audit": "Aktivitätenprotokoll", "audit": "Aktivitätenprotokoll",
"audit-screen": { "audit-screen": {
"action": {
"info": "Info"
},
"all-users": "Alle Benutzer", "all-users": "Alle Benutzer",
"audit-info-dialog": {
"title": "Audit Info"
},
"categories": { "categories": {
"all-categories": "Alle Bereiche", "all-categories": "Alle Bereiche",
"audit": "Aktivitätenprotokoll", "audit": "Aktivitätenprotokoll",
@ -1961,6 +1967,7 @@
"save": "", "save": "",
"undo": "" "undo": ""
}, },
"annotations": "",
"title": "" "title": ""
}, },
"rules-screen": { "rules-screen": {

View File

@ -402,7 +402,13 @@
}, },
"audit": "Audit", "audit": "Audit",
"audit-screen": { "audit-screen": {
"action": {
"info": "Info"
},
"all-users": "All Users", "all-users": "All Users",
"audit-info-dialog": {
"title": "Audit Info"
},
"categories": { "categories": {
"all-categories": "All Categories", "all-categories": "All Categories",
"audit": "Audit", "audit": "Audit",

View File

@ -4,11 +4,12 @@ import { IAudit } from './audit';
export class Audit implements IAudit, IListable { export class Audit implements IAudit, IListable {
readonly recordId: string; readonly recordId: string;
readonly category: string; readonly category: string;
readonly details?: unknown; readonly details?: { [key: string]: string };
readonly message: string; readonly message: string;
readonly objectId?: string; readonly objectId?: string;
readonly recordDate: string; readonly recordDate: string;
readonly userId: string; readonly userId: string;
readonly hasDetails: boolean;
constructor(audit: IAudit) { constructor(audit: IAudit) {
this.category = audit.category; this.category = audit.category;
@ -18,6 +19,7 @@ export class Audit implements IAudit, IListable {
this.recordDate = audit.recordDate; this.recordDate = audit.recordDate;
this.recordId = audit.recordId; this.recordId = audit.recordId;
this.userId = audit.userId; this.userId = audit.userId;
this.hasDetails = audit.details ? Object.keys(audit.details).length > 0 : false;
} }
get id(): string { get id(): string {

View File

@ -1,6 +1,6 @@
export interface IAudit { export interface IAudit {
readonly category: string; readonly category: string;
readonly details?: unknown; readonly details?: { [key: string]: string };
readonly message: string; readonly message: string;
readonly objectId?: string; readonly objectId?: string;
readonly recordDate: string; readonly recordDate: string;