myio-js-library 0.1.485 → 0.1.487

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1159,7 +1159,7 @@ module.exports = __toCommonJS(index_exports);
1159
1159
  // package.json
1160
1160
  var package_default = {
1161
1161
  name: "myio-js-library",
1162
- version: "0.1.485",
1162
+ version: "0.1.487",
1163
1163
  description: "A clean, standalone JS SDK for MYIO projects",
1164
1164
  license: "MIT",
1165
1165
  repository: "github:gh-myio/myio-js-library",
@@ -62014,7 +62014,7 @@ var SettingsModalView = class {
62014
62014
  <div class="customer-name-container">
62015
62015
  <div class="customer-info-row">
62016
62016
  <div class="device-type-icon-wrapper">
62017
- ${this.getDeviceTypeIcon(deviceType)}
62017
+ ${this.getDeviceTypeIcon(deviceType ?? "")}
62018
62018
  </div>
62019
62019
  <div class="customer-info-content">
62020
62020
  <div class="customer-name-label">Shopping</div>
@@ -62620,7 +62620,7 @@ var SettingsModalView = class {
62620
62620
  not_installed: { text: "N\xE3o instalado", color: "#94a3b8" },
62621
62621
  unknown: { text: "Sem informa\xE7\xE3o", color: "#94a3b8" }
62622
62622
  };
62623
- const statusInfo = statusMap[mapDeviceStatusToCardStatus(deviceStatus) || ""] || {
62623
+ const statusInfo = statusMap[mapDeviceStatusToCardStatus(deviceStatus ?? "") || ""] || {
62624
62624
  text: "Desconhecido",
62625
62625
  color: "#6b7280"
62626
62626
  };
@@ -62713,6 +62713,13 @@ var SettingsModalView = class {
62713
62713
  }
62714
62714
 
62715
62715
  /* Header handled by ModalHeader (RFC-0121) */
62716
+ .myio-device-settings-modal .myio-modal-header__title {
62717
+ font-size: 14px;
62718
+ font-weight: 600;
62719
+ }
62720
+ .myio-device-settings-modal .myio-modal-header__icon {
62721
+ font-size: 15px;
62722
+ }
62716
62723
 
62717
62724
  .myio-device-settings-modal.is-maximized {
62718
62725
  width: 100vw !important;
@@ -89876,6 +89883,7 @@ var UserDetailTab = class {
89876
89883
  // RFC-0197: Assignments section state
89877
89884
  assignments = [];
89878
89885
  availableRoles = [];
89886
+ availablePolicies = [];
89879
89887
  assignmentsEl = null;
89880
89888
  assignmentsVersion = 0;
89881
89889
  constructor(config, user, callbacks) {
@@ -89969,11 +89977,11 @@ var UserDetailTab = class {
89969
89977
  const section = document.createElement("div");
89970
89978
  section.style.cssText = "margin-top:20px;border:1px solid var(--um-border);border-radius:10px;overflow:hidden;";
89971
89979
  const sectionHeader = document.createElement("div");
89972
- sectionHeader.style.cssText = "display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:var(--um-bg-surface);border-bottom:1px solid var(--um-border);";
89973
- sectionHeader.innerHTML = `<span style="font-size:13px;font-weight:600;color:var(--um-text-secondary);">\u{1F511} Atribui\xE7\xF5es de Fun\xE7\xF5es</span>`;
89980
+ sectionHeader.style.cssText = "display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:var(--um-accent);border-bottom:1px solid var(--um-btn-2-border);";
89981
+ sectionHeader.innerHTML = `<span style="font-size:13px;font-weight:600;color:#fff;">\u{1F511} Fun\xE7\xF5es / Pap\xE9is</span>`;
89974
89982
  const addBtn = document.createElement("button");
89975
89983
  addBtn.className = "um-btn um-btn--secondary um-btn--sm";
89976
- addBtn.textContent = "+ Atribuir Fun\xE7\xE3o";
89984
+ addBtn.textContent = "+ Adicionar";
89977
89985
  addBtn.addEventListener("click", () => this.showAssignForm());
89978
89986
  sectionHeader.appendChild(addBtn);
89979
89987
  section.appendChild(sectionHeader);
@@ -89988,12 +89996,14 @@ var UserDetailTab = class {
89988
89996
  async loadAssignments() {
89989
89997
  const userId = this.user.id.id;
89990
89998
  try {
89991
- const [assignRes, rolesRes] = await Promise.all([
89999
+ const [assignRes, rolesRes, policiesRes] = await Promise.all([
89992
90000
  fetch(`${this.gcdrBase()}/authorization/users/${userId}/assignments`, { headers: this.gcdrHeaders() }),
89993
- fetch(`${this.gcdrBase()}/roles?limit=100`, { headers: this.gcdrHeaders() })
90001
+ fetch(`${this.gcdrBase()}/roles?limit=100`, { headers: this.gcdrHeaders() }),
90002
+ fetch(`${this.gcdrBase()}/policies?limit=100`, { headers: this.gcdrHeaders() })
89994
90003
  ]);
89995
90004
  this.assignments = assignRes.ok ? this.unwrapList(await assignRes.json()) : [];
89996
90005
  this.availableRoles = rolesRes.ok ? this.unwrapList(await rolesRes.json()) : [];
90006
+ this.availablePolicies = policiesRes.ok ? this.unwrapList(await policiesRes.json()) : [];
89997
90007
  this.renderAssignments();
89998
90008
  } catch (err) {
89999
90009
  console.error("[UserDetailTab] loadAssignments error", err);
@@ -90041,14 +90051,19 @@ var UserDetailTab = class {
90041
90051
  overlay.style.zIndex = "100001";
90042
90052
  const modal = document.createElement("div");
90043
90053
  modal.className = "um-modal";
90044
- modal.style.cssText = "padding: 24px; width: min(480px, 92vw); max-height: 80vh; height: auto; aspect-ratio: unset; overflow-y: auto; display: block;";
90054
+ modal.style.cssText = "width: min(820px, 92vw); max-height: 85vh; height: auto; aspect-ratio: unset; overflow: hidden; display: flex; flex-direction: column;";
90045
90055
  const gcdrCid = window.MyIOOrchestrator?.gcdrCustomerId || "";
90046
90056
  const scopeOptions = [
90047
- { value: "*", label: "* (global)" },
90048
- ...gcdrCid ? [{ value: `customer:${gcdrCid}`, label: `customer:${gcdrCid}` }] : []
90057
+ { value: "*", label: "* (global \u2014 todos os clientes)" },
90058
+ ...gcdrCid ? [{ value: `customer:${gcdrCid}`, label: `Cliente atual (${gcdrCid.slice(0, 8)}...)` }] : []
90049
90059
  ];
90050
90060
  modal.innerHTML = `
90051
- <h4 style="margin:0 0 16px;font-size:15px;font-weight:600;color:var(--um-text-primary,#e2e8f0);">Atribuir Fun\xE7\xE3o</h4>
90061
+ <div style="display:flex;align-items:center;gap:10px;padding:14px 20px;background:var(--um-accent);border-bottom:1px solid var(--um-btn-2-border);flex-shrink:0;">
90062
+ <span style="font-size:15px;">\u{1F511}</span>
90063
+ <span style="flex:1;font-size:14px;font-weight:600;color:#fff;">Atribuir Fun\xE7\xE3o / Papel</span>
90064
+ <button type="button" class="assign-close" style="background:none;border:none;color:rgba(255,255,255,0.8);font-size:18px;cursor:pointer;padding:2px 6px;border-radius:4px;line-height:1;">\u2715</button>
90065
+ </div>
90066
+ <div style="padding:20px 24px;overflow-y:auto;flex:1;">
90052
90067
  <div class="um-form" style="max-width:100%;">
90053
90068
  <div class="um-form-group">
90054
90069
  <label class="um-label">Fun\xE7\xE3o <span class="um-req">*</span></label>
@@ -90058,6 +90073,10 @@ var UserDetailTab = class {
90058
90073
  </select>
90059
90074
  <span class="um-field-error" data-for="roleId"></span>
90060
90075
  </div>
90076
+ <div class="um-assign-policies-preview" style="display:none;border:1px solid var(--um-border);border-radius:8px;padding:12px;background:var(--um-bg-surface);">
90077
+ <div style="font-size:11px;font-weight:600;color:var(--um-text-muted);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:8px;">Pol\xEDticas inclu\xEDdas</div>
90078
+ <div class="um-assign-policies-list" style="display:flex;flex-direction:column;gap:6px;"></div>
90079
+ </div>
90061
90080
  <div class="um-form-group">
90062
90081
  <label class="um-label">Escopo <span class="um-req">*</span></label>
90063
90082
  <select class="um-input" name="scope">
@@ -90077,13 +90096,38 @@ var UserDetailTab = class {
90077
90096
  <button class="um-btn um-btn--primary assign-save">Atribuir</button>
90078
90097
  </div>
90079
90098
  </div>
90099
+ </div>
90080
90100
  `;
90081
90101
  overlay.appendChild(modal);
90082
90102
  document.body.appendChild(overlay);
90103
+ const roleSelect = modal.querySelector("[name=roleId]");
90104
+ const policiesPreview = modal.querySelector(".um-assign-policies-preview");
90105
+ const policiesList = modal.querySelector(".um-assign-policies-list");
90106
+ roleSelect.addEventListener("change", () => {
90107
+ const role = this.availableRoles.find((r) => r.id === roleSelect.value);
90108
+ const policyRefs = role?.policies ?? role?.policyIds ?? [];
90109
+ if (!role || policyRefs.length === 0) {
90110
+ policiesPreview.style.display = "none";
90111
+ return;
90112
+ }
90113
+ const policyByKey = new Map(this.availablePolicies.flatMap((p) => [
90114
+ [p.key || p.id, p],
90115
+ [p.id, p]
90116
+ ]));
90117
+ const matched = policyRefs.map((ref) => policyByKey.get(ref)).filter(Boolean);
90118
+ policiesList.innerHTML = matched.length > 0 ? matched.map((p) => `
90119
+ <div style="display:flex;align-items:flex-start;gap:8px;padding:6px 8px;background:var(--um-bg-input);border-radius:6px;">
90120
+ <span style="font-size:11px;font-weight:600;background:var(--um-btn-2-bg);color:var(--um-btn-2-text);border:1px solid var(--um-btn-2-border);border-radius:9999px;padding:2px 8px;white-space:nowrap;">${this.esc(p.displayName)}</span>
90121
+ ${p.description ? `<span style="font-size:11px;color:var(--um-text-muted);padding-top:2px;">${this.esc(p.description)}</span>` : ""}
90122
+ </div>`).join("") : policyRefs.map((ref) => `
90123
+ <span style="font-size:11px;font-family:monospace;background:var(--um-btn-2-bg);color:var(--um-btn-2-text);border:1px solid var(--um-btn-2-border);border-radius:9999px;padding:2px 8px;">${this.esc(ref)}</span>`).join("");
90124
+ policiesPreview.style.display = "block";
90125
+ });
90083
90126
  const close = () => overlay.remove();
90084
90127
  overlay.addEventListener("click", (e) => {
90085
90128
  if (e.target === overlay) close();
90086
90129
  });
90130
+ modal.querySelector(".assign-close").addEventListener("click", close);
90087
90131
  modal.querySelector(".assign-cancel").addEventListener("click", close);
90088
90132
  modal.querySelector(".assign-save").addEventListener("click", async () => {
90089
90133
  const roleId = modal.querySelector("[name=roleId]").value.trim();
@@ -90697,7 +90741,8 @@ var RolesTab = class {
90697
90741
  const item = document.createElement("div");
90698
90742
  item.className = `gm-accordion-item${this.expandedId === r.id ? " gm-accordion-item--open" : ""}`;
90699
90743
  const sysBadge = r.isSystem ? `<span class="gm-badge gm-badge--count">SISTEMA</span>` : "";
90700
- const policyCount = r.policyIds?.length || 0;
90744
+ const policyKeys = r.policies ?? r.policyIds ?? [];
90745
+ const policyCount = policyKeys.length;
90701
90746
  const policyBadge = `<span class="gm-badge gm-badge--domain">${policyCount} pol\xEDticas</span>`;
90702
90747
  item.innerHTML = `
90703
90748
  <div class="gm-accordion-header">
@@ -90737,20 +90782,26 @@ var RolesTab = class {
90737
90782
  return item;
90738
90783
  }
90739
90784
  buildRolePoliciesHtml(r) {
90740
- if (!r.policyIds?.length) {
90785
+ const policyKeys = r.policies ?? r.policyIds ?? [];
90786
+ if (!policyKeys.length) {
90741
90787
  return `<div class="gm-panel-section"><span class="gm-empty-inline">Nenhuma pol\xEDtica associada.</span></div>`;
90742
90788
  }
90743
- const policyMap = new Map(this.policies.map((p) => [p.id, p]));
90744
- const items = r.policyIds.map((pid) => {
90745
- const p = policyMap.get(pid);
90789
+ const policyByKey = new Map(this.policies.flatMap((p) => [
90790
+ [p.key || p.id, p],
90791
+ [p.id, p]
90792
+ ]));
90793
+ const items = policyKeys.map((ref) => {
90794
+ const p = policyByKey.get(ref);
90746
90795
  if (!p)
90747
- return `<div style="font-size:12px;color:var(--um-text-faint);padding:3px 0;">ID: ${this.esc(pid)}</div>`;
90748
- const allowStr = p.allow?.join(", ") || "\u2014";
90796
+ return `<div style="padding:6px 0;border-bottom:1px solid var(--um-border-sub);">
90797
+ <span style="font-size:11px;font-family:monospace;background:var(--um-btn-2-bg);color:var(--um-btn-2-text);border:1px solid var(--um-btn-2-border);border-radius:9999px;padding:2px 8px;">${this.esc(ref)}</span>
90798
+ </div>`;
90799
+ const allowStr = p.allow?.length ? p.allow.join(", ") : "\u2014";
90749
90800
  return `<div style="padding:6px 0;border-bottom:1px solid var(--um-border-sub);">
90750
- <div style="font-size:12px;font-weight:600;color:var(--um-text-secondary);">${this.esc(p.displayName)}</div>
90751
- ${p.description ? `<div style="font-size:11px;color:var(--um-text-faint);">${this.esc(p.description)}</div>` : ""}
90752
- <div style="font-size:11px;color:var(--um-badge-user-text);margin-top:2px;">allow: ${this.esc(allowStr)}</div>
90753
- </div>`;
90801
+ <div style="font-size:12px;font-weight:600;color:var(--um-text-secondary);">${this.esc(p.displayName)}</div>
90802
+ ${p.description ? `<div style="font-size:11px;color:var(--um-text-faint);">${this.esc(p.description)}</div>` : ""}
90803
+ <div style="font-size:11px;color:var(--um-badge-user-text);margin-top:2px;">allow: ${this.esc(allowStr)}</div>
90804
+ </div>`;
90754
90805
  }).join("");
90755
90806
  return `<div class="gm-panel-section">
90756
90807
  <div class="gm-panel-section-header"><span class="gm-panel-section-title">\u{1F4CB} Pol\xEDticas</span></div>
@@ -90766,10 +90817,12 @@ var RolesTab = class {
90766
90817
  const modal = document.createElement("div");
90767
90818
  modal.className = "um-modal";
90768
90819
  modal.style.cssText = "padding: 24px; width: min(520px, 92vw); max-height: 80vh; height: auto; aspect-ratio: unset; overflow-y: auto; display: block;";
90820
+ const existingPolicies = existing?.policies ?? existing?.policyIds ?? [];
90769
90821
  const policiesCheckboxes = this.policies.map((p) => {
90770
- const checked = existing?.policyIds?.includes(p.id) ? " checked" : "";
90822
+ const ref = p.key || p.id;
90823
+ const checked = existingPolicies.includes(ref) ? " checked" : "";
90771
90824
  return `<label style="display:flex;align-items:center;gap:8px;padding:4px 0;font-size:12px;color:var(--um-text-secondary);cursor:pointer;">
90772
- <input type="checkbox" class="um-role-policy-chk" value="${this.esc(p.id)}"${checked} />
90825
+ <input type="checkbox" class="um-role-policy-chk" value="${this.esc(ref)}"${checked} />
90773
90826
  ${this.esc(p.displayName)}${p.description ? ` <span style="color:var(--um-text-faint);">\u2014 ${this.esc(p.description)}</span>` : ""}
90774
90827
  </label>`;
90775
90828
  }).join("");
@@ -90813,14 +90866,14 @@ var RolesTab = class {
90813
90866
  }
90814
90867
  errEl.textContent = "";
90815
90868
  const description = modal.querySelector("[name=description]").value.trim();
90816
- const policyIds = Array.from(
90869
+ const policies = Array.from(
90817
90870
  modal.querySelectorAll(".um-role-policy-chk:checked")
90818
90871
  ).map((c) => c.value);
90819
90872
  const btn = modal.querySelector(".role-save");
90820
90873
  btn.disabled = true;
90821
90874
  btn.textContent = "...";
90822
90875
  try {
90823
- const body = { name, description: description || void 0, policyIds };
90876
+ const body = { name, description: description || void 0, policies };
90824
90877
  const url = isEdit ? `${this.gcdrBase()}/roles/${existing.id}` : `${this.gcdrBase()}/roles`;
90825
90878
  const res = await fetch(url, {
90826
90879
  method: isEdit ? "PUT" : "POST",
@@ -91107,6 +91160,13 @@ var UserManagementModalView = class {
91107
91160
  }
91108
91161
  // ── Styles ───────────────────────────────────────────────────────────────────
91109
91162
  injectStyles() {
91163
+ if (!document.getElementById("um-font-nunito")) {
91164
+ const link = document.createElement("link");
91165
+ link.id = "um-font-nunito";
91166
+ link.rel = "stylesheet";
91167
+ link.href = "https://fonts.googleapis.com/css2?family=Nunito:wght@400;500;600;700&display=swap";
91168
+ document.head.appendChild(link);
91169
+ }
91110
91170
  if (document.getElementById("um-styles")) return;
91111
91171
  const style = document.createElement("style");
91112
91172
  style.id = "um-styles";
@@ -91125,12 +91185,12 @@ var UserManagementModalView = class {
91125
91185
  --um-text-secondary: #374151;
91126
91186
  --um-text-muted: #64748b;
91127
91187
  --um-text-faint: #94a3b8;
91128
- --um-accent: #3b5bdb;
91129
- --um-accent-hover: #4c6ef5;
91130
- --um-btn-2-bg: #eff6ff;
91131
- --um-btn-2-text: #3b5bdb;
91132
- --um-btn-2-border: #bfdbfe;
91133
- --um-btn-2-hover: #dbeafe;
91188
+ --um-accent: #3f1a7d;
91189
+ --um-accent-hover: #5a2bab;
91190
+ --um-btn-2-bg: #f4effa;
91191
+ --um-btn-2-text: #3f1a7d;
91192
+ --um-btn-2-border: #c9a8e8;
91193
+ --um-btn-2-hover: #e8d8f5;
91134
91194
  --um-btn-ghost-border: #e2e8f0;
91135
91195
  --um-btn-ghost-hover: #f1f5f9;
91136
91196
  --um-btn-ghost-text-hover:#475569;
@@ -91149,14 +91209,14 @@ var UserManagementModalView = class {
91149
91209
  --um-toast-err-border: #ef4444;
91150
91210
  --um-toast-err-text: #dc2626;
91151
91211
  --um-spinner-border: #cbd5e1;
91152
- --um-spinner-top: #3b5bdb;
91212
+ --um-spinner-top: #3f1a7d;
91153
91213
  --um-toggle-off: #cbd5e1;
91154
- --um-notice-bg: #eff6ff;
91155
- --um-notice-border: #bfdbfe;
91214
+ --um-notice-bg: #f4effa;
91215
+ --um-notice-border: #c9a8e8;
91156
91216
  --um-shadow: 0 32px 80px rgba(0,0,0,0.15);
91157
91217
  --um-row-highlight: #f0fdf4;
91158
- --um-gm-badge-domain-bg: #eff6ff;
91159
- --um-gm-badge-domain-txt: #3b5bdb;
91218
+ --um-gm-badge-domain-bg: #f4effa;
91219
+ --um-gm-badge-domain-txt: #3f1a7d;
91160
91220
  --um-gm-badge-count-bg: #f1f5f9;
91161
91221
  --um-gm-badge-count-txt: #64748b;
91162
91222
  --um-gm-form-card-bg: #f8fafc;
@@ -91185,12 +91245,12 @@ var UserManagementModalView = class {
91185
91245
  --um-text-secondary: #c4ccd9;
91186
91246
  --um-text-muted: #64748b;
91187
91247
  --um-text-faint: #475569;
91188
- --um-accent: #60a5fa;
91189
- --um-accent-hover: #93c5fd;
91190
- --um-btn-2-bg: #1e2d4a;
91191
- --um-btn-2-text: #93c5fd;
91192
- --um-btn-2-border: #2a3b60;
91193
- --um-btn-2-hover: #253456;
91248
+ --um-accent: #a78bdb;
91249
+ --um-accent-hover: #c3a8f0;
91250
+ --um-btn-2-bg: #2a1a45;
91251
+ --um-btn-2-text: #c3a8f0;
91252
+ --um-btn-2-border: #4a2a80;
91253
+ --um-btn-2-hover: #3a2060;
91194
91254
  --um-btn-ghost-border: #2a3352;
91195
91255
  --um-btn-ghost-hover: #1a2235;
91196
91256
  --um-btn-ghost-text-hover:#94a3b8;
@@ -91209,14 +91269,14 @@ var UserManagementModalView = class {
91209
91269
  --um-toast-err-border: #ef4444;
91210
91270
  --um-toast-err-text: #f87171;
91211
91271
  --um-spinner-border: #2a3352;
91212
- --um-spinner-top: #60a5fa;
91272
+ --um-spinner-top: #a78bdb;
91213
91273
  --um-toggle-off: #2a3352;
91214
- --um-notice-bg: #1a2537;
91215
- --um-notice-border: #2a3b60;
91274
+ --um-notice-bg: #1e1235;
91275
+ --um-notice-border: #4a2a80;
91216
91276
  --um-shadow: 0 32px 80px rgba(0,0,0,0.6);
91217
91277
  --um-row-highlight: #0e2a1e;
91218
- --um-gm-badge-domain-bg: #1a2537;
91219
- --um-gm-badge-domain-txt: #60a5fa;
91278
+ --um-gm-badge-domain-bg: #2a1a45;
91279
+ --um-gm-badge-domain-txt: #c3a8f0;
91220
91280
  --um-gm-badge-count-bg: #1e2d4a;
91221
91281
  --um-gm-badge-count-txt: #94a3b8;
91222
91282
  --um-gm-form-card-bg: #0f1829;
@@ -91230,6 +91290,7 @@ var UserManagementModalView = class {
91230
91290
  /* \u2500\u2500 Component styles (theme-agnostic via variables) \u2500\u2500 */
91231
91291
  .um-modal {
91232
91292
  background: var(--um-modal-bg);
91293
+ font-family: 'Nunito', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
91233
91294
  border-radius: 14px; width: 92vw; max-width: 960px;
91234
91295
  --modal-header-radius: 14px 14px 0 0;
91235
91296
  aspect-ratio: 16/9; display: flex; flex-direction: column;
@@ -91239,6 +91300,20 @@ var UserManagementModalView = class {
91239
91300
 
91240
91301
  /* Header handled by ModalHeader (RFC-0121) */
91241
91302
 
91303
+ /* Force MyIO purple header regardless of light/dark theme \u2014 overrides myio-modal-header--light */
91304
+ .um-modal .myio-modal-header--light {
91305
+ background: #3f1a7d !important;
91306
+ border-bottom-color: #2e1260 !important;
91307
+ }
91308
+ .um-modal .myio-modal-header--light .myio-modal-header__title { color: #fff !important; }
91309
+ .um-modal .myio-modal-header--light .myio-modal-header__btn { color: rgba(255,255,255,0.8) !important; }
91310
+ .um-modal .myio-modal-header--light .myio-modal-header__btn:hover {
91311
+ background: rgba(255,255,255,0.15) !important; color: #fff !important;
91312
+ }
91313
+ .um-modal .myio-modal-header--light .myio-modal-header__btn--close:hover {
91314
+ background: rgba(239,68,68,0.3) !important; color: #fecaca !important;
91315
+ }
91316
+
91242
91317
  /* Maximize button \u2014 CSS window icon (emoji renders as plain square on some platforms) */
91243
91318
  #um-modal-maximize { font-size: 0 !important; position: relative; }
91244
91319
  #um-modal-maximize::before {
@@ -91389,7 +91464,7 @@ var UserManagementModalView = class {
91389
91464
  .um-check-label { font-size: 13px; color: var(--um-text-secondary); cursor: pointer; display: flex; align-items: center; gap: 8px; }
91390
91465
  .um-form-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 8px; }
91391
91466
 
91392
- .um-detail-card { max-width: 560px; }
91467
+ .um-detail-card { width: 100%; }
91393
91468
  .um-detail-section { border: 1px solid var(--um-border); border-radius: 10px; overflow: hidden; margin-bottom: 20px; }
91394
91469
  .um-detail-row {
91395
91470
  display: flex; align-items: flex-start; gap: 16px;
@@ -135492,6 +135567,50 @@ var STYLES3 = `
135492
135567
  font-size: 12px; font-weight: 600; color: #e67e22;
135493
135568
  background: #fef3e8; border-radius: 4px; padding: 1px 6px;
135494
135569
  }
135570
+ /* Collapse/expand */
135571
+ #${MODAL_ID3} .abm-device-card.collapsed .abm-rule-list { display: none; }
135572
+ #${MODAL_ID3} .abm-rule-group.collapsed .abm-rule-group-devices,
135573
+ #${MODAL_ID3} .abm-rule-group.collapsed .abm-rule-detail-wrap { display: none; }
135574
+ #${MODAL_ID3} .abm-chevron {
135575
+ font-size: 11px; color: #94a3b8; transition: transform 0.18s; flex-shrink: 0; user-select: none;
135576
+ }
135577
+ #${MODAL_ID3} .abm-device-card.collapsed .abm-chevron,
135578
+ #${MODAL_ID3} .abm-rule-group.collapsed .abm-chevron { transform: rotate(-90deg); }
135579
+ #${MODAL_ID3} .abm-device-header { cursor: pointer; }
135580
+ #${MODAL_ID3} .abm-device-header:hover { background: #f0f9f7; }
135581
+ #${MODAL_ID3} .abm-rule-group-header { cursor: pointer; }
135582
+ #${MODAL_ID3} .abm-rule-group-header:hover { background: #e6f4f1; }
135583
+ /* Alarm badge */
135584
+ #${MODAL_ID3} .abm-alarm-badge {
135585
+ display: inline-flex; align-items: center; gap: 3px;
135586
+ background: #fee2e2; color: #b91c1c; border-radius: 10px;
135587
+ font-size: 10px; font-weight: 700; padding: 1px 7px; flex-shrink: 0;
135588
+ }
135589
+ #${MODAL_ID3} .abm-alarm-badge.zero { background: #f0f9f7; color: #6b7280; }
135590
+ /* Filter bar */
135591
+ #${MODAL_ID3} .abm-filter-bar {
135592
+ display: flex; gap: 8px; align-items: center; margin-bottom: 14px;
135593
+ padding: 10px 12px; background: #f8faf9; border: 1px solid #e0eceb; border-radius: 8px;
135594
+ }
135595
+ #${MODAL_ID3} .abm-filter-input {
135596
+ flex: 1; border: 1px solid #d1d5db; border-radius: 6px; padding: 6px 10px;
135597
+ font-size: 12px; outline: none; transition: border-color 0.15s;
135598
+ }
135599
+ #${MODAL_ID3} .abm-filter-input:focus { border-color: ${TEAL2}; }
135600
+ #${MODAL_ID3} .abm-filter-select {
135601
+ border: 1px solid #d1d5db; border-radius: 6px; padding: 5px 8px;
135602
+ font-size: 12px; outline: none; min-width: 140px; max-width: 220px;
135603
+ background: #fff; cursor: pointer;
135604
+ }
135605
+ #${MODAL_ID3} .abm-filter-count {
135606
+ font-size: 11px; color: #64748b; white-space: nowrap; flex-shrink: 0;
135607
+ }
135608
+ #${MODAL_ID3} .abm-filter-clear {
135609
+ background: none; border: 1px solid #e0eceb; border-radius: 6px; cursor: pointer;
135610
+ font-size: 11px; color: #64748b; padding: 4px 8px; white-space: nowrap; flex-shrink: 0;
135611
+ transition: background 0.12s;
135612
+ }
135613
+ #${MODAL_ID3} .abm-filter-clear:hover { background: #f0f4f3; color: #1e293b; }
135495
135614
  /* Confirmation modal \u2014 value edit overwrite warning */
135496
135615
  .abm-confirm-overlay {
135497
135616
  position: fixed; inset: 0;
@@ -135922,6 +136041,8 @@ function bindRuleEditEvents(card, bundle, gcdrTenantId, baseUrl, getViewMode) {
135922
136041
  if (!ruleId) return;
135923
136042
  const rule = bundle.rules[ruleId];
135924
136043
  if (!rule) return;
136044
+ editBtn.closest(".abm-device-card")?.classList.remove("collapsed");
136045
+ editBtn.closest(".abm-rule-group")?.classList.remove("collapsed");
135925
136046
  const viewMode = getViewMode();
135926
136047
  if (viewMode === "por-regra") {
135927
136048
  const ruleGroup = editBtn.closest(".abm-rule-group");
@@ -135940,13 +136061,109 @@ function bindRuleEditEvents(card, bundle, gcdrTenantId, baseUrl, getViewMode) {
135940
136061
  openGranularValueEdit(ruleItem, rule, device, bundle, gcdrTenantId, baseUrl);
135941
136062
  });
135942
136063
  }
136064
+ function bindCollapseEvents(card) {
136065
+ card.querySelectorAll(".abm-device-header").forEach((header) => {
136066
+ header.addEventListener("click", (e) => {
136067
+ if (e.target.closest("button")) return;
136068
+ header.closest(".abm-device-card")?.classList.toggle("collapsed");
136069
+ });
136070
+ });
136071
+ card.querySelectorAll(".abm-rule-group-header").forEach((header) => {
136072
+ header.addEventListener("click", (e) => {
136073
+ if (e.target.closest("button")) return;
136074
+ header.closest(".abm-rule-group")?.classList.toggle("collapsed");
136075
+ });
136076
+ });
136077
+ }
136078
+ function bindFilterBar(card, bundle, getViewMode) {
136079
+ const input = card.querySelector("#abm-filter-input");
136080
+ const select = card.querySelector("#abm-filter-select");
136081
+ const countEl = card.querySelector("#abm-filter-count");
136082
+ const clearBtn = card.querySelector("#abm-filter-clear");
136083
+ if (!input) return;
136084
+ const updateSelectOptions = (text) => {
136085
+ if (!select) return;
136086
+ const lower = text.toLowerCase();
136087
+ const types = /* @__PURE__ */ new Set();
136088
+ bundle.devices.forEach((d) => {
136089
+ const name = (d.displayName || d.name).toLowerCase();
136090
+ if (!lower || name.includes(lower) || d.type.toLowerCase().includes(lower)) {
136091
+ types.add(d.type);
136092
+ }
136093
+ });
136094
+ const prev = new Set(Array.from(select.selectedOptions).map((o) => o.value));
136095
+ select.innerHTML = Array.from(types).sort().map(
136096
+ (t) => `<option value="${escHtml3(t)}"${prev.has(t) ? " selected" : ""}>${escHtml3(t)}</option>`
136097
+ ).join("");
136098
+ select.size = Math.min(types.size, 4) || 1;
136099
+ };
136100
+ const applyFilter = () => {
136101
+ const text = input.value.toLowerCase().trim();
136102
+ const selectedTypes = new Set(Array.from(select?.selectedOptions ?? []).map((o) => o.value));
136103
+ const mode = getViewMode();
136104
+ if (mode === "granular") {
136105
+ let visible = 0;
136106
+ card.querySelectorAll(".abm-device-card").forEach((dc) => {
136107
+ const name = (dc.querySelector(".abm-device-name")?.textContent ?? "").toLowerCase();
136108
+ const type = dc.querySelector(".abm-device-type")?.textContent ?? "";
136109
+ const ruleNames = Array.from(dc.querySelectorAll(".abm-rule-name")).map((el2) => (el2.textContent ?? "").toLowerCase());
136110
+ const matchText = !text || name.includes(text) || type.toLowerCase().includes(text) || ruleNames.some((r) => r.includes(text));
136111
+ const matchType = selectedTypes.size === 0 || selectedTypes.has(type);
136112
+ dc.style.display = matchText && matchType ? "" : "none";
136113
+ if (matchText && matchType) visible++;
136114
+ });
136115
+ if (countEl) countEl.textContent = `${visible} dispositivo(s)`;
136116
+ } else {
136117
+ let visible = 0;
136118
+ card.querySelectorAll(".abm-rule-group").forEach((rg) => {
136119
+ const ruleName = (rg.querySelector(".abm-rule-group-name")?.textContent ?? "").toLowerCase();
136120
+ const deviceTexts = Array.from(rg.querySelectorAll(".abm-rule-group-device span")).map((el2) => (el2.textContent ?? "").toLowerCase());
136121
+ const matchText = !text || ruleName.includes(text) || deviceTexts.some((t) => t.includes(text));
136122
+ rg.style.display = matchText ? "" : "none";
136123
+ if (matchText) visible++;
136124
+ });
136125
+ if (countEl) countEl.textContent = `${visible} regra(s)`;
136126
+ }
136127
+ updateSelectOptions(text);
136128
+ };
136129
+ input.addEventListener("input", applyFilter);
136130
+ select?.addEventListener("change", applyFilter);
136131
+ clearBtn?.addEventListener("click", () => {
136132
+ input.value = "";
136133
+ if (select) Array.from(select.options).forEach((o) => {
136134
+ o.selected = false;
136135
+ });
136136
+ applyFilter();
136137
+ });
136138
+ updateSelectOptions("");
136139
+ const totalDevices = bundle.devices.length;
136140
+ if (countEl) countEl.textContent = `${totalDevices} dispositivo(s)`;
136141
+ }
136142
+ var _OFFLINE_ALARM_TYPES = ["DEVICE OFFLINE", "DISPOSITIVO OFFLINE"];
136143
+ function _getActiveAlarmCount(gcdrDeviceId) {
136144
+ const aso = window.AlarmServiceOrchestrator;
136145
+ if (!aso?.deviceAlarmMap) return 0;
136146
+ const alarms = aso.deviceAlarmMap.get(gcdrDeviceId) || [];
136147
+ const showOffline = window.MyIOOrchestrator?.showOfflineAlarms === true;
136148
+ return showOffline ? alarms.length : alarms.filter((a) => {
136149
+ const t = (a.title ?? "").toUpperCase();
136150
+ return !(_OFFLINE_ALARM_TYPES.some((ex) => t.startsWith(ex)) || a.alarmType === "connectivity");
136151
+ }).length;
136152
+ }
136153
+ function _alarmBadgeHtml(count) {
136154
+ if (count === 0) return `<span class="abm-alarm-badge zero">0 \u{1F514}</span>`;
136155
+ return `<span class="abm-alarm-badge">\u{1F534} ${count}</span>`;
136156
+ }
135943
136157
  function renderDevice(device, rules, viewMode = "granular") {
135944
136158
  const deviceRules = (device.ruleIds ?? []).map((rid) => rules[rid]).filter(Boolean);
136159
+ const alarmCount = _getActiveAlarmCount(device.id);
135945
136160
  return `
135946
- <div class="abm-device-card">
136161
+ <div class="abm-device-card collapsed" data-device-id="${escHtml3(device.id)}">
135947
136162
  <div class="abm-device-header">
136163
+ <span class="abm-chevron">\u25BE</span>
135948
136164
  <span style="font-size:16px;">\u{1F4E1}</span>
135949
136165
  <span class="abm-device-name">${escHtml3(device.displayName || device.name)}</span>
136166
+ ${_alarmBadgeHtml(alarmCount)}
135950
136167
  <span class="abm-device-type">${escHtml3(device.type)}</span>
135951
136168
  </div>
135952
136169
  <ul class="abm-rule-list">
@@ -135973,11 +136190,14 @@ function renderByRuleView(bundle) {
135973
136190
  for (const [baseRuleId, devs] of ruleDevicesMap) {
135974
136191
  const baseRule = rules[baseRuleId];
135975
136192
  if (!baseRule) continue;
136193
+ const groupAlarmCount = devs.reduce((sum, d) => sum + _getActiveAlarmCount(d.id), 0);
135976
136194
  groups += `
135977
- <div class="abm-rule-group">
136195
+ <div class="abm-rule-group collapsed" data-rule-id="${escHtml3(baseRuleId)}">
135978
136196
  <div class="abm-rule-group-header">
136197
+ <span class="abm-chevron">\u25BE</span>
135979
136198
  <span style="font-size:16px;">\u{1F514}</span>
135980
136199
  <span class="abm-rule-group-name">${escHtml3(baseRule.name)}</span>
136200
+ ${_alarmBadgeHtml(groupAlarmCount)}
135981
136201
  <button class="abm-edit-rule-btn" data-edit-rule="${escHtml3(baseRuleId)}" title="Editar dias/hor\xE1rio">\u270F\uFE0F</button>
135982
136202
  </div>
135983
136203
  <div class="abm-rule-detail" style="padding:8px 14px;" data-rule-id="${escHtml3(baseRuleId)}">
@@ -136041,7 +136261,6 @@ function renderBundle(bundle, viewMode = "granular") {
136041
136261
  <div class="abm-summary-bar">
136042
136262
  <div class="abm-summary-item"><div class="abm-summary-num">${deviceCount}</div><div class="abm-summary-label">Dispositivos</div></div>
136043
136263
  <div class="abm-summary-item"><div class="abm-summary-num">${ruleCount}</div><div class="abm-summary-label">Regras</div></div>
136044
- <div class="abm-summary-item"><div class="abm-summary-num">${assets.length}</div><div class="abm-summary-label">Assets</div></div>
136045
136264
  </div>
136046
136265
  ${renderByRuleView(bundle)}
136047
136266
  `;
@@ -136075,10 +136294,6 @@ function renderBundle(bundle, viewMode = "granular") {
136075
136294
  <div class="abm-summary-num">${ruleCount}</div>
136076
136295
  <div class="abm-summary-label">Regras</div>
136077
136296
  </div>
136078
- <div class="abm-summary-item">
136079
- <div class="abm-summary-num">${assets.length}</div>
136080
- <div class="abm-summary-label">Assets</div>
136081
- </div>
136082
136297
  </div>
136083
136298
  ${groups}
136084
136299
  `;
@@ -136169,7 +136384,21 @@ async function openAlarmBundleMapModal(params) {
136169
136384
  const customerName = bundle.customer.displayName || bundle.customer.name || "";
136170
136385
  const rerenderBundle = () => {
136171
136386
  render3(renderBundle(bundle, viewMode), customerName, true);
136387
+ const body = card.querySelector(".abm-body");
136388
+ if (body) {
136389
+ const filterBarEl = document.createElement("div");
136390
+ filterBarEl.className = "abm-filter-bar";
136391
+ filterBarEl.innerHTML = `
136392
+ <input class="abm-filter-input" type="text" id="abm-filter-input" placeholder="Buscar dispositivo, regra, tipo..." />
136393
+ <select class="abm-filter-select" id="abm-filter-select" multiple size="1"></select>
136394
+ <span class="abm-filter-count" id="abm-filter-count"></span>
136395
+ <button class="abm-filter-clear" id="abm-filter-clear" type="button">\u2715 Limpar</button>
136396
+ `;
136397
+ body.insertBefore(filterBarEl, body.firstChild);
136398
+ }
136172
136399
  bindRuleEditEvents(card, bundle, params.gcdrTenantId, baseUrl, () => viewMode);
136400
+ bindCollapseEvents(card);
136401
+ bindFilterBar(card, bundle, () => viewMode);
136173
136402
  card.querySelectorAll("[data-view]").forEach((btn) => {
136174
136403
  btn.addEventListener("click", () => {
136175
136404
  const newMode = btn.getAttribute("data-view");
package/dist/index.d.cts CHANGED
@@ -10435,11 +10435,12 @@ interface TBUserPage {
10435
10435
  }
10436
10436
  interface GCDRPolicy {
10437
10437
  id: string;
10438
+ key?: string;
10438
10439
  displayName: string;
10439
10440
  description?: string;
10440
10441
  allow: string[];
10441
10442
  deny: string[];
10442
- riskLevel?: 'LOW' | 'MEDIUM' | 'HIGH';
10443
+ riskLevel?: 'low' | 'medium' | 'high' | 'critical' | 'LOW' | 'MEDIUM' | 'HIGH';
10443
10444
  /** System policies are immutable */
10444
10445
  isSystem?: boolean;
10445
10446
  createdAt?: string;
@@ -10447,10 +10448,14 @@ interface GCDRPolicy {
10447
10448
  }
10448
10449
  interface GCDRRole {
10449
10450
  id: string;
10451
+ key?: string;
10450
10452
  displayName: string;
10451
10453
  description?: string;
10452
- /** IDs of associated policies */
10453
- policyIds: string[];
10454
+ /** Policy keys as returned by the GCDR API (e.g. "policy:alarm-management") */
10455
+ policies?: string[];
10456
+ /** Legacy field — use `policies` instead */
10457
+ policyIds?: string[];
10458
+ riskLevel?: 'low' | 'medium' | 'high' | 'critical';
10454
10459
  isSystem?: boolean;
10455
10460
  createdAt?: string;
10456
10461
  updatedAt?: string;