myio-js-library 0.1.490 → 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 CHANGED
@@ -1162,7 +1162,7 @@ module.exports = __toCommonJS(index_exports);
1162
1162
  // package.json
1163
1163
  var package_default = {
1164
1164
  name: "myio-js-library",
1165
- version: "0.1.490",
1165
+ version: "0.1.491",
1166
1166
  description: "A clean, standalone JS SDK for MYIO projects",
1167
1167
  license: "MIT",
1168
1168
  repository: "github:gh-myio/myio-js-library",
@@ -84027,7 +84027,10 @@ function openUpsellModal(params) {
84027
84027
  bulkRelationModal: { open: false, target: "CUSTOMER", selectedAssetId: "", selectedAssetName: "", search: "", newAssetName: "", assetsLoaded: false, overrideCustomerId: "", overrideCustomerName: "", customerSearch: "", customerPickerOpen: false },
84028
84028
  checkFixLoading: false,
84029
84029
  checkFixReport: null,
84030
- checkFixFilter: "all"
84030
+ checkFixFilter: "all",
84031
+ checkFixSort: { field: "name", order: "asc" },
84032
+ checkFixAdvancedFilter: { statuses: [], connStatuses: [], domains: [], missingIngestionId: false, missingCentralSlave: false },
84033
+ checkFixFilterOpen: false
84031
84034
  };
84032
84035
  const savedTheme = localStorage.getItem("myio-upsell-modal-theme");
84033
84036
  if (savedTheme) state6.theme = savedTheme;
@@ -84166,6 +84169,12 @@ function renderModal4(container, state6, modalId, t, error) {
84166
84169
  font-size: 14px; font-weight: 500; font-family: 'Roboto', Arial, sans-serif;
84167
84170
  display: flex; align-items: center; gap: 6px;
84168
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>
84169
84178
  <button id="${modalId}-custom-shortcut" style="
84170
84179
  background: #ef4444; color: white; border: none;
84171
84180
  padding: 8px 16px; border-radius: 6px; cursor: pointer;
@@ -84832,6 +84841,29 @@ function renderStep1(state6, modalId, colors2, t) {
84832
84841
  </div>
84833
84842
  `;
84834
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
+ }
84835
84867
  function renderCheckFixRow(r, state6, modalId, colors2, dupPairIds, dupIngestionIds) {
84836
84868
  const isSelectedSingle = state6.deviceSelectionMode === "single" && getEntityId(state6.selectedDevice) === r.deviceId;
84837
84869
  const isSelectedMulti = state6.deviceSelectionMode === "multi" && state6.selectedDevices.some((d) => getEntityId(d) === r.deviceId);
@@ -84895,7 +84927,10 @@ function renderCheckFixRow(r, state6, modalId, colors2, dupPairIds, dupIngestion
84895
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>
84896
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>
84897
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>
84898
- <td style="padding:4px 6px; font-size:10px; color:${STATUS_COLOR[r.status]}; font-weight:700; white-space:nowrap;">${STATUS_ICON[r.status]} ${r.status}</td>
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>
84899
84934
  </tr>`;
84900
84935
  }
84901
84936
  function renderStep2(state6, modalId, colors2, t) {
@@ -85182,7 +85217,42 @@ function renderStep2(state6, modalId, colors2, t) {
85182
85217
  const multiCol = state6.deviceSelectionMode === "multi";
85183
85218
  if (state6.checkFixReport) {
85184
85219
  const idToRecord = new Map(state6.checkFixReport.records.map((r) => [r.deviceId, r]));
85185
- const cfRows = sortedDevices.map((d) => idToRecord.get(getEntityId(d))).filter((r) => r !== void 0).filter((r) => state6.checkFixFilter === "all" || r.status === state6.checkFixFilter);
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
+ });
85186
85256
  const pairCount = /* @__PURE__ */ new Map();
85187
85257
  cfRows.forEach((r) => {
85188
85258
  if (r.centralId && r.slaveId != null) {
@@ -85207,27 +85277,115 @@ function renderStep2(state6, modalId, colors2, t) {
85207
85277
  if (ids.length > 1) ids.forEach((id) => dupIngestionIds.add(id));
85208
85278
  });
85209
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 &amp; 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
+ ` : "";
85210
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>
85211
85353
  <div id="${modalId}-device-list" style="
85212
85354
  overflow-x:auto; max-height:${gridHeight};
85213
85355
  border:1px solid ${colors2.border}; border-radius:8px; background:${colors2.surface};
85214
85356
  ">
85215
- <table style="border-collapse:collapse; min-width:100%;">
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>
85216
85374
  <thead>
85217
85375
  <tr>
85218
85376
  ${multiCol ? `<th rowspan="2" style="${thStyle("width:28px")}">\u2611</th>` : ""}
85219
- <th rowspan="2" style="${thStyle("min-width:140px")}">Name</th>
85377
+ ${cfSortHeader("name", "Name", 2, "min-width:140px")}
85220
85378
  <th rowspan="2" style="${thStyle("min-width:90px")}">Label</th>
85221
85379
  <th colspan="2" style="${thStyle("text-align:center")}">TYPE</th>
85222
85380
  <th colspan="2" style="${thStyle("text-align:center")}">DevType</th>
85223
85381
  <th colspan="2" style="${thStyle("text-align:center")}">DevProfile</th>
85224
85382
  <th colspan="2" style="${thStyle("text-align:center")}">Last Telemetry</th>
85225
- <th rowspan="2" style="${thStyle("min-width:65px")}">Conn.</th>
85383
+ ${cfSortHeader("connStatus", "Conn.", 2, "min-width:65px")}
85226
85384
  <th rowspan="2" style="${thStyle("min-width:110px")}">gcdrId</th>
85227
- <th rowspan="2" style="${thStyle("min-width:110px")}">ingestionId</th>
85385
+ ${cfSortHeader("ingestionId", "ingestionId", 2, "min-width:110px")}
85228
85386
  <th rowspan="2" style="${thStyle("min-width:80px")}">centralId</th>
85229
85387
  <th rowspan="2" style="${thStyle("min-width:50px")}">slave</th>
85230
- <th rowspan="2" style="${thStyle()}">Result</th>
85388
+ ${cfSortHeader("status", "Result", 2)}
85231
85389
  </tr>
85232
85390
  <tr>
85233
85391
  <th style="${thStyle()}">Exp</th><th style="${thStyle()}">Act</th>
@@ -85241,6 +85399,7 @@ function renderStep2(state6, modalId, colors2, t) {
85241
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("")}
85242
85400
  </tbody>
85243
85401
  </table>
85402
+ </div>
85244
85403
  </div>`;
85245
85404
  }
85246
85405
  return `
@@ -86125,6 +86284,309 @@ function openRelationsDetailPanel(deviceId, deviceName, direction, rels, state6,
86125
86284
  renderPanel();
86126
86285
  void t;
86127
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
+ }
86128
86590
  function setupEventListeners3(container, state6, modalId, t, onClose) {
86129
86591
  const closeHandler = () => closeModal(container, onClose);
86130
86592
  const overlay = container.querySelector(".myio-upsell-modal-overlay");
@@ -86324,6 +86786,17 @@ function setupEventListeners3(container, state6, modalId, t, onClose) {
86324
86786
  if (e.key === "Escape") closeHandler();
86325
86787
  };
86326
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);
86327
86800
  document.getElementById(`${modalId}-load-relations`)?.addEventListener("click", () => {
86328
86801
  void loadDeviceRelations(state6, container, modalId, t, onClose);
86329
86802
  });
@@ -86470,6 +86943,82 @@ function setupEventListeners3(container, state6, modalId, t, onClose) {
86470
86943
  renderModal4(container, state6, modalId, t);
86471
86944
  setupEventListeners3(container, state6, modalId, t, onClose);
86472
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));
86473
87022
  document.getElementById(`${modalId}-mode-single`)?.addEventListener("click", () => {
86474
87023
  if (state6.deviceSelectionMode !== "single") {
86475
87024
  state6.deviceSelectionMode = "single";
@@ -86545,26 +87094,22 @@ function setupEventListeners3(container, state6, modalId, t, onClose) {
86545
87094
  if (!deviceId) return;
86546
87095
  const device = state6.devices.find((d) => d.id?.id === deviceId);
86547
87096
  if (!device) return;
86548
- setTimeout(() => {
86549
- const isChecked = input.checked;
86550
- if (isChecked) {
86551
- if (!state6.selectedDevices.some((d) => d.id?.id === deviceId)) {
86552
- state6.selectedDevices.push(device);
86553
- }
86554
- } else {
86555
- 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);
86556
87101
  }
86557
- const listEl = document.getElementById(`${modalId}-device-list`);
86558
- const savedScroll = listEl ? listEl.scrollTop : 0;
86559
- renderModal4(container, state6, modalId, t);
86560
- setupEventListeners3(container, state6, modalId, t, onClose);
86561
- requestAnimationFrame(() => {
86562
- requestAnimationFrame(() => {
86563
- const el2 = document.getElementById(`${modalId}-device-list`);
86564
- if (el2) el2.scrollTop = savedScroll;
86565
- });
86566
- });
86567
- }, 0);
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;
86568
87113
  };
86569
87114
  });
86570
87115
  document.getElementById(`${modalId}-bulk-attr`)?.addEventListener("click", () => {
@@ -86683,6 +87228,13 @@ function setupEventListeners3(container, state6, modalId, t, onClose) {
86683
87228
  if (!state6.selectedCustomer || state6.selectedDevices.length === 0) return;
86684
87229
  await handleBulkSyncIngestionId(state6, container, modalId, t, onClose);
86685
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
+ });
86686
87238
  document.getElementById(`${modalId}-bulk-relation`)?.addEventListener("click", () => {
86687
87239
  if (!state6.selectedCustomer) {
86688
87240
  alert("Selecione um Customer primeiro no Step 1");
@@ -87187,7 +87739,9 @@ function filterDeviceListVisual(container, devices, search, filters, sort) {
87187
87739
  return;
87188
87740
  }
87189
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);
87190
- item.style.display = matchesSearch ? "flex" : "none";
87742
+ const el2 = item;
87743
+ const isTableRow = el2.tagName === "TR";
87744
+ el2.style.display = matchesSearch ? isTableRow ? "" : "flex" : "none";
87191
87745
  });
87192
87746
  }
87193
87747
  async function tbFetch(state6, path) {
@@ -88801,7 +89355,7 @@ var UserListTab = class {
88801
89355
  const now = (/* @__PURE__ */ new Date()).toISOString();
88802
89356
  try {
88803
89357
  const searchRes = await fetch(
88804
- `${base}/api/v1/users?search=${encodeURIComponent(user.email)}&customerId=${encodeURIComponent(this.config.customerId)}&limit=10`,
89358
+ `${base}/users?search=${encodeURIComponent(user.email)}&customerId=${encodeURIComponent(this.config.customerId)}&limit=10`,
88805
89359
  { headers: this.gcdrHeaders() }
88806
89360
  );
88807
89361
  let gcdrUser = null;
@@ -88811,7 +89365,7 @@ var UserListTab = class {
88811
89365
  gcdrUser = items.find((u) => u.email?.toLowerCase() === user.email.toLowerCase()) ?? null;
88812
89366
  }
88813
89367
  if (!gcdrUser) {
88814
- const createRes = await fetch(`${base}/api/v1/users`, {
89368
+ const createRes = await fetch(`${base}/users`, {
88815
89369
  method: "POST",
88816
89370
  headers: this.gcdrHeaders(),
88817
89371
  body: JSON.stringify({