myio-js-library 0.1.501 → 0.1.502
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 +542 -169
- package/dist/index.d.cts +1 -0
- package/dist/index.js +542 -169
- package/dist/myio-js-library.umd.js +542 -128
- package/dist/myio-js-library.umd.min.js +1 -1
- package/package.json +1 -1
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.
|
|
1165
|
+
version: "0.1.502",
|
|
1166
1166
|
description: "A clean, standalone JS SDK for MYIO projects",
|
|
1167
1167
|
license: "MIT",
|
|
1168
1168
|
repository: "github:gh-myio/myio-js-library",
|
|
@@ -32992,6 +32992,13 @@ var AllReportModal = class {
|
|
|
32992
32992
|
domainConfig;
|
|
32993
32993
|
// Granularity: '1d' (daily) | '1h' (hourly)
|
|
32994
32994
|
granularity = "1d";
|
|
32995
|
+
// When true, devices flagged via `exclude_groups_totals` for this group are dropped
|
|
32996
|
+
// from the report so its total reconciles with the dashboard KPIs. Toggleable in the UI.
|
|
32997
|
+
considerExclusion = true;
|
|
32998
|
+
// Raw API response kept so the exclusion toggle can re-map without a new fetch.
|
|
32999
|
+
lastApiResponse = null;
|
|
33000
|
+
// Cleanup for the InfoTooltip attached to the exclusion-flag info icon.
|
|
33001
|
+
exclusionTooltipCleanup = null;
|
|
32995
33002
|
// Debug logging helper
|
|
32996
33003
|
debugLog(message, data) {
|
|
32997
33004
|
if (this.debugEnabled) {
|
|
@@ -33000,7 +33007,7 @@ var AllReportModal = class {
|
|
|
33000
33007
|
}
|
|
33001
33008
|
// Helper: normalize identifiers (upper, strip spaces and non-alphanum)
|
|
33002
33009
|
normalizeId(v) {
|
|
33003
|
-
return (v || "").toString().normalize("NFKC").toUpperCase().replace(/\s+/g, "").replace(/[
|
|
33010
|
+
return (v || "").toString().normalize("NFKC").toUpperCase().replace(/\s+/g, "").replace(/[0300-036f]/g, "");
|
|
33004
33011
|
}
|
|
33005
33012
|
// Helper: extract store identifier from API item
|
|
33006
33013
|
// Priority: assetName -> parse from name (last token or token after space) -> null
|
|
@@ -33069,6 +33076,10 @@ var AllReportModal = class {
|
|
|
33069
33076
|
this.dateRangePicker.destroy();
|
|
33070
33077
|
this.dateRangePicker = null;
|
|
33071
33078
|
}
|
|
33079
|
+
if (this.exclusionTooltipCleanup) {
|
|
33080
|
+
this.exclusionTooltipCleanup();
|
|
33081
|
+
this.exclusionTooltipCleanup = null;
|
|
33082
|
+
}
|
|
33072
33083
|
if (this.filterModal) {
|
|
33073
33084
|
this.filterModal.destroy();
|
|
33074
33085
|
this.filterModal = null;
|
|
@@ -33107,6 +33118,20 @@ var AllReportModal = class {
|
|
|
33107
33118
|
<button id="filter-btn" class="myio-btn myio-btn-secondary" style="background: var(--myio-brand-700); color: white;">
|
|
33108
33119
|
\u{1F50D} Filtros & Ordena\xE7\xE3o
|
|
33109
33120
|
</button>
|
|
33121
|
+
<div class="myio-form-group" style="margin-bottom: 0; display: flex; align-items: center; gap: 6px; align-self: flex-end; padding-bottom: 8px;">
|
|
33122
|
+
<label for="consider-exclusion" style="display: flex; align-items: center; gap: 6px; cursor: pointer; font-size: 13px; color: var(--myio-text, #374151); white-space: nowrap;">
|
|
33123
|
+
<input type="checkbox" id="consider-exclusion" checked style="cursor: pointer; width: 15px; height: 15px; accent-color: var(--myio-brand-700, #5b2c9d);">
|
|
33124
|
+
Considerar exclus\xE3o de totais
|
|
33125
|
+
</label>
|
|
33126
|
+
<span id="exclusion-info" aria-label="Sobre a exclus\xE3o de totais" style="
|
|
33127
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
33128
|
+
width: 16px; height: 16px; border-radius: 50%;
|
|
33129
|
+
background: var(--myio-brand-700, #5b2c9d); color: #fff;
|
|
33130
|
+
font-size: 11px; font-weight: 700; font-style: italic;
|
|
33131
|
+
font-family: Georgia, 'Times New Roman', serif; cursor: help;
|
|
33132
|
+
user-select: none;
|
|
33133
|
+
">i</span>
|
|
33134
|
+
</div>
|
|
33110
33135
|
<div class="myio-form-group" style="margin-bottom: 0; margin-left: auto;">
|
|
33111
33136
|
<label class="myio-label" for="search-input">Busca r\xE1pida</label>
|
|
33112
33137
|
<input type="text" id="search-input" class="myio-input" placeholder="Digite para filtrar..." style="width: 200px;">
|
|
@@ -33165,6 +33190,20 @@ var AllReportModal = class {
|
|
|
33165
33190
|
this.renderTable();
|
|
33166
33191
|
});
|
|
33167
33192
|
}
|
|
33193
|
+
const exclusionCheckbox = document.getElementById("consider-exclusion");
|
|
33194
|
+
exclusionCheckbox?.addEventListener("change", () => {
|
|
33195
|
+
this.considerExclusion = exclusionCheckbox.checked;
|
|
33196
|
+
this.remapAndRender();
|
|
33197
|
+
});
|
|
33198
|
+
const exclusionInfo = document.getElementById("exclusion-info");
|
|
33199
|
+
if (exclusionInfo) {
|
|
33200
|
+
this.exclusionTooltipCleanup?.();
|
|
33201
|
+
this.exclusionTooltipCleanup = InfoTooltip.attach(exclusionInfo, () => ({
|
|
33202
|
+
icon: "\u2139\uFE0F",
|
|
33203
|
+
title: "Exclus\xE3o de totais",
|
|
33204
|
+
content: this.buildExclusionTooltipContent()
|
|
33205
|
+
}));
|
|
33206
|
+
}
|
|
33168
33207
|
try {
|
|
33169
33208
|
this.dateRangePicker = await attach(dateRangeInput, {
|
|
33170
33209
|
presetStart: this.getDefaultStartDate(),
|
|
@@ -33207,6 +33246,7 @@ var AllReportModal = class {
|
|
|
33207
33246
|
const customerTotalsData = await this.fetchCustomerTotals(startISO, endISO);
|
|
33208
33247
|
this.debugLog("\u2705 API response received", customerTotalsData);
|
|
33209
33248
|
this.debugLog("\u{1F504} Processing API response...");
|
|
33249
|
+
this.lastApiResponse = customerTotalsData;
|
|
33210
33250
|
this.data = this.mapCustomerTotalsResponse(customerTotalsData);
|
|
33211
33251
|
this.debugLog("\u2705 Data mapping completed", {
|
|
33212
33252
|
mappedDataLength: this.data.length,
|
|
@@ -33426,48 +33466,6 @@ var AllReportModal = class {
|
|
|
33426
33466
|
});
|
|
33427
33467
|
}
|
|
33428
33468
|
renderPagination() {
|
|
33429
|
-
return;
|
|
33430
|
-
const container = document.getElementById("pagination-container");
|
|
33431
|
-
if (!container) return;
|
|
33432
|
-
const filteredData = this.getFilteredData();
|
|
33433
|
-
const totalPages = Math.ceil(filteredData.length / this.itemsPerPage);
|
|
33434
|
-
if (totalPages <= 1) {
|
|
33435
|
-
container.style.display = "none";
|
|
33436
|
-
return;
|
|
33437
|
-
}
|
|
33438
|
-
const startItem = (this.currentPage - 1) * this.itemsPerPage + 1;
|
|
33439
|
-
const endItem = Math.min(this.currentPage * this.itemsPerPage, filteredData.length);
|
|
33440
|
-
container.innerHTML = `
|
|
33441
|
-
<div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 16px;">
|
|
33442
|
-
<div style="color: var(--myio-text-muted);">
|
|
33443
|
-
Mostrando ${startItem}-${endItem} de ${filteredData.length} lojas
|
|
33444
|
-
</div>
|
|
33445
|
-
<div style="display: flex; gap: 8px; align-items: center;">
|
|
33446
|
-
<button id="prev-page" class="myio-btn myio-btn-outline" ${this.currentPage === 1 ? "disabled" : ""}>
|
|
33447
|
-
Anterior
|
|
33448
|
-
</button>
|
|
33449
|
-
<span style="padding: 0 12px; font-weight: bold;">
|
|
33450
|
-
${this.currentPage} / ${totalPages}
|
|
33451
|
-
</span>
|
|
33452
|
-
<button id="next-page" class="myio-btn myio-btn-outline" ${this.currentPage === totalPages ? "disabled" : ""}>
|
|
33453
|
-
Pr\xF3ximo
|
|
33454
|
-
</button>
|
|
33455
|
-
</div>
|
|
33456
|
-
</div>
|
|
33457
|
-
`;
|
|
33458
|
-
document.getElementById("prev-page")?.addEventListener("click", () => {
|
|
33459
|
-
if (this.currentPage > 1) {
|
|
33460
|
-
this.currentPage--;
|
|
33461
|
-
this.renderTable();
|
|
33462
|
-
}
|
|
33463
|
-
});
|
|
33464
|
-
document.getElementById("next-page")?.addEventListener("click", () => {
|
|
33465
|
-
if (this.currentPage < totalPages) {
|
|
33466
|
-
this.currentPage++;
|
|
33467
|
-
this.renderTable();
|
|
33468
|
-
}
|
|
33469
|
-
});
|
|
33470
|
-
container.style.display = "block";
|
|
33471
33469
|
}
|
|
33472
33470
|
calculateTotalConsumption() {
|
|
33473
33471
|
return this.data.reduce((sum, row) => sum + row.consumption, 0);
|
|
@@ -33511,7 +33509,7 @@ var AllReportModal = class {
|
|
|
33511
33509
|
}
|
|
33512
33510
|
generateStoreId(storeName) {
|
|
33513
33511
|
const name = (storeName || "SEM-ID").toString();
|
|
33514
|
-
return name.toLowerCase().replace(/\s+/g, "-").replace(/[
|
|
33512
|
+
return name.toLowerCase().replace(/\s+/g, "-").replace(/[0300-036f]/g, "");
|
|
33515
33513
|
}
|
|
33516
33514
|
applyFiltersAndSort(selectedIds, sortMode) {
|
|
33517
33515
|
this.selectedStoreIds = new Set(selectedIds);
|
|
@@ -33560,8 +33558,10 @@ var AllReportModal = class {
|
|
|
33560
33558
|
async fetchCustomerTotals(startISO, endISO) {
|
|
33561
33559
|
if (this.params.fetcher) {
|
|
33562
33560
|
const token2 = this.params.api.ingestionToken || await this.authClient.getBearer();
|
|
33561
|
+
const baseUrl2 = this.params.api.dataApiBaseUrl;
|
|
33562
|
+
if (!baseUrl2) throw new Error("dataApiBaseUrl n\xE3o configurado.");
|
|
33563
33563
|
return await this.params.fetcher({
|
|
33564
|
-
baseUrl:
|
|
33564
|
+
baseUrl: baseUrl2,
|
|
33565
33565
|
token: token2,
|
|
33566
33566
|
customerId: this.params.customerId,
|
|
33567
33567
|
startISO,
|
|
@@ -33598,6 +33598,87 @@ var AllReportModal = class {
|
|
|
33598
33598
|
this.debugLog("[AllReportModal] Customer totals response:", data);
|
|
33599
33599
|
return data;
|
|
33600
33600
|
}
|
|
33601
|
+
// Re-map the cached API response under the current exclusion flag and refresh the UI.
|
|
33602
|
+
// No-op until data has been loaded at least once.
|
|
33603
|
+
remapAndRender() {
|
|
33604
|
+
if (!this.lastApiResponse) return;
|
|
33605
|
+
this.data = this.mapCustomerTotalsResponse(this.lastApiResponse);
|
|
33606
|
+
this.selectedStoreIds = new Set(this.data.map((s) => this.generateStoreId(s.identifier)));
|
|
33607
|
+
this.currentPage = 1;
|
|
33608
|
+
this.renderSummary();
|
|
33609
|
+
this.renderTable();
|
|
33610
|
+
}
|
|
33611
|
+
// Premium tooltip content for the exclusion-flag info icon. Uses the library
|
|
33612
|
+
// InfoTooltip CSS classes (myio-info-tooltip__*) — injected by InfoTooltip itself.
|
|
33613
|
+
buildExclusionTooltipContent() {
|
|
33614
|
+
return `
|
|
33615
|
+
<div class="myio-info-tooltip__section" style="max-width:280px;">
|
|
33616
|
+
<div class="myio-info-tooltip__row" style="align-items:flex-start;padding:3px 0;">
|
|
33617
|
+
<span class="myio-info-tooltip__label" style="font-size:11px;line-height:1.5;white-space:normal;">
|
|
33618
|
+
Alguns dispositivos t\xEAm o atributo <strong>exclude_groups_totals</strong> e s\xE3o
|
|
33619
|
+
propositalmente removidos dos totais do dashboard (ex.: medidor de locat\xE1rio que
|
|
33620
|
+
n\xE3o \xE9 consumo operacional do shopping).
|
|
33621
|
+
</span>
|
|
33622
|
+
</div>
|
|
33623
|
+
<div class="myio-info-tooltip__row" style="align-items:flex-start;padding:3px 0;">
|
|
33624
|
+
<span class="myio-info-tooltip__label" style="font-size:11px;line-height:1.5;white-space:normal;">
|
|
33625
|
+
<strong>Ligado</strong> (padr\xE3o): esses dispositivos s\xE3o omitidos do relat\xF3rio \u2014
|
|
33626
|
+
o total bate com os cards do dashboard.
|
|
33627
|
+
</span>
|
|
33628
|
+
</div>
|
|
33629
|
+
<div class="myio-info-tooltip__row" style="align-items:flex-start;padding:3px 0;">
|
|
33630
|
+
<span class="myio-info-tooltip__label" style="font-size:11px;line-height:1.5;white-space:normal;">
|
|
33631
|
+
<strong>Desligado</strong>: todos os dispositivos do grupo entram \u2014 mostra o
|
|
33632
|
+
consumo bruto, inclusive os exclu\xEDdos.
|
|
33633
|
+
</span>
|
|
33634
|
+
</div>
|
|
33635
|
+
</div>
|
|
33636
|
+
<div class="myio-info-tooltip__notice">
|
|
33637
|
+
<span class="myio-info-tooltip__notice-icon">\u{1F4A1}</span>
|
|
33638
|
+
<span>N\xE3o altera nenhum dado \u2014 apenas o que o relat\xF3rio soma e lista.</span>
|
|
33639
|
+
</div>
|
|
33640
|
+
`;
|
|
33641
|
+
}
|
|
33642
|
+
// Resolve the canonical `exclude_groups_totals.groups` key for the report's current group.
|
|
33643
|
+
// Returns null for groupings that don't map to a single exclusion key (climatizavel, etc.).
|
|
33644
|
+
resolveExclusionGroupKey(item) {
|
|
33645
|
+
const g = String(this.params.group || "").toLowerCase();
|
|
33646
|
+
if (g === "entrada" || g === "lojas" || g === "area_comum") return g;
|
|
33647
|
+
if (g === "todos") {
|
|
33648
|
+
const gl = String(item.groupLabel || "").normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().trim();
|
|
33649
|
+
if (gl === "entrada") return "entrada";
|
|
33650
|
+
if (gl === "lojas") return "lojas";
|
|
33651
|
+
if (gl === "area comum" || gl === "areacomum") return "area_comum";
|
|
33652
|
+
if (gl === "climatizacao") return "climatizacao";
|
|
33653
|
+
if (gl === "elevadores") return "elevadores";
|
|
33654
|
+
if (gl === "escadas rolantes" || gl === "esc. rolantes") return "escadas_rolantes";
|
|
33655
|
+
if (gl === "outros" || gl === "outros equipamentos") return "outros";
|
|
33656
|
+
}
|
|
33657
|
+
return null;
|
|
33658
|
+
}
|
|
33659
|
+
// Mirrors getValorEfetivo (MAIN_VIEW): a device flagged in exclude_groups_totals for the
|
|
33660
|
+
// report's group is dropped, so the report total reconciles with the dashboard KPI card.
|
|
33661
|
+
isExcludedFromTotals(item) {
|
|
33662
|
+
const raw = item.excludeGroupsTotals;
|
|
33663
|
+
if (!raw) return false;
|
|
33664
|
+
let parsed;
|
|
33665
|
+
try {
|
|
33666
|
+
parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
33667
|
+
} catch {
|
|
33668
|
+
return false;
|
|
33669
|
+
}
|
|
33670
|
+
if (!parsed || parsed.enabled !== true) return false;
|
|
33671
|
+
const key = this.resolveExclusionGroupKey(item);
|
|
33672
|
+
if (!key) return false;
|
|
33673
|
+
if (parsed.groups && typeof parsed.groups === "object") {
|
|
33674
|
+
return parsed.groups[key] === true;
|
|
33675
|
+
}
|
|
33676
|
+
if (Array.isArray(parsed.excludedGroups)) {
|
|
33677
|
+
const ex = parsed.excludedGroups.map((x) => String(x).toLowerCase());
|
|
33678
|
+
return ex.includes(key) || ex.includes("all");
|
|
33679
|
+
}
|
|
33680
|
+
return false;
|
|
33681
|
+
}
|
|
33601
33682
|
mapCustomerTotalsResponse(apiResponse) {
|
|
33602
33683
|
this.debugLog("\u{1F50D} Starting mapCustomerTotalsResponse", { apiResponse });
|
|
33603
33684
|
const apiArray = Array.isArray(apiResponse?.data) ? apiResponse.data : Array.isArray(apiResponse) ? apiResponse : [];
|
|
@@ -33635,6 +33716,10 @@ var AllReportModal = class {
|
|
|
33635
33716
|
const apiId = String(apiItem?.id || "");
|
|
33636
33717
|
if (!apiId || !orchIdSet.has(apiId)) continue;
|
|
33637
33718
|
const meta = orchMeta.get(apiId);
|
|
33719
|
+
if (this.considerExclusion && meta && this.isExcludedFromTotals(meta)) {
|
|
33720
|
+
this.debugLog("[AllReportModal] device excluded via exclude_groups_totals:", meta.label);
|
|
33721
|
+
continue;
|
|
33722
|
+
}
|
|
33638
33723
|
const consumption = Math.round(this.pickConsumption(apiItem) * 100) / 100;
|
|
33639
33724
|
const result = {
|
|
33640
33725
|
identifier: meta?.identifier || apiItem.name || apiId,
|
|
@@ -84311,7 +84396,7 @@ async function fetchIngestionDevicesAllPaged(customerId) {
|
|
|
84311
84396
|
function findIngestionDeviceByCentralSlaveId(devices, centralId, slaveId) {
|
|
84312
84397
|
const slaveIdNum = typeof slaveId === "string" ? parseInt(slaveId, 10) : slaveId;
|
|
84313
84398
|
for (const device of devices) {
|
|
84314
|
-
const deviceGatewayId = device.gatewayId || device.gateway?.id;
|
|
84399
|
+
const deviceGatewayId = device.gateway?.hardwareUuid || device.gatewayId || device.gateway?.id;
|
|
84315
84400
|
if (deviceGatewayId === centralId && device.slaveId === slaveIdNum) {
|
|
84316
84401
|
console.log(
|
|
84317
84402
|
"[UpsellModal] Found matching device:",
|
|
@@ -84329,8 +84414,8 @@ function findIngestionDeviceByCentralSlaveId(devices, centralId, slaveId) {
|
|
|
84329
84414
|
console.log(
|
|
84330
84415
|
`[UpsellModal] Sample device ${i}:`,
|
|
84331
84416
|
d.name,
|
|
84332
|
-
"
|
|
84333
|
-
d.gatewayId || d.gateway?.id,
|
|
84417
|
+
"gateway(hwUuid|id):",
|
|
84418
|
+
d.gateway?.hardwareUuid || d.gatewayId || d.gateway?.id,
|
|
84334
84419
|
"slaveId:",
|
|
84335
84420
|
d.slaveId
|
|
84336
84421
|
);
|
|
@@ -84515,7 +84600,7 @@ function openUpsellModal(params) {
|
|
|
84515
84600
|
selectedDevices: [],
|
|
84516
84601
|
bulkAttributeModal: { open: false, attribute: "deviceType", value: "", saving: false },
|
|
84517
84602
|
bulkProfileModal: { open: false, selectedProfileId: "", saving: false },
|
|
84518
|
-
bulkOwnerModal: { open: false, saving: false },
|
|
84603
|
+
bulkOwnerModal: { open: false, saving: false, targetCustomerId: "" },
|
|
84519
84604
|
columnWidths: {
|
|
84520
84605
|
name: 180,
|
|
84521
84606
|
label: 120,
|
|
@@ -84552,6 +84637,7 @@ function openUpsellModal(params) {
|
|
|
84552
84637
|
lojasDeviceData: [],
|
|
84553
84638
|
lojasDataLoading: false,
|
|
84554
84639
|
lojasConfig: null,
|
|
84640
|
+
lojasApplyRelation: true,
|
|
84555
84641
|
customModeModal: { open: false },
|
|
84556
84642
|
bulkRelationModal: { open: false, target: "CUSTOMER", selectedAssetId: "", selectedAssetName: "", search: "", newAssetName: "", assetsLoaded: false, overrideCustomerId: "", overrideCustomerName: "", customerSearch: "", customerPickerOpen: false },
|
|
84557
84643
|
checkFixLoading: false,
|
|
@@ -84716,6 +84802,12 @@ function renderModal4(container, state6, modalId, t, error) {
|
|
|
84716
84802
|
font-size: 14px; font-weight: 500; font-family: 'Roboto', Arial, sans-serif;
|
|
84717
84803
|
display: flex; align-items: center; gap: 6px;
|
|
84718
84804
|
" ${!state6.selectedCustomer ? 'disabled title="Selecione um Customer primeiro"' : ""}>\u{1F504} Sync Ingestion ID (${state6.selectedDevices.length})</button>
|
|
84805
|
+
<button id="${modalId}-bulk-delete" style="
|
|
84806
|
+
background: #b91c1c; color: white; border: 1px solid #7f1d1d;
|
|
84807
|
+
padding: 8px 16px; border-radius: 6px; cursor: pointer;
|
|
84808
|
+
font-size: 14px; font-weight: 600; font-family: 'Roboto', Arial, sans-serif;
|
|
84809
|
+
display: flex; align-items: center; gap: 6px;
|
|
84810
|
+
" title="Deletar permanentemente os dispositivos selecionados (irrevers\xEDvel)">\u{1F5D1}\uFE0F Deletar (${state6.selectedDevices.length})</button>
|
|
84719
84811
|
` : ""}
|
|
84720
84812
|
${state6.currentStep === 3 && state6.lojasMode ? `
|
|
84721
84813
|
<button id="${modalId}-lojas-sync" style="
|
|
@@ -84954,7 +85046,18 @@ function renderModal4(container, state6, modalId, t, error) {
|
|
|
84954
85046
|
</div>
|
|
84955
85047
|
` : ""}
|
|
84956
85048
|
|
|
84957
|
-
${state6.bulkOwnerModal.open ?
|
|
85049
|
+
${state6.bulkOwnerModal.open ? (() => {
|
|
85050
|
+
const effId = state6.bulkOwnerModal.targetCustomerId || state6.selectedCustomer?.id?.id || "";
|
|
85051
|
+
const effCustomer = state6.customers.find((c) => c.id?.id === effId) || state6.selectedCustomer;
|
|
85052
|
+
const effName = effCustomer?.name || effCustomer?.title || "N\xE3o selecionado";
|
|
85053
|
+
const customerOptions = state6.customers.length === 0 ? `<option value="${effId}">${effName}</option>` : [...state6.customers].sort(
|
|
85054
|
+
(a, b) => (a.name || a.title || "").localeCompare(b.name || b.title || "", "pt-BR")
|
|
85055
|
+
).map((c) => {
|
|
85056
|
+
const cid = c.id?.id || "";
|
|
85057
|
+
const cname = c.name || c.title || cid;
|
|
85058
|
+
return `<option value="${cid}" ${cid === effId ? "selected" : ""}>${cname}</option>`;
|
|
85059
|
+
}).join("");
|
|
85060
|
+
return `
|
|
84958
85061
|
<!-- Bulk Owner Modal -->
|
|
84959
85062
|
<div class="myio-bulk-owner-overlay" style="
|
|
84960
85063
|
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
|
|
@@ -84980,17 +85083,25 @@ function renderModal4(container, state6, modalId, t, error) {
|
|
|
84980
85083
|
<div style="font-size: 14px; color: ${colors2.text}; font-weight: 500;">${state6.selectedDevices.length} dispositivos</div>
|
|
84981
85084
|
</div>
|
|
84982
85085
|
|
|
84983
|
-
<div style="margin-bottom: 16px;
|
|
84984
|
-
<div style="font-size: 12px; color: ${colors2.textMuted}; margin-bottom:
|
|
84985
|
-
|
|
85086
|
+
<div style="margin-bottom: 16px;">
|
|
85087
|
+
<div style="font-size: 12px; color: ${colors2.textMuted}; margin-bottom: 6px;">
|
|
85088
|
+
Novo Owner (Customer):
|
|
85089
|
+
</div>
|
|
85090
|
+
<select id="${modalId}-bulk-owner-customer" style="
|
|
85091
|
+
width: 100%; padding: 9px 10px; border-radius: 8px;
|
|
85092
|
+
border: 1px solid ${colors2.border}; background: ${colors2.inputBg};
|
|
85093
|
+
color: ${colors2.text}; font-size: 13px; cursor: pointer;
|
|
85094
|
+
">
|
|
85095
|
+
${customerOptions}
|
|
85096
|
+
</select>
|
|
84986
85097
|
<div style="font-size: 11px; color: ${colors2.textMuted}; margin-top: 4px;">
|
|
84987
|
-
ID: ${
|
|
85098
|
+
ID: ${effId || "N/A"}${state6.customers.length === 0 ? " \xB7 carregando lista de clientes\u2026" : ""}
|
|
84988
85099
|
</div>
|
|
84989
85100
|
</div>
|
|
84990
85101
|
|
|
84991
85102
|
<div style="margin-bottom: 16px; padding: 12px; background: ${colors2.warning}20; border-radius: 8px; border: 1px solid ${colors2.warning}40;">
|
|
84992
85103
|
<div style="font-size: 12px; color: ${colors2.warning}; font-weight: 500;">
|
|
84993
|
-
\u26A0\uFE0F Aten\xE7\xE3o: Esta a\xE7\xE3o ir\xE1 atribuir todos os ${state6.selectedDevices.length} devices selecionados ao customer "${
|
|
85104
|
+
\u26A0\uFE0F Aten\xE7\xE3o: Esta a\xE7\xE3o ir\xE1 atribuir todos os ${state6.selectedDevices.length} devices selecionados ao customer "${effName}".
|
|
84994
85105
|
</div>
|
|
84995
85106
|
</div>
|
|
84996
85107
|
|
|
@@ -85004,13 +85115,14 @@ function renderModal4(container, state6, modalId, t, error) {
|
|
|
85004
85115
|
background: #10b981; color: white; border: none;
|
|
85005
85116
|
padding: 10px 20px; border-radius: 6px; cursor: pointer;
|
|
85006
85117
|
font-size: 14px; font-weight: 500;
|
|
85007
|
-
" ${state6.bulkOwnerModal.saving || !
|
|
85118
|
+
" ${state6.bulkOwnerModal.saving || !effId ? "disabled" : ""}>
|
|
85008
85119
|
${state6.bulkOwnerModal.saving ? "Salvando..." : "Atribuir Owner para " + state6.selectedDevices.length + " devices"}
|
|
85009
85120
|
</button>
|
|
85010
85121
|
</div>
|
|
85011
85122
|
</div>
|
|
85012
85123
|
</div>
|
|
85013
|
-
|
|
85124
|
+
`;
|
|
85125
|
+
})() : ""}
|
|
85014
85126
|
|
|
85015
85127
|
${state6.customModeModal.open ? `
|
|
85016
85128
|
<!-- CUSTOM Mode Picker Modal -->
|
|
@@ -85230,6 +85342,7 @@ function renderModal4(container, state6, modalId, t, error) {
|
|
|
85230
85342
|
`;
|
|
85231
85343
|
})() : ""}
|
|
85232
85344
|
`;
|
|
85345
|
+
delete container.dataset.upsellListenersBound;
|
|
85233
85346
|
setupEventListeners3(container, state6, modalId, t);
|
|
85234
85347
|
}
|
|
85235
85348
|
function renderStepIndicator(step, label, currentStep, colors2) {
|
|
@@ -85433,6 +85546,8 @@ function renderCheckFixRow(r, state6, modalId, colors2, dupPairIds, dupIngestion
|
|
|
85433
85546
|
};
|
|
85434
85547
|
const valStr = Object.entries(r.telemetry.values).map(([k, v]) => v != null ? `${k}:<b>${v}${unit[k] ?? ""}</b>` : null).filter(Boolean).join(" \xB7 ") || "\u2014";
|
|
85435
85548
|
const connColor = r.connStatus ? CONN_COLOR[r.connStatus] || colors2.textMuted : colors2.textMuted;
|
|
85549
|
+
const valCopy = Object.entries(r.telemetry.values).filter(([, v]) => v != null).map(([k, v]) => `${k}: ${v}${unit[k] ?? ""}`).join(" \xB7 ");
|
|
85550
|
+
const copyAttr = (v) => `class="myio-copy-cell" data-copy="${encodeURIComponent(String(v ?? ""))}"`;
|
|
85436
85551
|
return `
|
|
85437
85552
|
<tr class="myio-list-item ${isSelected ? "selected" : ""}" data-device-id="${r.deviceId}"
|
|
85438
85553
|
style="border-bottom:1px solid ${colors2.border}; cursor:pointer;">
|
|
@@ -85441,21 +85556,21 @@ function renderCheckFixRow(r, state6, modalId, colors2, dupPairIds, dupIngestion
|
|
|
85441
85556
|
<input type="checkbox" class="myio-device-checkbox" data-device-id="${r.deviceId}"
|
|
85442
85557
|
${isSelectedMulti ? "checked" : ""} style="width:14px;height:14px;cursor:pointer;accent-color:${MYIO_PURPLE};"/>
|
|
85443
85558
|
</td>` : ""}
|
|
85444
|
-
<td style="${cell()} max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="${r.deviceName}">${r.deviceName}</td>
|
|
85445
|
-
<td style="${cell()} max-width:100px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:${colors2.textMuted};" title="${r.deviceLabel}">${r.deviceLabel || dash}</td>
|
|
85446
|
-
<td style="${cell(typeActWrong ? "ok" : "none", true)}">${r.inferred.deviceType}</td>
|
|
85447
|
-
<td style="${cell(typeActWrong ? r.typeEqualsProfile ? "warn" : "bad" : "none", true)}" title="${typeActWrong ? `esperado: ${r.inferred.deviceType}` : ""}">${r.actual.type || dash}</td>
|
|
85448
|
-
<td style="${cell(devTypeWrong ? "ok" : "none", true)}">${r.inferred.deviceType}</td>
|
|
85449
|
-
<td style="${cell(devTypeWrong ? "bad" : "none", true)}" title="${devTypeWrong ? `esperado: ${r.inferred.deviceType}` : ""}">${r.actual.deviceType || dash}</td>
|
|
85450
|
-
<td style="${cell(devProfWrong ? "ok" : "none", true)}">${r.inferred.deviceProfile}</td>
|
|
85451
|
-
<td style="${cell(devProfWrong ? "bad" : "none", true)}" title="${devProfWrong ? `esperado: ${r.inferred.deviceProfile}` : ""}">${r.actual.deviceProfile || dash}</td>
|
|
85452
|
-
<td style="${cell()} white-space:nowrap; color:${colors2.textMuted}; font-size:9px;">${tsStr}</td>
|
|
85453
|
-
<td style="${cell()} font-size:9px;">${valStr}</td>
|
|
85454
|
-
<td style="${cell()} color:${connColor}; font-weight:600; white-space:nowrap;">${r.connStatus || dash}</td>
|
|
85455
|
-
<td style="${cell()} font-size:9px; font-family:monospace; color:${colors2.textMuted}; max-width:110px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" title="${r.gcdrDeviceId || ""}">${r.gcdrDeviceId || dash}</td>
|
|
85456
|
-
<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>
|
|
85457
|
-
<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>
|
|
85458
|
-
<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>
|
|
85559
|
+
<td ${copyAttr(r.deviceName)} style="${cell()} max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="${r.deviceName}">${r.deviceName}</td>
|
|
85560
|
+
<td ${copyAttr(r.deviceLabel)} style="${cell()} max-width:100px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:${colors2.textMuted};" title="${r.deviceLabel}">${r.deviceLabel || dash}</td>
|
|
85561
|
+
<td ${copyAttr(r.inferred.deviceType)} style="${cell(typeActWrong ? "ok" : "none", true)}">${r.inferred.deviceType}</td>
|
|
85562
|
+
<td ${copyAttr(r.actual.type ?? "")} style="${cell(typeActWrong ? r.typeEqualsProfile ? "warn" : "bad" : "none", true)}" title="${typeActWrong ? `esperado: ${r.inferred.deviceType}` : ""}">${r.actual.type || dash}</td>
|
|
85563
|
+
<td ${copyAttr(r.inferred.deviceType)} style="${cell(devTypeWrong ? "ok" : "none", true)}">${r.inferred.deviceType}</td>
|
|
85564
|
+
<td ${copyAttr(r.actual.deviceType ?? "")} style="${cell(devTypeWrong ? "bad" : "none", true)}" title="${devTypeWrong ? `esperado: ${r.inferred.deviceType}` : ""}">${r.actual.deviceType || dash}</td>
|
|
85565
|
+
<td ${copyAttr(r.inferred.deviceProfile)} style="${cell(devProfWrong ? "ok" : "none", true)}">${r.inferred.deviceProfile}</td>
|
|
85566
|
+
<td ${copyAttr(r.actual.deviceProfile ?? "")} style="${cell(devProfWrong ? "bad" : "none", true)}" title="${devProfWrong ? `esperado: ${r.inferred.deviceProfile}` : ""}">${r.actual.deviceProfile || dash}</td>
|
|
85567
|
+
<td ${copyAttr(r.telemetry.ts ? tsStr : "")} style="${cell()} white-space:nowrap; color:${colors2.textMuted}; font-size:9px;">${tsStr}</td>
|
|
85568
|
+
<td ${copyAttr(valCopy)} style="${cell()} font-size:9px;">${valStr}</td>
|
|
85569
|
+
<td ${copyAttr(r.connStatus ?? "")} style="${cell()} color:${connColor}; font-weight:600; white-space:nowrap;">${r.connStatus || dash}</td>
|
|
85570
|
+
<td ${copyAttr(r.gcdrDeviceId ?? "")} style="${cell()} font-size:9px; font-family:monospace; color:${colors2.textMuted}; max-width:110px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" title="${r.gcdrDeviceId || ""}">${r.gcdrDeviceId || dash}</td>
|
|
85571
|
+
<td ${copyAttr(r.ingestionId ?? "")} 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>
|
|
85572
|
+
<td ${copyAttr(r.centralId ?? "")} 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>
|
|
85573
|
+
<td ${copyAttr(r.slaveId ?? "")} 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>
|
|
85459
85574
|
<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"};"
|
|
85460
85575
|
${r.status !== "ok" ? `data-cf-status="${r.status}" data-cf-device="${encodeURIComponent(r.deviceName)}" data-cf-detail="${encodeURIComponent(_buildCfStatusDetail(r))}"` : ""}>
|
|
85461
85576
|
${STATUS_ICON[r.status]} ${r.status}
|
|
@@ -85501,16 +85616,7 @@ function renderStep2(state6, modalId, colors2, t) {
|
|
|
85501
85616
|
}
|
|
85502
85617
|
return true;
|
|
85503
85618
|
});
|
|
85504
|
-
const searchFilteredDevices = searchTerm ? filteredDevices.filter((d) =>
|
|
85505
|
-
const name = (d.name || "").toLowerCase();
|
|
85506
|
-
const label = (d.label || "").toLowerCase();
|
|
85507
|
-
const type = (d.type || "").toLowerCase();
|
|
85508
|
-
const deviceType = (d.serverAttrs?.deviceType || "").toLowerCase();
|
|
85509
|
-
const deviceProfile = (d.serverAttrs?.deviceProfile || "").toLowerCase();
|
|
85510
|
-
const slaveId = String(d.serverAttrs?.slaveId ?? "").toLowerCase();
|
|
85511
|
-
const status = (d.latestTelemetry?.connectionStatus?.value || "").toLowerCase();
|
|
85512
|
-
return name.includes(searchTerm) || label.includes(searchTerm) || type.includes(searchTerm) || deviceType.includes(searchTerm) || deviceProfile.includes(searchTerm) || slaveId.includes(searchTerm) || status.includes(searchTerm);
|
|
85513
|
-
}) : filteredDevices;
|
|
85619
|
+
const searchFilteredDevices = searchTerm ? filteredDevices.filter((d) => buildDeviceSearchHaystack(d, state6).includes(searchTerm)) : filteredDevices;
|
|
85514
85620
|
const sortedDevices = sortDevices2(searchFilteredDevices, sortField, sortOrder);
|
|
85515
85621
|
const gridHeight = state6.isMaximized ? "calc(100vh - 340px)" : "360px";
|
|
85516
85622
|
const hasActiveFilters = filterTypes.length > 0 || filterDeviceTypes.length > 0 || filterDeviceProfiles.length > 0 || filterStatuses.length > 0 || filterTelemetryKeys.length > 0;
|
|
@@ -85978,6 +86084,95 @@ function renderStep2(state6, modalId, colors2, t) {
|
|
|
85978
86084
|
})()}
|
|
85979
86085
|
`;
|
|
85980
86086
|
}
|
|
86087
|
+
function buildDeviceSearchHaystack(d, state6) {
|
|
86088
|
+
const id = getEntityId(d);
|
|
86089
|
+
const relTo = (state6.deviceRelToMap.get(id) || []).map((r) => r.name || "").join(" ");
|
|
86090
|
+
const relFrom = (state6.deviceRelFromMap.get(id) || []).map((r) => r.name || "").join(" ");
|
|
86091
|
+
const tel = d.latestTelemetry || {};
|
|
86092
|
+
const a = d.serverAttrs || {};
|
|
86093
|
+
return [
|
|
86094
|
+
d.name,
|
|
86095
|
+
d.label,
|
|
86096
|
+
d.type,
|
|
86097
|
+
d.createdTime ? formatDate6(d.createdTime, state6.locale) : "",
|
|
86098
|
+
relTo,
|
|
86099
|
+
relFrom,
|
|
86100
|
+
a.centralId,
|
|
86101
|
+
a.slaveId,
|
|
86102
|
+
a.deviceType,
|
|
86103
|
+
a.deviceProfile,
|
|
86104
|
+
tel.pulses?.value,
|
|
86105
|
+
tel.consumption?.value,
|
|
86106
|
+
tel.temperature?.value,
|
|
86107
|
+
tel.connectionStatus?.value
|
|
86108
|
+
].map((v) => String(v ?? "").toLowerCase()).join("");
|
|
86109
|
+
}
|
|
86110
|
+
function getGridVisibleDevices(state6) {
|
|
86111
|
+
const {
|
|
86112
|
+
types: filterTypes,
|
|
86113
|
+
deviceTypes: filterDeviceTypes,
|
|
86114
|
+
deviceProfiles: filterDeviceProfiles,
|
|
86115
|
+
statuses: filterStatuses,
|
|
86116
|
+
telemetryKeys: filterTelemetryKeys
|
|
86117
|
+
} = state6.deviceFilters;
|
|
86118
|
+
const searchTerm = state6.deviceSearchTerm.toLowerCase();
|
|
86119
|
+
let result = state6.devices.filter((d) => {
|
|
86120
|
+
if (filterTypes.length > 0 && !filterTypes.includes(d.type || "")) return false;
|
|
86121
|
+
if (filterDeviceTypes.length > 0 && !filterDeviceTypes.includes(d.serverAttrs?.deviceType || ""))
|
|
86122
|
+
return false;
|
|
86123
|
+
if (filterDeviceProfiles.length > 0 && !filterDeviceProfiles.includes(d.serverAttrs?.deviceProfile || ""))
|
|
86124
|
+
return false;
|
|
86125
|
+
if (filterStatuses.length > 0) {
|
|
86126
|
+
const status = d.latestTelemetry?.connectionStatus?.value || "offline";
|
|
86127
|
+
if (!filterStatuses.includes(status)) return false;
|
|
86128
|
+
}
|
|
86129
|
+
if (filterTelemetryKeys.length > 0) {
|
|
86130
|
+
const telem = d.latestTelemetry;
|
|
86131
|
+
const hasMatch = filterTelemetryKeys.some((k) => {
|
|
86132
|
+
if (k === "pulses") return telem?.pulses != null;
|
|
86133
|
+
if (k === "consumption") return telem?.consumption != null;
|
|
86134
|
+
return false;
|
|
86135
|
+
});
|
|
86136
|
+
if (!hasMatch) return false;
|
|
86137
|
+
}
|
|
86138
|
+
return true;
|
|
86139
|
+
});
|
|
86140
|
+
if (searchTerm) {
|
|
86141
|
+
result = result.filter((d) => buildDeviceSearchHaystack(d, state6).includes(searchTerm));
|
|
86142
|
+
}
|
|
86143
|
+
if (state6.checkFixReport) {
|
|
86144
|
+
const idToRecord = new Map(state6.checkFixReport.records.map((r) => [r.deviceId, r]));
|
|
86145
|
+
const af = state6.checkFixAdvancedFilter;
|
|
86146
|
+
result = result.filter((d) => {
|
|
86147
|
+
const r = idToRecord.get(getEntityId(d));
|
|
86148
|
+
if (!r) return false;
|
|
86149
|
+
if (!(state6.checkFixFilter === "all" || r.status === state6.checkFixFilter)) return false;
|
|
86150
|
+
if (af.statuses.length > 0 && !af.statuses.includes(r.status)) return false;
|
|
86151
|
+
if (af.connStatuses.length > 0 && !af.connStatuses.includes(r.connStatus || "null")) return false;
|
|
86152
|
+
if (af.domains.length > 0 && !af.domains.includes(r.domain || "null")) return false;
|
|
86153
|
+
if (af.missingIngestionId && r.ingestionId) return false;
|
|
86154
|
+
if (af.missingCentralSlave && r.centralId && r.slaveId != null) return false;
|
|
86155
|
+
return true;
|
|
86156
|
+
});
|
|
86157
|
+
}
|
|
86158
|
+
return result;
|
|
86159
|
+
}
|
|
86160
|
+
function copyCellValue(text, el2) {
|
|
86161
|
+
if (!text) return;
|
|
86162
|
+
const flash = (ok) => {
|
|
86163
|
+
const prev = el2.style.backgroundColor;
|
|
86164
|
+
el2.style.transition = "background-color 0.15s ease";
|
|
86165
|
+
el2.style.backgroundColor = ok ? "rgba(34,197,94,0.35)" : "rgba(239,68,68,0.35)";
|
|
86166
|
+
setTimeout(() => {
|
|
86167
|
+
el2.style.backgroundColor = prev;
|
|
86168
|
+
}, 350);
|
|
86169
|
+
};
|
|
86170
|
+
if (navigator.clipboard?.writeText) {
|
|
86171
|
+
navigator.clipboard.writeText(text).then(() => flash(true), () => flash(false));
|
|
86172
|
+
} else {
|
|
86173
|
+
flash(false);
|
|
86174
|
+
}
|
|
86175
|
+
}
|
|
85981
86176
|
function renderDeviceRow(device, state6, modalId, colors2) {
|
|
85982
86177
|
const deviceId = getEntityId(device);
|
|
85983
86178
|
const isSelectedSingle = state6.deviceSelectionMode === "single" && getEntityId(state6.selectedDevice) === deviceId;
|
|
@@ -86092,6 +86287,10 @@ function renderDeviceRow(device, state6, modalId, colors2) {
|
|
|
86092
86287
|
" title="${statusTs}">(+)</span>` : ""}
|
|
86093
86288
|
`;
|
|
86094
86289
|
};
|
|
86290
|
+
const relToNames = state6.relationsLoaded ? (state6.deviceRelToMap.get(deviceId) || []).map((r) => r.name || "").filter(Boolean).join(", ") : "";
|
|
86291
|
+
const relFromNames = state6.relationsLoaded ? (state6.deviceRelFromMap.get(deviceId) || []).map((r) => r.name || "").filter(Boolean).join(", ") : "";
|
|
86292
|
+
const telemetryCopy = telemetryItems.map((it) => `${it.label}: ${it.value}${it.unit}`).join("; ");
|
|
86293
|
+
const copyAttr = (v) => `class="myio-copy-cell" data-copy="${encodeURIComponent(String(v ?? ""))}" title="Clique para copiar valor"`;
|
|
86095
86294
|
return `
|
|
86096
86295
|
<div class="myio-list-item ${isSelected ? "selected" : ""}"
|
|
86097
86296
|
data-device-id="${deviceId}" style="
|
|
@@ -86107,8 +86306,8 @@ function renderDeviceRow(device, state6, modalId, colors2) {
|
|
|
86107
86306
|
" />
|
|
86108
86307
|
</div>
|
|
86109
86308
|
` : ""}
|
|
86110
|
-
<div style="width: 28px; font-size: 16px; flex-shrink: 0;">${getDeviceIcon3(device.type)}</div>
|
|
86111
|
-
<div style="width: ${state6.columnWidths.name}px; padding: 0 6px; overflow: hidden; display: flex; align-items: center; gap: 4px;">
|
|
86309
|
+
<div style="width: 28px; font-size: 16px; flex-shrink: 0; cursor: pointer;" title="Clique para selecionar o dispositivo">${getDeviceIcon3(device.type)}</div>
|
|
86310
|
+
<div ${copyAttr(device.name)} style="width: ${state6.columnWidths.name}px; padding: 0 6px; overflow: hidden; display: flex; align-items: center; gap: 4px; cursor: pointer;">
|
|
86112
86311
|
<div style="font-weight: 600; color: ${colors2.text}; font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1;" title="${device.name}">
|
|
86113
86312
|
${device.name}
|
|
86114
86313
|
</div>
|
|
@@ -86118,22 +86317,22 @@ function renderDeviceRow(device, state6, modalId, colors2) {
|
|
|
86118
86317
|
display: flex; align-items: center; justify-content: center; border: 1px solid ${colors2.border};
|
|
86119
86318
|
" title="Ver detalhes">\u24D8</span>
|
|
86120
86319
|
</div>
|
|
86121
|
-
<div style="width: ${state6.columnWidths.label}px; padding: 0 6px; overflow: hidden; flex-shrink: 0;">
|
|
86320
|
+
<div ${copyAttr(device.label ?? "")} style="width: ${state6.columnWidths.label}px; padding: 0 6px; overflow: hidden; flex-shrink: 0; cursor: pointer;">
|
|
86122
86321
|
<div style="font-size: 10px; color: ${colors2.textMuted}; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" title="${device.label ?? ""}">
|
|
86123
86322
|
${device.label ?? ""}
|
|
86124
86323
|
</div>
|
|
86125
86324
|
</div>
|
|
86126
|
-
<div style="width: ${state6.columnWidths.type}px; padding: 0 6px; text-align: center; flex-shrink: 0; overflow: hidden;">
|
|
86325
|
+
<div ${copyAttr(device.type ?? "")} style="width: ${state6.columnWidths.type}px; padding: 0 6px; text-align: center; flex-shrink: 0; overflow: hidden; cursor: pointer;">
|
|
86127
86326
|
<div style="font-size: 9px; padding: 2px 4px; border-radius: 3px; display: inline-block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
|
|
86128
86327
|
background: ${device.type?.includes("HIDRO") ? "#dbeafe" : "#fef3c7"};
|
|
86129
86328
|
color: ${device.type?.includes("HIDRO") ? "#1e40af" : "#92400e"};" title="${device.type || ""}">
|
|
86130
86329
|
${device.type || "\u2014"}
|
|
86131
86330
|
</div>
|
|
86132
86331
|
</div>
|
|
86133
|
-
<div style="width: ${state6.columnWidths.createdTime}px; padding: 0 6px; text-align: center; flex-shrink: 0;">
|
|
86332
|
+
<div ${copyAttr(device.createdTime ? createdTimeStr : "")} style="width: ${state6.columnWidths.createdTime}px; padding: 0 6px; text-align: center; flex-shrink: 0; cursor: pointer;">
|
|
86134
86333
|
<span style="font-size: 9px; color: ${colors2.textMuted};">${createdTimeStr}</span>
|
|
86135
86334
|
</div>
|
|
86136
|
-
<div style="width: ${state6.columnWidths.relationTo}px; padding: 0 6px; flex-shrink: 0; overflow: hidden; display:flex; align-items:center; justify-content:center; gap:3px;">
|
|
86335
|
+
<div ${copyAttr(relToNames)} style="width: ${state6.columnWidths.relationTo}px; padding: 0 6px; flex-shrink: 0; overflow: hidden; display:flex; align-items:center; justify-content:center; gap:3px; cursor: pointer;">
|
|
86137
86336
|
${(() => {
|
|
86138
86337
|
if (!state6.relationsLoaded) return `<span style="font-size: 8px; color: ${colors2.textMuted}; font-style: italic;">\u2014</span>`;
|
|
86139
86338
|
const rels = state6.deviceRelToMap.get(deviceId) || [];
|
|
@@ -86143,7 +86342,7 @@ function renderDeviceRow(device, state6, modalId, colors2) {
|
|
|
86143
86342
|
return `<span style="font-size:9px;color:${colors2.text};overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:${more > 0 ? 68 : 108}px" title="${first.name}">${first.name}</span>${more > 0 ? `<button data-show-relto="${deviceId}" data-device-name="${encodeURIComponent(device.name || "")}" style="flex-shrink:0;font-size:9px;background:#ede9ff;color:#4c1d95;border:none;border-radius:3px;padding:1px 4px;cursor:pointer;font-weight:700;line-height:1.4">+${more}</button>` : ""}`;
|
|
86144
86343
|
})()}
|
|
86145
86344
|
</div>
|
|
86146
|
-
<div style="width: ${state6.columnWidths.relationFrom}px; padding: 0 6px; flex-shrink: 0; overflow: hidden; display:flex; align-items:center; justify-content:center; gap:3px;">
|
|
86345
|
+
<div ${copyAttr(relFromNames)} style="width: ${state6.columnWidths.relationFrom}px; padding: 0 6px; flex-shrink: 0; overflow: hidden; display:flex; align-items:center; justify-content:center; gap:3px; cursor: pointer;">
|
|
86147
86346
|
${(() => {
|
|
86148
86347
|
if (!state6.relationsLoaded) return `<span style="font-size: 8px; color: ${colors2.textMuted}; font-style: italic;">\u2014</span>`;
|
|
86149
86348
|
const rels = state6.deviceRelFromMap.get(deviceId) || [];
|
|
@@ -86153,25 +86352,25 @@ function renderDeviceRow(device, state6, modalId, colors2) {
|
|
|
86153
86352
|
return `<span style="font-size:9px;color:${colors2.text};overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:${more > 0 ? 68 : 98}px" title="${first.name}">${first.name}</span>${more > 0 ? `<button data-show-relfrom="${deviceId}" data-device-name="${encodeURIComponent(device.name || "")}" style="flex-shrink:0;font-size:9px;background:#dbeafe;color:#1e40af;border:none;border-radius:3px;padding:1px 4px;cursor:pointer;font-weight:700;line-height:1.4">+${more}</button>` : ""}`;
|
|
86154
86353
|
})()}
|
|
86155
86354
|
</div>
|
|
86156
|
-
<div style="width: ${state6.columnWidths.centralId}px; padding: 0 6px; text-align: center; flex-shrink: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;">
|
|
86355
|
+
<div ${copyAttr(attrs.centralId ?? "")} style="width: ${state6.columnWidths.centralId}px; padding: 0 6px; text-align: center; flex-shrink: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; cursor: pointer;">
|
|
86157
86356
|
${!state6.deviceAttrsLoaded ? `<span style="font-size: 8px; color: ${colors2.textMuted}; font-style: italic;">\u2014</span>` : attrs.centralId ? `<span style="font-size: 9px; color: ${colors2.text};" title="${attrs.centralId}">${attrs.centralId}</span>` : `<span style="font-size: 9px; color: ${colors2.textMuted};">\u2014</span>`}
|
|
86158
86357
|
</div>
|
|
86159
|
-
<div style="width: ${state6.columnWidths.slaveId}px; padding: 0 6px; text-align: center; flex-shrink: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;">
|
|
86358
|
+
<div ${copyAttr(attrs.slaveId ?? "")} style="width: ${state6.columnWidths.slaveId}px; padding: 0 6px; text-align: center; flex-shrink: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; cursor: pointer;">
|
|
86160
86359
|
${!state6.deviceAttrsLoaded ? `<span style="font-size: 8px; color: ${colors2.textMuted}; font-style: italic;">\u2014</span>` : attrs.slaveId != null && attrs.slaveId !== "" ? `<span style="font-size: 9px; color: ${colors2.text};" title="${attrs.slaveId}">${attrs.slaveId}</span>` : `<span style="font-size: 9px; color: ${colors2.textMuted};">\u2014</span>`}
|
|
86161
86360
|
</div>
|
|
86162
|
-
<div style="width: ${state6.columnWidths.deviceType}px; padding: 0 6px; text-align: center; flex-shrink: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;">
|
|
86361
|
+
<div ${copyAttr(state6.deviceAttrsLoaded ? attrs.deviceType ?? "" : "")} style="width: ${state6.columnWidths.deviceType}px; padding: 0 6px; text-align: center; flex-shrink: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; cursor: pointer;">
|
|
86163
86362
|
${renderDeviceTypeValue()}
|
|
86164
86363
|
</div>
|
|
86165
|
-
<div style="width: ${state6.columnWidths.deviceProfile}px; padding: 0 6px; text-align: center; flex-shrink: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;">
|
|
86364
|
+
<div ${copyAttr(state6.deviceAttrsLoaded ? attrs.deviceProfile ?? "" : "")} style="width: ${state6.columnWidths.deviceProfile}px; padding: 0 6px; text-align: center; flex-shrink: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; cursor: pointer;">
|
|
86166
86365
|
${renderDeviceProfileValue()}
|
|
86167
86366
|
</div>
|
|
86168
|
-
<div style="width: ${state6.columnWidths.telemetry}px; padding: 0 6px; text-align: center; flex-shrink: 0; display: flex; align-items: center; justify-content: center; gap: 4px; flex-wrap: wrap;">
|
|
86367
|
+
<div ${copyAttr(telemetryCopy)} style="width: ${state6.columnWidths.telemetry}px; padding: 0 6px; text-align: center; flex-shrink: 0; display: flex; align-items: center; justify-content: center; gap: 4px; flex-wrap: wrap; cursor: pointer;">
|
|
86169
86368
|
${renderTelemetryValue()}
|
|
86170
86369
|
</div>
|
|
86171
|
-
<div style="width: ${state6.columnWidths.status}px; padding: 0 6px; text-align: center; flex-shrink: 0; display: flex; align-items: center; justify-content: center; gap: 2px;">
|
|
86370
|
+
<div ${copyAttr(connStatus ?? "")} style="width: ${state6.columnWidths.status}px; padding: 0 6px; text-align: center; flex-shrink: 0; display: flex; align-items: center; justify-content: center; gap: 2px; cursor: pointer;">
|
|
86172
86371
|
${renderStatusValue()}
|
|
86173
86372
|
</div>
|
|
86174
|
-
<div style="width: 24px; flex-shrink: 0; text-align: center;">
|
|
86373
|
+
<div style="width: 24px; flex-shrink: 0; text-align: center; cursor: pointer;" title="Clique para selecionar o dispositivo">
|
|
86175
86374
|
${isSelected ? `<span style="color: ${colors2.success}; font-size: 14px;">\u2713</span>` : ""}
|
|
86176
86375
|
</div>
|
|
86177
86376
|
</div>
|
|
@@ -86553,7 +86752,11 @@ function renderLojasStep3(state6, modalId, colors2, t) {
|
|
|
86553
86752
|
<span>Profile alvo: <strong style="color: ${colors2.text};">3F_MEDIDOR</strong></span>
|
|
86554
86753
|
<span>deviceType: <strong style="color: ${colors2.text};">3F_MEDIDOR</strong></span>
|
|
86555
86754
|
<span>deviceProfile: <strong style="color: ${colors2.text};">3F_MEDIDOR</strong></span>
|
|
86556
|
-
<
|
|
86755
|
+
<label style="display: flex; align-items: center; gap: 6px; cursor: pointer;"
|
|
86756
|
+
title="Marcado: for\xE7a a rela\xE7\xE3o Customer \u2192 Device. Desmarcado: n\xE3o altera rela\xE7\xF5es.">
|
|
86757
|
+
<input type="checkbox" id="${modalId}-lojas-apply-relation" ${state6.lojasApplyRelation ? "checked" : ""} style="accent-color: ${MYIO_PURPLE}; cursor: pointer;" />
|
|
86758
|
+
<span>Rela\xE7\xE3o: <strong style="color: ${colors2.text};">CUSTOMER \u2192 DEVICE (Contains)</strong></span>
|
|
86759
|
+
</label>
|
|
86557
86760
|
</div>
|
|
86558
86761
|
</div>
|
|
86559
86762
|
`;
|
|
@@ -87117,6 +87320,8 @@ async function openClearGcdrIdsModal(state6) {
|
|
|
87117
87320
|
}
|
|
87118
87321
|
}
|
|
87119
87322
|
function setupEventListeners3(container, state6, modalId, t, onClose) {
|
|
87323
|
+
if (container.dataset.upsellListenersBound === "true") return;
|
|
87324
|
+
container.dataset.upsellListenersBound = "true";
|
|
87120
87325
|
const closeHandler = () => closeModal(container, onClose);
|
|
87121
87326
|
const overlay = container.querySelector(".myio-upsell-modal-overlay");
|
|
87122
87327
|
if (overlay) {
|
|
@@ -87232,10 +87437,18 @@ function setupEventListeners3(container, state6, modalId, t, onClose) {
|
|
|
87232
87437
|
});
|
|
87233
87438
|
});
|
|
87234
87439
|
});
|
|
87440
|
+
container.querySelectorAll(".myio-copy-cell").forEach((cell) => {
|
|
87441
|
+
cell.addEventListener("click", (e) => {
|
|
87442
|
+
e.stopPropagation();
|
|
87443
|
+
const target = e.target;
|
|
87444
|
+
if (target.closest("button, input, .myio-info-btn, .myio-ts-btn")) return;
|
|
87445
|
+
copyCellValue(decodeURIComponent(cell.dataset.copy || ""), cell);
|
|
87446
|
+
});
|
|
87447
|
+
});
|
|
87235
87448
|
document.getElementById(`${modalId}-device-search`)?.addEventListener("input", (e) => {
|
|
87236
87449
|
const search = e.target.value.toLowerCase();
|
|
87237
87450
|
state6.deviceSearchTerm = e.target.value;
|
|
87238
|
-
filterDeviceListVisual(container, state6.devices, search, state6.deviceFilters, state6.deviceSort);
|
|
87451
|
+
filterDeviceListVisual(container, state6.devices, search, state6.deviceFilters, state6.deviceSort, state6);
|
|
87239
87452
|
});
|
|
87240
87453
|
document.getElementById(`${modalId}-device-type-filter`)?.addEventListener("change", (e) => {
|
|
87241
87454
|
const select = e.target;
|
|
@@ -87457,15 +87670,17 @@ function setupEventListeners3(container, state6, modalId, t, onClose) {
|
|
|
87457
87670
|
renderModal4(container, state6, modalId, t);
|
|
87458
87671
|
setupEventListeners3(container, state6, modalId, t, onClose);
|
|
87459
87672
|
});
|
|
87460
|
-
document.getElementById(`${modalId}-check-fix`)?.addEventListener("click",
|
|
87673
|
+
document.getElementById(`${modalId}-check-fix`)?.addEventListener("click", () => {
|
|
87461
87674
|
if (state6.checkFixLoading) return;
|
|
87462
|
-
state6
|
|
87463
|
-
|
|
87464
|
-
|
|
87465
|
-
|
|
87466
|
-
|
|
87467
|
-
|
|
87468
|
-
|
|
87675
|
+
openCheckFixScopeDialog(state6, async (scopeDevices) => {
|
|
87676
|
+
state6.checkFixLoading = true;
|
|
87677
|
+
renderModal4(container, state6, modalId, t);
|
|
87678
|
+
setupEventListeners3(container, state6, modalId, t, onClose);
|
|
87679
|
+
await runCheckFixRoutine(state6, container, modalId, t, onClose, scopeDevices);
|
|
87680
|
+
state6.checkFixLoading = false;
|
|
87681
|
+
renderModal4(container, state6, modalId, t);
|
|
87682
|
+
setupEventListeners3(container, state6, modalId, t, onClose);
|
|
87683
|
+
});
|
|
87469
87684
|
});
|
|
87470
87685
|
document.getElementById(`${modalId}-checkfix-filter`)?.addEventListener("change", (e) => {
|
|
87471
87686
|
state6.checkFixFilter = e.target.value;
|
|
@@ -87563,35 +87778,7 @@ function setupEventListeners3(container, state6, modalId, t, onClose) {
|
|
|
87563
87778
|
}
|
|
87564
87779
|
});
|
|
87565
87780
|
document.getElementById(`${modalId}-select-all`)?.addEventListener("click", () => {
|
|
87566
|
-
|
|
87567
|
-
types: filterTypes,
|
|
87568
|
-
deviceTypes: filterDeviceTypes,
|
|
87569
|
-
deviceProfiles: filterDeviceProfiles,
|
|
87570
|
-
statuses: filterStatuses,
|
|
87571
|
-
telemetryKeys: filterTelemetryKeys
|
|
87572
|
-
} = state6.deviceFilters;
|
|
87573
|
-
let filteredDevices = state6.devices.filter((d) => {
|
|
87574
|
-
if (filterTypes.length > 0 && !filterTypes.includes(d.type || "")) return false;
|
|
87575
|
-
if (filterDeviceTypes.length > 0 && !filterDeviceTypes.includes(d.serverAttrs?.deviceType || ""))
|
|
87576
|
-
return false;
|
|
87577
|
-
if (filterDeviceProfiles.length > 0 && !filterDeviceProfiles.includes(d.serverAttrs?.deviceProfile || ""))
|
|
87578
|
-
return false;
|
|
87579
|
-
if (filterStatuses.length > 0) {
|
|
87580
|
-
const status = d.latestTelemetry?.connectionStatus?.value || "offline";
|
|
87581
|
-
if (!filterStatuses.includes(status)) return false;
|
|
87582
|
-
}
|
|
87583
|
-
if (filterTelemetryKeys.length > 0) {
|
|
87584
|
-
const telem = d.latestTelemetry;
|
|
87585
|
-
const hasMatch = filterTelemetryKeys.some((k) => {
|
|
87586
|
-
if (k === "pulses") return telem?.pulses != null;
|
|
87587
|
-
if (k === "consumption") return telem?.consumption != null;
|
|
87588
|
-
return false;
|
|
87589
|
-
});
|
|
87590
|
-
if (!hasMatch) return false;
|
|
87591
|
-
}
|
|
87592
|
-
return true;
|
|
87593
|
-
});
|
|
87594
|
-
state6.selectedDevices = [...filteredDevices];
|
|
87781
|
+
state6.selectedDevices = [...getGridVisibleDevices(state6)];
|
|
87595
87782
|
const listEl = document.getElementById(`${modalId}-device-list`);
|
|
87596
87783
|
const savedScroll = listEl ? listEl.scrollTop : 0;
|
|
87597
87784
|
renderModal4(container, state6, modalId, t);
|
|
@@ -87707,12 +87894,27 @@ function setupEventListeners3(container, state6, modalId, t, onClose) {
|
|
|
87707
87894
|
document.getElementById(`${modalId}-bulk-profile-save`)?.addEventListener("click", async () => {
|
|
87708
87895
|
await saveBulkProfile(state6, container, modalId, t, onClose);
|
|
87709
87896
|
});
|
|
87710
|
-
document.getElementById(`${modalId}-bulk-owner`)?.addEventListener("click", () => {
|
|
87897
|
+
document.getElementById(`${modalId}-bulk-owner`)?.addEventListener("click", async () => {
|
|
87711
87898
|
if (!state6.selectedCustomer) {
|
|
87712
87899
|
alert("Selecione um Customer primeiro no Step 1");
|
|
87713
87900
|
return;
|
|
87714
87901
|
}
|
|
87715
87902
|
state6.bulkOwnerModal.open = true;
|
|
87903
|
+
if (!state6.bulkOwnerModal.targetCustomerId) {
|
|
87904
|
+
state6.bulkOwnerModal.targetCustomerId = state6.selectedCustomer.id?.id || "";
|
|
87905
|
+
}
|
|
87906
|
+
renderModal4(container, state6, modalId, t);
|
|
87907
|
+
setupEventListeners3(container, state6, modalId, t, onClose);
|
|
87908
|
+
if (state6.customers.length === 0) {
|
|
87909
|
+
await loadCustomers(state6, container, modalId, t, onClose);
|
|
87910
|
+
if (state6.bulkOwnerModal.open) {
|
|
87911
|
+
renderModal4(container, state6, modalId, t);
|
|
87912
|
+
setupEventListeners3(container, state6, modalId, t, onClose);
|
|
87913
|
+
}
|
|
87914
|
+
}
|
|
87915
|
+
});
|
|
87916
|
+
document.getElementById(`${modalId}-bulk-owner-customer`)?.addEventListener("change", (e) => {
|
|
87917
|
+
state6.bulkOwnerModal.targetCustomerId = e.target.value;
|
|
87716
87918
|
renderModal4(container, state6, modalId, t);
|
|
87717
87919
|
setupEventListeners3(container, state6, modalId, t, onClose);
|
|
87718
87920
|
});
|
|
@@ -87757,6 +87959,10 @@ function setupEventListeners3(container, state6, modalId, t, onClose) {
|
|
|
87757
87959
|
if (!state6.selectedCustomer || state6.selectedDevices.length === 0) return;
|
|
87758
87960
|
await handleBulkSyncIngestionId(state6, container, modalId, t, onClose);
|
|
87759
87961
|
});
|
|
87962
|
+
document.getElementById(`${modalId}-bulk-delete`)?.addEventListener("click", async () => {
|
|
87963
|
+
if (state6.selectedDevices.length === 0) return;
|
|
87964
|
+
await handleBulkDeleteDevices(state6, container, modalId, t, onClose);
|
|
87965
|
+
});
|
|
87760
87966
|
document.getElementById(`${modalId}-clear-gcdr-ids`)?.addEventListener("click", () => {
|
|
87761
87967
|
if (!state6.selectedCustomer) {
|
|
87762
87968
|
alert("Selecione um Customer primeiro no Step 1");
|
|
@@ -87886,6 +88092,14 @@ function setupEventListeners3(container, state6, modalId, t, onClose) {
|
|
|
87886
88092
|
});
|
|
87887
88093
|
}
|
|
87888
88094
|
});
|
|
88095
|
+
const applyRelCb = document.getElementById(
|
|
88096
|
+
`${modalId}-lojas-apply-relation`
|
|
88097
|
+
);
|
|
88098
|
+
if (applyRelCb) {
|
|
88099
|
+
applyRelCb.addEventListener("change", () => {
|
|
88100
|
+
state6.lojasApplyRelation = applyRelCb.checked;
|
|
88101
|
+
});
|
|
88102
|
+
}
|
|
87889
88103
|
}
|
|
87890
88104
|
document.getElementById(`${modalId}-change-owner`)?.addEventListener("click", () => {
|
|
87891
88105
|
const form = document.getElementById(`${modalId}-change-owner-form`);
|
|
@@ -88107,7 +88321,8 @@ Total de devices no customer: ${ingestionDevices.length}`
|
|
|
88107
88321
|
state6.devices,
|
|
88108
88322
|
state6.deviceSearchTerm.toLowerCase(),
|
|
88109
88323
|
state6.deviceFilters,
|
|
88110
|
-
state6.deviceSort
|
|
88324
|
+
state6.deviceSort,
|
|
88325
|
+
state6
|
|
88111
88326
|
);
|
|
88112
88327
|
}
|
|
88113
88328
|
}
|
|
@@ -88247,7 +88462,7 @@ function filterCustomerList(container, customers, search, selected, sort) {
|
|
|
88247
88462
|
item.style.display = matches ? "flex" : "none";
|
|
88248
88463
|
});
|
|
88249
88464
|
}
|
|
88250
|
-
function filterDeviceListVisual(container, devices, search, filters, sort) {
|
|
88465
|
+
function filterDeviceListVisual(container, devices, search, filters, sort, state6) {
|
|
88251
88466
|
const listContainer = container.querySelector('[id$="-device-list"]');
|
|
88252
88467
|
if (!listContainer) return;
|
|
88253
88468
|
let filtered = devices.filter((d) => {
|
|
@@ -88267,7 +88482,7 @@ function filterDeviceListVisual(container, devices, search, filters, sort) {
|
|
|
88267
88482
|
item.style.display = "none";
|
|
88268
88483
|
return;
|
|
88269
88484
|
}
|
|
88270
|
-
const matchesSearch = !search ||
|
|
88485
|
+
const matchesSearch = !search || buildDeviceSearchHaystack(device, state6).includes(search);
|
|
88271
88486
|
const el2 = item;
|
|
88272
88487
|
const isTableRow = el2.tagName === "TR";
|
|
88273
88488
|
el2.style.display = matchesSearch ? isTableRow ? "" : "flex" : "none";
|
|
@@ -88618,6 +88833,56 @@ async function loadLojasData(state6, container, modalId, t, onClose) {
|
|
|
88618
88833
|
setupEventListeners3(container, state6, modalId, t, onClose);
|
|
88619
88834
|
}
|
|
88620
88835
|
}
|
|
88836
|
+
async function handleBulkDeleteDevices(state6, container, modalId, t, onClose) {
|
|
88837
|
+
const devices = [...state6.selectedDevices];
|
|
88838
|
+
if (devices.length === 0) return;
|
|
88839
|
+
const preview = devices.slice(0, 8).map((d) => `\u2022 ${d.name || d.label || getEntityId(d)}`).join("\n");
|
|
88840
|
+
const more = devices.length > 8 ? `
|
|
88841
|
+
\u2026 e mais ${devices.length - 8}` : "";
|
|
88842
|
+
const confirm1 = `\u26A0\uFE0F DELETAR ${devices.length} dispositivo(s) do ThingsBoard?
|
|
88843
|
+
|
|
88844
|
+
${preview}${more}
|
|
88845
|
+
|
|
88846
|
+
Esta a\xE7\xE3o \xE9 IRREVERS\xCDVEL \u2014 os dispositivos, suas rela\xE7\xF5es e telemetria ser\xE3o removidos permanentemente.`;
|
|
88847
|
+
if (!confirm(confirm1)) return;
|
|
88848
|
+
if (!confirm(
|
|
88849
|
+
`Confirma\xE7\xE3o final: deletar ${devices.length} dispositivo(s)?
|
|
88850
|
+
Esta a\xE7\xE3o N\xC3O pode ser desfeita.`
|
|
88851
|
+
))
|
|
88852
|
+
return;
|
|
88853
|
+
showBusyProgress(`Deletando ${devices.length} dispositivos...`, devices.length);
|
|
88854
|
+
let okCount = 0;
|
|
88855
|
+
let failCount = 0;
|
|
88856
|
+
const errors = [];
|
|
88857
|
+
const deletedIds = /* @__PURE__ */ new Set();
|
|
88858
|
+
for (let i = 0; i < devices.length; i++) {
|
|
88859
|
+
const d = devices[i];
|
|
88860
|
+
const id = getEntityId(d);
|
|
88861
|
+
try {
|
|
88862
|
+
await tbDelete(state6, `/api/device/${id}`);
|
|
88863
|
+
deletedIds.add(id);
|
|
88864
|
+
okCount++;
|
|
88865
|
+
} catch (err) {
|
|
88866
|
+
failCount++;
|
|
88867
|
+
errors.push(`${d.name || id}: ${err.message}`);
|
|
88868
|
+
}
|
|
88869
|
+
updateBusyProgress(i + 1);
|
|
88870
|
+
}
|
|
88871
|
+
hideBusyProgress();
|
|
88872
|
+
state6.devices = state6.devices.filter((d) => !deletedIds.has(getEntityId(d)));
|
|
88873
|
+
state6.selectedDevices = state6.selectedDevices.filter((d) => !deletedIds.has(getEntityId(d)));
|
|
88874
|
+
if (state6.selectedDevice && deletedIds.has(getEntityId(state6.selectedDevice))) {
|
|
88875
|
+
state6.selectedDevice = null;
|
|
88876
|
+
}
|
|
88877
|
+
renderModal4(container, state6, modalId, t);
|
|
88878
|
+
setupEventListeners3(container, state6, modalId, t, onClose);
|
|
88879
|
+
alert(
|
|
88880
|
+
`Dispositivos deletados: ${okCount}` + (failCount > 0 ? `
|
|
88881
|
+
Falhas: ${failCount}
|
|
88882
|
+
${errors.slice(0, 5).join("\n")}` + (errors.length > 5 ? `
|
|
88883
|
+
\u2026 e mais ${errors.length - 5} erros` : "") : "")
|
|
88884
|
+
);
|
|
88885
|
+
}
|
|
88621
88886
|
async function handleBulkSyncIngestionId(state6, container, modalId, t, onClose) {
|
|
88622
88887
|
if (!state6.selectedCustomer || state6.selectedDevices.length === 0) return;
|
|
88623
88888
|
const customerId = getEntityId(state6.selectedCustomer);
|
|
@@ -88764,15 +89029,17 @@ async function handleLojasApply(state6, container, modalId, t, onClose) {
|
|
|
88764
89029
|
if (identifierInput) data[i].identifier = identifierInput.value;
|
|
88765
89030
|
}
|
|
88766
89031
|
const activeConfig = state6.lojasConfig ?? CUSTOM_MODES[0];
|
|
89032
|
+
const applyRelation = state6.lojasApplyRelation;
|
|
88767
89033
|
const confirmMsg = `Aplicar configura\xE7\xE3o "${activeConfig.label}" para ${data.length} dispositivos?
|
|
88768
89034
|
|
|
88769
89035
|
Cada device receber\xE1:
|
|
88770
89036
|
- Label atualizado (etiqueta)
|
|
88771
89037
|
- Profile: ${activeConfig.deviceProfile}
|
|
88772
89038
|
- deviceType/deviceProfile: ${activeConfig.deviceType} / ${activeConfig.deviceProfile}
|
|
88773
|
-
|
|
89039
|
+
` + (applyRelation ? `- Rela\xE7\xF5es existentes removidas
|
|
88774
89040
|
- Nova rela\xE7\xE3o: Customer \u2192 Device (Contains)
|
|
88775
|
-
|
|
89041
|
+
` : `- Rela\xE7\xF5es N\xC3O ser\xE3o alteradas (checkbox de rela\xE7\xE3o desmarcado)
|
|
89042
|
+
`) + `
|
|
88776
89043
|
Deseja continuar?`;
|
|
88777
89044
|
if (!confirm(confirmMsg)) return;
|
|
88778
89045
|
showBusyProgress(`Aplicando ${activeConfig.label}...`, data.length);
|
|
@@ -88800,31 +89067,33 @@ Deseja continuar?`;
|
|
|
88800
89067
|
attrs.ingestionId = d.ingestionId;
|
|
88801
89068
|
}
|
|
88802
89069
|
await tbPost(state6, `/api/plugins/telemetry/DEVICE/${d.deviceId}/attributes/SERVER_SCOPE`, attrs);
|
|
88803
|
-
if (
|
|
88804
|
-
|
|
88805
|
-
|
|
88806
|
-
|
|
88807
|
-
|
|
88808
|
-
|
|
88809
|
-
|
|
88810
|
-
|
|
88811
|
-
|
|
88812
|
-
|
|
88813
|
-
|
|
88814
|
-
|
|
88815
|
-
|
|
88816
|
-
|
|
88817
|
-
|
|
89070
|
+
if (applyRelation) {
|
|
89071
|
+
if (d.currentRelations.length > 0) {
|
|
89072
|
+
updateBusyProgress(i + 1, `[${i + 1}/${data.length}] ${d.name}: Removendo rela\xE7\xF5es...`);
|
|
89073
|
+
for (const rel of d.currentRelations) {
|
|
89074
|
+
try {
|
|
89075
|
+
const params = new URLSearchParams({
|
|
89076
|
+
fromId: rel.from.id,
|
|
89077
|
+
fromType: rel.from.entityType,
|
|
89078
|
+
toId: d.deviceId,
|
|
89079
|
+
toType: "DEVICE",
|
|
89080
|
+
relationType: rel.type || "Contains",
|
|
89081
|
+
relationTypeGroup: rel.typeGroup || "COMMON"
|
|
89082
|
+
});
|
|
89083
|
+
await tbDelete(state6, `/api/relation?${params.toString()}`);
|
|
89084
|
+
} catch (e) {
|
|
89085
|
+
console.warn("[UpsellModal] Error deleting relation for LOJAS:", e);
|
|
89086
|
+
}
|
|
88818
89087
|
}
|
|
88819
89088
|
}
|
|
89089
|
+
updateBusyProgress(i + 1, `[${i + 1}/${data.length}] ${d.name}: Criando rela\xE7\xE3o...`);
|
|
89090
|
+
await tbPost(state6, "/api/relation", {
|
|
89091
|
+
from: { entityType: "CUSTOMER", id: customerId },
|
|
89092
|
+
to: { entityType: "DEVICE", id: d.deviceId },
|
|
89093
|
+
type: "Contains",
|
|
89094
|
+
typeGroup: "COMMON"
|
|
89095
|
+
});
|
|
88820
89096
|
}
|
|
88821
|
-
updateBusyProgress(i + 1, `[${i + 1}/${data.length}] ${d.name}: Criando rela\xE7\xE3o...`);
|
|
88822
|
-
await tbPost(state6, "/api/relation", {
|
|
88823
|
-
from: { entityType: "CUSTOMER", id: customerId },
|
|
88824
|
-
to: { entityType: "DEVICE", id: d.deviceId },
|
|
88825
|
-
type: "Contains",
|
|
88826
|
-
typeGroup: "COMMON"
|
|
88827
|
-
});
|
|
88828
89097
|
successCount++;
|
|
88829
89098
|
} catch (error) {
|
|
88830
89099
|
errorCount++;
|
|
@@ -89112,8 +89381,111 @@ async function loadDeviceTelemetryInBatch(state6, container, modalId, t, onClose
|
|
|
89112
89381
|
hideBusyProgress();
|
|
89113
89382
|
}
|
|
89114
89383
|
}
|
|
89115
|
-
|
|
89116
|
-
const
|
|
89384
|
+
function openCheckFixScopeDialog(state6, onConfirm) {
|
|
89385
|
+
const DIALOG_ID = "myio-upsell-cf-scope-dialog";
|
|
89386
|
+
document.getElementById(DIALOG_ID)?.remove();
|
|
89387
|
+
const c = getThemeColors5(state6.theme);
|
|
89388
|
+
const allDevices = state6.devices;
|
|
89389
|
+
const allTypes = [...new Set(allDevices.map((d) => d.type).filter(Boolean))].sort();
|
|
89390
|
+
let namePattern = "";
|
|
89391
|
+
let caseSensitive = false;
|
|
89392
|
+
const selectedTypes = /* @__PURE__ */ new Set();
|
|
89393
|
+
function computeSubset() {
|
|
89394
|
+
let result = allDevices;
|
|
89395
|
+
if (namePattern) {
|
|
89396
|
+
const pat = caseSensitive ? namePattern : namePattern.toLowerCase();
|
|
89397
|
+
result = result.filter((d) => {
|
|
89398
|
+
const n = caseSensitive ? d.name || "" : (d.name || "").toLowerCase();
|
|
89399
|
+
return n.includes(pat);
|
|
89400
|
+
});
|
|
89401
|
+
}
|
|
89402
|
+
if (selectedTypes.size > 0) {
|
|
89403
|
+
result = result.filter((d) => selectedTypes.has(d.type || ""));
|
|
89404
|
+
}
|
|
89405
|
+
return result;
|
|
89406
|
+
}
|
|
89407
|
+
const overlay = document.createElement("div");
|
|
89408
|
+
overlay.id = DIALOG_ID;
|
|
89409
|
+
overlay.style.cssText = "position:fixed;inset:0;z-index:10000;background:rgba(0,0,0,0.55);display:flex;align-items:center;justify-content:center;font-family:Roboto,Inter,system-ui,sans-serif;";
|
|
89410
|
+
overlay.innerHTML = `
|
|
89411
|
+
<div style="background:${c.surface};border-radius:12px;width:480px;max-width:92vw;max-height:88vh;
|
|
89412
|
+
overflow:hidden;display:flex;flex-direction:column;box-shadow:0 12px 40px rgba(0,0,0,0.4);">
|
|
89413
|
+
<div style="background:${MYIO_PURPLE};color:#fff;padding:12px 16px;font-size:14px;font-weight:700;">
|
|
89414
|
+
\u{1F52C} Escopo do CHECK & FIX
|
|
89415
|
+
</div>
|
|
89416
|
+
<div style="padding:16px;overflow-y:auto;display:flex;flex-direction:column;gap:14px;">
|
|
89417
|
+
<div style="font-size:11px;color:${c.textMuted};">
|
|
89418
|
+
Os filtros abaixo se <strong>combinam (E)</strong>. Deixe ambos vazios para
|
|
89419
|
+
diagnosticar todos os ${allDevices.length} dispositivos.
|
|
89420
|
+
</div>
|
|
89421
|
+
|
|
89422
|
+
<div style="display:flex;flex-direction:column;gap:8px;">
|
|
89423
|
+
<span style="font-size:12px;font-weight:600;color:${c.text};">Filtro por nome</span>
|
|
89424
|
+
<input id="cf-scope-name-input" type="text" placeholder="nome cont\xE9m\u2026 (ex.: TEMP.)" style="
|
|
89425
|
+
font-size:12px;padding:7px 9px;border-radius:6px;border:1px solid ${c.border};
|
|
89426
|
+
background:${c.inputBg};color:${c.text};" />
|
|
89427
|
+
<label style="display:flex;gap:6px;align-items:center;font-size:11px;color:${c.textMuted};cursor:pointer;">
|
|
89428
|
+
<input id="cf-scope-case" type="checkbox" style="accent-color:${MYIO_PURPLE};" />
|
|
89429
|
+
Diferenciar mai\xFAsculas/min\xFAsculas (case-sensitive)
|
|
89430
|
+
</label>
|
|
89431
|
+
</div>
|
|
89432
|
+
|
|
89433
|
+
<div style="display:flex;flex-direction:column;gap:8px;">
|
|
89434
|
+
<span style="font-size:12px;font-weight:600;color:${c.text};">Filtro por tipo (device.type)</span>
|
|
89435
|
+
<div style="display:flex;flex-wrap:wrap;gap:6px;max-height:160px;overflow-y:auto;">
|
|
89436
|
+
${allTypes.length === 0 ? `<span style="font-size:11px;color:${c.textMuted};">Nenhum tipo dispon\xEDvel.</span>` : allTypes.map(
|
|
89437
|
+
(tp) => `
|
|
89438
|
+
<label style="display:flex;gap:5px;align-items:center;font-size:11px;color:${c.text};
|
|
89439
|
+
cursor:pointer;border:1px solid ${c.border};border-radius:6px;padding:3px 8px;background:${c.cardBg};">
|
|
89440
|
+
<input type="checkbox" class="cf-scope-type-cb" value="${tp}" style="accent-color:${MYIO_PURPLE};" />
|
|
89441
|
+
${tp}
|
|
89442
|
+
</label>`
|
|
89443
|
+
).join("")}
|
|
89444
|
+
</div>
|
|
89445
|
+
</div>
|
|
89446
|
+
</div>
|
|
89447
|
+
<div style="padding:12px 16px;border-top:1px solid ${c.border};display:flex;justify-content:flex-end;gap:8px;">
|
|
89448
|
+
<button id="cf-scope-cancel" style="font-size:12px;font-weight:600;padding:8px 14px;border-radius:6px;
|
|
89449
|
+
border:1px solid ${c.border};background:${c.cardBg};color:${c.text};cursor:pointer;">Cancelar</button>
|
|
89450
|
+
<button id="cf-scope-run" style="font-size:12px;font-weight:700;padding:8px 14px;border-radius:6px;
|
|
89451
|
+
border:none;background:${MYIO_PURPLE};color:#fff;cursor:pointer;">Executar diagn\xF3stico (${allDevices.length})</button>
|
|
89452
|
+
</div>
|
|
89453
|
+
</div>`;
|
|
89454
|
+
document.body.appendChild(overlay);
|
|
89455
|
+
const runBtn = overlay.querySelector("#cf-scope-run");
|
|
89456
|
+
function refresh() {
|
|
89457
|
+
const n = computeSubset().length;
|
|
89458
|
+
runBtn.textContent = `Executar diagn\xF3stico (${n})`;
|
|
89459
|
+
runBtn.disabled = n === 0;
|
|
89460
|
+
runBtn.style.opacity = n === 0 ? "0.5" : "1";
|
|
89461
|
+
runBtn.style.cursor = n === 0 ? "not-allowed" : "pointer";
|
|
89462
|
+
}
|
|
89463
|
+
overlay.querySelector("#cf-scope-name-input").addEventListener("input", (e) => {
|
|
89464
|
+
namePattern = e.target.value;
|
|
89465
|
+
refresh();
|
|
89466
|
+
});
|
|
89467
|
+
overlay.querySelector("#cf-scope-case").addEventListener("change", (e) => {
|
|
89468
|
+
caseSensitive = e.target.checked;
|
|
89469
|
+
refresh();
|
|
89470
|
+
});
|
|
89471
|
+
overlay.querySelectorAll(".cf-scope-type-cb").forEach((cb) => {
|
|
89472
|
+
cb.addEventListener("change", () => {
|
|
89473
|
+
if (cb.checked) selectedTypes.add(cb.value);
|
|
89474
|
+
else selectedTypes.delete(cb.value);
|
|
89475
|
+
refresh();
|
|
89476
|
+
});
|
|
89477
|
+
});
|
|
89478
|
+
overlay.querySelector("#cf-scope-cancel").addEventListener("click", () => overlay.remove());
|
|
89479
|
+
runBtn.addEventListener("click", () => {
|
|
89480
|
+
const subset = computeSubset();
|
|
89481
|
+
if (subset.length === 0) return;
|
|
89482
|
+
overlay.remove();
|
|
89483
|
+
onConfirm(subset);
|
|
89484
|
+
});
|
|
89485
|
+
refresh();
|
|
89486
|
+
}
|
|
89487
|
+
async function runCheckFixRoutine(state6, container, modalId, t, onClose, scopeDevices) {
|
|
89488
|
+
const devices = scopeDevices ?? state6.devices;
|
|
89117
89489
|
if (devices.length === 0) return;
|
|
89118
89490
|
const BATCH_SIZE = 5;
|
|
89119
89491
|
const BATCH_DELAY_MS = 1500;
|
|
@@ -89396,10 +89768,11 @@ ${errors.slice(0, 5).join("\n")}` + (errors.length > 5 ? `
|
|
|
89396
89768
|
}
|
|
89397
89769
|
async function saveBulkOwner(state6, container, modalId, t, onClose) {
|
|
89398
89770
|
const devices = state6.selectedDevices;
|
|
89399
|
-
const newCustomerId = state6.selectedCustomer?.id?.id;
|
|
89400
|
-
const
|
|
89771
|
+
const newCustomerId = state6.bulkOwnerModal.targetCustomerId || state6.selectedCustomer?.id?.id;
|
|
89772
|
+
const targetCustomer = state6.customers.find((c) => c.id?.id === newCustomerId) || state6.selectedCustomer;
|
|
89773
|
+
const customerName = targetCustomer?.name || targetCustomer?.title || "Unknown";
|
|
89401
89774
|
if (!newCustomerId) {
|
|
89402
|
-
alert("Por favor, selecione um Customer
|
|
89775
|
+
alert("Por favor, selecione um Customer de destino.");
|
|
89403
89776
|
return;
|
|
89404
89777
|
}
|
|
89405
89778
|
if (devices.length === 0) {
|