myio-js-library 0.1.538 → 0.1.540
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 +580 -4
- package/dist/index.d.cts +83 -1
- package/dist/index.d.ts +83 -1
- package/dist/index.js +579 -4
- package/dist/myio-js-library.umd.js +579 -4
- package/dist/myio-js-library.umd.min.js +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -155,6 +155,7 @@ __export(src_exports, {
|
|
|
155
155
|
DeviceOperationalCardGridController: () => DeviceOperationalCardGridController,
|
|
156
156
|
DeviceOperationalCardGridView: () => DeviceOperationalCardGridView,
|
|
157
157
|
DeviceOperationalCardView: () => DeviceOperationalCardView,
|
|
158
|
+
DeviceProfileTab: () => DeviceProfileTab,
|
|
158
159
|
DeviceStatusType: () => DeviceStatusType,
|
|
159
160
|
DomainType: () => DomainType,
|
|
160
161
|
ENERGY_SORT_OPTIONS: () => ENERGY_SORT_OPTIONS,
|
|
@@ -890,7 +891,7 @@ module.exports = __toCommonJS(src_exports);
|
|
|
890
891
|
// package.json
|
|
891
892
|
var package_default = {
|
|
892
893
|
name: "myio-js-library",
|
|
893
|
-
version: "0.1.
|
|
894
|
+
version: "0.1.540",
|
|
894
895
|
description: "A clean, standalone JS SDK for MYIO projects",
|
|
895
896
|
license: "MIT",
|
|
896
897
|
repository: "github:gh-myio/myio-js-library",
|
|
@@ -2373,6 +2374,7 @@ var DEVICE_TYPE_CONFIG = {
|
|
|
2373
2374
|
RELOGIO: { category: "energy", image: deviceIcons.RELOGIO },
|
|
2374
2375
|
ENTRADA: { category: "energy", image: deviceIcons.ENTRADA },
|
|
2375
2376
|
SUBESTACAO: { category: "energy", image: deviceIcons.SUBESTACAO },
|
|
2377
|
+
TRANSFORMADOR: { category: "energy", image: deviceIcons.TRANSFORMADOR },
|
|
2376
2378
|
FANCOIL: { category: "energy", image: deviceIcons.FANCOIL },
|
|
2377
2379
|
CHILLER: { category: "energy", image: deviceIcons.CHILLER },
|
|
2378
2380
|
AR_CONDICIONADO: { category: "energy", image: null },
|
|
@@ -60307,6 +60309,517 @@ var ExclusionGroupsTab = class {
|
|
|
60307
60309
|
}
|
|
60308
60310
|
};
|
|
60309
60311
|
|
|
60312
|
+
// src/components/premium-modals/settings/device-profile/DeviceProfileTab.ts
|
|
60313
|
+
var TB_NULL_CUSTOMER_ID = "13814000-1dd2-11b2-8080-808080808080";
|
|
60314
|
+
var STYLES_ID3 = "dpt-styles";
|
|
60315
|
+
var EDITABLE_ATTRS = [
|
|
60316
|
+
{ key: "deviceProfile", label: "Device Profile" },
|
|
60317
|
+
{ key: "identifier", label: "Identifier" },
|
|
60318
|
+
{ key: "gcdrDeviceId", label: "GCDR Device ID" },
|
|
60319
|
+
{ key: "gcdrCustomerId", label: "GCDR Customer ID" },
|
|
60320
|
+
{ key: "gcdrAssetId", label: "GCDR Asset ID" }
|
|
60321
|
+
];
|
|
60322
|
+
var READONLY_ATTRS = [
|
|
60323
|
+
{ key: "deviceType", label: "Device Type" },
|
|
60324
|
+
{ key: "slaveId", label: "Slave ID" },
|
|
60325
|
+
{ key: "centralId", label: "Central ID" },
|
|
60326
|
+
{ key: "centralName", label: "Central" }
|
|
60327
|
+
];
|
|
60328
|
+
var DeviceProfileTab = class {
|
|
60329
|
+
config;
|
|
60330
|
+
entity = null;
|
|
60331
|
+
attributes = [];
|
|
60332
|
+
ownerName = null;
|
|
60333
|
+
deviceProfileOptions = null;
|
|
60334
|
+
customerOptions = null;
|
|
60335
|
+
constructor(config2) {
|
|
60336
|
+
this.config = config2;
|
|
60337
|
+
}
|
|
60338
|
+
async init() {
|
|
60339
|
+
this.config.container.innerHTML = this.getLoadingHTML();
|
|
60340
|
+
this.injectStyles();
|
|
60341
|
+
await this.loadAll();
|
|
60342
|
+
this.renderAndAttach();
|
|
60343
|
+
}
|
|
60344
|
+
destroy() {
|
|
60345
|
+
}
|
|
60346
|
+
// --------------------------------------------------------------------------
|
|
60347
|
+
// Private — data
|
|
60348
|
+
// --------------------------------------------------------------------------
|
|
60349
|
+
headers(extra) {
|
|
60350
|
+
return { "X-Authorization": `Bearer ${this.config.jwtToken}`, ...extra || {} };
|
|
60351
|
+
}
|
|
60352
|
+
async loadAll() {
|
|
60353
|
+
const [entity, attributes] = await Promise.all([this.fetchEntity(), this.fetchAttributes()]);
|
|
60354
|
+
this.entity = entity;
|
|
60355
|
+
this.attributes = attributes;
|
|
60356
|
+
const customerId = entity?.customerId?.id;
|
|
60357
|
+
this.ownerName = customerId && customerId !== TB_NULL_CUSTOMER_ID ? await this.fetchCustomerName(customerId) : null;
|
|
60358
|
+
}
|
|
60359
|
+
async fetchEntity() {
|
|
60360
|
+
try {
|
|
60361
|
+
const { tbBaseUrl, deviceId } = this.config;
|
|
60362
|
+
const res = await fetch(`${tbBaseUrl}/api/device/${deviceId}`, { headers: this.headers() });
|
|
60363
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
60364
|
+
return await res.json();
|
|
60365
|
+
} catch (e) {
|
|
60366
|
+
console.warn("[DeviceProfileTab] Failed to fetch device entity:", e);
|
|
60367
|
+
return null;
|
|
60368
|
+
}
|
|
60369
|
+
}
|
|
60370
|
+
async fetchAttributes() {
|
|
60371
|
+
try {
|
|
60372
|
+
const { tbBaseUrl, deviceId } = this.config;
|
|
60373
|
+
const res = await fetch(
|
|
60374
|
+
`${tbBaseUrl}/api/plugins/telemetry/DEVICE/${deviceId}/values/attributes/SERVER_SCOPE`,
|
|
60375
|
+
{ headers: this.headers() }
|
|
60376
|
+
);
|
|
60377
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
60378
|
+
const raw = await res.json();
|
|
60379
|
+
return Array.isArray(raw) ? raw : [];
|
|
60380
|
+
} catch (e) {
|
|
60381
|
+
console.warn("[DeviceProfileTab] Failed to fetch device attributes:", e);
|
|
60382
|
+
return [];
|
|
60383
|
+
}
|
|
60384
|
+
}
|
|
60385
|
+
async fetchCustomerName(customerId) {
|
|
60386
|
+
try {
|
|
60387
|
+
const res = await fetch(`${this.config.tbBaseUrl}/api/customer/${customerId}`, {
|
|
60388
|
+
headers: this.headers()
|
|
60389
|
+
});
|
|
60390
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
60391
|
+
const c = await res.json();
|
|
60392
|
+
return c?.title || c?.name || null;
|
|
60393
|
+
} catch (e) {
|
|
60394
|
+
console.warn("[DeviceProfileTab] Failed to fetch customer name:", e);
|
|
60395
|
+
return null;
|
|
60396
|
+
}
|
|
60397
|
+
}
|
|
60398
|
+
async fetchDeviceProfileOptions() {
|
|
60399
|
+
if (this.deviceProfileOptions) return this.deviceProfileOptions;
|
|
60400
|
+
try {
|
|
60401
|
+
const res = await fetch(`${this.config.tbBaseUrl}/api/deviceProfile/names?activeOnly=true`, {
|
|
60402
|
+
headers: this.headers()
|
|
60403
|
+
});
|
|
60404
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
60405
|
+
const raw = await res.json();
|
|
60406
|
+
this.deviceProfileOptions = raw.map((p) => ({ id: p.id.id, name: p.name }));
|
|
60407
|
+
} catch (e) {
|
|
60408
|
+
console.warn("[DeviceProfileTab] Failed to fetch device profiles:", e);
|
|
60409
|
+
this.deviceProfileOptions = [];
|
|
60410
|
+
}
|
|
60411
|
+
return this.deviceProfileOptions;
|
|
60412
|
+
}
|
|
60413
|
+
async fetchCustomerOptions() {
|
|
60414
|
+
if (this.customerOptions) return this.customerOptions;
|
|
60415
|
+
try {
|
|
60416
|
+
const res = await fetch(
|
|
60417
|
+
`${this.config.tbBaseUrl}/api/customers?pageSize=200&page=0&sortProperty=title&sortOrder=ASC`,
|
|
60418
|
+
{ headers: this.headers() }
|
|
60419
|
+
);
|
|
60420
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
60421
|
+
const json = await res.json();
|
|
60422
|
+
const data = json?.data || [];
|
|
60423
|
+
this.customerOptions = data.map((c) => ({ id: c.id.id, title: c.title }));
|
|
60424
|
+
} catch (e) {
|
|
60425
|
+
console.warn("[DeviceProfileTab] Failed to fetch customers:", e);
|
|
60426
|
+
this.customerOptions = [];
|
|
60427
|
+
}
|
|
60428
|
+
return this.customerOptions;
|
|
60429
|
+
}
|
|
60430
|
+
attrValue(key) {
|
|
60431
|
+
return this.attributes.find((a) => a.key === key)?.value;
|
|
60432
|
+
}
|
|
60433
|
+
// --------------------------------------------------------------------------
|
|
60434
|
+
// Private — writes (one call per field, mirroring openUpsellModal.ts 1:1)
|
|
60435
|
+
// --------------------------------------------------------------------------
|
|
60436
|
+
/** `POST .../attributes/SERVER_SCOPE` — same call as the bulk "Forçar Atributo" tool. */
|
|
60437
|
+
async saveAttribute(key, value) {
|
|
60438
|
+
const { tbBaseUrl, deviceId } = this.config;
|
|
60439
|
+
const res = await fetch(`${tbBaseUrl}/api/plugins/telemetry/DEVICE/${deviceId}/attributes/SERVER_SCOPE`, {
|
|
60440
|
+
method: "POST",
|
|
60441
|
+
headers: this.headers({ "Content-Type": "application/json" }),
|
|
60442
|
+
body: JSON.stringify({ [key]: value })
|
|
60443
|
+
});
|
|
60444
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
60445
|
+
const idx = this.attributes.findIndex((a) => a.key === key);
|
|
60446
|
+
if (idx >= 0) this.attributes[idx] = { key, value };
|
|
60447
|
+
else this.attributes.push({ key, value });
|
|
60448
|
+
}
|
|
60449
|
+
/** Fetch → patch one field → `POST /api/device` (same pattern as saveEntityLabel/changeDeviceProfile). */
|
|
60450
|
+
async saveEntityField(patch) {
|
|
60451
|
+
const { tbBaseUrl, deviceId } = this.config;
|
|
60452
|
+
const getRes = await fetch(`${tbBaseUrl}/api/device/${deviceId}`, { headers: this.headers() });
|
|
60453
|
+
if (!getRes.ok) throw new Error(`HTTP ${getRes.status}`);
|
|
60454
|
+
const device = await getRes.json();
|
|
60455
|
+
const postRes = await fetch(`${tbBaseUrl}/api/device`, {
|
|
60456
|
+
method: "POST",
|
|
60457
|
+
headers: this.headers({ "Content-Type": "application/json" }),
|
|
60458
|
+
body: JSON.stringify({ ...device, ...patch })
|
|
60459
|
+
});
|
|
60460
|
+
if (!postRes.ok) throw new Error(`HTTP ${postRes.status}`);
|
|
60461
|
+
this.entity = { ...this.entity, ...patch };
|
|
60462
|
+
}
|
|
60463
|
+
/** `POST /api/owner/CUSTOMER/{id}/DEVICE/{id}` with CE fallback — same as changeDeviceOwner(). */
|
|
60464
|
+
async saveOwner(newCustomerId) {
|
|
60465
|
+
const { tbBaseUrl, deviceId } = this.config;
|
|
60466
|
+
const peRes = await fetch(`${tbBaseUrl}/api/owner/CUSTOMER/${newCustomerId}/DEVICE/${deviceId}`, {
|
|
60467
|
+
method: "POST",
|
|
60468
|
+
headers: this.headers({ "Content-Type": "application/json" }),
|
|
60469
|
+
body: JSON.stringify([])
|
|
60470
|
+
});
|
|
60471
|
+
if (!peRes.ok) {
|
|
60472
|
+
if (peRes.status !== 404) throw new Error(`HTTP ${peRes.status}`);
|
|
60473
|
+
const ceRes = await fetch(`${tbBaseUrl}/api/customer/${newCustomerId}/device/${deviceId}`, {
|
|
60474
|
+
method: "POST",
|
|
60475
|
+
headers: this.headers({ "Content-Type": "application/json" }),
|
|
60476
|
+
body: JSON.stringify({})
|
|
60477
|
+
});
|
|
60478
|
+
if (!ceRes.ok) throw new Error(`HTTP ${ceRes.status}`);
|
|
60479
|
+
}
|
|
60480
|
+
this.entity = { ...this.entity, customerId: { id: newCustomerId, entityType: "CUSTOMER" } };
|
|
60481
|
+
this.ownerName = await this.fetchCustomerName(newCustomerId);
|
|
60482
|
+
}
|
|
60483
|
+
/**
|
|
60484
|
+
* Single-device "Sync Ingestion ID" — identical match algorithm to the bulk
|
|
60485
|
+
* tool (customer's `ingestionId` attr → this device's centralId+slaveId →
|
|
60486
|
+
* paged ingestion-API device list → match by gateway hardwareUuid+slaveId),
|
|
60487
|
+
* scoped to one device. Reuses the same ingestion-API app credentials
|
|
60488
|
+
* already shipped in openUpsellModal.ts (not a new secret).
|
|
60489
|
+
*/
|
|
60490
|
+
async syncIngestionId() {
|
|
60491
|
+
const INGESTION_AUTH_URL2 = "https://api.data.apps.myio-bas.com/api/v1/auth";
|
|
60492
|
+
const INGESTION_API_BASE2 = "https://api.data.apps.myio-bas.com/api/v1";
|
|
60493
|
+
const INGESTION_CLIENT_ID2 = "myioadmi_mekj7xw7_sccibe";
|
|
60494
|
+
const INGESTION_CLIENT_SECRET2 = "KmXhNZu0uydeWZ8scAi43h7P2pntGoWkdzNVMSjbVj3slEsZ5hGVXyayshgJAoqA";
|
|
60495
|
+
const centralId = this.attrValue("centralId");
|
|
60496
|
+
const slaveId = this.attrValue("slaveId");
|
|
60497
|
+
if (!centralId || slaveId === void 0 || slaveId === null || slaveId === "") {
|
|
60498
|
+
return { ok: false, message: "Device sem centralId/slaveId \u2014 n\xE3o \xE9 poss\xEDvel localizar na Ingestion API." };
|
|
60499
|
+
}
|
|
60500
|
+
const customerId = this.entity?.customerId?.id;
|
|
60501
|
+
if (!customerId || customerId === TB_NULL_CUSTOMER_ID) {
|
|
60502
|
+
return { ok: false, message: "Device sem Owner (customer) \u2014 n\xE3o \xE9 poss\xEDvel resolver o customer da Ingestion API." };
|
|
60503
|
+
}
|
|
60504
|
+
const customerAttrs = await this.fetchCustomerAttrs(customerId);
|
|
60505
|
+
const ingestionCustomerId = customerAttrs.find((a) => a.key === "ingestionId")?.value;
|
|
60506
|
+
if (!ingestionCustomerId) {
|
|
60507
|
+
return { ok: false, message: "Customer n\xE3o tem atributo ingestionId configurado no ThingsBoard." };
|
|
60508
|
+
}
|
|
60509
|
+
const authRes = await fetch(INGESTION_AUTH_URL2, {
|
|
60510
|
+
method: "POST",
|
|
60511
|
+
headers: { "Content-Type": "application/json" },
|
|
60512
|
+
body: JSON.stringify({ client_id: INGESTION_CLIENT_ID2, client_secret: INGESTION_CLIENT_SECRET2 })
|
|
60513
|
+
});
|
|
60514
|
+
if (!authRes.ok) throw new Error(`Ingestion auth HTTP ${authRes.status}`);
|
|
60515
|
+
const authJson = await authRes.json();
|
|
60516
|
+
const token = authJson?.access_token;
|
|
60517
|
+
if (!token) throw new Error("Ingestion auth: access_token ausente na resposta.");
|
|
60518
|
+
const slaveIdNum = typeof slaveId === "string" ? parseInt(slaveId, 10) : slaveId;
|
|
60519
|
+
let page = 1;
|
|
60520
|
+
let match = null;
|
|
60521
|
+
for (; page <= 50 && !match; page++) {
|
|
60522
|
+
const res = await fetch(
|
|
60523
|
+
`${INGESTION_API_BASE2}/management/devices?page=${page}&limit=100&customerId=${encodeURIComponent(
|
|
60524
|
+
ingestionCustomerId
|
|
60525
|
+
)}&includeInactive=false&sortBy=name&sortOrder=asc`,
|
|
60526
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
60527
|
+
);
|
|
60528
|
+
if (!res.ok) throw new Error(`Ingestion devices HTTP ${res.status}`);
|
|
60529
|
+
const json = await res.json();
|
|
60530
|
+
const list = json?.data || [];
|
|
60531
|
+
for (const d of list) {
|
|
60532
|
+
const gwId = d.gateway?.hardwareUuid || d.gatewayId || d.gateway?.id;
|
|
60533
|
+
if (gwId === centralId && d.slaveId === slaveIdNum) {
|
|
60534
|
+
match = { id: d.id };
|
|
60535
|
+
break;
|
|
60536
|
+
}
|
|
60537
|
+
}
|
|
60538
|
+
const totalPages = json?.pagination?.pages || 1;
|
|
60539
|
+
if (page >= totalPages) break;
|
|
60540
|
+
}
|
|
60541
|
+
if (!match) {
|
|
60542
|
+
return { ok: false, message: `Nenhum device na Ingestion API com centralId=${centralId} e slaveId=${slaveIdNum}.` };
|
|
60543
|
+
}
|
|
60544
|
+
await this.saveAttribute("ingestionId", match.id);
|
|
60545
|
+
return { ok: true, message: `ingestionId atualizado: ${match.id}` };
|
|
60546
|
+
}
|
|
60547
|
+
async fetchCustomerAttrs(customerId) {
|
|
60548
|
+
const res = await fetch(
|
|
60549
|
+
`${this.config.tbBaseUrl}/api/plugins/telemetry/CUSTOMER/${customerId}/values/attributes/SERVER_SCOPE`,
|
|
60550
|
+
{ headers: this.headers() }
|
|
60551
|
+
);
|
|
60552
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
60553
|
+
const raw = await res.json();
|
|
60554
|
+
return Array.isArray(raw) ? raw : [];
|
|
60555
|
+
}
|
|
60556
|
+
// --------------------------------------------------------------------------
|
|
60557
|
+
// Private — render
|
|
60558
|
+
// --------------------------------------------------------------------------
|
|
60559
|
+
esc(s) {
|
|
60560
|
+
return s.replace(
|
|
60561
|
+
/[&<>"']/g,
|
|
60562
|
+
(c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]
|
|
60563
|
+
);
|
|
60564
|
+
}
|
|
60565
|
+
displayText(value) {
|
|
60566
|
+
if (value === void 0 || value === null || value === "") return "\u2014";
|
|
60567
|
+
if (typeof value === "object") {
|
|
60568
|
+
try {
|
|
60569
|
+
return JSON.stringify(value);
|
|
60570
|
+
} catch {
|
|
60571
|
+
return String(value);
|
|
60572
|
+
}
|
|
60573
|
+
}
|
|
60574
|
+
return String(value);
|
|
60575
|
+
}
|
|
60576
|
+
formatDate(ts) {
|
|
60577
|
+
if (!ts) return "\u2014";
|
|
60578
|
+
try {
|
|
60579
|
+
return new Date(ts).toLocaleString("pt-BR");
|
|
60580
|
+
} catch {
|
|
60581
|
+
return String(ts);
|
|
60582
|
+
}
|
|
60583
|
+
}
|
|
60584
|
+
getLoadingHTML() {
|
|
60585
|
+
return `
|
|
60586
|
+
<div style="padding: 20px; text-align: center; color: #6c757d;">
|
|
60587
|
+
<div class="loading-spinner"></div>
|
|
60588
|
+
<p>Carregando perfil do dispositivo...</p>
|
|
60589
|
+
</div>`;
|
|
60590
|
+
}
|
|
60591
|
+
/** One editable/read-only key-value row. `kind` picks the edit widget. */
|
|
60592
|
+
renderRow(field, label, value, kind) {
|
|
60593
|
+
const text2 = this.esc(this.displayText(value));
|
|
60594
|
+
if (kind === "readonly") {
|
|
60595
|
+
return `
|
|
60596
|
+
<div class="dpt-row" data-field="${this.esc(field)}">
|
|
60597
|
+
<span class="dpt-label">${this.esc(label)}</span>
|
|
60598
|
+
<span class="dpt-value">${text2}</span>
|
|
60599
|
+
</div>`;
|
|
60600
|
+
}
|
|
60601
|
+
const syncBtn = kind === "ingestion" ? `<button type="button" class="dpt-icon-btn" data-action="sync-ingestion" title="Buscar ingestionId na Ingestion API">\u{1F504}</button>` : "";
|
|
60602
|
+
return `
|
|
60603
|
+
<div class="dpt-row dpt-row--editable" data-field="${this.esc(field)}" data-kind="${kind}">
|
|
60604
|
+
<span class="dpt-label">${this.esc(label)}</span>
|
|
60605
|
+
<span class="dpt-value-wrap">
|
|
60606
|
+
<span class="dpt-value" data-view>${text2}</span>
|
|
60607
|
+
${syncBtn}
|
|
60608
|
+
<button type="button" class="dpt-icon-btn" data-action="edit" title="Editar">\u270F\uFE0F</button>
|
|
60609
|
+
</span>
|
|
60610
|
+
</div>`;
|
|
60611
|
+
}
|
|
60612
|
+
renderTab() {
|
|
60613
|
+
const e = this.entity;
|
|
60614
|
+
const ownerId = e?.customerId?.id;
|
|
60615
|
+
const ownerDisplay = ownerId && ownerId !== TB_NULL_CUSTOMER_ID ? this.ownerName || ownerId : "\u2014 (sem customer)";
|
|
60616
|
+
const highlightedRows = [
|
|
60617
|
+
this.renderRow("type", "Type (TB)", e?.type, "entity-profile"),
|
|
60618
|
+
this.renderRow("label", "Label", e?.label, "entity-label"),
|
|
60619
|
+
this.renderRow("owner", "Owner", ownerDisplay, "owner")
|
|
60620
|
+
].join("");
|
|
60621
|
+
const editableAttrRows = EDITABLE_ATTRS.map(
|
|
60622
|
+
({ key, label }) => key === "ingestionId" ? "" : this.renderRow(key, label, this.attrValue(key), "attr-text")
|
|
60623
|
+
).join("");
|
|
60624
|
+
const ingestionRow = this.renderRow("ingestionId", "Ingestion ID", this.attrValue("ingestionId"), "ingestion");
|
|
60625
|
+
const readonlyRows = READONLY_ATTRS.map(
|
|
60626
|
+
({ key, label }) => this.renderRow(key, label, this.attrValue(key), "readonly")
|
|
60627
|
+
).join("");
|
|
60628
|
+
const highlightedKeys = /* @__PURE__ */ new Set([...EDITABLE_ATTRS.map((a) => a.key), ...READONLY_ATTRS.map((a) => a.key)]);
|
|
60629
|
+
const otherAttrs = this.attributes.filter((a) => !highlightedKeys.has(a.key)).sort((a, b) => a.key.localeCompare(b.key));
|
|
60630
|
+
const otherRows = otherAttrs.length ? otherAttrs.map((a) => this.renderRow(a.key, a.key, a.value, "readonly")).join("") : `<div class="dpt-empty">Nenhum outro atributo SERVER_SCOPE.</div>`;
|
|
60631
|
+
return `
|
|
60632
|
+
<div class="dpt-tab">
|
|
60633
|
+
<div class="dpt-notice">\u{1F512} Aba vis\xEDvel apenas para administradores MyIO \u2014 altera dados direto no ThingsBoard.</div>
|
|
60634
|
+
|
|
60635
|
+
<div class="dpt-section">
|
|
60636
|
+
<div class="dpt-section-title">Identifica\xE7\xE3o (ThingsBoard)</div>
|
|
60637
|
+
<div class="dpt-card">
|
|
60638
|
+
${highlightedRows}
|
|
60639
|
+
<div class="dpt-row"><span class="dpt-label">Nome</span><span class="dpt-value">${this.esc(this.displayText(e?.name))}</span></div>
|
|
60640
|
+
<div class="dpt-row"><span class="dpt-label">Device Profile (TB)</span><span class="dpt-value">${this.esc(this.displayText(e?.deviceProfileName))}</span></div>
|
|
60641
|
+
<div class="dpt-row"><span class="dpt-label">ID</span><span class="dpt-value dpt-mono">${this.esc(this.displayText(e?.id?.id || this.config.deviceId))}</span></div>
|
|
60642
|
+
<div class="dpt-row"><span class="dpt-label">Criado em</span><span class="dpt-value">${this.formatDate(e?.createdTime)}</span></div>
|
|
60643
|
+
</div>
|
|
60644
|
+
</div>
|
|
60645
|
+
|
|
60646
|
+
<div class="dpt-section">
|
|
60647
|
+
<div class="dpt-section-title">Classifica\xE7\xE3o (atributos da aplica\xE7\xE3o)</div>
|
|
60648
|
+
<div class="dpt-card">
|
|
60649
|
+
${editableAttrRows}
|
|
60650
|
+
${ingestionRow}
|
|
60651
|
+
${readonlyRows}
|
|
60652
|
+
</div>
|
|
60653
|
+
</div>
|
|
60654
|
+
|
|
60655
|
+
<div class="dpt-section">
|
|
60656
|
+
<div class="dpt-section-title">Todos os atributos SERVER_SCOPE (${this.attributes.length})</div>
|
|
60657
|
+
<div class="dpt-card dpt-card--scroll">
|
|
60658
|
+
${otherRows}
|
|
60659
|
+
</div>
|
|
60660
|
+
</div>
|
|
60661
|
+
</div>`;
|
|
60662
|
+
}
|
|
60663
|
+
renderAndAttach() {
|
|
60664
|
+
this.config.container.innerHTML = this.renderTab();
|
|
60665
|
+
this.attachListeners();
|
|
60666
|
+
}
|
|
60667
|
+
// --------------------------------------------------------------------------
|
|
60668
|
+
// Private — interaction
|
|
60669
|
+
// --------------------------------------------------------------------------
|
|
60670
|
+
attachListeners() {
|
|
60671
|
+
const root = this.config.container;
|
|
60672
|
+
root.addEventListener("click", (ev) => this.onClick(ev));
|
|
60673
|
+
}
|
|
60674
|
+
async onClick(ev) {
|
|
60675
|
+
const target = ev.target;
|
|
60676
|
+
const btn = target.closest("button[data-action]");
|
|
60677
|
+
if (!btn) return;
|
|
60678
|
+
const row = btn.closest(".dpt-row");
|
|
60679
|
+
if (!row) return;
|
|
60680
|
+
const field = row.dataset.field || "";
|
|
60681
|
+
const kind = row.dataset.kind || "";
|
|
60682
|
+
const action = btn.dataset.action;
|
|
60683
|
+
if (action === "sync-ingestion") {
|
|
60684
|
+
await this.handleSyncIngestion(row, btn);
|
|
60685
|
+
return;
|
|
60686
|
+
}
|
|
60687
|
+
if (action === "edit") {
|
|
60688
|
+
await this.beginEdit(row, field, kind);
|
|
60689
|
+
return;
|
|
60690
|
+
}
|
|
60691
|
+
if (action === "save") {
|
|
60692
|
+
await this.commitEdit(row, field, kind);
|
|
60693
|
+
return;
|
|
60694
|
+
}
|
|
60695
|
+
if (action === "cancel") {
|
|
60696
|
+
this.renderAndAttach();
|
|
60697
|
+
}
|
|
60698
|
+
}
|
|
60699
|
+
async handleSyncIngestion(row, btn) {
|
|
60700
|
+
btn.disabled = true;
|
|
60701
|
+
const original = btn.textContent;
|
|
60702
|
+
btn.textContent = "\u23F3";
|
|
60703
|
+
try {
|
|
60704
|
+
const result = await this.syncIngestionId();
|
|
60705
|
+
if (result.ok) {
|
|
60706
|
+
this.renderAndAttach();
|
|
60707
|
+
} else {
|
|
60708
|
+
alert(result.message);
|
|
60709
|
+
}
|
|
60710
|
+
} catch (e) {
|
|
60711
|
+
alert("Erro ao sincronizar ingestionId: " + (e instanceof Error ? e.message : String(e)));
|
|
60712
|
+
} finally {
|
|
60713
|
+
btn.disabled = false;
|
|
60714
|
+
btn.textContent = original;
|
|
60715
|
+
}
|
|
60716
|
+
}
|
|
60717
|
+
async beginEdit(row, field, kind) {
|
|
60718
|
+
const wrap = row.querySelector(".dpt-value-wrap");
|
|
60719
|
+
if (!wrap) return;
|
|
60720
|
+
if (kind === "entity-profile") {
|
|
60721
|
+
const options = await this.fetchDeviceProfileOptions();
|
|
60722
|
+
const currentId = this.entity?.deviceProfileId?.id || "";
|
|
60723
|
+
wrap.innerHTML = this.editorHtml(
|
|
60724
|
+
`<select class="dpt-input" data-edit>
|
|
60725
|
+
${options.map((o) => `<option value="${this.esc(o.id)}" ${o.id === currentId ? "selected" : ""}>${this.esc(o.name)}</option>`).join("")}
|
|
60726
|
+
</select>`
|
|
60727
|
+
);
|
|
60728
|
+
} else if (kind === "owner") {
|
|
60729
|
+
const options = await this.fetchCustomerOptions();
|
|
60730
|
+
const currentId = this.entity?.customerId?.id || "";
|
|
60731
|
+
wrap.innerHTML = this.editorHtml(
|
|
60732
|
+
`<select class="dpt-input" data-edit>
|
|
60733
|
+
<option value="">\u2014 selecione \u2014</option>
|
|
60734
|
+
${options.map((o) => `<option value="${this.esc(o.id)}" ${o.id === currentId ? "selected" : ""}>${this.esc(o.title)}</option>`).join("")}
|
|
60735
|
+
</select>`
|
|
60736
|
+
);
|
|
60737
|
+
} else {
|
|
60738
|
+
const currentValue = kind === "entity-label" ? this.entity?.label || "" : this.attrValue(field) || "";
|
|
60739
|
+
wrap.innerHTML = this.editorHtml(
|
|
60740
|
+
`<input class="dpt-input" data-edit type="text" value="${this.esc(String(currentValue))}" />`
|
|
60741
|
+
);
|
|
60742
|
+
const input = wrap.querySelector("input[data-edit]");
|
|
60743
|
+
input?.focus();
|
|
60744
|
+
input?.select();
|
|
60745
|
+
}
|
|
60746
|
+
}
|
|
60747
|
+
editorHtml(inputHtml) {
|
|
60748
|
+
return `
|
|
60749
|
+
${inputHtml}
|
|
60750
|
+
<button type="button" class="dpt-icon-btn" data-action="save" title="Salvar">\u2714\uFE0F</button>
|
|
60751
|
+
<button type="button" class="dpt-icon-btn" data-action="cancel" title="Cancelar">\u2716\uFE0F</button>
|
|
60752
|
+
`;
|
|
60753
|
+
}
|
|
60754
|
+
async commitEdit(row, field, kind) {
|
|
60755
|
+
const editEl = row.querySelector("[data-edit]");
|
|
60756
|
+
if (!editEl) return;
|
|
60757
|
+
const value = editEl.value.trim();
|
|
60758
|
+
const saveBtn = row.querySelector('button[data-action="save"]');
|
|
60759
|
+
if (saveBtn) saveBtn.disabled = true;
|
|
60760
|
+
try {
|
|
60761
|
+
if (kind === "entity-label") {
|
|
60762
|
+
await this.saveEntityField({ label: value });
|
|
60763
|
+
} else if (kind === "entity-profile") {
|
|
60764
|
+
if (!value) throw new Error("Selecione um Device Profile.");
|
|
60765
|
+
await this.saveEntityField({ deviceProfileId: { id: value, entityType: "DEVICE_PROFILE" } });
|
|
60766
|
+
} else if (kind === "owner") {
|
|
60767
|
+
if (!value) throw new Error("Selecione um Owner.");
|
|
60768
|
+
await this.saveOwner(value);
|
|
60769
|
+
} else {
|
|
60770
|
+
await this.saveAttribute(field, value);
|
|
60771
|
+
}
|
|
60772
|
+
this.renderAndAttach();
|
|
60773
|
+
} catch (e) {
|
|
60774
|
+
alert("Erro ao salvar: " + (e instanceof Error ? e.message : String(e)));
|
|
60775
|
+
} finally {
|
|
60776
|
+
if (saveBtn) saveBtn.disabled = false;
|
|
60777
|
+
}
|
|
60778
|
+
}
|
|
60779
|
+
injectStyles() {
|
|
60780
|
+
if (document.getElementById(STYLES_ID3)) return;
|
|
60781
|
+
const style = document.createElement("style");
|
|
60782
|
+
style.id = STYLES_ID3;
|
|
60783
|
+
style.textContent = `
|
|
60784
|
+
.dpt-tab { padding: 4px 2px 20px; }
|
|
60785
|
+
.dpt-notice {
|
|
60786
|
+
background: #fef3c7; border: 1px solid #f59e0b; color: #92400e;
|
|
60787
|
+
border-radius: 8px; padding: 8px 12px; font-size: 12px; margin-bottom: 16px;
|
|
60788
|
+
}
|
|
60789
|
+
.dpt-section { margin-bottom: 20px; }
|
|
60790
|
+
.dpt-section-title {
|
|
60791
|
+
font-size: 12px; font-weight: 700; text-transform: uppercase;
|
|
60792
|
+
letter-spacing: 0.6px; color: #6b7280; margin-bottom: 8px;
|
|
60793
|
+
}
|
|
60794
|
+
.dpt-card {
|
|
60795
|
+
background: #fff; border: 1px solid #e5e7eb; border-radius: 10px;
|
|
60796
|
+
padding: 4px 16px;
|
|
60797
|
+
}
|
|
60798
|
+
.dpt-card--scroll { max-height: 280px; overflow-y: auto; }
|
|
60799
|
+
.dpt-row {
|
|
60800
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
60801
|
+
gap: 12px; padding: 9px 0; border-bottom: 1px solid #f1f5f9;
|
|
60802
|
+
}
|
|
60803
|
+
.dpt-row:last-child { border-bottom: none; }
|
|
60804
|
+
.dpt-label { font-size: 12px; color: #6b7280; font-weight: 600; white-space: nowrap; }
|
|
60805
|
+
.dpt-value { font-size: 13px; color: #111827; text-align: right; word-break: break-word; }
|
|
60806
|
+
.dpt-value-wrap { display: flex; align-items: center; gap: 6px; }
|
|
60807
|
+
.dpt-mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
|
|
60808
|
+
.dpt-empty { padding: 16px 0; text-align: center; color: #9ca3af; font-size: 13px; }
|
|
60809
|
+
.dpt-icon-btn {
|
|
60810
|
+
border: none; background: transparent; cursor: pointer; font-size: 13px;
|
|
60811
|
+
padding: 2px 4px; border-radius: 6px; line-height: 1;
|
|
60812
|
+
}
|
|
60813
|
+
.dpt-icon-btn:hover { background: #f3f4f6; }
|
|
60814
|
+
.dpt-input {
|
|
60815
|
+
font-size: 12px; padding: 4px 6px; border: 1px solid #d1d5db; border-radius: 6px;
|
|
60816
|
+
max-width: 220px;
|
|
60817
|
+
}
|
|
60818
|
+
`;
|
|
60819
|
+
document.head.appendChild(style);
|
|
60820
|
+
}
|
|
60821
|
+
};
|
|
60822
|
+
|
|
60310
60823
|
// src/services/freshdesk/FreshdeskClient.ts
|
|
60311
60824
|
var FreshdeskClient_exports = {};
|
|
60312
60825
|
__export(FreshdeskClient_exports, {
|
|
@@ -61890,6 +62403,7 @@ var SettingsModalView = class _SettingsModalView {
|
|
|
61890
62403
|
chamadosTabHandle = null;
|
|
61891
62404
|
// Exclusão de Grupos tab
|
|
61892
62405
|
exclusionGroupsTab = null;
|
|
62406
|
+
deviceProfileTab = null;
|
|
61893
62407
|
currentTab = "general";
|
|
61894
62408
|
currentUser = null;
|
|
61895
62409
|
permissions = null;
|
|
@@ -61968,6 +62482,7 @@ var SettingsModalView = class _SettingsModalView {
|
|
|
61968
62482
|
this.initAlarmsTab();
|
|
61969
62483
|
this.initChamadosTab();
|
|
61970
62484
|
this.initExclusionGroupsTab();
|
|
62485
|
+
this.initDeviceProfileTab();
|
|
61971
62486
|
if (this.config.isGateway && this.isSuperAdmin()) {
|
|
61972
62487
|
this.bindIncidentsTabEvents();
|
|
61973
62488
|
this.initIncidentsDateRange();
|
|
@@ -62183,6 +62698,44 @@ var SettingsModalView = class _SettingsModalView {
|
|
|
62183
62698
|
container.innerHTML = '<p style="color:#dc3545;padding:20px;text-align:center;">Erro ao carregar configura\xE7\xF5es de exclus\xE3o</p>';
|
|
62184
62699
|
}
|
|
62185
62700
|
}
|
|
62701
|
+
// Perfil de Dispositivo: ThingsBoard identity + SERVER_SCOPE attribute dump,
|
|
62702
|
+
// with inline edit/force for the fields support needs to fix — MyIO admins
|
|
62703
|
+
// only (writes ThingsBoard entity/attributes/owner directly).
|
|
62704
|
+
async initDeviceProfileTab() {
|
|
62705
|
+
if (!this.isSuperAdmin()) {
|
|
62706
|
+
this.modal.querySelector('[data-tab="device-profile"]')?.remove();
|
|
62707
|
+
return;
|
|
62708
|
+
}
|
|
62709
|
+
const container = this.modal.querySelector("#device-profile-tab-content");
|
|
62710
|
+
if (!container) return;
|
|
62711
|
+
const { deviceId, jwtToken, tbBaseUrl } = this.config;
|
|
62712
|
+
if (!deviceId || !jwtToken) {
|
|
62713
|
+
container.innerHTML = `
|
|
62714
|
+
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;
|
|
62715
|
+
min-height:320px;padding:40px 24px;text-align:center;">
|
|
62716
|
+
<div style="font-size:15px;font-weight:600;color:#374151;margin-bottom:8px;">
|
|
62717
|
+
Perfil de dispositivo n\xE3o dispon\xEDvel
|
|
62718
|
+
</div>
|
|
62719
|
+
<div style="font-size:13px;color:#9ca3af;max-width:320px;line-height:1.6;">
|
|
62720
|
+
deviceId ou jwtToken n\xE3o dispon\xEDveis para carregar os atributos.
|
|
62721
|
+
</div>
|
|
62722
|
+
</div>`;
|
|
62723
|
+
return;
|
|
62724
|
+
}
|
|
62725
|
+
try {
|
|
62726
|
+
this.deviceProfileTab = new DeviceProfileTab({
|
|
62727
|
+
container,
|
|
62728
|
+
deviceId,
|
|
62729
|
+
jwtToken,
|
|
62730
|
+
tbBaseUrl: tbBaseUrl ?? window.location.origin
|
|
62731
|
+
});
|
|
62732
|
+
await this.deviceProfileTab.init();
|
|
62733
|
+
console.log("[SettingsModalView] DeviceProfileTab initialized");
|
|
62734
|
+
} catch (error) {
|
|
62735
|
+
console.error("[SettingsModalView] Failed to initialize DeviceProfileTab:", error);
|
|
62736
|
+
container.innerHTML = '<p style="color:#dc3545;padding:20px;text-align:center;">Erro ao carregar perfil do dispositivo</p>';
|
|
62737
|
+
}
|
|
62738
|
+
}
|
|
62186
62739
|
// RFC-0104 / RFC-0180 / RFC-0198: Switch between tabs
|
|
62187
62740
|
switchTab(tab) {
|
|
62188
62741
|
this.currentTab = tab;
|
|
@@ -62195,11 +62748,13 @@ var SettingsModalView = class _SettingsModalView {
|
|
|
62195
62748
|
const alarmsContent = this.modal.querySelector("#alarms-tab-content");
|
|
62196
62749
|
const chamadosContent = this.modal.querySelector("#chamados-tab-content");
|
|
62197
62750
|
const exclusionGroupsContent = this.modal.querySelector("#exclusion-groups-tab-content");
|
|
62751
|
+
const deviceProfileContent = this.modal.querySelector("#device-profile-tab-content");
|
|
62198
62752
|
const incidentsContent = this.modal.querySelector("#incidents-tab-content");
|
|
62199
62753
|
if (generalContent) generalContent.style.display = tab === "general" ? "block" : "none";
|
|
62200
62754
|
if (annotationsContent) annotationsContent.style.display = tab === "annotations" ? "block" : "none";
|
|
62201
62755
|
if (alarmsContent) alarmsContent.style.display = tab === "alarms" ? "block" : "none";
|
|
62202
62756
|
if (chamadosContent) chamadosContent.style.display = tab === "chamados" ? "block" : "none";
|
|
62757
|
+
if (deviceProfileContent) deviceProfileContent.style.display = tab === "device-profile" ? "block" : "none";
|
|
62203
62758
|
if (exclusionGroupsContent) exclusionGroupsContent.style.display = tab === "exclusion-groups" ? "block" : "none";
|
|
62204
62759
|
if (incidentsContent) incidentsContent.style.display = tab === "incidents" ? "block" : "none";
|
|
62205
62760
|
const saveBtn = this.modal.querySelector(".btn-save");
|
|
@@ -62226,6 +62781,10 @@ var SettingsModalView = class _SettingsModalView {
|
|
|
62226
62781
|
this.exclusionGroupsTab.destroy();
|
|
62227
62782
|
this.exclusionGroupsTab = null;
|
|
62228
62783
|
}
|
|
62784
|
+
if (this.deviceProfileTab) {
|
|
62785
|
+
this.deviceProfileTab.destroy();
|
|
62786
|
+
this.deviceProfileTab = null;
|
|
62787
|
+
}
|
|
62229
62788
|
if (this.incidentsDateRangeController) {
|
|
62230
62789
|
this.incidentsDateRangeController.destroy();
|
|
62231
62790
|
this.incidentsDateRangeController = null;
|
|
@@ -62396,6 +62955,15 @@ var SettingsModalView = class _SettingsModalView {
|
|
|
62396
62955
|
Excluir Grupos
|
|
62397
62956
|
<span class="modal-tab-badge modal-tab-badge--exclusion" id="tab-badge-exclusion-groups" style="display:none"></span>
|
|
62398
62957
|
</button>
|
|
62958
|
+
<!-- Perfil de Dispositivo Tab \u2014 MyIO admins only (edits ThingsBoard directly) -->
|
|
62959
|
+
${this.isSuperAdmin() ? `<button type="button" class="modal-tab" data-tab="device-profile">
|
|
62960
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
62961
|
+
<rect x="3" y="3" width="18" height="18" rx="2"></rect>
|
|
62962
|
+
<line x1="3" y1="9" x2="21" y2="9"></line>
|
|
62963
|
+
<line x1="9" y1="21" x2="9" y2="9"></line>
|
|
62964
|
+
</svg>
|
|
62965
|
+
Perfil de Dispositivo
|
|
62966
|
+
</button>` : ""}
|
|
62399
62967
|
<!-- RFC-0232: Incidentes (interpola\xE7\xE3o) \u2014 central + admin MyIO only -->
|
|
62400
62968
|
${this.config.isGateway && this.isSuperAdmin() ? `<button type="button" class="modal-tab" data-tab="incidents">
|
|
62401
62969
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
@@ -62443,6 +63011,13 @@ var SettingsModalView = class _SettingsModalView {
|
|
|
62443
63011
|
<p>Carregando configura\xE7\xF5es de exclus\xE3o...</p>
|
|
62444
63012
|
</div>
|
|
62445
63013
|
</div>
|
|
63014
|
+
<!-- Perfil de Dispositivo Tab Content -->
|
|
63015
|
+
<div id="device-profile-tab-content" class="tab-content" style="display: none;">
|
|
63016
|
+
<div style="padding: 20px; text-align: center; color: #6c757d;">
|
|
63017
|
+
<div class="loading-spinner"></div>
|
|
63018
|
+
<p>Carregando perfil do dispositivo...</p>
|
|
63019
|
+
</div>
|
|
63020
|
+
</div>
|
|
62446
63021
|
<!-- RFC-0232: Incidentes (interpola\xE7\xE3o) Tab Content \u2014 central + admin MyIO only.
|
|
62447
63022
|
Rendered synchronously (data comes pre-fetched via config.interpolatedSlots,
|
|
62448
63023
|
same as getGatewayInfoHTML() \u2014 no async loading state needed here). -->
|
|
@@ -162493,7 +163068,7 @@ var VirtualList = class {
|
|
|
162493
163068
|
|
|
162494
163069
|
// src/components/header-annotations-panel/ExportModal.ts
|
|
162495
163070
|
var MODAL_DOM_ID = "myio-annotations-export-modal";
|
|
162496
|
-
var
|
|
163071
|
+
var STYLES_ID4 = "myio-annotations-export-modal-styles";
|
|
162497
163072
|
var STYLES4 = `
|
|
162498
163073
|
.${MODAL_DOM_ID}-backdrop {
|
|
162499
163074
|
position: fixed; inset: 0; z-index: 99999;
|
|
@@ -162570,9 +163145,9 @@ var STYLES4 = `
|
|
|
162570
163145
|
`;
|
|
162571
163146
|
function injectStyles22() {
|
|
162572
163147
|
if (typeof document === "undefined") return;
|
|
162573
|
-
if (document.getElementById(
|
|
163148
|
+
if (document.getElementById(STYLES_ID4)) return;
|
|
162574
163149
|
const el2 = document.createElement("style");
|
|
162575
|
-
el2.id =
|
|
163150
|
+
el2.id = STYLES_ID4;
|
|
162576
163151
|
el2.textContent = STYLES4;
|
|
162577
163152
|
document.head.appendChild(el2);
|
|
162578
163153
|
}
|
|
@@ -165805,6 +166380,7 @@ var version = package_default.version || "0.0.0";
|
|
|
165805
166380
|
DeviceOperationalCardGridController,
|
|
165806
166381
|
DeviceOperationalCardGridView,
|
|
165807
166382
|
DeviceOperationalCardView,
|
|
166383
|
+
DeviceProfileTab,
|
|
165808
166384
|
DeviceStatusType,
|
|
165809
166385
|
DomainType,
|
|
165810
166386
|
ENERGY_SORT_OPTIONS,
|