@esfaenza/admin-module 20.3.13 → 20.3.16

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.
@@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common';
3
3
  import * as i7 from '@angular/forms';
4
4
  import { FormsModule } from '@angular/forms';
5
5
  import * as i0 from '@angular/core';
6
- import { InjectionToken, Injectable, Inject, Component, ViewChild, EventEmitter, Output, Input, Pipe, NgModule } from '@angular/core';
6
+ import { InjectionToken, Injectable, Inject, Component, ViewChild, EventEmitter, Output, Input, Pipe, inject, NgModule } from '@angular/core';
7
7
  import * as i4 from '@angular/router';
8
8
  import { RouterModule } from '@angular/router';
9
9
  import * as i8$1 from 'ngx-bootstrap/modal';
@@ -30,6 +30,7 @@ import * as i10 from '@esfaenza/core/directives';
30
30
  import { DirectivesModule } from '@esfaenza/core/directives';
31
31
  import { GenericItem, KeyValue as KeyValue$1 } from '@esfaenza/core/domain';
32
32
  import { __decorate } from 'tslib';
33
+ import * as am5 from '@amcharts/amcharts5';
33
34
  import { ComponentsModule } from '@esfaenza/core/components';
34
35
 
35
36
  class AuditsView extends AppSearch {
@@ -42,6 +43,7 @@ class AuditsView extends AppSearch {
42
43
 
43
44
  const ADM_MODULE_PATH = new InjectionToken('ADM_MODULE_PATH');
44
45
  const ADM_CONVERSATIONS = new InjectionToken('ADM_CONVERSATIONS');
46
+ const ADM_TOPACT_V2 = new InjectionToken('ADM_TOPACT_V2');
45
47
 
46
48
  class AdmAuditLoc extends LocalizationService {
47
49
  constructor(injector) {
@@ -152,6 +154,12 @@ class AdminModuleBaseService {
152
154
  let ret = await firstValueFrom(this.http.post(POST_Director_GetTopActivities, sv, pars));
153
155
  return ret;
154
156
  }
157
+ // Stesso URL di getTopActivities: con DirectorFeature.TopActivityV2 attivo il backend-for-frontend risponde con il contratto v2
158
+ async getTopActivitiesV2(be, req) {
159
+ var pars = new HttpParams().set("be", be ?? "");
160
+ let ret = await firstValueFrom(this.http.post(POST_Director_GetTopActivities, req, pars));
161
+ return ret;
162
+ }
155
163
  async getInfos(be) {
156
164
  var pars = new HttpParams().set("be", be ?? "");
157
165
  let ret = await firstValueFrom(this.http.get(GET_Director_GetInfos, pars, null, false).pipe(catchError(_ => of(null))));
@@ -1094,6 +1102,633 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImpo
1094
1102
  args: [ADM_MODULE_PATH]
1095
1103
  }] }] });
1096
1104
 
