myio-js-library 0.1.489 → 0.1.491
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 +873 -38
- package/dist/index.d.cts +149 -1
- package/dist/index.js +870 -38
- package/dist/myio-js-library.umd.js +870 -38
- package/dist/myio-js-library.umd.min.js +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -711,6 +711,7 @@ __export(index_exports, {
|
|
|
711
711
|
MenuShoppingView: () => MenuShoppingView,
|
|
712
712
|
MenuView: () => MenuView,
|
|
713
713
|
ModalHeader: () => ModalHeader,
|
|
714
|
+
MyIOAuthContext: () => MyIOAuthContext,
|
|
714
715
|
MyIOChartModal: () => MyIOChartModal,
|
|
715
716
|
MyIODraggableCard: () => MyIODraggableCard,
|
|
716
717
|
MyIOSelectionStore: () => MyIOSelectionStore,
|
|
@@ -739,6 +740,7 @@ __export(index_exports, {
|
|
|
739
740
|
OperationalGeneralListController: () => OperationalGeneralListController,
|
|
740
741
|
OperationalGeneralListView: () => OperationalGeneralListView,
|
|
741
742
|
OperationalHeaderDevicesGridView: () => OperationalHeaderDevicesGridView,
|
|
743
|
+
PERM: () => PERM,
|
|
742
744
|
POWER_LIMITS_DEVICE_TYPES: () => DEVICE_TYPES,
|
|
743
745
|
POWER_LIMITS_STATUS_CONFIG: () => STATUS_CONFIG,
|
|
744
746
|
POWER_LIMITS_TELEMETRY_TYPES: () => TELEMETRY_TYPES2,
|
|
@@ -1007,6 +1009,7 @@ __export(index_exports, {
|
|
|
1007
1009
|
groupByDay: () => groupByDay,
|
|
1008
1010
|
handleDeviceType: () => handleDeviceType,
|
|
1009
1011
|
hasSelectedDays: () => hasSelectedDays,
|
|
1012
|
+
initMyIOAuthContext: () => initMyIOAuthContext,
|
|
1010
1013
|
initOnOffTimelineTooltips: () => initOnOffTimelineTooltips,
|
|
1011
1014
|
injectActionButtonStyles: () => injectActionButtonStyles,
|
|
1012
1015
|
injectAlarmsNotificationsPanelStyles: () => injectAlarmsNotificationsPanelStyles,
|
|
@@ -1159,7 +1162,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
1159
1162
|
// package.json
|
|
1160
1163
|
var package_default = {
|
|
1161
1164
|
name: "myio-js-library",
|
|
1162
|
-
version: "0.1.
|
|
1165
|
+
version: "0.1.491",
|
|
1163
1166
|
description: "A clean, standalone JS SDK for MYIO projects",
|
|
1164
1167
|
license: "MIT",
|
|
1165
1168
|
repository: "github:gh-myio/myio-js-library",
|
|
@@ -58602,15 +58605,24 @@ var AlarmsTab = class {
|
|
|
58602
58605
|
* Falls back to prefetchedAlarms prop when ASO is not available (e.g. standalone showcase).
|
|
58603
58606
|
*/
|
|
58604
58607
|
readAlarmsFromASO() {
|
|
58605
|
-
const
|
|
58608
|
+
const win = window;
|
|
58609
|
+
const aso = win.AlarmServiceOrchestrator;
|
|
58610
|
+
const showOffline = win.MyIOOrchestrator?.showOfflineAlarms === true;
|
|
58611
|
+
const _OFFLINE_TYPES = ["DEVICE OFFLINE", "DISPOSITIVO OFFLINE"];
|
|
58612
|
+
const _isOffline = (a) => {
|
|
58613
|
+
const t = ((a.title ?? a.alarmType) || "").toUpperCase();
|
|
58614
|
+
return _OFFLINE_TYPES.some((ex) => t.startsWith(ex)) || a.alarmType === "connectivity";
|
|
58615
|
+
};
|
|
58616
|
+
let alarms = [];
|
|
58606
58617
|
if (aso && this.config.gcdrDeviceId) {
|
|
58607
|
-
|
|
58608
|
-
}
|
|
58609
|
-
|
|
58610
|
-
|
|
58611
|
-
|
|
58618
|
+
alarms = aso.getAlarmsForDevice(this.config.gcdrDeviceId);
|
|
58619
|
+
} else {
|
|
58620
|
+
const prefetched = this.config.prefetchedAlarms;
|
|
58621
|
+
if (prefetched != null) {
|
|
58622
|
+
alarms = prefetched.filter((a) => a.deviceId === this.config.gcdrDeviceId);
|
|
58623
|
+
}
|
|
58612
58624
|
}
|
|
58613
|
-
return
|
|
58625
|
+
return showOffline ? alarms : alarms.filter((a) => !_isOffline(a));
|
|
58614
58626
|
}
|
|
58615
58627
|
// ============================================================================
|
|
58616
58628
|
// Card mapping — separado (Disp. + Tipo) view: one card per individual alarm
|
|
@@ -84015,7 +84027,10 @@ function openUpsellModal(params) {
|
|
|
84015
84027
|
bulkRelationModal: { open: false, target: "CUSTOMER", selectedAssetId: "", selectedAssetName: "", search: "", newAssetName: "", assetsLoaded: false, overrideCustomerId: "", overrideCustomerName: "", customerSearch: "", customerPickerOpen: false },
|
|
84016
84028
|
checkFixLoading: false,
|
|
84017
84029
|
checkFixReport: null,
|
|
84018
|
-
checkFixFilter: "all"
|
|
84030
|
+
checkFixFilter: "all",
|
|
84031
|
+
checkFixSort: { field: "name", order: "asc" },
|
|
84032
|
+
checkFixAdvancedFilter: { statuses: [], connStatuses: [], domains: [], missingIngestionId: false, missingCentralSlave: false },
|
|
84033
|
+
checkFixFilterOpen: false
|
|
84019
84034
|
};
|
|
84020
84035
|
const savedTheme = localStorage.getItem("myio-upsell-modal-theme");
|
|
84021
84036
|
if (savedTheme) state6.theme = savedTheme;
|
|
@@ -84154,6 +84169,12 @@ function renderModal4(container, state6, modalId, t, error) {
|
|
|
84154
84169
|
font-size: 14px; font-weight: 500; font-family: 'Roboto', Arial, sans-serif;
|
|
84155
84170
|
display: flex; align-items: center; gap: 6px;
|
|
84156
84171
|
" ${!state6.selectedCustomer ? 'disabled title="Selecione um Customer primeiro"' : ""}>\u{1F517} For\xE7ar Rela\xE7\xE3o (${state6.selectedDevices.length})</button>
|
|
84172
|
+
<button id="${modalId}-clear-gcdr-ids" style="
|
|
84173
|
+
background: #dc2626; color: white; border: none;
|
|
84174
|
+
padding: 8px 16px; border-radius: 6px; cursor: pointer;
|
|
84175
|
+
font-size: 14px; font-weight: 500; font-family: 'Roboto', Arial, sans-serif;
|
|
84176
|
+
display: flex; align-items: center; gap: 6px;
|
|
84177
|
+
" ${!state6.selectedCustomer ? 'disabled title="Selecione um Customer primeiro"' : ""}>\u{1F9F9} Clear GCDR IDs</button>
|
|
84157
84178
|
<button id="${modalId}-custom-shortcut" style="
|
|
84158
84179
|
background: #ef4444; color: white; border: none;
|
|
84159
84180
|
padding: 8px 16px; border-radius: 6px; cursor: pointer;
|
|
@@ -84820,6 +84841,29 @@ function renderStep1(state6, modalId, colors2, t) {
|
|
|
84820
84841
|
</div>
|
|
84821
84842
|
`;
|
|
84822
84843
|
}
|
|
84844
|
+
function _buildCfStatusDetail(r) {
|
|
84845
|
+
if (r.status === "ok") return "";
|
|
84846
|
+
const lines = [];
|
|
84847
|
+
if (r.status === "mismatch") {
|
|
84848
|
+
if (r.actual.type !== r.inferred.deviceType)
|
|
84849
|
+
lines.push(`TYPE: actual="${r.actual.type}" \u2260 expected="${r.inferred.deviceType}"`);
|
|
84850
|
+
if (r.actual.deviceType !== r.inferred.deviceType)
|
|
84851
|
+
lines.push(`deviceType: actual="${r.actual.deviceType}" \u2260 expected="${r.inferred.deviceType}"`);
|
|
84852
|
+
if (r.actual.deviceProfile !== r.inferred.deviceProfile)
|
|
84853
|
+
lines.push(`deviceProfile: actual="${r.actual.deviceProfile}" \u2260 expected="${r.inferred.deviceProfile}"`);
|
|
84854
|
+
} else if (r.status === "missing") {
|
|
84855
|
+
if (!r.actual.type) lines.push("TYPE: n\xE3o definido");
|
|
84856
|
+
if (!r.actual.deviceType) lines.push("deviceType: n\xE3o definido");
|
|
84857
|
+
if (!r.actual.deviceProfile) lines.push("deviceProfile: n\xE3o definido");
|
|
84858
|
+
} else if (r.status === "undefined") {
|
|
84859
|
+
lines.push(`Nome n\xE3o permite inferir o tipo de dispositivo: "${r.deviceName}"`);
|
|
84860
|
+
lines.push("Use Check & Fix manual ou ajuste o nome do dispositivo.");
|
|
84861
|
+
}
|
|
84862
|
+
if (!r.ingestionId) lines.push("ingestionId: ausente (n\xE3o integrado ao backend de ingest\xE3o)");
|
|
84863
|
+
if (!r.centralId) lines.push("centralId: ausente");
|
|
84864
|
+
if (r.connStatus === "offline") lines.push("Dispositivo offline no momento");
|
|
84865
|
+
return lines.join("\n") || r.status;
|
|
84866
|
+
}
|
|
84823
84867
|
function renderCheckFixRow(r, state6, modalId, colors2, dupPairIds, dupIngestionIds) {
|
|
84824
84868
|
const isSelectedSingle = state6.deviceSelectionMode === "single" && getEntityId(state6.selectedDevice) === r.deviceId;
|
|
84825
84869
|
const isSelectedMulti = state6.deviceSelectionMode === "multi" && state6.selectedDevices.some((d) => getEntityId(d) === r.deviceId);
|
|
@@ -84883,7 +84927,10 @@ function renderCheckFixRow(r, state6, modalId, colors2, dupPairIds, dupIngestion
|
|
|
84883
84927
|
<td style="${cell(dupIngestionIds.has(r.deviceId) ? "bad" : "none")} font-size:9px; font-family:monospace; max-width:110px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" title="${r.ingestionId || ""}">${r.ingestionId || dash}</td>
|
|
84884
84928
|
<td style="${cell(dupPairIds.has(r.deviceId) ? "bad" : "none")} font-size:9px; font-family:monospace; max-width:80px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" title="${r.centralId || ""}">${r.centralId || dash}</td>
|
|
84885
84929
|
<td style="${cell(dupPairIds.has(r.deviceId) ? "bad" : "none")} font-size:9px; font-family:monospace; max-width:50px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">${r.slaveId || dash}</td>
|
|
84886
|
-
<td style="padding:4px 6px; font-size:10px; color:${STATUS_COLOR[r.status]}; font-weight:700; white-space:nowrap;
|
|
84930
|
+
<td style="padding:4px 6px; font-size:10px; color:${STATUS_COLOR[r.status]}; font-weight:700; white-space:nowrap; cursor:${r.status !== "ok" ? "help" : "default"};"
|
|
84931
|
+
${r.status !== "ok" ? `data-cf-status="${r.status}" data-cf-device="${encodeURIComponent(r.deviceName)}" data-cf-detail="${encodeURIComponent(_buildCfStatusDetail(r))}"` : ""}>
|
|
84932
|
+
${STATUS_ICON[r.status]} ${r.status}
|
|
84933
|
+
</td>
|
|
84887
84934
|
</tr>`;
|
|
84888
84935
|
}
|
|
84889
84936
|
function renderStep2(state6, modalId, colors2, t) {
|
|
@@ -85170,7 +85217,42 @@ function renderStep2(state6, modalId, colors2, t) {
|
|
|
85170
85217
|
const multiCol = state6.deviceSelectionMode === "multi";
|
|
85171
85218
|
if (state6.checkFixReport) {
|
|
85172
85219
|
const idToRecord = new Map(state6.checkFixReport.records.map((r) => [r.deviceId, r]));
|
|
85173
|
-
|
|
85220
|
+
let cfRows = sortedDevices.map((d) => idToRecord.get(getEntityId(d))).filter((r) => r !== void 0).filter((r) => state6.checkFixFilter === "all" || r.status === state6.checkFixFilter);
|
|
85221
|
+
const af = state6.checkFixAdvancedFilter;
|
|
85222
|
+
if (af.statuses.length > 0) cfRows = cfRows.filter((r) => af.statuses.includes(r.status));
|
|
85223
|
+
if (af.connStatuses.length > 0) cfRows = cfRows.filter((r) => af.connStatuses.includes(r.connStatus || "null"));
|
|
85224
|
+
if (af.domains.length > 0) cfRows = cfRows.filter((r) => af.domains.includes(r.domain || "null"));
|
|
85225
|
+
if (af.missingIngestionId) cfRows = cfRows.filter((r) => !r.ingestionId);
|
|
85226
|
+
if (af.missingCentralSlave) cfRows = cfRows.filter((r) => !r.centralId || r.slaveId == null);
|
|
85227
|
+
const cfSortField = state6.checkFixSort.field;
|
|
85228
|
+
const cfSortOrder = state6.checkFixSort.order;
|
|
85229
|
+
cfRows = [...cfRows].sort((a, b) => {
|
|
85230
|
+
let va = "", vb = "";
|
|
85231
|
+
if (cfSortField === "name") {
|
|
85232
|
+
va = a.deviceName || "";
|
|
85233
|
+
vb = b.deviceName || "";
|
|
85234
|
+
} else if (cfSortField === "status") {
|
|
85235
|
+
va = a.status || "";
|
|
85236
|
+
vb = b.status || "";
|
|
85237
|
+
} else if (cfSortField === "connStatus") {
|
|
85238
|
+
va = a.connStatus || "";
|
|
85239
|
+
vb = b.connStatus || "";
|
|
85240
|
+
} else if (cfSortField === "deviceType") {
|
|
85241
|
+
va = a.actual.deviceType || "";
|
|
85242
|
+
vb = b.actual.deviceType || "";
|
|
85243
|
+
} else if (cfSortField === "deviceProfile") {
|
|
85244
|
+
va = a.actual.deviceProfile || "";
|
|
85245
|
+
vb = b.actual.deviceProfile || "";
|
|
85246
|
+
} else if (cfSortField === "ingestionId") {
|
|
85247
|
+
va = a.ingestionId || "";
|
|
85248
|
+
vb = b.ingestionId || "";
|
|
85249
|
+
} else if (cfSortField === "domain") {
|
|
85250
|
+
va = a.domain || "";
|
|
85251
|
+
vb = b.domain || "";
|
|
85252
|
+
}
|
|
85253
|
+
const cmp = va.toLowerCase().localeCompare(vb.toLowerCase());
|
|
85254
|
+
return cfSortOrder === "asc" ? cmp : -cmp;
|
|
85255
|
+
});
|
|
85174
85256
|
const pairCount = /* @__PURE__ */ new Map();
|
|
85175
85257
|
cfRows.forEach((r) => {
|
|
85176
85258
|
if (r.centralId && r.slaveId != null) {
|
|
@@ -85195,27 +85277,115 @@ function renderStep2(state6, modalId, colors2, t) {
|
|
|
85195
85277
|
if (ids.length > 1) ids.forEach((id) => dupIngestionIds.add(id));
|
|
85196
85278
|
});
|
|
85197
85279
|
const colSpan = multiCol ? 18 : 17;
|
|
85280
|
+
const cfSortHeader = (field, label, rowspan = 1, extraStyle = "") => {
|
|
85281
|
+
const isActive = state6.checkFixSort.field === field;
|
|
85282
|
+
const arrow = isActive ? state6.checkFixSort.order === "asc" ? "\u25B2" : "\u25BC" : "\u25BD";
|
|
85283
|
+
return `<th ${rowspan > 1 ? `rowspan="${rowspan}"` : ""} id="${modalId}-cf-sort-${field}" data-cf-sort="${field}" style="${thStyle(extraStyle)} cursor:pointer; user-select:none; ${isActive ? `background:rgba(62,26,125,0.18);` : ""}">
|
|
85284
|
+
<span style="display:flex;align-items:center;gap:3px;white-space:nowrap;">${label}<span style="font-size:7px;color:${isActive ? MYIO_PURPLE : colors2.textMuted};">${arrow}</span></span>
|
|
85285
|
+
</th>`;
|
|
85286
|
+
};
|
|
85287
|
+
const allDomains = [...new Set(state6.checkFixReport.records.map((r) => r.domain || "null"))].sort();
|
|
85288
|
+
const allConns = [...new Set(state6.checkFixReport.records.map((r) => r.connStatus || "null"))].sort();
|
|
85289
|
+
const hasAdvFilter = af.statuses.length > 0 || af.connStatuses.length > 0 || af.domains.length > 0 || af.missingIngestionId || af.missingCentralSlave;
|
|
85290
|
+
const filterPanel = state6.checkFixFilterOpen ? `
|
|
85291
|
+
<div id="${modalId}-cf-filter-panel" style="
|
|
85292
|
+
position:absolute; top:calc(100% + 4px); right:0; z-index:200;
|
|
85293
|
+
background:${colors2.cardBg}; border:1px solid ${colors2.border}; border-radius:10px;
|
|
85294
|
+
box-shadow:0 8px 32px rgba(0,0,0,0.22); padding:16px; min-width:300px;
|
|
85295
|
+
font-size:11px; color:${colors2.text};
|
|
85296
|
+
">
|
|
85297
|
+
<div style="font-weight:700; font-size:12px; margin-bottom:12px; color:${colors2.text};">\u{1F527} Filtros Avan\xE7ados \u2014 Check & Fix</div>
|
|
85298
|
+
<div style="margin-bottom:10px;">
|
|
85299
|
+
<div style="font-weight:600; margin-bottom:4px; color:${colors2.textMuted}; text-transform:uppercase; font-size:9px;">Status</div>
|
|
85300
|
+
<div style="display:flex;gap:6px;flex-wrap:wrap;">
|
|
85301
|
+
${["ok", "mismatch", "missing", "undefined"].map((s) => {
|
|
85302
|
+
const icons = { ok: "\u2705", mismatch: "\u26A0\uFE0F", missing: "\u274C", undefined: "\u2753" };
|
|
85303
|
+
const active = af.statuses.includes(s);
|
|
85304
|
+
return `<button data-cf-flt-status="${s}" style="padding:3px 8px;border-radius:4px;font-size:10px;cursor:pointer;border:1px solid ${active ? MYIO_PURPLE : colors2.border};background:${active ? MYIO_PURPLE : colors2.inputBg};color:${active ? "white" : colors2.text};">${icons[s]} ${s}</button>`;
|
|
85305
|
+
}).join("")}
|
|
85306
|
+
</div>
|
|
85307
|
+
</div>
|
|
85308
|
+
<div style="margin-bottom:10px;">
|
|
85309
|
+
<div style="font-weight:600; margin-bottom:4px; color:${colors2.textMuted}; text-transform:uppercase; font-size:9px;">Conn. Status</div>
|
|
85310
|
+
<div style="display:flex;gap:6px;flex-wrap:wrap;">
|
|
85311
|
+
${allConns.map((c) => {
|
|
85312
|
+
const active = af.connStatuses.includes(c);
|
|
85313
|
+
return `<button data-cf-flt-conn="${c}" style="padding:3px 8px;border-radius:4px;font-size:10px;cursor:pointer;border:1px solid ${active ? MYIO_PURPLE : colors2.border};background:${active ? MYIO_PURPLE : colors2.inputBg};color:${active ? "white" : colors2.text};">${c}</button>`;
|
|
85314
|
+
}).join("")}
|
|
85315
|
+
</div>
|
|
85316
|
+
</div>
|
|
85317
|
+
<div style="margin-bottom:10px;">
|
|
85318
|
+
<div style="font-weight:600; margin-bottom:4px; color:${colors2.textMuted}; text-transform:uppercase; font-size:9px;">Domain</div>
|
|
85319
|
+
<div style="display:flex;gap:6px;flex-wrap:wrap;">
|
|
85320
|
+
${allDomains.map((d) => {
|
|
85321
|
+
const active = af.domains.includes(d);
|
|
85322
|
+
return `<button data-cf-flt-domain="${d}" style="padding:3px 8px;border-radius:4px;font-size:10px;cursor:pointer;border:1px solid ${active ? MYIO_PURPLE : colors2.border};background:${active ? MYIO_PURPLE : colors2.inputBg};color:${active ? "white" : colors2.text};">${d}</button>`;
|
|
85323
|
+
}).join("")}
|
|
85324
|
+
</div>
|
|
85325
|
+
</div>
|
|
85326
|
+
<div style="margin-bottom:12px;display:flex;gap:12px;flex-wrap:wrap;">
|
|
85327
|
+
<label style="display:flex;align-items:center;gap:5px;cursor:pointer;">
|
|
85328
|
+
<input type="checkbox" id="${modalId}-cf-flt-noing" ${af.missingIngestionId ? "checked" : ""} style="accent-color:${MYIO_PURPLE};"/>
|
|
85329
|
+
<span>Sem ingestionId</span>
|
|
85330
|
+
</label>
|
|
85331
|
+
<label style="display:flex;align-items:center;gap:5px;cursor:pointer;">
|
|
85332
|
+
<input type="checkbox" id="${modalId}-cf-flt-nocentral" ${af.missingCentralSlave ? "checked" : ""} style="accent-color:${MYIO_PURPLE};"/>
|
|
85333
|
+
<span>Sem centralId/slaveId</span>
|
|
85334
|
+
</label>
|
|
85335
|
+
</div>
|
|
85336
|
+
<div style="display:flex;gap:8px;justify-content:flex-end;">
|
|
85337
|
+
<button id="${modalId}-cf-flt-clear" style="padding:5px 12px;border-radius:5px;font-size:11px;cursor:pointer;background:transparent;border:1px solid ${colors2.border};color:${colors2.textMuted};">Limpar</button>
|
|
85338
|
+
<button id="${modalId}-cf-flt-close" style="padding:5px 12px;border-radius:5px;font-size:11px;cursor:pointer;background:${MYIO_PURPLE};border:none;color:white;font-weight:600;">Fechar</button>
|
|
85339
|
+
</div>
|
|
85340
|
+
</div>
|
|
85341
|
+
` : "";
|
|
85198
85342
|
return `
|
|
85343
|
+
<div style="position:relative;">
|
|
85344
|
+
<div style="display:flex;align-items:center;gap:6px;margin-bottom:6px;flex-wrap:wrap;">
|
|
85345
|
+
<span style="font-size:10px;color:${colors2.textMuted};">${cfRows.length} / ${state6.checkFixReport.records.length} registros</span>
|
|
85346
|
+
<div style="position:relative;margin-left:auto;">
|
|
85347
|
+
<button id="${modalId}-cf-filter-btn" style="padding:3px 10px;border-radius:5px;font-size:10px;cursor:pointer;border:1px solid ${hasAdvFilter ? MYIO_PURPLE : colors2.border};background:${hasAdvFilter ? MYIO_PURPLE : colors2.inputBg};color:${hasAdvFilter ? "white" : colors2.text};font-weight:${hasAdvFilter ? "700" : "400"};">
|
|
85348
|
+
\u{1F527} Filtros${hasAdvFilter ? ` (${af.statuses.length + af.connStatuses.length + af.domains.length + (af.missingIngestionId ? 1 : 0) + (af.missingCentralSlave ? 1 : 0)})` : ""}
|
|
85349
|
+
</button>
|
|
85350
|
+
${filterPanel}
|
|
85351
|
+
</div>
|
|
85352
|
+
</div>
|
|
85199
85353
|
<div id="${modalId}-device-list" style="
|
|
85200
85354
|
overflow-x:auto; max-height:${gridHeight};
|
|
85201
85355
|
border:1px solid ${colors2.border}; border-radius:8px; background:${colors2.surface};
|
|
85202
85356
|
">
|
|
85203
|
-
<table style="border-collapse:collapse;
|
|
85357
|
+
<table style="border-collapse:collapse; table-layout:fixed; width:100%; min-width:1100px;">
|
|
85358
|
+
<colgroup>
|
|
85359
|
+
${multiCol ? `<col style="width:28px"/>` : ""}
|
|
85360
|
+
<col style="width:150px"/>
|
|
85361
|
+
<col style="width:95px"/>
|
|
85362
|
+
<col style="width:70px"/><col style="width:70px"/>
|
|
85363
|
+
<col style="width:90px"/><col style="width:90px"/>
|
|
85364
|
+
<col style="width:90px"/><col style="width:90px"/>
|
|
85365
|
+
<col style="width:85px"/>
|
|
85366
|
+
<col style="width:110px"/>
|
|
85367
|
+
<col style="width:70px"/>
|
|
85368
|
+
<col style="width:110px"/>
|
|
85369
|
+
<col style="width:110px"/>
|
|
85370
|
+
<col style="width:80px"/>
|
|
85371
|
+
<col style="width:50px"/>
|
|
85372
|
+
<col style="width:90px"/>
|
|
85373
|
+
</colgroup>
|
|
85204
85374
|
<thead>
|
|
85205
85375
|
<tr>
|
|
85206
85376
|
${multiCol ? `<th rowspan="2" style="${thStyle("width:28px")}">\u2611</th>` : ""}
|
|
85207
|
-
|
|
85377
|
+
${cfSortHeader("name", "Name", 2, "min-width:140px")}
|
|
85208
85378
|
<th rowspan="2" style="${thStyle("min-width:90px")}">Label</th>
|
|
85209
85379
|
<th colspan="2" style="${thStyle("text-align:center")}">TYPE</th>
|
|
85210
85380
|
<th colspan="2" style="${thStyle("text-align:center")}">DevType</th>
|
|
85211
85381
|
<th colspan="2" style="${thStyle("text-align:center")}">DevProfile</th>
|
|
85212
85382
|
<th colspan="2" style="${thStyle("text-align:center")}">Last Telemetry</th>
|
|
85213
|
-
|
|
85383
|
+
${cfSortHeader("connStatus", "Conn.", 2, "min-width:65px")}
|
|
85214
85384
|
<th rowspan="2" style="${thStyle("min-width:110px")}">gcdrId</th>
|
|
85215
|
-
|
|
85385
|
+
${cfSortHeader("ingestionId", "ingestionId", 2, "min-width:110px")}
|
|
85216
85386
|
<th rowspan="2" style="${thStyle("min-width:80px")}">centralId</th>
|
|
85217
85387
|
<th rowspan="2" style="${thStyle("min-width:50px")}">slave</th>
|
|
85218
|
-
|
|
85388
|
+
${cfSortHeader("status", "Result", 2)}
|
|
85219
85389
|
</tr>
|
|
85220
85390
|
<tr>
|
|
85221
85391
|
<th style="${thStyle()}">Exp</th><th style="${thStyle()}">Act</th>
|
|
@@ -85229,6 +85399,7 @@ function renderStep2(state6, modalId, colors2, t) {
|
|
|
85229
85399
|
${cfRows.length === 0 ? `<tr><td colspan="${colSpan}" style="padding:20px; text-align:center; color:${colors2.textMuted};">${t.noResults}</td></tr>` : cfRows.map((r) => renderCheckFixRow(r, state6, modalId, colors2, dupPairIds, dupIngestionIds)).join("")}
|
|
85230
85400
|
</tbody>
|
|
85231
85401
|
</table>
|
|
85402
|
+
</div>
|
|
85232
85403
|
</div>`;
|
|
85233
85404
|
}
|
|
85234
85405
|
return `
|
|
@@ -86113,6 +86284,309 @@ function openRelationsDetailPanel(deviceId, deviceName, direction, rels, state6,
|
|
|
86113
86284
|
renderPanel();
|
|
86114
86285
|
void t;
|
|
86115
86286
|
}
|
|
86287
|
+
function _setupCfStatusTooltip(container, modalId, colors2) {
|
|
86288
|
+
const TOOLTIP_ID = `${modalId}-cf-status-tip`;
|
|
86289
|
+
let destroyTimer = null;
|
|
86290
|
+
function ensureTip() {
|
|
86291
|
+
let tip2 = document.getElementById(TOOLTIP_ID);
|
|
86292
|
+
if (tip2) return tip2;
|
|
86293
|
+
tip2 = document.createElement("div");
|
|
86294
|
+
tip2.id = TOOLTIP_ID;
|
|
86295
|
+
tip2.style.cssText = `
|
|
86296
|
+
position:fixed; z-index:99990; display:none;
|
|
86297
|
+
background:${colors2.cardBg}; border:1px solid ${colors2.border};
|
|
86298
|
+
border-radius:10px; box-shadow:0 8px 28px rgba(0,0,0,0.26);
|
|
86299
|
+
padding:12px 14px; max-width:340px; font-size:11px; color:${colors2.text};
|
|
86300
|
+
line-height:1.6; pointer-events:auto;
|
|
86301
|
+
`;
|
|
86302
|
+
document.body.appendChild(tip2);
|
|
86303
|
+
return tip2;
|
|
86304
|
+
}
|
|
86305
|
+
function showTip(el2, status, deviceName, detail) {
|
|
86306
|
+
if (destroyTimer) {
|
|
86307
|
+
clearTimeout(destroyTimer);
|
|
86308
|
+
destroyTimer = null;
|
|
86309
|
+
}
|
|
86310
|
+
const tip2 = ensureTip();
|
|
86311
|
+
const STATUS_LABEL = { mismatch: "\u26A0\uFE0F Mismatch", missing: "\u274C Missing", undefined: "\u2753 Undefined" };
|
|
86312
|
+
const STATUS_COLOR_TIP = { mismatch: colors2.warning, missing: colors2.danger, undefined: colors2.textMuted };
|
|
86313
|
+
const lines = detail.split("\n").filter(Boolean);
|
|
86314
|
+
tip2.innerHTML = `
|
|
86315
|
+
<div style="font-weight:700; font-size:12px; color:${STATUS_COLOR_TIP[status] || colors2.text}; margin-bottom:6px;">${STATUS_LABEL[status] || status}</div>
|
|
86316
|
+
<div style="font-size:10px; color:${colors2.textMuted}; margin-bottom:8px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" title="${deviceName}">${deviceName}</div>
|
|
86317
|
+
<div style="border-top:1px solid ${colors2.border}; padding-top:8px;">
|
|
86318
|
+
${lines.map((l) => `<div style="margin-bottom:3px; font-family:monospace; font-size:10px;">${l}</div>`).join("")}
|
|
86319
|
+
</div>
|
|
86320
|
+
`;
|
|
86321
|
+
const rect = el2.getBoundingClientRect();
|
|
86322
|
+
const tipW = 340, tipH = 80;
|
|
86323
|
+
let left = rect.left;
|
|
86324
|
+
let top = rect.bottom + 6;
|
|
86325
|
+
if (left + tipW > window.innerWidth - 10) left = window.innerWidth - tipW - 10;
|
|
86326
|
+
if (top + tipH > window.innerHeight - 10) top = rect.top - tipH - 6;
|
|
86327
|
+
tip2.style.left = `${left}px`;
|
|
86328
|
+
tip2.style.top = `${top}px`;
|
|
86329
|
+
tip2.style.display = "block";
|
|
86330
|
+
}
|
|
86331
|
+
function hideTip(force = false) {
|
|
86332
|
+
if (force) {
|
|
86333
|
+
const tip2 = document.getElementById(TOOLTIP_ID);
|
|
86334
|
+
if (tip2) tip2.style.display = "none";
|
|
86335
|
+
if (destroyTimer) {
|
|
86336
|
+
clearTimeout(destroyTimer);
|
|
86337
|
+
destroyTimer = null;
|
|
86338
|
+
}
|
|
86339
|
+
return;
|
|
86340
|
+
}
|
|
86341
|
+
destroyTimer = setTimeout(() => {
|
|
86342
|
+
const tip2 = document.getElementById(TOOLTIP_ID);
|
|
86343
|
+
if (tip2) tip2.style.display = "none";
|
|
86344
|
+
destroyTimer = null;
|
|
86345
|
+
}, 2e3);
|
|
86346
|
+
}
|
|
86347
|
+
container.querySelectorAll("[data-cf-status]").forEach((cell) => {
|
|
86348
|
+
const el2 = cell;
|
|
86349
|
+
const status = el2.dataset.cfStatus;
|
|
86350
|
+
const device = decodeURIComponent(el2.dataset.cfDevice || "");
|
|
86351
|
+
const detail = decodeURIComponent(el2.dataset.cfDetail || "");
|
|
86352
|
+
el2.addEventListener("mouseenter", () => showTip(el2, status, device, detail));
|
|
86353
|
+
el2.addEventListener("mouseleave", () => hideTip());
|
|
86354
|
+
el2.addEventListener("focus", () => {
|
|
86355
|
+
if (destroyTimer) {
|
|
86356
|
+
clearTimeout(destroyTimer);
|
|
86357
|
+
destroyTimer = null;
|
|
86358
|
+
}
|
|
86359
|
+
showTip(el2, status, device, detail);
|
|
86360
|
+
});
|
|
86361
|
+
el2.addEventListener("blur", () => hideTip());
|
|
86362
|
+
});
|
|
86363
|
+
const tip = ensureTip();
|
|
86364
|
+
tip.addEventListener("mouseenter", () => {
|
|
86365
|
+
if (destroyTimer) {
|
|
86366
|
+
clearTimeout(destroyTimer);
|
|
86367
|
+
destroyTimer = null;
|
|
86368
|
+
}
|
|
86369
|
+
});
|
|
86370
|
+
tip.addEventListener("mouseleave", () => hideTip());
|
|
86371
|
+
}
|
|
86372
|
+
var GCDR_CLEAR_DEVICE_KEYS = ["gcdrDeviceId", "gcdrId", "gcdrSyncAt", "gcdrAssetId", "gcdrCustomerId"];
|
|
86373
|
+
var GCDR_CLEAR_ASSET_KEYS = ["gcdrAssetId", "gcdrId", "gcdrSyncAt", "gcdrParentAssetId", "gcdrCustomerId"];
|
|
86374
|
+
async function openClearGcdrIdsModal(state6) {
|
|
86375
|
+
if (!state6.selectedCustomer) return;
|
|
86376
|
+
const customerId = getEntityId(state6.selectedCustomer);
|
|
86377
|
+
const customerName = state6.selectedCustomer.name || state6.selectedCustomer.title || customerId;
|
|
86378
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
86379
|
+
const overlay = document.createElement("div");
|
|
86380
|
+
overlay.style.cssText = `
|
|
86381
|
+
position:fixed; inset:0; z-index:99999;
|
|
86382
|
+
background:rgba(0,0,0,0.55); backdrop-filter:blur(4px);
|
|
86383
|
+
display:flex; align-items:center; justify-content:center;
|
|
86384
|
+
font-family:'Roboto',Inter,system-ui,sans-serif;
|
|
86385
|
+
`;
|
|
86386
|
+
overlay.addEventListener("click", (e) => {
|
|
86387
|
+
if (e.target === overlay) close();
|
|
86388
|
+
});
|
|
86389
|
+
document.body.appendChild(overlay);
|
|
86390
|
+
function close() {
|
|
86391
|
+
if (overlay.parentNode) overlay.parentNode.removeChild(overlay);
|
|
86392
|
+
}
|
|
86393
|
+
function renderShell(bodyHtml, footerHtml) {
|
|
86394
|
+
overlay.innerHTML = `
|
|
86395
|
+
<div style="
|
|
86396
|
+
background:#1e1e2e; color:#e5e7eb; border:1px solid rgba(255,255,255,0.1);
|
|
86397
|
+
border-radius:14px; box-shadow:0 16px 48px rgba(0,0,0,0.4);
|
|
86398
|
+
max-width:860px; width:92%; max-height:85vh; display:flex; flex-direction:column;
|
|
86399
|
+
">
|
|
86400
|
+
<div style="padding:18px 22px; border-bottom:1px solid rgba(255,255,255,0.08); display:flex; justify-content:space-between; align-items:flex-start; flex-shrink:0;">
|
|
86401
|
+
<div>
|
|
86402
|
+
<div style="font-size:16px; font-weight:700; color:#f9fafb;">\u{1F9F9} Clear GCDR IDs</div>
|
|
86403
|
+
<div style="font-size:12px; color:#9ca3af; margin-top:2px;">Customer: ${customerName}</div>
|
|
86404
|
+
</div>
|
|
86405
|
+
<button id="gcdr-clear-x" style="background:none;border:none;color:#9ca3af;font-size:20px;cursor:pointer;padding:4px;line-height:1;">\u2715</button>
|
|
86406
|
+
</div>
|
|
86407
|
+
<div id="gcdr-clear-body" style="padding:18px 22px; overflow-y:auto; flex:1;">${bodyHtml}</div>
|
|
86408
|
+
<div id="gcdr-clear-footer" style="padding:14px 22px; border-top:1px solid rgba(255,255,255,0.08); display:flex; justify-content:flex-end; gap:10px; flex-shrink:0;">${footerHtml}</div>
|
|
86409
|
+
</div>`;
|
|
86410
|
+
overlay.querySelector("#gcdr-clear-x")?.addEventListener("click", close);
|
|
86411
|
+
}
|
|
86412
|
+
function btn(id, label, primary = false, disabled = false) {
|
|
86413
|
+
return `<button id="${id}" style="
|
|
86414
|
+
padding:8px 18px; border-radius:6px; font-size:13px; font-weight:600; cursor:${disabled ? "not-allowed" : "pointer"};
|
|
86415
|
+
${primary ? "background:#dc2626; color:white; border:none;" : "background:transparent; color:#9ca3af; border:1px solid rgba(255,255,255,0.15);"}
|
|
86416
|
+
opacity:${disabled ? "0.5" : "1"};
|
|
86417
|
+
" ${disabled ? "disabled" : ""}>${label}</button>`;
|
|
86418
|
+
}
|
|
86419
|
+
function progressHtml(phase, done, total) {
|
|
86420
|
+
const pct = total > 0 ? Math.round(done / total * 100) : 0;
|
|
86421
|
+
return `
|
|
86422
|
+
<div style="padding:8px 0;">
|
|
86423
|
+
<div style="font-size:13px; font-weight:600; color:#d1d5db; margin-bottom:14px;">\u23F3 ${phase}</div>
|
|
86424
|
+
<div style="background:rgba(255,255,255,0.1); border-radius:8px; height:8px; overflow:hidden; margin-bottom:6px;">
|
|
86425
|
+
<div style="background:#dc2626; height:100%; width:${pct}%; border-radius:8px; transition:width 0.2s;"></div>
|
|
86426
|
+
</div>
|
|
86427
|
+
<div style="display:flex; justify-content:space-between; font-size:12px; color:#9ca3af;">
|
|
86428
|
+
<span>${done} / ${total} entidades</span><span style="font-weight:700; color:#dc2626;">${pct}%</span>
|
|
86429
|
+
</div>
|
|
86430
|
+
</div>`;
|
|
86431
|
+
}
|
|
86432
|
+
function keyBadges(present, keys) {
|
|
86433
|
+
return keys.filter((k) => present[k]).map((k) => `<span style="font-size:9px; background:rgba(220,38,38,0.25); color:#fca5a5; padding:1px 5px; border-radius:3px; margin:1px; display:inline-block;">${k}</span>`).join("");
|
|
86434
|
+
}
|
|
86435
|
+
function tableHtml(rows, keys) {
|
|
86436
|
+
if (rows.length === 0) return '<div style="font-size:12px;color:#6b7280;padding:6px 0;">Nenhum item a limpar.</div>';
|
|
86437
|
+
return `<div style="overflow-x:auto; margin-top:4px;">
|
|
86438
|
+
<table style="border-collapse:collapse; width:100%; font-size:11px;">
|
|
86439
|
+
<thead><tr>
|
|
86440
|
+
<th style="text-align:left; padding:5px 8px; border-bottom:1px solid rgba(255,255,255,0.08); color:#9ca3af; font-weight:600;">Nome TB</th>
|
|
86441
|
+
<th style="text-align:left; padding:5px 8px; border-bottom:1px solid rgba(255,255,255,0.08); color:#9ca3af; font-weight:600;">Chaves a remover</th>
|
|
86442
|
+
</tr></thead>
|
|
86443
|
+
<tbody>${rows.map((r) => `<tr>
|
|
86444
|
+
<td style="padding:5px 8px; border-bottom:1px solid rgba(255,255,255,0.05); color:#d1d5db;" title="${r.tbId}">
|
|
86445
|
+
${r.name}<br><span style="color:#6b7280; font-family:monospace; font-size:9px;">${r.tbId.substring(0, 8)}\u2026</span>
|
|
86446
|
+
</td>
|
|
86447
|
+
<td style="padding:5px 8px; border-bottom:1px solid rgba(255,255,255,0.05);">${keyBadges(r.present, keys)}</td>
|
|
86448
|
+
</tr>`).join("")}</tbody>
|
|
86449
|
+
</table>
|
|
86450
|
+
</div>`;
|
|
86451
|
+
}
|
|
86452
|
+
function previewHtml(deviceRows, assetRows) {
|
|
86453
|
+
const devToClear = deviceRows.filter((r) => r.hasAny);
|
|
86454
|
+
const assetToClear = assetRows.filter((r) => r.hasAny);
|
|
86455
|
+
const badge = (txt, danger = false) => `<span style="font-size:11px; padding:3px 10px; border-radius:4px; background:${danger ? "rgba(220,38,38,0.2)" : "rgba(255,255,255,0.07)"}; color:${danger ? "#fca5a5" : "#9ca3af"};">${txt}</span>`;
|
|
86456
|
+
return `
|
|
86457
|
+
<div style="display:flex; gap:8px; flex-wrap:wrap; margin-bottom:14px;">
|
|
86458
|
+
${badge(`\u{1F9F9} ${devToClear.length} devices a limpar`, devToClear.length > 0)}
|
|
86459
|
+
${badge(`\u2713 ${deviceRows.length - devToClear.length} devices j\xE1 limpos`)}
|
|
86460
|
+
${badge(`\u{1F9F9} ${assetToClear.length} assets a limpar`, assetToClear.length > 0)}
|
|
86461
|
+
${badge(`\u2713 ${assetRows.length - assetToClear.length} assets j\xE1 limpos`)}
|
|
86462
|
+
</div>
|
|
86463
|
+
<div style="font-size:10px; font-weight:700; color:#9ca3af; text-transform:uppercase; letter-spacing:.5px; margin-bottom:4px;">
|
|
86464
|
+
\u{1F4DF} Devices (${deviceRows.length})
|
|
86465
|
+
<span style="font-size:9px; font-weight:400; text-transform:none; margin-left:6px;">chaves: ${GCDR_CLEAR_DEVICE_KEYS.join(", ")}</span>
|
|
86466
|
+
</div>
|
|
86467
|
+
${tableHtml(devToClear, GCDR_CLEAR_DEVICE_KEYS)}
|
|
86468
|
+
<div style="font-size:10px; font-weight:700; color:#9ca3af; text-transform:uppercase; letter-spacing:.5px; margin:14px 0 4px;">
|
|
86469
|
+
\u{1F4C1} Assets (${assetRows.length})
|
|
86470
|
+
<span style="font-size:9px; font-weight:400; text-transform:none; margin-left:6px;">chaves: ${GCDR_CLEAR_ASSET_KEYS.join(", ")}</span>
|
|
86471
|
+
</div>
|
|
86472
|
+
${tableHtml(assetToClear, GCDR_CLEAR_ASSET_KEYS)}
|
|
86473
|
+
${devToClear.length + assetToClear.length === 0 ? '<div style="text-align:center; color:#6b7280; padding:16px 0; font-size:13px;">\u2705 Nenhum device ou asset possui essas chaves. Nada a limpar.</div>' : ""}`;
|
|
86474
|
+
}
|
|
86475
|
+
renderShell(progressHtml("Buscando devices e assets\u2026", 0, 0), "");
|
|
86476
|
+
try {
|
|
86477
|
+
const [devicesResp, assetsResp] = await Promise.all([
|
|
86478
|
+
tbFetch(state6, `/api/customer/${customerId}/devices?pageSize=1000&page=0`),
|
|
86479
|
+
tbFetch(state6, `/api/customer/${customerId}/assets?pageSize=1000&page=0`)
|
|
86480
|
+
]);
|
|
86481
|
+
const tbDevices = devicesResp.data || [];
|
|
86482
|
+
const tbAssets = assetsResp.data || [];
|
|
86483
|
+
const total = tbDevices.length + tbAssets.length;
|
|
86484
|
+
const deviceRows = [];
|
|
86485
|
+
const assetRows = [];
|
|
86486
|
+
async function processBatch(items, entityType, keysToCheck, targetRows, fetchAttrs) {
|
|
86487
|
+
const chunks = [];
|
|
86488
|
+
for (let i = 0; i < items.length; i += 10) chunks.push(items.slice(i, i + 10));
|
|
86489
|
+
for (let ci = 0; ci < chunks.length; ci++) {
|
|
86490
|
+
if (ci > 0) await sleep(800);
|
|
86491
|
+
await Promise.all(chunks[ci].map(async (entity) => {
|
|
86492
|
+
const tbId = entity.id?.id || "";
|
|
86493
|
+
const name = entity.name || entity.label || tbId;
|
|
86494
|
+
let present = {};
|
|
86495
|
+
try {
|
|
86496
|
+
const attrs = await fetchAttrs(tbId);
|
|
86497
|
+
for (const k of keysToCheck) present[k] = attrs[k] != null && attrs[k] !== "";
|
|
86498
|
+
} catch {
|
|
86499
|
+
}
|
|
86500
|
+
targetRows.push({ tbId, name, entityType, present, hasAny: keysToCheck.some((k) => present[k]) });
|
|
86501
|
+
}));
|
|
86502
|
+
const done = deviceRows.length + assetRows.length;
|
|
86503
|
+
const bodyEl = overlay.querySelector("#gcdr-clear-body");
|
|
86504
|
+
if (bodyEl) bodyEl.innerHTML = progressHtml(`Lendo SERVER_SCOPE (${entityType})\u2026`, done, total);
|
|
86505
|
+
}
|
|
86506
|
+
}
|
|
86507
|
+
const fetchDeviceAttrs = async (id) => {
|
|
86508
|
+
const data = await tbFetch(
|
|
86509
|
+
state6,
|
|
86510
|
+
`/api/plugins/telemetry/DEVICE/${id}/values/attributes/SERVER_SCOPE`
|
|
86511
|
+
);
|
|
86512
|
+
return Object.fromEntries(data.map((a) => [a.key, a.value]));
|
|
86513
|
+
};
|
|
86514
|
+
const fetchAssetAttrs = async (id) => {
|
|
86515
|
+
const data = await tbFetch(
|
|
86516
|
+
state6,
|
|
86517
|
+
`/api/plugins/telemetry/ASSET/${id}/values/attributes/SERVER_SCOPE`
|
|
86518
|
+
);
|
|
86519
|
+
return Object.fromEntries(data.map((a) => [a.key, a.value]));
|
|
86520
|
+
};
|
|
86521
|
+
await processBatch(tbDevices, "DEVICE", GCDR_CLEAR_DEVICE_KEYS, deviceRows, fetchDeviceAttrs);
|
|
86522
|
+
await processBatch(tbAssets, "ASSET", GCDR_CLEAR_ASSET_KEYS, assetRows, fetchAssetAttrs);
|
|
86523
|
+
const devToClear = deviceRows.filter((r) => r.hasAny);
|
|
86524
|
+
const assetToClear = assetRows.filter((r) => r.hasAny);
|
|
86525
|
+
const totalToClear = devToClear.length + assetToClear.length;
|
|
86526
|
+
renderShell(
|
|
86527
|
+
previewHtml(deviceRows, assetRows),
|
|
86528
|
+
btn("gcdr-clear-cancel", "Cancelar") + btn("gcdr-clear-apply", `\u{1F9F9} Limpar ${totalToClear} entidade${totalToClear !== 1 ? "s" : ""}`, true, totalToClear === 0)
|
|
86529
|
+
);
|
|
86530
|
+
overlay.querySelector("#gcdr-clear-cancel")?.addEventListener("click", close);
|
|
86531
|
+
overlay.querySelector("#gcdr-clear-apply")?.addEventListener("click", async () => {
|
|
86532
|
+
let done3 = 0;
|
|
86533
|
+
overlay.querySelector("#gcdr-clear-footer").innerHTML = "";
|
|
86534
|
+
overlay.querySelector("#gcdr-clear-body").innerHTML = progressHtml("Removendo atributos GCDR\u2026", 0, totalToClear);
|
|
86535
|
+
async function execClear(rows, keys, entityType) {
|
|
86536
|
+
const results = [];
|
|
86537
|
+
const chunks = [];
|
|
86538
|
+
for (let i = 0; i < rows.length; i += 10) chunks.push(rows.slice(i, i + 10));
|
|
86539
|
+
for (let ci = 0; ci < chunks.length; ci++) {
|
|
86540
|
+
if (ci > 0) await sleep(800);
|
|
86541
|
+
await Promise.all(chunks[ci].map(async (row) => {
|
|
86542
|
+
const keysToDelete = keys.filter((k) => row.present[k]);
|
|
86543
|
+
try {
|
|
86544
|
+
await tbDelete(state6, `/api/plugins/telemetry/${entityType}/${row.tbId}/values/attributes/SERVER_SCOPE?keys=${encodeURIComponent(keysToDelete.join(","))}`);
|
|
86545
|
+
results.push({ name: row.name, ok: true, cleared: keysToDelete });
|
|
86546
|
+
} catch (err2) {
|
|
86547
|
+
results.push({ name: row.name, ok: false, error: err2.message });
|
|
86548
|
+
}
|
|
86549
|
+
done3++;
|
|
86550
|
+
const progEl = overlay.querySelector("#gcdr-clear-body");
|
|
86551
|
+
if (progEl) progEl.innerHTML = progressHtml("Removendo atributos GCDR\u2026", done3, totalToClear);
|
|
86552
|
+
}));
|
|
86553
|
+
}
|
|
86554
|
+
return results;
|
|
86555
|
+
}
|
|
86556
|
+
const devResults = await execClear(devToClear, GCDR_CLEAR_DEVICE_KEYS, "DEVICE");
|
|
86557
|
+
const assetResults = await execClear(assetToClear, GCDR_CLEAR_ASSET_KEYS, "ASSET");
|
|
86558
|
+
const allResults = [...devResults, ...assetResults];
|
|
86559
|
+
const ok = allResults.filter((r) => r.ok).length;
|
|
86560
|
+
const err = allResults.filter((r) => !r.ok).length;
|
|
86561
|
+
const resultHtml = `
|
|
86562
|
+
<div style="display:flex; gap:8px; margin-bottom:14px;">
|
|
86563
|
+
<span style="font-size:12px; padding:3px 10px; border-radius:4px; background:rgba(16,185,129,0.2); color:#6ee7b7;">\u2713 ${ok} limpos</span>
|
|
86564
|
+
${err > 0 ? `<span style="font-size:12px; padding:3px 10px; border-radius:4px; background:rgba(220,38,38,0.2); color:#fca5a5;">\u2717 ${err} erros</span>` : ""}
|
|
86565
|
+
</div>
|
|
86566
|
+
${[{ title: "\u{1F4DF} Devices", results: devResults }, { title: "\u{1F4C1} Assets", results: assetResults }].filter((s) => s.results.length > 0).map((s) => `
|
|
86567
|
+
<div style="font-size:10px; font-weight:700; color:#9ca3af; text-transform:uppercase; margin:10px 0 4px;">${s.title}</div>
|
|
86568
|
+
<ul style="list-style:none; margin:0; padding:0;">
|
|
86569
|
+
${s.results.map((r) => `
|
|
86570
|
+
<li style="display:flex; align-items:flex-start; gap:8px; padding:5px 0; border-bottom:1px solid rgba(255,255,255,0.05);">
|
|
86571
|
+
<span>${r.ok ? "\u2705" : "\u274C"}</span>
|
|
86572
|
+
<div>
|
|
86573
|
+
<div style="font-size:11px; color:#d1d5db;">${r.name}</div>
|
|
86574
|
+
<div style="font-size:10px; color:${r.ok ? "#6ee7b7" : "#fca5a5"};">
|
|
86575
|
+
${r.ok ? `Removidas: ${r.cleared?.join(", ")}` : r.error}
|
|
86576
|
+
</div>
|
|
86577
|
+
</div>
|
|
86578
|
+
</li>`).join("")}
|
|
86579
|
+
</ul>`).join("")}`;
|
|
86580
|
+
overlay.querySelector("#gcdr-clear-body").innerHTML = resultHtml;
|
|
86581
|
+
overlay.querySelector("#gcdr-clear-footer").innerHTML = btn("gcdr-clear-done", "Fechar");
|
|
86582
|
+
overlay.querySelector("#gcdr-clear-done")?.addEventListener("click", close);
|
|
86583
|
+
});
|
|
86584
|
+
} catch (err) {
|
|
86585
|
+
overlay.querySelector("#gcdr-clear-body").innerHTML = `<div style="color:#fca5a5; font-size:13px; padding:8px 0;">\u274C ${err.message}</div>`;
|
|
86586
|
+
overlay.querySelector("#gcdr-clear-footer").innerHTML = btn("gcdr-clear-err-close", "Fechar");
|
|
86587
|
+
overlay.querySelector("#gcdr-clear-err-close")?.addEventListener("click", close);
|
|
86588
|
+
}
|
|
86589
|
+
}
|
|
86116
86590
|
function setupEventListeners3(container, state6, modalId, t, onClose) {
|
|
86117
86591
|
const closeHandler = () => closeModal(container, onClose);
|
|
86118
86592
|
const overlay = container.querySelector(".myio-upsell-modal-overlay");
|
|
@@ -86312,6 +86786,17 @@ function setupEventListeners3(container, state6, modalId, t, onClose) {
|
|
|
86312
86786
|
if (e.key === "Escape") closeHandler();
|
|
86313
86787
|
};
|
|
86314
86788
|
document.addEventListener("keydown", escHandler);
|
|
86789
|
+
const cfFilterPanelCloseHandler = (e) => {
|
|
86790
|
+
if (!state6.checkFixFilterOpen) return;
|
|
86791
|
+
const btn = document.getElementById(`${modalId}-cf-filter-btn`);
|
|
86792
|
+
const panel = document.getElementById(`${modalId}-cf-filter-panel`);
|
|
86793
|
+
if (btn && !btn.contains(e.target) && panel && !panel.contains(e.target)) {
|
|
86794
|
+
state6.checkFixFilterOpen = false;
|
|
86795
|
+
renderModal4(container, state6, modalId, t);
|
|
86796
|
+
setupEventListeners3(container, state6, modalId, t, onClose);
|
|
86797
|
+
}
|
|
86798
|
+
};
|
|
86799
|
+
document.addEventListener("click", cfFilterPanelCloseHandler);
|
|
86315
86800
|
document.getElementById(`${modalId}-load-relations`)?.addEventListener("click", () => {
|
|
86316
86801
|
void loadDeviceRelations(state6, container, modalId, t, onClose);
|
|
86317
86802
|
});
|
|
@@ -86458,6 +86943,82 @@ function setupEventListeners3(container, state6, modalId, t, onClose) {
|
|
|
86458
86943
|
renderModal4(container, state6, modalId, t);
|
|
86459
86944
|
setupEventListeners3(container, state6, modalId, t, onClose);
|
|
86460
86945
|
});
|
|
86946
|
+
container.querySelectorAll("[data-cf-sort]").forEach((el2) => {
|
|
86947
|
+
el2.addEventListener("click", () => {
|
|
86948
|
+
const field = el2.dataset.cfSort;
|
|
86949
|
+
if (state6.checkFixSort.field === field) {
|
|
86950
|
+
state6.checkFixSort.order = state6.checkFixSort.order === "asc" ? "desc" : "asc";
|
|
86951
|
+
} else {
|
|
86952
|
+
state6.checkFixSort = { field, order: "asc" };
|
|
86953
|
+
}
|
|
86954
|
+
const listEl = document.getElementById(`${modalId}-device-list`);
|
|
86955
|
+
const savedScroll = listEl ? listEl.scrollTop : 0;
|
|
86956
|
+
renderModal4(container, state6, modalId, t);
|
|
86957
|
+
setupEventListeners3(container, state6, modalId, t, onClose);
|
|
86958
|
+
const elAfter = document.getElementById(`${modalId}-device-list`);
|
|
86959
|
+
if (elAfter) elAfter.scrollTop = savedScroll;
|
|
86960
|
+
});
|
|
86961
|
+
});
|
|
86962
|
+
document.getElementById(`${modalId}-cf-filter-btn`)?.addEventListener("click", (e) => {
|
|
86963
|
+
e.stopPropagation();
|
|
86964
|
+
state6.checkFixFilterOpen = !state6.checkFixFilterOpen;
|
|
86965
|
+
renderModal4(container, state6, modalId, t);
|
|
86966
|
+
setupEventListeners3(container, state6, modalId, t, onClose);
|
|
86967
|
+
});
|
|
86968
|
+
document.getElementById(`${modalId}-cf-flt-close`)?.addEventListener("click", () => {
|
|
86969
|
+
state6.checkFixFilterOpen = false;
|
|
86970
|
+
renderModal4(container, state6, modalId, t);
|
|
86971
|
+
setupEventListeners3(container, state6, modalId, t, onClose);
|
|
86972
|
+
});
|
|
86973
|
+
document.getElementById(`${modalId}-cf-flt-clear`)?.addEventListener("click", () => {
|
|
86974
|
+
state6.checkFixAdvancedFilter = { statuses: [], connStatuses: [], domains: [], missingIngestionId: false, missingCentralSlave: false };
|
|
86975
|
+
renderModal4(container, state6, modalId, t);
|
|
86976
|
+
setupEventListeners3(container, state6, modalId, t, onClose);
|
|
86977
|
+
});
|
|
86978
|
+
document.getElementById(`${modalId}-cf-flt-noing`)?.addEventListener("change", (e) => {
|
|
86979
|
+
state6.checkFixAdvancedFilter.missingIngestionId = e.target.checked;
|
|
86980
|
+
renderModal4(container, state6, modalId, t);
|
|
86981
|
+
setupEventListeners3(container, state6, modalId, t, onClose);
|
|
86982
|
+
});
|
|
86983
|
+
document.getElementById(`${modalId}-cf-flt-nocentral`)?.addEventListener("change", (e) => {
|
|
86984
|
+
state6.checkFixAdvancedFilter.missingCentralSlave = e.target.checked;
|
|
86985
|
+
renderModal4(container, state6, modalId, t);
|
|
86986
|
+
setupEventListeners3(container, state6, modalId, t, onClose);
|
|
86987
|
+
});
|
|
86988
|
+
const cfFilterPanel = document.getElementById(`${modalId}-cf-filter-panel`);
|
|
86989
|
+
if (cfFilterPanel) {
|
|
86990
|
+
cfFilterPanel.querySelectorAll("[data-cf-flt-status]").forEach((btn) => {
|
|
86991
|
+
btn.addEventListener("click", () => {
|
|
86992
|
+
const s = btn.dataset.cfFltStatus;
|
|
86993
|
+
const idx = state6.checkFixAdvancedFilter.statuses.indexOf(s);
|
|
86994
|
+
if (idx >= 0) state6.checkFixAdvancedFilter.statuses.splice(idx, 1);
|
|
86995
|
+
else state6.checkFixAdvancedFilter.statuses.push(s);
|
|
86996
|
+
renderModal4(container, state6, modalId, t);
|
|
86997
|
+
setupEventListeners3(container, state6, modalId, t, onClose);
|
|
86998
|
+
});
|
|
86999
|
+
});
|
|
87000
|
+
cfFilterPanel.querySelectorAll("[data-cf-flt-conn]").forEach((btn) => {
|
|
87001
|
+
btn.addEventListener("click", () => {
|
|
87002
|
+
const c = btn.dataset.cfFltConn;
|
|
87003
|
+
const idx = state6.checkFixAdvancedFilter.connStatuses.indexOf(c);
|
|
87004
|
+
if (idx >= 0) state6.checkFixAdvancedFilter.connStatuses.splice(idx, 1);
|
|
87005
|
+
else state6.checkFixAdvancedFilter.connStatuses.push(c);
|
|
87006
|
+
renderModal4(container, state6, modalId, t);
|
|
87007
|
+
setupEventListeners3(container, state6, modalId, t, onClose);
|
|
87008
|
+
});
|
|
87009
|
+
});
|
|
87010
|
+
cfFilterPanel.querySelectorAll("[data-cf-flt-domain]").forEach((btn) => {
|
|
87011
|
+
btn.addEventListener("click", () => {
|
|
87012
|
+
const d = btn.dataset.cfFltDomain;
|
|
87013
|
+
const idx = state6.checkFixAdvancedFilter.domains.indexOf(d);
|
|
87014
|
+
if (idx >= 0) state6.checkFixAdvancedFilter.domains.splice(idx, 1);
|
|
87015
|
+
else state6.checkFixAdvancedFilter.domains.push(d);
|
|
87016
|
+
renderModal4(container, state6, modalId, t);
|
|
87017
|
+
setupEventListeners3(container, state6, modalId, t, onClose);
|
|
87018
|
+
});
|
|
87019
|
+
});
|
|
87020
|
+
}
|
|
87021
|
+
_setupCfStatusTooltip(container, modalId, getThemeColors5(state6.theme));
|
|
86461
87022
|
document.getElementById(`${modalId}-mode-single`)?.addEventListener("click", () => {
|
|
86462
87023
|
if (state6.deviceSelectionMode !== "single") {
|
|
86463
87024
|
state6.deviceSelectionMode = "single";
|
|
@@ -86533,26 +87094,22 @@ function setupEventListeners3(container, state6, modalId, t, onClose) {
|
|
|
86533
87094
|
if (!deviceId) return;
|
|
86534
87095
|
const device = state6.devices.find((d) => d.id?.id === deviceId);
|
|
86535
87096
|
if (!device) return;
|
|
86536
|
-
|
|
86537
|
-
|
|
86538
|
-
if (
|
|
86539
|
-
|
|
86540
|
-
state6.selectedDevices.push(device);
|
|
86541
|
-
}
|
|
86542
|
-
} else {
|
|
86543
|
-
state6.selectedDevices = state6.selectedDevices.filter((d) => d.id?.id !== deviceId);
|
|
87097
|
+
const isChecked = input.checked;
|
|
87098
|
+
if (isChecked) {
|
|
87099
|
+
if (!state6.selectedDevices.some((d) => d.id?.id === deviceId)) {
|
|
87100
|
+
state6.selectedDevices.push(device);
|
|
86544
87101
|
}
|
|
86545
|
-
|
|
86546
|
-
|
|
86547
|
-
|
|
86548
|
-
|
|
86549
|
-
|
|
86550
|
-
|
|
86551
|
-
|
|
86552
|
-
|
|
86553
|
-
|
|
86554
|
-
|
|
86555
|
-
|
|
87102
|
+
} else {
|
|
87103
|
+
state6.selectedDevices = state6.selectedDevices.filter((d) => d.id?.id !== deviceId);
|
|
87104
|
+
}
|
|
87105
|
+
const listEl = document.getElementById(`${modalId}-device-list`);
|
|
87106
|
+
const savedScroll = listEl ? listEl.scrollTop : 0;
|
|
87107
|
+
const containerScrollTop = container.scrollTop;
|
|
87108
|
+
renderModal4(container, state6, modalId, t);
|
|
87109
|
+
setupEventListeners3(container, state6, modalId, t, onClose);
|
|
87110
|
+
const elAfter = document.getElementById(`${modalId}-device-list`);
|
|
87111
|
+
if (elAfter) elAfter.scrollTop = savedScroll;
|
|
87112
|
+
container.scrollTop = containerScrollTop;
|
|
86556
87113
|
};
|
|
86557
87114
|
});
|
|
86558
87115
|
document.getElementById(`${modalId}-bulk-attr`)?.addEventListener("click", () => {
|
|
@@ -86671,6 +87228,13 @@ function setupEventListeners3(container, state6, modalId, t, onClose) {
|
|
|
86671
87228
|
if (!state6.selectedCustomer || state6.selectedDevices.length === 0) return;
|
|
86672
87229
|
await handleBulkSyncIngestionId(state6, container, modalId, t, onClose);
|
|
86673
87230
|
});
|
|
87231
|
+
document.getElementById(`${modalId}-clear-gcdr-ids`)?.addEventListener("click", () => {
|
|
87232
|
+
if (!state6.selectedCustomer) {
|
|
87233
|
+
alert("Selecione um Customer primeiro no Step 1");
|
|
87234
|
+
return;
|
|
87235
|
+
}
|
|
87236
|
+
void openClearGcdrIdsModal(state6);
|
|
87237
|
+
});
|
|
86674
87238
|
document.getElementById(`${modalId}-bulk-relation`)?.addEventListener("click", () => {
|
|
86675
87239
|
if (!state6.selectedCustomer) {
|
|
86676
87240
|
alert("Selecione um Customer primeiro no Step 1");
|
|
@@ -87175,7 +87739,9 @@ function filterDeviceListVisual(container, devices, search, filters, sort) {
|
|
|
87175
87739
|
return;
|
|
87176
87740
|
}
|
|
87177
87741
|
const matchesSearch = !search || device.name?.toLowerCase().includes(search) || device.label?.toLowerCase().includes(search) || device.type?.toLowerCase().includes(search) || device.serverAttrs?.deviceType?.toLowerCase().includes(search) || device.serverAttrs?.deviceProfile?.toLowerCase().includes(search);
|
|
87178
|
-
|
|
87742
|
+
const el2 = item;
|
|
87743
|
+
const isTableRow = el2.tagName === "TR";
|
|
87744
|
+
el2.style.display = matchesSearch ? isTableRow ? "" : "flex" : "none";
|
|
87179
87745
|
});
|
|
87180
87746
|
}
|
|
87181
87747
|
async function tbFetch(state6, path) {
|
|
@@ -88789,7 +89355,7 @@ var UserListTab = class {
|
|
|
88789
89355
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
88790
89356
|
try {
|
|
88791
89357
|
const searchRes = await fetch(
|
|
88792
|
-
`${base}/
|
|
89358
|
+
`${base}/users?search=${encodeURIComponent(user.email)}&customerId=${encodeURIComponent(this.config.customerId)}&limit=10`,
|
|
88793
89359
|
{ headers: this.gcdrHeaders() }
|
|
88794
89360
|
);
|
|
88795
89361
|
let gcdrUser = null;
|
|
@@ -88799,7 +89365,7 @@ var UserListTab = class {
|
|
|
88799
89365
|
gcdrUser = items.find((u) => u.email?.toLowerCase() === user.email.toLowerCase()) ?? null;
|
|
88800
89366
|
}
|
|
88801
89367
|
if (!gcdrUser) {
|
|
88802
|
-
const createRes = await fetch(`${base}/
|
|
89368
|
+
const createRes = await fetch(`${base}/users`, {
|
|
88803
89369
|
method: "POST",
|
|
88804
89370
|
headers: this.gcdrHeaders(),
|
|
88805
89371
|
body: JSON.stringify({
|
|
@@ -92182,6 +92748,272 @@ function openUserManagementModal(params) {
|
|
|
92182
92748
|
controller.show();
|
|
92183
92749
|
}
|
|
92184
92750
|
|
|
92751
|
+
// src/components/gcdr-auth/MyIOAuthContext.ts
|
|
92752
|
+
var MyIOAuthContext = class _MyIOAuthContext {
|
|
92753
|
+
allowedActions = /* @__PURE__ */ new Set();
|
|
92754
|
+
deniedActions = /* @__PURE__ */ new Set();
|
|
92755
|
+
snap;
|
|
92756
|
+
constructor(snap) {
|
|
92757
|
+
this.snap = snap;
|
|
92758
|
+
}
|
|
92759
|
+
// ── Public read-only state ────────────────────────────────────────────────
|
|
92760
|
+
get ready() {
|
|
92761
|
+
return this.snap.ready;
|
|
92762
|
+
}
|
|
92763
|
+
get gcdrUserId() {
|
|
92764
|
+
return this.snap.gcdrUserId;
|
|
92765
|
+
}
|
|
92766
|
+
get scope() {
|
|
92767
|
+
return this.snap.scope;
|
|
92768
|
+
}
|
|
92769
|
+
get error() {
|
|
92770
|
+
return this.snap.error;
|
|
92771
|
+
}
|
|
92772
|
+
get allowAll() {
|
|
92773
|
+
return this.snap.allowAll;
|
|
92774
|
+
}
|
|
92775
|
+
get assignments() {
|
|
92776
|
+
return this.snap.assignments;
|
|
92777
|
+
}
|
|
92778
|
+
// ── Permission checks ─────────────────────────────────────────────────────
|
|
92779
|
+
/**
|
|
92780
|
+
* Returns `true` if the current user is allowed to perform `action`.
|
|
92781
|
+
*
|
|
92782
|
+
* Evaluation order (deny-first):
|
|
92783
|
+
* 1. If allowAll (TENANT_ADMIN) → always true
|
|
92784
|
+
* 2. If action (or `*`) is in the deny set → false
|
|
92785
|
+
* 3. If action (or `*`) is in the allow set → true
|
|
92786
|
+
* 4. Otherwise → false (closed-by-default)
|
|
92787
|
+
*
|
|
92788
|
+
* Returns `false` while not yet ready.
|
|
92789
|
+
*/
|
|
92790
|
+
can(action) {
|
|
92791
|
+
if (!this.snap.ready) return false;
|
|
92792
|
+
if (this.snap.allowAll) return true;
|
|
92793
|
+
if (this.deniedActions.has("*") || this.deniedActions.has(action)) return false;
|
|
92794
|
+
return this.allowedActions.has("*") || this.allowedActions.has(action);
|
|
92795
|
+
}
|
|
92796
|
+
/** True if the user can perform **at least one** of the given actions. */
|
|
92797
|
+
canAny(...actions) {
|
|
92798
|
+
return actions.some((a) => this.can(a));
|
|
92799
|
+
}
|
|
92800
|
+
/** True if the user can perform **all** of the given actions. */
|
|
92801
|
+
canAll(...actions) {
|
|
92802
|
+
return actions.every((a) => this.can(a));
|
|
92803
|
+
}
|
|
92804
|
+
// ── Factory ───────────────────────────────────────────────────────────────
|
|
92805
|
+
/**
|
|
92806
|
+
* Initialises the auth context.
|
|
92807
|
+
* Resolves immediately when `allowAll` is set or GCDR is not configured.
|
|
92808
|
+
* Always dispatches `myio:auth-ready` when done (success or error).
|
|
92809
|
+
*/
|
|
92810
|
+
static async init(config) {
|
|
92811
|
+
const scope = config.scope ?? (config.customerId ? `customer:${config.customerId}` : "*");
|
|
92812
|
+
const ctx = new _MyIOAuthContext({
|
|
92813
|
+
ready: false,
|
|
92814
|
+
gcdrUserId: null,
|
|
92815
|
+
scope,
|
|
92816
|
+
allowAll: config.allowAll ?? false,
|
|
92817
|
+
error: null,
|
|
92818
|
+
assignments: []
|
|
92819
|
+
});
|
|
92820
|
+
if (config.allowAll) {
|
|
92821
|
+
ctx.snap.ready = true;
|
|
92822
|
+
ctx._dispatch();
|
|
92823
|
+
return ctx;
|
|
92824
|
+
}
|
|
92825
|
+
if (!config.gcdrApiBaseUrl) {
|
|
92826
|
+
ctx.snap.ready = true;
|
|
92827
|
+
ctx.snap.error = "GCDR not configured";
|
|
92828
|
+
ctx._dispatch();
|
|
92829
|
+
return ctx;
|
|
92830
|
+
}
|
|
92831
|
+
try {
|
|
92832
|
+
const hdrs = ctx._gcdrHeaders(config);
|
|
92833
|
+
const gcdrUserId = await ctx._resolveGcdrUserId(config, hdrs);
|
|
92834
|
+
if (!gcdrUserId) {
|
|
92835
|
+
ctx.snap.error = "GCDR user not found";
|
|
92836
|
+
ctx.snap.ready = true;
|
|
92837
|
+
ctx._dispatch();
|
|
92838
|
+
return ctx;
|
|
92839
|
+
}
|
|
92840
|
+
ctx.snap.gcdrUserId = gcdrUserId;
|
|
92841
|
+
const assignRes = await fetch(
|
|
92842
|
+
`${config.gcdrApiBaseUrl}/authorization/users/${gcdrUserId}/assignments`,
|
|
92843
|
+
{ headers: hdrs }
|
|
92844
|
+
);
|
|
92845
|
+
let allAssignments = [];
|
|
92846
|
+
if (assignRes.ok) {
|
|
92847
|
+
const json = await assignRes.json();
|
|
92848
|
+
allAssignments = Array.isArray(json) ? json : json.assignments ?? [];
|
|
92849
|
+
}
|
|
92850
|
+
const relevant = allAssignments.filter(
|
|
92851
|
+
(a) => a.status === "active" && (a.scope === "*" || a.scope === scope)
|
|
92852
|
+
);
|
|
92853
|
+
ctx.snap.assignments = relevant;
|
|
92854
|
+
if (relevant.length === 0) {
|
|
92855
|
+
ctx.snap.ready = true;
|
|
92856
|
+
ctx._dispatch();
|
|
92857
|
+
return ctx;
|
|
92858
|
+
}
|
|
92859
|
+
const roleKeys = [...new Set(relevant.map((a) => a.roleKey))];
|
|
92860
|
+
const roles = await ctx._fetchRoles(config.gcdrApiBaseUrl, roleKeys, hdrs);
|
|
92861
|
+
const policyKeys = [...new Set(
|
|
92862
|
+
roles.flatMap((r) => [...r.policies ?? [], ...r.policyIds ?? []])
|
|
92863
|
+
)];
|
|
92864
|
+
if (policyKeys.length > 0) {
|
|
92865
|
+
const policies = await ctx._fetchPolicies(config.gcdrApiBaseUrl, policyKeys, hdrs);
|
|
92866
|
+
ctx._applyPolicies(policies);
|
|
92867
|
+
}
|
|
92868
|
+
} catch (err) {
|
|
92869
|
+
ctx.snap.error = err?.message ?? "Auth init failed";
|
|
92870
|
+
}
|
|
92871
|
+
ctx.snap.ready = true;
|
|
92872
|
+
ctx._dispatch();
|
|
92873
|
+
return ctx;
|
|
92874
|
+
}
|
|
92875
|
+
// ── Private helpers ───────────────────────────────────────────────────────
|
|
92876
|
+
_gcdrHeaders(config) {
|
|
92877
|
+
return {
|
|
92878
|
+
"Content-Type": "application/json",
|
|
92879
|
+
"X-API-Key": config.gcdrApiKey || "",
|
|
92880
|
+
"X-Tenant-ID": config.gcdrTenantId || ""
|
|
92881
|
+
};
|
|
92882
|
+
}
|
|
92883
|
+
/**
|
|
92884
|
+
* Two-step GCDR user lookup:
|
|
92885
|
+
* 1. Read `gcdrUserConfigs` from TB SERVER_SCOPE attribute (fast path if previously synced)
|
|
92886
|
+
* 2. Email search via GCDR API
|
|
92887
|
+
*/
|
|
92888
|
+
async _resolveGcdrUserId(config, gcdrHeaders) {
|
|
92889
|
+
if (config.currentUserTbId && config.tbBaseUrl && config.jwtToken) {
|
|
92890
|
+
try {
|
|
92891
|
+
const res = await fetch(
|
|
92892
|
+
`${config.tbBaseUrl}/api/plugins/telemetry/USER/${config.currentUserTbId}/values/attributes/SERVER_SCOPE?keys=gcdrUserConfigs`,
|
|
92893
|
+
{ headers: { "X-Authorization": `Bearer ${config.jwtToken}` } }
|
|
92894
|
+
);
|
|
92895
|
+
if (res.ok) {
|
|
92896
|
+
const attrs = await res.json();
|
|
92897
|
+
const gcdrId = attrs.find((a) => a.key === "gcdrUserConfigs")?.value?.gcdrUserId;
|
|
92898
|
+
if (gcdrId) return gcdrId;
|
|
92899
|
+
}
|
|
92900
|
+
} catch {
|
|
92901
|
+
}
|
|
92902
|
+
}
|
|
92903
|
+
if (!config.currentUserEmail) return null;
|
|
92904
|
+
try {
|
|
92905
|
+
const q = encodeURIComponent(config.currentUserEmail);
|
|
92906
|
+
const cid = config.customerId ? `&customerId=${encodeURIComponent(config.customerId)}` : "";
|
|
92907
|
+
const res = await fetch(
|
|
92908
|
+
`${config.gcdrApiBaseUrl}/api/v1/users?search=${q}${cid}&limit=10`,
|
|
92909
|
+
{ headers: gcdrHeaders }
|
|
92910
|
+
);
|
|
92911
|
+
if (res.ok) {
|
|
92912
|
+
const data = await res.json();
|
|
92913
|
+
const items = Array.isArray(data) ? data : data?.data?.items ?? data?.items ?? [];
|
|
92914
|
+
const found = items.find(
|
|
92915
|
+
(u) => u.email?.toLowerCase() === config.currentUserEmail.toLowerCase()
|
|
92916
|
+
);
|
|
92917
|
+
if (found) return found.id;
|
|
92918
|
+
}
|
|
92919
|
+
} catch {
|
|
92920
|
+
}
|
|
92921
|
+
return null;
|
|
92922
|
+
}
|
|
92923
|
+
/**
|
|
92924
|
+
* Fetches roles — tries GET /roles bulk endpoint first, falls back to per-key.
|
|
92925
|
+
*/
|
|
92926
|
+
async _fetchRoles(base, roleKeys, headers) {
|
|
92927
|
+
try {
|
|
92928
|
+
const res = await fetch(`${base}/roles`, { headers });
|
|
92929
|
+
if (res.ok) {
|
|
92930
|
+
const json = await res.json();
|
|
92931
|
+
const all = Array.isArray(json) ? json : json?.data?.items ?? json?.items ?? json?.data ?? [];
|
|
92932
|
+
return all.filter((r) => roleKeys.includes(r.key ?? r.id));
|
|
92933
|
+
}
|
|
92934
|
+
} catch {
|
|
92935
|
+
}
|
|
92936
|
+
const settled = await Promise.allSettled(
|
|
92937
|
+
roleKeys.map(
|
|
92938
|
+
(key) => fetch(`${base}/roles/${key}`, { headers }).then((r) => r.ok ? r.json() : null)
|
|
92939
|
+
)
|
|
92940
|
+
);
|
|
92941
|
+
return settled.filter((r) => r.status === "fulfilled" && r.value != null).map((r) => r.value);
|
|
92942
|
+
}
|
|
92943
|
+
/**
|
|
92944
|
+
* Fetches policies — tries GET /policies bulk endpoint first, falls back to per-key.
|
|
92945
|
+
*/
|
|
92946
|
+
async _fetchPolicies(base, policyKeys, headers) {
|
|
92947
|
+
try {
|
|
92948
|
+
const res = await fetch(`${base}/policies`, { headers });
|
|
92949
|
+
if (res.ok) {
|
|
92950
|
+
const json = await res.json();
|
|
92951
|
+
const all = Array.isArray(json) ? json : json?.data?.items ?? json?.items ?? json?.data ?? [];
|
|
92952
|
+
return all.filter((p) => policyKeys.includes(p.key ?? p.id));
|
|
92953
|
+
}
|
|
92954
|
+
} catch {
|
|
92955
|
+
}
|
|
92956
|
+
const settled = await Promise.allSettled(
|
|
92957
|
+
policyKeys.map(
|
|
92958
|
+
(key) => fetch(`${base}/policies/${key}`, { headers }).then((r) => r.ok ? r.json() : null)
|
|
92959
|
+
)
|
|
92960
|
+
);
|
|
92961
|
+
return settled.filter((r) => r.status === "fulfilled" && r.value != null).map((r) => r.value);
|
|
92962
|
+
}
|
|
92963
|
+
/**
|
|
92964
|
+
* Merges all policy allow/deny lists into the context's permission sets.
|
|
92965
|
+
* Deny always wins over allow (deny-first strategy).
|
|
92966
|
+
*/
|
|
92967
|
+
_applyPolicies(policies) {
|
|
92968
|
+
for (const policy of policies) {
|
|
92969
|
+
for (const action of policy.allow ?? []) this.allowedActions.add(action);
|
|
92970
|
+
for (const action of policy.deny ?? []) this.deniedActions.add(action);
|
|
92971
|
+
}
|
|
92972
|
+
}
|
|
92973
|
+
/** Dispatches `myio:auth-ready` so widgets can react without polling. */
|
|
92974
|
+
_dispatch() {
|
|
92975
|
+
window.dispatchEvent(
|
|
92976
|
+
new CustomEvent("myio:auth-ready", {
|
|
92977
|
+
detail: {
|
|
92978
|
+
ready: this.snap.ready,
|
|
92979
|
+
scope: this.snap.scope,
|
|
92980
|
+
allowAll: this.snap.allowAll,
|
|
92981
|
+
error: this.snap.error
|
|
92982
|
+
}
|
|
92983
|
+
})
|
|
92984
|
+
);
|
|
92985
|
+
}
|
|
92986
|
+
};
|
|
92987
|
+
async function initMyIOAuthContext(config) {
|
|
92988
|
+
return MyIOAuthContext.init(config);
|
|
92989
|
+
}
|
|
92990
|
+
|
|
92991
|
+
// src/components/gcdr-auth/permissions.ts
|
|
92992
|
+
var PERM = {
|
|
92993
|
+
// ── Alarm actions ────────────────────────────────────────────────────────
|
|
92994
|
+
ALARM_VIEW: "alarm:view",
|
|
92995
|
+
ALARM_ACK: "alarm:ack",
|
|
92996
|
+
ALARM_ESCALATE: "alarm:escalate",
|
|
92997
|
+
ALARM_SNOOZE: "alarm:snooze",
|
|
92998
|
+
ALARM_CLOSE: "alarm:close",
|
|
92999
|
+
// ── Reports ──────────────────────────────────────────────────────────────
|
|
93000
|
+
REPORT_VIEW: "report:view",
|
|
93001
|
+
REPORT_EXPORT: "report:export",
|
|
93002
|
+
// ── Settings modal ───────────────────────────────────────────────────────
|
|
93003
|
+
SETTINGS_VIEW: "settings:view",
|
|
93004
|
+
SETTINGS_EDIT: "settings:edit",
|
|
93005
|
+
// ── User management (GCDR) ───────────────────────────────────────────────
|
|
93006
|
+
USER_VIEW: "user:view",
|
|
93007
|
+
USER_MANAGE: "user:manage",
|
|
93008
|
+
// ── Integration config ───────────────────────────────────────────────────
|
|
93009
|
+
INTEGRATION_VIEW: "integration:view",
|
|
93010
|
+
INTEGRATION_EDIT: "integration:edit",
|
|
93011
|
+
// ── Dashboard config ─────────────────────────────────────────────────────
|
|
93012
|
+
DASHBOARD_CONFIG: "dashboard:configure",
|
|
93013
|
+
// ── Shopping / customer selector ─────────────────────────────────────────
|
|
93014
|
+
SHOPPING_SELECT: "shopping:select"
|
|
93015
|
+
};
|
|
93016
|
+
|
|
92185
93017
|
// src/components/menu/types.ts
|
|
92186
93018
|
var DEFAULT_LIGHT_THEME3 = {
|
|
92187
93019
|
// Tabs
|
|
@@ -139604,6 +140436,7 @@ var version = package_default.version || "0.0.0";
|
|
|
139604
140436
|
MenuShoppingView,
|
|
139605
140437
|
MenuView,
|
|
139606
140438
|
ModalHeader,
|
|
140439
|
+
MyIOAuthContext,
|
|
139607
140440
|
MyIOChartModal,
|
|
139608
140441
|
MyIODraggableCard,
|
|
139609
140442
|
MyIOSelectionStore,
|
|
@@ -139632,6 +140465,7 @@ var version = package_default.version || "0.0.0";
|
|
|
139632
140465
|
OperationalGeneralListController,
|
|
139633
140466
|
OperationalGeneralListView,
|
|
139634
140467
|
OperationalHeaderDevicesGridView,
|
|
140468
|
+
PERM,
|
|
139635
140469
|
POWER_LIMITS_DEVICE_TYPES,
|
|
139636
140470
|
POWER_LIMITS_STATUS_CONFIG,
|
|
139637
140471
|
POWER_LIMITS_TELEMETRY_TYPES,
|
|
@@ -139900,6 +140734,7 @@ var version = package_default.version || "0.0.0";
|
|
|
139900
140734
|
groupByDay,
|
|
139901
140735
|
handleDeviceType,
|
|
139902
140736
|
hasSelectedDays,
|
|
140737
|
+
initMyIOAuthContext,
|
|
139903
140738
|
initOnOffTimelineTooltips,
|
|
139904
140739
|
injectActionButtonStyles,
|
|
139905
140740
|
injectAlarmsNotificationsPanelStyles,
|