myio-js-library 0.1.551 → 0.1.552

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
@@ -899,7 +899,7 @@ module.exports = __toCommonJS(src_exports);
899
899
  // package.json
900
900
  var package_default = {
901
901
  name: "myio-js-library",
902
- version: "0.1.551",
902
+ version: "0.1.552",
903
903
  description: "A clean, standalone JS SDK for MYIO projects",
904
904
  license: "MIT",
905
905
  repository: "github:gh-myio/myio-js-library",
@@ -5568,12 +5568,14 @@ var ModalHeader = {
5568
5568
  showClose = true,
5569
5569
  primaryColor = "#3e1a7d",
5570
5570
  borderRadius = "10px 10px 0 0",
5571
- draggable = true
5571
+ draggable = true,
5572
+ solid = false
5572
5573
  } = options;
5573
5574
  const isDark = theme === "dark";
5574
- const bgColor = isDark ? primaryColor : "linear-gradient(90deg, #f1f5f9 0%, #e2e8f0 100%)";
5575
- const textColor = isDark ? "white" : "#475569";
5576
- const btnColor = isDark ? "rgba(255,255,255,0.8)" : "rgba(71,85,105,0.8)";
5575
+ const solidStyle = isDark || solid;
5576
+ const bgColor = solidStyle ? primaryColor : "linear-gradient(90deg, #f1f5f9 0%, #e2e8f0 100%)";
5577
+ const textColor = solidStyle ? "white" : "#475569";
5578
+ const btnColor = solidStyle ? "rgba(255,255,255,0.8)" : "rgba(71,85,105,0.8)";
5577
5579
  const headerStyle = `
5578
5580
  padding: 8px 12px;
5579
5581
  display: flex;
@@ -35659,11 +35661,26 @@ var DeviceReportModal = class {
35659
35661
  );
35660
35662
  const zeroCount = this.data.filter((r) => r.consumption <= 0).length;
35661
35663
  const rowLabel = isHourly ? "Hora" : "Dia";
35664
+ let perDay;
35665
+ if (isTemperature) {
35666
+ const byDay = /* @__PURE__ */ new Map();
35667
+ for (const r of rows) {
35668
+ const k = r.date.slice(0, 10);
35669
+ const acc = byDay.get(k) || { sum: 0, n: 0 };
35670
+ acc.sum += r.consumption;
35671
+ acc.n += 1;
35672
+ byDay.set(k, acc);
35673
+ }
35674
+ const dayAvgs = [...byDay.values()].map((d) => d.sum / d.n);
35675
+ perDay = dayAvgs.length ? dayAvgs.reduce((x, y) => x + y, 0) / dayAvgs.length : 0;
35676
+ } else {
35677
+ perDay = total / dayCount;
35678
+ }
35662
35679
  const kpis = [
35663
35680
  { value: `${fmt2(summaryValue)} ${unit}`, label: `${this.domainConfig.summaryLabel} (${unit})` },
35664
- { value: fmt2(total / dayCount), label: `M\xE9dia por Dia (${unit})` }
35681
+ { value: fmt2(perDay), label: `M\xE9dia por Dia (${unit})` }
35665
35682
  ];
35666
- if (isHourly) {
35683
+ if (isHourly && !isTemperature) {
35667
35684
  kpis.push({ value: fmt2(rows.length ? total / rows.length : 0), label: `M\xE9dia por Hora (${unit})` });
35668
35685
  }
35669
35686
  kpis.push(
@@ -79676,6 +79693,8 @@ function renderModal3(container, state6, modalId, error) {
79676
79693
  showMaximize: true,
79677
79694
  showClose: true,
79678
79695
  primaryColor: state6.accent,
79696
+ solid: true,
79697
+ // padrão dos relatórios: cor do dashboard também no tema light
79679
79698
  borderRadius: "10px 10px 0 0",
79680
79699
  draggable: false
79681
79700
  })}
@@ -80014,12 +80033,19 @@ function drawChart2(modalId, state6) {
80014
80033
  ctx.textAlign = "center";
80015
80034
  const numLabels = Math.min(8, chartData.length);
80016
80035
  const labelInterval = Math.max(1, Math.floor(chartData.length / numLabels));
80036
+ const xSpan = chartData.length > 1 ? chartData[chartData.length - 1].x - chartData[0].x : 0;
80037
+ const multiDay = state6.granularity === "hour" && xSpan > 24 * 60 * 60 * 1e3;
80017
80038
  for (let i = 0; i < chartData.length; i += labelInterval) {
80018
80039
  const point = chartData[i];
80019
80040
  const date = new Date(point.x);
80020
80041
  let label;
80042
+ let subLabel = "";
80021
80043
  if (state6.granularity === "hour") {
80022
80044
  label = date.toLocaleTimeString(state6.locale, { hour: "2-digit", minute: "2-digit" });
80045
+ if (multiDay) {
80046
+ subLabel = label;
80047
+ label = date.toLocaleDateString(state6.locale, { day: "2-digit", month: "2-digit" });
80048
+ }
80023
80049
  } else {
80024
80050
  label = date.toLocaleDateString(state6.locale, { day: "2-digit", month: "2-digit" });
80025
80051
  }
@@ -80031,6 +80057,7 @@ function drawChart2(modalId, state6) {
80031
80057
  ctx.stroke();
80032
80058
  ctx.fillStyle = colors2.textMuted;
80033
80059
  ctx.fillText(label, point.screenX, height - paddingBottom + 18);
80060
+ if (subLabel) ctx.fillText(subLabel, point.screenX, height - paddingBottom + 31);
80034
80061
  }
80035
80062
  ctx.strokeStyle = colors2.border;
80036
80063
  ctx.lineWidth = 1;
@@ -80321,6 +80348,21 @@ function mountChrome(container, state6, modalId) {
80321
80348
  }
80322
80349
 
80323
80350
  // src/components/temperature/TemperatureComparisonModal.ts
80351
+ var _cmpFooters = /* @__PURE__ */ new Map();
80352
+ function resolveAccent2(palette) {
80353
+ const fallback = "#3e1a7d";
80354
+ const src = palette ?? (typeof window !== "undefined" ? window.MyIOUtils?.theme : void 0);
80355
+ if (!src) return fallback;
80356
+ const vars = typeof src.cssVars === "function" ? src.cssVars() : src;
80357
+ const accent = src.accent || vars?.["--myio-brand-700"];
80358
+ return typeof accent === "string" && accent ? accent : fallback;
80359
+ }
80360
+ function resolveCustomerName2(explicit) {
80361
+ if (explicit) return explicit;
80362
+ if (typeof window === "undefined") return "";
80363
+ const w = window;
80364
+ return w.MyIOOrchestrator?.customerName || w.MyIOUtils?.customerName || "";
80365
+ }
80324
80366
  async function openTemperatureComparisonModal(params) {
80325
80367
  const modalId = `myio-temp-comparison-modal-${Date.now()}`;
80326
80368
  const defaultDateRange = getTodaySoFar();
@@ -80342,7 +80384,9 @@ async function openTemperatureComparisonModal(params) {
80342
80384
  // All periods selected by default
80343
80385
  temperatureMin: params.temperatureMin ?? null,
80344
80386
  temperatureMax: params.temperatureMax ?? null,
80345
- dataFetcher: params.dataFetcher ?? null
80387
+ dataFetcher: params.dataFetcher ?? null,
80388
+ customerName: resolveCustomerName2(params.customerName),
80389
+ accent: resolveAccent2(params.palette)
80346
80390
  };
80347
80391
  const savedGranularity = localStorage.getItem("myio-temp-comparison-granularity");
80348
80392
  const savedTheme = localStorage.getItem("myio-temp-comparison-theme");
@@ -80358,6 +80402,8 @@ async function openTemperatureComparisonModal(params) {
80358
80402
  await setupEventListeners4(modalContainer, state6, modalId, params.onClose);
80359
80403
  return {
80360
80404
  destroy: () => {
80405
+ _cmpFooters.get(modalId)?.destroy();
80406
+ _cmpFooters.delete(modalId);
80361
80407
  modalContainer.remove();
80362
80408
  params.onClose?.();
80363
80409
  },
@@ -80384,7 +80430,9 @@ async function fetchAllDevicesData(state6) {
80384
80430
  try {
80385
80431
  const raw = state6.dataFetcher ? await state6.dataFetcher(device, state6.startTs, state6.endTs) : await fetchTemperatureData(state6.token, deviceId, state6.startTs, state6.endTs);
80386
80432
  const offset = Number(device.temperatureOffset) || 0;
80387
- const data = offset ? raw.map((p) => ({ ...p, value: Number(p.value) + offset })) : raw;
80433
+ const data = raw.map((p) => ({ ...p, value: Number(p.value) + offset })).filter(
80434
+ (p) => Number.isFinite(p.value) && p.value >= state6.clampRange.min && p.value <= state6.clampRange.max
80435
+ );
80388
80436
  const stats = calculateStats(data, state6.clampRange);
80389
80437
  return {
80390
80438
  device,
@@ -80481,7 +80529,7 @@ function renderModal4(container, state6, modalId) {
80481
80529
  <!-- Header - MyIO Premium Style -->
80482
80530
  <div style="
80483
80531
  padding: 4px 8px; display: flex; align-items: center; justify-content: space-between;
80484
- background: #3e1a7d; color: white; border-radius: ${isMaximized ? "0" : "10px 10px 0 0"};
80532
+ background: ${state6.accent}; color: white; border-radius: ${isMaximized ? "0" : "10px 10px 0 0"};
80485
80533
  min-height: 20px;
80486
80534
  ">
80487
80535
  <h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">
@@ -80563,7 +80611,7 @@ function renderModal4(container, state6, modalId) {
80563
80611
  name="${modalId}-period"
80564
80612
  value="${period.id}"
80565
80613
  ${state6.selectedPeriods.includes(period.id) ? "checked" : ""}
80566
- style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">
80614
+ style="width: 16px; height: 16px; cursor: pointer; accent-color: ${state6.accent};">
80567
80615
  ${period.label}
80568
80616
  </label>
80569
80617
  `).join("")}