1105
+ // Enum della Top Activity v2: i numeri sono quelli del backend (le risposte li riportano come numeri)
1106
+ var TopActivityDimension;
1107
+ (function (TopActivityDimension) {
1108
+ TopActivityDimension[TopActivityDimension["WaitClass"] = 0] = "WaitClass";
1109
+ TopActivityDimension[TopActivityDimension["EventType"] = 1] = "EventType";
1110
+ TopActivityDimension[TopActivityDimension["WaitEvent"] = 2] = "WaitEvent";
1111
+ TopActivityDimension[TopActivityDimension["Instance"] = 3] = "Instance";
1112
+ TopActivityDimension[TopActivityDimension["UserSession"] = 4] = "UserSession";
1113
+ TopActivityDimension[TopActivityDimension["UserName"] = 5] = "UserName";
1114
+ TopActivityDimension[TopActivityDimension["Tenant"] = 6] = "Tenant";
1115
+ TopActivityDimension[TopActivityDimension["SessionType"] = 7] = "SessionType";
1116
+ TopActivityDimension[TopActivityDimension["BackgroundSession"] = 8] = "BackgroundSession";
1117
+ TopActivityDimension[TopActivityDimension["JobName"] = 9] = "JobName";
1118
+ TopActivityDimension[TopActivityDimension["ConsumerGroup"] = 10] = "ConsumerGroup";
1119
+ TopActivityDimension[TopActivityDimension["ThreadType"] = 11] = "ThreadType";
1120
+ TopActivityDimension[TopActivityDimension["Api"] = 12] = "Api";
1121
+ TopActivityDimension[TopActivityDimension["Machine"] = 13] = "Machine";
1122
+ })(TopActivityDimension || (TopActivityDimension = {}));
1123
+ var TopActivityDataSource;
1124
+ (function (TopActivityDataSource) {
1125
+ TopActivityDataSource[TopActivityDataSource["Memory"] = 0] = "Memory";
1126
+ TopActivityDataSource[TopActivityDataSource["MemoryAndStored"] = 1] = "MemoryAndStored";
1127
+ })(TopActivityDataSource || (TopActivityDataSource = {}));
1128
+ var TopActivityResolution;
1129
+ (function (TopActivityResolution) {
1130
+ TopActivityResolution[TopActivityResolution["Low"] = 0] = "Low";
1131
+ TopActivityResolution[TopActivityResolution["Medium"] = 1] = "Medium";
1132
+ TopActivityResolution[TopActivityResolution["High"] = 2] = "High";
1133
+ TopActivityResolution[TopActivityResolution["Maximum"] = 3] = "Maximum";
1134
+ })(TopActivityResolution || (TopActivityResolution = {}));
1135
+ var TopActivityMaximumThreads;
1136
+ (function (TopActivityMaximumThreads) {
1137
+ TopActivityMaximumThreads[TopActivityMaximumThreads["None"] = 0] = "None";
1138
+ TopActivityMaximumThreads[TopActivityMaximumThreads["Cores"] = 1] = "Cores";
1139
+ TopActivityMaximumThreads[TopActivityMaximumThreads["Threads"] = 2] = "Threads";
1140
+ })(TopActivityMaximumThreads || (TopActivityMaximumThreads = {}));
1141
+
1142
+ class TopActivityV2Filter {
1143
+ }
1144
+ // Richiesta della Top Activity v2: date in ISO UTC (…Z), campi non valorizzati = default del backend
1145
+ class TopActivityV2Request {
1146
+ }
1147
+
1148
+ class AdmTopActivityV2Loc extends LocalizationService {
1149
+ constructor(injector) {
1150
+ super(injector);
1151
+ super.set("it->en", "Attività Principali", ["Top Activities"]);
1152
+ super.set("it->en", "CPU", ["CPUs"]);
1153
+ super.set("it->en", "Da", ["From"]);
1154
+ super.set("it->en", "A", ["To"]);
1155
+ super.set("it->en", "Istanza", ["Instance"]);
1156
+ super.set("it->en", "Istanza corrente", ["Current instance"]);
1157
+ super.set("it->en", "Tutte le istanze", ["All instances"]);
1158
+ super.set("it->en", "Origine dati", ["Data source"]);
1159
+ super.set("it->en", "Memoria e storico", ["Memory and history"]);
1160
+ super.set("it->en", "Solo memoria", ["Memory only"]);
1161
+ super.set("it->en", "Aggiornamento automatico", ["Auto refresh"]);
1162
+ super.set("it->en", "Spento", ["Off"]);
1163
+ super.set("it->en", "Dimensione", ["Dimension"]);
1164
+ super.set("it->en", "Risoluzione", ["Resolution"]);
1165
+ super.set("it->en", "Bassa", ["Low"]);
1166
+ super.set("it->en", "Media", ["Medium"]);
1167
+ super.set("it->en", "Alta", ["High"]);
1168
+ super.set("it->en", "Massima", ["Maximum"]);
1169
+ super.set("it->en", "Attività totale", ["Total activity"]);
1170
+ super.set("it->en", "Limite", ["Limit"]);
1171
+ super.set("it->en", "Worker thread", ["Worker threads"]);
1172
+ super.set("it->en", "CPU Massime", ["Maximum CPUs"]);
1173
+ super.set("it->en", "Thread Massimi", ["Maximum Threads"]);
1174
+ super.set("it->en", "Filtri", ["Filters"]);
1175
+ super.set("it->en", "Filtra", ["Filter"]);
1176
+ super.set("it->en", "Rimuovi", ["Remove"]);
1177
+ super.set("it->en", "Prima di questo istante i dati vengono dallo storico (un campione ogni 10 secondi)", ["Before this instant data comes from history (one sample every 10 seconds)"]);
1178
+ super.set("it->en", "Sessioni attive medie", ["Average active sessions"]);
1179
+ super.set("it->en", "sessioni", ["sessions"]);
1180
+ super.set("it->en", "Carico i Dati", ["Loading Data"]);
1181
+ super.set("it->en", "Nessun dato nella finestra", ["No data in the window"]);
1182
+ super.set("it->en", "Tabelle", ["Tables"]);
1183
+ super.set("it->en", "agganciato alla fine", ["anchored to the end"]);
1184
+ super.set("it->en", "Attività", ["Activity"]);
1185
+ super.set("it->en", "Altri", ["Others"]);
1186
+ super.set("it->en", "(non attribuito)", ["(not attributed)"]);
1187
+ super.set("it->en", "(nessuno)", ["(none)"]);
1188
+ super.set("it->en", "Classe di attesa", ["Wait class"]);
1189
+ super.set("it->en", "Tipo evento", ["Event type"]);
1190
+ super.set("it->en", "Nodo", ["Node"]);
1191
+ super.set("it->en", "Sessione", ["Session"]);
1192
+ super.set("it->en", "Utente", ["User"]);
1193
+ super.set("it->en", "Tenant", ["Tenant"]);
1194
+ super.set("it->en", "Tipo sessione", ["Session type"]);
1195
+ super.set("it->en", "Sessione di background", ["Background session"]);
1196
+ super.set("it->en", "Job", ["Job"]);
1197
+ super.set("it->en", "Gruppo di consumo", ["Consumer group"]);
1198
+ super.set("it->en", "Tipo thread", ["Thread type"]);
1199
+ super.set("it->en", "Macchina", ["Machine"]);
1200
+ super.set("it->en", "Tipo", ["Type"]);
1201
+ super.set("it->en", "Processo", ["Process"]);
1202
+ }
1203
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: AdmTopActivityV2Loc, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable }); }
1204
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: AdmTopActivityV2Loc }); }
1205
+ }
1206
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: AdmTopActivityV2Loc, decorators: [{
1207
+ type: Injectable
1208
+ }], ctorParameters: () => [{ type: i0.Injector }] });
1209
+
1210
+ class SessionDetailLoc extends LocalizationService {
1211
+ constructor(injector) {
1212
+ super(injector);
1213
+ super.set("it->en", "Dettaglio Sessione", ["Session Detail"]);
1214
+ super.set("it->en", "Istanza", ["Instance"]);
1215
+ super.set("it->en", "Seriale", ["Serial"]);
1216
+ super.set("it->en", "Stato", ["Status"]);
1217
+ super.set("it->en", "Tipo", ["Type"]);
1218
+ super.set("it->en", "Utente", ["User"]);
1219
+ super.set("it->en", "Prime Istruzioni", ["Top Nodes"]);
1220
+ super.set("it->en", "Nodo", ["Node"]);
1221
+ super.set("it->en", "Descrizione Nodo", ["Node Description"]);
1222
+ super.set("it->en", "Attività", ["Activity"]);
1223
+ super.set("it->en", "Tipo Nodo", ["Node Type"]);
1224
+ super.set("it->en", "Classe Attesa", ["Wait Class Name"]);
1225
+ super.set("it->en", "Statistiche Thread", ["Thread Statistics"]);
1226
+ super.set("it->en", "Attesa corrente", ["Current Wait"]);
1227
+ super.set("it->en", "Nodo di attesa", ["Wait Node"]);
1228
+ super.set("it->en", "Attesa su", ["Waiting On"]);
1229
+ super.set("it->en", "Classe", ["Class"]);
1230
+ super.set("it->en", "Tempo Attesa", ["Wait Time"]);
1231
+ super.set("it->en", "Chiudi", ["Close"]);
1232
+ }
1233
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: SessionDetailLoc, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable }); }
1234
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: SessionDetailLoc }); }
1235
+ }
1236
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: SessionDetailLoc, decorators: [{
1237
+ type: Injectable
1238
+ }], ctorParameters: () => [{ type: i0.Injector }] });
1239
+
1240
+ // Angular
1241
+ // Dettaglio sessione condiviso (stesso contenuto del dialog della pagina Top Activity di sempre): il dialog che lo ospita resta del chiamante,
1242
+ // il clic su un nodo viene rilanciato con onNodeSelected perché il dettaglio nodo è anch'esso un dialog del chiamante
1243
+ class SessionDetailComponent {
1244
+ constructor(lc) {
1245
+ this.lc = lc;
1246
+ this.onClose = new EventEmitter();
1247
+ this.onNodeSelected = new EventEmitter();
1248
+ this.seriesPropertiesMapping = {};
1249
+ this.propertiesSeriesMapping = {
1250
+ ["cputime"]: "CPU",
1251
+ ["waittime"]: "Wait",
1252
+ ["scripttime"]: "Scripting",
1253
+ ["dbtime"]: "DB",
1254
+ ["nettime"]: "Network",
1255
+ ["throttletime"]: "Throttling",
1256
+ ["sleeptime"]: "Sleep",
1257
+ ["iotime"]: "IO"
1258
+ };
1259
+ }
1260
+ ngOnInit() {
1261
+ this.threadSelectionChanged(null);
1262
+ }
1263
+ threadSelectionChanged(selection) {
1264
+ this.SelectedTopNodes = null;
1265
+ this.SelectedSessions = null;
1266
+ requestAnimationFrame(() => {
1267
+ let sessDet = (selection?.count || 0) == 0 ? this.SessionDetails : selection.items[0];
1268
+ this.SelectedTopNodes = sessDet.nodes;
1269
+ if (!sessDet.sessions || sessDet.sessions.length == 0)
1270
+ return;
1271
+ let series = [];
1272
+ let props = Object.keys(sessDet.sessions[0]);
1273
+ for (let i = 0; i < props.length; i++) {
1274
+ if (props[i] != 'sampletime') {
1275
+ let s = new LineSerie(this.propertiesSeriesMapping[props[i]], [], null, null, "sessions");
1276
+ series.push(s);
1277
+ this.seriesPropertiesMapping[props[i]] = s;
1278
+ }
1279
+ }
1280
+ for (let i = 0; i < sessDet.sessions.length; i++) {
1281
+ let itemref = sessDet.sessions[i];
1282
+ let theD = new Date(itemref.sampletime);
1283
+ for (let i = 0; i < props.length; i++)
1284
+ if (props[i] != 'sampletime')
1285
+ this.seriesPropertiesMapping[props[i]].values.push(new DateValue(theD, itemref[props[i]]));
1286
+ }
1287
+ this.SelectedSessions = new LineChartData(series);
1288
+ });
1289
+ }
1290
+ onChartLoaded(chart) {
1291
+ let readyHandler = () => {
1292
+ chart.Chart.xAxes.each(x => x.zoom(0, 1, 0));
1293
+ chart.Chart.yAxes.each(y => y.zoom(0, 1, 0));
1294
+ chart.Chart.root.events.off("frameended", readyHandler);
1295
+ };
1296
+ chart.Chart.root.events.onDebounced("frameended", readyHandler, 500);
1297
+ }
1298
+ closeDialog() {
1299
+ this.onClose.emit(true);
1300
+ }
1301
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: SessionDetailComponent, deps: [{ token: i1.LocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
1302
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.34", type: SessionDetailComponent, isStandalone: false, selector: "session-detail", inputs: { SessionDetails: "SessionDetails" }, outputs: { onClose: "onClose", onNodeSelected: "onNodeSelected" }, ngImport: i0, template: "<div class=\"modal-dialog modal-feminist\" emaDraggable>\r\n <div class=\"modal-content\">\r\n <div class=\"modal-header\">\r\n <h4 class=\"modal-title pull-left\">{{'Dettaglio Sessione' | localize: lc}}<span *ngIf=\"SessionDetails\">&nbsp;{{SessionDetails.sid}}</span></h4>\r\n <button type=\"button\" class=\"close pull-right\" (click)=\"closeDialog()\" aria-label=\"Close\"><span aria-hidden=\"true\">&times;</span></button>\r\n </div>\r\n <div class=\"modal-body\" *ngIf=\"SessionDetails\">\r\n <div class=\"row\">\r\n <div *ngIf=\"SessionDetails.instanceid\" class=\"col-md-6\"><strong>{{'Istanza' | localize: lc}}</strong>:&nbsp;{{SessionDetails.instanceid}}&nbsp;</div>\r\n <div class=\"col-md-6\"><strong>{{'Seriale' | localize: lc}}</strong>:&nbsp;{{SessionDetails.serial}}&nbsp;</div>\r\n <div class=\"col-md-6\"><strong>{{'Stato' | localize: lc}}</strong>:&nbsp;{{SessionDetails.status}}&nbsp;</div>\r\n <div class=\"col-md-6\"><strong>{{'Tipo' | localize: lc}}</strong>:&nbsp;{{SessionDetails.type}}</div>\r\n <div class=\"col-md-6\"><strong>{{'Utente' | localize: lc}}</strong>:&nbsp;{{SessionDetails.user}}&nbsp;<span *ngIf=\"SessionDetails.tenant\">({{SessionDetails.tenant}})</span></div>\r\n </div>\r\n\r\n <div style=\"display: flex; width: 100%; margin-top: 15px;\">\r\n <div style=\"flex-grow: 0;display: flex;flex-direction: column\">\r\n <h5>{{'Prime Istruzioni' | localize: lc}}</h5>\r\n <div *ngIf=\"SelectedTopNodes\">\r\n <es-table [(ngModel)]=\"SelectedTopNodes\" [ArraymodeItemsPerPage]=\"10\">\r\n <ng-template #header>\r\n <th class=\"app-col-min\">{{'Nodo' | localize: lc}}</th>\r\n <th>{{'Descrizione Nodo' | localize: lc}}</th>\r\n <th class=\"app-col-min\">{{'Attivit\u00E0' | localize: lc}}</th>\r\n <th class=\"app-col-min app-nowrap\">{{'Tipo Nodo' | localize: lc}}</th>\r\n <th class=\"app-col-min app-nowrap\">{{'Classe Attesa' | localize: lc}}</th>\r\n </ng-template>\r\n\r\n <ng-template #body let-node>\r\n <td class=\"app-col-min\"><span class=\"app-link\" (click)=\"onNodeSelected.emit(node.profnodeid)\">{{node.profnodeid}}</span></td>\r\n <td><span title=\"{{node.profnodedesc}}\">{{node.profnodedesc?.length > 40 ? node.profnodedesc.substr(0,40) + \"...\" : node.profnodedesc}}</span></td>\r\n <td class=\"app-nowrap\">{{(node.activity == \"NaN\" ? null : node.activity) | number : '1.1-2' : 'it'}} %</td>\r\n <td>{{node.nodetypename}}</td>\r\n <td>{{node.waitclassname}}</td>\r\n </ng-template>\r\n </es-table>\r\n </div>\r\n </div>\r\n <div style=\"width: 15px;\"></div>\r\n <div style=\"flex-grow: 2;display: flex;flex-direction: column\">\r\n <h5>{{'Attivit\u00E0' | localize: lc}}</h5>\r\n <div *ngIf=\"SelectedSessions\" style=\"width: 100%; height: 290px\">\r\n <es-line-chart [MinGridDistance]=\"65\"\r\n [Timeline]=\"false\"\r\n [FinalInstants]=\"false\"\r\n [Stacked]=\"true\"\r\n [LegendPadding]=\"{top: 0, bottom: 7, right: 0, left: 10}\"\r\n [Fill]=\"true\"\r\n [MergeDatasets]=\"true\"\r\n [Data]=\"SelectedSessions\"\r\n [Legend]=\"true\"\r\n name=\"shared_session_details\"\r\n [DataGroupingThreshold]=\"99999999\"\r\n (chartLoaded)=\"onChartLoaded($event)\">\r\n </es-line-chart>\r\n </div>\r\n </div>\r\n </div>\r\n <div style=\"display: flex; width: 100%;\">\r\n <div style=\"flex-grow: 1;\">\r\n <h5>{{'Statistiche Thread' | localize: lc}}</h5>\r\n <div>\r\n <es-table [(ngModel)]=\"SessionDetails.threads\" [ArraymodeItemsPerPage]=\"10\" [Selection]=\"true\" [SingleSelection]=\"true\" (onSelectionChanged)=\"threadSelectionChanged($event)\">\r\n <ng-template #header>\r\n <th>Thread</th>\r\n <th>{{'Stato' | localize: lc}}</th>\r\n <th>{{'Tipo' | localize: lc}}</th>\r\n <th>API</th>\r\n <th>{{'Attesa corrente' | localize: lc}}</th>\r\n <th>{{'Nodo di attesa' | localize: lc}}</th>\r\n <th>{{'Attesa su' | localize: lc}}</th>\r\n <th>{{'Classe' | localize: lc}}</th>\r\n <th>Par. 1</th>\r\n <th>Par. 2</th>\r\n <th>Par. 3</th>\r\n <th>{{'Tempo Attesa' | localize: lc}}</th>\r\n </ng-template>\r\n\r\n <ng-template #body let-thread>\r\n <td>{{thread.tid}}</td>\r\n <td>{{thread.status}}</td>\r\n <td>{{thread.type}}</td>\r\n <td>{{thread.api}}</td>\r\n <td>{{thread.wait}}</td>\r\n <td>{{thread.waitnode}}</td>\r\n <td>{{thread.waiteventtype}}</td>\r\n <td>{{thread.waitclassname}}</td>\r\n <td>{{thread.waitpar1}}</td>\r\n <td>{{thread.waitpar2}}</td>\r\n <td>{{thread.waitpar3}}</td>\r\n <td>{{(thread.waittime == \"NaN\" ? null : thread.waittime) | number : '1.1-2' : 'it'}}</td>\r\n </ng-template>\r\n </es-table>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"modal-footer\">\r\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"closeDialog()\">{{'Chiudi' | localize: lc}}</button>\r\n </div>\r\n </div>\r\n</div>\r\n", dependencies: [{ kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i7.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i7.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i8.EsTableComponent, selector: "es-table", inputs: ["globalCheck", "autoUpdate", "seconds", "researchInProgress", "globalColVisCheck", "EsThTdProvider", "CornerMenuOptions", "ColumnsResizable", "ColumnsPinnable", "AutoUpdate", "Height", "EmptySpaceBackgroundColor", "EsTableHandledSearch", "MaxHeight", "ContainerClass", "XLSXExport", "CSVExport", "AllSearch", "HighCellDensity", "Selection", "ShowLoadingOnBootstrap", "Removal", "RemovalCondition", "Export", "ShiftClick", "CountLabel", "HidePaging", "HidePagingCount", "HidePagingButtons", "ContextMenu", "ExportFileName", "TableClass", "ExportFunction", "ExportOnlyVisibleColumns", "HasHeaderGroup", "HasSecondaryHeaderGroup", "HeaderHidden", "BodyHidden", "SelectionDisabled", "SingleSelection", "RowClassAssigner", "OrderByColumn", "MultipleOrderingDirectives", "HiddenColumns", "ColumnsOrdering", "SearchView", "SelectAll", "UseArrayModePaging", "DynamicRowColumnsDefinition", "DynamicOperations", "Hierarchy", "ParentKey", "OwnKey", "AutoSortHierarchy", "StartsExpanded", "SavePreferences", "DefaultAlignment", "PagingStyle", "RowGroupingPagingStyle", "ItemSourceProperty", "UseSelectionCache", "ShowItemGroupsColumns", "Editable", "RangeSelection", "SearchThrottle", "ArraymodeItemsPerPage"], outputs: ["globalCheckChange", "autoUpdateChange", "secondsChange", "researchInProgressChange", "globalColVisCheckChange", "onOrderChanged", "onSearchRequest", "onSelectionChanged", "onRemoval", "onAbortRemoval", "onModelChange", "onOpenContextMenu", "onCornerAction", "onDynamicOperation"] }, { kind: "component", type: i2.EsLineChartComponent, selector: "es-line-chart", inputs: ["Data", "DataArray", "Step", "DataGroupBucketSize", "DataGroupingThreshold", "StrokeSize", "AllowGaps", "MergeDatasets", "FinalInstants", "Min", "Max", "Fill", "Stacked"] }, { kind: "directive", type: i10.Draggable, selector: "[emaDraggable]", inputs: ["emaDraggable"] }, { kind: "pipe", type: i5.DecimalPipe, name: "number" }, { kind: "pipe", type: i1.LocalizePipe, name: "localize" }], viewProviders: [{ provide: LocalizationService, useClass: SessionDetailLoc }] }); }
1303
+ }
1304
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: SessionDetailComponent, decorators: [{
1305
+ type: Component,
1306
+ args: [{ selector: "session-detail", viewProviders: [{ provide: LocalizationService, useClass: SessionDetailLoc }], standalone: false, template: "<div class=\"modal-dialog modal-feminist\" emaDraggable>\r\n <div class=\"modal-content\">\r\n <div class=\"modal-header\">\r\n <h4 class=\"modal-title pull-left\">{{'Dettaglio Sessione' | localize: lc}}<span *ngIf=\"SessionDetails\">&nbsp;{{SessionDetails.sid}}</span></h4>\r\n <button type=\"button\" class=\"close pull-right\" (click)=\"closeDialog()\" aria-label=\"Close\"><span aria-hidden=\"true\">&times;</span></button>\r\n </div>\r\n <div class=\"modal-body\" *ngIf=\"SessionDetails\">\r\n <div class=\"row\">\r\n <div *ngIf=\"SessionDetails.instanceid\" class=\"col-md-6\"><strong>{{'Istanza' | localize: lc}}</strong>:&nbsp;{{SessionDetails.instanceid}}&nbsp;</div>\r\n <div class=\"col-md-6\"><strong>{{'Seriale' | localize: lc}}</strong>:&nbsp;{{SessionDetails.serial}}&nbsp;</div>\r\n <div class=\"col-md-6\"><strong>{{'Stato' | localize: lc}}</strong>:&nbsp;{{SessionDetails.status}}&nbsp;</div>\r\n <div class=\"col-md-6\"><strong>{{'Tipo' | localize: lc}}</strong>:&nbsp;{{SessionDetails.type}}</div>\r\n <div class=\"col-md-6\"><strong>{{'Utente' | localize: lc}}</strong>:&nbsp;{{SessionDetails.user}}&nbsp;<span *ngIf=\"SessionDetails.tenant\">({{SessionDetails.tenant}})</span></div>\r\n </div>\r\n\r\n <div style=\"display: flex; width: 100%; margin-top: 15px;\">\r\n <div style=\"flex-grow: 0;display: flex;flex-direction: column\">\r\n <h5>{{'Prime Istruzioni' | localize: lc}}</h5>\r\n <div *ngIf=\"SelectedTopNodes\">\r\n <es-table [(ngModel)]=\"SelectedTopNodes\" [ArraymodeItemsPerPage]=\"10\">\r\n <ng-template #header>\r\n <th class=\"app-col-min\">{{'Nodo' | localize: lc}}</th>\r\n <th>{{'Descrizione Nodo' | localize: lc}}</th>\r\n <th class=\"app-col-min\">{{'Attivit\u00E0' | localize: lc}}</th>\r\n <th class=\"app-col-min app-nowrap\">{{'Tipo Nodo' | localize: lc}}</th>\r\n <th class=\"app-col-min app-nowrap\">{{'Classe Attesa' | localize: lc}}</th>\r\n </ng-template>\r\n\r\n <ng-template #body let-node>\r\n <td class=\"app-col-min\"><span class=\"app-link\" (click)=\"onNodeSelected.emit(node.profnodeid)\">{{node.profnodeid}}</span></td>\r\n <td><span title=\"{{node.profnodedesc}}\">{{node.profnodedesc?.length > 40 ? node.profnodedesc.substr(0,40) + \"...\" : node.profnodedesc}}</span></td>\r\n <td class=\"app-nowrap\">{{(node.activity == \"NaN\" ? null : node.activity) | number : '1.1-2' : 'it'}} %</td>\r\n <td>{{node.nodetypename}}</td>\r\n <td>{{node.waitclassname}}</td>\r\n </ng-template>\r\n </es-table>\r\n </div>\r\n </div>\r\n <div style=\"width: 15px;\"></div>\r\n <div style=\"flex-grow: 2;display: flex;flex-direction: column\">\r\n <h5>{{'Attivit\u00E0' | localize: lc}}</h5>\r\n <div *ngIf=\"SelectedSessions\" style=\"width: 100%; height: 290px\">\r\n <es-line-chart [MinGridDistance]=\"65\"\r\n [Timeline]=\"false\"\r\n [FinalInstants]=\"false\"\r\n [Stacked]=\"true\"\r\n [LegendPadding]=\"{top: 0, bottom: 7, right: 0, left: 10}\"\r\n [Fill]=\"true\"\r\n [MergeDatasets]=\"true\"\r\n [Data]=\"SelectedSessions\"\r\n [Legend]=\"true\"\r\n name=\"shared_session_details\"\r\n [DataGroupingThreshold]=\"99999999\"\r\n (chartLoaded)=\"onChartLoaded($event)\">\r\n </es-line-chart>\r\n </div>\r\n </div>\r\n </div>\r\n <div style=\"display: flex; width: 100%;\">\r\n <div style=\"flex-grow: 1;\">\r\n <h5>{{'Statistiche Thread' | localize: lc}}</h5>\r\n <div>\r\n <es-table [(ngModel)]=\"SessionDetails.threads\" [ArraymodeItemsPerPage]=\"10\" [Selection]=\"true\" [SingleSelection]=\"true\" (onSelectionChanged)=\"threadSelectionChanged($event)\">\r\n <ng-template #header>\r\n <th>Thread</th>\r\n <th>{{'Stato' | localize: lc}}</th>\r\n <th>{{'Tipo' | localize: lc}}</th>\r\n <th>API</th>\r\n <th>{{'Attesa corrente' | localize: lc}}</th>\r\n <th>{{'Nodo di attesa' | localize: lc}}</th>\r\n <th>{{'Attesa su' | localize: lc}}</th>\r\n <th>{{'Classe' | localize: lc}}</th>\r\n <th>Par. 1</th>\r\n <th>Par. 2</th>\r\n <th>Par. 3</th>\r\n <th>{{'Tempo Attesa' | localize: lc}}</th>\r\n </ng-template>\r\n\r\n <ng-template #body let-thread>\r\n <td>{{thread.tid}}</td>\r\n <td>{{thread.status}}</td>\r\n <td>{{thread.type}}</td>\r\n <td>{{thread.api}}</td>\r\n <td>{{thread.wait}}</td>\r\n <td>{{thread.waitnode}}</td>\r\n <td>{{thread.waiteventtype}}</td>\r\n <td>{{thread.waitclassname}}</td>\r\n <td>{{thread.waitpar1}}</td>\r\n <td>{{thread.waitpar2}}</td>\r\n <td>{{thread.waitpar3}}</td>\r\n <td>{{(thread.waittime == \"NaN\" ? null : thread.waittime) | number : '1.1-2' : 'it'}}</td>\r\n </ng-template>\r\n </es-table>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"modal-footer\">\r\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"closeDialog()\">{{'Chiudi' | localize: lc}}</button>\r\n </div>\r\n </div>\r\n</div>\r\n" }]
1307
+ }], ctorParameters: () => [{ type: i1.LocalizationService }], propDecorators: { SessionDetails: [{
1308
+ type: Input
1309
+ }], onClose: [{
1310
+ type: Output
1311
+ }], onNodeSelected: [{
1312
+ type: Output
1313
+ }] } });
1314
+
1315
+ // Angular
1316
+ const STORAGE_KEY = "HotSearchTopActV2";
1317
+ const TABLES_WINDOW_MS = 5 * 60 * 1000;
1318
+ const INSTANCE_CURRENT = "@current";
1319
+ const INSTANCE_ALL = "@all";
1320
+ // WaitClass ed EventType hanno valori fissi: colori fissi, così il grafico si legge sempre allo stesso modo
1321
+ const FIXED_COLORS = {
1322
+ Cpu: "#43a047", Database: "#1e88e5", Script: "#8e24aa", Network: "#8d6e63", IO: "#0288d1", Sleep: "#bdbdbd", Throttle: "#fb8c00", Wait: "#fdd835",
1323
+ Latch: "#e53935", Semaphore: "#f06292", Enqueue: "#c62828", ReadWriteLock: "#ad1457", Fence: "#6d4c41", ConditionVariable: "#00897b", Scheduler: "#5e35b1"
1324
+ };
1325
+ const PALETTE = ["#1e88e5", "#43a047", "#fb8c00", "#8e24aa", "#e53935", "#00897b", "#fdd835", "#6d4c41", "#3949ab", "#c0ca33"];
1326
+ const OTHER_COLOR = "#9e9e9e";
1327
+ const TOTAL_COLOR = "#212121";
1328
+ class TopActivityV2LegendItem {
1329
+ }
1330
+ class TopActivityV2FilterChip {
1331
+ }
1332
+ // Impostazioni ricordate fra una visita e l'altra (la finestra temporale riparte sempre dall'ultima ora)
1333
+ class TopActivityV2Settings {
1334
+ }
1335
+ class AdmTopActivityV2Component {
1336
+ constructor(dateExts, lc, localStorage, intercom, route, adms, zone) {
1337
+ this.dateExts = dateExts;
1338
+ this.lc = lc;
1339
+ this.localStorage = localStorage;
1340
+ this.intercom = intercom;
1341
+ this.route = route;
1342
+ this.adms = adms;
1343
+ this.zone = zone;
1344
+ this.Dim = TopActivityDimension;
1345
+ // Filtri
1346
+ this.filterfrom = "";
1347
+ this.filterto = "";
1348
+ this.instance = INSTANCE_CURRENT;
1349
+ this.dataSource = String(TopActivityDataSource.MemoryAndStored);
1350
+ this.dimension = String(TopActivityDimension.WaitClass);
1351
+ this.resolution = String(TopActivityResolution.Medium);
1352
+ this.totalActivity = false;
1353
+ this.maxCpu = true;
1354
+ this.maxThreadsKind = String(TopActivityMaximumThreads.Threads);
1355
+ this.tableDims = [String(TopActivityDimension.UserSession), String(TopActivityDimension.WaitEvent)];
1356
+ this.filters = [];
1357
+ this.refreshSeconds = "0";
1358
+ // Sorgenti delle select
1359
+ this.Instances = [];
1360
+ this.DataSources = [];
1361
+ this.Dimensions = [];
1362
+ this.Resolutions = [];
1363
+ this.ThreadKinds = [];
1364
+ this.RefreshOptions = [];
1365
+ // Risultato
1366
+ this.loading = false;
1367
+ this.legend = [];
1368
+ this.tables = [];
1369
+ // Time slider: fine della finestra di 5 minuti delle tabelle, in millisecondi
1370
+ this.sliderMin = 0;
1371
+ this.sliderMax = 0;
1372
+ this.sliderStep = 1000;
1373
+ this.sliderValue = 0;
1374
+ this.sliderAnchored = true;
1375
+ this.tablesWindowLabel = "";
1376
+ this.chartFrom = 0;
1377
+ this.chartTo = 0;
1378
+ this.colors = {};
1379
+ this.be = route.snapshot.params["be"] || "";
1380
+ this.intercom.send(InboundMessageTypes.Navigation, { sub: false, routeTitle: this.lc.loc(this.route.snapshot.data["title"]) });
1381
+ }
1382
+ async ngOnInit() {
1383
+ this.buildSources();
1384
+ let saved = this.route.snapshot.queryParams["ignoreStorage"] ? null : await firstValueFrom(this.localStorage.getItem(STORAGE_KEY), { defaultValue: null });
1385
+ if (saved)
1386
+ this.applySettings(saved);
1387
+ this.setLastHour();
1388
+ this.loadInstances();
1389
+ this.refreshInfos();
1390
+ this.search();
1391
+ }
1392
+ ngOnDestroy() {
1393
+ clearInterval(this.refreshTimer);
1394
+ }
1395
+ // #region Ricerca
1396
+ searchClicked() {
1397
+ this.refreshInfos();
1398
+ this.search();
1399
+ }
1400
+ async search() {
1401
+ let req = this.buildRequest();
1402
+ this.loading = true;
1403
+ try {
1404
+ let res = await this.adms.getTopActivitiesV2(this.be, req);
1405
+ this.applyResult(req, res);
1406
+ this.saveSettings();
1407
+ }
1408
+ catch {
1409
+ // L'errore (finestra non valida, backend non raggiungibile...) lo mostra già HTTPService
1410
+ }
1411
+ finally {
1412
+ this.loading = false;
1413
+ }
1414
+ }
1415
+ buildRequest() {
1416
+ let to = this.toDate(this.filterto) ?? new Date();
1417
+ let from = this.toDate(this.filterfrom) ?? new Date(to.getTime() - 3600 * 1000);
1418
+ let req = new TopActivityV2Request();
1419
+ req.From = from.toISOString();
1420
+ req.To = to.toISOString();
1421
+ req.Global = this.instance != INSTANCE_CURRENT;
1422
+ if (this.instance != INSTANCE_CURRENT && this.instance != INSTANCE_ALL)
1423
+ req.InstanceId = this.instance;
1424
+ req.DataSource = +this.dataSource;
1425
+ req.Dimension = +this.dimension;
1426
+ if (this.filters.length > 0)
1427
+ req.Filters = this.filters.map(t => t.filter);
1428
+ req.TotalActivity = this.totalActivity;
1429
+ req.MaximumThreads = this.maxCpu ? +this.maxThreadsKind : TopActivityMaximumThreads.None;
1430
+ req.Resolution = +this.resolution;
1431
+ req.Tables = this.tableDims.map(t => +t);
1432
+ req.TopCount = 10;
1433
+ // Lo slider agganciato a destra segue la fine del grafico; spostato resta fermo finché cade dentro la finestra
1434
+ let outside = this.sliderValue <= from.getTime() || this.sliderValue > to.getTime();
1435
+ if (this.sliderAnchored || outside) {
1436
+ this.sliderAnchored = true;
1437
+ this.sliderValue = to.getTime();
1438
+ }
1439
+ req.TablesTo = new Date(this.sliderValue).toISOString();
1440
+ req.TablesFrom = new Date(Math.max(from.getTime(), this.sliderValue - TABLES_WINDOW_MS)).toISOString();
1441
+ return req;
1442
+ }
1443
+ applyResult(req, res) {
1444
+ this.result = res;
1445
+ this.tables = res?.Tables ?? [];
1446
+ this.chartFrom = new Date(req.From).getTime();
1447
+ this.chartTo = new Date(req.To).getTime();
1448
+ this.sliderStep = Math.max(1, res?.BucketSeconds || 1) * 1000;
1449
+ this.sliderMin = Math.min(this.chartTo, this.chartFrom + TABLES_WINDOW_MS);
1450
+ this.sliderMax = this.chartTo;
1451
+ this.sliderValue = new Date(req.TablesTo).getTime();
1452
+ this.updateTablesWindowLabel();
1453
+ if (!res) {
1454
+ this.chartData = null;
1455
+ this.legend = [];
1456
+ this.lastChartKey = null;
1457
+ return;
1458
+ }
1459
+ let memoryFrom = res.MemoryFrom ? this.parseUtc(res.MemoryFrom) : null;
1460
+ this.memoryFrom = memoryFrom && memoryFrom.getTime() > this.chartFrom ? this.fmt(memoryFrom) : null;
1461
+ // Se è cambiata solo la finestra delle tabelle il grafico resta quello di prima: ridisegnarlo lo farebbe lampeggiare
1462
+ let chartKey = JSON.stringify({ ...req, TablesFrom: null, TablesTo: null, Tables: null, TopCount: null });
1463
+ if (chartKey == this.lastChartKey && this.chartData) {
1464
+ this.drawTablesWindow();
1465
+ return;
1466
+ }
1467
+ this.lastChartKey = chartKey;
1468
+ let times = (res.Times ?? []).map(t => this.parseUtc(t));
1469
+ let fixed = res.Dimension == TopActivityDimension.WaitClass || res.Dimension == TopActivityDimension.EventType;
1470
+ let uom = this.lc.loc("sessioni");
1471
+ let series = [];
1472
+ this.colors = {};
1473
+ this.legend = (res.Components ?? []).map((c, i) => {
1474
+ let color = c.Other ? OTHER_COLOR : (fixed && FIXED_COLORS[c.Key]) || PALETTE[i % PALETTE.length];
1475
+ if (c.Key != null)
1476
+ this.colors[c.Key] = color;
1477
+ let label = this.valueLabel(c.Label, c.Other, res.Dimension);
1478
+ series.push(new LineSerie(label, times.map((t, j) => new DateValue(t, c.Values?.[j] ?? 0)), null, color, uom));
1479
+ return { key: c.Key, label: label, color: color, other: c.Other, activity: c.Activity, percentage: c.Percentage };
1480
+ });
1481
+ // L'attività totale è una linea scura sopra le aree impilate, non impilata a sua volta
1482
+ if (res.TotalActivity)
1483
+ series.push(new LineSerie(this.lc.loc("Attività totale"), times.map((t, j) => new DateValue(t, res.TotalActivity[j] ?? 0)), null, TOTAL_COLOR, uom, false, false));
1484
+ let limit = null;
1485
+ if (res.MaximumThreads != null)
1486
+ limit = new Limit(res.MaximumThreads, this.lc.loc(+this.maxThreadsKind == TopActivityMaximumThreads.Cores ? "CPU Massime" : "Thread Massimi"));
1487
+ // L'asse Y non si allarga da solo per le linee limite
1488
+ let peak = Math.max(res.PeakActiveSessions ?? 0, ...(res.TotalActivity ?? [0]));
1489
+ this.chartMax = limit ? Math.max(peak, res.MaximumThreads) * 1.1 : undefined;
1490
+ this.chartData = series.length > 0 ? new LineChartData(series, limit) : null;
1491
+ }
1492
+ // #endregion
1493
+ // #region Grafico, legenda e filtri
1494
+ onChartLoaded(chart) {
1495
+ // Il grafico viene ricreato a ogni cambio di dati: l'area della finestra delle tabelle va rifatta
1496
+ this.chart = chart;
1497
+ this.windowAxis = null;
1498
+ this.drawTablesWindow();
1499
+ }
1500
+ drawTablesWindow() {
1501
+ let dateAxis = this.chart?.ChartService?.dateAxis;
1502
+ if (!dateAxis || !this.sliderMax)
1503
+ return;
1504
+ let end = this.sliderValue;
1505
+ let start = Math.max(this.chartFrom, end - TABLES_WINDOW_MS);
1506
+ this.zone.runOutsideAngular(() => {
1507
+ if (this.windowAxis !== dateAxis) {
1508
+ this.windowAxis = dateAxis;
1509
+ this.windowRange = dateAxis.makeDataItem({ value: start, endValue: end });
1510
+ dateAxis.createAxisRange(this.windowRange);
1511
+ this.windowRange.get("axisFill")?.setAll({ fill: am5.color(0x546e7a), fillOpacity: 0.18, visible: true });
1512
+ this.windowRange.get("grid")?.set("forceHidden", true);
1513
+ this.windowRange.get("label")?.set("forceHidden", true);
1514
+ }
1515
+ else
1516
+ this.windowRange.setAll({ value: start, endValue: end });
1517
+ });
1518
+ }
1519
+ addFilter(item) {
1520
+ // Il componente "altri" non è filtrabile, come in Oracle
1521
+ if (item.other || !this.result)
1522
+ return;
1523
+ let filter = new TopActivityV2Filter();
1524
+ filter.Dimension = this.result.Dimension;
1525
+ filter.Value = item.key ?? "";
1526
+ if (this.filters.some(t => t.filter.Dimension == filter.Dimension && t.filter.Value == filter.Value))
1527
+ return;
1528
+ this.filters.push({ filter: filter, label: `${this.dimensionLabel(filter.Dimension)} = ${item.label}` });
1529
+ this.totalActivity = true;
1530
+ this.search();
1531
+ }
1532
+ removeFilter(index) {
1533
+ this.filters.splice(index, 1);
1534
+ this.search();
1535
+ }
1536
+ // #endregion
1537
+ // #region Time slider e tabelle
1538
+ sliderMoving() {
1539
+ this.updateTablesWindowLabel();
1540
+ this.drawTablesWindow();
1541
+ }
1542
+ sliderChanged() {
1543
+ // Il range input arrotonda al passo: "agganciato" se è a meno di un passo dalla fine
1544
+ this.sliderAnchored = this.sliderMax - this.sliderValue < this.sliderStep;
1545
+ this.search();
1546
+ }
1547
+ updateTablesWindowLabel() {
1548
+ if (!this.sliderMax) {
1549
+ this.tablesWindowLabel = "";
1550
+ return;
1551
+ }
1552
+ let start = Math.max(this.chartFrom, this.sliderValue - TABLES_WINDOW_MS);
1553
+ this.tablesWindowLabel = `${this.fmt(new Date(start))} - ${this.fmt(new Date(this.sliderValue))}`;
1554
+ }
1555
+ isClickable(item) {
1556
+ return !item.Other && (!!item.Session || !!item.Event?.EventId);
1557
+ }
1558
+ openItem(item) {
1559
+ if (!this.isClickable(item))
1560
+ return;
1561
+ if (item.Session)
1562
+ this.loadSessionDetails(item.Session);
1563
+ else
1564
+ this.loadNodeDetails(item.Event.EventId);
1565
+ }
1566
+ breakdownWidth(item, part) {
1567
+ return item.Activity ? part.Activity / item.Activity * 100 : 0;
1568
+ }
1569
+ breakdownColor(part) {
1570
+ return part.Other || part.Key == null ? OTHER_COLOR : this.colors[part.Key] ?? OTHER_COLOR;
1571
+ }
1572
+ itemLabel(item, table) {
1573
+ return this.valueLabel(item.Label, item.Other, table.Dimension);
1574
+ }
1575
+ loadNodeDetails(nodeId) {
1576
+ this.NodeDetails = null;
1577
+ this.dlgNodeDetails.show();
1578
+ requestAnimationFrame(async () => {
1579
+ this.NodeDetails = await this.adms.getNodeStats(this.be, nodeId, this.instance != INSTANCE_CURRENT);
1580
+ });
1581
+ }
1582
+ loadSessionDetails(session) {
1583
+ this.SessionDetails = null;
1584
+ this.dlgSessionDetails.show();
1585
+ requestAnimationFrame(async () => {
1586
+ this.SessionDetails = await this.adms.getSessionStats(this.be, session.SessionId?.toString(), session.SerialNo?.toString(), session.InstanceId);
1587
+ });
1588
+ }
1589
+ // #endregion
1590
+ // #region Aggiornamento automatico
1591
+ refreshChanged() {
1592
+ clearInterval(this.refreshTimer);
1593
+ this.refreshTimer = null;
1594
+ let seconds = +this.refreshSeconds;
1595
+ if (seconds > 0)
1596
+ this.refreshTimer = setInterval(() => this.shiftWindow(), seconds * 1000);
1597
+ }
1598
+ // Sposta in avanti la finestra della stessa durata: lo slider agganciato la segue, quello spostato resta dov'è
1599
+ shiftWindow() {
1600
+ if (this.loading)
1601
+ return;
1602
+ let to = this.toDate(this.filterto) ?? new Date();
1603
+ let from = this.toDate(this.filterfrom) ?? new Date(to.getTime() - 3600 * 1000);
1604
+ let now = new Date();
1605
+ this.filterfrom = this.fmt(new Date(now.getTime() - (to.getTime() - from.getTime())));
1606
+ this.filterto = this.fmt(now);
1607
+ this.search();
1608
+ }
1609
+ // #endregion
1610
+ // #region Supporto
1611
+ buildSources() {
1612
+ this.DataSources = [
1613
+ new KeyValue(String(TopActivityDataSource.MemoryAndStored), this.lc.loc("Memoria e storico")),
1614
+ new KeyValue(String(TopActivityDataSource.Memory), this.lc.loc("Solo memoria"))
1615
+ ];
1616
+ this.Dimensions = Object.keys(TopActivityDimension).filter(t => !isNaN(+t)).map(t => new KeyValue(t, this.dimensionLabel(+t)));
1617
+ this.Resolutions = [
1618
+ new KeyValue(String(TopActivityResolution.Low), this.lc.loc("Bassa")),
1619
+ new KeyValue(String(TopActivityResolution.Medium), this.lc.loc("Media")),
1620
+ new KeyValue(String(TopActivityResolution.High), this.lc.loc("Alta")),
1621
+ new KeyValue(String(TopActivityResolution.Maximum), this.lc.loc("Massima"))
1622
+ ];
1623
+ this.ThreadKinds = [
1624
+ new KeyValue(String(TopActivityMaximumThreads.Threads), this.lc.loc("Worker thread")),
1625
+ new KeyValue(String(TopActivityMaximumThreads.Cores), "CPU")
1626
+ ];
1627
+ this.RefreshOptions = [
1628
+ new KeyValue("0", this.lc.loc("Spento")),
1629
+ new KeyValue("15", "15 s"),
1630
+ new KeyValue("30", "30 s"),
1631
+ new KeyValue("60", "60 s")
1632
+ ];
1633
+ this.Instances = [
1634
+ new KeyValue(INSTANCE_CURRENT, this.lc.loc("Istanza corrente")),
1635
+ new KeyValue(INSTANCE_ALL, this.lc.loc("Tutte le istanze"))
1636
+ ];
1637
+ }
1638
+ async loadInstances() {
1639
+ try {
1640
+ let instances = await this.adms.getInstances(this.be);
1641
+ this.Instances = [...this.Instances.slice(0, 2), ...(instances ?? []).map(t => new KeyValue(t.id, t.description))];
1642
+ }
1643
+ catch {
1644
+ // Senza elenco istanze restano "corrente" e "tutte"
1645
+ }
1646
+ }
1647
+ async refreshInfos() {
1648
+ // DirectorInfos è facoltativo: se scoppia la pagina va avanti lo stesso
1649
+ this.DirInfos = await this.adms.getInfos(this.be);
1650
+ }
1651
+ dimensionLabel(dimension) {
1652
+ switch (dimension) {
1653
+ case TopActivityDimension.WaitClass: return this.lc.loc("Classe di attesa");
1654
+ case TopActivityDimension.EventType: return this.lc.loc("Tipo evento");
1655
+ case TopActivityDimension.WaitEvent: return this.lc.loc("Nodo");
1656
+ case TopActivityDimension.Instance: return this.lc.loc("Istanza");
1657
+ case TopActivityDimension.UserSession: return this.lc.loc("Sessione");
1658
+ case TopActivityDimension.UserName: return this.lc.loc("Utente");
1659
+ case TopActivityDimension.Tenant: return this.lc.loc("Tenant");
1660
+ case TopActivityDimension.SessionType: return this.lc.loc("Tipo sessione");
1661
+ case TopActivityDimension.BackgroundSession: return this.lc.loc("Sessione di background");
1662
+ case TopActivityDimension.JobName: return this.lc.loc("Job");
1663
+ case TopActivityDimension.ConsumerGroup: return this.lc.loc("Gruppo di consumo");
1664
+ case TopActivityDimension.ThreadType: return this.lc.loc("Tipo thread");
1665
+ case TopActivityDimension.Api: return "API";
1666
+ case TopActivityDimension.Machine: return this.lc.loc("Macchina");
1667
+ }
1668
+ return String(dimension);
1669
+ }
1670
+ valueLabel(label, other, dimension) {
1671
+ if (other)
1672
+ return this.lc.loc("Altri");
1673
+ if (label == null || label === "")
1674
+ return this.lc.loc(dimension == TopActivityDimension.WaitEvent ? "(non attribuito)" : "(nessuno)");
1675
+ return label;
1676
+ }
1677
+ applySettings(saved) {
1678
+ this.instance = saved.instance ?? this.instance;
1679
+ this.dataSource = saved.dataSource ?? this.dataSource;
1680
+ this.dimension = saved.dimension ?? this.dimension;
1681
+ this.resolution = saved.resolution ?? this.resolution;
1682
+ this.totalActivity = saved.totalActivity ?? this.totalActivity;
1683
+ this.maxCpu = saved.maxCpu ?? this.maxCpu;
1684
+ this.maxThreadsKind = saved.maxThreadsKind ?? this.maxThreadsKind;
1685
+ this.tableDims = saved.tableDims?.length == 2 ? saved.tableDims : this.tableDims;
1686
+ this.filters = saved.filters ?? [];
1687
+ }
1688
+ saveSettings() {
1689
+ let settings = {
1690
+ instance: this.instance,
1691
+ dataSource: this.dataSource,
1692
+ dimension: this.dimension,
1693
+ resolution: this.resolution,
1694
+ totalActivity: this.totalActivity,
1695
+ maxCpu: this.maxCpu,
1696
+ maxThreadsKind: this.maxThreadsKind,
1697
+ tableDims: this.tableDims,
1698
+ filters: this.filters
1699
+ };
1700
+ this.localStorage.setItem(STORAGE_KEY, settings).subscribe();
1701
+ }
1702
+ setLastHour() {
1703
+ let now = new Date();
1704
+ this.filterfrom = this.fmt(new Date(now.getTime() - 3600 * 1000));
1705
+ this.filterto = this.fmt(now);
1706
+ }
1707
+ toDate(value) {
1708
+ return value ? this.dateExts.getDateConvertion(value).toDate() : null;
1709
+ }
1710
+ fmt(date) {
1711
+ return dayjs(date).format(this.lc.token("getFullDateDisplayFormat"));
1712
+ }
1713
+ // Le date della risposta sono UTC "dd/MM/yyyy HH:mm:ss" senza fuso
1714
+ parseUtc(value) {
1715
+ let m = /^(\d{2})\/(\d{2})\/(\d{4}) (\d{2}):(\d{2}):(\d{2})$/.exec(value ?? "");
1716
+ return m ? new Date(Date.UTC(+m[3], +m[2] - 1, +m[1], +m[4], +m[5], +m[6])) : new Date(value);
1717
+ }
1718
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: AdmTopActivityV2Component, deps: [{ token: i1$1.DateService }, { token: i1.LocalizationService }, { token: i3.PreferencesService }, { token: i1$1.InterComService }, { token: i4.ActivatedRoute }, { token: AdminModuleBaseService }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); }
1719
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", type: AdmTopActivityV2Component, isStandalone: false, selector: "topact_v2", providers: [{ provide: LocalizationService, useClass: AdmTopActivityV2Loc }], viewQueries: [{ propertyName: "dlgNodeDetails", first: true, predicate: ["dlgNodeDetails"], descendants: true }, { propertyName: "dlgSessionDetails", first: true, predicate: ["dlgSessionDetails"], descendants: true }], ngImport: i0, template: "<div class=\"row\">\r\n <div class=\"col-12\">\r\n <div class=\"card app-overflow-visible\" widget>\r\n <div class=\"card-header\">\r\n <span class=\"text-uppercase\">\r\n {{'Attivit\u00E0 Principali' | localize: lc}}\r\n @if (DirInfos) {\r\n <span>&nbsp;-&nbsp;\r\n {{'' + DirInfos.cpus + ' ' + ('CPU' | localize: lc) + (DirInfos.rssplan ? ' - ' + DirInfos.rssplan : '')}}&nbsp;\r\n Load average: {{ DirInfos.lavg1 | number : '1.1-2' : 'it'}}&nbsp;{{ DirInfos.lavg5 | number : '1.1-2' : 'it'}}&nbsp;{{ DirInfos.lavg15 | number : '1.1-2' : 'it'}}\r\n </span>\r\n }\r\n </span>\r\n </div>\r\n <div class=\"card-body widget-body\">\r\n <form role=\"form\">\r\n <div class=\"row app-first-filter-line\">\r\n <div class=\"col-xl-2 col-lg-2 col-md-6 app-filter-wrapper\">\r\n <form-datetime [readonly]=\"refreshSeconds != '0'\" [FormLayout]=\"false\" [Validation]=\"false\" [(ngModel)]=\"filterfrom\" name=\"filterfrom\" [Placeholder]=\"'Da' | localize: lc\"></form-datetime>\r\n </div>\r\n <div class=\"col-xl-2 col-lg-2 col-md-6 app-filter-wrapper\">\r\n <form-datetime [readonly]=\"refreshSeconds != '0'\" [FormLayout]=\"false\" [Validation]=\"false\" [(ngModel)]=\"filterto\" name=\"filterto\" [Placeholder]=\"'A' | localize: lc\"></form-datetime>\r\n </div>\r\n <div class=\"col-xl-2 col-lg-2 col-md-6 app-filter-wrapper\">\r\n <form-select [FormLayout]=\"false\" [Validation]=\"false\" [(ngModel)]=\"instance\" name=\"instance\" [Source]=\"Instances\" [Placeholder]=\"'Istanza' | localize: lc\" (inputChange)=\"search()\"></form-select>\r\n </div>\r\n <div class=\"col-xl-2 col-lg-2 col-md-6 app-filter-wrapper\">\r\n <form-select [FormLayout]=\"false\" [Validation]=\"false\" [(ngModel)]=\"dataSource\" name=\"dataSource\" [Source]=\"DataSources\" [Placeholder]=\"'Origine dati' | localize: lc\" (inputChange)=\"search()\"></form-select>\r\n </div>\r\n <div class=\"col-xl-1 col-lg-1 col-md-2\">\r\n <button [disabled]=\"loading || refreshSeconds != '0'\" class=\"input-group-addon btn app-btn-search app-margin-top-20\" (click)=\"searchClicked()\" type=\"submit\"><i class=\"fa\" [class.fa-search]=\"!loading\" [class.fa-spinner]=\"loading\" [class.fa-spin]=\"loading\"></i></button>\r\n </div>\r\n <div class=\"col-xl-2 col-lg-2 col-md-4 app-filter-wrapper\">\r\n <form-select [FormLayout]=\"false\" [Validation]=\"false\" [(ngModel)]=\"refreshSeconds\" name=\"refreshSeconds\" [Source]=\"RefreshOptions\" [Placeholder]=\"'Aggiornamento automatico' | localize: lc\" (inputChange)=\"refreshChanged()\"></form-select>\r\n </div>\r\n </div>\r\n <div class=\"row\">\r\n <div class=\"col-xl-2 col-lg-2 col-md-6 app-filter-wrapper\">\r\n <form-select [FormLayout]=\"false\" [Validation]=\"false\" [(ngModel)]=\"dimension\" name=\"dimension\" [Source]=\"Dimensions\" [Placeholder]=\"'Dimensione' | localize: lc\" (inputChange)=\"search()\"></form-select>\r\n </div>\r\n <div class=\"col-xl-2 col-lg-2 col-md-6 app-filter-wrapper\">\r\n <form-select [FormLayout]=\"false\" [Validation]=\"false\" [(ngModel)]=\"resolution\" name=\"resolution\" [Source]=\"Resolutions\" [Placeholder]=\"'Risoluzione' | localize: lc\" (inputChange)=\"search()\"></form-select>\r\n </div>\r\n <div class=\"col-xl-2 col-lg-2 col-md-6 app-filter-wrapper\" style=\"display: flex; height: 38px;\">\r\n <form-checkbox [FormLayout]=\"false\" [SliderMode]=\"true\" [Validation]=\"false\" [(ngModel)]=\"totalActivity\" name=\"totalActivity\" (inputChange)=\"search()\"></form-checkbox>&nbsp;\r\n <span class=\"app-padding-top-5 app-padding-left-5\">{{'Attivit\u00E0 totale' | localize: lc}}</span>\r\n </div>\r\n <div class=\"col-xl-2 col-lg-2 col-md-6 app-filter-wrapper\" style=\"display: flex; height: 38px;\">\r\n <form-checkbox [FormLayout]=\"false\" [SliderMode]=\"true\" [Validation]=\"false\" [(ngModel)]=\"maxCpu\" name=\"maxCpu\" (inputChange)=\"search()\"></form-checkbox>&nbsp;\r\n <span class=\"app-padding-top-5 app-padding-left-5\">{{'Limite' | localize: lc}}</span>\r\n </div>\r\n <div class=\"col-xl-2 col-lg-2 col-md-6 app-filter-wrapper\">\r\n <form-select [FormLayout]=\"false\" [Validation]=\"false\" [disabled]=\"!maxCpu\" [(ngModel)]=\"maxThreadsKind\" name=\"maxThreadsKind\" [Source]=\"ThreadKinds\" (inputChange)=\"search()\"></form-select>\r\n </div>\r\n </div>\r\n </form>\r\n\r\n @if (filters.length > 0) {\r\n <div class=\"app-margin-top-5\">\r\n <strong>{{'Filtri' | localize: lc}}:</strong>&nbsp;\r\n @for (chip of filters; track $index) {\r\n <span style=\"display: inline-block; border: 1px solid #bbb; border-radius: 12px; padding: 1px 8px; margin: 0 5px 3px 0;\">\r\n {{chip.label}}&nbsp;<a class=\"app-pointer\" [title]=\"'Rimuovi' | localize: lc\" (click)=\"removeFilter($index)\">&times;</a>\r\n </span>\r\n }\r\n </div>\r\n }\r\n\r\n @if (memoryFrom) {\r\n <div class=\"app-margin-top-5\"><small><em>{{'Prima di questo istante i dati vengono dallo storico (un campione ogni 10 secondi)' | localize: lc}}: {{memoryFrom}}</em></small></div>\r\n }\r\n\r\n <div class=\"row\">\r\n <div class=\"col-md-12 app-margin-top-5\">\r\n <div style=\"width: 100%; height: 300px\">\r\n @if (chartData) {\r\n <es-line-chart [MinGridDistance]=\"65\"\r\n [Timeline]=\"false\"\r\n [FinalInstants]=\"false\"\r\n [Stacked]=\"true\"\r\n [Step]=\"true\"\r\n [Fill]=\"true\"\r\n [MergeDatasets]=\"true\"\r\n [Data]=\"chartData\"\r\n [Max]=\"chartMax\"\r\n [Legend]=\"false\"\r\n [YTitle]=\"'Sessioni attive medie' | localize: lc\"\r\n name=\"topactv2\"\r\n [DataGroupingThreshold]=\"99999999\"\r\n (chartLoaded)=\"onChartLoaded($event)\"></es-line-chart>\r\n } @else {\r\n <p><em>{{(loading ? 'Carico i Dati' : 'Nessun dato nella finestra') | localize: lc}}</em></p>\r\n }\r\n </div>\r\n\r\n <!-- Legenda: un clic su una voce la aggiunge ai filtri (\"altri\" non \u00E8 filtrabile) -->\r\n <div class=\"app-margin-top-5\">\r\n @for (item of legend; track $index) {\r\n <span [class.app-pointer]=\"!item.other\" [title]=\"item.other ? '' : ('Filtra' | localize: lc)\" (click)=\"addFilter(item)\" style=\"display: inline-block; margin: 0 12px 3px 0; white-space: nowrap;\">\r\n <span [style.background]=\"item.color\" style=\"display: inline-block; width: 10px; height: 10px; margin-right: 4px;\"></span>{{item.label}}&nbsp;<small>{{item.percentage | number : '1.0-1' : 'it'}}%</small>\r\n </span>\r\n }\r\n @if (result?.TotalActivity) {\r\n <span style=\"display: inline-block; margin: 0 12px 3px 0; white-space: nowrap;\">\r\n <span style=\"display: inline-block; width: 14px; height: 2px; background: #212121; margin: 0 4px 3px 0;\"></span>{{'Attivit\u00E0 totale' | localize: lc}}\r\n </span>\r\n }\r\n </div>\r\n\r\n @if (sliderMax > sliderMin) {\r\n <div style=\"display: flex; align-items: center; gap: 10px;\" class=\"app-margin-top-5\">\r\n <span class=\"app-nowrap\"><strong>{{'Tabelle' | localize: lc}}</strong>: {{tablesWindowLabel}}</span>\r\n <input type=\"range\" style=\"flex-grow: 1;\" [min]=\"sliderMin\" [max]=\"sliderMax\" [step]=\"sliderStep\" [ngModel]=\"sliderValue\" (ngModelChange)=\"sliderValue = $event; sliderMoving()\" (change)=\"sliderChanged()\" />\r\n @if (sliderAnchored) {\r\n <small class=\"app-nowrap\"><em>{{'agganciato alla fine' | localize: lc}}</em></small>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div class=\"row app-margin-top-5\">\r\n @for (table of tables; track $index; let i = $index) {\r\n <div class=\"col-md-6\">\r\n <div style=\"display: flex; align-items: center; gap: 8px;\">\r\n <div style=\"width: 220px;\">\r\n <form-select [FormLayout]=\"false\" [Validation]=\"false\" [(ngModel)]=\"tableDims[i]\" name=\"tabledim{{i}}\" [Source]=\"Dimensions\" (inputChange)=\"search()\"></form-select>\r\n </div>\r\n <small>{{'Attivit\u00E0' | localize: lc}}: {{table.Activity | number : '1.1-3' : 'it'}}</small>\r\n </div>\r\n <es-table [(ngModel)]=\"table.Items\" [HidePaging]=\"true\">\r\n <ng-template #header>\r\n <th>{{dimensionLabel(table.Dimension)}}</th>\r\n <th class=\"app-col-min\">{{'Attivit\u00E0' | localize: lc}}</th>\r\n <th class=\"app-col-min\">%</th>\r\n @if (table.Dimension == Dim.UserSession) {\r\n <th>{{'Utente' | localize: lc}}</th>\r\n <th>{{'Tenant' | localize: lc}}</th>\r\n <th>{{'Tipo' | localize: lc}}</th>\r\n <th>{{'Processo' | localize: lc}}</th>\r\n <th>{{'Gruppo di consumo' | localize: lc}}</th>\r\n }\r\n @if (table.Dimension == Dim.WaitEvent) {\r\n <th>{{'Tipo evento' | localize: lc}}</th>\r\n <th>{{'Classe di attesa' | localize: lc}}</th>\r\n }\r\n </ng-template>\r\n <ng-template #body let-item>\r\n <td>\r\n @if (isClickable(item)) {\r\n <span class=\"app-link\" [title]=\"item.Key\" (click)=\"openItem(item)\">{{itemLabel(item, table)}}</span>\r\n } @else {\r\n <span>{{itemLabel(item, table)}}</span>\r\n }\r\n </td>\r\n <td class=\"app-nowrap\">\r\n <div style=\"display: flex; align-items: center; gap: 6px;\">\r\n <div style=\"display: flex; width: 90px; height: 10px; background: #eee;\">\r\n @for (part of item.Breakdown; track $index) {\r\n <div [style.width.%]=\"breakdownWidth(item, part)\" [style.background]=\"breakdownColor(part)\"></div>\r\n }\r\n </div>\r\n {{item.Activity | number : '1.2-2' : 'it'}}\r\n </div>\r\n </td>\r\n <td class=\"app-nowrap\">{{item.Percentage | number : '1.1-2' : 'it'}} %</td>\r\n @if (table.Dimension == Dim.UserSession) {\r\n <td>{{item.Session?.UserName}}</td>\r\n <td>{{item.Session?.TenantId}}</td>\r\n <td>{{item.Session?.SessionType}}</td>\r\n <td>{{item.Session?.BgProcessName || item.Session?.JobName}}</td>\r\n <td>{{item.Session?.ConsumerGroup}}</td>\r\n }\r\n @if (table.Dimension == Dim.WaitEvent) {\r\n <td>{{item.Event?.EventType}}</td>\r\n <td>{{item.Event?.WaitClass}}</td>\r\n }\r\n </ng-template>\r\n </es-table>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<div bsModal #dlgNodeDetails=\"bs-modal\" class=\"modal fade\" role=\"dialog\" aria-hidden=\"true\">\r\n @if (NodeDetails) {\r\n <node-detail [NodeDetails]=\"NodeDetails\" (onClose)=\"dlgNodeDetails.hide()\"></node-detail>\r\n }\r\n</div>\r\n\r\n<div bsModal #dlgSessionDetails=\"bs-modal\" class=\"modal fade\" role=\"dialog\" aria-hidden=\"true\">\r\n @if (SessionDetails) {\r\n <session-detail [SessionDetails]=\"SessionDetails\" (onClose)=\"dlgSessionDetails.hide()\" (onNodeSelected)=\"loadNodeDetails($event)\"></session-detail>\r\n }\r\n</div>\r\n", dependencies: [{ kind: "directive", type: i7.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i7.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i7.RangeValueAccessor, selector: "input[type=range][formControlName],input[type=range][formControl],input[type=range][ngModel]" }, { kind: "directive", type: i7.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i7.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i7.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i7.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i8$1.ModalDirective, selector: "[bsModal]", inputs: ["config", "closeInterceptor"], outputs: ["onShow", "onShown", "onHide", "onHidden"], exportAs: ["bs-modal"] }, { kind: "component", type: i8.EsTableComponent, selector: "es-table", inputs: ["globalCheck", "autoUpdate", "seconds", "researchInProgress", "globalColVisCheck", "EsThTdProvider", "CornerMenuOptions", "ColumnsResizable", "ColumnsPinnable", "AutoUpdate", "Height", "EmptySpaceBackgroundColor", "EsTableHandledSearch", "MaxHeight", "ContainerClass", "XLSXExport", "CSVExport", "AllSearch", "HighCellDensity", "Selection", "ShowLoadingOnBootstrap", "Removal", "RemovalCondition", "Export", "ShiftClick", "CountLabel", "HidePaging", "HidePagingCount", "HidePagingButtons", "ContextMenu", "ExportFileName", "TableClass", "ExportFunction", "ExportOnlyVisibleColumns", "HasHeaderGroup", "HasSecondaryHeaderGroup", "HeaderHidden", "BodyHidden", "SelectionDisabled", "SingleSelection", "RowClassAssigner", "OrderByColumn", "MultipleOrderingDirectives", "HiddenColumns", "ColumnsOrdering", "SearchView", "SelectAll", "UseArrayModePaging", "DynamicRowColumnsDefinition", "DynamicOperations", "Hierarchy", "ParentKey", "OwnKey", "AutoSortHierarchy", "StartsExpanded", "SavePreferences", "DefaultAlignment", "PagingStyle", "RowGroupingPagingStyle", "ItemSourceProperty", "UseSelectionCache", "ShowItemGroupsColumns", "Editable", "RangeSelection", "SearchThrottle", "ArraymodeItemsPerPage"], outputs: ["globalCheckChange", "autoUpdateChange", "secondsChange", "researchInProgressChange", "globalColVisCheckChange", "onOrderChanged", "onSearchRequest", "onSelectionChanged", "onRemoval", "onAbortRemoval", "onModelChange", "onOpenContextMenu", "onCornerAction", "onDynamicOperation"] }, { kind: "component", type: i9.EsFormSelectComponent, selector: "form-select", inputs: ["OptionsTemplate"] }, { kind: "component", type: i9.EsFormDateTimeComponent, selector: "form-datetime" }, { kind: "component", type: i9.EsFormCheckboxComponent, selector: "form-checkbox", inputs: ["SliderMode"] }, { kind: "component", type: i2.EsLineChartComponent, selector: "es-line-chart", inputs: ["Data", "DataArray", "Step", "DataGroupBucketSize", "DataGroupingThreshold", "StrokeSize", "AllowGaps", "MergeDatasets", "FinalInstants", "Min", "Max", "Fill", "Stacked"] }, { kind: "directive", type: i10.Widget, selector: "[widget]" }, { kind: "component", type: SessionDetailComponent, selector: "session-detail", inputs: ["SessionDetails"], outputs: ["onClose", "onNodeSelected"] }, { kind: "component", type: NodeDetailComponent, selector: "node-detail", inputs: ["NodeDetails"], outputs: ["onClose"] }, { kind: "pipe", type: i5.DecimalPipe, name: "number" }, { kind: "pipe", type: i1.LocalizePipe, name: "localize" }] }); }
1720
+ }
1721
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: AdmTopActivityV2Component, decorators: [{
1722
+ type: Component,
1723
+ args: [{ selector: "topact_v2", providers: [{ provide: LocalizationService, useClass: AdmTopActivityV2Loc }], standalone: false, template: "<div class=\"row\">\r\n <div class=\"col-12\">\r\n <div class=\"card app-overflow-visible\" widget>\r\n <div class=\"card-header\">\r\n <span class=\"text-uppercase\">\r\n {{'Attivit\u00E0 Principali' | localize: lc}}\r\n @if (DirInfos) {\r\n <span>&nbsp;-&nbsp;\r\n {{'' + DirInfos.cpus + ' ' + ('CPU' | localize: lc) + (DirInfos.rssplan ? ' - ' + DirInfos.rssplan : '')}}&nbsp;\r\n Load average: {{ DirInfos.lavg1 | number : '1.1-2' : 'it'}}&nbsp;{{ DirInfos.lavg5 | number : '1.1-2' : 'it'}}&nbsp;{{ DirInfos.lavg15 | number : '1.1-2' : 'it'}}\r\n </span>\r\n }\r\n </span>\r\n </div>\r\n <div class=\"card-body widget-body\">\r\n <form role=\"form\">\r\n <div class=\"row app-first-filter-line\">\r\n <div class=\"col-xl-2 col-lg-2 col-md-6 app-filter-wrapper\">\r\n <form-datetime [readonly]=\"refreshSeconds != '0'\" [FormLayout]=\"false\" [Validation]=\"false\" [(ngModel)]=\"filterfrom\" name=\"filterfrom\" [Placeholder]=\"'Da' | localize: lc\"></form-datetime>\r\n </div>\r\n <div class=\"col-xl-2 col-lg-2 col-md-6 app-filter-wrapper\">\r\n <form-datetime [readonly]=\"refreshSeconds != '0'\" [FormLayout]=\"false\" [Validation]=\"false\" [(ngModel)]=\"filterto\" name=\"filterto\" [Placeholder]=\"'A' | localize: lc\"></form-datetime>\r\n </div>\r\n <div class=\"col-xl-2 col-lg-2 col-md-6 app-filter-wrapper\">\r\n <form-select [FormLayout]=\"false\" [Validation]=\"false\" [(ngModel)]=\"instance\" name=\"instance\" [Source]=\"Instances\" [Placeholder]=\"'Istanza' | localize: lc\" (inputChange)=\"search()\"></form-select>\r\n </div>\r\n <div class=\"col-xl-2 col-lg-2 col-md-6 app-filter-wrapper\">\r\n <form-select [FormLayout]=\"false\" [Validation]=\"false\" [(ngModel)]=\"dataSource\" name=\"dataSource\" [Source]=\"DataSources\" [Placeholder]=\"'Origine dati' | localize: lc\" (inputChange)=\"search()\"></form-select>\r\n </div>\r\n <div class=\"col-xl-1 col-lg-1 col-md-2\">\r\n <button [disabled]=\"loading || refreshSeconds != '0'\" class=\"input-group-addon btn app-btn-search app-margin-top-20\" (click)=\"searchClicked()\" type=\"submit\"><i class=\"fa\" [class.fa-search]=\"!loading\" [class.fa-spinner]=\"loading\" [class.fa-spin]=\"loading\"></i></button>\r\n </div>\r\n <div class=\"col-xl-2 col-lg-2 col-md-4 app-filter-wrapper\">\r\n <form-select [FormLayout]=\"false\" [Validation]=\"false\" [(ngModel)]=\"refreshSeconds\" name=\"refreshSeconds\" [Source]=\"RefreshOptions\" [Placeholder]=\"'Aggiornamento automatico' | localize: lc\" (inputChange)=\"refreshChanged()\"></form-select>\r\n </div>\r\n </div>\r\n <div class=\"row\">\r\n <div class=\"col-xl-2 col-lg-2 col-md-6 app-filter-wrapper\">\r\n <form-select [FormLayout]=\"false\" [Validation]=\"false\" [(ngModel)]=\"dimension\" name=\"dimension\" [Source]=\"Dimensions\" [Placeholder]=\"'Dimensione' | localize: lc\" (inputChange)=\"search()\"></form-select>\r\n </div>\r\n <div class=\"col-xl-2 col-lg-2 col-md-6 app-filter-wrapper\">\r\n <form-select [FormLayout]=\"false\" [Validation]=\"false\" [(ngModel)]=\"resolution\" name=\"resolution\" [Source]=\"Resolutions\" [Placeholder]=\"'Risoluzione' | localize: lc\" (inputChange)=\"search()\"></form-select>\r\n </div>\r\n <div class=\"col-xl-2 col-lg-2 col-md-6 app-filter-wrapper\" style=\"display: flex; height: 38px;\">\r\n <form-checkbox [FormLayout]=\"false\" [SliderMode]=\"true\" [Validation]=\"false\" [(ngModel)]=\"totalActivity\" name=\"totalActivity\" (inputChange)=\"search()\"></form-checkbox>&nbsp;\r\n <span class=\"app-padding-top-5 app-padding-left-5\">{{'Attivit\u00E0 totale' | localize: lc}}</span>\r\n </div>\r\n <div class=\"col-xl-2 col-lg-2 col-md-6 app-filter-wrapper\" style=\"display: flex; height: 38px;\">\r\n <form-checkbox [FormLayout]=\"false\" [SliderMode]=\"true\" [Validation]=\"false\" [(ngModel)]=\"maxCpu\" name=\"maxCpu\" (inputChange)=\"search()\"></form-checkbox>&nbsp;\r\n <span class=\"app-padding-top-5 app-padding-left-5\">{{'Limite' | localize: lc}}</span>\r\n </div>\r\n <div class=\"col-xl-2 col-lg-2 col-md-6 app-filter-wrapper\">\r\n <form-select [FormLayout]=\"false\" [Validation]=\"false\" [disabled]=\"!maxCpu\" [(ngModel)]=\"maxThreadsKind\" name=\"maxThreadsKind\" [Source]=\"ThreadKinds\" (inputChange)=\"search()\"></form-select>\r\n </div>\r\n </div>\r\n </form>\r\n\r\n @if (filters.length > 0) {\r\n <div class=\"app-margin-top-5\">\r\n <strong>{{'Filtri' | localize: lc}}:</strong>&nbsp;\r\n @for (chip of filters; track $index) {\r\n <span style=\"display: inline-block; border: 1px solid #bbb; border-radius: 12px; padding: 1px 8px; margin: 0 5px 3px 0;\">\r\n {{chip.label}}&nbsp;<a class=\"app-pointer\" [title]=\"'Rimuovi' | localize: lc\" (click)=\"removeFilter($index)\">&times;</a>\r\n </span>\r\n }\r\n </div>\r\n }\r\n\r\n @if (memoryFrom) {\r\n <div class=\"app-margin-top-5\"><small><em>{{'Prima di questo istante i dati vengono dallo storico (un campione ogni 10 secondi)' | localize: lc}}: {{memoryFrom}}</em></small></div>\r\n }\r\n\r\n <div class=\"row\">\r\n <div class=\"col-md-12 app-margin-top-5\">\r\n <div style=\"width: 100%; height: 300px\">\r\n @if (chartData) {\r\n <es-line-chart [MinGridDistance]=\"65\"\r\n [Timeline]=\"false\"\r\n [FinalInstants]=\"false\"\r\n [Stacked]=\"true\"\r\n [Step]=\"true\"\r\n [Fill]=\"true\"\r\n [MergeDatasets]=\"true\"\r\n [Data]=\"chartData\"\r\n [Max]=\"chartMax\"\r\n [Legend]=\"false\"\r\n [YTitle]=\"'Sessioni attive medie' | localize: lc\"\r\n name=\"topactv2\"\r\n [DataGroupingThreshold]=\"99999999\"\r\n (chartLoaded)=\"onChartLoaded($event)\"></es-line-chart>\r\n } @else {\r\n <p><em>{{(loading ? 'Carico i Dati' : 'Nessun dato nella finestra') | localize: lc}}</em></p>\r\n }\r\n </div>\r\n\r\n <!-- Legenda: un clic su una voce la aggiunge ai filtri (\"altri\" non \u00E8 filtrabile) -->\r\n <div class=\"app-margin-top-5\">\r\n @for (item of legend; track $index) {\r\n <span [class.app-pointer]=\"!item.other\" [title]=\"item.other ? '' : ('Filtra' | localize: lc)\" (click)=\"addFilter(item)\" style=\"display: inline-block; margin: 0 12px 3px 0; white-space: nowrap;\">\r\n <span [style.background]=\"item.color\" style=\"display: inline-block; width: 10px; height: 10px; margin-right: 4px;\"></span>{{item.label}}&nbsp;<small>{{item.percentage | number : '1.0-1' : 'it'}}%</small>\r\n </span>\r\n }\r\n @if (result?.TotalActivity) {\r\n <span style=\"display: inline-block; margin: 0 12px 3px 0; white-space: nowrap;\">\r\n <span style=\"display: inline-block; width: 14px; height: 2px; background: #212121; margin: 0 4px 3px 0;\"></span>{{'Attivit\u00E0 totale' | localize: lc}}\r\n </span>\r\n }\r\n </div>\r\n\r\n @if (sliderMax > sliderMin) {\r\n <div style=\"display: flex; align-items: center; gap: 10px;\" class=\"app-margin-top-5\">\r\n <span class=\"app-nowrap\"><strong>{{'Tabelle' | localize: lc}}</strong>: {{tablesWindowLabel}}</span>\r\n <input type=\"range\" style=\"flex-grow: 1;\" [min]=\"sliderMin\" [max]=\"sliderMax\" [step]=\"sliderStep\" [ngModel]=\"sliderValue\" (ngModelChange)=\"sliderValue = $event; sliderMoving()\" (change)=\"sliderChanged()\" />\r\n @if (sliderAnchored) {\r\n <small class=\"app-nowrap\"><em>{{'agganciato alla fine' | localize: lc}}</em></small>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div class=\"row app-margin-top-5\">\r\n @for (table of tables; track $index; let i = $index) {\r\n <div class=\"col-md-6\">\r\n <div style=\"display: flex; align-items: center; gap: 8px;\">\r\n <div style=\"width: 220px;\">\r\n <form-select [FormLayout]=\"false\" [Validation]=\"false\" [(ngModel)]=\"tableDims[i]\" name=\"tabledim{{i}}\" [Source]=\"Dimensions\" (inputChange)=\"search()\"></form-select>\r\n </div>\r\n <small>{{'Attivit\u00E0' | localize: lc}}: {{table.Activity | number : '1.1-3' : 'it'}}</small>\r\n </div>\r\n <es-table [(ngModel)]=\"table.Items\" [HidePaging]=\"true\">\r\n <ng-template #header>\r\n <th>{{dimensionLabel(table.Dimension)}}</th>\r\n <th class=\"app-col-min\">{{'Attivit\u00E0' | localize: lc}}</th>\r\n <th class=\"app-col-min\">%</th>\r\n @if (table.Dimension == Dim.UserSession) {\r\n <th>{{'Utente' | localize: lc}}</th>\r\n <th>{{'Tenant' | localize: lc}}</th>\r\n <th>{{'Tipo' | localize: lc}}</th>\r\n <th>{{'Processo' | localize: lc}}</th>\r\n <th>{{'Gruppo di consumo' | localize: lc}}</th>\r\n }\r\n @if (table.Dimension == Dim.WaitEvent) {\r\n <th>{{'Tipo evento' | localize: lc}}</th>\r\n <th>{{'Classe di attesa' | localize: lc}}</th>\r\n }\r\n </ng-template>\r\n <ng-template #body let-item>\r\n <td>\r\n @if (isClickable(item)) {\r\n <span class=\"app-link\" [title]=\"item.Key\" (click)=\"openItem(item)\">{{itemLabel(item, table)}}</span>\r\n } @else {\r\n <span>{{itemLabel(item, table)}}</span>\r\n }\r\n </td>\r\n <td class=\"app-nowrap\">\r\n <div style=\"display: flex; align-items: center; gap: 6px;\">\r\n <div style=\"display: flex; width: 90px; height: 10px; background: #eee;\">\r\n @for (part of item.Breakdown; track $index) {\r\n <div [style.width.%]=\"breakdownWidth(item, part)\" [style.background]=\"breakdownColor(part)\"></div>\r\n }\r\n </div>\r\n {{item.Activity | number : '1.2-2' : 'it'}}\r\n </div>\r\n </td>\r\n <td class=\"app-nowrap\">{{item.Percentage | number : '1.1-2' : 'it'}} %</td>\r\n @if (table.Dimension == Dim.UserSession) {\r\n <td>{{item.Session?.UserName}}</td>\r\n <td>{{item.Session?.TenantId}}</td>\r\n <td>{{item.Session?.SessionType}}</td>\r\n <td>{{item.Session?.BgProcessName || item.Session?.JobName}}</td>\r\n <td>{{item.Session?.ConsumerGroup}}</td>\r\n }\r\n @if (table.Dimension == Dim.WaitEvent) {\r\n <td>{{item.Event?.EventType}}</td>\r\n <td>{{item.Event?.WaitClass}}</td>\r\n }\r\n </ng-template>\r\n </es-table>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<div bsModal #dlgNodeDetails=\"bs-modal\" class=\"modal fade\" role=\"dialog\" aria-hidden=\"true\">\r\n @if (NodeDetails) {\r\n <node-detail [NodeDetails]=\"NodeDetails\" (onClose)=\"dlgNodeDetails.hide()\"></node-detail>\r\n }\r\n</div>\r\n\r\n<div bsModal #dlgSessionDetails=\"bs-modal\" class=\"modal fade\" role=\"dialog\" aria-hidden=\"true\">\r\n @if (SessionDetails) {\r\n <session-detail [SessionDetails]=\"SessionDetails\" (onClose)=\"dlgSessionDetails.hide()\" (onNodeSelected)=\"loadNodeDetails($event)\"></session-detail>\r\n }\r\n</div>\r\n" }]
1724
+ }], ctorParameters: () => [{ type: i1$1.DateService }, { type: i1.LocalizationService }, { type: i3.PreferencesService }, { type: i1$1.InterComService }, { type: i4.ActivatedRoute }, { type: AdminModuleBaseService }, { type: i0.NgZone }], propDecorators: { dlgNodeDetails: [{
1725
+ type: ViewChild,
1726
+ args: ["dlgNodeDetails"]
1727
+ }], dlgSessionDetails: [{
1728
+ type: ViewChild,
1729
+ args: ["dlgSessionDetails"]
1730
+ }] } });
1731
+
1097
1732
  class LocksView extends AppSearch {
1098
1733
  constructor() { super(); }
1099
1734
  }
