@masterteam/audit-logs 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/audit-logs.css +1 -1
- package/assets/i18n/ar.json +60 -1
- package/assets/i18n/en.json +60 -1
- package/fesm2022/masterteam-audit-logs.mjs +650 -145
- package/fesm2022/masterteam-audit-logs.mjs.map +1 -1
- package/package.json +15 -14
- package/types/masterteam-audit-logs.d.ts +201 -80
|
@@ -1,59 +1,87 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, Component, Injectable, computed, viewChild, linkedSignal } from '@angular/core';
|
|
2
|
+
import { inject, ChangeDetectionStrategy, Component, Injectable, computed, input, output, signal, effect, viewChild, linkedSignal } from '@angular/core';
|
|
3
|
+
import { toSignal } from '@angular/core/rxjs-interop';
|
|
3
4
|
import { Router, RouterOutlet } from '@angular/router';
|
|
4
5
|
import { TranslocoService, TranslocoDirective } from '@jsverse/transloco';
|
|
5
6
|
import { Breadcrumb } from '@masterteam/components/breadcrumb';
|
|
6
7
|
import { Page } from '@masterteam/components/page';
|
|
8
|
+
import * as i2 from '@angular/common';
|
|
7
9
|
import { CommonModule } from '@angular/common';
|
|
10
|
+
import { Button } from '@masterteam/components/button';
|
|
11
|
+
import { Chip } from '@masterteam/components/chip';
|
|
12
|
+
import { StatisticCard } from '@masterteam/components/statistic-card';
|
|
8
13
|
import { Table } from '@masterteam/components/table';
|
|
9
|
-
import { SkeletonModule } from 'primeng/skeleton';
|
|
10
|
-
import { Avatar } from '@masterteam/components/avatar';
|
|
11
|
-
import { Action, Selector, State, Store, select } from '@ngxs/store';
|
|
12
14
|
import { HttpClient, HttpContext } from '@angular/common/http';
|
|
13
|
-
import { CrudStateBase,
|
|
15
|
+
import { CrudStateBase, REQUEST_CONTEXT, handleApiRequest } from '@masterteam/components';
|
|
16
|
+
import { Action, Selector, State, Store, select } from '@ngxs/store';
|
|
17
|
+
import { Card } from '@masterteam/components/card';
|
|
18
|
+
import { Drawer } from '@masterteam/components/drawer';
|
|
19
|
+
import { EntityStatus, EntityUser } from '@masterteam/components/entities';
|
|
20
|
+
import { ModalService } from '@masterteam/components/modal';
|
|
21
|
+
import * as i1 from 'primeng/skeleton';
|
|
22
|
+
import { SkeletonModule } from 'primeng/skeleton';
|
|
14
23
|
|
|
15
24
|
class AuditLogs {
|
|
16
25
|
router = inject(Router);
|
|
17
26
|
translocoService = inject(TranslocoService);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
activeLang = toSignal(this.translocoService.langChanges$, {
|
|
28
|
+
initialValue: this.translocoService.getActiveLang(),
|
|
29
|
+
});
|
|
30
|
+
get breadcrumbItems() {
|
|
31
|
+
this.activeLang();
|
|
32
|
+
return [
|
|
33
|
+
{
|
|
34
|
+
label: '',
|
|
35
|
+
icon: 'general.home-line',
|
|
36
|
+
routerLink: '/control-panel/workspaces',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
label: this.translocoService.translate('product-settings.product-settings'),
|
|
40
|
+
routerLink: '/control-panel/product-settings',
|
|
41
|
+
},
|
|
42
|
+
{ label: this.translocoService.translate('audit-logs.audit-logs') },
|
|
43
|
+
];
|
|
44
|
+
}
|
|
30
45
|
goBack() {
|
|
31
46
|
this.router.navigate(['control-panel/product-settings']);
|
|
32
47
|
}
|
|
33
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
34
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.
|
|
48
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: AuditLogs, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
49
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.8", type: AuditLogs, isStandalone: true, selector: "mt-audit-logs", ngImport: i0, template: "<ng-container *transloco=\"let t\">\r\n <mt-page\r\n [title]=\"t('audit-logs.audit-logs')\"\r\n [contentClass]=\"'max-[1025px]:p-4 max-[640px]:p-3'\"\r\n [avatarIcon]=\"'time.clock-fast-forward'\"\r\n [avatarStyle]=\"{\r\n '--p-avatar-background': 'var(--p-lime-50)',\r\n '--p-avatar-color': 'var(--p-lime-700)',\r\n }\"\r\n (backButtonClick)=\"goBack()\"\r\n backButton\r\n >\r\n <div class=\"flex min-h-0 flex-col gap-4\">\r\n <mt-breadcrumb\r\n [items]=\"breadcrumbItems\"\r\n [styleClass]=\"'mx-1 flex justify-start'\"\r\n />\r\n\r\n <router-outlet />\r\n </div>\r\n </mt-page>\r\n</ng-container>\r\n", styles: [":host{display:block;height:100%}\n"], dependencies: [{ kind: "component", type: Page, selector: "mt-page", inputs: ["backButton", "backButtonIcon", "avatarIcon", "avatarStyle", "avatarShape", "title", "tabs", "activeTab", "contentClass", "contentId"], outputs: ["backButtonClick", "tabChange"] }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Breadcrumb, selector: "mt-breadcrumb", inputs: ["items", "styleClass"], outputs: ["onItemClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
35
50
|
}
|
|
36
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
51
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: AuditLogs, decorators: [{
|
|
37
52
|
type: Component,
|
|
38
|
-
args: [{ selector: 'mt-audit-logs', imports: [Page, RouterOutlet, TranslocoDirective, Breadcrumb], template: "<ng-container *transloco=\"let t\">\r\n <mt-page\r\n [title]=\"t('audit-logs.audit-logs')\"\r\n [avatarIcon]=\"'time.clock-fast-forward'\"\r\n [avatarStyle]=\"{\r\n '--p-avatar-background': 'var(--p-lime-50)',\r\n '--p-avatar-color': 'var(--p-lime-700)',\r\n }\"\r\n (backButtonClick)=\"goBack()\"\r\n backButton\r\n >\r\n <
|
|
53
|
+
args: [{ selector: 'mt-audit-logs', imports: [Page, RouterOutlet, TranslocoDirective, Breadcrumb], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t\">\r\n <mt-page\r\n [title]=\"t('audit-logs.audit-logs')\"\r\n [contentClass]=\"'max-[1025px]:p-4 max-[640px]:p-3'\"\r\n [avatarIcon]=\"'time.clock-fast-forward'\"\r\n [avatarStyle]=\"{\r\n '--p-avatar-background': 'var(--p-lime-50)',\r\n '--p-avatar-color': 'var(--p-lime-700)',\r\n }\"\r\n (backButtonClick)=\"goBack()\"\r\n backButton\r\n >\r\n <div class=\"flex min-h-0 flex-col gap-4\">\r\n <mt-breadcrumb\r\n [items]=\"breadcrumbItems\"\r\n [styleClass]=\"'mx-1 flex justify-start'\"\r\n />\r\n\r\n <router-outlet />\r\n </div>\r\n </mt-page>\r\n</ng-container>\r\n", styles: [":host{display:block;height:100%}\n"] }]
|
|
39
54
|
}] });
|
|
40
55
|
|
|
41
56
|
var AuditLogsActionKey;
|
|
42
57
|
(function (AuditLogsActionKey) {
|
|
43
58
|
AuditLogsActionKey["GetAuditLogs"] = "getAuditLogs";
|
|
59
|
+
AuditLogsActionKey["GetAuditLogDetail"] = "getAuditLogDetail";
|
|
44
60
|
})(AuditLogsActionKey || (AuditLogsActionKey = {}));
|
|
45
61
|
|
|
46
62
|
class GetAuditLogs {
|
|
47
63
|
page;
|
|
48
64
|
pageSize;
|
|
49
65
|
filters;
|
|
50
|
-
static type = '[AuditLogs] Get Audit
|
|
51
|
-
constructor(page, pageSize, filters) {
|
|
66
|
+
static type = '[AuditLogs] Get Audit Trail Search';
|
|
67
|
+
constructor(page = 1, pageSize = 20, filters = {}) {
|
|
52
68
|
this.page = page;
|
|
53
69
|
this.pageSize = pageSize;
|
|
54
70
|
this.filters = filters;
|
|
55
71
|
}
|
|
56
72
|
}
|
|
73
|
+
class GetAuditLogDetail {
|
|
74
|
+
eventId;
|
|
75
|
+
language;
|
|
76
|
+
static type = '[AuditLogs] Get Audit Trail Detail';
|
|
77
|
+
constructor(eventId, language) {
|
|
78
|
+
this.eventId = eventId;
|
|
79
|
+
this.language = language;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
class ClearAuditLogDetail {
|
|
83
|
+
static type = '[AuditLogs] Clear Audit Trail Detail';
|
|
84
|
+
}
|
|
57
85
|
|
|
58
86
|
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
59
87
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -61,67 +89,135 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
61
89
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
62
90
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
63
91
|
};
|
|
92
|
+
const DEFAULT_SEARCH_RESPONSE = {
|
|
93
|
+
totalCount: 0,
|
|
94
|
+
page: 1,
|
|
95
|
+
pageSize: 20,
|
|
96
|
+
items: [],
|
|
97
|
+
};
|
|
64
98
|
let AuditLogsState = class AuditLogsState extends CrudStateBase {
|
|
65
99
|
http = inject(HttpClient);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
100
|
+
context = new HttpContext().set(REQUEST_CONTEXT, {
|
|
101
|
+
useBaseUrl: false,
|
|
102
|
+
});
|
|
103
|
+
baseUrl = 'control-panel/audit-trail';
|
|
69
104
|
static getAuditLogs(state) {
|
|
70
105
|
return state.auditLogs;
|
|
71
106
|
}
|
|
72
|
-
static
|
|
73
|
-
return state.
|
|
107
|
+
static getFilters(state) {
|
|
108
|
+
return state.filters;
|
|
109
|
+
}
|
|
110
|
+
static getSelectedEventId(state) {
|
|
111
|
+
return state.selectedEventId;
|
|
74
112
|
}
|
|
75
|
-
static
|
|
76
|
-
return state.
|
|
113
|
+
static getSelectedEventDetail(state) {
|
|
114
|
+
return state.selectedEventDetail;
|
|
77
115
|
}
|
|
78
|
-
// ============================================================================
|
|
79
|
-
// Loading/Error Slice Selectors - REQUIRED for optimal performance
|
|
80
|
-
// ============================================================================
|
|
81
116
|
static getLoadingActive(state) {
|
|
82
117
|
return state.loadingActive;
|
|
83
118
|
}
|
|
84
119
|
static getErrors(state) {
|
|
85
120
|
return state.errors;
|
|
86
121
|
}
|
|
87
|
-
// ============================================================================
|
|
88
|
-
// Actions
|
|
89
|
-
// ============================================================================
|
|
90
122
|
getAuditLogs(ctx, { page, pageSize, filters }) {
|
|
91
|
-
const
|
|
92
|
-
page: page.toString(),
|
|
93
|
-
pageSize: pageSize.toString(),
|
|
123
|
+
const query = this.buildQueryParams({
|
|
94
124
|
...filters,
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
params: params,
|
|
125
|
+
page: String(page),
|
|
126
|
+
pageSize: String(pageSize),
|
|
98
127
|
});
|
|
99
128
|
return handleApiRequest({
|
|
100
129
|
ctx,
|
|
101
130
|
key: AuditLogsActionKey.GetAuditLogs,
|
|
102
|
-
request$:
|
|
131
|
+
request$: this.http.get(`${this.baseUrl}/search`, {
|
|
132
|
+
params: query,
|
|
133
|
+
context: this.context,
|
|
134
|
+
}),
|
|
103
135
|
onSuccess: (response) => ({
|
|
104
|
-
auditLogs: response.data
|
|
105
|
-
|
|
106
|
-
|
|
136
|
+
auditLogs: response.data
|
|
137
|
+
? {
|
|
138
|
+
...DEFAULT_SEARCH_RESPONSE,
|
|
139
|
+
...response.data,
|
|
140
|
+
items: response.data.items ?? [],
|
|
141
|
+
}
|
|
142
|
+
: {
|
|
143
|
+
...DEFAULT_SEARCH_RESPONSE,
|
|
144
|
+
page,
|
|
145
|
+
pageSize,
|
|
146
|
+
},
|
|
147
|
+
filters: this.sanitizeFilters(filters),
|
|
148
|
+
}),
|
|
149
|
+
onError: () => ({
|
|
150
|
+
filters: this.sanitizeFilters(filters),
|
|
107
151
|
}),
|
|
108
152
|
});
|
|
109
153
|
}
|
|
110
|
-
|
|
111
|
-
|
|
154
|
+
getAuditLogDetail(ctx, { eventId, language }) {
|
|
155
|
+
ctx.patchState({
|
|
156
|
+
selectedEventId: eventId,
|
|
157
|
+
selectedEventDetail: null,
|
|
158
|
+
});
|
|
159
|
+
return handleApiRequest({
|
|
160
|
+
ctx,
|
|
161
|
+
key: AuditLogsActionKey.GetAuditLogDetail,
|
|
162
|
+
request$: this.http.get(`${this.baseUrl}/events/${encodeURIComponent(eventId)}`, {
|
|
163
|
+
params: this.buildQueryParams(language ? { language } : undefined),
|
|
164
|
+
context: this.context,
|
|
165
|
+
}),
|
|
166
|
+
onSuccess: (response, state) => state.selectedEventId === eventId
|
|
167
|
+
? {
|
|
168
|
+
selectedEventDetail: response.data ?? null,
|
|
169
|
+
}
|
|
170
|
+
: {},
|
|
171
|
+
onError: (_error, state) => state.selectedEventId === eventId
|
|
172
|
+
? {
|
|
173
|
+
selectedEventDetail: null,
|
|
174
|
+
}
|
|
175
|
+
: {},
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
clearAuditLogDetail(ctx) {
|
|
179
|
+
ctx.patchState({
|
|
180
|
+
selectedEventId: null,
|
|
181
|
+
selectedEventDetail: null,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
buildQueryParams(query) {
|
|
185
|
+
return Object.entries(query ?? {}).reduce((params, [key, value]) => {
|
|
186
|
+
if (value === null || value === undefined || value.trim() === '') {
|
|
187
|
+
return params;
|
|
188
|
+
}
|
|
189
|
+
params[key] = value;
|
|
190
|
+
return params;
|
|
191
|
+
}, {});
|
|
192
|
+
}
|
|
193
|
+
sanitizeFilters(filters) {
|
|
194
|
+
const { language: _language, ...rest } = filters;
|
|
195
|
+
return rest;
|
|
196
|
+
}
|
|
197
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: AuditLogsState, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
198
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: AuditLogsState });
|
|
112
199
|
};
|
|
113
200
|
__decorate([
|
|
114
201
|
Action(GetAuditLogs)
|
|
115
202
|
], AuditLogsState.prototype, "getAuditLogs", null);
|
|
203
|
+
__decorate([
|
|
204
|
+
Action(GetAuditLogDetail)
|
|
205
|
+
], AuditLogsState.prototype, "getAuditLogDetail", null);
|
|
206
|
+
__decorate([
|
|
207
|
+
Action(ClearAuditLogDetail)
|
|
208
|
+
], AuditLogsState.prototype, "clearAuditLogDetail", null);
|
|
116
209
|
__decorate([
|
|
117
210
|
Selector()
|
|
118
211
|
], AuditLogsState, "getAuditLogs", null);
|
|
119
212
|
__decorate([
|
|
120
213
|
Selector()
|
|
121
|
-
], AuditLogsState, "
|
|
214
|
+
], AuditLogsState, "getFilters", null);
|
|
122
215
|
__decorate([
|
|
123
216
|
Selector()
|
|
124
|
-
], AuditLogsState, "
|
|
217
|
+
], AuditLogsState, "getSelectedEventId", null);
|
|
218
|
+
__decorate([
|
|
219
|
+
Selector()
|
|
220
|
+
], AuditLogsState, "getSelectedEventDetail", null);
|
|
125
221
|
__decorate([
|
|
126
222
|
Selector()
|
|
127
223
|
], AuditLogsState, "getLoadingActive", null);
|
|
@@ -132,53 +228,125 @@ AuditLogsState = __decorate([
|
|
|
132
228
|
State({
|
|
133
229
|
name: 'auditLogs',
|
|
134
230
|
defaults: {
|
|
135
|
-
auditLogs:
|
|
136
|
-
|
|
137
|
-
|
|
231
|
+
auditLogs: DEFAULT_SEARCH_RESPONSE,
|
|
232
|
+
filters: {},
|
|
233
|
+
selectedEventId: null,
|
|
234
|
+
selectedEventDetail: null,
|
|
138
235
|
loadingActive: [],
|
|
139
236
|
errors: {},
|
|
140
237
|
},
|
|
141
238
|
})
|
|
142
239
|
], AuditLogsState);
|
|
143
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
240
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: AuditLogsState, decorators: [{
|
|
144
241
|
type: Injectable
|
|
145
|
-
}], propDecorators: { getAuditLogs: [] } });
|
|
242
|
+
}], propDecorators: { getAuditLogs: [], getAuditLogDetail: [], clearAuditLogDetail: [] } });
|
|
146
243
|
|
|
147
244
|
class AuditLogsFacade {
|
|
148
245
|
store = inject(Store);
|
|
149
|
-
// ============================================================================
|
|
150
|
-
// Data Selectors - Memoized by NGXS (fine-grained reactivity)
|
|
151
|
-
// ============================================================================
|
|
152
246
|
auditLogs = select(AuditLogsState.getAuditLogs);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
// Loading/Error Slices - Memoized by NGXS
|
|
157
|
-
// ============================================================================
|
|
247
|
+
filters = select(AuditLogsState.getFilters);
|
|
248
|
+
selectedEventId = select(AuditLogsState.getSelectedEventId);
|
|
249
|
+
selectedEventDetail = select(AuditLogsState.getSelectedEventDetail);
|
|
158
250
|
loadingActive = select(AuditLogsState.getLoadingActive);
|
|
159
251
|
errors = select(AuditLogsState.getErrors);
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
// ============================================================================
|
|
167
|
-
getAuditLogsError = computed(() => this.errors()[AuditLogsActionKey.GetAuditLogs] ?? null, ...(ngDevMode ? [{ debugName: "getAuditLogsError" }] : []));
|
|
168
|
-
// ============================================================================
|
|
169
|
-
// Action Dispatchers
|
|
170
|
-
// ============================================================================
|
|
171
|
-
getAuditLogs(page = 1, pageSize = 10, filters) {
|
|
252
|
+
isLoadingAuditLogs = computed(() => this.loadingActive().includes(AuditLogsActionKey.GetAuditLogs), ...(ngDevMode ? [{ debugName: "isLoadingAuditLogs" }] : /* istanbul ignore next */ []));
|
|
253
|
+
isLoadingAuditLogDetail = computed(() => this.loadingActive().includes(AuditLogsActionKey.GetAuditLogDetail), ...(ngDevMode ? [{ debugName: "isLoadingAuditLogDetail" }] : /* istanbul ignore next */ []));
|
|
254
|
+
getAuditLogsError = computed(() => this.errors()[AuditLogsActionKey.GetAuditLogs] ?? null, ...(ngDevMode ? [{ debugName: "getAuditLogsError" }] : /* istanbul ignore next */ []));
|
|
255
|
+
getAuditLogDetailError = computed(() => this.errors()[AuditLogsActionKey.GetAuditLogDetail] ?? null, ...(ngDevMode ? [{ debugName: "getAuditLogDetailError" }] : /* istanbul ignore next */ []));
|
|
256
|
+
activeFilterCount = computed(() => Object.values(this.filters()).filter((value) => value !== null && value !== undefined && value !== '').length, ...(ngDevMode ? [{ debugName: "activeFilterCount" }] : /* istanbul ignore next */ []));
|
|
257
|
+
getAuditLogs(page = 1, pageSize = 20, filters = {}) {
|
|
172
258
|
return this.store.dispatch(new GetAuditLogs(page, pageSize, filters));
|
|
173
259
|
}
|
|
174
|
-
|
|
175
|
-
|
|
260
|
+
getAuditLogDetail(eventId, language) {
|
|
261
|
+
return this.store.dispatch(new GetAuditLogDetail(eventId, language));
|
|
262
|
+
}
|
|
263
|
+
clearAuditLogDetail() {
|
|
264
|
+
return this.store.dispatch(new ClearAuditLogDetail());
|
|
265
|
+
}
|
|
266
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: AuditLogsFacade, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
267
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: AuditLogsFacade, providedIn: 'root' });
|
|
176
268
|
}
|
|
177
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
269
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: AuditLogsFacade, decorators: [{
|
|
178
270
|
type: Injectable,
|
|
179
271
|
args: [{ providedIn: 'root' }]
|
|
180
272
|
}] });
|
|
181
273
|
|
|
274
|
+
const FALLBACK_STATUS_COLOR = '#475569';
|
|
275
|
+
const STATUS_COLOR_MAPS = {
|
|
276
|
+
category: {
|
|
277
|
+
Runtime: '#047857',
|
|
278
|
+
ControlPanel: '#0369a1',
|
|
279
|
+
Discussion: '#7c3aed',
|
|
280
|
+
Process: '#0f766e',
|
|
281
|
+
Escalation: '#c2410c',
|
|
282
|
+
PhaseGate: '#0e7490',
|
|
283
|
+
Governance: '#b45309',
|
|
284
|
+
System: '#475569',
|
|
285
|
+
},
|
|
286
|
+
operation: {
|
|
287
|
+
Create: '#047857',
|
|
288
|
+
Update: '#0369a1',
|
|
289
|
+
Delete: '#be123c',
|
|
290
|
+
Close: '#475569',
|
|
291
|
+
Move: '#b45309',
|
|
292
|
+
Progress: '#0e7490',
|
|
293
|
+
Configure: '#4338ca',
|
|
294
|
+
Comment: '#7c3aed',
|
|
295
|
+
BulkUpdate: '#1d4ed8',
|
|
296
|
+
Change: '#0369a1',
|
|
297
|
+
Unknown: '#475569',
|
|
298
|
+
},
|
|
299
|
+
importance: {
|
|
300
|
+
Low: '#475569',
|
|
301
|
+
Medium: '#0369a1',
|
|
302
|
+
High: '#b45309',
|
|
303
|
+
Critical: '#be123c',
|
|
304
|
+
},
|
|
305
|
+
surface: {
|
|
306
|
+
Runtime: '#047857',
|
|
307
|
+
ControlPanel: '#4338ca',
|
|
308
|
+
Background: '#b45309',
|
|
309
|
+
System: '#475569',
|
|
310
|
+
},
|
|
311
|
+
};
|
|
312
|
+
const STATUS_LABELS = {
|
|
313
|
+
category: 'Category',
|
|
314
|
+
operation: 'Operation',
|
|
315
|
+
importance: 'Importance',
|
|
316
|
+
surface: 'Surface',
|
|
317
|
+
};
|
|
318
|
+
function getAuditStatusMap(kind) {
|
|
319
|
+
return Object.fromEntries(Object.entries(STATUS_COLOR_MAPS[kind]).map(([key, color]) => [
|
|
320
|
+
key,
|
|
321
|
+
{ color },
|
|
322
|
+
]));
|
|
323
|
+
}
|
|
324
|
+
function getAuditStatusColor(kind, code) {
|
|
325
|
+
if (!code) {
|
|
326
|
+
return FALLBACK_STATUS_COLOR;
|
|
327
|
+
}
|
|
328
|
+
return STATUS_COLOR_MAPS[kind][code] ?? FALLBACK_STATUS_COLOR;
|
|
329
|
+
}
|
|
330
|
+
function getAuditStatusEntity(kind, item) {
|
|
331
|
+
const display = item?.label || item?.code;
|
|
332
|
+
const code = item?.code || display;
|
|
333
|
+
if (!display || !code) {
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
return {
|
|
337
|
+
name: STATUS_LABELS[kind],
|
|
338
|
+
viewType: 'Status',
|
|
339
|
+
value: {
|
|
340
|
+
key: code,
|
|
341
|
+
display,
|
|
342
|
+
color: getAuditStatusColor(kind, code),
|
|
343
|
+
},
|
|
344
|
+
configuration: {
|
|
345
|
+
hideName: true,
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
182
350
|
/**
|
|
183
351
|
* Recursively normalizes Date objects to YYYY-MM-DDTHH:mm:ss format.
|
|
184
352
|
* Prevents UTC timezone leaks by converting dates to local datetime strings.
|
|
@@ -210,85 +378,422 @@ function normalizeDates(value) {
|
|
|
210
378
|
return value;
|
|
211
379
|
}
|
|
212
380
|
|
|
381
|
+
class AuditLogDetailDrawer {
|
|
382
|
+
modal = inject(ModalService);
|
|
383
|
+
visible = input(false, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
384
|
+
detail = input(null, ...(ngDevMode ? [{ debugName: "detail" }] : /* istanbul ignore next */ []));
|
|
385
|
+
loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
386
|
+
error = input(null, ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
|
|
387
|
+
visibleChange = output();
|
|
388
|
+
expandedEvidence = signal({}, ...(ngDevMode ? [{ debugName: "expandedEvidence" }] : /* istanbul ignore next */ []));
|
|
389
|
+
drawerTitle = computed(() => this.detail()?.summary ?? '', ...(ngDevMode ? [{ debugName: "drawerTitle" }] : /* istanbul ignore next */ []));
|
|
390
|
+
constructor() {
|
|
391
|
+
effect(() => {
|
|
392
|
+
this.detail();
|
|
393
|
+
this.expandedEvidence.set({});
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
onDrawerVisibleChange(visible) {
|
|
397
|
+
this.visibleChange.emit(visible);
|
|
398
|
+
}
|
|
399
|
+
getStatusEntity(kind, item) {
|
|
400
|
+
return getAuditStatusEntity(kind, item);
|
|
401
|
+
}
|
|
402
|
+
isEmptyValue(value) {
|
|
403
|
+
return value === null || value === undefined || value === '';
|
|
404
|
+
}
|
|
405
|
+
formatValue(value, valueKind) {
|
|
406
|
+
if (this.isEmptyValue(value)) {
|
|
407
|
+
return '';
|
|
408
|
+
}
|
|
409
|
+
if (this.isJsonValue(valueKind, value)) {
|
|
410
|
+
return this.formatJson(value);
|
|
411
|
+
}
|
|
412
|
+
if (valueKind === 'Boolean') {
|
|
413
|
+
if (value === 'true') {
|
|
414
|
+
return 'True';
|
|
415
|
+
}
|
|
416
|
+
if (value === 'false') {
|
|
417
|
+
return 'False';
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return value ?? '';
|
|
421
|
+
}
|
|
422
|
+
isJsonValue(valueKind, value) {
|
|
423
|
+
if (valueKind === 'Json') {
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
426
|
+
const trimmed = value?.trim();
|
|
427
|
+
return !!trimmed && (trimmed.startsWith('{') || trimmed.startsWith('['));
|
|
428
|
+
}
|
|
429
|
+
formatJson(value) {
|
|
430
|
+
if (!value) {
|
|
431
|
+
return '';
|
|
432
|
+
}
|
|
433
|
+
try {
|
|
434
|
+
return JSON.stringify(JSON.parse(value), null, 2);
|
|
435
|
+
}
|
|
436
|
+
catch {
|
|
437
|
+
return value;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
getFieldLabel(change) {
|
|
441
|
+
return change.fieldLabel || this.humanizeKey(change.fieldKey);
|
|
442
|
+
}
|
|
443
|
+
getChangeSource(change) {
|
|
444
|
+
return change.changeSource ? this.humanizeKey(change.changeSource) : null;
|
|
445
|
+
}
|
|
446
|
+
getSubjectLabel(subject) {
|
|
447
|
+
return (subject.label ||
|
|
448
|
+
`${this.humanizeKey(subject.subjectType)} ${subject.subjectId}`.trim());
|
|
449
|
+
}
|
|
450
|
+
getActorUserEntity(value) {
|
|
451
|
+
if (!value) {
|
|
452
|
+
return null;
|
|
453
|
+
}
|
|
454
|
+
return {
|
|
455
|
+
viewType: 'User',
|
|
456
|
+
value,
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
isEvidenceExpanded(index) {
|
|
460
|
+
return !!this.expandedEvidence()[String(index)];
|
|
461
|
+
}
|
|
462
|
+
toggleEvidence(index) {
|
|
463
|
+
const key = String(index);
|
|
464
|
+
this.expandedEvidence.update((state) => ({
|
|
465
|
+
...state,
|
|
466
|
+
[key]: !state[key],
|
|
467
|
+
}));
|
|
468
|
+
}
|
|
469
|
+
humanizeKey(value) {
|
|
470
|
+
if (!value) {
|
|
471
|
+
return '';
|
|
472
|
+
}
|
|
473
|
+
return value
|
|
474
|
+
.replace(/[_-]+/g, ' ')
|
|
475
|
+
.replace(/\s+/g, ' ')
|
|
476
|
+
.trim()
|
|
477
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
478
|
+
}
|
|
479
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: AuditLogDetailDrawer, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
480
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: AuditLogDetailDrawer, isStandalone: true, selector: "mt-audit-log-detail-drawer", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null }, detail: { classPropertyName: "detail", publicName: "detail", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { visibleChange: "visibleChange" }, host: { classAttribute: "block h-full" }, ngImport: i0, template: "<mt-drawer\r\n [visible]=\"visible()\"\r\n [title]=\"drawerTitle()\"\r\n [loadingHeader]=\"loading()\"\r\n styleClass=\"mt-audit-log-drawer !absolute !w-[90%] xl:!w-[68rem]\"\r\n appendTo=\"page-content\"\r\n position=\"right\"\r\n (visibleChange)=\"onDrawerVisibleChange($event)\"\r\n *transloco=\"let t; prefix: 'audit-logs'\"\r\n>\r\n <ng-container content>\r\n @if (loading()) {\r\n <div [class]=\"modal.contentClass + ' h-full overflow-y-auto p-5'\">\r\n <div class=\"flex flex-col gap-5\">\r\n <div class=\"flex flex-wrap gap-3\">\r\n @for (_ of [1, 2, 3, 4]; track $index) {\r\n <p-skeleton width=\"7rem\" height=\"2rem\" borderRadius=\"0.5rem\" />\r\n }\r\n </div>\r\n\r\n <div\r\n class=\"grid gap-5 xl:grid-cols-[minmax(0,1.35fr)_minmax(18rem,0.85fr)]\"\r\n >\r\n <div class=\"flex flex-col gap-5\">\r\n <mt-card [title]=\"t('drawer.change-groups')\">\r\n <div class=\"flex flex-col gap-4\">\r\n <p-skeleton height=\"12rem\" class=\"rounded-lg\" />\r\n <p-skeleton height=\"14rem\" class=\"rounded-lg\" />\r\n </div>\r\n </mt-card>\r\n\r\n <mt-card [title]=\"t('drawer.evidence')\">\r\n <div class=\"flex flex-col gap-4\">\r\n <p-skeleton height=\"9rem\" class=\"rounded-lg\" />\r\n </div>\r\n </mt-card>\r\n </div>\r\n\r\n <div class=\"flex flex-col gap-5\">\r\n <mt-card [title]=\"t('drawer.request-context')\">\r\n <div class=\"flex flex-col gap-4\">\r\n <p-skeleton height=\"1rem\" borderRadius=\"0.5rem\" />\r\n <p-skeleton height=\"1rem\" borderRadius=\"0.5rem\" />\r\n <p-skeleton height=\"1rem\" borderRadius=\"0.5rem\" />\r\n <p-skeleton height=\"1rem\" borderRadius=\"0.5rem\" />\r\n <p-skeleton height=\"3rem\" borderRadius=\"0.75rem\" />\r\n </div>\r\n </mt-card>\r\n\r\n <mt-card [title]=\"t('drawer.subjects')\">\r\n <div class=\"flex flex-col gap-4\">\r\n <p-skeleton height=\"8rem\" class=\"rounded-lg\" />\r\n </div>\r\n </mt-card>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n } @else if (error(); as errorMessage) {\r\n <div [class]=\"modal.contentClass + ' h-full overflow-y-auto p-5'\">\r\n <mt-card class=\"min-h-[28rem]\">\r\n <div\r\n class=\"flex min-h-[28rem] items-center justify-center p-6 text-center\"\r\n >\r\n <p class=\"max-w-2xl text-sm font-medium text-rose-700\">\r\n {{ errorMessage }}\r\n </p>\r\n </div>\r\n </mt-card>\r\n </div>\r\n } @else if (detail(); as event) {\r\n <div [class]=\"modal.contentClass + ' h-full overflow-y-auto p-5'\">\r\n <div class=\"flex flex-col gap-5\">\r\n <div\r\n class=\"grid gap-5 xl:grid-cols-[minmax(0,1.35fr)_minmax(18rem,0.85fr)]\"\r\n >\r\n <div class=\"flex min-w-0 flex-col gap-5\">\r\n <mt-card [title]=\"t('drawer.change-groups')\">\r\n @if (event.changeGroups.length > 0) {\r\n @let hasMultipleChangeGroups = event.changeGroups.length > 1;\r\n <div class=\"flex flex-col gap-5\">\r\n @for (group of event.changeGroups; track group.key) {\r\n @if (hasMultipleChangeGroups) {\r\n <div\r\n class=\"overflow-hidden rounded-xl border border-surface-200\"\r\n >\r\n <div class=\"border-b border-surface-200 px-4 py-3\">\r\n <h5 class=\"font-semibold text-surface-900\">\r\n {{ group.label || humanizeKey(group.key) }}\r\n </h5>\r\n </div>\r\n\r\n <div class=\"divide-y divide-surface-100\">\r\n @for (\r\n change of group.changes;\r\n track change.fieldKey + \"-\" + $index\r\n ) {\r\n <div class=\"flex flex-col gap-3 px-4 py-4\">\r\n <div\r\n class=\"flex flex-wrap items-start justify-between gap-2\"\r\n >\r\n <div class=\"flex flex-col gap-1\">\r\n <span class=\"font-medium text-surface-900\">\r\n {{ getFieldLabel(change) }}\r\n </span>\r\n\r\n <div\r\n class=\"flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-surface-500\"\r\n >\r\n @if (\r\n getChangeSource(change);\r\n as changeSource\r\n ) {\r\n <span>\r\n {{ t(\"drawer.change-source\") }}:\r\n {{ changeSource }}\r\n </span>\r\n }\r\n\r\n <span>\r\n {{ t(\"drawer.value-kind\") }}:\r\n {{ change.valueKind }}\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div\r\n class=\"grid gap-3 lg:grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)]\"\r\n >\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 p-4\"\r\n >\r\n <p\r\n class=\"mb-2 text-xs font-semibold uppercase tracking-[0.18em] text-surface-500\"\r\n >\r\n {{ t(\"drawer.from\") }}\r\n </p>\r\n\r\n @if (isEmptyValue(change.oldValue)) {\r\n <span class=\"text-sm text-surface-400\">\r\n {{ t(\"drawer.empty-value\") }}\r\n </span>\r\n } @else if (\r\n isJsonValue(\r\n change.valueKind,\r\n change.oldValue\r\n )\r\n ) {\r\n <pre class=\"audit-json-value\">{{\r\n formatValue(\r\n change.oldValue,\r\n change.valueKind\r\n )\r\n }}</pre>\r\n } @else {\r\n <span class=\"text-sm text-surface-700\">\r\n {{\r\n formatValue(\r\n change.oldValue,\r\n change.valueKind\r\n )\r\n }}\r\n </span>\r\n }\r\n </div>\r\n\r\n <div\r\n class=\"flex items-center justify-center text-xl text-surface-300\"\r\n >\r\n ->\r\n </div>\r\n\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-0 p-4\"\r\n >\r\n <p\r\n class=\"mb-2 text-xs font-semibold uppercase tracking-[0.18em] text-surface-500\"\r\n >\r\n {{ t(\"drawer.to\") }}\r\n </p>\r\n\r\n @if (isEmptyValue(change.newValue)) {\r\n <span class=\"text-sm text-surface-500\">\r\n {{ t(\"drawer.empty-value\") }}\r\n </span>\r\n } @else if (\r\n isJsonValue(\r\n change.valueKind,\r\n change.newValue\r\n )\r\n ) {\r\n <pre class=\"audit-json-value\">{{\r\n formatValue(\r\n change.newValue,\r\n change.valueKind\r\n )\r\n }}</pre>\r\n } @else {\r\n <span class=\"text-sm text-surface-700\">\r\n {{\r\n formatValue(\r\n change.newValue,\r\n change.valueKind\r\n )\r\n }}\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"flex flex-col gap-4\">\r\n @if (group.label || group.key) {\r\n <h5 class=\"font-semibold text-surface-900\">\r\n {{ group.label || humanizeKey(group.key) }}\r\n </h5>\r\n }\r\n\r\n <div class=\"flex flex-col gap-5\">\r\n @for (\r\n change of group.changes;\r\n track change.fieldKey + \"-\" + $index\r\n ) {\r\n <div class=\"flex flex-col gap-3\">\r\n <div\r\n class=\"flex flex-wrap items-start justify-between gap-2\"\r\n >\r\n <div class=\"flex flex-col gap-1\">\r\n <span class=\"font-medium text-surface-900\">\r\n {{ getFieldLabel(change) }}\r\n </span>\r\n\r\n <div\r\n class=\"flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-surface-500\"\r\n >\r\n @if (\r\n getChangeSource(change);\r\n as changeSource\r\n ) {\r\n <span>\r\n {{ t(\"drawer.change-source\") }}:\r\n {{ changeSource }}\r\n </span>\r\n }\r\n\r\n <span>\r\n {{ t(\"drawer.value-kind\") }}:\r\n {{ change.valueKind }}\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div\r\n class=\"grid gap-3 lg:grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)]\"\r\n >\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 p-4\"\r\n >\r\n <p\r\n class=\"mb-2 text-xs font-semibold uppercase tracking-[0.18em] text-surface-500\"\r\n >\r\n {{ t(\"drawer.from\") }}\r\n </p>\r\n\r\n @if (isEmptyValue(change.oldValue)) {\r\n <span class=\"text-sm text-surface-400\">\r\n {{ t(\"drawer.empty-value\") }}\r\n </span>\r\n } @else if (\r\n isJsonValue(\r\n change.valueKind,\r\n change.oldValue\r\n )\r\n ) {\r\n <pre class=\"audit-json-value\">{{\r\n formatValue(\r\n change.oldValue,\r\n change.valueKind\r\n )\r\n }}</pre>\r\n } @else {\r\n <span class=\"text-sm text-surface-700\">\r\n {{\r\n formatValue(\r\n change.oldValue,\r\n change.valueKind\r\n )\r\n }}\r\n </span>\r\n }\r\n </div>\r\n\r\n <div\r\n class=\"flex items-center justify-center text-xl text-surface-300\"\r\n >\r\n ->\r\n </div>\r\n\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-0 p-4\"\r\n >\r\n <p\r\n class=\"mb-2 text-xs font-semibold uppercase tracking-[0.18em] text-surface-500\"\r\n >\r\n {{ t(\"drawer.to\") }}\r\n </p>\r\n\r\n @if (isEmptyValue(change.newValue)) {\r\n <span class=\"text-sm text-surface-500\">\r\n {{ t(\"drawer.empty-value\") }}\r\n </span>\r\n } @else if (\r\n isJsonValue(\r\n change.valueKind,\r\n change.newValue\r\n )\r\n ) {\r\n <pre class=\"audit-json-value\">{{\r\n formatValue(\r\n change.newValue,\r\n change.valueKind\r\n )\r\n }}</pre>\r\n } @else {\r\n <span class=\"text-sm text-surface-700\">\r\n {{\r\n formatValue(\r\n change.newValue,\r\n change.valueKind\r\n )\r\n }}\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n }\r\n </div>\r\n } @else {\r\n <div class=\"text-sm text-surface-500\">\r\n {{ t(\"drawer.no-changes\") }}\r\n </div>\r\n }\r\n </mt-card>\r\n </div>\r\n\r\n <div class=\"flex min-w-0 flex-col gap-5\">\r\n <mt-card [title]=\"t('drawer.request-context')\">\r\n <dl class=\"grid gap-4\">\r\n <div class=\"grid gap-1\">\r\n <dt\r\n class=\"text-xs font-semibold uppercase text-surface-500\"\r\n >\r\n {{ t(\"drawer.actor\") }}\r\n </dt>\r\n <dd class=\"text-sm text-surface-800\">\r\n @if (\r\n getActorUserEntity(event.requestContext.actorUser);\r\n as actorUserEntity\r\n ) {\r\n <mt-entity-user [data]=\"actorUserEntity\" />\r\n } @else {\r\n <span class=\"break-all\">\r\n {{ event.requestContext.actorUserId || \"system\" }}\r\n </span>\r\n }\r\n </dd>\r\n </div>\r\n\r\n <div class=\"grid gap-1\">\r\n <dt\r\n class=\"text-xs font-semibold uppercase text-surface-500\"\r\n >\r\n {{ t(\"drawer.occurred-at\") }}\r\n </dt>\r\n <dd class=\"text-sm text-surface-800\">\r\n {{ event.requestContext.occurredAtUtc | date: \"medium\" }}\r\n </dd>\r\n </div>\r\n\r\n <div class=\"grid gap-1\">\r\n <dt\r\n class=\"text-xs font-semibold uppercase text-surface-500\"\r\n >\r\n {{ t(\"category\") }}\r\n </dt>\r\n <dd>\r\n @if (\r\n getStatusEntity(\"category\", event.category);\r\n as status\r\n ) {\r\n <mt-entity-status [data]=\"status\" />\r\n }\r\n </dd>\r\n </div>\r\n\r\n <div class=\"grid gap-1\">\r\n <dt\r\n class=\"text-xs font-semibold uppercase text-surface-500\"\r\n >\r\n {{ t(\"operation\") }}\r\n </dt>\r\n <dd>\r\n @if (\r\n getStatusEntity(\"operation\", event.operation);\r\n as status\r\n ) {\r\n <mt-entity-status [data]=\"status\" />\r\n }\r\n </dd>\r\n </div>\r\n\r\n <div class=\"grid gap-1\">\r\n <dt\r\n class=\"text-xs font-semibold uppercase text-surface-500\"\r\n >\r\n {{ t(\"importance\") }}\r\n </dt>\r\n <dd>\r\n @if (\r\n getStatusEntity(\"importance\", event.importance);\r\n as status\r\n ) {\r\n <mt-entity-status [data]=\"status\" />\r\n }\r\n </dd>\r\n </div>\r\n\r\n <div class=\"grid gap-1\">\r\n <dt\r\n class=\"text-xs font-semibold uppercase text-surface-500\"\r\n >\r\n {{ t(\"surface\") }}\r\n </dt>\r\n <dd>\r\n @if (\r\n getStatusEntity(\"surface\", event.surface);\r\n as status\r\n ) {\r\n <mt-entity-status [data]=\"status\" />\r\n }\r\n </dd>\r\n </div>\r\n </dl>\r\n </mt-card>\r\n\r\n <mt-card [title]=\"t('drawer.subjects')\">\r\n @if (event.subjects.length > 0) {\r\n <div\r\n class=\"overflow-hidden rounded-xl border border-surface-200\"\r\n >\r\n <div class=\"divide-y divide-surface-100\">\r\n @for (\r\n subject of event.subjects;\r\n track subject.subjectType + \"-\" + subject.subjectId\r\n ) {\r\n <div class=\"flex flex-col gap-3 px-4 py-4\">\r\n <div\r\n class=\"flex flex-wrap items-start justify-between gap-3\"\r\n >\r\n <div class=\"flex flex-col gap-1\">\r\n <span class=\"font-medium text-surface-900\">\r\n {{ getSubjectLabel(subject) }}\r\n </span>\r\n <span class=\"text-xs text-surface-500\">\r\n {{ humanizeKey(subject.subjectType) }}\r\n </span>\r\n </div>\r\n\r\n <span\r\n class=\"rounded-full bg-surface-100 px-3 py-1 text-xs font-medium text-surface-700\"\r\n >\r\n {{ humanizeKey(subject.relation) }}\r\n </span>\r\n </div>\r\n\r\n <div\r\n class=\"flex flex-wrap gap-2 text-xs text-surface-500\"\r\n >\r\n <span class=\"rounded-full bg-surface-100 px-3 py-1\">\r\n {{ t(\"drawer.scope\") }}: {{ subject.subjectId }}\r\n </span>\r\n @if (\r\n subject.levelId !== null &&\r\n subject.levelId !== undefined\r\n ) {\r\n <span\r\n class=\"rounded-full bg-surface-100 px-3 py-1\"\r\n >\r\n {{ t(\"drawer.level-id\") }}:\r\n {{ subject.levelId }}\r\n </span>\r\n }\r\n @if (\r\n subject.levelDataId !== null &&\r\n subject.levelDataId !== undefined\r\n ) {\r\n <span\r\n class=\"rounded-full bg-surface-100 px-3 py-1\"\r\n >\r\n {{ t(\"drawer.level-data-id\") }}:\r\n {{ subject.levelDataId }}\r\n </span>\r\n }\r\n @if (\r\n subject.moduleDataId !== null &&\r\n subject.moduleDataId !== undefined\r\n ) {\r\n <span\r\n class=\"rounded-full bg-surface-100 px-3 py-1\"\r\n >\r\n {{ t(\"drawer.module-data-id\") }}:\r\n {{ subject.moduleDataId }}\r\n </span>\r\n }\r\n @if (subject.moduleKey) {\r\n <span\r\n class=\"rounded-full bg-surface-100 px-3 py-1\"\r\n >\r\n {{ t(\"drawer.module-key\") }}:\r\n {{ subject.moduleKey }}\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"text-sm text-surface-500\">\r\n {{ t(\"drawer.no-subjects\") }}\r\n </div>\r\n }\r\n </mt-card>\r\n </div>\r\n </div>\r\n\r\n <mt-card [title]=\"t('drawer.evidence')\">\r\n <p class=\"mb-4 text-sm text-surface-500\">\r\n {{ t(\"drawer.advanced-description\") }}\r\n </p>\r\n\r\n @if (event.evidence.length > 0) {\r\n <div class=\"overflow-hidden rounded-xl border border-surface-200\">\r\n <div class=\"divide-y divide-surface-100\">\r\n @for (\r\n evidence of event.evidence;\r\n track evidence.type + \"-\" + $index\r\n ) {\r\n <section>\r\n <div\r\n class=\"flex flex-wrap items-center justify-between gap-3 px-4 py-3\"\r\n >\r\n <span class=\"font-medium text-surface-900\">\r\n {{ evidence.type || t(\"drawer.technical-evidence\") }}\r\n </span>\r\n\r\n <mt-button\r\n [label]=\"t('drawer.view-json')\"\r\n severity=\"secondary\"\r\n variant=\"text\"\r\n size=\"small\"\r\n (onClick)=\"toggleEvidence($index)\"\r\n />\r\n </div>\r\n\r\n @if (isEvidenceExpanded($index)) {\r\n <pre class=\"audit-json-panel\">{{\r\n formatJson(evidence.contentJson)\r\n }}</pre>\r\n }\r\n </section>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"text-sm text-surface-500\">\r\n {{ t(\"drawer.no-evidence\") }}\r\n </div>\r\n }\r\n </mt-card>\r\n </div>\r\n </div>\r\n } @else {\r\n <div [class]=\"modal.contentClass + ' h-full overflow-y-auto p-5'\">\r\n <mt-card class=\"min-h-[28rem]\">\r\n <div\r\n class=\"flex min-h-[28rem] items-center justify-center p-6 text-center\"\r\n >\r\n <p class=\"max-w-2xl text-sm text-surface-500\">\r\n {{ t(\"drawer.no-event-selected\") }}\r\n </p>\r\n </div>\r\n </mt-card>\r\n </div>\r\n }\r\n </ng-container>\r\n</mt-drawer>\r\n", styles: [":host{display:block}.audit-json-panel,.audit-json-value{margin:0;overflow-x:auto;white-space:pre-wrap;word-break:break-word;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.audit-json-panel{border-top:1px solid var(--p-surface-100);background:color-mix(in srgb,var(--p-surface-50) 80%,white);padding:1rem;font-size:.8rem;line-height:1.55}.audit-json-value{font-size:.78rem;line-height:1.55;color:var(--p-surface-700)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Drawer, selector: "mt-drawer", inputs: ["visible", "position", "fullScreen", "closeOnEscape", "blockScroll", "dismissible", "title", "subtitle", "loadingHeader", "styleClass", "transitionOptions", "appendTo", "modal"], outputs: ["visibleChange", "onShow", "onHide"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: EntityStatus, selector: "mt-entity-status", inputs: ["data", "name", "value"] }, { kind: "component", type: EntityUser, selector: "mt-entity-user", inputs: ["data"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "pipe", type: i2.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
481
|
+
}
|
|
482
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: AuditLogDetailDrawer, decorators: [{
|
|
483
|
+
type: Component,
|
|
484
|
+
args: [{ selector: 'mt-audit-log-detail-drawer', imports: [
|
|
485
|
+
CommonModule,
|
|
486
|
+
TranslocoDirective,
|
|
487
|
+
Drawer,
|
|
488
|
+
Button,
|
|
489
|
+
Card,
|
|
490
|
+
EntityStatus,
|
|
491
|
+
EntityUser,
|
|
492
|
+
SkeletonModule,
|
|
493
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
494
|
+
class: 'block h-full',
|
|
495
|
+
}, template: "<mt-drawer\r\n [visible]=\"visible()\"\r\n [title]=\"drawerTitle()\"\r\n [loadingHeader]=\"loading()\"\r\n styleClass=\"mt-audit-log-drawer !absolute !w-[90%] xl:!w-[68rem]\"\r\n appendTo=\"page-content\"\r\n position=\"right\"\r\n (visibleChange)=\"onDrawerVisibleChange($event)\"\r\n *transloco=\"let t; prefix: 'audit-logs'\"\r\n>\r\n <ng-container content>\r\n @if (loading()) {\r\n <div [class]=\"modal.contentClass + ' h-full overflow-y-auto p-5'\">\r\n <div class=\"flex flex-col gap-5\">\r\n <div class=\"flex flex-wrap gap-3\">\r\n @for (_ of [1, 2, 3, 4]; track $index) {\r\n <p-skeleton width=\"7rem\" height=\"2rem\" borderRadius=\"0.5rem\" />\r\n }\r\n </div>\r\n\r\n <div\r\n class=\"grid gap-5 xl:grid-cols-[minmax(0,1.35fr)_minmax(18rem,0.85fr)]\"\r\n >\r\n <div class=\"flex flex-col gap-5\">\r\n <mt-card [title]=\"t('drawer.change-groups')\">\r\n <div class=\"flex flex-col gap-4\">\r\n <p-skeleton height=\"12rem\" class=\"rounded-lg\" />\r\n <p-skeleton height=\"14rem\" class=\"rounded-lg\" />\r\n </div>\r\n </mt-card>\r\n\r\n <mt-card [title]=\"t('drawer.evidence')\">\r\n <div class=\"flex flex-col gap-4\">\r\n <p-skeleton height=\"9rem\" class=\"rounded-lg\" />\r\n </div>\r\n </mt-card>\r\n </div>\r\n\r\n <div class=\"flex flex-col gap-5\">\r\n <mt-card [title]=\"t('drawer.request-context')\">\r\n <div class=\"flex flex-col gap-4\">\r\n <p-skeleton height=\"1rem\" borderRadius=\"0.5rem\" />\r\n <p-skeleton height=\"1rem\" borderRadius=\"0.5rem\" />\r\n <p-skeleton height=\"1rem\" borderRadius=\"0.5rem\" />\r\n <p-skeleton height=\"1rem\" borderRadius=\"0.5rem\" />\r\n <p-skeleton height=\"3rem\" borderRadius=\"0.75rem\" />\r\n </div>\r\n </mt-card>\r\n\r\n <mt-card [title]=\"t('drawer.subjects')\">\r\n <div class=\"flex flex-col gap-4\">\r\n <p-skeleton height=\"8rem\" class=\"rounded-lg\" />\r\n </div>\r\n </mt-card>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n } @else if (error(); as errorMessage) {\r\n <div [class]=\"modal.contentClass + ' h-full overflow-y-auto p-5'\">\r\n <mt-card class=\"min-h-[28rem]\">\r\n <div\r\n class=\"flex min-h-[28rem] items-center justify-center p-6 text-center\"\r\n >\r\n <p class=\"max-w-2xl text-sm font-medium text-rose-700\">\r\n {{ errorMessage }}\r\n </p>\r\n </div>\r\n </mt-card>\r\n </div>\r\n } @else if (detail(); as event) {\r\n <div [class]=\"modal.contentClass + ' h-full overflow-y-auto p-5'\">\r\n <div class=\"flex flex-col gap-5\">\r\n <div\r\n class=\"grid gap-5 xl:grid-cols-[minmax(0,1.35fr)_minmax(18rem,0.85fr)]\"\r\n >\r\n <div class=\"flex min-w-0 flex-col gap-5\">\r\n <mt-card [title]=\"t('drawer.change-groups')\">\r\n @if (event.changeGroups.length > 0) {\r\n @let hasMultipleChangeGroups = event.changeGroups.length > 1;\r\n <div class=\"flex flex-col gap-5\">\r\n @for (group of event.changeGroups; track group.key) {\r\n @if (hasMultipleChangeGroups) {\r\n <div\r\n class=\"overflow-hidden rounded-xl border border-surface-200\"\r\n >\r\n <div class=\"border-b border-surface-200 px-4 py-3\">\r\n <h5 class=\"font-semibold text-surface-900\">\r\n {{ group.label || humanizeKey(group.key) }}\r\n </h5>\r\n </div>\r\n\r\n <div class=\"divide-y divide-surface-100\">\r\n @for (\r\n change of group.changes;\r\n track change.fieldKey + \"-\" + $index\r\n ) {\r\n <div class=\"flex flex-col gap-3 px-4 py-4\">\r\n <div\r\n class=\"flex flex-wrap items-start justify-between gap-2\"\r\n >\r\n <div class=\"flex flex-col gap-1\">\r\n <span class=\"font-medium text-surface-900\">\r\n {{ getFieldLabel(change) }}\r\n </span>\r\n\r\n <div\r\n class=\"flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-surface-500\"\r\n >\r\n @if (\r\n getChangeSource(change);\r\n as changeSource\r\n ) {\r\n <span>\r\n {{ t(\"drawer.change-source\") }}:\r\n {{ changeSource }}\r\n </span>\r\n }\r\n\r\n <span>\r\n {{ t(\"drawer.value-kind\") }}:\r\n {{ change.valueKind }}\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div\r\n class=\"grid gap-3 lg:grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)]\"\r\n >\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 p-4\"\r\n >\r\n <p\r\n class=\"mb-2 text-xs font-semibold uppercase tracking-[0.18em] text-surface-500\"\r\n >\r\n {{ t(\"drawer.from\") }}\r\n </p>\r\n\r\n @if (isEmptyValue(change.oldValue)) {\r\n <span class=\"text-sm text-surface-400\">\r\n {{ t(\"drawer.empty-value\") }}\r\n </span>\r\n } @else if (\r\n isJsonValue(\r\n change.valueKind,\r\n change.oldValue\r\n )\r\n ) {\r\n <pre class=\"audit-json-value\">{{\r\n formatValue(\r\n change.oldValue,\r\n change.valueKind\r\n )\r\n }}</pre>\r\n } @else {\r\n <span class=\"text-sm text-surface-700\">\r\n {{\r\n formatValue(\r\n change.oldValue,\r\n change.valueKind\r\n )\r\n }}\r\n </span>\r\n }\r\n </div>\r\n\r\n <div\r\n class=\"flex items-center justify-center text-xl text-surface-300\"\r\n >\r\n ->\r\n </div>\r\n\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-0 p-4\"\r\n >\r\n <p\r\n class=\"mb-2 text-xs font-semibold uppercase tracking-[0.18em] text-surface-500\"\r\n >\r\n {{ t(\"drawer.to\") }}\r\n </p>\r\n\r\n @if (isEmptyValue(change.newValue)) {\r\n <span class=\"text-sm text-surface-500\">\r\n {{ t(\"drawer.empty-value\") }}\r\n </span>\r\n } @else if (\r\n isJsonValue(\r\n change.valueKind,\r\n change.newValue\r\n )\r\n ) {\r\n <pre class=\"audit-json-value\">{{\r\n formatValue(\r\n change.newValue,\r\n change.valueKind\r\n )\r\n }}</pre>\r\n } @else {\r\n <span class=\"text-sm text-surface-700\">\r\n {{\r\n formatValue(\r\n change.newValue,\r\n change.valueKind\r\n )\r\n }}\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"flex flex-col gap-4\">\r\n @if (group.label || group.key) {\r\n <h5 class=\"font-semibold text-surface-900\">\r\n {{ group.label || humanizeKey(group.key) }}\r\n </h5>\r\n }\r\n\r\n <div class=\"flex flex-col gap-5\">\r\n @for (\r\n change of group.changes;\r\n track change.fieldKey + \"-\" + $index\r\n ) {\r\n <div class=\"flex flex-col gap-3\">\r\n <div\r\n class=\"flex flex-wrap items-start justify-between gap-2\"\r\n >\r\n <div class=\"flex flex-col gap-1\">\r\n <span class=\"font-medium text-surface-900\">\r\n {{ getFieldLabel(change) }}\r\n </span>\r\n\r\n <div\r\n class=\"flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-surface-500\"\r\n >\r\n @if (\r\n getChangeSource(change);\r\n as changeSource\r\n ) {\r\n <span>\r\n {{ t(\"drawer.change-source\") }}:\r\n {{ changeSource }}\r\n </span>\r\n }\r\n\r\n <span>\r\n {{ t(\"drawer.value-kind\") }}:\r\n {{ change.valueKind }}\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div\r\n class=\"grid gap-3 lg:grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)]\"\r\n >\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 p-4\"\r\n >\r\n <p\r\n class=\"mb-2 text-xs font-semibold uppercase tracking-[0.18em] text-surface-500\"\r\n >\r\n {{ t(\"drawer.from\") }}\r\n </p>\r\n\r\n @if (isEmptyValue(change.oldValue)) {\r\n <span class=\"text-sm text-surface-400\">\r\n {{ t(\"drawer.empty-value\") }}\r\n </span>\r\n } @else if (\r\n isJsonValue(\r\n change.valueKind,\r\n change.oldValue\r\n )\r\n ) {\r\n <pre class=\"audit-json-value\">{{\r\n formatValue(\r\n change.oldValue,\r\n change.valueKind\r\n )\r\n }}</pre>\r\n } @else {\r\n <span class=\"text-sm text-surface-700\">\r\n {{\r\n formatValue(\r\n change.oldValue,\r\n change.valueKind\r\n )\r\n }}\r\n </span>\r\n }\r\n </div>\r\n\r\n <div\r\n class=\"flex items-center justify-center text-xl text-surface-300\"\r\n >\r\n ->\r\n </div>\r\n\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-0 p-4\"\r\n >\r\n <p\r\n class=\"mb-2 text-xs font-semibold uppercase tracking-[0.18em] text-surface-500\"\r\n >\r\n {{ t(\"drawer.to\") }}\r\n </p>\r\n\r\n @if (isEmptyValue(change.newValue)) {\r\n <span class=\"text-sm text-surface-500\">\r\n {{ t(\"drawer.empty-value\") }}\r\n </span>\r\n } @else if (\r\n isJsonValue(\r\n change.valueKind,\r\n change.newValue\r\n )\r\n ) {\r\n <pre class=\"audit-json-value\">{{\r\n formatValue(\r\n change.newValue,\r\n change.valueKind\r\n )\r\n }}</pre>\r\n } @else {\r\n <span class=\"text-sm text-surface-700\">\r\n {{\r\n formatValue(\r\n change.newValue,\r\n change.valueKind\r\n )\r\n }}\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n }\r\n </div>\r\n } @else {\r\n <div class=\"text-sm text-surface-500\">\r\n {{ t(\"drawer.no-changes\") }}\r\n </div>\r\n }\r\n </mt-card>\r\n </div>\r\n\r\n <div class=\"flex min-w-0 flex-col gap-5\">\r\n <mt-card [title]=\"t('drawer.request-context')\">\r\n <dl class=\"grid gap-4\">\r\n <div class=\"grid gap-1\">\r\n <dt\r\n class=\"text-xs font-semibold uppercase text-surface-500\"\r\n >\r\n {{ t(\"drawer.actor\") }}\r\n </dt>\r\n <dd class=\"text-sm text-surface-800\">\r\n @if (\r\n getActorUserEntity(event.requestContext.actorUser);\r\n as actorUserEntity\r\n ) {\r\n <mt-entity-user [data]=\"actorUserEntity\" />\r\n } @else {\r\n <span class=\"break-all\">\r\n {{ event.requestContext.actorUserId || \"system\" }}\r\n </span>\r\n }\r\n </dd>\r\n </div>\r\n\r\n <div class=\"grid gap-1\">\r\n <dt\r\n class=\"text-xs font-semibold uppercase text-surface-500\"\r\n >\r\n {{ t(\"drawer.occurred-at\") }}\r\n </dt>\r\n <dd class=\"text-sm text-surface-800\">\r\n {{ event.requestContext.occurredAtUtc | date: \"medium\" }}\r\n </dd>\r\n </div>\r\n\r\n <div class=\"grid gap-1\">\r\n <dt\r\n class=\"text-xs font-semibold uppercase text-surface-500\"\r\n >\r\n {{ t(\"category\") }}\r\n </dt>\r\n <dd>\r\n @if (\r\n getStatusEntity(\"category\", event.category);\r\n as status\r\n ) {\r\n <mt-entity-status [data]=\"status\" />\r\n }\r\n </dd>\r\n </div>\r\n\r\n <div class=\"grid gap-1\">\r\n <dt\r\n class=\"text-xs font-semibold uppercase text-surface-500\"\r\n >\r\n {{ t(\"operation\") }}\r\n </dt>\r\n <dd>\r\n @if (\r\n getStatusEntity(\"operation\", event.operation);\r\n as status\r\n ) {\r\n <mt-entity-status [data]=\"status\" />\r\n }\r\n </dd>\r\n </div>\r\n\r\n <div class=\"grid gap-1\">\r\n <dt\r\n class=\"text-xs font-semibold uppercase text-surface-500\"\r\n >\r\n {{ t(\"importance\") }}\r\n </dt>\r\n <dd>\r\n @if (\r\n getStatusEntity(\"importance\", event.importance);\r\n as status\r\n ) {\r\n <mt-entity-status [data]=\"status\" />\r\n }\r\n </dd>\r\n </div>\r\n\r\n <div class=\"grid gap-1\">\r\n <dt\r\n class=\"text-xs font-semibold uppercase text-surface-500\"\r\n >\r\n {{ t(\"surface\") }}\r\n </dt>\r\n <dd>\r\n @if (\r\n getStatusEntity(\"surface\", event.surface);\r\n as status\r\n ) {\r\n <mt-entity-status [data]=\"status\" />\r\n }\r\n </dd>\r\n </div>\r\n </dl>\r\n </mt-card>\r\n\r\n <mt-card [title]=\"t('drawer.subjects')\">\r\n @if (event.subjects.length > 0) {\r\n <div\r\n class=\"overflow-hidden rounded-xl border border-surface-200\"\r\n >\r\n <div class=\"divide-y divide-surface-100\">\r\n @for (\r\n subject of event.subjects;\r\n track subject.subjectType + \"-\" + subject.subjectId\r\n ) {\r\n <div class=\"flex flex-col gap-3 px-4 py-4\">\r\n <div\r\n class=\"flex flex-wrap items-start justify-between gap-3\"\r\n >\r\n <div class=\"flex flex-col gap-1\">\r\n <span class=\"font-medium text-surface-900\">\r\n {{ getSubjectLabel(subject) }}\r\n </span>\r\n <span class=\"text-xs text-surface-500\">\r\n {{ humanizeKey(subject.subjectType) }}\r\n </span>\r\n </div>\r\n\r\n <span\r\n class=\"rounded-full bg-surface-100 px-3 py-1 text-xs font-medium text-surface-700\"\r\n >\r\n {{ humanizeKey(subject.relation) }}\r\n </span>\r\n </div>\r\n\r\n <div\r\n class=\"flex flex-wrap gap-2 text-xs text-surface-500\"\r\n >\r\n <span class=\"rounded-full bg-surface-100 px-3 py-1\">\r\n {{ t(\"drawer.scope\") }}: {{ subject.subjectId }}\r\n </span>\r\n @if (\r\n subject.levelId !== null &&\r\n subject.levelId !== undefined\r\n ) {\r\n <span\r\n class=\"rounded-full bg-surface-100 px-3 py-1\"\r\n >\r\n {{ t(\"drawer.level-id\") }}:\r\n {{ subject.levelId }}\r\n </span>\r\n }\r\n @if (\r\n subject.levelDataId !== null &&\r\n subject.levelDataId !== undefined\r\n ) {\r\n <span\r\n class=\"rounded-full bg-surface-100 px-3 py-1\"\r\n >\r\n {{ t(\"drawer.level-data-id\") }}:\r\n {{ subject.levelDataId }}\r\n </span>\r\n }\r\n @if (\r\n subject.moduleDataId !== null &&\r\n subject.moduleDataId !== undefined\r\n ) {\r\n <span\r\n class=\"rounded-full bg-surface-100 px-3 py-1\"\r\n >\r\n {{ t(\"drawer.module-data-id\") }}:\r\n {{ subject.moduleDataId }}\r\n </span>\r\n }\r\n @if (subject.moduleKey) {\r\n <span\r\n class=\"rounded-full bg-surface-100 px-3 py-1\"\r\n >\r\n {{ t(\"drawer.module-key\") }}:\r\n {{ subject.moduleKey }}\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"text-sm text-surface-500\">\r\n {{ t(\"drawer.no-subjects\") }}\r\n </div>\r\n }\r\n </mt-card>\r\n </div>\r\n </div>\r\n\r\n <mt-card [title]=\"t('drawer.evidence')\">\r\n <p class=\"mb-4 text-sm text-surface-500\">\r\n {{ t(\"drawer.advanced-description\") }}\r\n </p>\r\n\r\n @if (event.evidence.length > 0) {\r\n <div class=\"overflow-hidden rounded-xl border border-surface-200\">\r\n <div class=\"divide-y divide-surface-100\">\r\n @for (\r\n evidence of event.evidence;\r\n track evidence.type + \"-\" + $index\r\n ) {\r\n <section>\r\n <div\r\n class=\"flex flex-wrap items-center justify-between gap-3 px-4 py-3\"\r\n >\r\n <span class=\"font-medium text-surface-900\">\r\n {{ evidence.type || t(\"drawer.technical-evidence\") }}\r\n </span>\r\n\r\n <mt-button\r\n [label]=\"t('drawer.view-json')\"\r\n severity=\"secondary\"\r\n variant=\"text\"\r\n size=\"small\"\r\n (onClick)=\"toggleEvidence($index)\"\r\n />\r\n </div>\r\n\r\n @if (isEvidenceExpanded($index)) {\r\n <pre class=\"audit-json-panel\">{{\r\n formatJson(evidence.contentJson)\r\n }}</pre>\r\n }\r\n </section>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"text-sm text-surface-500\">\r\n {{ t(\"drawer.no-evidence\") }}\r\n </div>\r\n }\r\n </mt-card>\r\n </div>\r\n </div>\r\n } @else {\r\n <div [class]=\"modal.contentClass + ' h-full overflow-y-auto p-5'\">\r\n <mt-card class=\"min-h-[28rem]\">\r\n <div\r\n class=\"flex min-h-[28rem] items-center justify-center p-6 text-center\"\r\n >\r\n <p class=\"max-w-2xl text-sm text-surface-500\">\r\n {{ t(\"drawer.no-event-selected\") }}\r\n </p>\r\n </div>\r\n </mt-card>\r\n </div>\r\n }\r\n </ng-container>\r\n</mt-drawer>\r\n", styles: [":host{display:block}.audit-json-panel,.audit-json-value{margin:0;overflow-x:auto;white-space:pre-wrap;word-break:break-word;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.audit-json-panel{border-top:1px solid var(--p-surface-100);background:color-mix(in srgb,var(--p-surface-50) 80%,white);padding:1rem;font-size:.8rem;line-height:1.55}.audit-json-value{font-size:.78rem;line-height:1.55;color:var(--p-surface-700)}\n"] }]
|
|
496
|
+
}], ctorParameters: () => [], propDecorators: { visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }], detail: [{ type: i0.Input, args: [{ isSignal: true, alias: "detail", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], visibleChange: [{ type: i0.Output, args: ["visibleChange"] }] } });
|
|
497
|
+
|
|
498
|
+
const CATEGORY_OPTIONS = [
|
|
499
|
+
'Runtime',
|
|
500
|
+
'ControlPanel',
|
|
501
|
+
'Discussion',
|
|
502
|
+
'Process',
|
|
503
|
+
'Escalation',
|
|
504
|
+
'PhaseGate',
|
|
505
|
+
'Governance',
|
|
506
|
+
'System',
|
|
507
|
+
].map((value) => ({
|
|
508
|
+
label: value,
|
|
509
|
+
value,
|
|
510
|
+
}));
|
|
511
|
+
const OPERATION_OPTIONS = [
|
|
512
|
+
'Create',
|
|
513
|
+
'Update',
|
|
514
|
+
'Delete',
|
|
515
|
+
'Close',
|
|
516
|
+
'Move',
|
|
517
|
+
'Progress',
|
|
518
|
+
'Configure',
|
|
519
|
+
'Comment',
|
|
520
|
+
'BulkUpdate',
|
|
521
|
+
'Change',
|
|
522
|
+
'Unknown',
|
|
523
|
+
].map((value) => ({
|
|
524
|
+
label: value,
|
|
525
|
+
value,
|
|
526
|
+
}));
|
|
527
|
+
const IMPORTANCE_OPTIONS = [
|
|
528
|
+
'Low',
|
|
529
|
+
'Medium',
|
|
530
|
+
'High',
|
|
531
|
+
'Critical',
|
|
532
|
+
].map((value) => ({
|
|
533
|
+
label: value,
|
|
534
|
+
value,
|
|
535
|
+
}));
|
|
213
536
|
class AuditLogsList {
|
|
214
|
-
|
|
537
|
+
summaryCol = viewChild.required('summaryCol');
|
|
538
|
+
actorCol = viewChild.required('actorCol');
|
|
215
539
|
facade = inject(AuditLogsFacade);
|
|
216
540
|
translocoService = inject(TranslocoService);
|
|
217
|
-
|
|
218
|
-
|
|
541
|
+
activeLang = toSignal(this.translocoService.langChanges$, {
|
|
542
|
+
initialValue: this.translocoService.getActiveLang(),
|
|
219
543
|
});
|
|
220
|
-
auditLogs =
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
544
|
+
auditLogs = this.facade.auditLogs;
|
|
545
|
+
appliedFilters = this.facade.filters;
|
|
546
|
+
loading = this.facade.isLoadingAuditLogs;
|
|
547
|
+
error = this.facade.getAuditLogsError;
|
|
548
|
+
detail = this.facade.selectedEventDetail;
|
|
549
|
+
detailError = this.facade.getAuditLogDetailError;
|
|
550
|
+
loadingDetail = this.facade.isLoadingAuditLogDetail;
|
|
551
|
+
drawerVisible = signal(false, ...(ngDevMode ? [{ debugName: "drawerVisible" }] : /* istanbul ignore next */ []));
|
|
552
|
+
tableFilters = signal({}, ...(ngDevMode ? [{ debugName: "tableFilters" }] : /* istanbul ignore next */ []));
|
|
553
|
+
summaryCards = computed(() => {
|
|
554
|
+
this.activeLang();
|
|
555
|
+
const response = this.auditLogs();
|
|
556
|
+
const items = response.items ?? [];
|
|
557
|
+
const elevatedCount = items.filter((item) => ['High', 'Critical'].includes(item.importance?.code ?? '')).length;
|
|
558
|
+
const uniqueActors = new Set(items
|
|
559
|
+
.map((item) => item.actorUserId?.trim())
|
|
560
|
+
.filter((value) => !!value)).size;
|
|
561
|
+
return [
|
|
562
|
+
{
|
|
563
|
+
title: String(response.totalCount ?? 0),
|
|
564
|
+
subTitle: this.translate('total-events'),
|
|
565
|
+
icon: 'file.file-05',
|
|
566
|
+
color: 'blue',
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
title: String(items.length),
|
|
570
|
+
subTitle: this.translate('visible-results'),
|
|
571
|
+
icon: 'general.building-02',
|
|
572
|
+
color: 'emerald',
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
title: String(elevatedCount),
|
|
576
|
+
subTitle: this.translate('high-impact'),
|
|
577
|
+
icon: 'alert.bell-02',
|
|
578
|
+
color: 'amber',
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
title: String(uniqueActors),
|
|
582
|
+
subTitle: this.translate('unique-actors'),
|
|
583
|
+
icon: 'custom.user-pp',
|
|
584
|
+
color: 'violet',
|
|
585
|
+
},
|
|
586
|
+
];
|
|
587
|
+
}, ...(ngDevMode ? [{ debugName: "summaryCards" }] : /* istanbul ignore next */ []));
|
|
588
|
+
activeFilterBadges = computed(() => {
|
|
589
|
+
this.activeLang();
|
|
590
|
+
const filters = this.appliedFilters();
|
|
591
|
+
const badges = [];
|
|
592
|
+
this.pushFilterBadge(badges, this.translate('search'), filters.search);
|
|
593
|
+
this.pushFilterBadge(badges, this.translate('category'), filters.category);
|
|
594
|
+
this.pushFilterBadge(badges, this.translate('operation'), filters.operation);
|
|
595
|
+
this.pushFilterBadge(badges, this.translate('importance'), filters.importance);
|
|
596
|
+
this.pushFilterBadge(badges, this.translate('surface'), filters.surface);
|
|
597
|
+
this.pushFilterBadge(badges, this.translate('actor'), filters.actorUserId);
|
|
598
|
+
if (filters.fromUtc) {
|
|
599
|
+
badges.push(`${this.translate('from')}: ${this.formatFilterDate(filters.fromUtc)}`);
|
|
600
|
+
}
|
|
601
|
+
if (filters.toUtc) {
|
|
602
|
+
badges.push(`${this.translate('to')}: ${this.formatFilterDate(filters.toUtc)}`);
|
|
603
|
+
}
|
|
604
|
+
return badges;
|
|
605
|
+
}, ...(ngDevMode ? [{ debugName: "activeFilterBadges" }] : /* istanbul ignore next */ []));
|
|
606
|
+
tableColumns = linkedSignal(() => {
|
|
607
|
+
this.activeLang();
|
|
608
|
+
return [
|
|
609
|
+
{
|
|
610
|
+
key: 'summary',
|
|
611
|
+
label: this.translate('summary'),
|
|
612
|
+
type: 'custom',
|
|
613
|
+
customCellTpl: this.summaryCol(),
|
|
614
|
+
width: '27rem',
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
key: 'category.code',
|
|
618
|
+
label: this.translate('category'),
|
|
619
|
+
type: 'status',
|
|
620
|
+
statusMap: getAuditStatusMap('category'),
|
|
621
|
+
filterConfig: {
|
|
622
|
+
type: 'select',
|
|
623
|
+
label: this.translate('category'),
|
|
624
|
+
options: CATEGORY_OPTIONS,
|
|
625
|
+
},
|
|
626
|
+
width: '10rem',
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
key: 'operation.code',
|
|
630
|
+
label: this.translate('operation'),
|
|
631
|
+
type: 'status',
|
|
632
|
+
statusMap: getAuditStatusMap('operation'),
|
|
633
|
+
filterConfig: {
|
|
634
|
+
type: 'select',
|
|
635
|
+
label: this.translate('operation'),
|
|
636
|
+
options: OPERATION_OPTIONS,
|
|
637
|
+
},
|
|
638
|
+
width: '10rem',
|
|
639
|
+
},
|
|
640
|
+
{
|
|
641
|
+
key: 'importance.code',
|
|
642
|
+
label: this.translate('importance'),
|
|
643
|
+
type: 'status',
|
|
644
|
+
statusMap: getAuditStatusMap('importance'),
|
|
645
|
+
filterConfig: {
|
|
646
|
+
type: 'select',
|
|
647
|
+
label: this.translate('importance'),
|
|
648
|
+
options: IMPORTANCE_OPTIONS,
|
|
649
|
+
},
|
|
650
|
+
width: '10rem',
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
key: 'actorUser',
|
|
654
|
+
label: this.translate('actor'),
|
|
237
655
|
type: 'user',
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
656
|
+
filterConfig: {
|
|
657
|
+
type: 'user',
|
|
658
|
+
label: this.translate('actor'),
|
|
659
|
+
},
|
|
660
|
+
width: '14rem',
|
|
241
661
|
},
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
label: this.translocoService.translate('audit-logs.activity'),
|
|
246
|
-
},
|
|
247
|
-
{
|
|
248
|
-
key: 'userIpAddress',
|
|
249
|
-
label: this.translocoService.translate('audit-logs.ip-address'),
|
|
250
|
-
},
|
|
251
|
-
{
|
|
252
|
-
key: 'date.displayValue',
|
|
253
|
-
label: this.translocoService.translate('audit-logs.date'),
|
|
254
|
-
filterConfig: {
|
|
662
|
+
{
|
|
663
|
+
key: 'occurredAtUtc',
|
|
664
|
+
label: this.translate('occurred-at'),
|
|
255
665
|
type: 'date',
|
|
256
|
-
|
|
666
|
+
filterConfig: {
|
|
667
|
+
type: 'date',
|
|
668
|
+
label: this.translate('occurred-at'),
|
|
669
|
+
},
|
|
670
|
+
width: '12rem',
|
|
257
671
|
},
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
loading = this.facade.isLoadingAuditLogs;
|
|
672
|
+
];
|
|
673
|
+
}, ...(ngDevMode ? [{ debugName: "tableColumns" }] : /* istanbul ignore next */ []));
|
|
261
674
|
onLazyLoad(event) {
|
|
262
|
-
|
|
263
|
-
const apiFilters = {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
675
|
+
const filters = event.filters ?? {};
|
|
676
|
+
const apiFilters = {
|
|
677
|
+
language: this.currentLanguage(),
|
|
678
|
+
};
|
|
679
|
+
const generalSearch = this.readFilterValue(filters['generalSearch']);
|
|
680
|
+
if (generalSearch) {
|
|
681
|
+
apiFilters.search = generalSearch;
|
|
682
|
+
}
|
|
683
|
+
const category = this.readFilterValue(filters['category.code']);
|
|
684
|
+
if (category) {
|
|
685
|
+
apiFilters.category = category;
|
|
686
|
+
}
|
|
687
|
+
const operation = this.readFilterValue(filters['operation.code']);
|
|
688
|
+
if (operation) {
|
|
689
|
+
apiFilters.operation = operation;
|
|
690
|
+
}
|
|
691
|
+
const importance = this.readFilterValue(filters['importance.code']);
|
|
692
|
+
if (importance) {
|
|
693
|
+
apiFilters.importance = importance;
|
|
694
|
+
}
|
|
695
|
+
const surface = this.readFilterValue(filters['surface.code']);
|
|
696
|
+
if (surface) {
|
|
697
|
+
apiFilters.surface = surface;
|
|
698
|
+
}
|
|
699
|
+
const actorUserId = this.readFilterValue(filters['actorUserId']);
|
|
700
|
+
if (actorUserId) {
|
|
701
|
+
apiFilters.actorUserId = actorUserId;
|
|
702
|
+
}
|
|
703
|
+
const occurredAtRange = filters['occurredAtUtc'];
|
|
704
|
+
if (occurredAtRange?.from) {
|
|
705
|
+
apiFilters.fromUtc = this.normalizeDateBoundary(occurredAtRange.from, 'start');
|
|
706
|
+
}
|
|
707
|
+
if (occurredAtRange?.to) {
|
|
708
|
+
apiFilters.toUtc = this.normalizeDateBoundary(occurredAtRange.to, 'end');
|
|
709
|
+
}
|
|
710
|
+
this.facade.getAuditLogs(event.currentPage ?? 1, event.pageSize ?? this.auditLogs().pageSize ?? 20, apiFilters);
|
|
711
|
+
}
|
|
712
|
+
openEventDetail(row) {
|
|
713
|
+
this.drawerVisible.set(true);
|
|
714
|
+
this.facade.getAuditLogDetail(row.eventId, this.currentLanguage());
|
|
715
|
+
}
|
|
716
|
+
onDrawerVisibleChange(visible) {
|
|
717
|
+
this.drawerVisible.set(visible);
|
|
718
|
+
if (!visible) {
|
|
719
|
+
this.facade.clearAuditLogDetail();
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
retry() {
|
|
723
|
+
const response = this.auditLogs();
|
|
724
|
+
this.facade.getAuditLogs(response.page || 1, response.pageSize || 20, {
|
|
725
|
+
...this.appliedFilters(),
|
|
726
|
+
language: this.currentLanguage(),
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
formatSubjectType(row) {
|
|
730
|
+
return row.primarySubjectType || this.translate('subject-type-unknown');
|
|
731
|
+
}
|
|
732
|
+
currentLanguage() {
|
|
733
|
+
return this.activeLang() === 'ar' ? 'ar' : 'en';
|
|
734
|
+
}
|
|
735
|
+
translate(key) {
|
|
736
|
+
this.activeLang();
|
|
737
|
+
return this.translocoService.translate(`audit-logs.${key}`);
|
|
738
|
+
}
|
|
739
|
+
pushFilterBadge(badges, label, value) {
|
|
740
|
+
if (!value) {
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
badges.push(`${label}: ${value}`);
|
|
744
|
+
}
|
|
745
|
+
formatFilterDate(value) {
|
|
746
|
+
const date = new Date(value);
|
|
747
|
+
if (Number.isNaN(date.getTime())) {
|
|
748
|
+
return value;
|
|
749
|
+
}
|
|
750
|
+
return new Intl.DateTimeFormat(this.currentLanguage() === 'ar' ? 'ar' : undefined, {
|
|
751
|
+
dateStyle: 'medium',
|
|
752
|
+
}).format(date);
|
|
753
|
+
}
|
|
754
|
+
readFilterValue(value) {
|
|
755
|
+
if (typeof value === 'string' && value.trim().length > 0) {
|
|
756
|
+
return value.trim();
|
|
757
|
+
}
|
|
758
|
+
if (typeof value === 'number') {
|
|
759
|
+
return String(value);
|
|
760
|
+
}
|
|
761
|
+
if (value &&
|
|
762
|
+
typeof value === 'object' &&
|
|
763
|
+
'value' in value &&
|
|
764
|
+
typeof value.value === 'string' &&
|
|
765
|
+
value.value.trim().length > 0) {
|
|
766
|
+
return value.value.trim();
|
|
767
|
+
}
|
|
768
|
+
return undefined;
|
|
769
|
+
}
|
|
770
|
+
normalizeDateBoundary(value, boundary) {
|
|
771
|
+
const normalizedDate = new Date(value);
|
|
772
|
+
if (boundary === 'start') {
|
|
773
|
+
normalizedDate.setHours(0, 0, 0, 0);
|
|
774
|
+
}
|
|
775
|
+
else {
|
|
776
|
+
normalizedDate.setHours(23, 59, 59, 999);
|
|
282
777
|
}
|
|
283
|
-
|
|
778
|
+
return normalizeDates(normalizedDate);
|
|
284
779
|
}
|
|
285
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
286
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.
|
|
780
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: AuditLogsList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
781
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: AuditLogsList, isStandalone: true, selector: "mt-audit-logs-list", host: { classAttribute: "block h-full" }, viewQueries: [{ propertyName: "summaryCol", first: true, predicate: ["summaryCol"], descendants: true, isSignal: true }, { propertyName: "actorCol", first: true, predicate: ["actorCol"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'audit-logs'\">\r\n <div class=\"flex flex-col gap-4\">\r\n @if (activeFilterBadges().length > 0) {\r\n <div class=\"flex flex-wrap gap-2\">\r\n @for (badge of activeFilterBadges(); track badge) {\r\n <mt-chip\r\n [label]=\"badge\"\r\n styleClass=\"border border-surface-200 !bg-surface-0 !text-surface-700 text-xs\"\r\n />\r\n }\r\n </div>\r\n }\r\n\r\n <div class=\"grid gap-3 md:grid-cols-2 xl:grid-cols-4\">\r\n @for (card of summaryCards(); track card.subTitle) {\r\n <mt-statistic-card [data]=\"card\" cardClass=\"shadow-sm\" />\r\n }\r\n </div>\r\n\r\n @if (error(); as errorMessage) {\r\n <section\r\n class=\"flex flex-col gap-3 rounded-xl border border-rose-200 bg-rose-50 px-5 py-4 lg:flex-row lg:items-center lg:justify-between\"\r\n >\r\n <div class=\"space-y-1\">\r\n <p class=\"text-sm font-semibold text-rose-700\">\r\n {{ t(\"load-error-title\") }}\r\n </p>\r\n <p class=\"text-sm text-rose-600\">\r\n {{ errorMessage }}\r\n </p>\r\n </div>\r\n\r\n <mt-button\r\n severity=\"danger\"\r\n variant=\"outlined\"\r\n icon=\"general.refresh-cw-05\"\r\n [label]=\"t('retry')\"\r\n (onClick)=\"retry()\"\r\n />\r\n </section>\r\n }\r\n\r\n <mt-table\r\n [(filters)]=\"tableFilters\"\r\n [data]=\"auditLogs().items\"\r\n [columns]=\"tableColumns()\"\r\n [loading]=\"loading()\"\r\n [lazy]=\"true\"\r\n [showFilters]=\"true\"\r\n [generalSearch]=\"true\"\r\n [clickableRows]=\"true\"\r\n [lazyTotalRecords]=\"auditLogs().totalCount\"\r\n dataKey=\"eventId\"\r\n storageKey=\"audit-logs-list-table\"\r\n (lazyLoad)=\"onLazyLoad($event)\"\r\n (rowClick)=\"openEventDetail($event)\"\r\n />\r\n </div>\r\n\r\n <ng-template #summaryCol let-row>\r\n <div class=\"flex flex-col gap-2 py-1\">\r\n <p class=\"line-clamp-2 text-sm font-semibold text-surface-900\">\r\n {{ row.summary }}\r\n </p>\r\n <div\r\n class=\"flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-surface-500\"\r\n >\r\n <span>{{ t(\"event-id\") }}: {{ row.eventId }}</span>\r\n @if (row.primarySubjectType) {\r\n <span>{{ formatSubjectType(row) }}</span>\r\n }\r\n </div>\r\n </div>\r\n </ng-template>\r\n\r\n <ng-template #actorCol let-row>\r\n <div class=\"flex flex-col gap-1 py-1\">\r\n <span class=\"break-all text-sm font-medium text-surface-800\">\r\n {{ row.actorUserId || t(\"actor-unknown\") }}\r\n </span>\r\n <span class=\"text-xs text-surface-500\">\r\n {{ row.moduleKey || row.levelDataId || row.moduleDataId || \"audit\" }}\r\n </span>\r\n </div>\r\n </ng-template>\r\n\r\n <mt-audit-log-detail-drawer\r\n [visible]=\"drawerVisible()\"\r\n [detail]=\"detail()\"\r\n [loading]=\"loadingDetail()\"\r\n [error]=\"detailError()\"\r\n (visibleChange)=\"onDrawerVisibleChange($event)\"\r\n />\r\n</ng-container>\r\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "lazyLocalSearch", "showFilters", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "storageKey", "storageMode", "exportable", "exportFilename", "actionShape", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "paginatorPosition", "rowsPerPageOptions", "pageSize", "currentPage", "first", "filterTerm"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange"] }, { kind: "component", type: Chip, selector: "mt-chip", inputs: ["label", "icon", "image", "removable", "removeIcon", "styleClass"], outputs: ["onRemove", "onImageError"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: StatisticCard, selector: "mt-statistic-card", inputs: ["data", "cardClass"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: AuditLogDetailDrawer, selector: "mt-audit-log-detail-drawer", inputs: ["visible", "detail", "loading", "error"], outputs: ["visibleChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
287
782
|
}
|
|
288
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
783
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: AuditLogsList, decorators: [{
|
|
289
784
|
type: Component,
|
|
290
|
-
args: [{ selector: 'mt-audit-logs-list',
|
|
291
|
-
|
|
785
|
+
args: [{ selector: 'mt-audit-logs-list', imports: [
|
|
786
|
+
CommonModule,
|
|
787
|
+
Table,
|
|
788
|
+
Chip,
|
|
789
|
+
Button,
|
|
790
|
+
StatisticCard,
|
|
791
|
+
TranslocoDirective,
|
|
792
|
+
AuditLogDetailDrawer,
|
|
793
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
794
|
+
class: 'block h-full',
|
|
795
|
+
}, template: "<ng-container *transloco=\"let t; prefix: 'audit-logs'\">\r\n <div class=\"flex flex-col gap-4\">\r\n @if (activeFilterBadges().length > 0) {\r\n <div class=\"flex flex-wrap gap-2\">\r\n @for (badge of activeFilterBadges(); track badge) {\r\n <mt-chip\r\n [label]=\"badge\"\r\n styleClass=\"border border-surface-200 !bg-surface-0 !text-surface-700 text-xs\"\r\n />\r\n }\r\n </div>\r\n }\r\n\r\n <div class=\"grid gap-3 md:grid-cols-2 xl:grid-cols-4\">\r\n @for (card of summaryCards(); track card.subTitle) {\r\n <mt-statistic-card [data]=\"card\" cardClass=\"shadow-sm\" />\r\n }\r\n </div>\r\n\r\n @if (error(); as errorMessage) {\r\n <section\r\n class=\"flex flex-col gap-3 rounded-xl border border-rose-200 bg-rose-50 px-5 py-4 lg:flex-row lg:items-center lg:justify-between\"\r\n >\r\n <div class=\"space-y-1\">\r\n <p class=\"text-sm font-semibold text-rose-700\">\r\n {{ t(\"load-error-title\") }}\r\n </p>\r\n <p class=\"text-sm text-rose-600\">\r\n {{ errorMessage }}\r\n </p>\r\n </div>\r\n\r\n <mt-button\r\n severity=\"danger\"\r\n variant=\"outlined\"\r\n icon=\"general.refresh-cw-05\"\r\n [label]=\"t('retry')\"\r\n (onClick)=\"retry()\"\r\n />\r\n </section>\r\n }\r\n\r\n <mt-table\r\n [(filters)]=\"tableFilters\"\r\n [data]=\"auditLogs().items\"\r\n [columns]=\"tableColumns()\"\r\n [loading]=\"loading()\"\r\n [lazy]=\"true\"\r\n [showFilters]=\"true\"\r\n [generalSearch]=\"true\"\r\n [clickableRows]=\"true\"\r\n [lazyTotalRecords]=\"auditLogs().totalCount\"\r\n dataKey=\"eventId\"\r\n storageKey=\"audit-logs-list-table\"\r\n (lazyLoad)=\"onLazyLoad($event)\"\r\n (rowClick)=\"openEventDetail($event)\"\r\n />\r\n </div>\r\n\r\n <ng-template #summaryCol let-row>\r\n <div class=\"flex flex-col gap-2 py-1\">\r\n <p class=\"line-clamp-2 text-sm font-semibold text-surface-900\">\r\n {{ row.summary }}\r\n </p>\r\n <div\r\n class=\"flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-surface-500\"\r\n >\r\n <span>{{ t(\"event-id\") }}: {{ row.eventId }}</span>\r\n @if (row.primarySubjectType) {\r\n <span>{{ formatSubjectType(row) }}</span>\r\n }\r\n </div>\r\n </div>\r\n </ng-template>\r\n\r\n <ng-template #actorCol let-row>\r\n <div class=\"flex flex-col gap-1 py-1\">\r\n <span class=\"break-all text-sm font-medium text-surface-800\">\r\n {{ row.actorUserId || t(\"actor-unknown\") }}\r\n </span>\r\n <span class=\"text-xs text-surface-500\">\r\n {{ row.moduleKey || row.levelDataId || row.moduleDataId || \"audit\" }}\r\n </span>\r\n </div>\r\n </ng-template>\r\n\r\n <mt-audit-log-detail-drawer\r\n [visible]=\"drawerVisible()\"\r\n [detail]=\"detail()\"\r\n [loading]=\"loadingDetail()\"\r\n [error]=\"detailError()\"\r\n (visibleChange)=\"onDrawerVisibleChange($event)\"\r\n />\r\n</ng-container>\r\n", styles: [":host{display:block}\n"] }]
|
|
796
|
+
}], propDecorators: { summaryCol: [{ type: i0.ViewChild, args: ['summaryCol', { isSignal: true }] }], actorCol: [{ type: i0.ViewChild, args: ['actorCol', { isSignal: true }] }] } });
|
|
292
797
|
|
|
293
798
|
// store/index.ts
|
|
294
799
|
|
|
@@ -296,5 +801,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
|
|
|
296
801
|
* Generated bundle index. Do not edit.
|
|
297
802
|
*/
|
|
298
803
|
|
|
299
|
-
export { AuditLogs, AuditLogsActionKey, AuditLogsFacade, AuditLogsList, AuditLogsState, GetAuditLogs };
|
|
804
|
+
export { AuditLogs, AuditLogsActionKey, AuditLogsFacade, AuditLogsList, AuditLogsState, ClearAuditLogDetail, GetAuditLogDetail, GetAuditLogs };
|
|
300
805
|
//# sourceMappingURL=masterteam-audit-logs.mjs.map
|