Browse Source

Merge pull request #14262 from mtsymbarov-del/feat/audit-log-filters

Added filters dialog for audit log table
pull/14398/head
Vladyslav Prykhodko 11 months ago
committed by GitHub
parent
commit
ac1de1a361
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 68
      ui-ngx/src/app/core/http/audit-log.service.ts
  2. 61
      ui-ngx/src/app/modules/home/components/audit-log/audit-log-filter.component.html
  3. 35
      ui-ngx/src/app/modules/home/components/audit-log/audit-log-filter.component.scss
  4. 198
      ui-ngx/src/app/modules/home/components/audit-log/audit-log-filter.component.ts
  5. 20
      ui-ngx/src/app/modules/home/components/audit-log/audit-log-header.component.html
  6. 21
      ui-ngx/src/app/modules/home/components/audit-log/audit-log-header.component.scss
  7. 38
      ui-ngx/src/app/modules/home/components/audit-log/audit-log-header.component.ts
  8. 19
      ui-ngx/src/app/modules/home/components/audit-log/audit-log-table-config.ts
  9. 6
      ui-ngx/src/app/modules/home/components/entity/entity-table-header.component.ts
  10. 4
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  11. 3
      ui-ngx/src/app/modules/home/pages/audit-log/audit-log.module.ts
  12. 9
      ui-ngx/src/app/shared/models/audit-log.models.ts
  13. 8
      ui-ngx/src/assets/locale/locale.constant-en_US.json

68
ui-ngx/src/app/core/http/audit-log.service.ts