@@ -80596,7 +80644,7 @@ function renderModal4(container, state6, modalId) {
80596
80644
  </div>
80597
80645
  <!-- Query Button -->
80598
80646
  <button id="${modalId}-query" style="
80599
- background: #3e1a7d; color: white; border: none;
80647
+ background: ${state6.accent}; color: white; border: none;
80600
80648
  padding: 8px 16px; border-radius: 6px; cursor: pointer;
80601
80649
  font-size: 14px; font-weight: 500; height: 38px;
80602
80650
  display: flex; align-items: center; gap: 8px;
@@ -80640,33 +80688,15 @@ function renderModal4(container, state6, modalId) {
80640
80688
  ${statsHTML}
80641
80689
  </div>
80642
80690
 
80643
- <!-- Actions -->
80644
- <div style="display: flex; justify-content: flex-end; gap: 12px;">
80645
- <button id="${modalId}-export" style="
80646
- background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
80647
- color: ${colors2.text}; border: 1px solid ${colors2.border};
80648
- padding: 8px 16px; border-radius: 6px; cursor: pointer;
80649
- font-size: 14px; display: flex; align-items: center; gap: 8px;
80650
- font-family: 'Nunito', system-ui, sans-serif;
80651
- " ${state6.deviceData.every((dd) => dd.data.length === 0) ? "disabled" : ""}>
80652
- \u{1F4E5} Exportar CSV
80653
- </button>
80654
- <button id="${modalId}-close-btn" style="
80655
- background: #3e1a7d; color: white; border: none;
80656
- padding: 8px 16px; border-radius: 6px; cursor: pointer;
80657
- font-size: 14px; font-weight: 500;
80658
- font-family: 'Nunito', system-ui, sans-serif;
80659
- ">
80660
- Fechar
80661
- </button>
80662
- </div>
80663
80691
  </div><!-- End Body -->
80692
+ <!-- Footer premium (customer \xB7 rel\xF3gio \xB7 vers\xE3o | Powered by MYIO | CSV) \u2014 padr\xE3o dos relat\xF3rios -->
80693
+ <div id="${modalId}-footer-slot"></div>
80664
80694
  </div>
80665
80695
  </div>
80666
80696
  <style>
80667
80697
  @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
80668
80698
  #${modalId} select:focus, #${modalId} input:focus {
80669
- outline: 2px solid #3e1a7d;
80699
+ outline: 2px solid ${state6.accent};
80670
80700
  outline-offset: 2px;
80671
80701
  }
80672
80702
  #${modalId} button:hover:not(:disabled) {
@@ -80682,6 +80712,7 @@ function renderModal4(container, state6, modalId) {
80682
80712
  }
80683
80713
  </style>
80684
80714
  `;
80715
+ mountCmpFooter(container, state6, modalId);
80685
80716
  }
80686
80717
  function drawComparisonChart2(modalId, state6) {
80687
80718
  const chartContainer = document.getElementById(`${modalId}-chart`);
@@ -80888,12 +80919,19 @@ function drawComparisonChart2(modalId, state6) {
80888
80919
  const xAxisPoints = processedData[0]?.points || [];
80889
80920
  const numLabels = Math.min(8, xAxisPoints.length);
80890
80921
  const labelInterval = Math.max(1, Math.floor(xAxisPoints.length / numLabels));
80922
+ const xSpan = xAxisPoints.length > 1 ? xAxisPoints[xAxisPoints.length - 1].x - xAxisPoints[0].x : 0;
80923
+ const multiDay = state6.granularity === "hour" && xSpan > 24 * 60 * 60 * 1e3;
80891
80924
  for (let i = 0; i < xAxisPoints.length; i += labelInterval) {
80892
80925
  const point = xAxisPoints[i];
80893
80926
  const date = new Date(point.x);
80894
80927
  let label;
80928
+ let subLabel = "";
80895
80929
  if (state6.granularity === "hour") {
80896
80930
  label = date.toLocaleTimeString(state6.locale, { hour: "2-digit", minute: "2-digit" });
80931
+ if (multiDay) {
80932
+ subLabel = label;
80933
+ label = date.toLocaleDateString(state6.locale, { day: "2-digit", month: "2-digit" });
80934
+ }
80897
80935
  } else {
80898
80936
  label = date.toLocaleDateString(state6.locale, { day: "2-digit", month: "2-digit" });
80899
80937
  }
@@ -80905,6 +80943,7 @@ function drawComparisonChart2(modalId, state6) {
80905
80943
  ctx.stroke();
80906
80944
  ctx.fillStyle = colors2.textMuted;
80907
80945
  ctx.fillText(label, point.screenX, height - paddingBottom + 18);
80946
+ if (subLabel) ctx.fillText(subLabel, point.screenX, height - paddingBottom + 31);
80908
80947
  }
80909
80948
  ctx.strokeStyle = colors2.border;
80910
80949
  ctx.lineWidth = 1;
@@ -81014,6 +81053,8 @@ function setupComparisonChartTooltip2(canvas, container, chartData, state6, colo
81014
81053
  }
81015
81054
  async function setupEventListeners4(container, state6, modalId, onClose) {
81016
81055
  const closeModal2 = () => {
81056
+ _cmpFooters.get(modalId)?.destroy();
81057
+ _cmpFooters.delete(modalId);
81017
81058
  container.remove();
81018
81059
  onClose?.();
81019
81060
  };
@@ -81135,10 +81176,30 @@ async function setupEventListeners4(container, state6, modalId, onClose) {
81135
81176
  drawComparisonChart2(modalId, state6);
81136
81177
  await setupEventListeners4(container, state6, modalId, onClose);
81137
81178
  });
81138
- document.getElementById(`${modalId}-export`)?.addEventListener("click", () => {
81139
- if (state6.deviceData.every((dd) => dd.data.length === 0)) return;
81140
- exportComparisonCSV2(state6);
81179
+ }
81180
+ function mountCmpFooter(container, state6, modalId) {
81181
+ _cmpFooters.get(modalId)?.destroy();
81182
+ _cmpFooters.delete(modalId);
81183
+ const slot = container.querySelector(`#${modalId}-footer-slot`);
81184
+ if (!slot) return;
81185
+ const lib = typeof window !== "undefined" ? window.MyIOLibrary : void 0;
81186
+ const footer = createModalFooter({
81187
+ customerName: state6.customerName,
81188
+ libVersion: lib?.version ? { current: lib.version } : false,
81189
+ themeMode: state6.theme === "dark" ? "dark" : "light",
81190
+ exports: {
81191
+ csv: {
81192
+ onClick: () => {
81193
+ if (state6.deviceData.every((dd) => dd.data.length === 0)) return;
81194
+ exportComparisonCSV2(state6);
81195
+ },
81196
+ disabled: state6.deviceData.every((dd) => dd.data.length === 0),
81197
+ tooltipText: "Exporta em CSV as leituras de cada sensor (offset aplicado)"
81198
+ }
81199
+ }
81141
81200
  });
81201
+ slot.appendChild(footer.element);
81202
+ _cmpFooters.set(modalId, footer);
81142
81203
  }