@@ -1805,11 +2440,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImpo
1805
2440
  }], ctorParameters: () => [{ type: i1$1.InterComService }] });
1806
2441
 
1807
2442
  // Angular
2443
+ // La Top Activity v2 prende la rotta "topact" solo se DirectorFeature.TopActivityV2 è attivo nella configurazione del modulo,
2444
+ // altrimenti la guardia fallisce e la rotta successiva apre la pagina di sempre
2445
+ const topActivityV2Enabled = () => inject(ADM_TOPACT_V2, { optional: true }) ?? false;
1808
2446
  const routeConfig = [
1809
2447
  {
1810
2448
  path: "", children: [
1811
2449
  { path: "serverworkers/:be", component: AdmWrkComponent, data: { title: "Ricerca Processi Applicativi", internalNavigation: false } },
1812
2450
  { path: "profiler/:be", component: AdmProfilerComponent, data: { title: "Profiler del Server", internalNavigation: false } },
2451
+ { path: "topact/:be", component: AdmTopActivityV2Component, canMatch: [topActivityV2Enabled], data: { title: "Attività Principali", internalNavigation: false } },
1813
2452
  { path: "topact/:be", component: AdmTopActivitiesComponent, data: { title: "Attività Principali", internalNavigation: false } },
1814
2453
  { path: "metrics/:be", component: AdmMetricsComponent, data: { title: "Metriche di Sistema", internalNavigation: false } },
1815
2454
  { path: "serverlogs/:be", component: AdmLogsComponent, data: { title: "Log del Server", internalNavigation: false } },
@@ -1819,6 +2458,7 @@ const routeConfig = [
1819
2458
  { path: "metadataquery/:be", component: MtdQueryComponent, data: { title: "Ricerca Viste Dinamiche Applicative", internalNavigation: false } },
1820
2459
  { path: "serverworkers", component: AdmWrkComponent, data: { title: "Ricerca Processi Applicativi", internalNavigation: false } },
1821
2460
  { path: "profiler", component: AdmProfilerComponent, data: { title: "Profiler del Server", internalNavigation: false } },
2461
+ { path: "topact", component: AdmTopActivityV2Component, canMatch: [topActivityV2Enabled], data: { title: "Attività Principali", internalNavigation: false } },
1822
2462
  { path: "topact", component: AdmTopActivitiesComponent, data: { title: "Attività Principali", internalNavigation: false } },
1823
2463
  { path: "metrics", component: AdmMetricsComponent, data: { title: "Metriche di Sistema", internalNavigation: false } },
1824
2464
  { path: "serverlogs", component: AdmLogsComponent, data: { title: "Log del Server", internalNavigation: false } },
@@ -1854,6 +2494,8 @@ const COMPONENTS = [
1854
2494
  AdmWrkComponent,
1855
2495
  AdmProfilerComponent,
1856
2496
  AdmTopActivitiesComponent,
2497
+ AdmTopActivityV2Component,
2498
+ SessionDetailComponent,
1857
2499
  AdmDynamicViewsComponent,
1858
2500
  AdmLogsComponent,
1859
2501
  AdmAuditsComponent,
@@ -1876,6 +2518,8 @@ class AdmModule {
1876
2518
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.34", ngImport: i0, type: AdmModule, declarations: [AdmWrkComponent,
1877
2519
  AdmProfilerComponent,
1878
2520
  AdmTopActivitiesComponent,
2521
+ AdmTopActivityV2Component,
2522
+ SessionDetailComponent,
1879
2523
  AdmDynamicViewsComponent,
1880
2524
  AdmLogsComponent,
1881
2525
  AdmAuditsComponent,
@@ -1903,7 +2547,8 @@ class AdmModule {
1903
2547
  DirectivesModule, i4.RouterModule, FormatTimePipe] }); }
1904
2548
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: AdmModule, providers: [
1905
2549
  AdminModuleCommService,
1906
- { provide: ADM_CONVERSATIONS, useFactory: () => !!myconfig.TransactionTimeoutSeconds }
2550
+ { provide: ADM_CONVERSATIONS, useFactory: () => !!myconfig.TransactionTimeoutSeconds },
2551
+ { provide: ADM_TOPACT_V2, useFactory: () => !!myconfig?.DirectorFeature?.TopActivityV2 }
1907
2552
  ], imports: [MODULES] }); }
