myio-js-library 0.1.504 → 0.1.506

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
@@ -1164,7 +1164,7 @@ module.exports = __toCommonJS(index_exports);
1164
1164
  // package.json
1165
1165
  var package_default = {
1166
1166
  name: "myio-js-library",
1167
- version: "0.1.504",
1167
+ version: "0.1.506",
1168
1168
  description: "A clean, standalone JS SDK for MYIO projects",
1169
1169
  license: "MIT",
1170
1170
  repository: "github:gh-myio/myio-js-library",
@@ -14674,8 +14674,10 @@ function renderCardComponentV5({
14674
14674
  temperature,
14675
14675
  temperatureMin,
14676
14676
  temperatureMax,
14677
- temperatureStatus
14677
+ temperatureStatus,
14678
14678
  // 'ok' | 'above' | 'below' | undefined
14679
+ // Per-device exclude_groups_totals attribute (SERVER_SCOPE) — drives the orange marker
14680
+ excludeGroupsTotals
14679
14681
  } = entityObject;
14680
14682
  const MyIOToast2 = (function() {
14681
14683
  let toastContainer = null;
@@ -14904,6 +14906,20 @@ function renderCardComponentV5({
14904
14906
  height: 100%;
14905
14907
  }
14906
14908
 
14909
+ /* Subtle orange marker \u2014 device flagged in exclude_groups_totals */
14910
+ .myio-enhanced-card-container-v5.myio-card-excluded::after {
14911
+ content: '';
14912
+ position: absolute;
14913
+ left: 12px;
14914
+ right: 12px;
14915
+ bottom: 3px;
14916
+ height: 3px;
14917
+ border-radius: 2px;
14918
+ background: linear-gradient(90deg, rgba(245, 158, 11, 0), #f59e0b 50%, rgba(245, 158, 11, 0));
14919
+ pointer-events: none;
14920
+ z-index: 5;
14921
+ }
14922
+
14907
14923
  .myio-enhanced-card-container-v5 .myio-draggable-card {
14908
14924
  width: 100%;
14909
14925
  border-radius: 10px;
@@ -15196,6 +15212,12 @@ function renderCardComponentV5({
15196
15212
  `;
15197
15213
  container.innerHTML = cardHTML;
15198
15214
  const enhancedCardElement = container.querySelector(".device-card-centered");
15215
+ try {
15216
+ const _excl = typeof excludeGroupsTotals === "string" ? JSON.parse(excludeGroupsTotals) : excludeGroupsTotals;
15217
+ const _isExcluded = !!(_excl && _excl.enabled === true && (_excl.groups && Object.values(_excl.groups).some((v) => v === true) || Array.isArray(_excl.excludedGroups) && _excl.excludedGroups.length > 0));
15218
+ if (_isExcluded) container.classList.add("myio-card-excluded");
15219
+ } catch (e) {
15220
+ }
15199
15221
  if (!document.getElementById("myio-enhanced-card-layout-styles-v5")) {
15200
15222
  const layoutStyle = document.createElement("style");
15201
15223
  layoutStyle.id = "myio-enhanced-card-layout-styles-v5";
@@ -16035,8 +16057,8 @@ var INFO_TOOLTIP_CSS = `
16035
16057
  border: 1px solid #e2e8f0;
16036
16058
  border-radius: 12px;
16037
16059
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.12), 0 2px 10px rgba(0, 0, 0, 0.08);
16038
- min-width: 320px;
16039
- max-width: 400px;
16060
+ width: 395px;
16061
+ max-width: 90vw;
16040
16062
  font-size: 12px;
16041
16063
  color: #1e293b;
16042
16064
  overflow: hidden;
@@ -27013,28 +27035,22 @@ function validateOptions(options) {
27013
27035
  }
27014
27036
  }
27015
27037
  function normalizeToSaoPauloISO(dateLike, endOfDay = false) {
27016
- let date;
27038
+ let ymd;
27017
27039
  if (typeof dateLike === "string") {
27018
- if (/^\d{4}-\d{2}-\d{2}$/.test(dateLike)) {
27019
- date = /* @__PURE__ */ new Date(dateLike + "T00:00:00-03:00");
27020
- } else {
27021
- date = new Date(dateLike);
27022
- }
27040
+ ymd = dateLike.split("T")[0];
27023
27041
  } else {
27024
- date = new Date(dateLike);
27025
- }
27026
- const saoPauloOffset = -3 * 60;
27027
- const localOffset = date.getTimezoneOffset();
27028
- const offsetDiff = saoPauloOffset - localOffset;
27029
- if (offsetDiff !== 0) {
27030
- date.setMinutes(date.getMinutes() + offsetDiff);
27042
+ const SP_OFFSET_MS = 3 * 60 * 60 * 1e3;
27043
+ const d = new Date(dateLike.getTime() - SP_OFFSET_MS);
27044
+ const y = d.getUTCFullYear();
27045
+ const mo = String(d.getUTCMonth() + 1).padStart(2, "0");
27046
+ const da = String(d.getUTCDate()).padStart(2, "0");
27047
+ ymd = `${y}-${mo}-${da}`;
27031
27048
  }
27032
- if (endOfDay) {
27033
- date.setHours(23, 59, 59, 999);
27034
- } else {
27035
- date.setHours(0, 0, 0, 0);
27049
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(ymd)) {
27050
+ throw new Error(`normalizeToSaoPauloISO: formato de data inv\xE1lido: "${String(dateLike)}"`);
27036
27051
  }
27037
- return date.toISOString().replace("Z", "-03:00");
27052
+ const time = endOfDay ? "T23:59:59.000" : "T00:00:00.000";
27053
+ return `${ymd}${time}-03:00`;
27038
27054
  }
27039
27055
  function resolveDeviceAttributes(attributes) {
27040
27056
  return {
@@ -31491,10 +31507,12 @@ var EnergyModal = class {
31491
31507
  return;
31492
31508
  }
31493
31509
  this.view.showLoadingState();
31510
+ const normalizedStart = normalizeToSaoPauloISO(startISO, false);
31511
+ const normalizedEnd = normalizeToSaoPauloISO(endISO, true);
31494
31512
  const energyData = await this.dataFetcher.fetchEnergyData({
31495
31513
  ingestionId: this.context.resolved.ingestionId,
31496
- startISO,
31497
- endISO,
31514
+ startISO: normalizedStart,
31515
+ endISO: normalizedEnd,
31498
31516
  granularity: this.params.granularity || "1d",
31499
31517
  readingType: this.params.readingType
31500
31518
  });
@@ -32941,6 +32959,235 @@ function attachFilterOrderingModal(props) {
32941
32959
  };
32942
32960
  }
32943
32961
 
32962
+ // src/components/telemetry-grid-shopping/export.ts
32963
+ var import_jspdf = require("jspdf");
32964
+ function makeCols(unit) {
32965
+ return [
32966
+ { key: "idx", label: "#", pdfW: 10 },
32967
+ { key: "nome", label: "Nome", pdfW: 100 },
32968
+ { key: "identificador", label: "Identificador", pdfW: 60 },
32969
+ { key: "consumo", label: unit ? `Consumo (${unit})` : "Consumo", pdfW: 50 },
32970
+ { key: "perc", label: "%", pdfW: 20 }
32971
+ ];
32972
+ }
32973
+ function buildRow(d, idx) {
32974
+ const fmtVal = () => {
32975
+ if (d.val === null || d.val === void 0) return "\u2014";
32976
+ return Number(d.val).toLocaleString("pt-BR", { maximumFractionDigits: 3, useGrouping: false });
32977
+ };
32978
+ const pd = resolvePercentDecimals();
32979
+ return {
32980
+ idx: String(idx + 1),
32981
+ nome: d.labelOrName || d.name || "\u2014",
32982
+ identificador: d.deviceIdentifier || "\u2014",
32983
+ consumo: fmtVal(),
32984
+ perc: d.perc !== void 0 ? `${d.perc.toFixed(pd).replace(".", ",")}%` : "\u2014"
32985
+ };
32986
+ }
32987
+ function fmtPeriod(period) {
32988
+ if (!period?.startISO) return "";
32989
+ const fmt2 = (iso) => new Date(iso).toLocaleDateString("pt-BR");
32990
+ return period.endISO ? `${fmt2(period.startISO)} \u2014 ${fmt2(period.endISO)}` : fmt2(period.startISO);
32991
+ }
32992
+ function slugify(s) {
32993
+ return s.normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^\w]+/g, "-").toLowerCase().replace(/^-+|-+$/g, "").slice(0, 40);
32994
+ }
32995
+ function datestamp() {
32996
+ const d = /* @__PURE__ */ new Date();
32997
+ const p = (n) => String(n).padStart(2, "0");
32998
+ return `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}-${p(d.getHours())}${p(d.getMinutes())}`;
32999
+ }
33000
+ function buildFilenameBase(label, customerName) {
33001
+ const parts = [];
33002
+ const cSlug = customerName ? slugify(customerName) : "";
33003
+ if (cSlug) parts.push(cSlug);
33004
+ parts.push(slugify(label));
33005
+ parts.push(datestamp());
33006
+ return parts.join("-");
33007
+ }
33008
+ function triggerDownload(blob, filename) {
33009
+ const url = URL.createObjectURL(blob);
33010
+ const a = document.createElement("a");
33011
+ a.href = url;
33012
+ a.download = filename;
33013
+ a.click();
33014
+ setTimeout(() => URL.revokeObjectURL(url), 1e3);
33015
+ }
33016
+ function escXml(s) {
33017
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
33018
+ }
33019
+ function truncate(s, max) {
33020
+ return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
33021
+ }
33022
+ function exportGridCsv(devices, label, unit, period, customerName) {
33023
+ const periodLabel = fmtPeriod(period);
33024
+ const metaRows = [
33025
+ `"${label}"`,
33026
+ customerName ? `"Cliente";"${customerName}"` : null,
33027
+ periodLabel ? `"Per\xEDodo";"${periodLabel}"` : null,
33028
+ `"Gerado em";"${(/* @__PURE__ */ new Date()).toLocaleString("pt-BR")}"`,
33029
+ ""
33030
+ ].filter((v) => v !== null);
33031
+ const cols = makeCols(unit);
33032
+ const header = cols.map((c) => `"${c.label}"`).join(";");
33033
+ const rows = devices.map((d, i) => {
33034
+ const r = buildRow(d, i);
33035
+ return cols.map((c) => `"${String(r[c.key]).replace(/"/g, '""')}"`).join(";");
33036
+ });
33037
+ const csv = "\uFEFF" + [...metaRows, header, ...rows].join("\r\n");
33038
+ triggerDownload(
33039
+ new Blob([csv], { type: "text/csv;charset=utf-8;" }),
33040
+ `${buildFilenameBase(label, customerName)}.csv`
33041
+ );
33042
+ }
33043
+ function exportGridXls(devices, label, unit, period, customerName) {
33044
+ const periodLabel = fmtPeriod(period);
33045
+ const cols = makeCols(unit);
33046
+ const span = cols.length - 1;
33047
+ const metaRow = (key, val) => `<Row><Cell ss:StyleID="m"><Data ss:Type="String">${escXml(key)}</Data></Cell><Cell ss:MergeAcross="${span - 1}"><Data ss:Type="String">${escXml(val)}</Data></Cell></Row>`;
33048
+ const headerCells = cols.map(
33049
+ (c) => `<Cell ss:StyleID="h"><Data ss:Type="String">${escXml(c.label)}</Data></Cell>`
33050
+ ).join("");
33051
+ const dataRows = devices.map((d, i) => {
33052
+ const r = buildRow(d, i);
33053
+ const cells = cols.map((c) => {
33054
+ const v = escXml(String(r[c.key]));
33055
+ return `<Cell><Data ss:Type="String">${v}</Data></Cell>`;
33056
+ }).join("");
33057
+ return `<Row>${cells}</Row>`;
33058
+ }).join("\n");
33059
+ const xml = `<?xml version="1.0" encoding="UTF-8"?>
33060
+ <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
33061
+ xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
33062
+ <Styles>
33063
+ <Style ss:ID="h">
33064
+ <Font ss:Bold="1" ss:Color="#FFFFFF"/>
33065
+ <Interior ss:Color="#3E1A7D" ss:Pattern="Solid"/>
33066
+ </Style>
33067
+ <Style ss:ID="m">
33068
+ <Font ss:Bold="1"/>
33069
+ <Interior ss:Color="#F0EDF9" ss:Pattern="Solid"/>
33070
+ </Style>
33071
+ </Styles>
33072
+ <Worksheet ss:Name="${escXml(label.slice(0, 31))}">
33073
+ <Table>
33074
+ ${metaRow("Relat\xF3rio", label)}
33075
+ ${customerName ? metaRow("Cliente", customerName) : ""}
33076
+ ${periodLabel ? metaRow("Per\xEDodo", periodLabel) : ""}
33077
+ ${metaRow("Gerado em", (/* @__PURE__ */ new Date()).toLocaleString("pt-BR"))}
33078
+ <Row/>
33079
+ <Row>${headerCells}</Row>
33080
+ ${dataRows}
33081
+ </Table>
33082
+ </Worksheet>
33083
+ </Workbook>`;
33084
+ triggerDownload(
33085
+ new Blob([xml], { type: "application/vnd.ms-excel;charset=utf-8;" }),
33086
+ `${buildFilenameBase(label, customerName)}.xls`
33087
+ );
33088
+ }
33089
+ function exportGridPdf(devices, label, unit, period, customerName) {
33090
+ const generatedAt = (/* @__PURE__ */ new Date()).toLocaleString("pt-BR");
33091
+ const doc = new import_jspdf.jsPDF({ orientation: "landscape", unit: "mm", format: "a4" });
33092
+ const PW = doc.internal.pageSize.getWidth();
33093
+ const PH = doc.internal.pageSize.getHeight();
33094
+ const MARGIN = 10;
33095
+ const HDR_H = 13;
33096
+ const FTR_H = 10;
33097
+ const ROW_H = 7;
33098
+ const HEAD_H = 8;
33099
+ const TABLE_Y = HDR_H + MARGIN;
33100
+ const MAX_Y = PH - FTR_H - MARGIN;
33101
+ const TABLE_W = PW - MARGIN * 2;
33102
+ const cols = makeCols(unit);
33103
+ const rawTotal = cols.reduce((s, c) => s + c.pdfW, 0);
33104
+ const scale = TABLE_W / rawTotal;
33105
+ const colWidths = cols.map((c) => c.pdfW * scale);
33106
+ const colX = (ci) => MARGIN + colWidths.slice(0, ci).reduce((s, w) => s + w, 0);
33107
+ function drawPageHeader(pageNo2) {
33108
+ doc.setFillColor(62, 26, 125);
33109
+ doc.rect(0, 0, PW, HDR_H, "F");
33110
+ doc.setTextColor(255, 255, 255);
33111
+ doc.setFont("helvetica", "bold");
33112
+ doc.setFontSize(10);
33113
+ const titleText = customerName ? `${customerName} \u2014 ${label}` : label;
33114
+ doc.text(titleText, MARGIN, HDR_H / 2 + 1.5);
33115
+ doc.setFont("helvetica", "normal");
33116
+ doc.setFontSize(8);
33117
+ const periodLabel = fmtPeriod(period);
33118
+ const periodPart = periodLabel ? `Per\xEDodo: ${periodLabel} \u2022 ` : "";
33119
+ const info = `${periodPart}Gerado em: ${generatedAt} \u2022 ${devices.length} dispositivo(s) \u2022 Unidade: ${unit} \u2022 P\xE1g. ${pageNo2}`;
33120
+ doc.text(info, PW - MARGIN, HDR_H / 2 + 1.5, { align: "right" });
33121
+ }
33122
+ function drawColumnHeaders(y) {
33123
+ doc.setFillColor(240, 237, 250);
33124
+ doc.rect(MARGIN, y, TABLE_W, HEAD_H, "F");
33125
+ doc.setTextColor(62, 26, 125);
33126
+ doc.setFont("helvetica", "bold");
33127
+ doc.setFontSize(7);
33128
+ cols.forEach((c, ci) => {
33129
+ const x = colX(ci) + 1.5;
33130
+ doc.text(c.label, x, y + HEAD_H / 2 + 2.5);
33131
+ });
33132
+ doc.setDrawColor(200, 195, 220);
33133
+ doc.setLineWidth(0.2);
33134
+ doc.rect(MARGIN, y, TABLE_W, HEAD_H);
33135
+ }
33136
+ function drawDataRow(r, y, even) {
33137
+ if (even) {
33138
+ doc.setFillColor(250, 249, 255);
33139
+ doc.rect(MARGIN, y, TABLE_W, ROW_H, "F");
33140
+ }
33141
+ doc.setTextColor(40, 40, 40);
33142
+ doc.setFont("helvetica", "normal");
33143
+ doc.setFontSize(6.5);
33144
+ cols.forEach((c, ci) => {
33145
+ const x = colX(ci) + 1.5;
33146
+ const maxChars = Math.floor(colWidths[ci] / 1.8);
33147
+ const text = truncate(String(r[c.key]), maxChars);
33148
+ doc.text(text, x, y + ROW_H / 2 + 2.2);
33149
+ });
33150
+ doc.setDrawColor(230, 228, 240);
33151
+ doc.setLineWidth(0.1);
33152
+ doc.line(MARGIN, y + ROW_H, MARGIN + TABLE_W, y + ROW_H);
33153
+ }
33154
+ function drawFooter2() {
33155
+ doc.setFillColor(250, 249, 255);
33156
+ doc.rect(0, PH - FTR_H, PW, FTR_H, "F");
33157
+ doc.setDrawColor(210, 205, 230);
33158
+ doc.setLineWidth(0.2);
33159
+ doc.line(MARGIN, PH - FTR_H + 0.5, PW - MARGIN, PH - FTR_H + 0.5);
33160
+ doc.setTextColor(120, 110, 150);
33161
+ doc.setFont("helvetica", "normal");
33162
+ doc.setFontSize(7);
33163
+ doc.text(
33164
+ `Gerado em ${generatedAt} \u2014 MyIO`,
33165
+ MARGIN,
33166
+ PH - FTR_H + 6
33167
+ );
33168
+ }
33169
+ let pageNo = 1;
33170
+ let currentY = TABLE_Y;
33171
+ drawPageHeader(pageNo);
33172
+ drawColumnHeaders(currentY);
33173
+ currentY += HEAD_H;
33174
+ devices.forEach((d, i) => {
33175
+ if (currentY + ROW_H > MAX_Y) {
33176
+ drawFooter2();
33177
+ doc.addPage();
33178
+ pageNo++;
33179
+ currentY = TABLE_Y;
33180
+ drawPageHeader(pageNo);
33181
+ drawColumnHeaders(currentY);
33182
+ currentY += HEAD_H;
33183
+ }
33184
+ drawDataRow(buildRow(d, i), currentY, i % 2 === 0);
33185
+ currentY += ROW_H;
33186
+ });
33187
+ drawFooter2();
33188
+ doc.save(`${buildFilenameBase(label, customerName)}.pdf`);
33189
+ }
33190
+
32944
33191
  // src/components/premium-modals/report-all/AllReportModal.ts
32945
33192
  var DOMAIN_CONFIG2 = {
32946
33193
  energy: {
@@ -33011,6 +33258,8 @@ var AllReportModal = class {
33011
33258
  considerExclusion = true;
33012
33259
  // Raw API response kept so the exclusion toggle can re-map without a new fetch.
33013
33260
  lastApiResponse = null;
33261
+ // Search period of the last load — fed to the PDF/XLS export headers.
33262
+ exportPeriod = null;
33014
33263
  // Cleanup for the InfoTooltip attached to the exclusion-flag info icon.
33015
33264
  exclusionTooltipCleanup = null;
33016
33265
  // Debug logging helper
@@ -33129,6 +33378,12 @@ var AllReportModal = class {
33129
33378
  <button id="export-btn" class="myio-btn myio-btn-secondary" disabled>
33130
33379
  Exportar CSV
33131
33380
  </button>
33381
+ <button id="export-pdf-btn" class="myio-btn myio-btn-secondary" disabled>
33382
+ Exportar PDF
33383
+ </button>
33384
+ <button id="export-xls-btn" class="myio-btn myio-btn-secondary" disabled>
33385
+ Exportar XLS
33386
+ </button>
33132
33387
  <button id="filter-btn" class="myio-btn myio-btn-secondary" style="background: var(--myio-brand-700); color: white;">
33133
33388
  \u{1F50D} Filtros & Ordena\xE7\xE3o
33134
33389
  </button>
@@ -33180,6 +33435,8 @@ var AllReportModal = class {
33180
33435
  const searchInput = document.getElementById("search-input");
33181
33436
  loadBtn?.addEventListener("click", () => this.loadData());
33182
33437
  exportBtn?.addEventListener("click", () => this.exportCSV());
33438
+ document.getElementById("export-pdf-btn")?.addEventListener("click", () => this.exportPDF());
33439
+ document.getElementById("export-xls-btn")?.addEventListener("click", () => this.exportXLS());
33183
33440
  filterBtn?.addEventListener("click", () => this.openFilterModal());
33184
33441
  const granToggle = document.getElementById("granularity-toggle");
33185
33442
  granToggle?.addEventListener("click", (e) => {
@@ -33250,6 +33507,7 @@ var AllReportModal = class {
33250
33507
  try {
33251
33508
  const { startISO, endISO } = this.dateRangePicker.getDates();
33252
33509
  this.debugLog("\u{1F4C5} Date range selected", { startISO, endISO });
33510
+ this.exportPeriod = { startISO, endISO };
33253
33511
  if (!startISO || !endISO) {
33254
33512
  this.showError("Selecione um per\xEDodo v\xE1lido");
33255
33513
  return;
@@ -33277,6 +33535,10 @@ var AllReportModal = class {
33277
33535
  this.renderSummary();
33278
33536
  this.renderTable();
33279
33537
  exportBtn.disabled = false;
33538
+ const pdfBtn = document.getElementById("export-pdf-btn");
33539
+ const xlsBtn = document.getElementById("export-xls-btn");
33540
+ if (pdfBtn) pdfBtn.disabled = false;
33541
+ if (xlsBtn) xlsBtn.disabled = false;
33280
33542
  this.debugLog("\u{1F389} Load process completed successfully");
33281
33543
  this.emit("loaded", {
33282
33544
  date: { start: startDate, end: endDate },
@@ -33569,6 +33831,41 @@ var AllReportModal = class {
33569
33831
  const csvContent = toCsv(csvData);
33570
33832
  this.downloadCSV(csvContent, `relatorio-geral-lojas-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.csv`);
33571
33833
  }
33834
+ // Maps the report rows to the TelemetryDevice shape consumed by the shared
33835
+ // TELEMETRY grid exporters (only labelOrName/name/deviceIdentifier/val/perc are read).
33836
+ buildExportDevices() {
33837
+ const sorted = [...this.data].sort((a, b) => b.consumption - a.consumption);
33838
+ const total = sorted.reduce((s, r) => s + (r.consumption || 0), 0);
33839
+ return sorted.map((r) => ({
33840
+ labelOrName: r.name,
33841
+ name: r.name,
33842
+ deviceIdentifier: r.identifier,
33843
+ val: r.consumption,
33844
+ perc: total > 0 ? r.consumption / total * 100 : 0
33845
+ }));
33846
+ }
33847
+ // PDF export — same premium layout as the TELEMETRY grid export.
33848
+ exportPDF() {
33849
+ if (!this.data.length) return;
33850
+ exportGridPdf(
33851
+ this.buildExportDevices(),
33852
+ this.resolveTitle(),
33853
+ this.domainConfig.unit,
33854
+ this.exportPeriod,
33855
+ null
33856
+ );
33857
+ }
33858
+ // XLS export (XML Spreadsheet) — same as the TELEMETRY grid export.
33859
+ exportXLS() {
33860
+ if (!this.data.length) return;
33861
+ exportGridXls(
33862
+ this.buildExportDevices(),
33863
+ this.resolveTitle(),
33864
+ this.domainConfig.unit,
33865
+ this.exportPeriod,
33866
+ null
33867
+ );
33868
+ }
33572
33869
  async fetchCustomerTotals(startISO, endISO) {
33573
33870
  if (this.params.fetcher) {
33574
33871
  const token2 = this.params.api.ingestionToken || await this.authClient.getBearer();
@@ -69988,8 +70285,7 @@ var ENERGY_SUMMARY_TOOLTIP_CSS = `
69988
70285
  border: 1px solid #e2e8f0;
69989
70286
  border-radius: 12px;
69990
70287
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15), 0 2px 10px rgba(0, 0, 0, 0.08);
69991
- min-width: 380px;
69992
- width: max-content;
70288
+ width: 395px;
69993
70289
  max-width: 90vw;
69994
70290
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
69995
70291
  font-size: 12px;
@@ -71819,7 +72115,7 @@ var EnergySummaryTooltip = {
71819
72115
  };
71820
72116
  summary.byCategory = [entrada, lojas, areaComum];
71821
72117
  summary.totalDevices = entrada.deviceCount + lojas.deviceCount + areaComumDeviceCount;
71822
- summary.totalConsumption = state6.grandTotal || entrada.consumption;
72118
+ summary.totalConsumption = entrada.consumption || state6.grandTotal || 0;
71823
72119
  const widgetAggregation = receivedData?.deviceStatusAggregation;
71824
72120
  if (widgetAggregation && widgetAggregation.hasData) {
71825
72121
  summary.byStatus = {
@@ -72042,8 +72338,7 @@ var WATER_SUMMARY_TOOLTIP_CSS = `
72042
72338
  border: 1px solid #e2e8f0;
72043
72339
  border-radius: 12px;
72044
72340
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15), 0 2px 10px rgba(0, 0, 0, 0.08);
72045
- min-width: 380px;
72046
- width: max-content;
72341
+ width: 395px;
72047
72342
  max-width: 90vw;
72048
72343
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
72049
72344
  font-size: 12px;
@@ -73877,7 +74172,7 @@ var PIE_COLORS = [
73877
74172
  ];
73878
74173
  var COLUMN_SUMMARY_CSS = `
73879
74174
  .myio-col-summary {
73880
- max-width: 300px;
74175
+ width: 100%;
73881
74176
  font-family: 'Nunito', 'Segoe UI', system-ui, sans-serif;
73882
74177
  }
73883
74178
  .myio-col-summary__kpis {
@@ -73895,14 +74190,10 @@ var COLUMN_SUMMARY_CSS = `
73895
74190
  .myio-col-summary__kpi-value {
73896
74191
  font-size: 12px; font-weight: 700; color: #1e293b; text-align: right;
73897
74192
  }
73898
- .myio-col-summary__kpi-value--accent {
73899
- font-size: 14px; color: #3e1a7d;
73900
- }
74193
+ .myio-col-summary__kpi-value--accent { font-size: 14px; color: #3e1a7d; }
73901
74194
  .myio-col-summary__body { display: block; }
73902
74195
  .myio-col-summary__lists { display: flex; flex-direction: column; }
73903
- .myio-col-summary__group {
73904
- display: flex; flex-direction: column; gap: 1px; margin-top: 8px;
73905
- }
74196
+ .myio-col-summary__group { display: flex; flex-direction: column; gap: 1px; margin-top: 8px; }
73906
74197
  .myio-col-summary__group-label {
73907
74198
  font-size: 10px; font-weight: 800; letter-spacing: 0.3px;
73908
74199
  text-transform: uppercase; color: #64748b; margin-bottom: 3px;
@@ -73915,9 +74206,7 @@ var COLUMN_SUMMARY_CSS = `
73915
74206
  flex: 1 1 auto; min-width: 0;
73916
74207
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
73917
74208
  }
73918
- .myio-col-summary__val {
73919
- flex: 0 0 auto; font-weight: 700; color: #16a34a; text-align: right;
73920
- }
74209
+ .myio-col-summary__val { flex: 0 0 auto; font-weight: 700; color: #16a34a; text-align: right; }
73921
74210
  .myio-col-summary__pct {
73922
74211
  flex: 0 0 auto; min-width: 44px; text-align: right;
73923
74212
  font-size: 10px; font-weight: 600; color: #64748b;
@@ -73927,25 +74216,43 @@ var COLUMN_SUMMARY_CSS = `
73927
74216
  font-size: 11px; color: #94a3b8;
73928
74217
  }
73929
74218
 
73930
- /* Pie chart \u2014 hidden in the compact view, revealed when maximized. */
74219
+ /* Pie chart + legend \u2014 hidden in the compact view, revealed when maximized. */
73931
74220
  .myio-col-summary__chart { display: none; }
73932
74221
  .myio-col-summary__chart-title {
73933
74222
  font-size: 11px; font-weight: 800; letter-spacing: 0.3px;
73934
74223
  text-transform: uppercase; color: #3e1a7d; margin-bottom: 8px;
73935
74224
  }
73936
- .myio-col-summary__chart-body {
73937
- display: flex; gap: 16px; align-items: flex-start;
74225
+ .myio-col-summary__chart-hint {
74226
+ font-size: 10px; font-weight: 500; color: #94a3b8; margin: 0 0 8px;
73938
74227
  }
73939
74228
  .myio-col-summary__pie {
73940
- width: 190px; height: 190px; border-radius: 50%; flex-shrink: 0;
73941
- box-shadow: 0 2px 12px rgba(0,0,0,0.18);
74229
+ display: block; width: 100%; height: auto; aspect-ratio: 1 / 1;
74230
+ filter: drop-shadow(0 2px 8px rgba(0,0,0,0.15));
73942
74231
  }
74232
+ .myio-col-summary__slice {
74233
+ stroke: #ffffff; stroke-width: 1.5;
74234
+ transition: opacity 0.12s ease;
74235
+ cursor: pointer;
74236
+ }
74237
+ .myio-col-summary__slice.is-hl {
74238
+ stroke: #1e293b; stroke-width: 3;
74239
+ }
74240
+ .myio-col-summary__slice:hover { opacity: 0.85; }
73943
74241
  .myio-col-summary__legend {
73944
- flex: 1 1 auto; min-width: 0; max-height: 300px; overflow-y: auto;
73945
- display: flex; flex-direction: column; gap: 1px;
74242
+ display: none;
74243
+ min-width: 0; overflow-y: auto;
74244
+ flex-direction: column; gap: 1px;
73946
74245
  }
73947
74246
  .myio-col-summary__legend-row {
73948
- display: flex; align-items: center; gap: 6px; padding: 2px 0; font-size: 11px;
74247
+ display: flex; align-items: center; gap: 6px; padding: 3px 5px; font-size: 11px;
74248
+ border-radius: 4px; cursor: pointer;
74249
+ transition: background 0.12s ease;
74250
+ }
74251
+ .myio-col-summary__legend-row:hover,
74252
+ .myio-col-summary__legend-row.is-hl { background: #f1ecfa; }
74253
+ .myio-col-summary__legend-row.is-excluded { opacity: 0.45; }
74254
+ .myio-col-summary__legend-row.is-excluded .myio-col-summary__legend-name {
74255
+ text-decoration: line-through;
73949
74256
  }
73950
74257
  .myio-col-summary__legend-dot {
73951
74258
  width: 9px; height: 9px; border-radius: 2px; flex-shrink: 0;
@@ -73959,14 +74266,33 @@ var COLUMN_SUMMARY_CSS = `
73959
74266
  flex: 0 0 auto; min-width: 42px; text-align: right; font-size: 10px; color: #64748b;
73960
74267
  }
73961
74268
 
73962
- /* Maximized \u2014 use the extra space: widen, show the pie chart, lists side-by-side. */
73963
- .myio-info-tooltip.maximized .myio-col-summary { max-width: none; }
74269
+ /* Maximized \u2014 fill the whole panel: KPIs on top, then a 2-column grid
74270
+ (pie + lists on the left, full-height legend on the right). No dead space. */
74271
+ .myio-info-tooltip.maximized .myio-col-summary {
74272
+ max-width: none; height: 100%;
74273
+ display: flex; flex-direction: column;
74274
+ }
74275
+ .myio-info-tooltip.maximized .myio-col-summary__kpis { flex: 0 0 auto; }
74276
+ .myio-info-tooltip.maximized .myio-col-summary__body {
74277
+ flex: 1 1 auto; min-height: 0;
74278
+ display: grid;
74279
+ grid-template-columns: minmax(340px, 440px) 1fr;
74280
+ grid-template-rows: auto 1fr;
74281
+ gap: 14px 22px;
74282
+ }
73964
74283
  .myio-info-tooltip.maximized .myio-col-summary__chart {
73965
- display: block; margin-bottom: 16px;
73966
- padding-bottom: 14px; border-bottom: 1px solid #e3d9f3;
74284
+ display: block; grid-column: 1; grid-row: 1; min-width: 0;
73967
74285
  }
74286
+ .myio-info-tooltip.maximized .myio-col-summary__pie { max-width: 440px; margin: 0 auto; }
73968
74287
  .myio-info-tooltip.maximized .myio-col-summary__lists {
73969
- display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px;
74288
+ grid-column: 1; grid-row: 2; min-height: 0; overflow-y: auto;
74289
+ display: flex; flex-direction: column; gap: 14px;
74290
+ padding-right: 4px;
74291
+ }
74292
+ .myio-info-tooltip.maximized .myio-col-summary__legend {
74293
+ display: flex; grid-column: 2; grid-row: 1 / 3;
74294
+ min-height: 0; max-height: none;
74295
+ border-left: 1px solid #e3d9f3; padding-left: 20px;
73970
74296
  }
73971
74297
  .myio-info-tooltip.maximized .myio-col-summary__group { margin-top: 0; }
73972
74298
  `;
@@ -73998,79 +74324,90 @@ function fmtPct(value, total, decimals) {
73998
74324
  const p = total > 0 ? (Number(value) || 0) / total * 100 : 0;
73999
74325
  return p.toFixed(decimals).replace(".", ",") + "%";
74000
74326
  }
74001
- function buildPieChart(devices, total, fmt2, pctDecimals) {
74002
- const sorted = devices.slice().sort((a, b) => (Number(b.value) || 0) - (Number(a.value) || 0));
74003
- if (total <= 0 || !sorted.length) {
74004
- return `<div class="myio-col-summary__chart">
74005
- <div class="myio-col-summary__empty">Sem dados para o gr\xE1fico.</div>
74006
- </div>`;
74327
+ function sliceColor(originalIndex) {
74328
+ return PIE_COLORS[originalIndex % PIE_COLORS.length];
74329
+ }
74330
+ function polar(cx, cy, r, deg) {
74331
+ const rad = (deg - 90) * Math.PI / 180;
74332
+ return [cx + r * Math.cos(rad), cy + r * Math.sin(rad)];
74333
+ }
74334
+ function arcPath(cx, cy, r, a0, a1) {
74335
+ const [x0, y0] = polar(cx, cy, r, a0);
74336
+ const [x1, y1] = polar(cx, cy, r, a1);
74337
+ const large = a1 - a0 > 180 ? 1 : 0;
74338
+ return `M${cx},${cy} L${x0.toFixed(2)},${y0.toFixed(2)} A${r},${r} 0 ${large} 1 ${x1.toFixed(
74339
+ 2
74340
+ )},${y1.toFixed(2)} Z`;
74341
+ }
74342
+ var _state = null;
74343
+ var _rootEl = null;
74344
+ function visibleDevices() {
74345
+ if (!_state) return [];
74346
+ return _state.data.devices.map((d, idx) => ({ d, idx })).filter((x) => !_state.excluded.has(x.idx));
74347
+ }
74348
+ function buildPieSvg(visible, total) {
74349
+ if (!_state) return "";
74350
+ const positives = visible.filter((v) => (Number(v.d.value) || 0) > 0);
74351
+ if (!positives.length || total <= 0) {
74352
+ return `<svg class="myio-col-summary__pie" viewBox="0 0 220 220" role="img" aria-label="Sem dados">
74353
+ <circle cx="110" cy="110" r="100" fill="#f1f5f9"></circle>
74354
+ <text x="110" y="115" text-anchor="middle" font-size="12" fill="#94a3b8">sem dados</text>
74355
+ </svg>`;
74356
+ }
74357
+ const { fmt: fmt2, pd } = _state;
74358
+ if (positives.length === 1) {
74359
+ const v = positives[0];
74360
+ const val = Number(v.d.value) || 0;
74361
+ return `<svg class="myio-col-summary__pie" viewBox="0 0 220 220">
74362
+ <circle class="myio-col-summary__slice" data-idx="${v.idx}" cx="110" cy="110" r="100"
74363
+ fill="${sliceColor(v.idx)}"><title>${esc3(v.d.name)} \u2014 ${esc3(fmt2(val))} (${fmtPct(
74364
+ val,
74365
+ total,
74366
+ pd
74367
+ )})</title></circle>
74368
+ </svg>`;
74007
74369
  }
74008
74370
  let acc = 0;
74009
- const stops = [];
74010
- const legend = [];
74011
- sorted.forEach((d, i) => {
74012
- const v = Number(d.value) || 0;
74013
- const color = PIE_COLORS[i % PIE_COLORS.length];
74014
- const start = acc / total * 360;
74015
- acc += v;
74016
- const end = acc / total * 360;
74017
- stops.push(`${color} ${start.toFixed(3)}deg ${end.toFixed(3)}deg`);
74018
- legend.push(`<div class="myio-col-summary__legend-row">
74019
- <span class="myio-col-summary__legend-dot" style="background:${color};"></span>
74020
- <span class="myio-col-summary__legend-name" title="${esc3(d.name)}">${esc3(d.name)}</span>
74021
- <span class="myio-col-summary__legend-val">${esc3(fmt2(v))}</span>
74022
- <span class="myio-col-summary__legend-pct">${fmtPct(v, total, pctDecimals)}</span>
74023
- </div>`);
74024
- });
74025
- return `<div class="myio-col-summary__chart">
74026
- <div class="myio-col-summary__chart-title">Distribui\xE7\xE3o \u2014 ${sorted.length} dispositivos</div>
74027
- <div class="myio-col-summary__chart-body">
74028
- <div class="myio-col-summary__pie" style="background: conic-gradient(${stops.join(", ")});"></div>
74029
- <div class="myio-col-summary__legend">${legend.join("")}</div>
74030
- </div>
74031
- </div>`;
74032
- }
74033
- function buildContent(data) {
74034
- const devices = Array.isArray(data.devices) ? data.devices.slice() : [];
74035
- const fmt2 = data.formatValue || defaultFormatter(data.unit || "");
74036
- const pd = resolvePercentDecimals(data.percentDecimals);
74037
- const count = devices.length;
74038
- const total = devices.reduce((s, d) => s + (Number(d.value) || 0), 0);
74371
+ const paths = positives.map((v) => {
74372
+ const val = Number(v.d.value) || 0;
74373
+ const a0 = acc / total * 360;
74374
+ acc += val;
74375
+ const a1 = acc / total * 360;
74376
+ return `<path class="myio-col-summary__slice" data-idx="${v.idx}"
74377
+ d="${arcPath(110, 110, 100, a0, a1)}" fill="${sliceColor(v.idx)}"
74378
+ ><title>${esc3(v.d.name)} \u2014 ${esc3(fmt2(val))} (${fmtPct(val, total, pd)})</title></path>`;
74379
+ }).join("");
74380
+ return `<svg class="myio-col-summary__pie" viewBox="0 0 220 220">${paths}</svg>`;
74381
+ }
74382
+ function buildLegend(total) {
74383
+ if (!_state) return "";
74384
+ const { data, excluded, fmt: fmt2, pd } = _state;
74385
+ const rows = data.devices.map((d, idx) => {
74386
+ const val = Number(d.value) || 0;
74387
+ const isExcl = excluded.has(idx);
74388
+ const pct = isExcl ? "\u2014" : fmtPct(val, total, pd);
74389
+ return `<div class="myio-col-summary__legend-row${isExcl ? " is-excluded" : ""}" data-idx="${idx}"
74390
+ title="Clique para ${isExcl ? "incluir" : "remover"} da pizza">
74391
+ <span class="myio-col-summary__legend-dot" style="background:${sliceColor(idx)};"></span>
74392
+ <span class="myio-col-summary__legend-name">${esc3(d.name)}</span>
74393
+ <span class="myio-col-summary__legend-val">${esc3(fmt2(val))}</span>
74394
+ <span class="myio-col-summary__legend-pct">${pct}</span>
74395
+ </div>`;
74396
+ }).join("");
74397
+ return `<div class="myio-col-summary__legend">${rows}</div>`;
74398
+ }
74399
+ function buildInner() {
74400
+ if (!_state) return "";
74401
+ const { data, fmt: fmt2, pd } = _state;
74402
+ const visible = visibleDevices();
74403
+ const count = visible.length;
74404
+ const total = visible.reduce((s, v) => s + (Number(v.d.value) || 0), 0);
74039
74405
  const avg = count ? total / count : 0;
74040
74406
  const periodRow = data.periodLabel ? `<div class="myio-col-summary__kpi">
74041
74407
  <span class="myio-col-summary__kpi-label">Per\xEDodo</span>
74042
74408
  <span class="myio-col-summary__kpi-value">${esc3(data.periodLabel)}</span>
74043
74409
  </div>` : "";
74044
- if (!count) {
74045
- return `<div class="myio-col-summary">
74046
- <div class="myio-col-summary__kpis">
74047
- ${periodRow}
74048
- <div class="myio-col-summary__kpi">
74049
- <span class="myio-col-summary__kpi-label">Dispositivos</span>
74050
- <span class="myio-col-summary__kpi-value">0</span>
74051
- </div>
74052
- </div>
74053
- <div class="myio-col-summary__empty">Nenhum dispositivo.</div>
74054
- </div>`;
74055
- }
74056
- const desc = devices.slice().sort((a, b) => (Number(b.value) || 0) - (Number(a.value) || 0));
74057
- const top3 = desc.slice(0, 3);
74058
- const bottom3 = desc.slice(-3).reverse();
74059
- const near3 = devices.slice().sort(
74060
- (a, b) => Math.abs((Number(a.value) || 0) - avg) - Math.abs((Number(b.value) || 0) - avg)
74061
- ).slice(0, 3);
74062
- const row = (d) => `
74063
- <div class="myio-col-summary__row">
74064
- <span class="myio-col-summary__name" title="${esc3(d.name)}">${esc3(d.name)}</span>
74065
- <span class="myio-col-summary__val">${esc3(fmt2(Number(d.value) || 0))}</span>
74066
- <span class="myio-col-summary__pct">${fmtPct(Number(d.value) || 0, total, pd)}</span>
74067
- </div>`;
74068
- const group = (label, list) => list.length ? `<div class="myio-col-summary__group">
74069
- <span class="myio-col-summary__group-label">${label}</span>
74070
- ${list.map(row).join("")}
74071
- </div>` : "";
74072
- return `<div class="myio-col-summary">
74073
- <div class="myio-col-summary__kpis">
74410
+ const kpis = `<div class="myio-col-summary__kpis">
74074
74411
  ${periodRow}
74075
74412
  <div class="myio-col-summary__kpi">
74076
74413
  <span class="myio-col-summary__kpi-label">Dispositivos</span>
@@ -74086,26 +74423,96 @@ function buildContent(data) {
74086
74423
  <span class="myio-col-summary__kpi-label">Consumo total</span>
74087
74424
  <span class="myio-col-summary__kpi-value">${esc3(fmt2(total))}</span>
74088
74425
  </div>
74089
- </div>
74426
+ </div>`;
74427
+ if (!data.devices.length) {
74428
+ return `${kpis}<div class="myio-col-summary__empty">Nenhum dispositivo.</div>`;
74429
+ }
74430
+ const desc = visible.slice().sort((a, b) => (Number(b.d.value) || 0) - (Number(a.d.value) || 0));
74431
+ const top3 = desc.slice(0, 3);
74432
+ const bottom3 = desc.slice(-3).reverse();
74433
+ const near3 = visible.slice().sort(
74434
+ (a, b) => Math.abs((Number(a.d.value) || 0) - avg) - Math.abs((Number(b.d.value) || 0) - avg)
74435
+ ).slice(0, 3);
74436
+ const row = (v) => `
74437
+ <div class="myio-col-summary__row">
74438
+ <span class="myio-col-summary__name" title="${esc3(v.d.name)}">${esc3(v.d.name)}</span>
74439
+ <span class="myio-col-summary__val">${esc3(fmt2(Number(v.d.value) || 0))}</span>
74440
+ <span class="myio-col-summary__pct">${fmtPct(Number(v.d.value) || 0, total, pd)}</span>
74441
+ </div>`;
74442
+ const group = (label, list) => list.length ? `<div class="myio-col-summary__group">
74443
+ <span class="myio-col-summary__group-label">${label}</span>
74444
+ ${list.map(row).join("")}
74445
+ </div>` : "";
74446
+ return `${kpis}
74090
74447
  <div class="myio-col-summary__body">
74091
- ${buildPieChart(devices, total, fmt2, pd)}
74448
+ <div class="myio-col-summary__chart">
74449
+ <div class="myio-col-summary__chart-title">Distribui\xE7\xE3o \u2014 ${count} dispositivos</div>
74450
+ <p class="myio-col-summary__chart-hint">Passe o mouse para destacar \xB7 clique na lista para remover da pizza</p>
74451
+ ${buildPieSvg(visible, total)}
74452
+ </div>
74453
+ ${buildLegend(total)}
74092
74454
  <div class="myio-col-summary__lists">
74093
74455
  ${group("\u25B2 3 maiores", top3)}
74094
74456
  ${group("\u25BC 3 menores", bottom3)}
74095
74457
  ${group("\u25CF 3 na m\xE9dia", near3)}
74096
74458
  </div>
74097
- </div>
74098
- </div>`;
74459
+ </div>`;
74460
+ }
74461
+ function setHighlight(idx) {
74462
+ if (!_rootEl) return;
74463
+ _rootEl.querySelectorAll(".is-hl").forEach((el2) => el2.classList.remove("is-hl"));
74464
+ if (idx == null) return;
74465
+ _rootEl.querySelectorAll(`[data-idx="${idx}"]`).forEach((el2) => el2.classList.add("is-hl"));
74466
+ }
74467
+ function rerender() {
74468
+ if (!_rootEl) return;
74469
+ _rootEl.innerHTML = buildInner();
74470
+ }
74471
+ function wireRoot(root) {
74472
+ root.addEventListener("mouseover", (e) => {
74473
+ const el2 = e.target?.closest?.("[data-idx]");
74474
+ setHighlight(el2 ? el2.getAttribute("data-idx") : null);
74475
+ });
74476
+ root.addEventListener("mouseout", (e) => {
74477
+ const el2 = e.target?.closest?.("[data-idx]");
74478
+ if (el2) setHighlight(null);
74479
+ });
74480
+ root.addEventListener("click", (e) => {
74481
+ const legendRow = e.target?.closest?.(
74482
+ ".myio-col-summary__legend-row"
74483
+ );
74484
+ if (!legendRow || !_state) return;
74485
+ const idx = Number(legendRow.getAttribute("data-idx"));
74486
+ if (!Number.isInteger(idx)) return;
74487
+ if (_state.excluded.has(idx)) _state.excluded.delete(idx);
74488
+ else _state.excluded.add(idx);
74489
+ rerender();
74490
+ });
74099
74491
  }
74100
74492
  var ColumnSummaryTooltip = {
74101
74493
  /** Shows the column summary tooltip anchored to the trigger element. */
74102
74494
  show(triggerElement, data) {
74103
74495
  injectCSS10();
74496
+ _state = {
74497
+ data: data && Array.isArray(data.devices) ? data : { ...data, devices: [] },
74498
+ excluded: /* @__PURE__ */ new Set(),
74499
+ fmt: data.formatValue || defaultFormatter(data.unit || ""),
74500
+ pd: resolvePercentDecimals(data.percentDecimals)
74501
+ };
74104
74502
  InfoTooltip.show(triggerElement, {
74105
74503
  icon: "\u{1F4CA}",
74106
74504
  title: data.title ? `Resumo \u2014 ${data.title}` : "Resumo da Coluna",
74107
- content: buildContent(data)
74505
+ content: `<div class="myio-col-summary">${buildInner()}</div>`
74108
74506
  });
74507
+ if (typeof requestAnimationFrame === "function") {
74508
+ requestAnimationFrame(() => {
74509
+ const roots = document.querySelectorAll(
74510
+ ".myio-info-tooltip .myio-col-summary"
74511
+ );
74512
+ _rootEl = roots.length ? roots[roots.length - 1] : null;
74513
+ if (_rootEl) wireRoot(_rootEl);
74514
+ });
74515
+ }
74109
74516
  },
74110
74517
  /** Hides the tooltip immediately. */
74111
74518
  hide() {
@@ -102659,11 +103066,11 @@ var FilterModalComponent = class {
102659
103066
  }
102660
103067
  selectAllDevices() {
102661
103068
  const devices = this.options.devices || [];
102662
- const visibleDevices = devices.filter((d) => {
103069
+ const visibleDevices2 = devices.filter((d) => {
102663
103070
  const el2 = this.overlay.querySelector(`[data-device-id="${d.id}"]`);
102664
103071
  return el2 && !el2.classList.contains("myio-fm__device-item--hidden");
102665
103072
  });
102666
- visibleDevices.forEach((device) => {
103073
+ visibleDevices2.forEach((device) => {
102667
103074
  this.selectedDeviceIds.add(device.id);
102668
103075
  this.updateDeviceUI(device.id);
102669
103076
  });
@@ -102671,11 +103078,11 @@ var FilterModalComponent = class {
102671
103078
  }
102672
103079
  selectNoDevices() {
102673
103080
  const devices = this.options.devices || [];
102674
- const visibleDevices = devices.filter((d) => {
103081
+ const visibleDevices2 = devices.filter((d) => {
102675
103082
  const el2 = this.overlay.querySelector(`[data-device-id="${d.id}"]`);
102676
103083
  return el2 && !el2.classList.contains("myio-fm__device-item--hidden");
102677
103084
  });
102678
- visibleDevices.forEach((device) => {
103085
+ visibleDevices2.forEach((device) => {
102679
103086
  this.selectedDeviceIds.delete(device.id);
102680
103087
  this.updateDeviceUI(device.id);
102681
103088
  });
@@ -108875,235 +109282,6 @@ function injectStyles10(_container) {
108875
109282
  document.head.appendChild(styleEl);
108876
109283
  }
108877
109284
 
108878
- // src/components/telemetry-grid-shopping/export.ts
108879
- var import_jspdf = require("jspdf");
108880
- function makeCols(unit) {
108881
- return [
108882
- { key: "idx", label: "#", pdfW: 10 },
108883
- { key: "nome", label: "Nome", pdfW: 100 },
108884
- { key: "identificador", label: "Identificador", pdfW: 60 },
108885
- { key: "consumo", label: unit ? `Consumo (${unit})` : "Consumo", pdfW: 50 },
108886
- { key: "perc", label: "%", pdfW: 20 }
108887
- ];
108888
- }
108889
- function buildRow(d, idx) {
108890
- const fmtVal = () => {
108891
- if (d.val === null || d.val === void 0) return "\u2014";
108892
- return Number(d.val).toLocaleString("pt-BR", { maximumFractionDigits: 3, useGrouping: false });
108893
- };
108894
- const pd = resolvePercentDecimals();
108895
- return {
108896
- idx: String(idx + 1),
108897
- nome: d.labelOrName || d.name || "\u2014",
108898
- identificador: d.deviceIdentifier || "\u2014",
108899
- consumo: fmtVal(),
108900
- perc: d.perc !== void 0 ? `${d.perc.toFixed(pd).replace(".", ",")}%` : "\u2014"
108901
- };
108902
- }
108903
- function fmtPeriod(period) {
108904
- if (!period?.startISO) return "";
108905
- const fmt2 = (iso) => new Date(iso).toLocaleDateString("pt-BR");
108906
- return period.endISO ? `${fmt2(period.startISO)} \u2014 ${fmt2(period.endISO)}` : fmt2(period.startISO);
108907
- }
108908
- function slugify(s) {
108909
- return s.normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^\w]+/g, "-").toLowerCase().replace(/^-+|-+$/g, "").slice(0, 40);
108910
- }
108911
- function datestamp() {
108912
- const d = /* @__PURE__ */ new Date();
108913
- const p = (n) => String(n).padStart(2, "0");
108914
- return `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}-${p(d.getHours())}${p(d.getMinutes())}`;
108915
- }
108916
- function buildFilenameBase(label, customerName) {
108917
- const parts = [];
108918
- const cSlug = customerName ? slugify(customerName) : "";
108919
- if (cSlug) parts.push(cSlug);
108920
- parts.push(slugify(label));
108921
- parts.push(datestamp());
108922
- return parts.join("-");
108923
- }
108924
- function triggerDownload(blob, filename) {
108925
- const url = URL.createObjectURL(blob);
108926
- const a = document.createElement("a");
108927
- a.href = url;
108928
- a.download = filename;
108929
- a.click();
108930
- setTimeout(() => URL.revokeObjectURL(url), 1e3);
108931
- }
108932
- function escXml(s) {
108933
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
108934
- }
108935
- function truncate(s, max) {
108936
- return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
108937
- }
108938
- function exportGridCsv(devices, label, unit, period, customerName) {
108939
- const periodLabel = fmtPeriod(period);
108940
- const metaRows = [
108941
- `"${label}"`,
108942
- customerName ? `"Cliente";"${customerName}"` : null,
108943
- periodLabel ? `"Per\xEDodo";"${periodLabel}"` : null,
108944
- `"Gerado em";"${(/* @__PURE__ */ new Date()).toLocaleString("pt-BR")}"`,
108945
- ""
108946
- ].filter((v) => v !== null);
108947
- const cols = makeCols(unit);
108948
- const header = cols.map((c) => `"${c.label}"`).join(";");
108949
- const rows = devices.map((d, i) => {
108950
- const r = buildRow(d, i);
108951
- return cols.map((c) => `"${String(r[c.key]).replace(/"/g, '""')}"`).join(";");
108952
- });
108953
- const csv = "\uFEFF" + [...metaRows, header, ...rows].join("\r\n");
108954
- triggerDownload(
108955
- new Blob([csv], { type: "text/csv;charset=utf-8;" }),
108956
- `${buildFilenameBase(label, customerName)}.csv`
108957
- );
108958
- }
108959
- function exportGridXls(devices, label, unit, period, customerName) {
108960
- const periodLabel = fmtPeriod(period);
108961
- const cols = makeCols(unit);
108962
- const span = cols.length - 1;
108963
- const metaRow = (key, val) => `<Row><Cell ss:StyleID="m"><Data ss:Type="String">${escXml(key)}</Data></Cell><Cell ss:MergeAcross="${span - 1}"><Data ss:Type="String">${escXml(val)}</Data></Cell></Row>`;
108964
- const headerCells = cols.map(
108965
- (c) => `<Cell ss:StyleID="h"><Data ss:Type="String">${escXml(c.label)}</Data></Cell>`
108966
- ).join("");
108967
- const dataRows = devices.map((d, i) => {
108968
- const r = buildRow(d, i);
108969
- const cells = cols.map((c) => {
108970
- const v = escXml(String(r[c.key]));
108971
- return `<Cell><Data ss:Type="String">${v}</Data></Cell>`;
108972
- }).join("");
108973
- return `<Row>${cells}</Row>`;
108974
- }).join("\n");
108975
- const xml = `<?xml version="1.0" encoding="UTF-8"?>
108976
- <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
108977
- xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
108978
- <Styles>
108979
- <Style ss:ID="h">
108980
- <Font ss:Bold="1" ss:Color="#FFFFFF"/>
108981
- <Interior ss:Color="#3E1A7D" ss:Pattern="Solid"/>
108982
- </Style>
108983
- <Style ss:ID="m">
108984
- <Font ss:Bold="1"/>
108985
- <Interior ss:Color="#F0EDF9" ss:Pattern="Solid"/>
108986
- </Style>
108987
- </Styles>
108988
- <Worksheet ss:Name="${escXml(label.slice(0, 31))}">
108989
- <Table>
108990
- ${metaRow("Relat\xF3rio", label)}
108991
- ${customerName ? metaRow("Cliente", customerName) : ""}
108992
- ${periodLabel ? metaRow("Per\xEDodo", periodLabel) : ""}
108993
- ${metaRow("Gerado em", (/* @__PURE__ */ new Date()).toLocaleString("pt-BR"))}
108994
- <Row/>
108995
- <Row>${headerCells}</Row>
108996
- ${dataRows}
108997
- </Table>
108998
- </Worksheet>
108999
- </Workbook>`;
109000
- triggerDownload(
109001
- new Blob([xml], { type: "application/vnd.ms-excel;charset=utf-8;" }),
109002
- `${buildFilenameBase(label, customerName)}.xls`
109003
- );
109004
- }
109005
- function exportGridPdf(devices, label, unit, period, customerName) {
109006
- const generatedAt = (/* @__PURE__ */ new Date()).toLocaleString("pt-BR");
109007
- const doc = new import_jspdf.jsPDF({ orientation: "landscape", unit: "mm", format: "a4" });
109008
- const PW = doc.internal.pageSize.getWidth();
109009
- const PH = doc.internal.pageSize.getHeight();
109010
- const MARGIN = 10;
109011
- const HDR_H = 13;
109012
- const FTR_H = 10;
109013
- const ROW_H = 7;
109014
- const HEAD_H = 8;
109015
- const TABLE_Y = HDR_H + MARGIN;
109016
- const MAX_Y = PH - FTR_H - MARGIN;
109017
- const TABLE_W = PW - MARGIN * 2;
109018
- const cols = makeCols(unit);
109019
- const rawTotal = cols.reduce((s, c) => s + c.pdfW, 0);
109020
- const scale = TABLE_W / rawTotal;
109021
- const colWidths = cols.map((c) => c.pdfW * scale);
109022
- const colX = (ci) => MARGIN + colWidths.slice(0, ci).reduce((s, w) => s + w, 0);
109023
- function drawPageHeader(pageNo2) {
109024
- doc.setFillColor(62, 26, 125);
109025
- doc.rect(0, 0, PW, HDR_H, "F");
109026
- doc.setTextColor(255, 255, 255);
109027
- doc.setFont("helvetica", "bold");
109028
- doc.setFontSize(10);
109029
- const titleText = customerName ? `${customerName} \u2014 ${label}` : label;
109030
- doc.text(titleText, MARGIN, HDR_H / 2 + 1.5);
109031
- doc.setFont("helvetica", "normal");
109032
- doc.setFontSize(8);
109033
- const periodLabel = fmtPeriod(period);
109034
- const periodPart = periodLabel ? `Per\xEDodo: ${periodLabel} \u2022 ` : "";
109035
- const info = `${periodPart}Gerado em: ${generatedAt} \u2022 ${devices.length} dispositivo(s) \u2022 Unidade: ${unit} \u2022 P\xE1g. ${pageNo2}`;
109036
- doc.text(info, PW - MARGIN, HDR_H / 2 + 1.5, { align: "right" });
109037
- }
109038
- function drawColumnHeaders(y) {
109039
- doc.setFillColor(240, 237, 250);
109040
- doc.rect(MARGIN, y, TABLE_W, HEAD_H, "F");
109041
- doc.setTextColor(62, 26, 125);
109042
- doc.setFont("helvetica", "bold");
109043
- doc.setFontSize(7);
109044
- cols.forEach((c, ci) => {
109045
- const x = colX(ci) + 1.5;
109046
- doc.text(c.label, x, y + HEAD_H / 2 + 2.5);
109047
- });
109048
- doc.setDrawColor(200, 195, 220);
109049
- doc.setLineWidth(0.2);
109050
- doc.rect(MARGIN, y, TABLE_W, HEAD_H);
109051
- }
109052
- function drawDataRow(r, y, even) {
109053
- if (even) {
109054
- doc.setFillColor(250, 249, 255);
109055
- doc.rect(MARGIN, y, TABLE_W, ROW_H, "F");
109056
- }
109057
- doc.setTextColor(40, 40, 40);
109058
- doc.setFont("helvetica", "normal");
109059
- doc.setFontSize(6.5);
109060
- cols.forEach((c, ci) => {
109061
- const x = colX(ci) + 1.5;
109062
- const maxChars = Math.floor(colWidths[ci] / 1.8);
109063
- const text = truncate(String(r[c.key]), maxChars);
109064
- doc.text(text, x, y + ROW_H / 2 + 2.2);
109065
- });
109066
- doc.setDrawColor(230, 228, 240);
109067
- doc.setLineWidth(0.1);
109068
- doc.line(MARGIN, y + ROW_H, MARGIN + TABLE_W, y + ROW_H);
109069
- }
109070
- function drawFooter2() {
109071
- doc.setFillColor(250, 249, 255);
109072
- doc.rect(0, PH - FTR_H, PW, FTR_H, "F");
109073
- doc.setDrawColor(210, 205, 230);
109074
- doc.setLineWidth(0.2);
109075
- doc.line(MARGIN, PH - FTR_H + 0.5, PW - MARGIN, PH - FTR_H + 0.5);
109076
- doc.setTextColor(120, 110, 150);
109077
- doc.setFont("helvetica", "normal");
109078
- doc.setFontSize(7);
109079
- doc.text(
109080
- `Gerado em ${generatedAt} \u2014 MyIO`,
109081
- MARGIN,
109082
- PH - FTR_H + 6
109083
- );
109084
- }
109085
- let pageNo = 1;
109086
- let currentY = TABLE_Y;
109087
- drawPageHeader(pageNo);
109088
- drawColumnHeaders(currentY);
109089
- currentY += HEAD_H;
109090
- devices.forEach((d, i) => {
109091
- if (currentY + ROW_H > MAX_Y) {
109092
- drawFooter2();
109093
- doc.addPage();
109094
- pageNo++;
109095
- currentY = TABLE_Y;
109096
- drawPageHeader(pageNo);
109097
- drawColumnHeaders(currentY);
109098
- currentY += HEAD_H;
109099
- }
109100
- drawDataRow(buildRow(d, i), currentY, i % 2 === 0);
109101
- currentY += ROW_H;
109102
- });
109103
- drawFooter2();
109104
- doc.save(`${buildFilenameBase(label, customerName)}.pdf`);
109105
- }
109106
-
109107
109285
  // src/components/telemetry-grid-shopping/TelemetryGridShoppingView.ts
109108
109286
  var TelemetryGridShoppingView = class {
109109
109287
  root;