Audit screen
This commit is contained in:
parent
889def7465
commit
38c5035521
38
apps/red-ui/src/app/models/audit-model-wrapper.model.ts
Normal file
38
apps/red-ui/src/app/models/audit-model-wrapper.model.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { AuditModel } from '@redaction/red-ui-http';
|
||||
import { Listable } from '@iqser/common-ui';
|
||||
|
||||
export class AuditModelWrapper implements Listable {
|
||||
constructor(public auditModel: AuditModel) {}
|
||||
|
||||
get category(): string {
|
||||
return this.auditModel.category;
|
||||
}
|
||||
|
||||
get details(): any {
|
||||
return this.auditModel.details;
|
||||
}
|
||||
|
||||
get message(): string {
|
||||
return this.auditModel.message;
|
||||
}
|
||||
|
||||
get recordId(): string {
|
||||
return this.auditModel.recordId;
|
||||
}
|
||||
|
||||
get recordDate(): string {
|
||||
return this.auditModel.recordDate;
|
||||
}
|
||||
|
||||
get objectId(): string {
|
||||
return this.auditModel.objectId;
|
||||
}
|
||||
|
||||
get userId(): string {
|
||||
return this.auditModel.userId;
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this.auditModel.recordDate;
|
||||
}
|
||||
}
|
||||
@ -20,115 +20,100 @@
|
||||
</div>
|
||||
<div class="red-content-inner">
|
||||
<div class="content-container">
|
||||
<div class="header-item">
|
||||
<span class="all-caps-label">
|
||||
{{ 'audit-screen.table-header.title' | translate: { length: logs?.totalHits || 0 } }}
|
||||
</span>
|
||||
<div class="actions-wrapper">
|
||||
<redaction-pagination
|
||||
(pageChanged)="pageChanged($event)"
|
||||
[settings]="{ currentPage: logs?.page || 0, totalPages: totalPages }"
|
||||
></redaction-pagination>
|
||||
<div class="separator">·</div>
|
||||
<form [formGroup]="filterForm">
|
||||
<div class="iqser-input-group w-150 mr-20">
|
||||
<mat-form-field class="no-label">
|
||||
<mat-select formControlName="category">
|
||||
<mat-option *ngFor="let category of categories" [value]="category">
|
||||
{{ translations[category] | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="iqser-input-group w-150">
|
||||
<mat-form-field class="no-label">
|
||||
<mat-select formControlName="userId">
|
||||
<mat-select-trigger>
|
||||
<redaction-initials-avatar
|
||||
*ngIf="filterForm.get('userId').value !== ALL_USERS"
|
||||
[userId]="filterForm.get('userId').value"
|
||||
[withName]="true"
|
||||
size="small"
|
||||
></redaction-initials-avatar>
|
||||
<div *ngIf="filterForm.get('userId').value === ALL_USERS" [translate]="ALL_USERS"></div>
|
||||
</mat-select-trigger>
|
||||
<mat-option *ngFor="let userId of userIds" [value]="userId">
|
||||
<redaction-initials-avatar
|
||||
*ngIf="userId !== ALL_USERS"
|
||||
[userId]="userId"
|
||||
[withName]="true"
|
||||
size="small"
|
||||
></redaction-initials-avatar>
|
||||
<div *ngIf="userId === ALL_USERS" [translate]="ALL_USERS"></div>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="separator">·</div>
|
||||
<div class="iqser-input-group datepicker-wrapper mr-20">
|
||||
<input [matDatepicker]="fromPicker" formControlName="from" placeholder="dd/mm/yy" />
|
||||
<mat-datepicker-toggle [for]="fromPicker" matSuffix>
|
||||
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
|
||||
</mat-datepicker-toggle>
|
||||
<mat-datepicker #fromPicker></mat-datepicker>
|
||||
</div>
|
||||
|
||||
<div class="mr-20" translate="audit-screen.to"></div>
|
||||
|
||||
<div class="iqser-input-group datepicker-wrapper">
|
||||
<input [matDatepicker]="toPicker" formControlName="to" placeholder="dd/mm/yy" />
|
||||
<mat-datepicker-toggle [for]="toPicker" matSuffix>
|
||||
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
|
||||
</mat-datepicker-toggle>
|
||||
<mat-datepicker #toPicker></mat-datepicker>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-header" iqserSyncWidth="table-item">
|
||||
<iqser-table-column-name
|
||||
[label]="'audit-screen.table-col-names.message' | translate"
|
||||
column="message"
|
||||
></iqser-table-column-name>
|
||||
<iqser-table-column-name
|
||||
[label]="'audit-screen.table-col-names.date' | translate"
|
||||
column="date"
|
||||
></iqser-table-column-name>
|
||||
<iqser-table-column-name
|
||||
[label]="'audit-screen.table-col-names.user' | translate"
|
||||
class="user-column"
|
||||
column="user"
|
||||
></iqser-table-column-name>
|
||||
<iqser-table-column-name
|
||||
[label]="'audit-screen.table-col-names.category' | translate"
|
||||
column="category"
|
||||
></iqser-table-column-name>
|
||||
<div class="scrollbar-placeholder"></div>
|
||||
</div>
|
||||
|
||||
<iqser-empty-state
|
||||
*ngIf="!logs?.totalHits"
|
||||
[text]="'audit-screen.no-data.title' | translate"
|
||||
icon="red:document"
|
||||
></iqser-empty-state>
|
||||
|
||||
<cdk-virtual-scroll-viewport [itemSize]="80" iqserHasScrollbar>
|
||||
<div *cdkVirtualFor="let log of logs?.data" class="table-item">
|
||||
<div>
|
||||
{{ log.message }}
|
||||
</div>
|
||||
<div class="small-label">
|
||||
{{ log.recordDate | date: 'd MMM. yyyy, hh:mm a' }}
|
||||
</div>
|
||||
<div class="user-column">
|
||||
<redaction-initials-avatar [userId]="log.userId" [withName]="true" size="small"></redaction-initials-avatar>
|
||||
</div>
|
||||
<div [translate]="translations[log.category]"></div>
|
||||
<div class="scrollbar-placeholder"></div>
|
||||
</div>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
<iqser-table
|
||||
[headerTemplate]="headerTemplate"
|
||||
[itemSize]="80"
|
||||
[noDataIcon]="'red:document'"
|
||||
[noDataText]="'audit-screen.no-data.title' | translate"
|
||||
[totalSize]="logs?.totalHits || 0"
|
||||
>
|
||||
</iqser-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<ng-template #headerTemplate>
|
||||
<div class="actions-wrapper">
|
||||
<redaction-pagination
|
||||
(pageChanged)="pageChanged($event)"
|
||||
[settings]="{ currentPage: logs?.page || 0, totalPages: totalPages }"
|
||||
></redaction-pagination>
|
||||
<div class="separator">·</div>
|
||||
<form [formGroup]="filterForm">
|
||||
<div class="iqser-input-group w-150 mr-20">
|
||||
<mat-form-field class="no-label">
|
||||
<mat-select formControlName="category">
|
||||
<mat-option *ngFor="let category of categories" [value]="category">
|
||||
{{ translations[category] | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="iqser-input-group w-150">
|
||||
<mat-form-field class="no-label">
|
||||
<mat-select formControlName="userId">
|
||||
<mat-select-trigger>
|
||||
<redaction-initials-avatar
|
||||
*ngIf="filterForm.get('userId').value !== ALL_USERS"
|
||||
[userId]="filterForm.get('userId').value"
|
||||
[withName]="true"
|
||||
size="small"
|
||||
></redaction-initials-avatar>
|
||||
<div *ngIf="filterForm.get('userId').value === ALL_USERS" [translate]="ALL_USERS"></div>
|
||||
</mat-select-trigger>
|
||||
<mat-option *ngFor="let userId of userIds" [value]="userId">
|
||||
<redaction-initials-avatar
|
||||
*ngIf="userId !== ALL_USERS"
|
||||
[userId]="userId"
|
||||
[withName]="true"
|
||||
size="small"
|
||||
></redaction-initials-avatar>
|
||||
<div *ngIf="userId === ALL_USERS" [translate]="ALL_USERS"></div>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="separator">·</div>
|
||||
<div class="iqser-input-group datepicker-wrapper mr-20">
|
||||
<input [matDatepicker]="fromPicker" formControlName="from" placeholder="dd/mm/yy" />
|
||||
<mat-datepicker-toggle [for]="fromPicker" matSuffix>
|
||||
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
|
||||
</mat-datepicker-toggle>
|
||||
<mat-datepicker #fromPicker></mat-datepicker>
|
||||
</div>
|
||||
|
||||
<div class="mr-20" translate="audit-screen.to"></div>
|
||||
|
||||
<div class="iqser-input-group datepicker-wrapper">
|
||||
<input [matDatepicker]="toPicker" formControlName="to" placeholder="dd/mm/yy" />
|
||||
<mat-datepicker-toggle [for]="toPicker" matSuffix>
|
||||
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
|
||||
</mat-datepicker-toggle>
|
||||
<mat-datepicker #toPicker></mat-datepicker>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #messageTemplate let-log="entity">
|
||||
<div class="cell">
|
||||
{{ log.message }}
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #dateTemplate let-log="entity">
|
||||
<div class="small-label cell">
|
||||
{{ log.recordDate | date: 'd MMM. yyyy, hh:mm a' }}
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #userTemplate let-log="entity">
|
||||
<div class="user-column cell">
|
||||
<redaction-initials-avatar [userId]="log.userId" [withName]="true" size="small"></redaction-initials-avatar>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #categoryTemplate let-log="entity">
|
||||
<div [translate]="translations[log.category]" class="cell"></div>
|
||||
</ng-template>
|
||||
|
||||
@ -1,44 +1,24 @@
|
||||
.content-container {
|
||||
.header-item {
|
||||
justify-content: space-between;
|
||||
:host ::ng-deep iqser-table iqser-table-header .header-item {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.actions-wrapper,
|
||||
form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.iqser-input-group {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
.actions-wrapper,
|
||||
form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.iqser-input-group {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
.separator {
|
||||
margin: 0 20px;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.mr-20 {
|
||||
margin-right: 20px;
|
||||
}
|
||||
.separator {
|
||||
margin: 0 20px;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
cdk-virtual-scroll-viewport {
|
||||
::ng-deep.cdk-virtual-scroll-content-wrapper {
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr 11px;
|
||||
|
||||
.table-item {
|
||||
> div {
|
||||
padding: 0 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.has-scrollbar:hover {
|
||||
::ng-deep.cdk-virtual-scroll-content-wrapper {
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
}
|
||||
}
|
||||
.mr-20 {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,41 +1,49 @@
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { Component, forwardRef, Injector, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { AuditControllerService, AuditResponse, AuditSearchRequest } from '@redaction/red-ui-http';
|
||||
import { AuditControllerService, AuditModel, AuditResponse, AuditSearchRequest } from '@redaction/red-ui-http';
|
||||
import { Moment } from 'moment';
|
||||
import { applyIntervalConstraints } from '@utils/date-inputs-utils';
|
||||
import { AutoUnsubscribe, LoadingService } from '@iqser/common-ui';
|
||||
import { DefaultListingServices, KeysOf, ListingComponent, LoadingService, TableColumnConfig } from '@iqser/common-ui';
|
||||
import { auditCategoriesTranslations } from '../../translations/audit-categories-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { AuditModelWrapper } from '../../../../models/audit-model-wrapper.model';
|
||||
|
||||
const PAGE_SIZE = 50;
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-audit-screen',
|
||||
templateUrl: './audit-screen.component.html',
|
||||
styleUrls: ['./audit-screen.component.scss']
|
||||
styleUrls: ['./audit-screen.component.scss'],
|
||||
providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => AuditScreenComponent) }]
|
||||
})
|
||||
export class AuditScreenComponent extends AutoUnsubscribe implements OnDestroy {
|
||||
export class AuditScreenComponent extends ListingComponent<AuditModelWrapper> implements OnDestroy, OnInit {
|
||||
readonly ALL_CATEGORIES = 'allCategories';
|
||||
readonly ALL_USERS = _('audit-screen.all-users');
|
||||
readonly translations = auditCategoriesTranslations;
|
||||
readonly currentUser = this._userService.currentUser;
|
||||
|
||||
@ViewChild('messageTemplate', { static: true }) messageTemplate: TemplateRef<never>;
|
||||
@ViewChild('dateTemplate', { static: true }) dateTemplate: TemplateRef<never>;
|
||||
@ViewChild('userTemplate', { static: true }) userTemplate: TemplateRef<never>;
|
||||
@ViewChild('categoryTemplate', { static: true }) categoryTemplate: TemplateRef<never>;
|
||||
filterForm: FormGroup;
|
||||
categories: string[] = [];
|
||||
userIds: Set<string>;
|
||||
logs: AuditResponse;
|
||||
|
||||
tableColumnConfigs: TableColumnConfig<AuditModelWrapper>[];
|
||||
readonly tableHeaderLabel = _('audit-screen.table-header.title');
|
||||
protected readonly _primaryKey: KeysOf<AuditModelWrapper> = 'recordDate';
|
||||
private _previousFrom: Moment;
|
||||
private _previousTo: Moment;
|
||||
|
||||
constructor(
|
||||
private readonly _userService: UserService,
|
||||
protected readonly _injector: Injector,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _auditControllerService: AuditControllerService
|
||||
) {
|
||||
super();
|
||||
super(_injector);
|
||||
this.filterForm = this._formBuilder.group({
|
||||
category: [this.ALL_CATEGORIES],
|
||||
userId: [this.ALL_USERS],
|
||||
@ -43,13 +51,11 @@ export class AuditScreenComponent extends AutoUnsubscribe implements OnDestroy {
|
||||
to: []
|
||||
});
|
||||
|
||||
this.addSubscription = this.filterForm.valueChanges.subscribe(value => {
|
||||
this.addSubscription = this.filterForm.valueChanges.subscribe(async value => {
|
||||
if (!this._updateDateFilters(value)) {
|
||||
this._fetchData();
|
||||
await this._fetchData();
|
||||
}
|
||||
});
|
||||
|
||||
this._fetchData();
|
||||
}
|
||||
|
||||
get totalPages(): number {
|
||||
@ -59,8 +65,35 @@ export class AuditScreenComponent extends AutoUnsubscribe implements OnDestroy {
|
||||
return Math.ceil(this.logs.totalHits / PAGE_SIZE);
|
||||
}
|
||||
|
||||
pageChanged(page: number) {
|
||||
this._fetchData(page);
|
||||
async pageChanged(page: number) {
|
||||
await this._fetchData(page);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this._configureTableColumns();
|
||||
await this._fetchData();
|
||||
}
|
||||
|
||||
private _configureTableColumns() {
|
||||
this.tableColumnConfigs = [
|
||||
{
|
||||
label: _('audit-screen.table-col-names.message'),
|
||||
template: this.messageTemplate
|
||||
},
|
||||
{
|
||||
label: _('audit-screen.table-col-names.date'),
|
||||
template: this.dateTemplate
|
||||
},
|
||||
{
|
||||
label: _('audit-screen.table-col-names.user'),
|
||||
class: 'user-column',
|
||||
template: this.userTemplate
|
||||
},
|
||||
{
|
||||
label: _('audit-screen.table-col-names.category'),
|
||||
template: this.categoryTemplate
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
private _updateDateFilters(value): boolean {
|
||||
@ -73,7 +106,7 @@ export class AuditScreenComponent extends AutoUnsubscribe implements OnDestroy {
|
||||
return false;
|
||||
}
|
||||
|
||||
private _fetchData(page?: number) {
|
||||
private async _fetchData(page?: number) {
|
||||
this._loadingService.start();
|
||||
const promises = [];
|
||||
const category = this.filterForm.get('category').value;
|
||||
@ -96,15 +129,16 @@ export class AuditScreenComponent extends AutoUnsubscribe implements OnDestroy {
|
||||
promises.push(this._auditControllerService.getAuditCategories().toPromise());
|
||||
promises.push(this._auditControllerService.searchAuditLog(logsRequestBody).toPromise());
|
||||
|
||||
Promise.all(promises).then(data => {
|
||||
this.categories = data[0].map(c => c.category);
|
||||
this.categories.splice(0, 0, this.ALL_CATEGORIES);
|
||||
this.logs = data[1];
|
||||
this.userIds = new Set<string>([this.ALL_USERS]);
|
||||
for (const id of this.logs.data.map(log => log.userId).filter(uid => !!uid)) {
|
||||
this.userIds.add(id);
|
||||
}
|
||||
this._loadingService.stop();
|
||||
});
|
||||
const data = await Promise.all(promises);
|
||||
this.categories = data[0].map(c => c.category);
|
||||
this.categories.splice(0, 0, this.ALL_CATEGORIES);
|
||||
this.logs = data[1];
|
||||
const entities = this.logs.data.map((log: AuditModel) => new AuditModelWrapper(log));
|
||||
this.entitiesService.setEntities(entities);
|
||||
this.userIds = new Set<string>([this.ALL_USERS]);
|
||||
for (const id of this.logs.data.map(log => log.userId).filter(uid => !!uid)) {
|
||||
this.userIds.add(id);
|
||||
}
|
||||
this._loadingService.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<div (click)="selectPage(currentPage - 1)" [class.disabled]="currentPage < 2" class="page" translate="pagination.previous"></div>
|
||||
<div (click)="selectPage(currentPage - 1)" [class.disabled]="currentPage < 1" class="page" translate="pagination.previous"></div>
|
||||
<span>|</span>
|
||||
<div
|
||||
(click)="selectPage(page)"
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
|
||||
const DISPLAYED_ITEMS = 5;
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-pagination',
|
||||
templateUrl: './pagination.component.html',
|
||||
styleUrls: ['./pagination.component.scss']
|
||||
styleUrls: ['./pagination.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class PaginationComponent {
|
||||
displayedPages: (number | string)[];
|
||||
@ -31,10 +30,6 @@ export class PaginationComponent {
|
||||
this._updatePagesArray();
|
||||
}
|
||||
|
||||
get allDisplayed(): boolean {
|
||||
return this.totalPages > DISPLAYED_ITEMS;
|
||||
}
|
||||
|
||||
selectPage(page: number | string) {
|
||||
if (page !== '...') {
|
||||
this.pageChanged.emit(page as number);
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 08222bdaeb1e16b37c75b66a2a49b0b3b96d9de7
|
||||
Subproject commit d6764126b4ad7f94b977b4c0f4e7a6ec31e6f374
|
||||
Loading…
x
Reference in New Issue
Block a user