myio-js-library 0.1.487 → 0.1.490
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 +748 -23
- package/dist/index.d.cts +160 -17
- package/dist/index.js +745 -23
- package/dist/myio-js-library.umd.js +745 -23
- package/dist/myio-js-library.umd.min.js +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -711,6 +711,7 @@ __export(index_exports, {
|
|
|
711
711
|
MenuShoppingView: () => MenuShoppingView,
|
|
712
712
|
MenuView: () => MenuView,
|
|
713
713
|
ModalHeader: () => ModalHeader,
|
|
714
|
+
MyIOAuthContext: () => MyIOAuthContext,
|
|
714
715
|
MyIOChartModal: () => MyIOChartModal,
|
|
715
716
|
MyIODraggableCard: () => MyIODraggableCard,
|
|
716
717
|
MyIOSelectionStore: () => MyIOSelectionStore,
|
|
@@ -739,6 +740,7 @@ __export(index_exports, {
|
|
|
739
740
|
OperationalGeneralListController: () => OperationalGeneralListController,
|
|
740
741
|
OperationalGeneralListView: () => OperationalGeneralListView,
|
|
741
742
|
OperationalHeaderDevicesGridView: () => OperationalHeaderDevicesGridView,
|
|
743
|
+
PERM: () => PERM,
|
|
742
744
|
POWER_LIMITS_DEVICE_TYPES: () => DEVICE_TYPES,
|
|
743
745
|
POWER_LIMITS_STATUS_CONFIG: () => STATUS_CONFIG,
|
|
744
746
|
POWER_LIMITS_TELEMETRY_TYPES: () => TELEMETRY_TYPES2,
|
|
@@ -1007,6 +1009,7 @@ __export(index_exports, {
|
|
|
1007
1009
|
groupByDay: () => groupByDay,
|
|
1008
1010
|
handleDeviceType: () => handleDeviceType,
|
|
1009
1011
|
hasSelectedDays: () => hasSelectedDays,
|
|
1012
|
+
initMyIOAuthContext: () => initMyIOAuthContext,
|
|
1010
1013
|
initOnOffTimelineTooltips: () => initOnOffTimelineTooltips,
|
|
1011
1014
|
injectActionButtonStyles: () => injectActionButtonStyles,
|
|
1012
1015
|
injectAlarmsNotificationsPanelStyles: () => injectAlarmsNotificationsPanelStyles,
|
|
@@ -1159,7 +1162,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
1159
1162
|
// package.json
|
|
1160
1163
|
var package_default = {
|
|
1161
1164
|
name: "myio-js-library",
|
|
1162
|
-
version: "0.1.
|
|
1165
|
+
version: "0.1.490",
|
|
1163
1166
|
description: "A clean, standalone JS SDK for MYIO projects",
|
|
1164
1167
|
license: "MIT",
|
|
1165
1168
|
repository: "github:gh-myio/myio-js-library",
|
|
@@ -58602,15 +58605,24 @@ var AlarmsTab = class {
|
|
|
58602
58605
|
* Falls back to prefetchedAlarms prop when ASO is not available (e.g. standalone showcase).
|
|
58603
58606
|
*/
|
|
58604
58607
|
readAlarmsFromASO() {
|
|
58605
|
-
const
|
|
58608
|
+
const win = window;
|
|
58609
|
+
const aso = win.AlarmServiceOrchestrator;
|
|
58610
|
+
const showOffline = win.MyIOOrchestrator?.showOfflineAlarms === true;
|
|
58611
|
+
const _OFFLINE_TYPES = ["DEVICE OFFLINE", "DISPOSITIVO OFFLINE"];
|
|
58612
|
+
const _isOffline = (a) => {
|
|
58613
|
+
const t = ((a.title ?? a.alarmType) || "").toUpperCase();
|
|
58614
|
+
return _OFFLINE_TYPES.some((ex) => t.startsWith(ex)) || a.alarmType === "connectivity";
|
|
58615
|
+
};
|
|
58616
|
+
let alarms = [];
|
|
58606
58617
|
if (aso && this.config.gcdrDeviceId) {
|
|
58607
|
-
|
|
58608
|
-
}
|
|
58609
|
-
|
|
58610
|
-
|
|
58611
|
-
|
|
58618
|
+
alarms = aso.getAlarmsForDevice(this.config.gcdrDeviceId);
|
|
58619
|
+
} else {
|
|
58620
|
+
const prefetched = this.config.prefetchedAlarms;
|
|
58621
|
+
if (prefetched != null) {
|
|
58622
|
+
alarms = prefetched.filter((a) => a.deviceId === this.config.gcdrDeviceId);
|
|
58623
|
+
}
|
|
58612
58624
|
}
|
|
58613
|
-
return
|
|
58625
|
+
return showOffline ? alarms : alarms.filter((a) => !_isOffline(a));
|
|
58614
58626
|
}
|
|
58615
58627
|
// ============================================================================
|
|
58616
58628
|
// Card mapping — separado (Disp. + Tipo) view: one card per individual alarm
|
|
@@ -88359,6 +88371,9 @@ var UserListTab = class {
|
|
|
88359
88371
|
loading = false;
|
|
88360
88372
|
users = [];
|
|
88361
88373
|
highlightedUserId = null;
|
|
88374
|
+
gcdrConfigs = /* @__PURE__ */ new Map();
|
|
88375
|
+
gcdrSyncing = /* @__PURE__ */ new Set();
|
|
88376
|
+
syncTooltipEl = null;
|
|
88362
88377
|
constructor(config, callbacks) {
|
|
88363
88378
|
this.config = config;
|
|
88364
88379
|
this.callbacks = callbacks;
|
|
@@ -88384,6 +88399,8 @@ var UserListTab = class {
|
|
|
88384
88399
|
<th>Nome</th>
|
|
88385
88400
|
<th>E-mail</th>
|
|
88386
88401
|
<th>Perfil</th>
|
|
88402
|
+
<th>Status</th>
|
|
88403
|
+
<th class="um-col-gcdr">GCDR</th>
|
|
88387
88404
|
<th class="um-col-actions">A\xE7\xF5es</th>
|
|
88388
88405
|
</tr>
|
|
88389
88406
|
</thead>
|
|
@@ -88449,6 +88466,7 @@ var UserListTab = class {
|
|
|
88449
88466
|
this.totalPages = page.totalPages;
|
|
88450
88467
|
this.renderRows();
|
|
88451
88468
|
this.renderPagination(page);
|
|
88469
|
+
this.fetchGcdrConfigsBatch();
|
|
88452
88470
|
} catch (err) {
|
|
88453
88471
|
console.error("[UserListTab] fetchUsers error", err);
|
|
88454
88472
|
this.callbacks.showToast("Erro ao carregar usu\xE1rios. Tente novamente.", "error");
|
|
@@ -88475,11 +88493,36 @@ var UserListTab = class {
|
|
|
88475
88493
|
}
|
|
88476
88494
|
const name = [user.firstName, user.lastName].filter(Boolean).join(" ") || "\u2014";
|
|
88477
88495
|
const role = user.authority === "TENANT_ADMIN" ? "Admin" : "Usu\xE1rio";
|
|
88496
|
+
const statusBadge = this.buildStatusBadge(user);
|
|
88497
|
+
const uid2 = user.id.id;
|
|
88478
88498
|
tr.innerHTML = `
|
|
88479
88499
|
<td>${this.esc(name)}</td>
|
|
88480
88500
|
<td>${this.esc(user.email)}</td>
|
|
88481
88501
|
<td><span class="um-badge um-badge--${user.authority === "TENANT_ADMIN" ? "admin" : "user"}">${role}</span></td>
|
|
88502
|
+
<td>${statusBadge}</td>
|
|
88503
|
+
<td class="um-col-gcdr">
|
|
88504
|
+
<span class="um-sync-icon um-sync-icon--loading" data-sync-uid="${uid2}">
|
|
88505
|
+
<span class="um-spinner" style="width:12px;height:12px;border-width:1.5px;display:block;margin:0 auto;"></span>
|
|
88506
|
+
</span>
|
|
88507
|
+
<button class="um-icon-btn um-force-sync-btn" title="Sincronizar com GCDR" style="margin-left:2px;">
|
|
88508
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
|
88509
|
+
stroke-linecap="round" stroke-linejoin="round">
|
|
88510
|
+
<polyline points="23 4 23 10 17 10"/>
|
|
88511
|
+
<polyline points="1 20 1 14 7 14"/>
|
|
88512
|
+
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
|
|
88513
|
+
</svg>
|
|
88514
|
+
</button>
|
|
88515
|
+
</td>
|
|
88482
88516
|
<td class="um-col-actions">
|
|
88517
|
+
<button class="um-icon-btn um-assign-btn" title="Ver Fun\xE7\xF5es / Pap\xE9is" style="margin-right:4px;">
|
|
88518
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
|
88519
|
+
stroke-linecap="round" stroke-linejoin="round">
|
|
88520
|
+
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
|
88521
|
+
<circle cx="9" cy="7" r="4"/>
|
|
88522
|
+
<path d="M23 21v-2a4 4 0 0 0-3-3.87"/>
|
|
88523
|
+
<path d="M16 3.13a4 4 0 0 1 0 7.75"/>
|
|
88524
|
+
</svg>
|
|
88525
|
+
</button>
|
|
88483
88526
|
<button class="um-icon-btn um-detail-btn" title="Ver Detalhes">
|
|
88484
88527
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
|
88485
88528
|
stroke-linecap="round" stroke-linejoin="round">
|
|
@@ -88489,6 +88532,11 @@ var UserListTab = class {
|
|
|
88489
88532
|
</td>
|
|
88490
88533
|
`;
|
|
88491
88534
|
tr.querySelector(".um-detail-btn").addEventListener("click", () => this.callbacks.onOpenUserDetail(user, false));
|
|
88535
|
+
tr.querySelector(".um-assign-btn").addEventListener("click", (e) => this.showAssignmentsPopup(user, e.currentTarget));
|
|
88536
|
+
tr.querySelector(".um-force-sync-btn").addEventListener("click", () => this.syncUserToGCDR(user));
|
|
88537
|
+
const syncIcon = tr.querySelector(".um-sync-icon");
|
|
88538
|
+
syncIcon.addEventListener("mouseenter", (e) => this.showSyncTooltip(user, e.currentTarget));
|
|
88539
|
+
syncIcon.addEventListener("mouseleave", () => this.hideSyncTooltip());
|
|
88492
88540
|
tbody.appendChild(tr);
|
|
88493
88541
|
}
|
|
88494
88542
|
}
|
|
@@ -88512,9 +88560,321 @@ var UserListTab = class {
|
|
|
88512
88560
|
if (loadEl) loadEl.style.display = on ? "" : "none";
|
|
88513
88561
|
if (tableWrap) tableWrap.style.opacity = on ? "0.5" : "1";
|
|
88514
88562
|
}
|
|
88563
|
+
buildStatusBadge(user) {
|
|
88564
|
+
const enabled = user.additionalInfo?.userCredentialsEnabled;
|
|
88565
|
+
if (enabled === false) {
|
|
88566
|
+
return `<span class="um-badge um-badge--blocked">Bloqueado</span>`;
|
|
88567
|
+
}
|
|
88568
|
+
return `<span class="um-badge um-badge--active">Ativo</span>`;
|
|
88569
|
+
}
|
|
88570
|
+
gcdrHeaders() {
|
|
88571
|
+
const orch = window.MyIOOrchestrator;
|
|
88572
|
+
return {
|
|
88573
|
+
"Content-Type": "application/json",
|
|
88574
|
+
"X-API-Key": orch?.gcdrApiKey || "",
|
|
88575
|
+
"X-Tenant-ID": orch?.gcdrTenantId || ""
|
|
88576
|
+
};
|
|
88577
|
+
}
|
|
88578
|
+
gcdrBase() {
|
|
88579
|
+
return window.MyIOOrchestrator?.gcdrApiBaseUrl || "";
|
|
88580
|
+
}
|
|
88581
|
+
unwrapList(json) {
|
|
88582
|
+
if (Array.isArray(json)) return json;
|
|
88583
|
+
const j = json;
|
|
88584
|
+
if (j?.data && typeof j.data === "object") {
|
|
88585
|
+
const d = j.data;
|
|
88586
|
+
if (Array.isArray(d.items)) return d.items;
|
|
88587
|
+
if (Array.isArray(d)) return d;
|
|
88588
|
+
}
|
|
88589
|
+
if (Array.isArray(j?.items)) return j.items;
|
|
88590
|
+
return [];
|
|
88591
|
+
}
|
|
88592
|
+
async showAssignmentsPopup(user, anchor) {
|
|
88593
|
+
document.querySelector(".um-assign-popup")?.remove();
|
|
88594
|
+
const popup = document.createElement("div");
|
|
88595
|
+
popup.className = "um-assign-popup";
|
|
88596
|
+
popup.setAttribute("data-theme", this.config.theme || "light");
|
|
88597
|
+
const displayName = [user.firstName, user.lastName].filter(Boolean).join(" ") || user.email;
|
|
88598
|
+
popup.innerHTML = `
|
|
88599
|
+
<div class="um-assign-popup-header">
|
|
88600
|
+
<span class="um-assign-popup-title">\u{1F511} Fun\xE7\xF5es \u2014 ${this.esc(displayName)}</span>
|
|
88601
|
+
<button class="um-assign-popup-close" type="button">\u2715</button>
|
|
88602
|
+
</div>
|
|
88603
|
+
<div class="um-assign-popup-body">
|
|
88604
|
+
<div class="um-assign-popup-loading" style="font-size:12px;color:var(--um-text-muted);padding:8px 0;">
|
|
88605
|
+
<span class="um-spinner" style="display:inline-block;"></span> Carregando...
|
|
88606
|
+
</div>
|
|
88607
|
+
</div>
|
|
88608
|
+
`;
|
|
88609
|
+
document.body.appendChild(popup);
|
|
88610
|
+
const rect = anchor.getBoundingClientRect();
|
|
88611
|
+
const popupW = 320;
|
|
88612
|
+
const left = Math.min(rect.right + 8, window.innerWidth - popupW - 12);
|
|
88613
|
+
popup.style.top = `${Math.max(rect.top - 10, 8)}px`;
|
|
88614
|
+
popup.style.left = `${left}px`;
|
|
88615
|
+
const close = () => popup.remove();
|
|
88616
|
+
popup.querySelector(".um-assign-popup-close").addEventListener("click", close);
|
|
88617
|
+
const outsideHandler = (e) => {
|
|
88618
|
+
if (!popup.contains(e.target) && e.target !== anchor) {
|
|
88619
|
+
close();
|
|
88620
|
+
document.removeEventListener("click", outsideHandler, true);
|
|
88621
|
+
}
|
|
88622
|
+
};
|
|
88623
|
+
setTimeout(() => document.addEventListener("click", outsideHandler, true), 50);
|
|
88624
|
+
const body = popup.querySelector(".um-assign-popup-body");
|
|
88625
|
+
const base = this.gcdrBase();
|
|
88626
|
+
if (!base) {
|
|
88627
|
+
body.innerHTML = `<div style="font-size:12px;color:var(--um-toast-err-text);padding:8px 0;">GCDR n\xE3o configurado.</div>`;
|
|
88628
|
+
return;
|
|
88629
|
+
}
|
|
88630
|
+
try {
|
|
88631
|
+
const res = await fetch(`${base}/authorization/users/${user.id.id}/assignments`, { headers: this.gcdrHeaders() });
|
|
88632
|
+
let assignments = [];
|
|
88633
|
+
if (res.ok) {
|
|
88634
|
+
const json = await res.json();
|
|
88635
|
+
assignments = Array.isArray(json) ? json : json.assignments ?? [];
|
|
88636
|
+
}
|
|
88637
|
+
if (assignments.length === 0) {
|
|
88638
|
+
body.innerHTML = `<div style="font-size:12px;color:var(--um-text-faint);padding:8px 0;">Nenhuma fun\xE7\xE3o atribu\xEDda.</div>`;
|
|
88639
|
+
return;
|
|
88640
|
+
}
|
|
88641
|
+
const statusColors = {
|
|
88642
|
+
active: "var(--um-badge-active-text)",
|
|
88643
|
+
expired: "var(--um-badge-blocked-text)",
|
|
88644
|
+
inactive: "var(--um-text-faint)"
|
|
88645
|
+
};
|
|
88646
|
+
body.innerHTML = assignments.map((a) => {
|
|
88647
|
+
const scopeLabel = a.scope === "*" ? "* (global)" : a.scope.startsWith("customer:") ? `Cliente (${a.scope.replace("customer:", "").slice(0, 8)}...)` : a.scope.startsWith("asset:") ? `Asset (${a.scope.replace("asset:", "").slice(0, 8)}...)` : this.esc(a.scope);
|
|
88648
|
+
const expires = a.expiresAt ? new Date(a.expiresAt).toLocaleDateString("pt-BR") : null;
|
|
88649
|
+
const color = statusColors[a.status] || "var(--um-text-faint)";
|
|
88650
|
+
return `
|
|
88651
|
+
<div class="um-assign-row">
|
|
88652
|
+
<span class="um-assign-role">${this.esc(a.roleDisplayName || a.roleKey)}</span>
|
|
88653
|
+
<span class="um-assign-meta">
|
|
88654
|
+
Escopo: ${scopeLabel}
|
|
88655
|
+
\xB7 <span style="color:${color};font-weight:600;">${a.status}</span>
|
|
88656
|
+
${expires ? ` \xB7 Expira ${expires}` : ""}
|
|
88657
|
+
</span>
|
|
88658
|
+
</div>`;
|
|
88659
|
+
}).join("");
|
|
88660
|
+
} catch {
|
|
88661
|
+
body.innerHTML = `<div style="font-size:12px;color:var(--um-toast-err-text);padding:8px 0;">Erro ao carregar.</div>`;
|
|
88662
|
+
}
|
|
88663
|
+
}
|
|
88515
88664
|
esc(s) {
|
|
88516
88665
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
88517
88666
|
}
|
|
88667
|
+
// ── GCDR Sync Column ─────────────────────────────────────────────────────────
|
|
88668
|
+
async fetchGcdrConfigsBatch() {
|
|
88669
|
+
if (this.users.length === 0) return;
|
|
88670
|
+
const { tbBaseUrl, jwtToken } = this.config;
|
|
88671
|
+
await Promise.allSettled(this.users.map(async (user) => {
|
|
88672
|
+
const uid2 = user.id.id;
|
|
88673
|
+
try {
|
|
88674
|
+
const res = await fetch(
|
|
88675
|
+
`${tbBaseUrl}/api/plugins/telemetry/USER/${uid2}/values/attributes/SERVER_SCOPE?keys=gcdrUserConfigs`,
|
|
88676
|
+
{ headers: { "X-Authorization": `Bearer ${jwtToken}` } }
|
|
88677
|
+
);
|
|
88678
|
+
let cfg = null;
|
|
88679
|
+
if (res.ok) {
|
|
88680
|
+
const attrs = await res.json();
|
|
88681
|
+
const entry = attrs.find((a) => a.key === "gcdrUserConfigs");
|
|
88682
|
+
if (entry?.value && typeof entry.value === "object") {
|
|
88683
|
+
cfg = entry.value;
|
|
88684
|
+
}
|
|
88685
|
+
}
|
|
88686
|
+
this.gcdrConfigs.set(uid2, cfg);
|
|
88687
|
+
this.updateSyncCell(uid2, cfg ? cfg.lastSyncResult ?? "none" : "none", cfg);
|
|
88688
|
+
} catch {
|
|
88689
|
+
this.updateSyncCell(uid2, "none", null);
|
|
88690
|
+
}
|
|
88691
|
+
}));
|
|
88692
|
+
}
|
|
88693
|
+
updateSyncCell(userId, state6, cfg = null) {
|
|
88694
|
+
const iconEl = this.el?.querySelector(`.um-sync-icon[data-sync-uid="${userId}"]`);
|
|
88695
|
+
if (!iconEl) return;
|
|
88696
|
+
iconEl.className = "um-sync-icon";
|
|
88697
|
+
if (state6 === "loading" || state6 === "syncing") {
|
|
88698
|
+
iconEl.innerHTML = `<span class="um-spinner" style="width:12px;height:12px;border-width:1.5px;display:block;margin:0 auto;"></span>`;
|
|
88699
|
+
return;
|
|
88700
|
+
}
|
|
88701
|
+
const gcdrStatus = cfg?.gcdrStatus;
|
|
88702
|
+
let dotClass = "um-sync-dot--none";
|
|
88703
|
+
let dotTitle = "Nunca sincronizado";
|
|
88704
|
+
if (state6 === "success") {
|
|
88705
|
+
dotClass = gcdrStatus === "ACTIVE" ? "um-sync-dot--active" : gcdrStatus === "INACTIVE" || gcdrStatus === "LOCKED" ? "um-sync-dot--warn" : "um-sync-dot--ok";
|
|
88706
|
+
dotTitle = `GCDR: ${gcdrStatus ?? "OK"}`;
|
|
88707
|
+
} else if (state6 === "error") {
|
|
88708
|
+
dotClass = "um-sync-dot--err";
|
|
88709
|
+
dotTitle = cfg?.lastError ?? "Erro";
|
|
88710
|
+
}
|
|
88711
|
+
iconEl.innerHTML = `<span class="um-sync-dot ${dotClass}" title="${this.esc(dotTitle)}"></span>`;
|
|
88712
|
+
}
|
|
88713
|
+
showSyncTooltip(user, anchor) {
|
|
88714
|
+
this.hideSyncTooltip();
|
|
88715
|
+
const uid2 = user.id.id;
|
|
88716
|
+
const cfg = this.gcdrConfigs.get(uid2);
|
|
88717
|
+
const tooltip = document.createElement("div");
|
|
88718
|
+
tooltip.className = "um-sync-tooltip";
|
|
88719
|
+
tooltip.setAttribute("data-theme", this.config.theme || "light");
|
|
88720
|
+
this.syncTooltipEl = tooltip;
|
|
88721
|
+
const syncing = this.gcdrSyncing.has(uid2);
|
|
88722
|
+
const displayName = [user.firstName, user.lastName].filter(Boolean).join(" ") || user.email;
|
|
88723
|
+
let bodyHtml;
|
|
88724
|
+
if (syncing) {
|
|
88725
|
+
bodyHtml = `<div class="um-sync-tooltip-row" style="justify-content:center;padding:8px 0;">
|
|
88726
|
+
<span class="um-spinner" style="width:13px;height:13px;"></span> Sincronizando...
|
|
88727
|
+
</div>`;
|
|
88728
|
+
} else if (!cfg) {
|
|
88729
|
+
bodyHtml = `<div class="um-sync-tooltip-row um-sync-tooltip-row--muted">Nunca sincronizado com GCDR.</div>`;
|
|
88730
|
+
} else {
|
|
88731
|
+
const syncedAt = cfg.syncedAt ? new Date(cfg.syncedAt).toLocaleString("pt-BR") : "\u2014";
|
|
88732
|
+
const updatedAt = cfg.updatedAt ? new Date(cfg.updatedAt).toLocaleString("pt-BR") : "\u2014";
|
|
88733
|
+
const resultColor = cfg.lastSyncResult === "success" ? "var(--um-badge-active-text)" : "var(--um-badge-blocked-text)";
|
|
88734
|
+
bodyHtml = `
|
|
88735
|
+
<div class="um-sync-tooltip-row">
|
|
88736
|
+
<span class="um-sync-tooltip-label">GCDR ID</span>
|
|
88737
|
+
<span class="um-sync-tooltip-value" style="font-family:monospace;font-size:10px;">${cfg.gcdrUserId ? cfg.gcdrUserId.slice(0, 16) + "\u2026" : "\u2014"}</span>
|
|
88738
|
+
</div>
|
|
88739
|
+
<div class="um-sync-tooltip-row">
|
|
88740
|
+
<span class="um-sync-tooltip-label">Status GCDR</span>
|
|
88741
|
+
<span class="um-sync-tooltip-value">${cfg.gcdrStatus ?? "\u2014"}</span>
|
|
88742
|
+
</div>
|
|
88743
|
+
<div class="um-sync-tooltip-row">
|
|
88744
|
+
<span class="um-sync-tooltip-label">\xDAltimo sync</span>
|
|
88745
|
+
<span class="um-sync-tooltip-value">${syncedAt}</span>
|
|
88746
|
+
</div>
|
|
88747
|
+
<div class="um-sync-tooltip-row">
|
|
88748
|
+
<span class="um-sync-tooltip-label">Atualizado</span>
|
|
88749
|
+
<span class="um-sync-tooltip-value">${updatedAt}</span>
|
|
88750
|
+
</div>
|
|
88751
|
+
<div class="um-sync-tooltip-row">
|
|
88752
|
+
<span class="um-sync-tooltip-label">Qtd syncs</span>
|
|
88753
|
+
<span class="um-sync-tooltip-value">${cfg.syncCount ?? 0}</span>
|
|
88754
|
+
</div>
|
|
88755
|
+
<div class="um-sync-tooltip-row">
|
|
88756
|
+
<span class="um-sync-tooltip-label">Resultado</span>
|
|
88757
|
+
<span class="um-sync-tooltip-value" style="color:${resultColor};font-weight:700;">${cfg.lastSyncResult === "success" ? "\u2713 Sucesso" : "\u2717 Erro"}</span>
|
|
88758
|
+
</div>
|
|
88759
|
+
${cfg.lastError ? `<div class="um-sync-tooltip-row um-sync-tooltip-row--error">
|
|
88760
|
+
<span class="um-sync-tooltip-label">Erro</span>
|
|
88761
|
+
<span class="um-sync-tooltip-value">${this.esc(cfg.lastError)}</span>
|
|
88762
|
+
</div>` : ""}
|
|
88763
|
+
`;
|
|
88764
|
+
}
|
|
88765
|
+
tooltip.innerHTML = `
|
|
88766
|
+
<div class="um-sync-tooltip-header">
|
|
88767
|
+
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"
|
|
88768
|
+
stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0;">
|
|
88769
|
+
<polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/>
|
|
88770
|
+
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
|
|
88771
|
+
</svg>
|
|
88772
|
+
<span>GCDR Sync \u2014 ${this.esc(displayName)}</span>
|
|
88773
|
+
</div>
|
|
88774
|
+
<div class="um-sync-tooltip-body">${bodyHtml}</div>
|
|
88775
|
+
`;
|
|
88776
|
+
const backdrop = this.el.closest(".um-backdrop") ?? document.body;
|
|
88777
|
+
backdrop.appendChild(tooltip);
|
|
88778
|
+
const rect = anchor.getBoundingClientRect();
|
|
88779
|
+
const ttW = 260;
|
|
88780
|
+
let left = rect.right + 8;
|
|
88781
|
+
if (left + ttW > window.innerWidth - 8) left = rect.left - ttW - 8;
|
|
88782
|
+
tooltip.style.left = `${Math.max(4, left)}px`;
|
|
88783
|
+
tooltip.style.top = `${Math.max(4, rect.top - 10)}px`;
|
|
88784
|
+
}
|
|
88785
|
+
hideSyncTooltip() {
|
|
88786
|
+
this.syncTooltipEl?.remove();
|
|
88787
|
+
this.syncTooltipEl = null;
|
|
88788
|
+
}
|
|
88789
|
+
async syncUserToGCDR(user) {
|
|
88790
|
+
const uid2 = user.id.id;
|
|
88791
|
+
if (this.gcdrSyncing.has(uid2)) return;
|
|
88792
|
+
const base = this.gcdrBase();
|
|
88793
|
+
if (!base) {
|
|
88794
|
+
this.callbacks.showToast("GCDR n\xE3o configurado.", "error");
|
|
88795
|
+
return;
|
|
88796
|
+
}
|
|
88797
|
+
this.gcdrSyncing.add(uid2);
|
|
88798
|
+
this.updateSyncCell(uid2, "syncing");
|
|
88799
|
+
const prev = this.gcdrConfigs.get(uid2) ?? null;
|
|
88800
|
+
const syncCount = (prev?.syncCount ?? 0) + 1;
|
|
88801
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
88802
|
+
try {
|
|
88803
|
+
const searchRes = await fetch(
|
|
88804
|
+
`${base}/api/v1/users?search=${encodeURIComponent(user.email)}&customerId=${encodeURIComponent(this.config.customerId)}&limit=10`,
|
|
88805
|
+
{ headers: this.gcdrHeaders() }
|
|
88806
|
+
);
|
|
88807
|
+
let gcdrUser = null;
|
|
88808
|
+
if (searchRes.ok) {
|
|
88809
|
+
const data = await searchRes.json();
|
|
88810
|
+
const items = Array.isArray(data) ? data : data?.data?.items ?? data?.items ?? [];
|
|
88811
|
+
gcdrUser = items.find((u) => u.email?.toLowerCase() === user.email.toLowerCase()) ?? null;
|
|
88812
|
+
}
|
|
88813
|
+
if (!gcdrUser) {
|
|
88814
|
+
const createRes = await fetch(`${base}/api/v1/users`, {
|
|
88815
|
+
method: "POST",
|
|
88816
|
+
headers: this.gcdrHeaders(),
|
|
88817
|
+
body: JSON.stringify({
|
|
88818
|
+
email: user.email,
|
|
88819
|
+
firstName: user.firstName || "",
|
|
88820
|
+
lastName: user.lastName || "",
|
|
88821
|
+
customerId: this.config.customerId,
|
|
88822
|
+
type: "CUSTOMER"
|
|
88823
|
+
})
|
|
88824
|
+
});
|
|
88825
|
+
if (!createRes.ok) throw new Error(`Criar usu\xE1rio GCDR: HTTP ${createRes.status}`);
|
|
88826
|
+
gcdrUser = await createRes.json();
|
|
88827
|
+
}
|
|
88828
|
+
const configs = {
|
|
88829
|
+
gcdrUserId: gcdrUser.id,
|
|
88830
|
+
gcdrStatus: gcdrUser.status,
|
|
88831
|
+
gcdrType: gcdrUser.type,
|
|
88832
|
+
syncedAt: now,
|
|
88833
|
+
syncCount,
|
|
88834
|
+
lastSyncResult: "success",
|
|
88835
|
+
lastError: null,
|
|
88836
|
+
createdAt: prev?.createdAt ?? now,
|
|
88837
|
+
updatedAt: now
|
|
88838
|
+
};
|
|
88839
|
+
await this.saveTBAttribute(uid2, configs);
|
|
88840
|
+
this.gcdrConfigs.set(uid2, configs);
|
|
88841
|
+
this.updateSyncCell(uid2, "success", configs);
|
|
88842
|
+
this.callbacks.showToast(`Sync GCDR conclu\xEDdo \u2014 ${user.email}`, "success");
|
|
88843
|
+
} catch (err) {
|
|
88844
|
+
const configs = {
|
|
88845
|
+
...prev ?? {},
|
|
88846
|
+
syncedAt: now,
|
|
88847
|
+
syncCount,
|
|
88848
|
+
lastSyncResult: "error",
|
|
88849
|
+
lastError: String(err?.message ?? "Erro desconhecido"),
|
|
88850
|
+
updatedAt: now
|
|
88851
|
+
};
|
|
88852
|
+
try {
|
|
88853
|
+
await this.saveTBAttribute(uid2, configs);
|
|
88854
|
+
} catch {
|
|
88855
|
+
}
|
|
88856
|
+
this.gcdrConfigs.set(uid2, configs);
|
|
88857
|
+
this.updateSyncCell(uid2, "error", configs);
|
|
88858
|
+
this.callbacks.showToast(`Erro sync GCDR: ${err?.message || "Falha"}`, "error");
|
|
88859
|
+
} finally {
|
|
88860
|
+
this.gcdrSyncing.delete(uid2);
|
|
88861
|
+
}
|
|
88862
|
+
}
|
|
88863
|
+
async saveTBAttribute(userId, configs) {
|
|
88864
|
+
const { tbBaseUrl, jwtToken } = this.config;
|
|
88865
|
+
const res = await fetch(
|
|
88866
|
+
`${tbBaseUrl}/api/plugins/telemetry/USER/${userId}/attributes/SERVER_SCOPE`,
|
|
88867
|
+
{
|
|
88868
|
+
method: "POST",
|
|
88869
|
+
headers: {
|
|
88870
|
+
"X-Authorization": `Bearer ${jwtToken}`,
|
|
88871
|
+
"Content-Type": "application/json"
|
|
88872
|
+
},
|
|
88873
|
+
body: JSON.stringify({ gcdrUserConfigs: configs })
|
|
88874
|
+
}
|
|
88875
|
+
);
|
|
88876
|
+
if (!res.ok) throw new Error(`Salvar atributo TB: HTTP ${res.status}`);
|
|
88877
|
+
}
|
|
88518
88878
|
};
|
|
88519
88879
|
|
|
88520
88880
|
// src/components/premium-modals/user-management/tabs/NewUserTab.ts
|
|
@@ -90001,7 +90361,12 @@ var UserDetailTab = class {
|
|
|
90001
90361
|
fetch(`${this.gcdrBase()}/roles?limit=100`, { headers: this.gcdrHeaders() }),
|
|
90002
90362
|
fetch(`${this.gcdrBase()}/policies?limit=100`, { headers: this.gcdrHeaders() })
|
|
90003
90363
|
]);
|
|
90004
|
-
|
|
90364
|
+
if (assignRes.ok) {
|
|
90365
|
+
const assignJson = await assignRes.json();
|
|
90366
|
+
this.assignments = Array.isArray(assignJson) ? assignJson : assignJson.assignments ?? [];
|
|
90367
|
+
} else {
|
|
90368
|
+
this.assignments = [];
|
|
90369
|
+
}
|
|
90005
90370
|
this.availableRoles = rolesRes.ok ? this.unwrapList(await rolesRes.json()) : [];
|
|
90006
90371
|
this.availablePolicies = policiesRes.ok ? this.unwrapList(await policiesRes.json()) : [];
|
|
90007
90372
|
this.renderAssignments();
|
|
@@ -90028,11 +90393,12 @@ var UserDetailTab = class {
|
|
|
90028
90393
|
const tbody = document.createElement("tbody");
|
|
90029
90394
|
this.assignments.forEach((a) => {
|
|
90030
90395
|
const tr = document.createElement("tr");
|
|
90031
|
-
const statusColor = a.status === "active" ? "var(--um-badge-
|
|
90396
|
+
const statusColor = a.status === "active" ? "var(--um-badge-active-text)" : a.status === "expired" ? "var(--um-badge-blocked-text)" : "var(--um-text-faint)";
|
|
90032
90397
|
const expiresAt = a.expiresAt ? new Date(a.expiresAt).toLocaleDateString("pt-BR") : "\u2014";
|
|
90398
|
+
const scopeLabel = a.scope === "*" ? "* (global)" : a.scope.startsWith("customer:") ? `Cliente (${a.scope.replace("customer:", "").slice(0, 8)}...)` : a.scope.startsWith("asset:") ? `Asset (${a.scope.replace("asset:", "").slice(0, 8)}...)` : a.scope;
|
|
90033
90399
|
tr.innerHTML = `
|
|
90034
90400
|
<td style="font-weight:500;">${this.esc(a.roleDisplayName || a.roleKey)}</td>
|
|
90035
|
-
<td
|
|
90401
|
+
<td style="font-size:12px;">${this.esc(scopeLabel)}</td>
|
|
90036
90402
|
<td><span style="color:${statusColor};font-weight:600;">${a.status}</span></td>
|
|
90037
90403
|
<td>${expiresAt}</td>
|
|
90038
90404
|
<td style="text-align:center;"><button class="um-btn um-btn--danger um-btn--sm revoke-btn">Revogar</button></td>
|
|
@@ -90142,13 +90508,14 @@ var UserDetailTab = class {
|
|
|
90142
90508
|
const expiresAt = expiresAtRaw ? new Date(expiresAtRaw).toISOString() : null;
|
|
90143
90509
|
const reason = modal.querySelector("[name=reason]").value.trim() || null;
|
|
90144
90510
|
const role = this.availableRoles.find((r) => r.id === roleId);
|
|
90511
|
+
const roleKey = role?.key || role?.id || roleId;
|
|
90145
90512
|
const btn = modal.querySelector(".assign-save");
|
|
90146
90513
|
btn.disabled = true;
|
|
90147
90514
|
btn.textContent = "...";
|
|
90148
90515
|
try {
|
|
90149
90516
|
const body = {
|
|
90150
90517
|
userId: this.user.id.id,
|
|
90151
|
-
|
|
90518
|
+
roleKey,
|
|
90152
90519
|
scope,
|
|
90153
90520
|
expiresAt,
|
|
90154
90521
|
reason
|
|
@@ -91202,6 +91569,12 @@ var UserManagementModalView = class {
|
|
|
91202
91569
|
--um-badge-admin-text: #3b5bdb;
|
|
91203
91570
|
--um-badge-user-bg: #f0fdf4;
|
|
91204
91571
|
--um-badge-user-text: #16a34a;
|
|
91572
|
+
--um-badge-active-bg: #dcfce7;
|
|
91573
|
+
--um-badge-active-text: #15803d;
|
|
91574
|
+
--um-badge-blocked-bg: #fee2e2;
|
|
91575
|
+
--um-badge-blocked-text: #b91c1c;
|
|
91576
|
+
--um-badge-pending-bg: #fef9c3;
|
|
91577
|
+
--um-badge-pending-text: #854d0e;
|
|
91205
91578
|
--um-toast-ok-bg: #f0fdf4;
|
|
91206
91579
|
--um-toast-ok-border: #22c55e;
|
|
91207
91580
|
--um-toast-ok-text: #16a34a;
|
|
@@ -91262,6 +91635,12 @@ var UserManagementModalView = class {
|
|
|
91262
91635
|
--um-badge-admin-text: #60a5fa;
|
|
91263
91636
|
--um-badge-user-bg: #1a2d24;
|
|
91264
91637
|
--um-badge-user-text: #4ade80;
|
|
91638
|
+
--um-badge-active-bg: #14532d;
|
|
91639
|
+
--um-badge-active-text: #86efac;
|
|
91640
|
+
--um-badge-blocked-bg: #450a0a;
|
|
91641
|
+
--um-badge-blocked-text: #fca5a5;
|
|
91642
|
+
--um-badge-pending-bg: #422006;
|
|
91643
|
+
--um-badge-pending-text: #fde68a;
|
|
91265
91644
|
--um-toast-ok-bg: #1e3a2e;
|
|
91266
91645
|
--um-toast-ok-border: #22c55e;
|
|
91267
91646
|
--um-toast-ok-text: #4ade80;
|
|
@@ -91291,14 +91670,21 @@ var UserManagementModalView = class {
|
|
|
91291
91670
|
.um-modal {
|
|
91292
91671
|
background: var(--um-modal-bg);
|
|
91293
91672
|
font-family: 'Nunito', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
91294
|
-
border-radius: 14px; width:
|
|
91673
|
+
border-radius: 14px; width: 96vw; max-width: 1200px;
|
|
91295
91674
|
--modal-header-radius: 14px 14px 0 0;
|
|
91296
|
-
|
|
91675
|
+
height: 84vh; display: flex; flex-direction: column;
|
|
91297
91676
|
box-shadow: var(--um-shadow); overflow: hidden; position: relative;
|
|
91298
91677
|
}
|
|
91299
|
-
@media (max-height: 600px) { .um-modal {
|
|
91678
|
+
@media (max-height: 600px) { .um-modal { height: 96vh; } }
|
|
91300
91679
|
|
|
91301
91680
|
/* Header handled by ModalHeader (RFC-0121) */
|
|
91681
|
+
.um-modal .myio-modal-header__title {
|
|
91682
|
+
font-size: 14px !important;
|
|
91683
|
+
font-weight: 600 !important;
|
|
91684
|
+
}
|
|
91685
|
+
.um-modal .myio-modal-header__icon {
|
|
91686
|
+
font-size: 15px !important;
|
|
91687
|
+
}
|
|
91302
91688
|
|
|
91303
91689
|
/* Force MyIO purple header regardless of light/dark theme \u2014 overrides myio-modal-header--light */
|
|
91304
91690
|
.um-modal .myio-modal-header--light {
|
|
@@ -91397,16 +91783,83 @@ var UserManagementModalView = class {
|
|
|
91397
91783
|
.um-table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
|
91398
91784
|
.um-table th {
|
|
91399
91785
|
text-align: left; padding: 8px 12px;
|
|
91400
|
-
color: var(--um-text-muted); font-weight:
|
|
91786
|
+
color: var(--um-text-muted); font-weight: 600; font-size: 11px;
|
|
91401
91787
|
text-transform: uppercase; letter-spacing: 0.04em;
|
|
91402
91788
|
border-bottom: 1px solid var(--um-border);
|
|
91403
91789
|
}
|
|
91404
91790
|
.um-table td {
|
|
91405
|
-
padding: 10px 12px; color: var(--um-text-
|
|
91791
|
+
padding: 10px 12px; color: var(--um-text-primary); font-weight: 500;
|
|
91406
91792
|
border-bottom: 1px solid var(--um-border-sub);
|
|
91407
91793
|
}
|
|
91408
91794
|
.um-table tr:hover td { background: var(--um-bg-surface); }
|
|
91409
|
-
.um-col-actions { width:
|
|
91795
|
+
.um-col-actions { width: 100px; text-align: center; }
|
|
91796
|
+
.um-col-gcdr { width: 72px; text-align: center; }
|
|
91797
|
+
|
|
91798
|
+
/* Sync status dot */
|
|
91799
|
+
.um-sync-icon { display: inline-flex; align-items: center; justify-content: center; }
|
|
91800
|
+
.um-sync-dot {
|
|
91801
|
+
display: inline-block; width: 10px; height: 10px; border-radius: 50%;
|
|
91802
|
+
flex-shrink: 0;
|
|
91803
|
+
}
|
|
91804
|
+
.um-sync-dot--none { background: var(--um-text-faint); opacity: 0.4; }
|
|
91805
|
+
.um-sync-dot--ok,
|
|
91806
|
+
.um-sync-dot--active { background: var(--um-badge-active-text); }
|
|
91807
|
+
.um-sync-dot--warn { background: var(--um-badge-pending-text); }
|
|
91808
|
+
.um-sync-dot--err { background: var(--um-badge-blocked-text); }
|
|
91809
|
+
|
|
91810
|
+
/* Premium sync tooltip */
|
|
91811
|
+
.um-sync-tooltip {
|
|
91812
|
+
position: fixed; z-index: 100020;
|
|
91813
|
+
background: var(--um-modal-bg); border: 1px solid var(--um-border);
|
|
91814
|
+
border-radius: 10px; box-shadow: 0 8px 32px rgba(0,0,0,0.2);
|
|
91815
|
+
width: 260px; pointer-events: none;
|
|
91816
|
+
font-family: 'Nunito', -apple-system, sans-serif;
|
|
91817
|
+
}
|
|
91818
|
+
.um-sync-tooltip-header {
|
|
91819
|
+
display: flex; align-items: center; gap: 7px;
|
|
91820
|
+
padding: 8px 12px; background: var(--um-accent);
|
|
91821
|
+
border-radius: 10px 10px 0 0;
|
|
91822
|
+
font-size: 11px; font-weight: 700; color: #fff;
|
|
91823
|
+
}
|
|
91824
|
+
.um-sync-tooltip-body {
|
|
91825
|
+
padding: 10px 12px; display: flex; flex-direction: column; gap: 4px;
|
|
91826
|
+
}
|
|
91827
|
+
.um-sync-tooltip-row {
|
|
91828
|
+
display: flex; justify-content: space-between; align-items: flex-start;
|
|
91829
|
+
font-size: 11px; gap: 8px;
|
|
91830
|
+
}
|
|
91831
|
+
.um-sync-tooltip-row--muted { color: var(--um-text-faint); justify-content: center; padding: 4px 0; }
|
|
91832
|
+
.um-sync-tooltip-row--error .um-sync-tooltip-value { color: var(--um-badge-blocked-text); word-break: break-all; }
|
|
91833
|
+
.um-sync-tooltip-label { color: var(--um-text-muted); flex-shrink: 0; }
|
|
91834
|
+
.um-sync-tooltip-value { color: var(--um-text-primary); font-weight: 600; text-align: right; }
|
|
91835
|
+
|
|
91836
|
+
/* Assignments quick-view popup */
|
|
91837
|
+
.um-assign-popup {
|
|
91838
|
+
position: fixed; z-index: 100010;
|
|
91839
|
+
background: var(--um-modal-bg); border: 1px solid var(--um-border);
|
|
91840
|
+
border-radius: 10px; box-shadow: 0 8px 32px rgba(0,0,0,0.18);
|
|
91841
|
+
width: 320px; max-height: 340px; overflow-y: auto;
|
|
91842
|
+
font-family: 'Nunito', -apple-system, sans-serif;
|
|
91843
|
+
}
|
|
91844
|
+
.um-assign-popup-header {
|
|
91845
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
91846
|
+
padding: 10px 14px; background: var(--um-accent);
|
|
91847
|
+
border-radius: 10px 10px 0 0;
|
|
91848
|
+
}
|
|
91849
|
+
.um-assign-popup-title { font-size: 12px; font-weight: 700; color: #fff; }
|
|
91850
|
+
.um-assign-popup-close {
|
|
91851
|
+
background: none; border: none; color: rgba(255,255,255,0.8);
|
|
91852
|
+
font-size: 14px; cursor: pointer; padding: 0 4px; line-height: 1;
|
|
91853
|
+
}
|
|
91854
|
+
.um-assign-popup-body { padding: 10px 14px; }
|
|
91855
|
+
.um-assign-row {
|
|
91856
|
+
display: flex; flex-direction: column; gap: 2px;
|
|
91857
|
+
padding: 7px 0; border-bottom: 1px solid var(--um-border-sub);
|
|
91858
|
+
font-size: 12px;
|
|
91859
|
+
}
|
|
91860
|
+
.um-assign-row:last-child { border-bottom: none; }
|
|
91861
|
+
.um-assign-role { font-weight: 600; color: var(--um-text-primary); }
|
|
91862
|
+
.um-assign-meta { color: var(--um-text-muted); font-size: 11px; }
|
|
91410
91863
|
.um-row--highlight td { background: var(--um-row-highlight) !important; }
|
|
91411
91864
|
|
|
91412
91865
|
.um-list-empty, .um-list-loading, .um-profiles-loading,
|
|
@@ -91442,8 +91895,11 @@ var UserManagementModalView = class {
|
|
|
91442
91895
|
.um-btn--sm { padding: 6px 12px; font-size: 12px; }
|
|
91443
91896
|
|
|
91444
91897
|
.um-badge { display: inline-block; font-size: 10px; font-weight: 600; padding: 2px 8px; border-radius: 9999px; }
|
|
91445
|
-
.um-badge--admin
|
|
91446
|
-
.um-badge--user
|
|
91898
|
+
.um-badge--admin { background: var(--um-badge-admin-bg); color: var(--um-badge-admin-text); }
|
|
91899
|
+
.um-badge--user { background: var(--um-badge-user-bg); color: var(--um-badge-user-text); }
|
|
91900
|
+
.um-badge--active { background: var(--um-badge-active-bg); color: var(--um-badge-active-text); }
|
|
91901
|
+
.um-badge--blocked { background: var(--um-badge-blocked-bg); color: var(--um-badge-blocked-text); }
|
|
91902
|
+
.um-badge--pending { background: var(--um-badge-pending-bg); color: var(--um-badge-pending-text); }
|
|
91447
91903
|
|
|
91448
91904
|
.um-form { display: flex; flex-direction: column; gap: 14px;}
|
|
91449
91905
|
.um-form-row { display: flex; gap: 12px; }
|
|
@@ -91738,6 +92194,272 @@ function openUserManagementModal(params) {
|
|
|
91738
92194
|
controller.show();
|
|
91739
92195
|
}
|
|
91740
92196
|
|
|
92197
|
+
// src/components/gcdr-auth/MyIOAuthContext.ts
|
|
92198
|
+
var MyIOAuthContext = class _MyIOAuthContext {
|
|
92199
|
+
allowedActions = /* @__PURE__ */ new Set();
|
|
92200
|
+
deniedActions = /* @__PURE__ */ new Set();
|
|
92201
|
+
snap;
|
|
92202
|
+
constructor(snap) {
|
|
92203
|
+
this.snap = snap;
|
|
92204
|
+
}
|
|
92205
|
+
// ── Public read-only state ────────────────────────────────────────────────
|
|
92206
|
+
get ready() {
|
|
92207
|
+
return this.snap.ready;
|
|
92208
|
+
}
|
|
92209
|
+
get gcdrUserId() {
|
|
92210
|
+
return this.snap.gcdrUserId;
|
|
92211
|
+
}
|
|
92212
|
+
get scope() {
|
|
92213
|
+
return this.snap.scope;
|
|
92214
|
+
}
|
|
92215
|
+
get error() {
|
|
92216
|
+
return this.snap.error;
|
|
92217
|
+
}
|
|
92218
|
+
get allowAll() {
|
|
92219
|
+
return this.snap.allowAll;
|
|
92220
|
+
}
|
|
92221
|
+
get assignments() {
|
|
92222
|
+
return this.snap.assignments;
|
|
92223
|
+
}
|
|
92224
|
+
// ── Permission checks ─────────────────────────────────────────────────────
|
|
92225
|
+
/**
|
|
92226
|
+
* Returns `true` if the current user is allowed to perform `action`.
|
|
92227
|
+
*
|
|
92228
|
+
* Evaluation order (deny-first):
|
|
92229
|
+
* 1. If allowAll (TENANT_ADMIN) → always true
|
|
92230
|
+
* 2. If action (or `*`) is in the deny set → false
|
|
92231
|
+
* 3. If action (or `*`) is in the allow set → true
|
|
92232
|
+
* 4. Otherwise → false (closed-by-default)
|
|
92233
|
+
*
|
|
92234
|
+
* Returns `false` while not yet ready.
|
|
92235
|
+
*/
|
|
92236
|
+
can(action) {
|
|
92237
|
+
if (!this.snap.ready) return false;
|
|
92238
|
+
if (this.snap.allowAll) return true;
|
|
92239
|
+
if (this.deniedActions.has("*") || this.deniedActions.has(action)) return false;
|
|
92240
|
+
return this.allowedActions.has("*") || this.allowedActions.has(action);
|
|
92241
|
+
}
|
|
92242
|
+
/** True if the user can perform **at least one** of the given actions. */
|
|
92243
|
+
canAny(...actions) {
|
|
92244
|
+
return actions.some((a) => this.can(a));
|
|
92245
|
+
}
|
|
92246
|
+
/** True if the user can perform **all** of the given actions. */
|
|
92247
|
+
canAll(...actions) {
|
|
92248
|
+
return actions.every((a) => this.can(a));
|
|
92249
|
+
}
|
|
92250
|
+
// ── Factory ───────────────────────────────────────────────────────────────
|
|
92251
|
+
/**
|
|
92252
|
+
* Initialises the auth context.
|
|
92253
|
+
* Resolves immediately when `allowAll` is set or GCDR is not configured.
|
|
92254
|
+
* Always dispatches `myio:auth-ready` when done (success or error).
|
|
92255
|
+
*/
|
|
92256
|
+
static async init(config) {
|
|
92257
|
+
const scope = config.scope ?? (config.customerId ? `customer:${config.customerId}` : "*");
|
|
92258
|
+
const ctx = new _MyIOAuthContext({
|
|
92259
|
+
ready: false,
|
|
92260
|
+
gcdrUserId: null,
|
|
92261
|
+
scope,
|
|
92262
|
+
allowAll: config.allowAll ?? false,
|
|
92263
|
+
error: null,
|
|
92264
|
+
assignments: []
|
|
92265
|
+
});
|
|
92266
|
+
if (config.allowAll) {
|
|
92267
|
+
ctx.snap.ready = true;
|
|
92268
|
+
ctx._dispatch();
|
|
92269
|
+
return ctx;
|
|
92270
|
+
}
|
|
92271
|
+
if (!config.gcdrApiBaseUrl) {
|
|
92272
|
+
ctx.snap.ready = true;
|
|
92273
|
+
ctx.snap.error = "GCDR not configured";
|
|
92274
|
+
ctx._dispatch();
|
|
92275
|
+
return ctx;
|
|
92276
|
+
}
|
|
92277
|
+
try {
|
|
92278
|
+
const hdrs = ctx._gcdrHeaders(config);
|
|
92279
|
+
const gcdrUserId = await ctx._resolveGcdrUserId(config, hdrs);
|
|
92280
|
+
if (!gcdrUserId) {
|
|
92281
|
+
ctx.snap.error = "GCDR user not found";
|
|
92282
|
+
ctx.snap.ready = true;
|
|
92283
|
+
ctx._dispatch();
|
|
92284
|
+
return ctx;
|
|
92285
|
+
}
|
|
92286
|
+
ctx.snap.gcdrUserId = gcdrUserId;
|
|
92287
|
+
const assignRes = await fetch(
|
|
92288
|
+
`${config.gcdrApiBaseUrl}/authorization/users/${gcdrUserId}/assignments`,
|
|
92289
|
+
{ headers: hdrs }
|
|
92290
|
+
);
|
|
92291
|
+
let allAssignments = [];
|
|
92292
|
+
if (assignRes.ok) {
|
|
92293
|
+
const json = await assignRes.json();
|
|
92294
|
+
allAssignments = Array.isArray(json) ? json : json.assignments ?? [];
|
|
92295
|
+
}
|
|
92296
|
+
const relevant = allAssignments.filter(
|
|
92297
|
+
(a) => a.status === "active" && (a.scope === "*" || a.scope === scope)
|
|
92298
|
+
);
|
|
92299
|
+
ctx.snap.assignments = relevant;
|
|
92300
|
+
if (relevant.length === 0) {
|
|
92301
|
+
ctx.snap.ready = true;
|
|
92302
|
+
ctx._dispatch();
|
|
92303
|
+
return ctx;
|
|
92304
|
+
}
|
|
92305
|
+
const roleKeys = [...new Set(relevant.map((a) => a.roleKey))];
|
|
92306
|
+
const roles = await ctx._fetchRoles(config.gcdrApiBaseUrl, roleKeys, hdrs);
|
|
92307
|
+
const policyKeys = [...new Set(
|
|
92308
|
+
roles.flatMap((r) => [...r.policies ?? [], ...r.policyIds ?? []])
|
|
92309
|
+
)];
|
|
92310
|
+
if (policyKeys.length > 0) {
|
|
92311
|
+
const policies = await ctx._fetchPolicies(config.gcdrApiBaseUrl, policyKeys, hdrs);
|
|
92312
|
+
ctx._applyPolicies(policies);
|
|
92313
|
+
}
|
|
92314
|
+
} catch (err) {
|
|
92315
|
+
ctx.snap.error = err?.message ?? "Auth init failed";
|
|
92316
|
+
}
|
|
92317
|
+
ctx.snap.ready = true;
|
|
92318
|
+
ctx._dispatch();
|
|
92319
|
+
return ctx;
|
|
92320
|
+
}
|
|
92321
|
+
// ── Private helpers ───────────────────────────────────────────────────────
|
|
92322
|
+
_gcdrHeaders(config) {
|
|
92323
|
+
return {
|
|
92324
|
+
"Content-Type": "application/json",
|
|
92325
|
+
"X-API-Key": config.gcdrApiKey || "",
|
|
92326
|
+
"X-Tenant-ID": config.gcdrTenantId || ""
|
|
92327
|
+
};
|
|
92328
|
+
}
|
|
92329
|
+
/**
|
|
92330
|
+
* Two-step GCDR user lookup:
|
|
92331
|
+
* 1. Read `gcdrUserConfigs` from TB SERVER_SCOPE attribute (fast path if previously synced)
|
|
92332
|
+
* 2. Email search via GCDR API
|
|
92333
|
+
*/
|
|
92334
|
+
async _resolveGcdrUserId(config, gcdrHeaders) {
|
|
92335
|
+
if (config.currentUserTbId && config.tbBaseUrl && config.jwtToken) {
|
|
92336
|
+
try {
|
|
92337
|
+
const res = await fetch(
|
|
92338
|
+
`${config.tbBaseUrl}/api/plugins/telemetry/USER/${config.currentUserTbId}/values/attributes/SERVER_SCOPE?keys=gcdrUserConfigs`,
|
|
92339
|
+
{ headers: { "X-Authorization": `Bearer ${config.jwtToken}` } }
|
|
92340
|
+
);
|
|
92341
|
+
if (res.ok) {
|
|
92342
|
+
const attrs = await res.json();
|
|
92343
|
+
const gcdrId = attrs.find((a) => a.key === "gcdrUserConfigs")?.value?.gcdrUserId;
|
|
92344
|
+
if (gcdrId) return gcdrId;
|
|
92345
|
+
}
|
|
92346
|
+
} catch {
|
|
92347
|
+
}
|
|
92348
|
+
}
|
|
92349
|
+
if (!config.currentUserEmail) return null;
|
|
92350
|
+
try {
|
|
92351
|
+
const q = encodeURIComponent(config.currentUserEmail);
|
|
92352
|
+
const cid = config.customerId ? `&customerId=${encodeURIComponent(config.customerId)}` : "";
|
|
92353
|
+
const res = await fetch(
|
|
92354
|
+
`${config.gcdrApiBaseUrl}/api/v1/users?search=${q}${cid}&limit=10`,
|
|
92355
|
+
{ headers: gcdrHeaders }
|
|
92356
|
+
);
|
|
92357
|
+
if (res.ok) {
|
|
92358
|
+
const data = await res.json();
|
|
92359
|
+
const items = Array.isArray(data) ? data : data?.data?.items ?? data?.items ?? [];
|
|
92360
|
+
const found = items.find(
|
|
92361
|
+
(u) => u.email?.toLowerCase() === config.currentUserEmail.toLowerCase()
|
|
92362
|
+
);
|
|
92363
|
+
if (found) return found.id;
|
|
92364
|
+
}
|
|
92365
|
+
} catch {
|
|
92366
|
+
}
|
|
92367
|
+
return null;
|
|
92368
|
+
}
|
|
92369
|
+
/**
|
|
92370
|
+
* Fetches roles — tries GET /roles bulk endpoint first, falls back to per-key.
|
|
92371
|
+
*/
|
|
92372
|
+
async _fetchRoles(base, roleKeys, headers) {
|
|
92373
|
+
try {
|
|
92374
|
+
const res = await fetch(`${base}/roles`, { headers });
|
|
92375
|
+
if (res.ok) {
|
|
92376
|
+
const json = await res.json();
|
|
92377
|
+
const all = Array.isArray(json) ? json : json?.data?.items ?? json?.items ?? json?.data ?? [];
|
|
92378
|
+
return all.filter((r) => roleKeys.includes(r.key ?? r.id));
|
|
92379
|
+
}
|
|
92380
|
+
} catch {
|
|
92381
|
+
}
|
|
92382
|
+
const settled = await Promise.allSettled(
|
|
92383
|
+
roleKeys.map(
|
|
92384
|
+
(key) => fetch(`${base}/roles/${key}`, { headers }).then((r) => r.ok ? r.json() : null)
|
|
92385
|
+
)
|
|
92386
|
+
);
|
|
92387
|
+
return settled.filter((r) => r.status === "fulfilled" && r.value != null).map((r) => r.value);
|
|
92388
|
+
}
|
|
92389
|
+
/**
|
|
92390
|
+
* Fetches policies — tries GET /policies bulk endpoint first, falls back to per-key.
|
|
92391
|
+
*/
|
|
92392
|
+
async _fetchPolicies(base, policyKeys, headers) {
|
|
92393
|
+
try {
|
|
92394
|
+
const res = await fetch(`${base}/policies`, { headers });
|
|
92395
|
+
if (res.ok) {
|
|
92396
|
+
const json = await res.json();
|
|
92397
|
+
const all = Array.isArray(json) ? json : json?.data?.items ?? json?.items ?? json?.data ?? [];
|
|
92398
|
+
return all.filter((p) => policyKeys.includes(p.key ?? p.id));
|
|
92399
|
+
}
|
|
92400
|
+
} catch {
|
|
92401
|
+
}
|
|
92402
|
+
const settled = await Promise.allSettled(
|
|
92403
|
+
policyKeys.map(
|
|
92404
|
+
(key) => fetch(`${base}/policies/${key}`, { headers }).then((r) => r.ok ? r.json() : null)
|
|
92405
|
+
)
|
|
92406
|
+
);
|
|
92407
|
+
return settled.filter((r) => r.status === "fulfilled" && r.value != null).map((r) => r.value);
|
|
92408
|
+
}
|
|
92409
|
+
/**
|
|
92410
|
+
* Merges all policy allow/deny lists into the context's permission sets.
|
|
92411
|
+
* Deny always wins over allow (deny-first strategy).
|
|
92412
|
+
*/
|
|
92413
|
+
_applyPolicies(policies) {
|
|
92414
|
+
for (const policy of policies) {
|
|
92415
|
+
for (const action of policy.allow ?? []) this.allowedActions.add(action);
|
|
92416
|
+
for (const action of policy.deny ?? []) this.deniedActions.add(action);
|
|
92417
|
+
}
|
|
92418
|
+
}
|
|
92419
|
+
/** Dispatches `myio:auth-ready` so widgets can react without polling. */
|
|
92420
|
+
_dispatch() {
|
|
92421
|
+
window.dispatchEvent(
|
|
92422
|
+
new CustomEvent("myio:auth-ready", {
|
|
92423
|
+
detail: {
|
|
92424
|
+
ready: this.snap.ready,
|
|
92425
|
+
scope: this.snap.scope,
|
|
92426
|
+
allowAll: this.snap.allowAll,
|
|
92427
|
+
error: this.snap.error
|
|
92428
|
+
}
|
|
92429
|
+
})
|
|
92430
|
+
);
|
|
92431
|
+
}
|
|
92432
|
+
};
|
|
92433
|
+
async function initMyIOAuthContext(config) {
|
|
92434
|
+
return MyIOAuthContext.init(config);
|
|
92435
|
+
}
|
|
92436
|
+
|
|
92437
|
+
// src/components/gcdr-auth/permissions.ts
|
|
92438
|
+
var PERM = {
|
|
92439
|
+
// ── Alarm actions ────────────────────────────────────────────────────────
|
|
92440
|
+
ALARM_VIEW: "alarm:view",
|
|
92441
|
+
ALARM_ACK: "alarm:ack",
|
|
92442
|
+
ALARM_ESCALATE: "alarm:escalate",
|
|
92443
|
+
ALARM_SNOOZE: "alarm:snooze",
|
|
92444
|
+
ALARM_CLOSE: "alarm:close",
|
|
92445
|
+
// ── Reports ──────────────────────────────────────────────────────────────
|
|
92446
|
+
REPORT_VIEW: "report:view",
|
|
92447
|
+
REPORT_EXPORT: "report:export",
|
|
92448
|
+
// ── Settings modal ───────────────────────────────────────────────────────
|
|
92449
|
+
SETTINGS_VIEW: "settings:view",
|
|
92450
|
+
SETTINGS_EDIT: "settings:edit",
|
|
92451
|
+
// ── User management (GCDR) ───────────────────────────────────────────────
|
|
92452
|
+
USER_VIEW: "user:view",
|
|
92453
|
+
USER_MANAGE: "user:manage",
|
|
92454
|
+
// ── Integration config ───────────────────────────────────────────────────
|
|
92455
|
+
INTEGRATION_VIEW: "integration:view",
|
|
92456
|
+
INTEGRATION_EDIT: "integration:edit",
|
|
92457
|
+
// ── Dashboard config ─────────────────────────────────────────────────────
|
|
92458
|
+
DASHBOARD_CONFIG: "dashboard:configure",
|
|
92459
|
+
// ── Shopping / customer selector ─────────────────────────────────────────
|
|
92460
|
+
SHOPPING_SELECT: "shopping:select"
|
|
92461
|
+
};
|
|
92462
|
+
|
|
91741
92463
|
// src/components/menu/types.ts
|
|
91742
92464
|
var DEFAULT_LIGHT_THEME3 = {
|
|
91743
92465
|
// Tabs
|
|
@@ -136086,7 +136808,7 @@ function bindFilterBar(card, bundle, getViewMode) {
|
|
|
136086
136808
|
const lower = text.toLowerCase();
|
|
136087
136809
|
const types = /* @__PURE__ */ new Set();
|
|
136088
136810
|
bundle.devices.forEach((d) => {
|
|
136089
|
-
const name = (d.displayName || d.name).toLowerCase();
|
|
136811
|
+
const name = (d.entityLabel || d.displayName || d.name).toLowerCase();
|
|
136090
136812
|
if (!lower || name.includes(lower) || d.type.toLowerCase().includes(lower)) {
|
|
136091
136813
|
types.add(d.type);
|
|
136092
136814
|
}
|
|
@@ -136162,7 +136884,7 @@ function renderDevice(device, rules, viewMode = "granular") {
|
|
|
136162
136884
|
<div class="abm-device-header">
|
|
136163
136885
|
<span class="abm-chevron">\u25BE</span>
|
|
136164
136886
|
<span style="font-size:16px;">\u{1F4E1}</span>
|
|
136165
|
-
<span class="abm-device-name">${escHtml3(device.displayName || device.name)}</span>
|
|
136887
|
+
<span class="abm-device-name">${escHtml3(device.entityLabel || device.displayName || device.name)}</span>
|
|
136166
136888
|
${_alarmBadgeHtml(alarmCount)}
|
|
136167
136889
|
<span class="abm-device-type">${escHtml3(device.type)}</span>
|
|
136168
136890
|
</div>
|
|
@@ -136212,7 +136934,7 @@ function renderByRuleView(bundle) {
|
|
|
136212
136934
|
return `
|
|
136213
136935
|
<li class="abm-rule-group-device" data-device-id="${escHtml3(d.id)}">
|
|
136214
136936
|
<span style="font-size:13px;">\u{1F4E1}</span>
|
|
136215
|
-
<span>${escHtml3(d.displayName || d.name)}</span>
|
|
136937
|
+
<span>${escHtml3(d.entityLabel || d.displayName || d.name)}</span>
|
|
136216
136938
|
<span style="font-size:11px;color:#888;">${escHtml3(d.type)}</span>
|
|
136217
136939
|
${overridePart}
|
|
136218
136940
|
</li>`;
|
|
@@ -139160,6 +139882,7 @@ var version = package_default.version || "0.0.0";
|
|
|
139160
139882
|
MenuShoppingView,
|
|
139161
139883
|
MenuView,
|
|
139162
139884
|
ModalHeader,
|
|
139885
|
+
MyIOAuthContext,
|
|
139163
139886
|
MyIOChartModal,
|
|
139164
139887
|
MyIODraggableCard,
|
|
139165
139888
|
MyIOSelectionStore,
|
|
@@ -139188,6 +139911,7 @@ var version = package_default.version || "0.0.0";
|
|
|
139188
139911
|
OperationalGeneralListController,
|
|
139189
139912
|
OperationalGeneralListView,
|
|
139190
139913
|
OperationalHeaderDevicesGridView,
|
|
139914
|
+
PERM,
|
|
139191
139915
|
POWER_LIMITS_DEVICE_TYPES,
|
|
139192
139916
|
POWER_LIMITS_STATUS_CONFIG,
|
|
139193
139917
|
POWER_LIMITS_TELEMETRY_TYPES,
|
|
@@ -139456,6 +140180,7 @@ var version = package_default.version || "0.0.0";
|
|
|
139456
140180
|
groupByDay,
|
|
139457
140181
|
handleDeviceType,
|
|
139458
140182
|
hasSelectedDays,
|
|
140183
|
+
initMyIOAuthContext,
|
|
139459
140184
|
initOnOffTimelineTooltips,
|
|
139460
140185
|
injectActionButtonStyles,
|
|
139461
140186
|
injectAlarmsNotificationsPanelStyles,
|