myio-js-library 0.1.556 → 0.1.558
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/dist/index.cjs +1086 -786
- package/dist/index.d.cts +31 -2
- package/dist/index.d.ts +31 -2
- package/dist/index.js +1086 -786
- package/dist/myio-js-library.umd.js +1087 -787
- package/dist/myio-js-library.umd.min.js +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -908,7 +908,7 @@ module.exports = __toCommonJS(src_exports);
|
|
|
908
908
|
// package.json
|
|
909
909
|
var package_default = {
|
|
910
910
|
name: "myio-js-library",
|
|
911
|
-
version: "0.1.
|
|
911
|
+
version: "0.1.558",
|
|
912
912
|
description: "A clean, standalone JS SDK for MYIO projects",
|
|
913
913
|
license: "MIT",
|
|
914
914
|
repository: "github:gh-myio/myio-js-library",
|
|
@@ -12855,6 +12855,26 @@ var CSS_STRING = `
|
|
|
12855
12855
|
margin-left: 4px;
|
|
12856
12856
|
}
|
|
12857
12857
|
|
|
12858
|
+
/* Consumo ainda carregando (spinner no lugar do valor) */
|
|
12859
|
+
.myio-ho-card__value.is-loading .num {
|
|
12860
|
+
display: inline-flex;
|
|
12861
|
+
align-items: center;
|
|
12862
|
+
gap: 8px;
|
|
12863
|
+
font-size: 14px;
|
|
12864
|
+
font-weight: 600;
|
|
12865
|
+
color: var(--myio-muted, #9ca3af);
|
|
12866
|
+
}
|
|
12867
|
+
.myio-ho-card__value-spinner {
|
|
12868
|
+
width: 14px;
|
|
12869
|
+
height: 14px;
|
|
12870
|
+
border: 2px solid rgba(124, 58, 237, 0.2);
|
|
12871
|
+
border-top-color: var(--myio-brand-700, #7c3aed);
|
|
12872
|
+
border-radius: 50%;
|
|
12873
|
+
animation: myio-consumption-spin 0.8s linear infinite;
|
|
12874
|
+
flex-shrink: 0;
|
|
12875
|
+
}
|
|
12876
|
+
@keyframes myio-consumption-spin { to { transform: rotate(360deg); } }
|
|
12877
|
+
|
|
12858
12878
|
/* Efficiency bar */
|
|
12859
12879
|
.myio-ho-card__eff {
|
|
12860
12880
|
display: flex;
|
|
@@ -16527,7 +16547,20 @@ function paint(root, state6) {
|
|
|
16527
16547
|
const primaryValue = formatValueByDomain(entityObject.val, entityObject.domain);
|
|
16528
16548
|
const numSpan = root.querySelector(".myio-ho-card__value .num");
|
|
16529
16549
|
const unitSpan = root.querySelector(".myio-ho-card__value .unit");
|
|
16530
|
-
|
|
16550
|
+
const valueBox = root.querySelector(".myio-ho-card__value");
|
|
16551
|
+
const dataLoading = !!entityObject.dataLoading && entityObject.domain !== "temperature";
|
|
16552
|
+
if (valueBox) {
|
|
16553
|
+
valueBox.classList.toggle("is-loading", dataLoading);
|
|
16554
|
+
if (dataLoading) valueBox.setAttribute("aria-busy", "true");
|
|
16555
|
+
else valueBox.removeAttribute("aria-busy");
|
|
16556
|
+
}
|
|
16557
|
+
if (dataLoading) {
|
|
16558
|
+
numSpan.innerHTML = '<span class="myio-ho-card__value-spinner" aria-hidden="true"></span>Carregando\u2026';
|
|
16559
|
+
numSpan.title = "Carregando consumo do per\xEDodo";
|
|
16560
|
+
} else {
|
|
16561
|
+
numSpan.textContent = primaryValue;
|
|
16562
|
+
numSpan.removeAttribute("title");
|
|
16563
|
+
}
|
|
16531
16564
|
const barContainer = root.querySelector(".bar");
|
|
16532
16565
|
const effContainer = root.querySelector(".myio-ho-card__eff");
|
|
16533
16566
|
if (state6.enableSelection) {
|
|
@@ -53080,6 +53113,444 @@ function escapeHtml4(text2) {
|
|
|
53080
53113
|
return div.innerHTML;
|
|
53081
53114
|
}
|
|
53082
53115
|
|
|
53116
|
+
// src/services/alarm/AlarmApiClient.ts
|
|
53117
|
+
var AlarmApiClient = class {
|
|
53118
|
+
baseUrl = "";
|
|
53119
|
+
apiKey = "";
|
|
53120
|
+
configure(baseUrl, apiKey) {
|
|
53121
|
+
if (baseUrl) this.baseUrl = baseUrl;
|
|
53122
|
+
if (apiKey) this.apiKey = apiKey;
|
|
53123
|
+
}
|
|
53124
|
+
get headers() {
|
|
53125
|
+
return {
|
|
53126
|
+
"Content-Type": "application/json",
|
|
53127
|
+
"X-API-Key": this.apiKey
|
|
53128
|
+
};
|
|
53129
|
+
}
|
|
53130
|
+
async request(url, options) {
|
|
53131
|
+
const response = await fetch(url, {
|
|
53132
|
+
method: "GET",
|
|
53133
|
+
headers: this.headers,
|
|
53134
|
+
...options
|
|
53135
|
+
});
|
|
53136
|
+
if (!response.ok) {
|
|
53137
|
+
throw new Error(`Alarms API error: ${response.status} ${response.statusText}`);
|
|
53138
|
+
}
|
|
53139
|
+
return response.json();
|
|
53140
|
+
}
|
|
53141
|
+
// Some endpoints return { success: true, data: T[] } — unwrap safely
|
|
53142
|
+
unwrapArray(raw) {
|
|
53143
|
+
if (Array.isArray(raw)) return raw;
|
|
53144
|
+
if (raw !== null && typeof raw === "object" && "data" in raw) {
|
|
53145
|
+
const d = raw.data;
|
|
53146
|
+
return Array.isArray(d) ? d : [];
|
|
53147
|
+
}
|
|
53148
|
+
return [];
|
|
53149
|
+
}
|
|
53150
|
+
// -----------------------------------------------------------------------
|
|
53151
|
+
// Availability (existing endpoint)
|
|
53152
|
+
// -----------------------------------------------------------------------
|
|
53153
|
+
async getAvailability(params) {
|
|
53154
|
+
const query = new URLSearchParams({
|
|
53155
|
+
customerId: params.customerId,
|
|
53156
|
+
startAt: params.startAt,
|
|
53157
|
+
endAt: params.endAt,
|
|
53158
|
+
includeByDevice: String(params.includeByDevice ?? true)
|
|
53159
|
+
});
|
|
53160
|
+
if (params.deviceType) query.set("deviceType", params.deviceType);
|
|
53161
|
+
if (params.deviceIds) query.set("deviceIds", params.deviceIds);
|
|
53162
|
+
const raw = await this.request(
|
|
53163
|
+
`${this.baseUrl}/alarms/stats/availability?${query.toString()}`
|
|
53164
|
+
);
|
|
53165
|
+
return raw?.data ?? raw;
|
|
53166
|
+
}
|
|
53167
|
+
// -----------------------------------------------------------------------
|
|
53168
|
+
// Alarm list (GET /alarms)
|
|
53169
|
+
// -----------------------------------------------------------------------
|
|
53170
|
+
async getAlarms(params = {}) {
|
|
53171
|
+
const query = new URLSearchParams();
|
|
53172
|
+
if (params.state?.length) query.set("state", params.state.join(","));
|
|
53173
|
+
if (params.severity?.length) query.set("severity", params.severity.join(","));
|
|
53174
|
+
if (params.alarmType) query.set("alarmType", params.alarmType);
|
|
53175
|
+
if (params.from) query.set("from", params.from);
|
|
53176
|
+
if (params.to) query.set("to", params.to);
|
|
53177
|
+
if (params.customerId) query.set("customerId", params.customerId);
|
|
53178
|
+
query.set("limit", String(params.limit ?? 100));
|
|
53179
|
+
if (params.page && params.page > 1) query.set("page", String(params.page));
|
|
53180
|
+
const qs = query.toString();
|
|
53181
|
+
return this.request(
|
|
53182
|
+
`${this.baseUrl}/alarms${qs ? `?${qs}` : ""}`
|
|
53183
|
+
);
|
|
53184
|
+
}
|
|
53185
|
+
// -----------------------------------------------------------------------
|
|
53186
|
+
// Stats (GET /api/v1/alarms/stats)
|
|
53187
|
+
// -----------------------------------------------------------------------
|
|
53188
|
+
async getAlarmStats(tenantId, period) {
|
|
53189
|
+
const query = new URLSearchParams({ tenantId, period });
|
|
53190
|
+
const raw = await this.request(
|
|
53191
|
+
`${this.baseUrl}/alarms/stats?${query.toString()}`
|
|
53192
|
+
);
|
|
53193
|
+
return raw?.data ?? raw;
|
|
53194
|
+
}
|
|
53195
|
+
// -----------------------------------------------------------------------
|
|
53196
|
+
// Trend (GET /api/v1/alarms/stats/trend)
|
|
53197
|
+
// -----------------------------------------------------------------------
|
|
53198
|
+
async getAlarmTrend(tenantId, period, groupBy) {
|
|
53199
|
+
const query = new URLSearchParams({ tenantId, period, groupBy });
|
|
53200
|
+
const raw = await this.request(
|
|
53201
|
+
`${this.baseUrl}/alarms/stats/trend?${query.toString()}`
|
|
53202
|
+
);
|
|
53203
|
+
return this.unwrapArray(raw);
|
|
53204
|
+
}
|
|
53205
|
+
// -----------------------------------------------------------------------
|
|
53206
|
+
// Top offenders (GET /api/v1/alarms/stats/top-offenders)
|
|
53207
|
+
// -----------------------------------------------------------------------
|
|
53208
|
+
async getTopOffenders(tenantId, limit = 5) {
|
|
53209
|
+
const query = new URLSearchParams({ tenantId, limit: String(limit) });
|
|
53210
|
+
const raw = await this.request(
|
|
53211
|
+
`${this.baseUrl}/alarms/stats/top-offenders?${query.toString()}`
|
|
53212
|
+
);
|
|
53213
|
+
return this.unwrapArray(raw);
|
|
53214
|
+
}
|
|
53215
|
+
// -----------------------------------------------------------------------
|
|
53216
|
+
// Per-device stats (GET /api/v1/alarms/stats/by-device)
|
|
53217
|
+
// -----------------------------------------------------------------------
|
|
53218
|
+
async getAlarmStatsByDevice(tenantId) {
|
|
53219
|
+
const query = new URLSearchParams({ tenantId });
|
|
53220
|
+
const raw = await this.request(
|
|
53221
|
+
`${this.baseUrl}/alarms/stats/by-device?${query.toString()}`
|
|
53222
|
+
);
|
|
53223
|
+
return this.unwrapArray(raw);
|
|
53224
|
+
}
|
|
53225
|
+
// -----------------------------------------------------------------------
|
|
53226
|
+
// Alarm history (GET /alarms/:id/history)
|
|
53227
|
+
// -----------------------------------------------------------------------
|
|
53228
|
+
async getAlarmHistory(id) {
|
|
53229
|
+
const raw = await this.request(`${this.baseUrl}/alarms/${encodeURIComponent(id)}/history`);
|
|
53230
|
+
return this.unwrapArray(raw);
|
|
53231
|
+
}
|
|
53232
|
+
// -----------------------------------------------------------------------
|
|
53233
|
+
// Alarm actions (POST /alarms/:id/...)
|
|
53234
|
+
// -----------------------------------------------------------------------
|
|
53235
|
+
async acknowledgeAlarm(id, acknowledgedBy, note) {
|
|
53236
|
+
await this.request(`${this.baseUrl}/alarms/${id}/ack`, {
|
|
53237
|
+
method: "POST",
|
|
53238
|
+
body: JSON.stringify({ acknowledgedBy, note })
|
|
53239
|
+
});
|
|
53240
|
+
}
|
|
53241
|
+
async silenceAlarm(id, silencedBy, duration, reason) {
|
|
53242
|
+
await this.request(`${this.baseUrl}/alarms/${id}/silence`, {
|
|
53243
|
+
method: "POST",
|
|
53244
|
+
body: JSON.stringify({ silencedBy, duration, reason })
|
|
53245
|
+
});
|
|
53246
|
+
}
|
|
53247
|
+
async escalateAlarm(id, escalatedBy, reason) {
|
|
53248
|
+
await this.request(`${this.baseUrl}/alarms/${id}/escalate`, {
|
|
53249
|
+
method: "POST",
|
|
53250
|
+
body: JSON.stringify({ escalatedBy, reason })
|
|
53251
|
+
});
|
|
53252
|
+
}
|
|
53253
|
+
async closeAlarm(id, closedBy, resolution) {
|
|
53254
|
+
await this.request(`${this.baseUrl}/alarms/${id}/close`, {
|
|
53255
|
+
method: "POST",
|
|
53256
|
+
body: JSON.stringify({ closedBy, resolution })
|
|
53257
|
+
});
|
|
53258
|
+
}
|
|
53259
|
+
// -----------------------------------------------------------------------
|
|
53260
|
+
// Batch alarm actions (POST /alarms/batch/*)
|
|
53261
|
+
// Up to 100 IDs per call; response contains succeeded/failed arrays.
|
|
53262
|
+
// -----------------------------------------------------------------------
|
|
53263
|
+
async batchAcknowledge(alarmIds, acknowledgedBy) {
|
|
53264
|
+
const res = await this.request(
|
|
53265
|
+
`${this.baseUrl}/alarms/batch/ack`,
|
|
53266
|
+
{ method: "POST", body: JSON.stringify({ alarmIds, acknowledgedBy }) }
|
|
53267
|
+
);
|
|
53268
|
+
return res.data;
|
|
53269
|
+
}
|
|
53270
|
+
async batchSilence(alarmIds, silencedBy, duration) {
|
|
53271
|
+
const res = await this.request(
|
|
53272
|
+
`${this.baseUrl}/alarms/batch/silence`,
|
|
53273
|
+
{ method: "POST", body: JSON.stringify({ alarmIds, silencedBy, duration }) }
|
|
53274
|
+
);
|
|
53275
|
+
return res.data;
|
|
53276
|
+
}
|
|
53277
|
+
async batchEscalate(alarmIds, escalatedBy) {
|
|
53278
|
+
const res = await this.request(
|
|
53279
|
+
`${this.baseUrl}/alarms/batch/escalate`,
|
|
53280
|
+
{ method: "POST", body: JSON.stringify({ alarmIds, escalatedBy }) }
|
|
53281
|
+
);
|
|
53282
|
+
return res.data;
|
|
53283
|
+
}
|
|
53284
|
+
async batchClose(alarmIds, closedBy, resolution) {
|
|
53285
|
+
const res = await this.request(
|
|
53286
|
+
`${this.baseUrl}/alarms/batch/close`,
|
|
53287
|
+
{ method: "POST", body: JSON.stringify({ alarmIds, closedBy, resolution }) }
|
|
53288
|
+
);
|
|
53289
|
+
return res.data;
|
|
53290
|
+
}
|
|
53291
|
+
};
|
|
53292
|
+
|
|
53293
|
+
// src/services/alarm/AlarmService.ts
|
|
53294
|
+
function isFresh(entry, ttlMs) {
|
|
53295
|
+
return !!entry && Date.now() - entry.timestamp < ttlMs;
|
|
53296
|
+
}
|
|
53297
|
+
function mapApiAlarm(api, customerMap) {
|
|
53298
|
+
return {
|
|
53299
|
+
id: api.id,
|
|
53300
|
+
customerId: api.customerId ?? api.tenantId,
|
|
53301
|
+
customerName: customerMap?.get(api.customerId ?? "") ?? "",
|
|
53302
|
+
source: api.deviceName || api.deviceId,
|
|
53303
|
+
severity: api.severity,
|
|
53304
|
+
state: api.state,
|
|
53305
|
+
title: api.title,
|
|
53306
|
+
description: api.description ?? "",
|
|
53307
|
+
tags: {
|
|
53308
|
+
alarmType: api.alarmType,
|
|
53309
|
+
deviceType: api.deviceType
|
|
53310
|
+
},
|
|
53311
|
+
firstOccurrence: api.raisedAt || api.raised_at || api.createdAt || void 0,
|
|
53312
|
+
lastOccurrence: api.lastUpdatedAt || api.updatedAt || api.updated_at || void 0,
|
|
53313
|
+
occurrenceCount: api.occurrenceCount ?? api.dispatchCount ?? 1,
|
|
53314
|
+
acknowledgedAt: api.acknowledgedAt || void 0,
|
|
53315
|
+
acknowledgedBy: api.acknowledgedBy,
|
|
53316
|
+
snoozedUntil: api.silencedUntil || api.snoozedUntil || void 0,
|
|
53317
|
+
closedAt: api.closedAt || void 0,
|
|
53318
|
+
closedBy: api.closedBy,
|
|
53319
|
+
closedReason: api.resolution,
|
|
53320
|
+
triggerValue: api.metadata?.value,
|
|
53321
|
+
centralId: api.centralId || void 0,
|
|
53322
|
+
deviceId: api.deviceId || void 0
|
|
53323
|
+
};
|
|
53324
|
+
}
|
|
53325
|
+
function mapApiStats(api) {
|
|
53326
|
+
return {
|
|
53327
|
+
total: api.total,
|
|
53328
|
+
bySeverity: {
|
|
53329
|
+
CRITICAL: api.bySeverity?.CRITICAL ?? 0,
|
|
53330
|
+
HIGH: api.bySeverity?.HIGH ?? 0,
|
|
53331
|
+
MEDIUM: api.bySeverity?.MEDIUM ?? 0,
|
|
53332
|
+
LOW: api.bySeverity?.LOW ?? 0,
|
|
53333
|
+
INFO: api.bySeverity?.INFO ?? 0
|
|
53334
|
+
},
|
|
53335
|
+
byState: {
|
|
53336
|
+
OPEN: api.byState?.OPEN ?? 0,
|
|
53337
|
+
ACK: api.byState?.ACK ?? 0,
|
|
53338
|
+
SNOOZED: api.byState?.SNOOZED ?? 0,
|
|
53339
|
+
ESCALATED: api.byState?.ESCALATED ?? 0,
|
|
53340
|
+
CLOSED: api.byState?.CLOSED ?? 0
|
|
53341
|
+
},
|
|
53342
|
+
openCritical: api.openCritical,
|
|
53343
|
+
openHigh: api.openHigh,
|
|
53344
|
+
last24Hours: api.last24Hours
|
|
53345
|
+
};
|
|
53346
|
+
}
|
|
53347
|
+
function mapApiTrend(apiTrend) {
|
|
53348
|
+
return (apiTrend || []).map((point) => ({
|
|
53349
|
+
label: point.label || point.period,
|
|
53350
|
+
timestamp: point.timestamp || new Date(point.period).getTime() || 0,
|
|
53351
|
+
total: point.total ?? point.count,
|
|
53352
|
+
bySeverity: point.bySeverity ? {
|
|
53353
|
+
CRITICAL: point.bySeverity.CRITICAL ?? 0,
|
|
53354
|
+
HIGH: point.bySeverity.HIGH ?? 0,
|
|
53355
|
+
MEDIUM: point.bySeverity.MEDIUM ?? 0,
|
|
53356
|
+
LOW: point.bySeverity.LOW ?? 0,
|
|
53357
|
+
INFO: point.bySeverity.INFO ?? 0
|
|
53358
|
+
} : void 0
|
|
53359
|
+
}));
|
|
53360
|
+
}
|
|
53361
|
+
function mapApiTopOffenders(items, deviceNameMap) {
|
|
53362
|
+
return (items || []).map((item) => ({
|
|
53363
|
+
name: deviceNameMap.get(item.deviceId) ?? item.deviceName ?? item.deviceId,
|
|
53364
|
+
location: item.customerName ?? "",
|
|
53365
|
+
downtime: item.downtimeHours ?? (item.totalDowntimeMinutes ?? 0) / 60,
|
|
53366
|
+
percentage: 0
|
|
53367
|
+
// computed by caller if needed
|
|
53368
|
+
}));
|
|
53369
|
+
}
|
|
53370
|
+
var AlarmServiceClass = class {
|
|
53371
|
+
client = new AlarmApiClient();
|
|
53372
|
+
cacheTtlMs = 3 * 60 * 1e3;
|
|
53373
|
+
// 3 min default; overridable via configure()
|
|
53374
|
+
availabilityCache = /* @__PURE__ */ new Map();
|
|
53375
|
+
alarmsCache = /* @__PURE__ */ new Map();
|
|
53376
|
+
statsCache = /* @__PURE__ */ new Map();
|
|
53377
|
+
trendCache = /* @__PURE__ */ new Map();
|
|
53378
|
+
// -----------------------------------------------------------------------
|
|
53379
|
+
// Availability (MTBF / MTTR per device)
|
|
53380
|
+
// -----------------------------------------------------------------------
|
|
53381
|
+
/**
|
|
53382
|
+
* Fetches MTBF, MTTR, and Availability metrics per device and fleet-wide.
|
|
53383
|
+
* Results are cached for 60 seconds.
|
|
53384
|
+
*/
|
|
53385
|
+
async getAvailability(customerId, startAt, endAt) {
|
|
53386
|
+
if (!customerId) {
|
|
53387
|
+
throw new Error("AlarmService.getAvailability: customerId is required");
|
|
53388
|
+
}
|
|
53389
|
+
const cacheKey = `avail:${customerId}:${startAt}:${endAt}`;
|
|
53390
|
+
const cached = this.availabilityCache.get(cacheKey);
|
|
53391
|
+
if (isFresh(cached, this.cacheTtlMs)) return cached.data;
|
|
53392
|
+
const data = await this.client.getAvailability({
|
|
53393
|
+
customerId,
|
|
53394
|
+
startAt,
|
|
53395
|
+
endAt,
|
|
53396
|
+
includeByDevice: true
|
|
53397
|
+
});
|
|
53398
|
+
this.availabilityCache.set(cacheKey, { data, timestamp: Date.now() });
|
|
53399
|
+
return data;
|
|
53400
|
+
}
|
|
53401
|
+
// -----------------------------------------------------------------------
|
|
53402
|
+
// Alarm list
|
|
53403
|
+
// -----------------------------------------------------------------------
|
|
53404
|
+
/**
|
|
53405
|
+
* Fetch alarm list with optional filters, auto-paginating until hasMore is false.
|
|
53406
|
+
* Returns { data: Alarm[], summary: AlarmListSummary } with all pages merged.
|
|
53407
|
+
* The summary comes from page 1 (reflects full dataset totals).
|
|
53408
|
+
*/
|
|
53409
|
+
async getAlarms(params = {}, customerMap) {
|
|
53410
|
+
const cacheKey = `alarms:${JSON.stringify(params)}`;
|
|
53411
|
+
const cached = this.alarmsCache.get(cacheKey);
|
|
53412
|
+
if (isFresh(cached, this.cacheTtlMs)) return cached.data;
|
|
53413
|
+
let allData = [];
|
|
53414
|
+
let summary;
|
|
53415
|
+
let totalPages = 1;
|
|
53416
|
+
let page = 1;
|
|
53417
|
+
while (page <= totalPages) {
|
|
53418
|
+
const response = await this.client.getAlarms({ ...params, page });
|
|
53419
|
+
const pageData = (response.data || []).map((a) => mapApiAlarm(a, customerMap));
|
|
53420
|
+
allData = allData.concat(pageData);
|
|
53421
|
+
if (page === 1) {
|
|
53422
|
+
summary = response.summary;
|
|
53423
|
+
totalPages = response.pagination?.totalPages ?? 1;
|
|
53424
|
+
}
|
|
53425
|
+
page++;
|
|
53426
|
+
}
|
|
53427
|
+
const result = { data: allData, summary };
|
|
53428
|
+
this.alarmsCache.set(cacheKey, { data: result, timestamp: Date.now() });
|
|
53429
|
+
return result;
|
|
53430
|
+
}
|
|
53431
|
+
// -----------------------------------------------------------------------
|
|
53432
|
+
// Alarm statistics
|
|
53433
|
+
// -----------------------------------------------------------------------
|
|
53434
|
+
/**
|
|
53435
|
+
* Fetch aggregated alarm statistics for a tenant and time period.
|
|
53436
|
+
* Returns mapped AlarmStats (from src/types/alarm.ts).
|
|
53437
|
+
*/
|
|
53438
|
+
async getAlarmStats(tenantId, period) {
|
|
53439
|
+
const cacheKey = `stats:${tenantId}:${period}`;
|
|
53440
|
+
const cached = this.statsCache.get(cacheKey);
|
|
53441
|
+
if (isFresh(cached, this.cacheTtlMs)) return cached.data;
|
|
53442
|
+
const raw = await this.client.getAlarmStats(tenantId, period);
|
|
53443
|
+
const data = mapApiStats(raw);
|
|
53444
|
+
this.statsCache.set(cacheKey, { data, timestamp: Date.now() });
|
|
53445
|
+
return data;
|
|
53446
|
+
}
|
|
53447
|
+
// -----------------------------------------------------------------------
|
|
53448
|
+
// Alarm trend
|
|
53449
|
+
// -----------------------------------------------------------------------
|
|
53450
|
+
/**
|
|
53451
|
+
* Fetch alarm trend data points.
|
|
53452
|
+
* Returns mapped AlarmTrendDataPoint[] (from src/types/alarm.ts).
|
|
53453
|
+
*/
|
|
53454
|
+
async getAlarmTrend(tenantId, period, groupBy) {
|
|
53455
|
+
const cacheKey = `trend:${tenantId}:${period}:${groupBy}`;
|
|
53456
|
+
const cached = this.trendCache.get(cacheKey);
|
|
53457
|
+
if (isFresh(cached, this.cacheTtlMs)) return cached.data;
|
|
53458
|
+
const raw = await this.client.getAlarmTrend(tenantId, period, groupBy);
|
|
53459
|
+
const data = mapApiTrend(raw);
|
|
53460
|
+
this.trendCache.set(cacheKey, { data, timestamp: Date.now() });
|
|
53461
|
+
return data;
|
|
53462
|
+
}
|
|
53463
|
+
// -----------------------------------------------------------------------
|
|
53464
|
+
// Top downtime / offenders
|
|
53465
|
+
// -----------------------------------------------------------------------
|
|
53466
|
+
/**
|
|
53467
|
+
* Fetch top devices by alarm count / downtime.
|
|
53468
|
+
* Returns mapped DowntimeEntry[] (from src/types/operational.ts).
|
|
53469
|
+
*
|
|
53470
|
+
* @param deviceNameMap - Optional map of deviceId → friendly name from ThingsBoard cache
|
|
53471
|
+
*/
|
|
53472
|
+
async getTopDowntime(tenantId, deviceNameMap = /* @__PURE__ */ new Map(), limit = 5) {
|
|
53473
|
+
const items = await this.client.getTopOffenders(tenantId, limit);
|
|
53474
|
+
return mapApiTopOffenders(items, deviceNameMap);
|
|
53475
|
+
}
|
|
53476
|
+
// -----------------------------------------------------------------------
|
|
53477
|
+
// Per-device alarm counts
|
|
53478
|
+
// -----------------------------------------------------------------------
|
|
53479
|
+
/**
|
|
53480
|
+
* Fetch alarm counts per device.
|
|
53481
|
+
* Returns a Map<deviceId, alarmCount>.
|
|
53482
|
+
*/
|
|
53483
|
+
async getDeviceAlarmCounts(tenantId) {
|
|
53484
|
+
const items = await this.client.getAlarmStatsByDevice(tenantId);
|
|
53485
|
+
const map = /* @__PURE__ */ new Map();
|
|
53486
|
+
(items || []).forEach((item) => map.set(item.deviceId, item.alarmCount));
|
|
53487
|
+
return map;
|
|
53488
|
+
}
|
|
53489
|
+
/**
|
|
53490
|
+
* Histórico de transições do alarme (abertura/reabertura/fechamento/ack…).
|
|
53491
|
+
* Falha de rede/API devolve [] — o detalhe do alarme degrada para só a abertura.
|
|
53492
|
+
*/
|
|
53493
|
+
async getAlarmHistory(id) {
|
|
53494
|
+
try {
|
|
53495
|
+
return await this.client.getAlarmHistory(id);
|
|
53496
|
+
} catch {
|
|
53497
|
+
return [];
|
|
53498
|
+
}
|
|
53499
|
+
}
|
|
53500
|
+
// -----------------------------------------------------------------------
|
|
53501
|
+
// Alarm actions
|
|
53502
|
+
// -----------------------------------------------------------------------
|
|
53503
|
+
async acknowledgeAlarm(id, userEmail, note) {
|
|
53504
|
+
return this.client.acknowledgeAlarm(id, userEmail, note);
|
|
53505
|
+
}
|
|
53506
|
+
async silenceAlarm(id, userEmail, duration, reason) {
|
|
53507
|
+
return this.client.silenceAlarm(id, userEmail, duration, reason);
|
|
53508
|
+
}
|
|
53509
|
+
async escalateAlarm(id, userEmail, reason) {
|
|
53510
|
+
return this.client.escalateAlarm(id, userEmail, reason);
|
|
53511
|
+
}
|
|
53512
|
+
async closeAlarm(id, userEmail, resolution) {
|
|
53513
|
+
return this.client.closeAlarm(id, userEmail, resolution);
|
|
53514
|
+
}
|
|
53515
|
+
// -----------------------------------------------------------------------
|
|
53516
|
+
// Batch alarm actions (POST /alarms/batch/*)
|
|
53517
|
+
// -----------------------------------------------------------------------
|
|
53518
|
+
async batchAcknowledge(alarmIds, userEmail) {
|
|
53519
|
+
return this.client.batchAcknowledge(alarmIds, userEmail);
|
|
53520
|
+
}
|
|
53521
|
+
async batchSilence(alarmIds, userEmail, duration) {
|
|
53522
|
+
return this.client.batchSilence(alarmIds, userEmail, duration);
|
|
53523
|
+
}
|
|
53524
|
+
async batchEscalate(alarmIds, userEmail) {
|
|
53525
|
+
return this.client.batchEscalate(alarmIds, userEmail);
|
|
53526
|
+
}
|
|
53527
|
+
async batchClose(alarmIds, userEmail, resolution) {
|
|
53528
|
+
return this.client.batchClose(alarmIds, userEmail, resolution);
|
|
53529
|
+
}
|
|
53530
|
+
// -----------------------------------------------------------------------
|
|
53531
|
+
// Configuration
|
|
53532
|
+
// -----------------------------------------------------------------------
|
|
53533
|
+
/**
|
|
53534
|
+
* Set the Alarms API base URL (e.g. from MAIN_VIEW orchestrator settings).
|
|
53535
|
+
* Clears the cache so subsequent calls use the new endpoint.
|
|
53536
|
+
*/
|
|
53537
|
+
configure(baseUrl, cacheTtlMs, apiKey) {
|
|
53538
|
+
this.client.configure(baseUrl ?? "", apiKey);
|
|
53539
|
+
if (cacheTtlMs && cacheTtlMs > 0) this.cacheTtlMs = cacheTtlMs;
|
|
53540
|
+
this.clearCache();
|
|
53541
|
+
}
|
|
53542
|
+
// -----------------------------------------------------------------------
|
|
53543
|
+
// Cache management
|
|
53544
|
+
// -----------------------------------------------------------------------
|
|
53545
|
+
clearCache() {
|
|
53546
|
+
this.availabilityCache.clear();
|
|
53547
|
+
this.alarmsCache.clear();
|
|
53548
|
+
this.statsCache.clear();
|
|
53549
|
+
this.trendCache.clear();
|
|
53550
|
+
}
|
|
53551
|
+
};
|
|
53552
|
+
var AlarmService = new AlarmServiceClass();
|
|
53553
|
+
|
|
53083
53554
|
// src/components/AlarmsNotificationsPanel/AlarmDetailsModal.ts
|
|
53084
53555
|
function fmt(isoString) {
|
|
53085
53556
|
if (!isoString) return "-";
|
|
@@ -53109,112 +53580,155 @@ function escHtml2(s) {
|
|
|
53109
53580
|
if (s == null) return "";
|
|
53110
53581
|
return String(s).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
53111
53582
|
}
|
|
53112
|
-
|
|
53113
|
-
|
|
53114
|
-
|
|
53115
|
-
|
|
53116
|
-
|
|
53117
|
-
|
|
53118
|
-
|
|
53583
|
+
var EVENT_LABEL = {
|
|
53584
|
+
raised: "Abertura",
|
|
53585
|
+
reopened: "Reabertura",
|
|
53586
|
+
closed: "Fechamento",
|
|
53587
|
+
acknowledged: "Reconhecido",
|
|
53588
|
+
silenced: "Adiado",
|
|
53589
|
+
escalated: "Escalado",
|
|
53590
|
+
other: "Atualiza\xE7\xE3o"
|
|
53591
|
+
};
|
|
53592
|
+
function classifyHistoryEvent(ev) {
|
|
53593
|
+
const action = String(ev.action || "").toUpperCase();
|
|
53594
|
+
const next = String(ev.newState || "").toUpperCase();
|
|
53595
|
+
const prev = String(ev.previousState || "").toUpperCase();
|
|
53596
|
+
if (next === "OPEN") return prev && prev !== "OPEN" ? prev === "CLOSED" ? "reopened" : "other" : "raised";
|
|
53597
|
+
if (next === "CLOSED" || /CLOSE/.test(action)) return "closed";
|
|
53598
|
+
if (next === "ACK" || next === "ACKNOWLEDGED" || /ACK/.test(action)) return "acknowledged";
|
|
53599
|
+
if (next === "SNOOZED" || next === "SILENCED" || /SILENC|SNOOZ/.test(action)) return "silenced";
|
|
53600
|
+
if (next === "ESCALATED" || /ESCALAT/.test(action)) return "escalated";
|
|
53601
|
+
if (/CREAT|RAISE/.test(action)) return "raised";
|
|
53602
|
+
return "other";
|
|
53603
|
+
}
|
|
53604
|
+
async function loadAlarmEvents(alarm) {
|
|
53605
|
+
const ids = alarm._groupAlarmIds?.length ? alarm._groupAlarmIds : [alarm.id];
|
|
53606
|
+
const aso = window.AlarmServiceOrchestrator?.alarms || [];
|
|
53607
|
+
const byId = new Map(aso.map((a) => [a?.id, a]));
|
|
53608
|
+
const single = ids.length === 1;
|
|
53609
|
+
const events = [];
|
|
53610
|
+
await Promise.all(
|
|
53611
|
+
ids.slice(0, 50).map(async (id) => {
|
|
53612
|
+
const raw = byId.get(id);
|
|
53613
|
+
const device = raw?.deviceName || alarm.source || "";
|
|
53614
|
+
const title = raw?.title || alarm.title;
|
|
53615
|
+
const raisedMs = new Date(raw?.raisedAt || (single ? alarm.firstOccurrence : "") || "").getTime();
|
|
53616
|
+
const hasRaise = Number.isFinite(raisedMs) && raisedMs > 0;
|
|
53617
|
+
if (hasRaise) events.push({ ts: raisedMs, kind: "raised", alarmId: id, device, title, occurrence: true });
|
|
53618
|
+
const hist = await AlarmService.getAlarmHistory(id);
|
|
53619
|
+
for (const ev of hist) {
|
|
53620
|
+
const ts = new Date(ev.timestamp).getTime();
|
|
53621
|
+
if (!Number.isFinite(ts)) continue;
|
|
53622
|
+
const kind = classifyHistoryEvent(ev);
|
|
53623
|
+
if (kind === "raised" && hasRaise && Math.abs(ts - raisedMs) < 6e4) continue;
|
|
53624
|
+
events.push({
|
|
53625
|
+
ts,
|
|
53626
|
+
kind,
|
|
53627
|
+
alarmId: id,
|
|
53628
|
+
device,
|
|
53629
|
+
title,
|
|
53630
|
+
actor: ev.actor,
|
|
53631
|
+
occurrence: kind === "raised" || kind === "reopened"
|
|
53632
|
+
});
|
|
53633
|
+
}
|
|
53634
|
+
})
|
|
53635
|
+
);
|
|
53636
|
+
if (!events.some((e) => e.occurrence)) {
|
|
53637
|
+
const ms = new Date(alarm.firstOccurrence).getTime();
|
|
53638
|
+
if (Number.isFinite(ms) && ms > 0) {
|
|
53639
|
+
events.push({ ts: ms, kind: "raised", alarmId: alarm.id, device: alarm.source, title: alarm.title, occurrence: true });
|
|
53640
|
+
}
|
|
53641
|
+
}
|
|
53642
|
+
return events.sort((a, b) => a.ts - b.ts);
|
|
53643
|
+
}
|
|
53644
|
+
function bucketEvents(occ, devices, period, fromMs, toMs) {
|
|
53645
|
+
const start = new Date(fromMs);
|
|
53646
|
+
if (period === "hora") start.setMinutes(0, 0, 0);
|
|
53647
|
+
else start.setHours(0, 0, 0, 0);
|
|
53648
|
+
if (period === "semana") start.setDate(start.getDate() - start.getDay());
|
|
53649
|
+
if (period === "mes") start.setDate(1);
|
|
53650
|
+
const keys = [];
|
|
53651
|
+
const cursor = new Date(start);
|
|
53652
|
+
while (cursor.getTime() <= toMs && keys.length < 5e3) {
|
|
53653
|
+
keys.push(cursor.getTime());
|
|
53654
|
+
if (period === "hora") cursor.setHours(cursor.getHours() + 1);
|
|
53655
|
+
else if (period === "dia") cursor.setDate(cursor.getDate() + 1);
|
|
53656
|
+
else if (period === "semana") cursor.setDate(cursor.getDate() + 7);
|
|
53657
|
+
else cursor.setMonth(cursor.getMonth() + 1);
|
|
53658
|
+
}
|
|
53659
|
+
const maxBuckets = period === "hora" ? 72 : period === "dia" ? 92 : period === "semana" ? 104 : 36;
|
|
53660
|
+
const ks = keys.slice(-maxBuckets);
|
|
53661
|
+
const months = ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"];
|
|
53662
|
+
const dm = (d) => `${String(d.getDate()).padStart(2, "0")}/${String(d.getMonth() + 1).padStart(2, "0")}`;
|
|
53663
|
+
const label = (ms) => {
|
|
53664
|
+
const d = new Date(ms);
|
|
53665
|
+
if (period === "hora") return `${dm(d)} ${String(d.getHours()).padStart(2, "0")}h`;
|
|
53666
|
+
if (period === "mes") return `${months[d.getMonth()]}/${String(d.getFullYear()).slice(2)}`;
|
|
53667
|
+
return dm(d);
|
|
53119
53668
|
};
|
|
53669
|
+
const buckets = ks.map((k) => ({ label: label(k), total: 0, byDevice: {} }));
|
|
53670
|
+
for (const e of occ) {
|
|
53671
|
+
let idx = -1;
|
|
53672
|
+
for (let i = ks.length - 1; i >= 0; i--) {
|
|
53673
|
+
if (ks[i] <= e.ts) {
|
|
53674
|
+
idx = i;
|
|
53675
|
+
break;
|
|
53676
|
+
}
|
|
53677
|
+
}
|
|
53678
|
+
if (idx < 0) continue;
|
|
53679
|
+
buckets[idx].total++;
|
|
53680
|
+
const dev = devices.includes(e.device) ? e.device : devices[0] ?? e.device;
|
|
53681
|
+
buckets[idx].byDevice[dev] = (buckets[idx].byDevice[dev] || 0) + 1;
|
|
53682
|
+
}
|
|
53683
|
+
return buckets;
|
|
53120
53684
|
}
|
|
53121
|
-
function
|
|
53122
|
-
|
|
53123
|
-
|
|
53124
|
-
return
|
|
53685
|
+
function dowFromEvents(occ) {
|
|
53686
|
+
const v = new Array(7).fill(0);
|
|
53687
|
+
occ.forEach((e) => v[new Date(e.ts).getDay()]++);
|
|
53688
|
+
return v;
|
|
53125
53689
|
}
|
|
53126
|
-
function
|
|
53127
|
-
const
|
|
53128
|
-
|
|
53129
|
-
|
|
53130
|
-
|
|
53131
|
-
|
|
53132
|
-
|
|
53133
|
-
|
|
53134
|
-
const
|
|
53135
|
-
let
|
|
53136
|
-
|
|
53137
|
-
|
|
53138
|
-
|
|
53139
|
-
|
|
53140
|
-
|
|
53141
|
-
|
|
53142
|
-
|
|
53143
|
-
|
|
53144
|
-
|
|
53145
|
-
numBuckets = Math.max(1, Math.min(31, Math.ceil(durMs / bucketMs)));
|
|
53146
|
-
labelFn = (ms) => {
|
|
53147
|
-
const d = new Date(ms);
|
|
53148
|
-
return `${String(d.getDate()).padStart(2, "0")}/${String(d.getMonth() + 1).padStart(2, "0")}`;
|
|
53149
|
-
};
|
|
53150
|
-
} else if (period === "semana") {
|
|
53151
|
-
bucketMs = 7 * 864e5;
|
|
53152
|
-
numBuckets = Math.max(1, Math.min(26, Math.ceil(durMs / bucketMs)));
|
|
53153
|
-
labelFn = (ms) => {
|
|
53154
|
-
const d = new Date(ms);
|
|
53155
|
-
return `${String(d.getDate()).padStart(2, "0")}/${String(d.getMonth() + 1).padStart(2, "0")}`;
|
|
53156
|
-
};
|
|
53157
|
-
} else {
|
|
53158
|
-
bucketMs = 30 * 864e5;
|
|
53159
|
-
numBuckets = Math.max(1, Math.min(24, Math.ceil(durMs / bucketMs)));
|
|
53160
|
-
const months = ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"];
|
|
53161
|
-
labelFn = (ms) => months[new Date(ms).getMonth()];
|
|
53162
|
-
}
|
|
53163
|
-
while (numBuckets > 1 && firstMs + (numBuckets - 1) * bucketMs > nowMs) numBuckets--;
|
|
53164
|
-
const seed = hashStr(alarm.id + period);
|
|
53165
|
-
const rand = seededRand(seed);
|
|
53166
|
-
const weights = Array.from({ length: numBuckets }, (_, i) => {
|
|
53167
|
-
const center = (numBuckets - 1) / 2;
|
|
53168
|
-
const bell = 1 - 0.45 * Math.abs(i - center) / (center || 1);
|
|
53169
|
-
return Math.max(0.1, bell * (0.6 + rand() * 0.8));
|
|
53170
|
-
});
|
|
53171
|
-
const wSum = weights.reduce((a, b) => a + b, 0);
|
|
53172
|
-
const totals = new Array(numBuckets).fill(0);
|
|
53173
|
-
let remaining = count;
|
|
53174
|
-
for (let i = 0; i < numBuckets - 1; i++) {
|
|
53175
|
-
const v = Math.round(weights[i] / wSum * count);
|
|
53176
|
-
totals[i] = Math.min(v, remaining);
|
|
53177
|
-
remaining -= totals[i];
|
|
53178
|
-
}
|
|
53179
|
-
totals[numBuckets - 1] = Math.max(0, remaining);
|
|
53180
|
-
return Array.from({ length: numBuckets }, (_, i) => {
|
|
53181
|
-
const ts = firstMs + i * bucketMs;
|
|
53182
|
-
const total = totals[i];
|
|
53183
|
-
const byDevice = {};
|
|
53184
|
-
if (devices.length === 1) {
|
|
53185
|
-
byDevice[devices[0]] = total;
|
|
53186
|
-
} else {
|
|
53187
|
-
let rem = total;
|
|
53188
|
-
for (let d = 0; d < devices.length - 1; d++) {
|
|
53189
|
-
const share = Math.round(rand() * rem * (1 / (devices.length - d)));
|
|
53190
|
-
byDevice[devices[d]] = share;
|
|
53191
|
-
rem -= share;
|
|
53690
|
+
function hodFromEvents(occ) {
|
|
53691
|
+
const v = new Array(24).fill(0);
|
|
53692
|
+
occ.forEach((e) => v[new Date(e.ts).getHours()]++);
|
|
53693
|
+
return v;
|
|
53694
|
+
}
|
|
53695
|
+
function resolveDeviceIdentity(alarm, src) {
|
|
53696
|
+
const orch = window.MyIOOrchestrator;
|
|
53697
|
+
const nameToLabel = orch?.entityNameToLabelMap;
|
|
53698
|
+
const gcdrToLabel = orch?.gcdrDeviceNameMap;
|
|
53699
|
+
let label = null;
|
|
53700
|
+
if (alarm.deviceId && parseDevices(alarm.source).length <= 1) label = gcdrToLabel?.get?.(alarm.deviceId) || null;
|
|
53701
|
+
if (!label && nameToLabel?.has?.(src)) label = nameToLabel.get(src) || null;
|
|
53702
|
+
if (!label) label = src;
|
|
53703
|
+
let name = nameToLabel?.has?.(src) ? src : null;
|
|
53704
|
+
if (!name && nameToLabel) {
|
|
53705
|
+
for (const [n, l] of nameToLabel) {
|
|
53706
|
+
if (l === label) {
|
|
53707
|
+
name = n;
|
|
53708
|
+
break;
|
|
53192
53709
|
}
|
|
53193
|
-
byDevice[devices[devices.length - 1]] = Math.max(0, rem);
|
|
53194
53710
|
}
|
|
53195
|
-
|
|
53196
|
-
}
|
|
53711
|
+
}
|
|
53712
|
+
return { label, name: name && name !== label ? name : null };
|
|
53197
53713
|
}
|
|
53198
|
-
function
|
|
53199
|
-
|
|
53200
|
-
const rand = seededRand(hashStr(alarm.id + "dow"));
|
|
53201
|
-
const weights = [0.2, 1, 0.95, 0.9, 0.85, 0.8, 0.3];
|
|
53202
|
-
const wSum = weights.reduce((a, b) => a + b, 0);
|
|
53203
|
-
const vals = weights.map((w) => Math.round(w / wSum * count * (0.7 + rand() * 0.6)));
|
|
53204
|
-
return vals;
|
|
53714
|
+
function resolveDeviceLabel(alarm, src) {
|
|
53715
|
+
return resolveDeviceIdentity(alarm, src).label;
|
|
53205
53716
|
}
|
|
53206
|
-
function
|
|
53207
|
-
const
|
|
53208
|
-
const
|
|
53209
|
-
const
|
|
53210
|
-
|
|
53211
|
-
|
|
53212
|
-
|
|
53213
|
-
|
|
53214
|
-
|
|
53215
|
-
|
|
53216
|
-
|
|
53217
|
-
|
|
53717
|
+
function brandColor(fallback = "#7c3aed") {
|
|
53718
|
+
const theme = window.MyIOUtils?.theme;
|
|
53719
|
+
const vars = typeof theme?.cssVars === "function" ? theme.cssVars() : theme;
|
|
53720
|
+
const c = vars?.["--myio-brand-700"];
|
|
53721
|
+
return typeof c === "string" && /^#[0-9a-f]{6}$/i.test(c.trim()) ? c.trim() : fallback;
|
|
53722
|
+
}
|
|
53723
|
+
var COPY_SVG = '<svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>';
|
|
53724
|
+
function deviceIdentityHtml(alarm, src) {
|
|
53725
|
+
const { label, name } = resolveDeviceIdentity(alarm, src);
|
|
53726
|
+
const copyValue = name || src;
|
|
53727
|
+
return `<span class="adm-dev-id">
|
|
53728
|
+
<span class="adm-dev-label">${escHtml2(label)}</span>
|
|
53729
|
+
${name ? `<span class="adm-dev-name">${escHtml2(name)}</span>` : ""}
|
|
53730
|
+
<button type="button" class="adm-copy-btn" data-copy="${escHtml2(copyValue)}" title="Copiar nome do dispositivo">${COPY_SVG}</button>
|
|
53731
|
+
</span>`;
|
|
53218
53732
|
}
|
|
53219
53733
|
var DEV_COLORS = [
|
|
53220
53734
|
"#7c3aed",
|
|
@@ -53304,8 +53818,8 @@ function createDowChart(canvas, vals) {
|
|
|
53304
53818
|
datasets: [{
|
|
53305
53819
|
label: "Ocorr\xEAncias",
|
|
53306
53820
|
data: vals,
|
|
53307
|
-
backgroundColor: "#
|
|
53308
|
-
borderColor:
|
|
53821
|
+
backgroundColor: brandColor("#8b5cf6") + "bb",
|
|
53822
|
+
borderColor: brandColor(),
|
|
53309
53823
|
borderWidth: 1,
|
|
53310
53824
|
borderRadius: 3
|
|
53311
53825
|
}]
|
|
@@ -53347,8 +53861,8 @@ function createHodChart(canvas, vals) {
|
|
|
53347
53861
|
datasets: [{
|
|
53348
53862
|
label: "Ocorr\xEAncias",
|
|
53349
53863
|
data: vals,
|
|
53350
|
-
backgroundColor: "#
|
|
53351
|
-
borderColor: "#2563eb",
|
|
53864
|
+
backgroundColor: brandColor("#3b82f6") + "bb",
|
|
53865
|
+
borderColor: brandColor("#2563eb"),
|
|
53352
53866
|
borderWidth: 1,
|
|
53353
53867
|
borderRadius: 2
|
|
53354
53868
|
}]
|
|
@@ -53409,12 +53923,12 @@ var HIDE_ANNOTATIONS_TAB = true;
|
|
|
53409
53923
|
function openAlarmDetailsModal(alarm, themeMode = "light", groupMode, onAction) {
|
|
53410
53924
|
const sev = SEVERITY_CONFIG[alarm.severity];
|
|
53411
53925
|
const st = STATE_CONFIG[alarm.state];
|
|
53412
|
-
const count = alarm.occurrenceCount || 1;
|
|
53413
53926
|
const firstMs = new Date(alarm.firstOccurrence).getTime();
|
|
53414
53927
|
const lastMs = new Date(alarm.lastOccurrence).getTime();
|
|
53415
|
-
const
|
|
53416
|
-
const
|
|
53417
|
-
const
|
|
53928
|
+
const isClosedState = alarm.state === "CLOSED";
|
|
53929
|
+
const closedMs = alarm.closedAt ? new Date(alarm.closedAt).getTime() : NaN;
|
|
53930
|
+
const ageMs = isNaN(firstMs) ? NaN : isClosedState ? (Number.isFinite(closedMs) ? closedMs : lastMs) - firstMs : Date.now() - firstMs;
|
|
53931
|
+
const ageKpiLabel = isClosedState ? "Dura\xE7\xE3o" : "Aberto h\xE1";
|
|
53418
53932
|
const devices = parseDevices(alarm.source);
|
|
53419
53933
|
const deviceCount = devices.length;
|
|
53420
53934
|
const row = (label, value) => value ? `<div class="adm-row"><span class="adm-row-label">${label}</span><span class="adm-row-value">${escHtml2(value)}</span></div>` : "";
|
|
@@ -53423,48 +53937,30 @@ function openAlarmDetailsModal(alarm, themeMode = "light", groupMode, onAction)
|
|
|
53423
53937
|
<div class="adm-device-row">
|
|
53424
53938
|
<span class="adm-device-row-index">${i + 1}</span>
|
|
53425
53939
|
<div class="adm-device-icon-sm">
|
|
53426
|
-
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="
|
|
53940
|
+
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
|
|
53427
53941
|
<rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/>
|
|
53428
53942
|
</svg>
|
|
53429
53943
|
</div>
|
|
53430
|
-
<span class="adm-device-row-name">${
|
|
53944
|
+
<span class="adm-device-row-name">${deviceIdentityHtml(alarm, dev)}</span>
|
|
53431
53945
|
<span class="adm-device-row-sub">${escHtml2(alarm.customerName || "-")}</span>
|
|
53432
53946
|
</div>`
|
|
53433
53947
|
).join("");
|
|
53434
|
-
const
|
|
53435
|
-
const
|
|
53436
|
-
const hodVals = generateHodDistribution(alarm);
|
|
53948
|
+
const spanMs = Number.isFinite(ageMs) ? ageMs : 0;
|
|
53949
|
+
const initialPeriod = spanMs < 3 * 864e5 ? "hora" : spanMs < 45 * 864e5 ? "dia" : spanMs < 180 * 864e5 ? "semana" : "mes";
|
|
53437
53950
|
const chartSingleColor = sev.text || "#7c3aed";
|
|
53951
|
+
let events = null;
|
|
53952
|
+
const occEvents = () => (events || []).filter((e) => e.occurrence);
|
|
53953
|
+
const onEventsReady = [];
|
|
53438
53954
|
const annotCount = getActiveAnnotationCount(alarm.id);
|
|
53439
|
-
const sourceDisplay = deviceCount > 1 ? `${deviceCount} dispositivos` : escHtml2(alarm.source);
|
|
53955
|
+
const sourceDisplay = deviceCount > 1 ? `${deviceCount} dispositivos` : escHtml2(resolveDeviceLabel(alarm, alarm.source) || alarm.source);
|
|
53440
53956
|
const overlay = document.createElement("div");
|
|
53441
53957
|
overlay.className = "adm-overlay";
|
|
53442
53958
|
overlay.setAttribute("data-theme", themeMode);
|
|
53443
53959
|
overlay.innerHTML = `
|
|
53444
53960
|
<div class="adm-drawer" role="dialog" aria-modal="true" aria-label="Detalhes do alarme">
|
|
53445
53961
|
|
|
53446
|
-
<!-- \u2500\u2500 Header \u2500\u2500 -->
|
|
53962
|
+
<!-- \u2500\u2500 Header (t\xEDtulo/maximizar/fechar v\xEAm da moldura padr\xE3o; aqui s\xF3 os badges) \u2500\u2500 -->
|
|
53447
53963
|
<div class="adm-header">
|
|
53448
|
-
<div class="adm-header-top">
|
|
53449
|
-
<div class="adm-title" title="${escHtml2(alarm.title)}">${escHtml2(alarm.title)}</div>
|
|
53450
|
-
<div class="adm-header-actions">
|
|
53451
|
-
<button class="adm-header-btn" id="admMaximize" title="Maximizar">
|
|
53452
|
-
<svg class="adm-icon-expand" viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
53453
|
-
<polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/>
|
|
53454
|
-
<line x1="21" y1="3" x2="14" y2="10"/><line x1="3" y1="21" x2="10" y2="14"/>
|
|
53455
|
-
</svg>
|
|
53456
|
-
<svg class="adm-icon-compress" viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display:none">
|
|
53457
|
-
<polyline points="4 14 10 14 10 20"/><polyline points="20 10 14 10 14 4"/>
|
|
53458
|
-
<line x1="10" y1="14" x2="3" y2="21"/><line x1="21" y1="3" x2="14" y2="10"/>
|
|
53459
|
-
</svg>
|
|
53460
|
-
</button>
|
|
53461
|
-
<button class="adm-header-btn adm-close" id="admClose" title="Fechar (Esc)">
|
|
53462
|
-
<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.5">
|
|
53463
|
-
<path d="M18 6L6 18M6 6l12 12"/>
|
|
53464
|
-
</svg>
|
|
53465
|
-
</button>
|
|
53466
|
-
</div>
|
|
53467
|
-
</div>
|
|
53468
53964
|
<div class="adm-badges">
|
|
53469
53965
|
<span class="adm-badge-severity" style="background:${sev.bg};color:${sev.text}">${sev.icon} ${sev.label}</span>
|
|
53470
53966
|
<span class="adm-badge-state" style="color:${st.color}">${st.label}</span>
|
|
@@ -53480,7 +53976,7 @@ function openAlarmDetailsModal(alarm, themeMode = "light", groupMode, onAction)
|
|
|
53480
53976
|
</button>
|
|
53481
53977
|
<button class="adm-tab" data-panel="timeline">
|
|
53482
53978
|
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><line x1="12" y1="2" x2="12" y2="6"/><line x1="12" y1="18" x2="12" y2="22"/><line x1="4.22" y1="4.22" x2="7.05" y2="7.05"/><line x1="16.95" y1="16.95" x2="19.78" y2="19.78"/><line x1="2" y1="12" x2="6" y2="12"/><line x1="18" y1="12" x2="22" y2="12"/><line x1="4.22" y1="19.78" x2="7.05" y2="16.95"/><line x1="16.95" y1="7.05" x2="19.78" y2="4.22"/></svg>
|
|
53483
|
-
Timeline <span class="adm-tab-badge"
|
|
53979
|
+
Timeline <span class="adm-tab-badge" id="admTlTabBadge">\u2026</span>
|
|
53484
53980
|
</button>
|
|
53485
53981
|
<button class="adm-tab" data-panel="dispositivos">
|
|
53486
53982
|
<svg viewBox="0 0 24 24" width="12" height="12" fill="currentColor"><path d="M20 3H4a1 1 0 00-1 1v12a1 1 0 001 1h8v2H8v2h8v-2h-4v-2h8a1 1 0 001-1V4a1 1 0 00-1-1zm-1 12H5V5h14v10z"/></svg>
|
|
@@ -53502,17 +53998,17 @@ function openAlarmDetailsModal(alarm, themeMode = "light", groupMode, onAction)
|
|
|
53502
53998
|
<!-- RESUMO -->
|
|
53503
53999
|
<div class="adm-panel is-active" data-panel="resumo">
|
|
53504
54000
|
${groupMode !== "separado" ? `<div class="adm-kpi-grid">
|
|
53505
|
-
<div class="adm-kpi">
|
|
53506
|
-
<div class="adm-kpi-value" style="color:${sev.text}"
|
|
54001
|
+
<div class="adm-kpi" title="Abertura + reaberturas registradas no hist\xF3rico do alarme">
|
|
54002
|
+
<div class="adm-kpi-value" id="admKpiOcc" style="color:${sev.text}">\u2026</div>
|
|
53507
54003
|
<div class="adm-kpi-label">Ocorr\xEAncias</div>
|
|
53508
54004
|
</div>
|
|
53509
54005
|
<div class="adm-kpi">
|
|
53510
54006
|
<div class="adm-kpi-value">${deviceCount}</div>
|
|
53511
54007
|
<div class="adm-kpi-label">Dispositivos</div>
|
|
53512
54008
|
</div>
|
|
53513
|
-
<div class="adm-kpi">
|
|
53514
|
-
<div class="adm-kpi-value">${
|
|
53515
|
-
<div class="adm-kpi-label"
|
|
54009
|
+
<div class="adm-kpi" title="${isClosedState ? "Da abertura ao fechamento" : "Desde a abertura do alarme at\xE9 agora"}">
|
|
54010
|
+
<div class="adm-kpi-value">${Number.isFinite(ageMs) ? durationLabel(ageMs) : "-"}</div>
|
|
54011
|
+
<div class="adm-kpi-label">${ageKpiLabel}</div>
|
|
53516
54012
|
</div>
|
|
53517
54013
|
</div>` : ""}
|
|
53518
54014
|
|
|
@@ -53529,15 +54025,16 @@ function openAlarmDetailsModal(alarm, themeMode = "light", groupMode, onAction)
|
|
|
53529
54025
|
<div class="adm-section">
|
|
53530
54026
|
<div class="adm-section-title">Identifica\xE7\xE3o</div>
|
|
53531
54027
|
${row("Shopping", alarm.customerName)}
|
|
53532
|
-
${row
|
|
54028
|
+
${devices.length ? `<div class="adm-row"><span class="adm-row-label">Dispositivo(s)</span><span class="adm-row-value adm-row-value--devices">${devices.map((d) => deviceIdentityHtml(alarm, d)).join("")}</span></div>` : ""}
|
|
53533
54029
|
${alarm.triggerValue != null ? row("Valor do disparo", String(alarm.triggerValue)) : ""}
|
|
53534
54030
|
${groupMode === "separado" ? row("ID", alarm.id) : ""}
|
|
53535
54031
|
</div>
|
|
53536
54032
|
|
|
53537
54033
|
<div class="adm-section">
|
|
53538
54034
|
<div class="adm-section-title">Datas</div>
|
|
53539
|
-
${row("
|
|
53540
|
-
|
|
54035
|
+
${row("Aberto em", fmt(alarm.firstOccurrence))}
|
|
54036
|
+
<div id="admReopenRows"></div>
|
|
54037
|
+
${row("\xDAltima atualiza\xE7\xE3o", fmt(alarm.lastOccurrence))}
|
|
53541
54038
|
${alarm.acknowledgedAt ? row("Reconhecido em", fmt(alarm.acknowledgedAt)) : ""}
|
|
53542
54039
|
${alarm.acknowledgedBy ? row("Reconhecido por", alarm.acknowledgedBy) : ""}
|
|
53543
54040
|
${alarm.snoozedUntil ? row("Adiado at\xE9", fmt(alarm.snoozedUntil)) : ""}
|
|
@@ -53562,7 +54059,7 @@ function openAlarmDetailsModal(alarm, themeMode = "light", groupMode, onAction)
|
|
|
53562
54059
|
<div class="adm-section-title">IDs de Alarme (${alarm._groupAlarmIds.length})</div>
|
|
53563
54060
|
<div class="adm-alarm-ids-list" style="display:flex;flex-wrap:wrap;gap:6px;">
|
|
53564
54061
|
${alarm._groupAlarmIds.map(
|
|
53565
|
-
(rawId) => `<span class="adm-alarm-id-chip" title="Clique para copiar" data-copy-id="${escHtml2(rawId)}" style="display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border:1px solid
|
|
54062
|
+
(rawId) => `<span class="adm-alarm-id-chip" title="Clique para copiar" data-copy-id="${escHtml2(rawId)}" style="display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border:1px solid color-mix(in srgb, var(--myio-brand-700, #7c3aed) 25%, transparent);background:color-mix(in srgb, var(--myio-brand-700, #7c3aed) 8%, transparent);border-radius:6px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;color:var(--myio-brand-700, #4c1d95);cursor:pointer;user-select:all;">${escHtml2(rawId)}</span>`
|
|
53566
54063
|
).join("")}
|
|
53567
54064
|
</div>
|
|
53568
54065
|
</div>` : ""}
|
|
@@ -53584,7 +54081,7 @@ function openAlarmDetailsModal(alarm, themeMode = "light", groupMode, onAction)
|
|
|
53584
54081
|
<div class="adm-panel" data-panel="timeline">
|
|
53585
54082
|
<div class="adm-tl-header" id="admTlHeader">
|
|
53586
54083
|
<div class="adm-tl-toolbar">
|
|
53587
|
-
<span class="adm-tl-summary" id="admTlSummary"
|
|
54084
|
+
<span class="adm-tl-summary" id="admTlSummary">Carregando hist\xF3rico\u2026</span>
|
|
53588
54085
|
<button class="adm-tl-batch-toggle" id="admTlBatchToggle" title="A\xE7\xF5es em lote">
|
|
53589
54086
|
<svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="5" width="4" height="4" rx="1"/><path d="M10 7h11"/><rect x="3" y="12" width="4" height="4" rx="1"/><path d="M10 14h11"/><rect x="3" y="19" width="4" height="4" rx="1"/><path d="M10 21h11"/></svg>
|
|
53590
54087
|
A\xE7\xE3o em lote
|
|
@@ -53610,7 +54107,7 @@ function openAlarmDetailsModal(alarm, themeMode = "light", groupMode, onAction)
|
|
|
53610
54107
|
<label class="adm-report-label">De</label>
|
|
53611
54108
|
<input type="date" class="adm-report-date-input" id="admRptStart" value="${fmtDateInput(firstMs)}">
|
|
53612
54109
|
<label class="adm-report-label">At\xE9</label>
|
|
53613
|
-
<input type="date" class="adm-report-date-input" id="admRptEnd" value="${fmtDateInput(
|
|
54110
|
+
<input type="date" class="adm-report-date-input" id="admRptEnd" value="${fmtDateInput(Date.now())}">
|
|
53614
54111
|
<button class="adm-report-emit-btn" id="admRptEmit">
|
|
53615
54112
|
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h11"/></svg>
|
|
53616
54113
|
Carregar
|
|
@@ -53661,7 +54158,8 @@ function openAlarmDetailsModal(alarm, themeMode = "light", groupMode, onAction)
|
|
|
53661
54158
|
</div>
|
|
53662
54159
|
|
|
53663
54160
|
<div class="adm-section">
|
|
53664
|
-
<div class="adm-section-title">
|
|
54161
|
+
<div class="adm-section-title">Ocorr\xEAncias no per\xEDodo</div>
|
|
54162
|
+
<div class="adm-chart-note" id="admChartNote">Carregando hist\xF3rico\u2026</div>
|
|
53665
54163
|
<div class="adm-chart-wrapper">
|
|
53666
54164
|
<canvas id="admMainChart" class="adm-chart-canvas"></canvas>
|
|
53667
54165
|
</div>
|
|
@@ -53719,21 +54217,45 @@ function openAlarmDetailsModal(alarm, themeMode = "light", groupMode, onAction)
|
|
|
53719
54217
|
let activeChartType = "bar";
|
|
53720
54218
|
let activeVizMode = "total";
|
|
53721
54219
|
let activePeriod = initialPeriod;
|
|
54220
|
+
const noteEl = graficoPanel.querySelector("#admChartNote");
|
|
54221
|
+
const updateNote = () => {
|
|
54222
|
+
if (!noteEl) return;
|
|
54223
|
+
if (!events) {
|
|
54224
|
+
noteEl.textContent = "Carregando hist\xF3rico\u2026";
|
|
54225
|
+
return;
|
|
54226
|
+
}
|
|
54227
|
+
const n = occEvents().length;
|
|
54228
|
+
noteEl.textContent = `${n} ocorr\xEAncia${n !== 1 ? "s" : ""} registrada${n !== 1 ? "s" : ""} (abertura + reaberturas do hist\xF3rico do alarme).` + (n < 3 ? " Poucos eventos para indicar tend\xEAncia." : "");
|
|
54229
|
+
};
|
|
53722
54230
|
const buildMain = () => {
|
|
53723
|
-
|
|
54231
|
+
updateNote();
|
|
54232
|
+
if (!events) return;
|
|
53724
54233
|
const canvas = graficoPanel.querySelector("#admMainChart");
|
|
53725
54234
|
if (!canvas) return;
|
|
54235
|
+
const occ = occEvents();
|
|
54236
|
+
const fromMs = occ.length ? occ[0].ts : Number.isFinite(firstMs) ? firstMs : Date.now();
|
|
54237
|
+
const nb = bucketEvents(occ, devices, activePeriod, fromMs, Date.now());
|
|
53726
54238
|
if (chartInstances[0]) {
|
|
53727
54239
|
chartInstances[0].destroy();
|
|
53728
54240
|
chartInstances[0] = null;
|
|
53729
54241
|
}
|
|
53730
54242
|
chartInstances[0] = createMainChart(canvas, nb, devices, activeChartType, activeVizMode, chartSingleColor);
|
|
53731
54243
|
};
|
|
53732
|
-
const
|
|
54244
|
+
const buildSecondary = () => {
|
|
54245
|
+
if (!events) return;
|
|
54246
|
+
const occ = occEvents();
|
|
53733
54247
|
const dowCanvas = graficoPanel.querySelector("#admDowChart");
|
|
53734
|
-
if (
|
|
54248
|
+
if (chartInstances[1]) {
|
|
54249
|
+
chartInstances[1].destroy();
|
|
54250
|
+
chartInstances[1] = null;
|
|
54251
|
+
}
|
|
54252
|
+
if (dowCanvas) chartInstances[1] = createDowChart(dowCanvas, dowFromEvents(occ));
|
|
53735
54253
|
const hodCanvas = graficoPanel.querySelector("#admHodChart");
|
|
53736
|
-
if (
|
|
54254
|
+
if (chartInstances[2]) {
|
|
54255
|
+
chartInstances[2].destroy();
|
|
54256
|
+
chartInstances[2] = null;
|
|
54257
|
+
}
|
|
54258
|
+
if (hodCanvas) chartInstances[2] = createHodChart(hodCanvas, hodFromEvents(occ));
|
|
53737
54259
|
};
|
|
53738
54260
|
graficoPanel.querySelectorAll("button[data-chart-type]").forEach((btn) => {
|
|
53739
54261
|
btn.addEventListener("click", () => {
|
|
@@ -53759,11 +54281,16 @@ function openAlarmDetailsModal(alarm, themeMode = "light", groupMode, onAction)
|
|
|
53759
54281
|
buildMain();
|
|
53760
54282
|
});
|
|
53761
54283
|
});
|
|
54284
|
+
const renderCharts = () => {
|
|
54285
|
+
buildMain();
|
|
54286
|
+
buildSecondary();
|
|
54287
|
+
};
|
|
53762
54288
|
overlay.querySelectorAll('.adm-tab[data-panel="grafico"]').forEach((tabBtn) => {
|
|
53763
|
-
tabBtn.addEventListener("click", () => setTimeout(
|
|
53764
|
-
|
|
53765
|
-
|
|
53766
|
-
|
|
54289
|
+
tabBtn.addEventListener("click", () => setTimeout(renderCharts, 30));
|
|
54290
|
+
});
|
|
54291
|
+
onEventsReady.push(() => {
|
|
54292
|
+
if (graficoPanel.classList.contains("is-active")) renderCharts();
|
|
54293
|
+
else updateNote();
|
|
53767
54294
|
});
|
|
53768
54295
|
}
|
|
53769
54296
|
const rptPanel = overlay.querySelector('.adm-panel[data-panel="timeline"]');
|
|
@@ -53779,159 +54306,126 @@ function openAlarmDetailsModal(alarm, themeMode = "light", groupMode, onAction)
|
|
|
53779
54306
|
const _svgAck = `<svg viewBox="0 0 24 24" width="11" height="11" fill="currentColor" aria-hidden="true"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>`;
|
|
53780
54307
|
const _svgSnooze = `<svg viewBox="0 0 24 24" width="11" height="11" fill="currentColor" aria-hidden="true"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67V7z"/></svg>`;
|
|
53781
54308
|
const _svgEscalate = `<svg viewBox="0 0 24 24" width="11" height="11" fill="currentColor" aria-hidden="true"><path d="M4 12l1.41 1.41L11 7.83V20h2V7.83l5.58 5.59L20 12l-8-8-8 8z"/></svg>`;
|
|
53782
|
-
const buildOccurrenceSection = (
|
|
53783
|
-
|
|
53784
|
-
|
|
53785
|
-
|
|
54309
|
+
const buildOccurrenceSection = (startMs, endMs) => {
|
|
54310
|
+
if (!events) {
|
|
54311
|
+
return { html: '<div class="adm-report-empty-hint">Carregando hist\xF3rico do alarme\u2026</div>', csvRows: [] };
|
|
54312
|
+
}
|
|
53786
54313
|
const showDevice = !isSeparado && devices.length > 0;
|
|
54314
|
+
const showAlarm = groupMode === "porDispositivo" && (alarm._groupAlarmIds?.length || 0) > 1;
|
|
53787
54315
|
const isActive = alarm.state === "OPEN" || alarm.state === "ESCALATED";
|
|
53788
54316
|
const isClosed = alarm.state === "CLOSED";
|
|
53789
|
-
const
|
|
53790
|
-
|
|
53791
|
-
|
|
53792
|
-
|
|
53793
|
-
|
|
53794
|
-
|
|
53795
|
-
|
|
53796
|
-
if (showDevice) csvHeader.push("Dispositivo");
|
|
53797
|
-
if (hasTrigger) csvHeader.push("Trigger");
|
|
54317
|
+
const occIndex = /* @__PURE__ */ new Map();
|
|
54318
|
+
occEvents().forEach((e, i) => occIndex.set(e, i + 1));
|
|
54319
|
+
const inRange = events.filter((e) => e.ts >= startMs && e.ts <= endMs).sort((a, b) => b.ts - a.ts || (occIndex.get(b) || 0) - (occIndex.get(a) || 0));
|
|
54320
|
+
const csvHeader = ["#", "Data/Hora", "Evento"];
|
|
54321
|
+
if (showAlarm) csvHeader.push("Alarme");
|
|
54322
|
+
if (showDevice) csvHeader.push("Dispositivo", "Name (TB)");
|
|
54323
|
+
csvHeader.push("Por");
|
|
53798
54324
|
const rows = [csvHeader];
|
|
53799
|
-
const
|
|
53800
|
-
|
|
53801
|
-
|
|
53802
|
-
|
|
53803
|
-
|
|
53804
|
-
|
|
53805
|
-
|
|
53806
|
-
|
|
53807
|
-
|
|
53808
|
-
const gHasTrig = gTrig != null;
|
|
53809
|
-
const gThead = `<thead><tr>
|
|
53810
|
-
<th class="adm-rpt-th adm-rpt-th--idx">#</th>
|
|
53811
|
-
<th class="adm-rpt-th">Data/Hora</th>
|
|
53812
|
-
${gHasTrig ? `<th class="adm-rpt-th adm-rpt-th--num">Trigger</th>` : ""}
|
|
53813
|
-
</tr></thead>`;
|
|
53814
|
-
const gRows = [];
|
|
53815
|
-
const MAX = 10;
|
|
53816
|
-
for (let i = gCount; i >= 1; i--) {
|
|
53817
|
-
const tsMs = gFirstMs + gInterval * (i - 1);
|
|
53818
|
-
const isSingle = gCount === 1;
|
|
53819
|
-
const isLatest = !isSingle && i === gCount;
|
|
53820
|
-
const isFirst = !isSingle && i === 1;
|
|
53821
|
-
const isEst = !isSingle && !isLatest && !isFirst;
|
|
53822
|
-
const timeStr = (isEst ? "~" : "") + fmt(tsMs);
|
|
53823
|
-
const style = idxStyle(isSingle, isLatest, isFirst);
|
|
53824
|
-
gRows.push(`<tr>
|
|
53825
|
-
<td class="adm-rpt-cell adm-rpt-cell--idx"${style ? ` style="${style}"` : ""}>#${i}</td>
|
|
53826
|
-
<td class="adm-rpt-cell">${timeStr}</td>
|
|
53827
|
-
${gHasTrig ? `<td class="adm-rpt-cell adm-rpt-cell--num">${escHtml2(gTrig)}</td>` : ""}
|
|
53828
|
-
</tr>`);
|
|
53829
|
-
const csvRow = [`#${i}`, fmt(tsMs)];
|
|
53830
|
-
if (gHasTrig) csvRow.push(gTrig);
|
|
53831
|
-
rows.push(csvRow);
|
|
53832
|
-
if (gRows.length >= MAX) break;
|
|
53833
|
-
}
|
|
53834
|
-
const sepStyle = gi > 0 ? ' style="margin-top:12px;padding-top:12px;border-top:1px dashed #e5e7eb"' : "";
|
|
53835
|
-
return `<div${sepStyle}>
|
|
53836
|
-
<div class="adm-timeline-group-header">
|
|
53837
|
-
<label class="adm-tl-cb-wrap" data-cb-wrap><input type="checkbox" class="adm-tl-row-cb" data-item-id="${alarm.id}__grp${gi}"></label>
|
|
53838
|
-
<span class="adm-tl-group-title">${escHtml2(grp.title)}</span>
|
|
53839
|
-
<span class="adm-timeline-group-count">${gCount}</span>
|
|
53840
|
-
<div class="adm-tl-row-btns adm-tl-row-btns--group" data-row-btns>
|
|
53841
|
-
${isActive ? `<button class="adm-tl-row-btn adm-tl-row-btn--ack" data-action="acknowledge" data-item-id="${alarm.id}" title="Reconhecer">${_svgAck}</button>` : ""}
|
|
53842
|
-
${!isClosed ? `<button class="adm-tl-row-btn adm-tl-row-btn--snooze" data-action="snooze" data-item-id="${alarm.id}" title="Adiar">${_svgSnooze}</button>` : ""}
|
|
53843
|
-
${!isClosed ? `<button class="adm-tl-row-btn adm-tl-row-btn--escalate" data-action="escalate" data-item-id="${alarm.id}" title="Escalar">${_svgEscalate}</button>` : ""}
|
|
53844
|
-
</div>
|
|
53845
|
-
</div>
|
|
53846
|
-
<div class="adm-rpt-table-wrap" style="margin-bottom:0">
|
|
53847
|
-
<table class="adm-rpt-table">${gThead}<tbody>${gRows.join("")}</tbody></table>
|
|
53848
|
-
</div>
|
|
53849
|
-
</div>`;
|
|
53850
|
-
}).join("");
|
|
53851
|
-
return { html: groupHtml, csvRows: rows };
|
|
53852
|
-
}
|
|
53853
|
-
const deviceTokens = alarm.source ? alarm.source.split(/[,;]+/).map((s) => s.trim()).filter(Boolean) : [];
|
|
54325
|
+
const kindStyle = {
|
|
54326
|
+
raised: "color:#ef4444;font-weight:700",
|
|
54327
|
+
reopened: "color:#d97706;font-weight:700",
|
|
54328
|
+
closed: "color:#16a34a;font-weight:700",
|
|
54329
|
+
acknowledged: "color:var(--myio-brand-700, #7c3aed);font-weight:600",
|
|
54330
|
+
silenced: "color:#6b7280;font-weight:600",
|
|
54331
|
+
escalated: "color:#dc2626;font-weight:600",
|
|
54332
|
+
other: "color:#6b7280"
|
|
54333
|
+
};
|
|
53854
54334
|
const thead = `<thead><tr>
|
|
53855
54335
|
<th class="adm-rpt-th adm-rpt-th--idx">#</th>
|
|
53856
54336
|
<th class="adm-rpt-th">Data/Hora</th>
|
|
54337
|
+
<th class="adm-rpt-th">Evento</th>
|
|
54338
|
+
${showAlarm ? `<th class="adm-rpt-th">Alarme</th>` : ""}
|
|
53857
54339
|
${showDevice ? `<th class="adm-rpt-th">Dispositivo</th>` : ""}
|
|
53858
|
-
|
|
54340
|
+
<th class="adm-rpt-th">Por</th>
|
|
53859
54341
|
<th class="adm-rpt-th adm-tl-actions-th">A\xE7\xF5es</th>
|
|
53860
54342
|
</tr></thead>`;
|
|
53861
|
-
const MAX_SHOWN =
|
|
53862
|
-
|
|
53863
|
-
const
|
|
53864
|
-
const
|
|
53865
|
-
const
|
|
53866
|
-
const
|
|
53867
|
-
|
|
53868
|
-
|
|
53869
|
-
|
|
53870
|
-
|
|
53871
|
-
|
|
53872
|
-
|
|
53873
|
-
<td class="adm-rpt-cell">${
|
|
53874
|
-
|
|
53875
|
-
|
|
54343
|
+
const MAX_SHOWN = 200;
|
|
54344
|
+
const tableRows = inRange.slice(0, MAX_SHOWN).map((e, ri) => {
|
|
54345
|
+
const n = occIndex.get(e);
|
|
54346
|
+
const ident = showDevice ? resolveDeviceIdentity(alarm, e.device) : null;
|
|
54347
|
+
const actor = e.kind === "raised" ? "regra" : e.actor === "system" ? "sistema" : e.actor || "-";
|
|
54348
|
+
const csvRow = [n ? `#${n}` : "", fmt(e.ts), EVENT_LABEL[e.kind]];
|
|
54349
|
+
if (showAlarm) csvRow.push(e.title);
|
|
54350
|
+
if (showDevice) csvRow.push(ident.label, ident.name || e.device);
|
|
54351
|
+
csvRow.push(actor);
|
|
54352
|
+
rows.push(csvRow);
|
|
54353
|
+
const itemId = `${e.alarmId}__ev${ri}`;
|
|
54354
|
+
return `<tr>
|
|
54355
|
+
<td class="adm-rpt-cell adm-rpt-cell--idx">${n ? `#${n}` : ""}</td>
|
|
54356
|
+
<td class="adm-rpt-cell">${fmt(e.ts)}</td>
|
|
54357
|
+
<td class="adm-rpt-cell" style="${kindStyle[e.kind]}">${EVENT_LABEL[e.kind]}</td>
|
|
54358
|
+
${showAlarm ? `<td class="adm-rpt-cell">${escHtml2(e.title)}</td>` : ""}
|
|
54359
|
+
${showDevice ? `<td class="adm-rpt-cell adm-rpt-cell--dev" title="${escHtml2(ident.name || e.device)}">${escHtml2(ident.label)}</td>` : ""}
|
|
54360
|
+
<td class="adm-rpt-cell">${escHtml2(actor)}</td>
|
|
53876
54361
|
<td class="adm-rpt-cell adm-tl-action-cell">
|
|
53877
|
-
|
|
54362
|
+
${e.occurrence ? `<label class="adm-tl-cb-wrap" data-cb-wrap><input type="checkbox" class="adm-tl-row-cb" data-item-id="${escHtml2(itemId)}"></label>
|
|
53878
54363
|
<div class="adm-tl-row-btns" data-row-btns>
|
|
53879
|
-
${isActive ? `<button class="adm-tl-row-btn adm-tl-row-btn--ack" data-action="acknowledge" data-item-id="${
|
|
53880
|
-
${!isClosed ? `<button class="adm-tl-row-btn adm-tl-row-btn--snooze" data-action="snooze" data-item-id="${
|
|
53881
|
-
${!isClosed ? `<button class="adm-tl-row-btn adm-tl-row-btn--escalate" data-action="escalate" data-item-id="${
|
|
53882
|
-
</div
|
|
54364
|
+
${isActive ? `<button class="adm-tl-row-btn adm-tl-row-btn--ack" data-action="acknowledge" data-item-id="${escHtml2(itemId)}" title="Reconhecer">${_svgAck}</button>` : ""}
|
|
54365
|
+
${!isClosed ? `<button class="adm-tl-row-btn adm-tl-row-btn--snooze" data-action="snooze" data-item-id="${escHtml2(itemId)}" title="Adiar">${_svgSnooze}</button>` : ""}
|
|
54366
|
+
${!isClosed ? `<button class="adm-tl-row-btn adm-tl-row-btn--escalate" data-action="escalate" data-item-id="${escHtml2(itemId)}" title="Escalar">${_svgEscalate}</button>` : ""}
|
|
54367
|
+
</div>` : ""}
|
|
53883
54368
|
</td>
|
|
53884
|
-
</tr
|
|
53885
|
-
|
|
53886
|
-
|
|
53887
|
-
|
|
53888
|
-
|
|
53889
|
-
if (tableRows.length >= MAX_SHOWN) break;
|
|
53890
|
-
}
|
|
53891
|
-
const html = `<div class="adm-rpt-table-wrap" id="admRptTableWrap" style="max-height:340px;overflow-y:auto">
|
|
54369
|
+
</tr>`;
|
|
54370
|
+
});
|
|
54371
|
+
const occInRange = inRange.filter((e) => e.occurrence).length;
|
|
54372
|
+
const colspan = 4 + (showAlarm ? 1 : 0) + (showDevice ? 1 : 0);
|
|
54373
|
+
const html = inRange.length ? `<div class="adm-rpt-table-wrap" id="admRptTableWrap" style="max-height:340px;overflow-y:auto">
|
|
53892
54374
|
<table class="adm-rpt-table">
|
|
53893
54375
|
${thead}
|
|
53894
54376
|
<tbody>${tableRows.join("")}</tbody>
|
|
53895
54377
|
<tfoot><tr>
|
|
53896
|
-
<td class="adm-rpt-cell adm-rpt-cell--total" colspan="${
|
|
53897
|
-
<td class="adm-rpt-cell adm-rpt-cell--num adm-rpt-cell--total">${count} ocorr\xEAncia${count !== 1 ? "s" : ""}</td>
|
|
54378
|
+
<td class="adm-rpt-cell adm-rpt-cell--total" colspan="${colspan}">Total: ${occInRange} ocorr\xEAncia${occInRange !== 1 ? "s" : ""} \xB7 ${inRange.length} evento${inRange.length !== 1 ? "s" : ""}</td>
|
|
53898
54379
|
<td class="adm-rpt-cell adm-rpt-cell--total"></td>
|
|
53899
54380
|
</tr></tfoot>
|
|
53900
54381
|
</table>
|
|
53901
|
-
</div
|
|
54382
|
+
</div>` : '<div class="adm-report-empty-hint">Nenhum evento no per\xEDodo selecionado.</div>';
|
|
53902
54383
|
return { html, csvRows: rows };
|
|
53903
54384
|
};
|
|
53904
54385
|
const buildReportTable = () => {
|
|
53905
54386
|
const startInput = rptPanel.querySelector("#admRptStart");
|
|
53906
54387
|
const endInput = rptPanel.querySelector("#admRptEnd");
|
|
53907
|
-
const
|
|
53908
|
-
|
|
53909
|
-
|
|
53910
|
-
|
|
54388
|
+
const toLocalDay = (v) => {
|
|
54389
|
+
const [y, m, d] = v.split("-").map(Number);
|
|
54390
|
+
return new Date(y, (m || 1) - 1, d || 1).getTime();
|
|
54391
|
+
};
|
|
54392
|
+
const startMs = startInput?.value ? toLocalDay(startInput.value) : 0;
|
|
54393
|
+
const endMs = endInput?.value ? toLocalDay(endInput.value) + 864e5 - 1 : Date.now();
|
|
53911
54394
|
const headerHtml = isSeparado ? `<div class="adm-rpt-device-header">
|
|
53912
|
-
<span class="adm-rpt-device-name">${escHtml2(alarm.source)}</span>
|
|
54395
|
+
<span class="adm-rpt-device-name" title="${escHtml2(alarm.source)}">${escHtml2(resolveDeviceLabel(alarm, alarm.source) || alarm.source)}</span>
|
|
53913
54396
|
<span class="adm-rpt-device-sep">\xB7</span>
|
|
53914
54397
|
<span class="adm-rpt-alarm-type">${escHtml2(alarm.title)}</span>
|
|
53915
54398
|
</div>` : "";
|
|
53916
|
-
const
|
|
53917
|
-
const { html: occHtml, csvRows } = buildOccurrenceSection(occFirst, occLast);
|
|
54399
|
+
const { html: occHtml, csvRows } = buildOccurrenceSection(startMs, endMs);
|
|
53918
54400
|
lastCsvRows = csvRows;
|
|
53919
|
-
const
|
|
54401
|
+
const occ = occEvents();
|
|
54402
|
+
const occN = occ.length;
|
|
54403
|
+
const sectionTitle = events ? `${occN} ocorr\xEAncia${occN !== 1 ? "s" : ""} \xB7 ${events.length} evento${events.length !== 1 ? "s" : ""} no hist\xF3rico` : "Hist\xF3rico do alarme";
|
|
54404
|
+
const reopenN = occ.filter((e) => e.kind === "reopened").length;
|
|
54405
|
+
const statsHtml = events && (occN > 1 || alarm.triggerValue != null) ? `<div class="adm-section">
|
|
53920
54406
|
<div class="adm-section-title">Estat\xEDsticas de recorr\xEAncia</div>
|
|
53921
|
-
${alarm.triggerValue != null ? row("Valor do disparo", String(alarm.triggerValue)) : ""}
|
|
53922
|
-
${
|
|
53923
|
-
${
|
|
53924
|
-
${
|
|
53925
|
-
${
|
|
54407
|
+
${alarm.triggerValue != null ? row("Valor do \xFAltimo disparo", String(alarm.triggerValue)) : ""}
|
|
54408
|
+
${occN > 1 ? row("Reaberturas", String(reopenN)) : ""}
|
|
54409
|
+
${occN > 1 ? row("Intervalo m\xE9dio entre ocorr\xEAncias", durationLabel((occ[occN - 1].ts - occ[0].ts) / (occN - 1))) : ""}
|
|
54410
|
+
${occN > 1 ? row("Primeira ocorr\xEAncia", fmt(occ[0].ts)) : ""}
|
|
54411
|
+
${occN > 1 ? row("\xDAltima ocorr\xEAncia", fmt(occ[occN - 1].ts)) : ""}
|
|
53926
54412
|
</div>` : "";
|
|
53927
54413
|
let summaryHtml = "";
|
|
53928
|
-
if (!isSeparado && devices.length > 0) {
|
|
53929
|
-
const
|
|
53930
|
-
|
|
53931
|
-
|
|
53932
|
-
|
|
53933
|
-
|
|
53934
|
-
|
|
54414
|
+
if (events && !isSeparado && devices.length > 0) {
|
|
54415
|
+
const byDev = /* @__PURE__ */ new Map();
|
|
54416
|
+
occ.forEach((e) => {
|
|
54417
|
+
const k = devices.includes(e.device) ? e.device : devices[0];
|
|
54418
|
+
byDev.set(k, [...byDev.get(k) || [], e]);
|
|
54419
|
+
});
|
|
54420
|
+
const devRows = devices.map((dev) => {
|
|
54421
|
+
const list = byDev.get(dev) || [];
|
|
54422
|
+
return `<tr>
|
|
54423
|
+
<td class="adm-rpt-cell" title="${escHtml2(dev)}">${escHtml2(resolveDeviceLabel(alarm, dev) || dev)}</td>
|
|
54424
|
+
<td class="adm-rpt-cell adm-rpt-cell--num">${list.length}</td>
|
|
54425
|
+
<td class="adm-rpt-cell">${list.length ? fmt(list[0].ts) : "-"}</td>
|
|
54426
|
+
<td class="adm-rpt-cell">${list.length ? fmt(list[list.length - 1].ts) : "-"}</td>
|
|
54427
|
+
</tr>`;
|
|
54428
|
+
}).join("");
|
|
53935
54429
|
summaryHtml = `
|
|
53936
54430
|
<div class="adm-rpt-section-title">Sum\xE1rio por dispositivo</div>
|
|
53937
54431
|
<div class="adm-rpt-table-wrap adm-rpt-table-wrap--summary">
|
|
@@ -53955,8 +54449,10 @@ function openAlarmDetailsModal(alarm, themeMode = "light", groupMode, onAction)
|
|
|
53955
54449
|
</div>
|
|
53956
54450
|
${statsHtml}
|
|
53957
54451
|
${summaryHtml}`;
|
|
53958
|
-
|
|
53959
|
-
|
|
54452
|
+
if (events) {
|
|
54453
|
+
csvBtn?.removeAttribute("disabled");
|
|
54454
|
+
pdfBtn?.removeAttribute("disabled");
|
|
54455
|
+
}
|
|
53960
54456
|
};
|
|
53961
54457
|
csvBtn?.addEventListener("click", () => {
|
|
53962
54458
|
if (!lastCsvRows.length) return;
|
|
@@ -53987,14 +54483,14 @@ function openAlarmDetailsModal(alarm, themeMode = "light", groupMode, onAction)
|
|
|
53987
54483
|
tfoot td{font-weight:700;background:#ede9fe;}
|
|
53988
54484
|
</style></head><body>
|
|
53989
54485
|
<h2>${escHtml2(alarm.title)}</h2>
|
|
53990
|
-
<p>${escHtml2(alarm.id)} \xB7 ${escHtml2(alarm.customerName || "")} \xB7 ${
|
|
53991
|
-
<h3>
|
|
54486
|
+
<p>${escHtml2(alarm.id)} \xB7 ${escHtml2(alarm.customerName || "")} \xB7 ${occEvents().length} ocorr\xEAncia${occEvents().length !== 1 ? "s" : ""} \xB7 Emitido em ${(/* @__PURE__ */ new Date()).toLocaleDateString("pt-BR")}</p>
|
|
54487
|
+
<h3>Hist\xF3rico</h3>${tbl}
|
|
53992
54488
|
${sumTbl ? `<h3>Sum\xE1rio por dispositivo</h3>${sumTbl}` : ""}
|
|
53993
54489
|
<script>window.onload=function(){window.print();window.close();}</script>
|
|
53994
54490
|
</body></html>`);
|
|
53995
54491
|
win2.document.close();
|
|
53996
54492
|
});
|
|
53997
|
-
|
|
54493
|
+
const refreshReport = () => {
|
|
53998
54494
|
buildReportTable();
|
|
53999
54495
|
if (batchActive && grid) {
|
|
54000
54496
|
grid.querySelectorAll("[data-cb-wrap]").forEach((el2) => {
|
|
@@ -54004,8 +54500,10 @@ function openAlarmDetailsModal(alarm, themeMode = "light", groupMode, onAction)
|
|
|
54004
54500
|
el2.style.display = "none";
|
|
54005
54501
|
});
|
|
54006
54502
|
}
|
|
54007
|
-
}
|
|
54503
|
+
};
|
|
54504
|
+
emitBtn?.addEventListener("click", refreshReport);
|
|
54008
54505
|
buildReportTable();
|
|
54506
|
+
onEventsReady.push(refreshReport);
|
|
54009
54507
|
const batchToggleBtn = rptPanel.querySelector("#admTlBatchToggle");
|
|
54010
54508
|
const batchBar = rptPanel.querySelector("#admTlBatchBar");
|
|
54011
54509
|
const selAllCb = rptPanel.querySelector("#admTlSelAll");
|
|
@@ -54110,17 +54608,48 @@ function openAlarmDetailsModal(alarm, themeMode = "light", groupMode, onAction)
|
|
|
54110
54608
|
}
|
|
54111
54609
|
}, tbCfg);
|
|
54112
54610
|
}
|
|
54611
|
+
const applyEvents = () => {
|
|
54612
|
+
const occ = occEvents();
|
|
54613
|
+
const occText = events ? String(occ.length || "-") : "\u2026";
|
|
54614
|
+
const kpi = overlay.querySelector("#admKpiOcc");
|
|
54615
|
+
if (kpi) kpi.textContent = occText;
|
|
54616
|
+
const badge = overlay.querySelector("#admTlTabBadge");
|
|
54617
|
+
if (badge) badge.textContent = occText;
|
|
54618
|
+
const summary = overlay.querySelector("#admTlSummary");
|
|
54619
|
+
if (summary && events) summary.textContent = `${occ.length} ocorr\xEAncia${occ.length !== 1 ? "s" : ""}`;
|
|
54620
|
+
const reopenEl = overlay.querySelector("#admReopenRows");
|
|
54621
|
+
const reopens = occ.filter((e) => e.kind === "reopened");
|
|
54622
|
+
if (reopenEl) {
|
|
54623
|
+
reopenEl.innerHTML = reopens.length ? row("\xDAltima reabertura", fmt(reopens[reopens.length - 1].ts)) + (reopens.length > 1 ? row("Reaberturas", String(reopens.length)) : "") : "";
|
|
54624
|
+
}
|
|
54625
|
+
onEventsReady.forEach((fn) => {
|
|
54626
|
+
try {
|
|
54627
|
+
fn();
|
|
54628
|
+
} catch (_) {
|
|
54629
|
+
}
|
|
54630
|
+
});
|
|
54631
|
+
};
|
|
54632
|
+
loadAlarmEvents(alarm).then((ev) => {
|
|
54633
|
+
events = ev;
|
|
54634
|
+
}).catch(() => {
|
|
54635
|
+
events = [];
|
|
54636
|
+
}).then(() => {
|
|
54637
|
+
if (overlay.isConnected) applyEvents();
|
|
54638
|
+
});
|
|
54113
54639
|
const drawer = overlay.querySelector(".adm-drawer");
|
|
54114
|
-
const
|
|
54115
|
-
|
|
54116
|
-
|
|
54117
|
-
|
|
54118
|
-
|
|
54119
|
-
|
|
54120
|
-
|
|
54121
|
-
|
|
54640
|
+
const chrome = createPremiumModalChrome({
|
|
54641
|
+
modalId: "adm-modal",
|
|
54642
|
+
icon: "\u{1F514}",
|
|
54643
|
+
title: escHtml2(alarm.title),
|
|
54644
|
+
modalEl: drawer,
|
|
54645
|
+
theme: themeMode,
|
|
54646
|
+
maximizedClass: "adm-drawer--maximized",
|
|
54647
|
+
headerRadius: "18px 18px 0 0",
|
|
54648
|
+
onClose: () => closeModal2(),
|
|
54649
|
+
footer: alarm.customerName ? { customerName: alarm.customerName } : {}
|
|
54122
54650
|
});
|
|
54123
54651
|
const closeModal2 = () => {
|
|
54652
|
+
chrome.destroy();
|
|
54124
54653
|
overlay.classList.remove("adm-overlay--visible");
|
|
54125
54654
|
setTimeout(() => {
|
|
54126
54655
|
overlay.remove();
|
|
@@ -54137,7 +54666,34 @@ function openAlarmDetailsModal(alarm, themeMode = "light", groupMode, onAction)
|
|
|
54137
54666
|
if (e.key === "Escape") closeModal2();
|
|
54138
54667
|
};
|
|
54139
54668
|
document.addEventListener("keydown", onKey);
|
|
54140
|
-
overlay.
|
|
54669
|
+
overlay.addEventListener("click", (e) => {
|
|
54670
|
+
const btn = e.target.closest(".adm-copy-btn");
|
|
54671
|
+
if (!btn) return;
|
|
54672
|
+
e.stopPropagation();
|
|
54673
|
+
const text2 = btn.getAttribute("data-copy") || "";
|
|
54674
|
+
const done = () => {
|
|
54675
|
+
btn.classList.add("is-copied");
|
|
54676
|
+
btn.title = "Copiado!";
|
|
54677
|
+
setTimeout(() => {
|
|
54678
|
+
btn.classList.remove("is-copied");
|
|
54679
|
+
btn.title = "Copiar nome do dispositivo";
|
|
54680
|
+
}, 1200);
|
|
54681
|
+
};
|
|
54682
|
+
if (navigator.clipboard?.writeText) navigator.clipboard.writeText(text2).then(done).catch(() => {
|
|
54683
|
+
});
|
|
54684
|
+
else {
|
|
54685
|
+
const ta = document.createElement("textarea");
|
|
54686
|
+
ta.value = text2;
|
|
54687
|
+
document.body.appendChild(ta);
|
|
54688
|
+
ta.select();
|
|
54689
|
+
try {
|
|
54690
|
+
document.execCommand("copy");
|
|
54691
|
+
done();
|
|
54692
|
+
} catch (_) {
|
|
54693
|
+
}
|
|
54694
|
+
ta.remove();
|
|
54695
|
+
}
|
|
54696
|
+
});
|
|
54141
54697
|
overlay.addEventListener("click", (e) => {
|
|
54142
54698
|
if (e.target === overlay) closeModal2();
|
|
54143
54699
|
});
|
|
@@ -54159,7 +54715,7 @@ function buildOccurrenceMatrix(alarm, devices) {
|
|
|
54159
54715
|
}
|
|
54160
54716
|
const deviceChip = (dev) => `<span class="adm-device-chip">
|
|
54161
54717
|
<svg viewBox="0 0 24 24" width="8" height="8" fill="currentColor"><circle cx="12" cy="12" r="10"/></svg>
|
|
54162
|
-
${escHtml2(dev)}
|
|
54718
|
+
${escHtml2(resolveDeviceLabel(alarm, dev) || dev)}
|
|
54163
54719
|
</span>`;
|
|
54164
54720
|
return show.map((item) => {
|
|
54165
54721
|
const devIndex = (item.n - 1) % devices.length;
|
|
@@ -56357,6 +56913,46 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
56357
56913
|
transform: translateY(0) scale(1);
|
|
56358
56914
|
}
|
|
56359
56915
|
|
|
56916
|
+
/* ---- Nota do gr\xE1fico (base real de ocorr\xEAncias) ---- */
|
|
56917
|
+
.adm-chart-note { font-size: 11px; color: #6b7280; margin: -2px 0 8px; }
|
|
56918
|
+
.adm-overlay[data-theme="dark"] .adm-chart-note { color: #94a3b8; }
|
|
56919
|
+
|
|
56920
|
+
/* ---- Device identity (label + name discreto + copiar) ---- */
|
|
56921
|
+
.adm-dev-id {
|
|
56922
|
+
display: inline-flex;
|
|
56923
|
+
align-items: baseline;
|
|
56924
|
+
gap: 6px;
|
|
56925
|
+
min-width: 0;
|
|
56926
|
+
flex-wrap: wrap;
|
|
56927
|
+
}
|
|
56928
|
+
.adm-dev-label { font-weight: 600; color: inherit; font-family: 'Nunito', system-ui, sans-serif; font-size: 13px; }
|
|
56929
|
+
.adm-dev-name {
|
|
56930
|
+
font-family: ui-monospace, 'SFMono-Regular', Consolas, monospace;
|
|
56931
|
+
font-size: 11px;
|
|
56932
|
+
color: #9ca3af;
|
|
56933
|
+
}
|
|
56934
|
+
.adm-copy-btn {
|
|
56935
|
+
display: inline-flex;
|
|
56936
|
+
align-items: center;
|
|
56937
|
+
justify-content: center;
|
|
56938
|
+
width: 20px;
|
|
56939
|
+
height: 20px;
|
|
56940
|
+
padding: 0;
|
|
56941
|
+
border: 1px solid #e5e7eb;
|
|
56942
|
+
border-radius: 4px;
|
|
56943
|
+
background: transparent;
|
|
56944
|
+
color: #9ca3af;
|
|
56945
|
+
cursor: pointer;
|
|
56946
|
+
align-self: center;
|
|
56947
|
+
transition: color 0.15s, border-color 0.15s, background 0.15s;
|
|
56948
|
+
}
|
|
56949
|
+
.adm-copy-btn:hover { color: var(--myio-brand-700, #7c3aed); border-color: var(--myio-brand-700, #7c3aed); }
|
|
56950
|
+
.adm-copy-btn.is-copied { color: #16a34a; border-color: #16a34a; background: #f0fdf4; }
|
|
56951
|
+
.adm-row-value--devices { display: flex; flex-direction: column; gap: 4px; }
|
|
56952
|
+
.adm-device-icon-sm svg { color: var(--myio-brand-700, #7c3aed); }
|
|
56953
|
+
.adm-overlay[data-theme="dark"] .adm-dev-name { color: #64748b; }
|
|
56954
|
+
.adm-overlay[data-theme="dark"] .adm-copy-btn { border-color: #2a2b3d; color: #64748b; }
|
|
56955
|
+
|
|
56360
56956
|
/* ---- Maximized state ---- */
|
|
56361
56957
|
.adm-drawer--maximized {
|
|
56362
56958
|
width: 100% !important;
|
|
@@ -56368,7 +56964,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
56368
56964
|
|
|
56369
56965
|
/* ---- Header ---- */
|
|
56370
56966
|
.adm-header {
|
|
56371
|
-
padding:
|
|
56967
|
+
padding: 12px 22px;
|
|
56372
56968
|
border-bottom: 1px solid #e5e7eb;
|
|
56373
56969
|
flex-shrink: 0;
|
|
56374
56970
|
}
|
|
@@ -56514,8 +57110,8 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
56514
57110
|
.adm-tab:hover { color: #111827; }
|
|
56515
57111
|
|
|
56516
57112
|
.adm-tab.is-active {
|
|
56517
|
-
color: #7c3aed;
|
|
56518
|
-
border-bottom-color: #7c3aed;
|
|
57113
|
+
color: var(--myio-brand-700, #7c3aed);
|
|
57114
|
+
border-bottom-color: var(--myio-brand-700, #7c3aed);
|
|
56519
57115
|
font-weight: 700;
|
|
56520
57116
|
}
|
|
56521
57117
|
|
|
@@ -56527,8 +57123,8 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
56527
57123
|
height: 18px;
|
|
56528
57124
|
padding: 0 4px;
|
|
56529
57125
|
border-radius: 9px;
|
|
56530
|
-
background: #
|
|
56531
|
-
color: #7c3aed;
|
|
57126
|
+
background: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 12%, #ffffff);
|
|
57127
|
+
color: var(--myio-brand-700, #7c3aed);
|
|
56532
57128
|
font-size: 10px;
|
|
56533
57129
|
font-weight: 700;
|
|
56534
57130
|
}
|
|
@@ -56629,9 +57225,9 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
56629
57225
|
padding: 3px 8px;
|
|
56630
57226
|
font-size: 10px;
|
|
56631
57227
|
font-weight: 500;
|
|
56632
|
-
background:
|
|
56633
|
-
color: #7c3aed;
|
|
56634
|
-
border: 1px solid
|
|
57228
|
+
background: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 12%, transparent);
|
|
57229
|
+
color: var(--myio-brand-700, #7c3aed);
|
|
57230
|
+
border: 1px solid color-mix(in srgb, var(--myio-brand-700, #7c3aed) 30%, transparent);
|
|
56635
57231
|
border-radius: 4px;
|
|
56636
57232
|
}
|
|
56637
57233
|
|
|
@@ -56676,7 +57272,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
56676
57272
|
}
|
|
56677
57273
|
|
|
56678
57274
|
.adm-tag-key {
|
|
56679
|
-
color: #7c3aed;
|
|
57275
|
+
color: var(--myio-brand-700, #7c3aed);
|
|
56680
57276
|
font-weight: 700;
|
|
56681
57277
|
}
|
|
56682
57278
|
|
|
@@ -56724,15 +57320,15 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
56724
57320
|
width: 12px;
|
|
56725
57321
|
height: 12px;
|
|
56726
57322
|
border-radius: 50%;
|
|
56727
|
-
background: #8b5cf6;
|
|
57323
|
+
background: var(--myio-brand-700, #8b5cf6);
|
|
56728
57324
|
border: 2px solid #fff;
|
|
56729
|
-
box-shadow: 0 0 0 2px #8b5cf6;
|
|
57325
|
+
box-shadow: 0 0 0 2px var(--myio-brand-700, #8b5cf6);
|
|
56730
57326
|
flex-shrink: 0;
|
|
56731
57327
|
}
|
|
56732
57328
|
|
|
56733
57329
|
.adm-timeline-item.is-last .adm-timeline-dot { background: #3b82f6; box-shadow: 0 0 0 2px #3b82f6; }
|
|
56734
57330
|
.adm-timeline-item.is-first .adm-timeline-dot { background: #ef4444; box-shadow: 0 0 0 2px #ef4444; }
|
|
56735
|
-
.adm-timeline-item.is-single .adm-timeline-dot { background: #7c3aed; box-shadow: 0 0 0 2px #7c3aed; }
|
|
57331
|
+
.adm-timeline-item.is-single .adm-timeline-dot { background: var(--myio-brand-700, #7c3aed); box-shadow: 0 0 0 2px var(--myio-brand-700, #7c3aed); }
|
|
56736
57332
|
|
|
56737
57333
|
.adm-timeline-num {
|
|
56738
57334
|
font-size: 11px;
|
|
@@ -56818,7 +57414,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
56818
57414
|
gap: 6px;
|
|
56819
57415
|
font-size: 10px;
|
|
56820
57416
|
font-weight: 700;
|
|
56821
|
-
color: #7c3aed;
|
|
57417
|
+
color: var(--myio-brand-700, #7c3aed);
|
|
56822
57418
|
margin-bottom: 6px;
|
|
56823
57419
|
padding: 3px 6px;
|
|
56824
57420
|
background: rgba(139, 92, 246, 0.08);
|
|
@@ -56829,7 +57425,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
56829
57425
|
font-size: 9px;
|
|
56830
57426
|
font-weight: 500;
|
|
56831
57427
|
background: rgba(139, 92, 246, 0.2);
|
|
56832
|
-
color: #7c3aed;
|
|
57428
|
+
color: var(--myio-brand-700, #7c3aed);
|
|
56833
57429
|
padding: 1px 5px;
|
|
56834
57430
|
border-radius: 8px;
|
|
56835
57431
|
}
|
|
@@ -56867,14 +57463,14 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
56867
57463
|
transition: border-color 0.15s, color 0.15s, background 0.15s;
|
|
56868
57464
|
}
|
|
56869
57465
|
.adm-tl-batch-toggle:hover {
|
|
56870
|
-
border-color: #7c3aed;
|
|
56871
|
-
color: #7c3aed;
|
|
57466
|
+
border-color: var(--myio-brand-700, #7c3aed);
|
|
57467
|
+
color: var(--myio-brand-700, #7c3aed);
|
|
56872
57468
|
background: rgba(124, 58, 237, 0.04);
|
|
56873
57469
|
}
|
|
56874
57470
|
.adm-tl-batch-toggle.is-active {
|
|
56875
|
-
border-color: #7c3aed;
|
|
57471
|
+
border-color: var(--myio-brand-700, #7c3aed);
|
|
56876
57472
|
background: rgba(124, 58, 237, 0.08);
|
|
56877
|
-
color: #7c3aed;
|
|
57473
|
+
color: var(--myio-brand-700, #7c3aed);
|
|
56878
57474
|
}
|
|
56879
57475
|
.adm-tl-batch-bar {
|
|
56880
57476
|
display: none;
|
|
@@ -56894,7 +57490,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
56894
57490
|
}
|
|
56895
57491
|
.adm-tl-sel-count {
|
|
56896
57492
|
font-size: 11px;
|
|
56897
|
-
color: #7c3aed;
|
|
57493
|
+
color: var(--myio-brand-700, #7c3aed);
|
|
56898
57494
|
font-weight: 600;
|
|
56899
57495
|
min-width: 86px;
|
|
56900
57496
|
}
|
|
@@ -56909,7 +57505,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
56909
57505
|
}
|
|
56910
57506
|
.adm-tl-exec-btn {
|
|
56911
57507
|
padding: 4px 12px;
|
|
56912
|
-
background: #7c3aed;
|
|
57508
|
+
background: var(--myio-brand-700, #7c3aed);
|
|
56913
57509
|
color: #fff;
|
|
56914
57510
|
border: none;
|
|
56915
57511
|
border-radius: 6px;
|
|
@@ -56919,7 +57515,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
56919
57515
|
transition: background 0.15s;
|
|
56920
57516
|
}
|
|
56921
57517
|
.adm-tl-exec-btn:disabled { background: #d1d5db; cursor: not-allowed; }
|
|
56922
|
-
.adm-tl-exec-btn:not(:disabled):hover { background: #6d28d9; }
|
|
57518
|
+
.adm-tl-exec-btn:not(:disabled):hover { background: var(--myio-brand-600, #6d28d9); }
|
|
56923
57519
|
.adm-tl-cancel-btn {
|
|
56924
57520
|
padding: 4px 10px;
|
|
56925
57521
|
background: transparent;
|
|
@@ -57005,7 +57601,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57005
57601
|
justify-content: center;
|
|
57006
57602
|
width: 22px;
|
|
57007
57603
|
height: 22px;
|
|
57008
|
-
background: #
|
|
57604
|
+
background: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 12%, #ffffff);
|
|
57009
57605
|
border-radius: 5px;
|
|
57010
57606
|
flex-shrink: 0;
|
|
57011
57607
|
}
|
|
@@ -57078,7 +57674,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57078
57674
|
|
|
57079
57675
|
.adm-matrix-n {
|
|
57080
57676
|
font-weight: 700;
|
|
57081
|
-
color: #7c3aed;
|
|
57677
|
+
color: var(--myio-brand-700, #7c3aed);
|
|
57082
57678
|
}
|
|
57083
57679
|
|
|
57084
57680
|
.adm-matrix-ts {
|
|
@@ -57106,8 +57702,8 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57106
57702
|
align-items: center;
|
|
57107
57703
|
gap: 4px;
|
|
57108
57704
|
padding: 2px 6px;
|
|
57109
|
-
background: #
|
|
57110
|
-
color: #6d28d9;
|
|
57705
|
+
background: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 12%, #ffffff);
|
|
57706
|
+
color: var(--myio-brand-600, #6d28d9);
|
|
57111
57707
|
border-radius: 4px;
|
|
57112
57708
|
font-size: 10px;
|
|
57113
57709
|
font-family: 'SF Mono', Monaco, monospace;
|
|
@@ -57159,8 +57755,8 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57159
57755
|
}
|
|
57160
57756
|
|
|
57161
57757
|
.adm-report-date-input:focus {
|
|
57162
|
-
border-color: #7c3aed;
|
|
57163
|
-
box-shadow: 0 0 0 3px
|
|
57758
|
+
border-color: var(--myio-brand-700, #7c3aed);
|
|
57759
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--myio-brand-700, #7c3aed) 12%, transparent);
|
|
57164
57760
|
}
|
|
57165
57761
|
|
|
57166
57762
|
.adm-report-emit-btn {
|
|
@@ -57168,7 +57764,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57168
57764
|
align-items: center;
|
|
57169
57765
|
gap: 6px;
|
|
57170
57766
|
padding: 8px 16px;
|
|
57171
|
-
background: #7c3aed;
|
|
57767
|
+
background: var(--myio-brand-700, #7c3aed);
|
|
57172
57768
|
color: #fff;
|
|
57173
57769
|
border: none;
|
|
57174
57770
|
border-radius: 8px;
|
|
@@ -57179,8 +57775,8 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57179
57775
|
align-self: flex-start;
|
|
57180
57776
|
}
|
|
57181
57777
|
|
|
57182
|
-
.adm-report-emit-btn:hover { background: #6d28d9; }
|
|
57183
|
-
.adm-report-emit-btn:active { background: #
|
|
57778
|
+
.adm-report-emit-btn:hover { background: var(--myio-brand-600, #6d28d9); }
|
|
57779
|
+
.adm-report-emit-btn:active { background: var(--myio-brand-600, #6d28d9); }
|
|
57184
57780
|
|
|
57185
57781
|
.adm-report-empty-hint {
|
|
57186
57782
|
font-size: 13px;
|
|
@@ -57190,7 +57786,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57190
57786
|
font-style: italic;
|
|
57191
57787
|
}
|
|
57192
57788
|
|
|
57193
|
-
.adm-report-empty-hint strong { color: #7c3aed; font-style: normal; }
|
|
57789
|
+
.adm-report-empty-hint strong { color: var(--myio-brand-700, #7c3aed); font-style: normal; }
|
|
57194
57790
|
|
|
57195
57791
|
.adm-rpt-table-wrap {
|
|
57196
57792
|
overflow-x: auto;
|
|
@@ -57229,7 +57825,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57229
57825
|
font-size: 11px;
|
|
57230
57826
|
}
|
|
57231
57827
|
|
|
57232
|
-
.adm-rpt-cell--num { text-align: right; font-weight: 700; color: #7c3aed; }
|
|
57828
|
+
.adm-rpt-cell--num { text-align: right; font-weight: 700; color: var(--myio-brand-700, #7c3aed); }
|
|
57233
57829
|
|
|
57234
57830
|
.adm-rpt-cell--dev {
|
|
57235
57831
|
font-family: inherit;
|
|
@@ -57244,7 +57840,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57244
57840
|
.adm-rpt-cell--total {
|
|
57245
57841
|
font-weight: 700;
|
|
57246
57842
|
color: #111827;
|
|
57247
|
-
background: #
|
|
57843
|
+
background: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 12%, #ffffff);
|
|
57248
57844
|
}
|
|
57249
57845
|
|
|
57250
57846
|
.adm-rpt-table tbody tr:nth-child(even) td { background: #fafafa; }
|
|
@@ -57256,7 +57852,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57256
57852
|
font-weight: 700;
|
|
57257
57853
|
text-transform: uppercase;
|
|
57258
57854
|
letter-spacing: 0.5px;
|
|
57259
|
-
color: #7c3aed;
|
|
57855
|
+
color: var(--myio-brand-700, #7c3aed);
|
|
57260
57856
|
margin: 12px 0 6px;
|
|
57261
57857
|
}
|
|
57262
57858
|
.adm-rpt-section-title:first-child { margin-top: 0; }
|
|
@@ -57289,7 +57885,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57289
57885
|
.adm-rpt-device-sep { color: #9ca3af; font-size: 11px; }
|
|
57290
57886
|
.adm-rpt-alarm-type {
|
|
57291
57887
|
font-size: 11px;
|
|
57292
|
-
color: #7c3aed;
|
|
57888
|
+
color: var(--myio-brand-700, #7c3aed);
|
|
57293
57889
|
font-weight: 600;
|
|
57294
57890
|
}
|
|
57295
57891
|
|
|
@@ -57370,7 +57966,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57370
57966
|
}
|
|
57371
57967
|
|
|
57372
57968
|
.adm-chart-btn.is-active {
|
|
57373
|
-
background: #7c3aed;
|
|
57969
|
+
background: var(--myio-brand-700, #7c3aed);
|
|
57374
57970
|
color: #fff;
|
|
57375
57971
|
}
|
|
57376
57972
|
|
|
@@ -57470,7 +58066,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57470
58066
|
|
|
57471
58067
|
.adm-dow-bar {
|
|
57472
58068
|
height: 100%;
|
|
57473
|
-
background: #7c3aed;
|
|
58069
|
+
background: var(--myio-brand-700, #7c3aed);
|
|
57474
58070
|
border-radius: 3px;
|
|
57475
58071
|
}
|
|
57476
58072
|
|
|
@@ -57502,7 +58098,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57502
58098
|
|
|
57503
58099
|
.adm-hod-bar {
|
|
57504
58100
|
width: 100%;
|
|
57505
|
-
background: #7c3aed;
|
|
58101
|
+
background: var(--myio-brand-700, #7c3aed);
|
|
57506
58102
|
border-radius: 2px 2px 0 0;
|
|
57507
58103
|
min-height: 4px;
|
|
57508
58104
|
}
|
|
@@ -57533,7 +58129,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57533
58129
|
align-items: center;
|
|
57534
58130
|
gap: 5px;
|
|
57535
58131
|
padding: 7px 14px;
|
|
57536
|
-
background: #7c3aed;
|
|
58132
|
+
background: var(--myio-brand-700, #7c3aed);
|
|
57537
58133
|
color: #fff;
|
|
57538
58134
|
border: none;
|
|
57539
58135
|
border-radius: 8px;
|
|
@@ -57542,8 +58138,8 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57542
58138
|
cursor: pointer;
|
|
57543
58139
|
transition: background 0.15s;
|
|
57544
58140
|
}
|
|
57545
|
-
.adm-annot-create-btn:hover { background: #6d28d9; }
|
|
57546
|
-
.adm-annot-create-btn:active { background: #
|
|
58141
|
+
.adm-annot-create-btn:hover { background: var(--myio-brand-600, #6d28d9); }
|
|
58142
|
+
.adm-annot-create-btn:active { background: var(--myio-brand-600, #6d28d9); }
|
|
57547
58143
|
|
|
57548
58144
|
.adm-annot-form-wrap { margin-bottom: 14px; }
|
|
57549
58145
|
|
|
@@ -57576,8 +58172,8 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57576
58172
|
transition: border-color 0.15s;
|
|
57577
58173
|
}
|
|
57578
58174
|
.adm-annot-textarea:focus {
|
|
57579
|
-
border-color: #7c3aed;
|
|
57580
|
-
box-shadow: 0 0 0 3px
|
|
58175
|
+
border-color: var(--myio-brand-700, #7c3aed);
|
|
58176
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--myio-brand-700, #7c3aed) 12%, transparent);
|
|
57581
58177
|
}
|
|
57582
58178
|
|
|
57583
58179
|
.adm-annot-char-count {
|
|
@@ -57664,7 +58260,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57664
58260
|
cursor: pointer;
|
|
57665
58261
|
transition: border-color 0.15s;
|
|
57666
58262
|
}
|
|
57667
|
-
.adm-annot-date-input:focus { border-color: #7c3aed; }
|
|
58263
|
+
.adm-annot-date-input:focus { border-color: var(--myio-brand-700, #7c3aed); }
|
|
57668
58264
|
|
|
57669
58265
|
/* Form action buttons */
|
|
57670
58266
|
.adm-annot-form-actions {
|
|
@@ -57685,8 +58281,8 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57685
58281
|
}
|
|
57686
58282
|
.adm-annot-form-btn--cancel { background: #e5e7eb; color: #374151; }
|
|
57687
58283
|
.adm-annot-form-btn--cancel:hover { background: #d1d5db; }
|
|
57688
|
-
.adm-annot-form-btn--save { background: #7c3aed; color: #fff; }
|
|
57689
|
-
.adm-annot-form-btn--save:hover { background: #6d28d9; }
|
|
58284
|
+
.adm-annot-form-btn--save { background: var(--myio-brand-700, #7c3aed); color: #fff; }
|
|
58285
|
+
.adm-annot-form-btn--save:hover { background: var(--myio-brand-600, #6d28d9); }
|
|
57690
58286
|
.adm-annot-form-btn--save:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
57691
58287
|
|
|
57692
58288
|
/* Empty state */
|
|
@@ -57786,8 +58382,8 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57786
58382
|
width: 20px;
|
|
57787
58383
|
height: 20px;
|
|
57788
58384
|
border-radius: 50%;
|
|
57789
|
-
background: #
|
|
57790
|
-
color: #7c3aed;
|
|
58385
|
+
background: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 12%, #ffffff);
|
|
58386
|
+
color: var(--myio-brand-700, #7c3aed);
|
|
57791
58387
|
font-size: 8px;
|
|
57792
58388
|
font-weight: 700;
|
|
57793
58389
|
flex-shrink: 0;
|
|
@@ -57890,13 +58486,13 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57890
58486
|
}
|
|
57891
58487
|
|
|
57892
58488
|
.adm-overlay[data-theme="dark"] .adm-tab.is-active {
|
|
57893
|
-
color: #
|
|
57894
|
-
border-bottom-color: #
|
|
58489
|
+
color: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 50%, #ffffff);
|
|
58490
|
+
border-bottom-color: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 50%, #ffffff);
|
|
57895
58491
|
}
|
|
57896
58492
|
|
|
57897
58493
|
.adm-overlay[data-theme="dark"] .adm-tab-badge {
|
|
57898
|
-
background: #
|
|
57899
|
-
color: #
|
|
58494
|
+
background: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 35%, #1e1f2e);
|
|
58495
|
+
color: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 50%, #ffffff);
|
|
57900
58496
|
}
|
|
57901
58497
|
|
|
57902
58498
|
.adm-overlay[data-theme="dark"] .adm-body {
|
|
@@ -57968,13 +58564,13 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57968
58564
|
}
|
|
57969
58565
|
|
|
57970
58566
|
.adm-overlay[data-theme="dark"] .adm-timeline-group-header {
|
|
57971
|
-
color: #
|
|
57972
|
-
background:
|
|
58567
|
+
color: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 50%, #ffffff);
|
|
58568
|
+
background: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 12%, transparent);
|
|
57973
58569
|
}
|
|
57974
58570
|
|
|
57975
58571
|
.adm-overlay[data-theme="dark"] .adm-timeline-group-count {
|
|
57976
|
-
background:
|
|
57977
|
-
color: #
|
|
58572
|
+
background: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 25%, transparent);
|
|
58573
|
+
color: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 50%, #ffffff);
|
|
57978
58574
|
}
|
|
57979
58575
|
|
|
57980
58576
|
.adm-overlay[data-theme="dark"] .adm-tl-header { border-color: #334155; }
|
|
@@ -57982,10 +58578,10 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
57982
58578
|
.adm-overlay[data-theme="dark"] .adm-tl-summary { color: #94a3b8; }
|
|
57983
58579
|
.adm-overlay[data-theme="dark"] .adm-tl-batch-toggle { border-color: #475569; background: #1e293b; color: #e2e8f0; }
|
|
57984
58580
|
.adm-overlay[data-theme="dark"] .adm-tl-batch-toggle:hover,
|
|
57985
|
-
.adm-overlay[data-theme="dark"] .adm-tl-batch-toggle.is-active { border-color: #
|
|
58581
|
+
.adm-overlay[data-theme="dark"] .adm-tl-batch-toggle.is-active { border-color: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 50%, #ffffff); color: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 50%, #ffffff); }
|
|
57986
58582
|
.adm-overlay[data-theme="dark"] .adm-tl-batch-bar { border-color: #334155; }
|
|
57987
58583
|
.adm-overlay[data-theme="dark"] .adm-tl-sel-all-label { color: #e2e8f0; }
|
|
57988
|
-
.adm-overlay[data-theme="dark"] .adm-tl-sel-count { color: #
|
|
58584
|
+
.adm-overlay[data-theme="dark"] .adm-tl-sel-count { color: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 50%, #ffffff); }
|
|
57989
58585
|
.adm-overlay[data-theme="dark"] .adm-tl-action-sel { background: #1e293b; border-color: #475569; color: #e2e8f0; }
|
|
57990
58586
|
.adm-overlay[data-theme="dark"] .adm-tl-cancel-btn { border-color: #475569; color: #94a3b8; }
|
|
57991
58587
|
.adm-overlay[data-theme="dark"] .adm-tl-cancel-btn:hover { color: #e2e8f0; }
|
|
@@ -58016,9 +58612,9 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
58016
58612
|
}
|
|
58017
58613
|
|
|
58018
58614
|
.adm-overlay[data-theme="dark"] .adm-chart-btn.is-active {
|
|
58019
|
-
background: #
|
|
58020
|
-
border-color: #6d28d9;
|
|
58021
|
-
color: #
|
|
58615
|
+
background: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 35%, #1e1f2e);
|
|
58616
|
+
border-color: var(--myio-brand-600, #6d28d9);
|
|
58617
|
+
color: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 50%, #ffffff);
|
|
58022
58618
|
}
|
|
58023
58619
|
|
|
58024
58620
|
.adm-overlay[data-theme="dark"] .adm-chart-btn:hover:not(.is-active) {
|
|
@@ -58082,13 +58678,13 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
|
|
|
58082
58678
|
background: #252636;
|
|
58083
58679
|
}
|
|
58084
58680
|
|
|
58085
|
-
.adm-overlay[data-theme="dark"] .adm-rpt-section-title { color: #
|
|
58681
|
+
.adm-overlay[data-theme="dark"] .adm-rpt-section-title { color: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 50%, #ffffff); }
|
|
58086
58682
|
.adm-overlay[data-theme="dark"] .adm-rpt-device-header {
|
|
58087
58683
|
background: rgba(139, 92, 246, 0.1);
|
|
58088
|
-
border-color:
|
|
58684
|
+
border-color: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 25%, transparent);
|
|
58089
58685
|
}
|
|
58090
58686
|
.adm-overlay[data-theme="dark"] .adm-rpt-device-name { color: #e2e8f0; }
|
|
58091
|
-
.adm-overlay[data-theme="dark"] .adm-rpt-alarm-type { color: #
|
|
58687
|
+
.adm-overlay[data-theme="dark"] .adm-rpt-alarm-type { color: color-mix(in srgb, var(--myio-brand-700, #7c3aed) 50%, #ffffff); }
|
|
58092
58688
|
.adm-overlay[data-theme="dark"] .adm-rpt-cell--total { background: rgba(139, 92, 246, 0.15); color: #e2e8f0; }
|
|
58093
58689
|
|
|
58094
58690
|
.adm-overlay[data-theme="dark"] .adm-annot-card {
|
|
@@ -71146,6 +71742,25 @@ var HeaderView = class {
|
|
|
71146
71742
|
transition: color 0.2s ease;
|
|
71147
71743
|
}
|
|
71148
71744
|
|
|
71745
|
+
/* Consumo ainda carregando */
|
|
71746
|
+
.${HEADER_CSS_PREFIX}-kpi-loading {
|
|
71747
|
+
display: inline-flex;
|
|
71748
|
+
align-items: center;
|
|
71749
|
+
gap: 10px;
|
|
71750
|
+
font-size: 1rem;
|
|
71751
|
+
font-weight: 600;
|
|
71752
|
+
opacity: 0.85;
|
|
71753
|
+
}
|
|
71754
|
+
.${HEADER_CSS_PREFIX}-kpi-spinner {
|
|
71755
|
+
width: 18px;
|
|
71756
|
+
height: 18px;
|
|
71757
|
+
border: 2.5px solid rgba(255, 255, 255, 0.3);
|
|
71758
|
+
border-top-color: currentColor;
|
|
71759
|
+
border-radius: 50%;
|
|
71760
|
+
animation: myio-consumption-spin 0.8s linear infinite;
|
|
71761
|
+
}
|
|
71762
|
+
@keyframes myio-consumption-spin { to { transform: rotate(360deg); } }
|
|
71763
|
+
|
|
71149
71764
|
.${HEADER_CSS_PREFIX}-card__subrow {
|
|
71150
71765
|
display: flex;
|
|
71151
71766
|
align-items: center;
|
|
@@ -71584,7 +72199,28 @@ var HeaderView = class {
|
|
|
71584
72199
|
/**
|
|
71585
72200
|
* Update energy card
|
|
71586
72201
|
*/
|
|
72202
|
+
/** Spinner nos KPIs de consumo (energia e água) enquanto o período carrega */
|
|
72203
|
+
setConsumptionLoading(loading) {
|
|
72204
|
+
if (!loading) {
|
|
72205
|
+
[this.energyKpiEl, this.waterKpiEl].forEach((el2) => {
|
|
72206
|
+
if (el2?.querySelector(`.${HEADER_CSS_PREFIX}-kpi-loading`)) el2.textContent = "-";
|
|
72207
|
+
});
|
|
72208
|
+
return;
|
|
72209
|
+
}
|
|
72210
|
+
const html = `<span class="${HEADER_CSS_PREFIX}-kpi-loading" aria-busy="true"><span class="${HEADER_CSS_PREFIX}-kpi-spinner" aria-hidden="true"></span>Carregando\u2026</span>`;
|
|
72211
|
+
if (this.energyKpiEl) this.energyKpiEl.innerHTML = html;
|
|
72212
|
+
if (this.waterKpiEl) this.waterKpiEl.innerHTML = html;
|
|
72213
|
+
if (this.energyTrendEl) this.energyTrendEl.textContent = "";
|
|
72214
|
+
if (this.waterTrendEl) this.waterTrendEl.textContent = "";
|
|
72215
|
+
}
|
|
71587
72216
|
updateEnergyCard(data) {
|
|
72217
|
+
if (data.loading) {
|
|
72218
|
+
if (this.energyKpiEl) {
|
|
72219
|
+
this.energyKpiEl.innerHTML = `<span class="${HEADER_CSS_PREFIX}-kpi-loading" aria-busy="true"><span class="${HEADER_CSS_PREFIX}-kpi-spinner" aria-hidden="true"></span>Carregando\u2026</span>`;
|
|
72220
|
+
}
|
|
72221
|
+
if (this.energyTrendEl) this.energyTrendEl.textContent = "";
|
|
72222
|
+
return;
|
|
72223
|
+
}
|
|
71588
72224
|
if (this.energyKpiEl) {
|
|
71589
72225
|
if (data.isFiltered && data.unfilteredTotal > 0) {
|
|
71590
72226
|
const filteredFormatted = this.formatEnergy(data.customerTotal);
|
|
@@ -71633,6 +72269,13 @@ var HeaderView = class {
|
|
|
71633
72269
|
* Update water card
|
|
71634
72270
|
*/
|
|
71635
72271
|
updateWaterCard(data) {
|
|
72272
|
+
if (data.loading) {
|
|
72273
|
+
if (this.waterKpiEl) {
|
|
72274
|
+
this.waterKpiEl.innerHTML = `<span class="${HEADER_CSS_PREFIX}-kpi-loading" aria-busy="true"><span class="${HEADER_CSS_PREFIX}-kpi-spinner" aria-hidden="true"></span>Carregando\u2026</span>`;
|
|
72275
|
+
}
|
|
72276
|
+
if (this.waterTrendEl) this.waterTrendEl.textContent = "";
|
|
72277
|
+
return;
|
|
72278
|
+
}
|
|
71636
72279
|
if (this.waterKpiEl) {
|
|
71637
72280
|
if (data.isFiltered && data.unfilteredTotal > 0) {
|
|
71638
72281
|
const filteredFormatted = this.formatWater(data.filteredTotal);
|
|
@@ -72618,6 +73261,9 @@ function createHeaderComponent(params) {
|
|
|
72618
73261
|
tooltipData.temperature = data;
|
|
72619
73262
|
view.updateTemperatureCard(data);
|
|
72620
73263
|
}
|
|
73264
|
+
function setConsumptionLoading(loading) {
|
|
73265
|
+
view.setConsumptionLoading(loading);
|
|
73266
|
+
}
|
|
72621
73267
|
function updateWaterKPI(data) {
|
|
72622
73268
|
tooltipData.water = data;
|
|
72623
73269
|
view.updateWaterCard(data);
|
|
@@ -72673,6 +73319,7 @@ function createHeaderComponent(params) {
|
|
|
72673
73319
|
updateEnergyKPI,
|
|
72674
73320
|
updateTemperatureKPI,
|
|
72675
73321
|
updateWaterKPI,
|
|
73322
|
+
setConsumptionLoading,
|
|
72676
73323
|
updateTooltipData,
|
|
72677
73324
|
// Shopping/Filter
|
|
72678
73325
|
updateShoppings,
|
|
@@ -123159,6 +123806,20 @@ var HEADER_DEVICES_GRID_STYLES = `
|
|
|
123159
123806
|
body.light-mode .telemetry-grid-wrap.maximized .telemetry-grid-header {
|
|
123160
123807
|
background: #f8fafc;
|
|
123161
123808
|
}
|
|
123809
|
+
|
|
123810
|
+
/* Consumo ainda carregando */
|
|
123811
|
+
.hdg-value-spinner {
|
|
123812
|
+
display: inline-block;
|
|
123813
|
+
width: 13px;
|
|
123814
|
+
height: 13px;
|
|
123815
|
+
margin-right: 6px;
|
|
123816
|
+
vertical-align: -2px;
|
|
123817
|
+
border: 2px solid rgba(124, 58, 237, 0.2);
|
|
123818
|
+
border-top-color: var(--myio-brand-700, #7c3aed);
|
|
123819
|
+
border-radius: 50%;
|
|
123820
|
+
animation: myio-consumption-spin 0.8s linear infinite;
|
|
123821
|
+
}
|
|
123822
|
+
@keyframes myio-consumption-spin { to { transform: rotate(360deg); } }
|
|
123162
123823
|
`;
|
|
123163
123824
|
function injectHeaderDevicesGridStyles() {
|
|
123164
123825
|
const styleId = "hdg-styles";
|
|
@@ -123176,14 +123837,15 @@ var DOMAIN_CONFIG7 = {
|
|
|
123176
123837
|
consumptionLabel: "Consumo Total",
|
|
123177
123838
|
zeroLabel: "Sem Consumo",
|
|
123178
123839
|
unit: "kWh",
|
|
123179
|
-
|
|
123840
|
+
// 3 casas: mesmo padrão do dashboard de shopping (RFC-0108, measurementSettings default)
|
|
123841
|
+
formatValue: (value) => formatEnergy(value, void 0, 3)
|
|
123180
123842
|
},
|
|
123181
123843
|
water: {
|
|
123182
123844
|
totalLabel: "Total Hidr\xF4metros",
|
|
123183
123845
|
consumptionLabel: "Consumo Total",
|
|
123184
123846
|
zeroLabel: "Sem Consumo",
|
|
123185
123847
|
unit: "m\xB3",
|
|
123186
|
-
formatValue: (value) =>
|
|
123848
|
+
formatValue: (value) => `${(Number(value) || 0).toLocaleString("pt-BR", { minimumFractionDigits: 3, maximumFractionDigits: 3 })} m\xB3`
|
|
123187
123849
|
},
|
|
123188
123850
|
temperature: {
|
|
123189
123851
|
totalLabel: "Total Sensores",
|
|
@@ -123351,6 +124013,16 @@ var HeaderDevicesGridView = class {
|
|
|
123351
124013
|
const percentage = total > 0 ? (online / total * 100).toFixed(1) : "0.0";
|
|
123352
124014
|
connectivityEl.textContent = `${online}/${total} (${percentage}%)`;
|
|
123353
124015
|
totalEl.textContent = total.toString();
|
|
124016
|
+
if (stats.loading) {
|
|
124017
|
+
const spin = '<span class="hdg-value-spinner" aria-hidden="true"></span>';
|
|
124018
|
+
consumptionEl.innerHTML = `${spin}Carregando\u2026`;
|
|
124019
|
+
zeroEl.innerHTML = spin;
|
|
124020
|
+
consumptionEl.setAttribute("aria-busy", "true");
|
|
124021
|
+
zeroEl.setAttribute("aria-busy", "true");
|
|
124022
|
+
return;
|
|
124023
|
+
}
|
|
124024
|
+
consumptionEl.removeAttribute("aria-busy");
|
|
124025
|
+
zeroEl.removeAttribute("aria-busy");
|
|
123354
124026
|
consumptionEl.textContent = this.domainConfig.formatValue(consumption);
|
|
123355
124027
|
zeroEl.textContent = zeroCount.toString();
|
|
123356
124028
|
}
|
|
@@ -123457,7 +124129,8 @@ var HeaderDevicesGridController = class {
|
|
|
123457
124129
|
online,
|
|
123458
124130
|
total: devices.length,
|
|
123459
124131
|
consumption: displayValue,
|
|
123460
|
-
zeroCount
|
|
124132
|
+
zeroCount,
|
|
124133
|
+
loading: this.domain !== "temperature" && devices.some((d) => d.dataLoading)
|
|
123461
124134
|
});
|
|
123462
124135
|
}
|
|
123463
124136
|
setThemeMode(mode) {
|
|
@@ -142202,425 +142875,6 @@ function createDeviceOperationalCardGridComponent(params) {
|
|
|
142202
142875
|
};
|
|
142203
142876
|
}
|
|
142204
142877
|
|
|
142205
|
-
// src/services/alarm/AlarmApiClient.ts
|
|
142206
|
-
var AlarmApiClient = class {
|
|
142207
|
-
baseUrl = "";
|
|
142208
|
-
apiKey = "";
|
|
142209
|
-
configure(baseUrl, apiKey) {
|
|
142210
|
-
if (baseUrl) this.baseUrl = baseUrl;
|
|
142211
|
-
if (apiKey) this.apiKey = apiKey;
|
|
142212
|
-
}
|
|
142213
|
-
get headers() {
|
|
142214
|
-
return {
|
|
142215
|
-
"Content-Type": "application/json",
|
|
142216
|
-
"X-API-Key": this.apiKey
|
|
142217
|
-
};
|
|
142218
|
-
}
|
|
142219
|
-
async request(url, options) {
|
|
142220
|
-
const response = await fetch(url, {
|
|
142221
|
-
method: "GET",
|
|
142222
|
-
headers: this.headers,
|
|
142223
|
-
...options
|
|
142224
|
-
});
|
|
142225
|
-
if (!response.ok) {
|
|
142226
|
-
throw new Error(`Alarms API error: ${response.status} ${response.statusText}`);
|
|
142227
|
-
}
|
|
142228
|
-
return response.json();
|
|
142229
|
-
}
|
|
142230
|
-
// Some endpoints return { success: true, data: T[] } — unwrap safely
|
|
142231
|
-
unwrapArray(raw) {
|
|
142232
|
-
if (Array.isArray(raw)) return raw;
|
|
142233
|
-
if (raw !== null && typeof raw === "object" && "data" in raw) {
|
|
142234
|
-
const d = raw.data;
|
|
142235
|
-
return Array.isArray(d) ? d : [];
|
|
142236
|
-
}
|
|
142237
|
-
return [];
|
|
142238
|
-
}
|
|
142239
|
-
// -----------------------------------------------------------------------
|
|
142240
|
-
// Availability (existing endpoint)
|
|
142241
|
-
// -----------------------------------------------------------------------
|
|
142242
|
-
async getAvailability(params) {
|
|
142243
|
-
const query = new URLSearchParams({
|
|
142244
|
-
customerId: params.customerId,
|
|
142245
|
-
startAt: params.startAt,
|
|
142246
|
-
endAt: params.endAt,
|
|
142247
|
-
includeByDevice: String(params.includeByDevice ?? true)
|
|
142248
|
-
});
|
|
142249
|
-
if (params.deviceType) query.set("deviceType", params.deviceType);
|
|
142250
|
-
if (params.deviceIds) query.set("deviceIds", params.deviceIds);
|
|
142251
|
-
const raw = await this.request(
|
|
142252
|
-
`${this.baseUrl}/alarms/stats/availability?${query.toString()}`
|
|
142253
|
-
);
|
|
142254
|
-
return raw?.data ?? raw;
|
|
142255
|
-
}
|
|
142256
|
-
// -----------------------------------------------------------------------
|
|
142257
|
-
// Alarm list (GET /alarms)
|
|
142258
|
-
// -----------------------------------------------------------------------
|
|
142259
|
-
async getAlarms(params = {}) {
|
|
142260
|
-
const query = new URLSearchParams();
|
|
142261
|
-
if (params.state?.length) query.set("state", params.state.join(","));
|
|
142262
|
-
if (params.severity?.length) query.set("severity", params.severity.join(","));
|
|
142263
|
-
if (params.alarmType) query.set("alarmType", params.alarmType);
|
|
142264
|
-
if (params.from) query.set("from", params.from);
|
|
142265
|
-
if (params.to) query.set("to", params.to);
|
|
142266
|
-
if (params.customerId) query.set("customerId", params.customerId);
|
|
142267
|
-
query.set("limit", String(params.limit ?? 100));
|
|
142268
|
-
if (params.page && params.page > 1) query.set("page", String(params.page));
|
|
142269
|
-
const qs = query.toString();
|
|
142270
|
-
return this.request(
|
|
142271
|
-
`${this.baseUrl}/alarms${qs ? `?${qs}` : ""}`
|
|
142272
|
-
);
|
|
142273
|
-
}
|
|
142274
|
-
// -----------------------------------------------------------------------
|
|
142275
|
-
// Stats (GET /api/v1/alarms/stats)
|
|
142276
|
-
// -----------------------------------------------------------------------
|
|
142277
|
-
async getAlarmStats(tenantId, period) {
|
|
142278
|
-
const query = new URLSearchParams({ tenantId, period });
|
|
142279
|
-
const raw = await this.request(
|
|
142280
|
-
`${this.baseUrl}/alarms/stats?${query.toString()}`
|
|
142281
|
-
);
|
|
142282
|
-
return raw?.data ?? raw;
|
|
142283
|
-
}
|
|
142284
|
-
// -----------------------------------------------------------------------
|
|
142285
|
-
// Trend (GET /api/v1/alarms/stats/trend)
|
|
142286
|
-
// -----------------------------------------------------------------------
|
|
142287
|
-
async getAlarmTrend(tenantId, period, groupBy) {
|
|
142288
|
-
const query = new URLSearchParams({ tenantId, period, groupBy });
|
|
142289
|
-
const raw = await this.request(
|
|
142290
|
-
`${this.baseUrl}/alarms/stats/trend?${query.toString()}`
|
|
142291
|
-
);
|
|
142292
|
-
return this.unwrapArray(raw);
|
|
142293
|
-
}
|
|
142294
|
-
// -----------------------------------------------------------------------
|
|
142295
|
-
// Top offenders (GET /api/v1/alarms/stats/top-offenders)
|
|
142296
|
-
// -----------------------------------------------------------------------
|
|
142297
|
-
async getTopOffenders(tenantId, limit = 5) {
|
|
142298
|
-
const query = new URLSearchParams({ tenantId, limit: String(limit) });
|
|
142299
|
-
const raw = await this.request(
|
|
142300
|
-
`${this.baseUrl}/alarms/stats/top-offenders?${query.toString()}`
|
|
142301
|
-
);
|
|
142302
|
-
return this.unwrapArray(raw);
|
|
142303
|
-
}
|
|
142304
|
-
// -----------------------------------------------------------------------
|
|
142305
|
-
// Per-device stats (GET /api/v1/alarms/stats/by-device)
|
|
142306
|
-
// -----------------------------------------------------------------------
|
|
142307
|
-
async getAlarmStatsByDevice(tenantId) {
|
|
142308
|
-
const query = new URLSearchParams({ tenantId });
|
|
142309
|
-
const raw = await this.request(
|
|
142310
|
-
`${this.baseUrl}/alarms/stats/by-device?${query.toString()}`
|
|
142311
|
-
);
|
|
142312
|
-
return this.unwrapArray(raw);
|
|
142313
|
-
}
|
|
142314
|
-
// -----------------------------------------------------------------------
|
|
142315
|
-
// Alarm actions (POST /alarms/:id/...)
|
|
142316
|
-
// -----------------------------------------------------------------------
|
|
142317
|
-
async acknowledgeAlarm(id, acknowledgedBy, note) {
|
|
142318
|
-
await this.request(`${this.baseUrl}/alarms/${id}/ack`, {
|
|
142319
|
-
method: "POST",
|
|
142320
|
-
body: JSON.stringify({ acknowledgedBy, note })
|
|
142321
|
-
});
|
|
142322
|
-
}
|
|
142323
|
-
async silenceAlarm(id, silencedBy, duration, reason) {
|
|
142324
|
-
await this.request(`${this.baseUrl}/alarms/${id}/silence`, {
|
|
142325
|
-
method: "POST",
|
|
142326
|
-
body: JSON.stringify({ silencedBy, duration, reason })
|
|
142327
|
-
});
|
|
142328
|
-
}
|
|
142329
|
-
async escalateAlarm(id, escalatedBy, reason) {
|
|
142330
|
-
await this.request(`${this.baseUrl}/alarms/${id}/escalate`, {
|
|
142331
|
-
method: "POST",
|
|
142332
|
-
body: JSON.stringify({ escalatedBy, reason })
|
|
142333
|
-
});
|
|
142334
|
-
}
|
|
142335
|
-
async closeAlarm(id, closedBy, resolution) {
|
|
142336
|
-
await this.request(`${this.baseUrl}/alarms/${id}/close`, {
|
|
142337
|
-
method: "POST",
|
|
142338
|
-
body: JSON.stringify({ closedBy, resolution })
|
|
142339
|
-
});
|
|
142340
|
-
}
|
|
142341
|
-
// -----------------------------------------------------------------------
|
|
142342
|
-
// Batch alarm actions (POST /alarms/batch/*)
|
|
142343
|
-
// Up to 100 IDs per call; response contains succeeded/failed arrays.
|
|
142344
|
-
// -----------------------------------------------------------------------
|
|
142345
|
-
async batchAcknowledge(alarmIds, acknowledgedBy) {
|
|
142346
|
-
const res = await this.request(
|
|
142347
|
-
`${this.baseUrl}/alarms/batch/ack`,
|
|
142348
|
-
{ method: "POST", body: JSON.stringify({ alarmIds, acknowledgedBy }) }
|
|
142349
|
-
);
|
|
142350
|
-
return res.data;
|
|
142351
|
-
}
|
|
142352
|
-
async batchSilence(alarmIds, silencedBy, duration) {
|
|
142353
|
-
const res = await this.request(
|
|
142354
|
-
`${this.baseUrl}/alarms/batch/silence`,
|
|
142355
|
-
{ method: "POST", body: JSON.stringify({ alarmIds, silencedBy, duration }) }
|
|
142356
|
-
);
|
|
142357
|
-
return res.data;
|
|
142358
|
-
}
|
|
142359
|
-
async batchEscalate(alarmIds, escalatedBy) {
|
|
142360
|
-
const res = await this.request(
|
|
142361
|
-
`${this.baseUrl}/alarms/batch/escalate`,
|
|
142362
|
-
{ method: "POST", body: JSON.stringify({ alarmIds, escalatedBy }) }
|
|
142363
|
-
);
|
|
142364
|
-
return res.data;
|
|
142365
|
-
}
|
|
142366
|
-
async batchClose(alarmIds, closedBy, resolution) {
|
|
142367
|
-
const res = await this.request(
|
|
142368
|
-
`${this.baseUrl}/alarms/batch/close`,
|
|
142369
|
-
{ method: "POST", body: JSON.stringify({ alarmIds, closedBy, resolution }) }
|
|
142370
|
-
);
|
|
142371
|
-
return res.data;
|
|
142372
|
-
}
|
|
142373
|
-
};
|
|
142374
|
-
|
|
142375
|
-
// src/services/alarm/AlarmService.ts
|
|
142376
|
-
function isFresh(entry, ttlMs) {
|
|
142377
|
-
return !!entry && Date.now() - entry.timestamp < ttlMs;
|
|
142378
|
-
}
|
|
142379
|
-
function mapApiAlarm(api, customerMap) {
|
|
142380
|
-
return {
|
|
142381
|
-
id: api.id,
|
|
142382
|
-
customerId: api.customerId ?? api.tenantId,
|
|
142383
|
-
customerName: customerMap?.get(api.customerId ?? "") ?? "",
|
|
142384
|
-
source: api.deviceName || api.deviceId,
|
|
142385
|
-
severity: api.severity,
|
|
142386
|
-
state: api.state,
|
|
142387
|
-
title: api.title,
|
|
142388
|
-
description: api.description ?? "",
|
|
142389
|
-
tags: {
|
|
142390
|
-
alarmType: api.alarmType,
|
|
142391
|
-
deviceType: api.deviceType
|
|
142392
|
-
},
|
|
142393
|
-
firstOccurrence: api.raisedAt || api.raised_at || api.createdAt || void 0,
|
|
142394
|
-
lastOccurrence: api.lastUpdatedAt || api.updatedAt || api.updated_at || void 0,
|
|
142395
|
-
occurrenceCount: api.occurrenceCount ?? api.dispatchCount ?? 1,
|
|
142396
|
-
acknowledgedAt: api.acknowledgedAt || void 0,
|
|
142397
|
-
acknowledgedBy: api.acknowledgedBy,
|
|
142398
|
-
snoozedUntil: api.silencedUntil || api.snoozedUntil || void 0,
|
|
142399
|
-
closedAt: api.closedAt || void 0,
|
|
142400
|
-
closedBy: api.closedBy,
|
|
142401
|
-
closedReason: api.resolution,
|
|
142402
|
-
triggerValue: api.metadata?.value,
|
|
142403
|
-
centralId: api.centralId || void 0
|
|
142404
|
-
};
|
|
142405
|
-
}
|
|
142406
|
-
function mapApiStats(api) {
|
|
142407
|
-
return {
|
|
142408
|
-
total: api.total,
|
|
142409
|
-
bySeverity: {
|
|
142410
|
-
CRITICAL: api.bySeverity?.CRITICAL ?? 0,
|
|
142411
|
-
HIGH: api.bySeverity?.HIGH ?? 0,
|
|
142412
|
-
MEDIUM: api.bySeverity?.MEDIUM ?? 0,
|
|
142413
|
-
LOW: api.bySeverity?.LOW ?? 0,
|
|
142414
|
-
INFO: api.bySeverity?.INFO ?? 0
|
|
142415
|
-
},
|
|
142416
|
-
byState: {
|
|
142417
|
-
OPEN: api.byState?.OPEN ?? 0,
|
|
142418
|
-
ACK: api.byState?.ACK ?? 0,
|
|
142419
|
-
SNOOZED: api.byState?.SNOOZED ?? 0,
|
|
142420
|
-
ESCALATED: api.byState?.ESCALATED ?? 0,
|
|
142421
|
-
CLOSED: api.byState?.CLOSED ?? 0
|
|
142422
|
-
},
|
|
142423
|
-
openCritical: api.openCritical,
|
|
142424
|
-
openHigh: api.openHigh,
|
|
142425
|
-
last24Hours: api.last24Hours
|
|
142426
|
-
};
|
|
142427
|
-
}
|
|
142428
|
-
function mapApiTrend(apiTrend) {
|
|
142429
|
-
return (apiTrend || []).map((point) => ({
|
|
142430
|
-
label: point.label || point.period,
|
|
142431
|
-
timestamp: point.timestamp || new Date(point.period).getTime() || 0,
|
|
142432
|
-
total: point.total ?? point.count,
|
|
142433
|
-
bySeverity: point.bySeverity ? {
|
|
142434
|
-
CRITICAL: point.bySeverity.CRITICAL ?? 0,
|
|
142435
|
-
HIGH: point.bySeverity.HIGH ?? 0,
|
|
142436
|
-
MEDIUM: point.bySeverity.MEDIUM ?? 0,
|
|
142437
|
-
LOW: point.bySeverity.LOW ?? 0,
|
|
142438
|
-
INFO: point.bySeverity.INFO ?? 0
|
|
142439
|
-
} : void 0
|
|
142440
|
-
}));
|
|
142441
|
-
}
|
|
142442
|
-
function mapApiTopOffenders(items, deviceNameMap) {
|
|
142443
|
-
return (items || []).map((item) => ({
|
|
142444
|
-
name: deviceNameMap.get(item.deviceId) ?? item.deviceName ?? item.deviceId,
|
|
142445
|
-
location: item.customerName ?? "",
|
|
142446
|
-
downtime: item.downtimeHours ?? (item.totalDowntimeMinutes ?? 0) / 60,
|
|
142447
|
-
percentage: 0
|
|
142448
|
-
// computed by caller if needed
|
|
142449
|
-
}));
|
|
142450
|
-
}
|
|
142451
|
-
var AlarmServiceClass = class {
|
|
142452
|
-
client = new AlarmApiClient();
|
|
142453
|
-
cacheTtlMs = 3 * 60 * 1e3;
|
|
142454
|
-
// 3 min default; overridable via configure()
|
|
142455
|
-
availabilityCache = /* @__PURE__ */ new Map();
|
|
142456
|
-
alarmsCache = /* @__PURE__ */ new Map();
|
|
142457
|
-
statsCache = /* @__PURE__ */ new Map();
|
|
142458
|
-
trendCache = /* @__PURE__ */ new Map();
|
|
142459
|
-
// -----------------------------------------------------------------------
|
|
142460
|
-
// Availability (MTBF / MTTR per device)
|
|
142461
|
-
// -----------------------------------------------------------------------
|
|
142462
|
-
/**
|
|
142463
|
-
* Fetches MTBF, MTTR, and Availability metrics per device and fleet-wide.
|
|
142464
|
-
* Results are cached for 60 seconds.
|
|
142465
|
-
*/
|
|
142466
|
-
async getAvailability(customerId, startAt, endAt) {
|
|
142467
|
-
if (!customerId) {
|
|
142468
|
-
throw new Error("AlarmService.getAvailability: customerId is required");
|
|
142469
|
-
}
|
|
142470
|
-
const cacheKey = `avail:${customerId}:${startAt}:${endAt}`;
|
|
142471
|
-
const cached = this.availabilityCache.get(cacheKey);
|
|
142472
|
-
if (isFresh(cached, this.cacheTtlMs)) return cached.data;
|
|
142473
|
-
const data = await this.client.getAvailability({
|
|
142474
|
-
customerId,
|
|
142475
|
-
startAt,
|
|
142476
|
-
endAt,
|
|
142477
|
-
includeByDevice: true
|
|
142478
|
-
});
|
|
142479
|
-
this.availabilityCache.set(cacheKey, { data, timestamp: Date.now() });
|
|
142480
|
-
return data;
|
|
142481
|
-
}
|
|
142482
|
-
// -----------------------------------------------------------------------
|
|
142483
|
-
// Alarm list
|
|
142484
|
-
// -----------------------------------------------------------------------
|
|
142485
|
-
/**
|
|
142486
|
-
* Fetch alarm list with optional filters, auto-paginating until hasMore is false.
|
|
142487
|
-
* Returns { data: Alarm[], summary: AlarmListSummary } with all pages merged.
|
|
142488
|
-
* The summary comes from page 1 (reflects full dataset totals).
|
|
142489
|
-
*/
|
|
142490
|
-
async getAlarms(params = {}, customerMap) {
|
|
142491
|
-
const cacheKey = `alarms:${JSON.stringify(params)}`;
|
|
142492
|
-
const cached = this.alarmsCache.get(cacheKey);
|
|
142493
|
-
if (isFresh(cached, this.cacheTtlMs)) return cached.data;
|
|
142494
|
-
let allData = [];
|
|
142495
|
-
let summary;
|
|
142496
|
-
let totalPages = 1;
|
|
142497
|
-
let page = 1;
|
|
142498
|
-
while (page <= totalPages) {
|
|
142499
|
-
const response = await this.client.getAlarms({ ...params, page });
|
|
142500
|
-
const pageData = (response.data || []).map((a) => mapApiAlarm(a, customerMap));
|
|
142501
|
-
allData = allData.concat(pageData);
|
|
142502
|
-
if (page === 1) {
|
|
142503
|
-
summary = response.summary;
|
|
142504
|
-
totalPages = response.pagination?.totalPages ?? 1;
|
|
142505
|
-
}
|
|
142506
|
-
page++;
|
|
142507
|
-
}
|
|
142508
|
-
const result = { data: allData, summary };
|
|
142509
|
-
this.alarmsCache.set(cacheKey, { data: result, timestamp: Date.now() });
|
|
142510
|
-
return result;
|
|
142511
|
-
}
|
|
142512
|
-
// -----------------------------------------------------------------------
|
|
142513
|
-
// Alarm statistics
|
|
142514
|
-
// -----------------------------------------------------------------------
|
|
142515
|
-
/**
|
|
142516
|
-
* Fetch aggregated alarm statistics for a tenant and time period.
|
|
142517
|
-
* Returns mapped AlarmStats (from src/types/alarm.ts).
|
|
142518
|
-
*/
|
|
142519
|
-
async getAlarmStats(tenantId, period) {
|
|
142520
|
-
const cacheKey = `stats:${tenantId}:${period}`;
|
|
142521
|
-
const cached = this.statsCache.get(cacheKey);
|
|
142522
|
-
if (isFresh(cached, this.cacheTtlMs)) return cached.data;
|
|
142523
|
-
const raw = await this.client.getAlarmStats(tenantId, period);
|
|
142524
|
-
const data = mapApiStats(raw);
|
|
142525
|
-
this.statsCache.set(cacheKey, { data, timestamp: Date.now() });
|
|
142526
|
-
return data;
|
|
142527
|
-
}
|
|
142528
|
-
// -----------------------------------------------------------------------
|
|
142529
|
-
// Alarm trend
|
|
142530
|
-
// -----------------------------------------------------------------------
|
|
142531
|
-
/**
|
|
142532
|
-
* Fetch alarm trend data points.
|
|
142533
|
-
* Returns mapped AlarmTrendDataPoint[] (from src/types/alarm.ts).
|
|
142534
|
-
*/
|
|
142535
|
-
async getAlarmTrend(tenantId, period, groupBy) {
|
|
142536
|
-
const cacheKey = `trend:${tenantId}:${period}:${groupBy}`;
|
|
142537
|
-
const cached = this.trendCache.get(cacheKey);
|
|
142538
|
-
if (isFresh(cached, this.cacheTtlMs)) return cached.data;
|
|
142539
|
-
const raw = await this.client.getAlarmTrend(tenantId, period, groupBy);
|
|
142540
|
-
const data = mapApiTrend(raw);
|
|
142541
|
-
this.trendCache.set(cacheKey, { data, timestamp: Date.now() });
|
|
142542
|
-
return data;
|
|
142543
|
-
}
|
|
142544
|
-
// -----------------------------------------------------------------------
|
|
142545
|
-
// Top downtime / offenders
|
|
142546
|
-
// -----------------------------------------------------------------------
|
|
142547
|
-
/**
|
|
142548
|
-
* Fetch top devices by alarm count / downtime.
|
|
142549
|
-
* Returns mapped DowntimeEntry[] (from src/types/operational.ts).
|
|
142550
|
-
*
|
|
142551
|
-
* @param deviceNameMap - Optional map of deviceId → friendly name from ThingsBoard cache
|
|
142552
|
-
*/
|
|
142553
|
-
async getTopDowntime(tenantId, deviceNameMap = /* @__PURE__ */ new Map(), limit = 5) {
|
|
142554
|
-
const items = await this.client.getTopOffenders(tenantId, limit);
|
|
142555
|
-
return mapApiTopOffenders(items, deviceNameMap);
|
|
142556
|
-
}
|
|
142557
|
-
// -----------------------------------------------------------------------
|
|
142558
|
-
// Per-device alarm counts
|
|
142559
|
-
// -----------------------------------------------------------------------
|
|
142560
|
-
/**
|
|
142561
|
-
* Fetch alarm counts per device.
|
|
142562
|
-
* Returns a Map<deviceId, alarmCount>.
|
|
142563
|
-
*/
|
|
142564
|
-
async getDeviceAlarmCounts(tenantId) {
|
|
142565
|
-
const items = await this.client.getAlarmStatsByDevice(tenantId);
|
|
142566
|
-
const map = /* @__PURE__ */ new Map();
|
|
142567
|
-
(items || []).forEach((item) => map.set(item.deviceId, item.alarmCount));
|
|
142568
|
-
return map;
|
|
142569
|
-
}
|
|
142570
|
-
// -----------------------------------------------------------------------
|
|
142571
|
-
// Alarm actions
|
|
142572
|
-
// -----------------------------------------------------------------------
|
|
142573
|
-
async acknowledgeAlarm(id, userEmail, note) {
|
|
142574
|
-
return this.client.acknowledgeAlarm(id, userEmail, note);
|
|
142575
|
-
}
|
|
142576
|
-
async silenceAlarm(id, userEmail, duration, reason) {
|
|
142577
|
-
return this.client.silenceAlarm(id, userEmail, duration, reason);
|
|
142578
|
-
}
|
|
142579
|
-
async escalateAlarm(id, userEmail, reason) {
|
|
142580
|
-
return this.client.escalateAlarm(id, userEmail, reason);
|
|
142581
|
-
}
|
|
142582
|
-
async closeAlarm(id, userEmail, resolution) {
|
|
142583
|
-
return this.client.closeAlarm(id, userEmail, resolution);
|
|
142584
|
-
}
|
|
142585
|
-
// -----------------------------------------------------------------------
|
|
142586
|
-
// Batch alarm actions (POST /alarms/batch/*)
|
|
142587
|
-
// -----------------------------------------------------------------------
|
|
142588
|
-
async batchAcknowledge(alarmIds, userEmail) {
|
|
142589
|
-
return this.client.batchAcknowledge(alarmIds, userEmail);
|
|
142590
|
-
}
|
|
142591
|
-
async batchSilence(alarmIds, userEmail, duration) {
|
|
142592
|
-
return this.client.batchSilence(alarmIds, userEmail, duration);
|
|
142593
|
-
}
|
|
142594
|
-
async batchEscalate(alarmIds, userEmail) {
|
|
142595
|
-
return this.client.batchEscalate(alarmIds, userEmail);
|
|
142596
|
-
}
|
|
142597
|
-
async batchClose(alarmIds, userEmail, resolution) {
|
|
142598
|
-
return this.client.batchClose(alarmIds, userEmail, resolution);
|
|
142599
|
-
}
|
|
142600
|
-
// -----------------------------------------------------------------------
|
|
142601
|
-
// Configuration
|
|
142602
|
-
// -----------------------------------------------------------------------
|
|
142603
|
-
/**
|
|
142604
|
-
* Set the Alarms API base URL (e.g. from MAIN_VIEW orchestrator settings).
|
|
142605
|
-
* Clears the cache so subsequent calls use the new endpoint.
|
|
142606
|
-
*/
|
|
142607
|
-
configure(baseUrl, cacheTtlMs, apiKey) {
|
|
142608
|
-
this.client.configure(baseUrl ?? "", apiKey);
|
|
142609
|
-
if (cacheTtlMs && cacheTtlMs > 0) this.cacheTtlMs = cacheTtlMs;
|
|
142610
|
-
this.clearCache();
|
|
142611
|
-
}
|
|
142612
|
-
// -----------------------------------------------------------------------
|
|
142613
|
-
// Cache management
|
|
142614
|
-
// -----------------------------------------------------------------------
|
|
142615
|
-
clearCache() {
|
|
142616
|
-
this.availabilityCache.clear();
|
|
142617
|
-
this.alarmsCache.clear();
|
|
142618
|
-
this.statsCache.clear();
|
|
142619
|
-
this.trendCache.clear();
|
|
142620
|
-
}
|
|
142621
|
-
};
|
|
142622
|
-
var AlarmService = new AlarmServiceClass();
|
|
142623
|
-
|
|
142624
142878
|
// src/components/AlarmsNotificationsPanel/AlarmsNotificationsPanelController.ts
|
|
142625
142879
|
var AlarmsNotificationsPanelController = class {
|
|
142626
142880
|
state;
|
|
@@ -143821,16 +144075,36 @@ var AlarmsNotificationsPanelView = class {
|
|
|
143821
144075
|
* Render dashboard tab content.
|
|
143822
144076
|
* Trend data is fetched asynchronously on first open and injected when ready.
|
|
143823
144077
|
*/
|
|
144078
|
+
/** KPIs do Dashboard calculados sobre uma lista de alarmes (a mesma exibida na Lista). */
|
|
144079
|
+
statsFromAlarms(alarms) {
|
|
144080
|
+
const bySeverity = { CRITICAL: 0, HIGH: 0, MEDIUM: 0, LOW: 0, INFO: 0 };
|
|
144081
|
+
const byState = { OPEN: 0, ACK: 0, SNOOZED: 0, ESCALATED: 0, CLOSED: 0 };
|
|
144082
|
+
let openCritical = 0;
|
|
144083
|
+
let openHigh = 0;
|
|
144084
|
+
let last24Hours = 0;
|
|
144085
|
+
const since = Date.now() - 24 * 60 * 60 * 1e3;
|
|
144086
|
+
for (const a of alarms) {
|
|
144087
|
+
if (a.severity in bySeverity) bySeverity[a.severity]++;
|
|
144088
|
+
if (a.state in byState) byState[a.state]++;
|
|
144089
|
+
const open = a.state !== "CLOSED";
|
|
144090
|
+
if (open && a.severity === "CRITICAL") openCritical++;
|
|
144091
|
+
if (open && a.severity === "HIGH") openHigh++;
|
|
144092
|
+
const first = new Date(a.firstOccurrence || a.lastOccurrence || 0).getTime();
|
|
144093
|
+
if (first >= since) last24Hours++;
|
|
144094
|
+
}
|
|
144095
|
+
return { total: alarms.length, bySeverity, byState, openCritical, openHigh, last24Hours };
|
|
144096
|
+
}
|
|
143824
144097
|
renderDashboardContent(state6) {
|
|
143825
144098
|
if (!this.root) return;
|
|
143826
144099
|
const container = this.root.querySelector("#dashboardContent");
|
|
143827
144100
|
if (!container) return;
|
|
144101
|
+
const stats = state6.allAlarms.length > 0 ? this.statsFromAlarms(state6.filteredAlarms) : state6.stats;
|
|
143828
144102
|
if (container.children.length > 0) {
|
|
143829
|
-
updateDashboard(container,
|
|
144103
|
+
updateDashboard(container, stats, this.trendData);
|
|
143830
144104
|
} else {
|
|
143831
|
-
container.innerHTML = renderDashboard(
|
|
144105
|
+
container.innerHTML = renderDashboard(stats, this.trendData);
|
|
143832
144106
|
}
|
|
143833
|
-
this.emit("stats-updated",
|
|
144107
|
+
this.emit("stats-updated", stats);
|
|
143834
144108
|
if (!this.trendFetched && this.params.tenantId) {
|
|
143835
144109
|
this.trendFetched = true;
|
|
143836
144110
|
AlarmService.getAlarmTrend(this.params.tenantId, "week", "day").then((data) => {
|
|
@@ -163286,6 +163560,11 @@ var STYLES3 = `
|
|
|
163286
163560
|
flex-direction: column;
|
|
163287
163561
|
overflow: hidden;
|
|
163288
163562
|
}
|
|
163563
|
+
#${MODAL_ID5} .abm-card.is-maximized {
|
|
163564
|
+
width: 100vw !important; max-width: 100vw !important;
|
|
163565
|
+
height: 100vh !important; max-height: 100vh !important; border-radius: 0;
|
|
163566
|
+
}
|
|
163567
|
+
#${MODAL_ID5} .myio-premium-chrome-header .abm-view-toggle { margin-left: 0; margin-right: 6px; }
|
|
163289
163568
|
#${MODAL_ID5} .abm-header {
|
|
163290
163569
|
background: ${TEAL2};
|
|
163291
163570
|
color: #fff;
|
|
@@ -164284,7 +164563,11 @@ async function openAlarmBundleMapModal(params) {
|
|
|
164284
164563
|
card.className = "abm-card";
|
|
164285
164564
|
overlay.appendChild(card);
|
|
164286
164565
|
document.body.appendChild(overlay);
|
|
164566
|
+
let chrome = null;
|
|
164567
|
+
let maximized = false;
|
|
164287
164568
|
const closeModal2 = () => {
|
|
164569
|
+
chrome?.destroy();
|
|
164570
|
+
chrome = null;
|
|
164288
164571
|
overlay.remove();
|
|
164289
164572
|
params.onClose?.();
|
|
164290
164573
|
};
|
|
@@ -164310,12 +164593,29 @@ async function openAlarmBundleMapModal(params) {
|
|
|
164310
164593
|
<button class="abm-close-btn" id="abm-close-btn" title="Fechar">\u2715</button>
|
|
164311
164594
|
</div>
|
|
164312
164595
|
<div class="abm-body">${bodyHtml}</div>
|
|
164313
|
-
<div class="abm-footer">
|
|
164314
|
-
<button class="abm-btn abm-btn-secondary" id="abm-close-btn-footer">Fechar</button>
|
|
164315
|
-
</div>
|
|
164316
164596
|
`;
|
|
164317
164597
|
card.querySelector("#abm-close-btn")?.addEventListener("click", closeModal2);
|
|
164318
|
-
card.querySelector("
|
|
164598
|
+
const toggleEl = card.querySelector(".abm-view-toggle");
|
|
164599
|
+
chrome?.destroy();
|
|
164600
|
+
chrome = createPremiumModalChrome({
|
|
164601
|
+
modalId: "abm-modal",
|
|
164602
|
+
icon: "\u{1F517}",
|
|
164603
|
+
title: `Mapa de Alarmes GCDR${subtitle ? ` \u2014 ${escHtml5(subtitle)}` : ""}`,
|
|
164604
|
+
modalEl: card,
|
|
164605
|
+
headerSlot: card.querySelector(".abm-header"),
|
|
164606
|
+
maximized,
|
|
164607
|
+
headerRadius: "16px 16px 0 0",
|
|
164608
|
+
onMaximizeChange: (m) => {
|
|
164609
|
+
maximized = m;
|
|
164610
|
+
placeToggle();
|
|
164611
|
+
},
|
|
164612
|
+
onClose: closeModal2
|
|
164613
|
+
});
|
|
164614
|
+
const placeToggle = () => {
|
|
164615
|
+
const actions = chrome?.header.lastElementChild;
|
|
164616
|
+
if (toggleEl && actions) actions.insertBefore(toggleEl, actions.firstChild);
|
|
164617
|
+
};
|
|
164618
|
+
placeToggle();
|
|
164319
164619
|
}
|
|
164320
164620
|
render3(`<div class="abm-loading">\u23F3 Carregando mapa de alarmes...</div>`);
|
|
164321
164621
|
try {
|