81143
81204
  function exportComparisonCSV2(state6) {
81144
81205
  const startDateStr = new Date(state6.startTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
@@ -93944,7 +94005,7 @@ function fmtPct3(pct) {
93944
94005
  if (pct == null || !isFinite(pct)) return "";
93945
94006
  return `${pct.toLocaleString("pt-BR", { minimumFractionDigits: 1, maximumFractionDigits: 1 })}%`;
93946
94007
  }
93947
- function resolveAccent2(explicit, doc) {
94008
+ function resolveAccent3(explicit, doc) {
93948
94009
  if (explicit) return explicit;
93949
94010
  try {
93950
94011
  const v = getComputedStyle(doc.documentElement).getPropertyValue("--myio-brand-700").trim();
@@ -93990,7 +94051,7 @@ function createGoalsBarTooltip(opts) {
93990
94051
  injectStyles12(doc);
93991
94052
  const el2 = doc.createElement("div");
93992
94053
  el2.className = "myio-gbt";
93993
- el2.style.setProperty("--myio-gbt-accent", resolveAccent2(opts?.accentColor, doc));
94054
+ el2.style.setProperty("--myio-gbt-accent", resolveAccent3(opts?.accentColor, doc));
93994
94055
  doc.body.appendChild(el2);
93995
94056
  let pinned = false;
93996
94057
  let hideTimer = null;
@@ -94048,7 +94109,7 @@ function createGoalsBarTooltip(opts) {
94048
94109
  });
94049
94110
  }
94050
94111
  function render3(data) {
94051
- const accent = resolveAccent2(data.accentColor ?? opts?.accentColor, doc);
94112
+ const accent = resolveAccent3(data.accentColor ?? opts?.accentColor, doc);
94052
94113
  el2.style.setProperty("--myio-gbt-accent", accent);
94053
94114
  const rowsHtml = data.rows && data.rows.length > 0 ? data.rows.map((r) => renderRow(r, 0)).join("") : `<div class="myio-gbt__empty">Sem dados para este per\xEDodo</div>`;
94054
94115
  el2.innerHTML = `
@@ -132804,19 +132865,19 @@ var TelemetryGridShoppingView = class {
132804
132865
  this.maximizeBtnEl?.addEventListener("click", this.toggleMaximize.bind(this));
132805
132866
  const label = this.params.labelWidget || CONTEXT_CONFIG2[this.params.context]?.headerLabel || "Dispositivos";
132806
132867
  const unit = DOMAIN_CONFIG8[this.params.domain]?.unit || "";
132807
- const resolveCustomerName2 = () => {
132868
+ const resolveCustomerName3 = () => {
132808
132869
  const devs = this.controller.getFilteredDevices();
132809
132870
  const name = devs.find((d) => d.customerName)?.customerName;
132810
132871
  return name ? String(name).trim() : null;
132811
132872
  };
132812
132873
  this.root.querySelector("#btnExportPdf")?.addEventListener("click", () => {
132813
- exportGridPdf(this.controller.getFilteredDevices(), label, unit, null, resolveCustomerName2());
132874
+ exportGridPdf(this.controller.getFilteredDevices(), label, unit, null, resolveCustomerName3());
132814
132875
  });
132815
132876
  this.root.querySelector("#btnExportXls")?.addEventListener("click", () => {
132816
- exportGridXls(this.controller.getFilteredDevices(), label, unit, null, resolveCustomerName2());
132877
+ exportGridXls(this.controller.getFilteredDevices(), label, unit, null, resolveCustomerName3());
132817
132878
  });
132818
132879
  this.root.querySelector("#btnExportCsv")?.addEventListener("click", () => {
132819
- exportGridCsv(this.controller.getFilteredDevices(), label, unit, null, resolveCustomerName2());
132880
+ exportGridCsv(this.controller.getFilteredDevices(), label, unit, null, resolveCustomerName3());
132820
132881
  });
132821
132882
  this.bindFilterModalEvents();
132822
132883
  }
package/dist/index.d.cts CHANGED
@@ -9180,6 +9180,12 @@ interface TemperatureComparisonModalParams {
9180
9180
  temperatureMax?: number;
9181
9181
  /** Custom data source per device (default: ThingsBoard timeseries via token) */
9182
9182
  dataFetcher?: TemperatureComparisonDataFetcher;
9183
+ /** Customer/shopping — footer premium (padrão AllReport/DeviceReport) */
9184
+ customerName?: string;
9185
+ /** Paleta do dashboard (createMyIOTheme ou mapa de CSS vars) — cor do header/botões */
9186
+ palette?: {
9187
+ cssVars(): Record<string, string>;
9188
+ } | Record<string, string>;
9183
9189
  }
9184
9190
  interface TemperatureComparisonModalInstance {
9185
9191
  /** Destroys the modal */
@@ -11118,6 +11124,11 @@ interface ModalHeaderOptions {
11118
11124
  showClose?: boolean;
11119
11125
  /** Custom primary color (default: #3e1a7d - MyIO Purple) */
11120
11126
  primaryColor?: string;
11127
+ /**
11128
+ * generateInlineHTML: header SEMPRE na cor primária com texto branco, mesmo no tema
11129
+ * light (padrão dos relatórios premium). O botão de tema segue refletindo `theme`.
11130
+ */
11131
+ solid?: boolean;
11121
11132
  /** Border radius when not maximized */
11122
11133
  borderRadius?: string;
11123
11134
  /** Show export button */
package/dist/index.d.ts CHANGED
@@ -9180,6 +9180,12 @@ interface TemperatureComparisonModalParams {
9180
9180
  temperatureMax?: number;
9181
9181
  /** Custom data source per device (default: ThingsBoard timeseries via token) */
9182
9182
  dataFetcher?: TemperatureComparisonDataFetcher;
9183
+ /** Customer/shopping — footer premium (padrão AllReport/DeviceReport) */
9184
+ customerName?: string;
9185
+ /** Paleta do dashboard (createMyIOTheme ou mapa de CSS vars) — cor do header/botões */
9186
+ palette?: {
9187
+ cssVars(): Record<string, string>;
9188
+ } | Record<string, string>;
9183
9189
  }
9184
9190
  interface TemperatureComparisonModalInstance {
9185
9191
  /** Destroys the modal */
@@ -11118,6 +11124,11 @@ interface ModalHeaderOptions {
11118
11124
  showClose?: boolean;
11119
11125
  /** Custom primary color (default: #3e1a7d - MyIO Purple) */
11120
11126
  primaryColor?: string;
11127
+ /**
11128
+ * generateInlineHTML: header SEMPRE na cor primária com texto branco, mesmo no tema
11129
+ * light (padrão dos relatórios premium). O botão de tema segue refletindo `theme`.
11130
+ */
11131
+ solid?: boolean;
11121
11132
  /** Border radius when not maximized */
11122
11133
  borderRadius?: string;
11123
11134
  /** Show export button */