1908
2553
  }
1909
2554
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: AdmModule, decorators: [{
@@ -1913,7 +2558,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImpo
1913
2558
  declarations: [...COMPONENTS],
1914
2559
  providers: [
1915
2560
  AdminModuleCommService,
1916
- { provide: ADM_CONVERSATIONS, useFactory: () => !!myconfig.TransactionTimeoutSeconds }
2561
+ { provide: ADM_CONVERSATIONS, useFactory: () => !!myconfig.TransactionTimeoutSeconds },
2562
+ { provide: ADM_TOPACT_V2, useFactory: () => !!myconfig?.DirectorFeature?.TopActivityV2 }
1917
2563
  ]
1918
2564
  }]
1919
2565
  }], ctorParameters: () => [{ type: AdminModuleCommService }] });
@@ -1926,5 +2572,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImpo
1926
2572
  * Generated bundle index. Do not edit.
1927
2573
  */
1928
2574
 
1929
- export { ADM_CONVERSATIONS, ADM_MODULE_PATH, AdmMessageTypes, AdmModule, MK_ADM, routeConfig };
2575
+ export { ADM_CONVERSATIONS, ADM_MODULE_PATH, ADM_TOPACT_V2, AdmMessageTypes, AdmModule, MK_ADM, routeConfig, topActivityV2Enabled };
1930
2576
  //# sourceMappingURL=esfaenza-admin-module.mjs.map