@ -15,12 +15,12 @@
///
import { Injectable } from '@angular/core';
import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils';
import { createDefaultHttpOptions, RequestConfig } from './http-utils';
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { TimePageLink } from '@shared/models/page/page-link';
import { PageData } from '@shared/models/page/page-data';
import { AuditLog } from '@shared/models/audit-log.models';
import { AuditLog, AuditLogFilter } from '@shared/models/audit-log.models';
import { EntityId } from '@shared/models/id/entity-id';
@Injectable({
@ -32,28 +32,58 @@ export class AuditLogService {
private http: HttpClient
) { }
public getAuditLogs(pageLink: TimePageLink,
config?: RequestConfig): Observable<PageData<AuditLog>> {
return this.http.get<PageData<AuditLog>>(`/api/audit/logs${pageLink.toQuery()}`,
defaultHttpOptionsFromConfig(config));
public getAuditLogs(pageLink: TimePageLink, config?: RequestConfig): Observable<PageData<AuditLog>>;
public getAuditLogs(pageLink: TimePageLink, filters: AuditLogFilter, config?: RequestConfig): Observable<PageData<AuditLog>>;
public getAuditLogs(
pageLink: TimePageLink,
filtersOrConfig?: AuditLogFilter | RequestConfig,
config?: RequestConfig
): Observable<PageData<AuditLog>> {
return this.http.get<PageData<AuditLog>>(
`/api/audit/logs${pageLink.toQuery()}`,
createDefaultHttpOptions(filtersOrConfig, config)
);
}
public getAuditLogsByCustomerId(customerId: string, pageLink: TimePageLink,
config?: RequestConfig): Observable<PageData<AuditLog>> {
return this.http.get<PageData<AuditLog>>(`/api/audit/logs/customer/${customerId}${pageLink.toQuery()}`,
defaultHttpOptionsFromConfig(config));
public getAuditLogsByCustomerId(customerId: string, pageLink: TimePageLink, config?: RequestConfig): Observable<PageData<AuditLog>>;
public getAuditLogsByCustomerId(customerId: string, pageLink: TimePageLink, filters: AuditLogFilter, config?: RequestConfig): Observable<PageData<AuditLog>>;
public getAuditLogsByCustomerId(
customerId: string,
pageLink: TimePageLink,
filtersOrConfig?: AuditLogFilter | RequestConfig,
config?: RequestConfig
): Observable<PageData<AuditLog>> {
return this.http.get<PageData<AuditLog>>(
`/api/audit/logs/customer/${customerId}${pageLink.toQuery()}`,
createDefaultHttpOptions(filtersOrConfig, config)
);
}
public getAuditLogsByUserId(userId: string, pageLink: TimePageLink,
config?: RequestConfig): Observable<PageData<AuditLog>> {
return this.http.get<PageData<AuditLog>>(`/api/audit/logs/user/${userId}${pageLink.toQuery()}`,
defaultHttpOptionsFromConfig(config));
public getAuditLogsByUserId(userId: string, pageLink: TimePageLink, config?: RequestConfig): Observable<PageData<AuditLog>>;
public getAuditLogsByUserId(userId: string, pageLink: TimePageLink, filters: AuditLogFilter, config?: RequestConfig): Observable<PageData<AuditLog>>;
public getAuditLogsByUserId(
userId: string,
pageLink: TimePageLink,
filtersOrConfig?: AuditLogFilter | RequestConfig,
config?: RequestConfig
): Observable<PageData<AuditLog>> {
return this.http.get<PageData<AuditLog>>(
`/api/audit/logs/user/${userId}${pageLink.toQuery()}`,
createDefaultHttpOptions(filtersOrConfig, config)
);
}
public getAuditLogsByEntityId(entityId: EntityId, pageLink: TimePageLink,
config?: RequestConfig): Observable<PageData<AuditLog>> {
return this.http.get<PageData<AuditLog>>(`/api/audit/logs/entity/${entityId.entityType}/${entityId.id}${pageLink.toQuery()}`,
defaultHttpOptionsFromConfig(config));
public getAuditLogsByEntityId(entityId: EntityId, pageLink: TimePageLink, config?: RequestConfig): Observable<PageData<AuditLog>>;
public getAuditLogsByEntityId(entityId: EntityId, pageLink: TimePageLink, filters: AuditLogFilter, config?: RequestConfig): Observable<PageData<AuditLog>>;
public getAuditLogsByEntityId(
entityId: EntityId,
pageLink: TimePageLink,
filtersOrConfig?: AuditLogFilter | RequestConfig,
config?: RequestConfig
): Observable<PageData<AuditLog>> {
return this.http.get<PageData<AuditLog>>(
`/api/audit/logs/entity/${entityId.entityType}/${entityId.id}${pageLink.toQuery()}`,
createDefaultHttpOptions(filtersOrConfig, config)
);
}
}

61
ui-ngx/src/app/modules/home/components/audit-log/audit-log-filter.component.html

@ -0,0 +1,61 @@
<!--
Copyright © 2016-2025 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<button color="primary"
matTooltip="{{buttonDisplayValue}}"
matTooltipPosition="above"
mat-stroked-button
(click)="toggleAuditLogFilterPanel($event)">
<mat-icon>filter_list</mat-icon>{{buttonDisplayValue}}
</button>
<ng-template #auditLogFilterPanel>
<form class="mat-content mat-padding flex flex-col" [formGroup]="auditLogFilterForm" (ngSubmit)="update()">
<div class="tb-form-panel flex-1 no-padding no-border">
<div class="tb-form-row flex-1 space-between column-xs">
<div class="fixed-title-width" translate>audit-log.filter-types</div>
<tb-string-items-list appearance="outline"
class="flex flex-1 gt-xs:w-64"
subscriptSizing="dynamic"
[placeholder]="'audit-log.filter-type-add' | translate"
[predefinedValues]="predefinedTypeValues"
formControlName="actionTypes">
</tb-string-items-list>
</div>
</div>
<div class="tb-panel-actions flex flex-row items-center justify-end">
<button type="button"
mat-button
color="primary"
(click)="reset()">
{{ 'action.reset' | translate }}
</button>
<span class="flex-1"></span>
<button type="button"
mat-button
(click)="cancel()">
{{ 'action.cancel' | translate }}
</button>
<button type="submit"
mat-raised-button
color="primary"
[disabled]="auditLogFilterForm.invalid || !auditLogFilterForm.dirty">
{{ 'action.update' | translate }}
</button>
</div>
</form>
</ng-template>

35
ui-ngx/src/app/modules/home/components/audit-log/audit-log-filter.component.scss

@ -0,0 +1,35 @@
/**
* Copyright © 2016-2025 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
:host {
display: flex;
max-width: 100%;
padding-right: 20px;
.mdc-button {
max-width: 100%;
}
}
:host ::ng-deep {
.mdc-button {
.mat-icon {
min-width: 24px;
}
.mdc-button__label {
overflow: hidden;
text-overflow: ellipsis;
}
}
}

198
ui-ngx/src/app/modules/home/components/audit-log/audit-log-filter.component.ts

@ -0,0 +1,198 @@
///
/// Copyright © 2016-2025 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal';
import { ChangeDetectorRef, Component, DestroyRef, ElementRef, forwardRef, Input, OnInit, TemplateRef, ViewChild, ViewContainerRef } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { isDefined, isDefinedAndNotNull } from '@app/core/utils';
import { StringItemsOption } from '@app/shared/components/string-items-list.component';
import {
ActionType,
actionTypeTranslations,
auditLogFilterEquals,
AuditLogFilter,
POSITION_MAP,
} from '@app/shared/public-api';
import { TranslateService } from '@ngx-translate/core';
// @dynamic
@Component({
selector: 'tb-audit-log-filter',
templateUrl: './audit-log-filter.component.html',
styleUrls: ['./audit-log-filter.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AuditLogFilterComponent),
multi: true
}
]
})
export class AuditLogFilterComponent implements OnInit, ControlValueAccessor {
@ViewChild('auditLogFilterPanel')
auditLogFilterPanel: TemplateRef<any>;
@Input() disabled: boolean;
ActionType = ActionType;
actionTypes = Object.values(ActionType);
actionTypeTranslations = actionTypeTranslations;
buttonDisplayValue = this.translate.instant('audit-log.filter');
auditLogFilterForm: UntypedFormGroup;
auditLogOverlayRef: OverlayRef;
initialAuditLogFilter: AuditLogFilter;
private auditLogFilter: AuditLogFilter;
private propagateChange = (_: any) => {};
constructor(private fb: UntypedFormBuilder,
private translate: TranslateService,
private overlay: Overlay,
private nativeElement: ElementRef,
private viewContainerRef: ViewContainerRef,
private cd: ChangeDetectorRef,
private destroyRef: DestroyRef) {}
ngOnInit(): void {
this.auditLogFilterForm = this.fb.group({
actionTypes: [[]]
});
this.auditLogFilterForm.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
() => {
this.updateValidators();
}
);
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {
this.auditLogFilterForm.disable({emitEvent: false});
} else {
this.auditLogFilterForm.enable({emitEvent: false});
this.updateValidators();
}
}
writeValue(auditLogFilter?: AuditLogFilter): void {
this.auditLogFilter = auditLogFilter;
if(!this.initialAuditLogFilter && isDefined(this.auditLogFilter)) {
this.initialAuditLogFilter = this.auditLogFilterForm.getRawValue();
}
this.updateButtonDisplayValue();
this.updateAuditLogFilterForm(auditLogFilter);
}
get predefinedTypeValues (): StringItemsOption[] {
return [...this.actionTypes].map(type => ({
name: this.translate.instant(this.actionTypeTranslations.get(type)),
value: type
}));
}
private updateValidators() {
}
toggleAuditLogFilterPanel($event: Event) {
if ($event) {
$event.stopPropagation();
}
const config = new OverlayConfig({
panelClass: 'tb-filter-panel',
backdropClass: 'cdk-overlay-transparent-backdrop',
hasBackdrop: true,
maxHeight: '80vh',
height: 'min-content',
minWidth: ''
});
config.hasBackdrop = true;
config.positionStrategy = this.overlay.position()
.flexibleConnectedTo(this.nativeElement)
.withPositions([POSITION_MAP.bottomLeft]);
this.auditLogOverlayRef = this.overlay.create(config);
this.auditLogOverlayRef.backdropClick().subscribe(() => {
this.auditLogOverlayRef.dispose();
});
this.auditLogOverlayRef.attach(new TemplatePortal(this.auditLogFilterPanel,
this.viewContainerRef));
}
cancel() {
this.updateAuditLogFilterForm(this.auditLogFilter);
this.auditLogOverlayRef.dispose();
}
update() {
this.auditLogFilterUpdated(this.auditLogFilterForm.value);
this.auditLogOverlayRef.dispose();
}
reset() {
if (this.initialAuditLogFilter) {
if (!auditLogFilterEquals(this.auditLogFilter, this.initialAuditLogFilter)) {
this.updateAuditLogFilterForm(this.initialAuditLogFilter);
this.auditLogFilterForm.markAsDirty();
}
}
}
private updateAuditLogFilterForm(auditLogFilter?: AuditLogFilter) {
this.auditLogFilterForm.patchValue({
actionTypes: auditLogFilter?.actionTypes,
}, {emitEvent: false});
this.updateValidators();
}
private auditLogFilterUpdated(auditLogFilter: AuditLogFilter) {
this.auditLogFilter = auditLogFilter;
this.updateButtonDisplayValue();
this.propagateChange(this.auditLogFilter);
}
private updateButtonDisplayValue() {
const filterTextParts: string[] = [];
if (isDefinedAndNotNull(this.auditLogFilter?.actionTypes)) {
const actionTypes = this.auditLogFilter.actionTypes;
actionTypes.forEach(actionType => filterTextParts.push(this.translate.instant(this.actionTypeTranslations.get(actionType as ActionType))))
}
if (!filterTextParts.length) {
this.buttonDisplayValue = this.translate.instant('audit-log.audit-log-filter-title');
} else {
this.buttonDisplayValue = this.translate.instant('audit-log.filter-title') + `: ${filterTextParts.join(', ')}`;
}
this.cd.detectChanges();
}
}

20
ui-ngx/src/app/modules/home/components/audit-log/audit-log-header.component.html

@ -0,0 +1,20 @@
<!--
Copyright © 2016-2025 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<tb-audit-log-filter [ngModel]="entitiesTableConfig.componentsData.auditLogFilter"
(ngModelChange)="auditLogFiltersChanged($event)"
></tb-audit-log-filter>

21
ui-ngx/src/app/modules/home/components/audit-log/audit-log-header.component.scss

@ -0,0 +1,21 @@
/**
* Copyright © 2016-2025 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
:host {
padding-right: 8px;
overflow: hidden;
max-width: 100%;
}

38
ui-ngx/src/app/modules/home/components/audit-log/audit-log-header.component.ts

@ -0,0 +1,38 @@
///
/// Copyright © 2016-2025 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component } from '@angular/core';
import { AppState } from '@app/core/public-api';
import { AuditLog, AuditLogFilter, TimePageLink } from '@app/shared/public-api';
import { EntityTableHeaderComponent } from '@home/components/entity/entity-table-header.component';
import { Store } from '@ngrx/store';
@Component({
selector: 'tb-audit-log-header',
templateUrl: './audit-log-header.component.html',
styles: ``
})
export class AuditLogHeaderComponent extends EntityTableHeaderComponent<AuditLog, TimePageLink> {
constructor(protected store: Store<AppState>) {
super(store)
}
auditLogFiltersChanged(auditLogFilter: AuditLogFilter) {
this.entitiesTableConfig.componentsData.auditLogFilter = auditLogFilter;
this.entitiesTableConfig.getTable().resetSortAndFilter(true, true);
}
}

19
ui-ngx/src/app/modules/home/components/audit-log/audit-log-table-config.ts

@ -25,7 +25,8 @@ import {
ActionType,
actionTypeTranslations,
AuditLog,
AuditLogMode
AuditLogMode,
AuditLogFilter
} from '@shared/models/audit-log.models';
import {
AliasEntityType,
@ -48,6 +49,8 @@ import {
AuditLogDetailsDialogComponent,
AuditLogDetailsDialogData
} from '@home/components/audit-log/audit-log-details-dialog.component';
import { deepClone } from '@app/core/utils';
import { AuditLogHeaderComponent } from '@home/components/audit-log/audit-log-header.component';
export class AuditLogTableConfig extends EntityTableConfig<AuditLog, TimePageLink> {
@ -71,6 +74,7 @@ export class AuditLogTableConfig extends EntityTableConfig<AuditLog, TimePageLin
this.searchEnabled = true;
this.addEnabled = false;
this.entitiesDeleteEnabled = false;
this.headerComponent = AuditLogHeaderComponent;
this.actionsColumnTitle = 'audit-log.details';
this.entityTranslations = {
noEntities: 'audit-log.no-audit-logs-prompt',
@ -79,6 +83,10 @@ export class AuditLogTableConfig extends EntityTableConfig<AuditLog, TimePageLin
this.entityResources = {
} as EntityTypeResource<AuditLog>;
this.componentsData = {
auditLogFilter: []
};
this.entitiesFetchFunction = pageLink => this.fetchAuditLogs(pageLink);
this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC};
@ -139,15 +147,16 @@ export class AuditLogTableConfig extends EntityTableConfig<AuditLog, TimePageLin
}
fetchAuditLogs(pageLink: TimePageLink): Observable<PageData<AuditLog>> {
const auditLogFilter: AuditLogFilter = deepClone(this.componentsData?.auditLogFilter) || {};
switch (this.auditLogMode) {
case AuditLogMode.TENANT:
return this.auditLogService.getAuditLogs(pageLink);
return this.auditLogService.getAuditLogs(pageLink, auditLogFilter);
case AuditLogMode.ENTITY:
return this.auditLogService.getAuditLogsByEntityId(this.entityId, pageLink);
return this.auditLogService.getAuditLogsByEntityId(this.entityId, pageLink, auditLogFilter);
case AuditLogMode.USER:
return this.auditLogService.getAuditLogsByUserId(this.userId.id, pageLink);
return this.auditLogService.getAuditLogsByUserId(this.userId.id, pageLink, auditLogFilter);
case AuditLogMode.CUSTOMER:
return this.auditLogService.getAuditLogsByCustomerId(this.customerId.id, pageLink);
return this.auditLogService.getAuditLogsByCustomerId(this.customerId.id, pageLink, auditLogFilter);
}
}

6
ui-ngx/src/app/modules/home/components/entity/entity-table-header.component.ts

@ -17,8 +17,6 @@
import { BaseData, HasId } from '@shared/models/base-data';
import { PageComponent } from '@shared/components/page.component';
import { Directive, Input, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
import { PageLink } from '@shared/models/page/page-link';
@ -41,8 +39,8 @@ export abstract class EntityTableHeaderComponent<T extends BaseData<HasId>,
return this.entitiesTableConfigValue;
}
protected constructor(protected store: Store<AppState>) {
super(store);
protected constructor(...args: unknown[]) {
super();
}
ngOnInit() {

4
ui-ngx/src/app/modules/home/components/home-components.module.ts

@ -23,6 +23,7 @@ import { DetailsPanelComponent } from '@home/components/details-panel.component'
import { EntityDetailsPanelComponent } from '@home/components/entity/entity-details-panel.component';
import { AuditLogDetailsDialogComponent } from '@home/components/audit-log/audit-log-details-dialog.component';
import { AuditLogTableComponent } from '@home/components/audit-log/audit-log-table.component';
import { AuditLogHeaderComponent } from '@home/components/audit-log/audit-log-header.component';
import { EventTableHeaderComponent } from '@home/components/event/event-table-header.component';
import { EventTableComponent } from '@home/components/event/event-table.component';
import { EventFilterPanelComponent } from '@home/components/event/event-filter-panel.component';
@ -202,6 +203,7 @@ import { AddApiKeyDialogComponent } from '@home/components/api-key/add-api-key-d
import { EditApiKeyDescriptionPanelComponent } from '@home/components/api-key/edit-api-key-description-panel.component';
import { ApiKeyGeneratedDialogComponent } from '@home/components/api-key/api-key-generated-dialog.component';
import { ApiKeysTableDialogComponent } from '@home/components/api-key/api-keys-table-dialog.component';
import { AuditLogFilterComponent } from "@home/components/audit-log/audit-log-filter.component";
@NgModule({
declarations:
@ -358,6 +360,8 @@ import { ApiKeysTableDialogComponent } from '@home/components/api-key/api-keys-t
AddApiKeyDialogComponent,
EditApiKeyDescriptionPanelComponent,
ApiKeyGeneratedDialogComponent,
AuditLogHeaderComponent,
AuditLogFilterComponent
],
imports: [
CommonModule,

3
ui-ngx/src/app/modules/home/pages/audit-log/audit-log.module.ts

@ -20,8 +20,7 @@ import { SharedModule } from '@shared/shared.module';
import { AuditLogRoutingModule } from '@modules/home/pages/audit-log/audit-log-routing.module';
@NgModule({
declarations: [
],
declarations: [],
imports: [
CommonModule,
SharedModule,

9
ui-ngx/src/app/shared/models/audit-log.models.ts

@ -20,6 +20,7 @@ import { CustomerId } from './id/customer-id';
import { EntityId } from './id/entity-id';
import { UserId } from './id/user-id';
import { TenantId } from './id/tenant-id';
import { isArraysEqualIgnoreUndefined } from "@core/utils";
export enum AuditLogMode {
TENANT,
@ -132,3 +133,11 @@ export interface AuditLog extends BaseData<AuditLogId> {
actionStatus: ActionStatus;
actionFailureDetails: string;
}
export interface AuditLogFilter {
actionTypes: string[];
}
export const auditLogFilterEquals = (filter1?: AuditLogFilter, filter2?: AuditLogFilter): boolean => {
return isArraysEqualIgnoreUndefined(filter1.actionTypes, filter2.actionTypes);
};

8
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -1075,7 +1075,13 @@
"type-provision-failure": "Device provisioning was failed",
"type-timeseries-updated": "Telemetry updated",
"type-timeseries-deleted": "Telemetry deleted",
"type-sms-sent": "SMS sent"
"type-sms-sent": "SMS sent",
"any":"Any",
"filter":"Audit Log Filters",
"audit-log-filter-title": "Audit Log Filter",
"filter-title": "Filter",
"filter-type-add": "+ Add type",
"filter-types": "Audit Log Types"
},
"debug-settings": {
"label": "Debug Configuration",

Loading…
Cancel
Save