myio-js-library 0.1.552 → 0.1.553
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 +421 -98
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +421 -98
- package/dist/myio-js-library.umd.js +421 -99
- package/dist/myio-js-library.umd.min.js +1 -1
- package/package.json +1 -1
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.
|
|
902
|
+
version: "0.1.553",
|
|
903
903
|
description: "A clean, standalone JS SDK for MYIO projects",
|
|
904
904
|
license: "MIT",
|
|
905
905
|
repository: "github:gh-myio/myio-js-library",
|
|
@@ -33574,31 +33574,52 @@ var fmtPt = (n) => new Intl.NumberFormat("pt-BR", { minimumFractionDigits: 2, ma
|
|
|
33574
33574
|
|
|
33575
33575
|
// src/components/telemetry-grid-shopping/export.ts
|
|
33576
33576
|
var import_jspdf = require("jspdf");
|
|
33577
|
+
function extras(d) {
|
|
33578
|
+
return d;
|
|
33579
|
+
}
|
|
33577
33580
|
function makeCols(unit, colOpts) {
|
|
33578
33581
|
const cols = [
|
|
33579
33582
|
{ key: "idx", label: "#", pdfW: 10 },
|
|
33580
33583
|
{ key: "nome", label: colOpts?.nameLabel || "Nome", pdfW: 100 },
|
|
33581
33584
|
{ key: "identificador", label: "Identificador", pdfW: 60 },
|
|
33582
33585
|
{ key: "consumo", label: colOpts?.valueLabel || (unit ? `Consumo (${unit})` : "Consumo"), pdfW: 50 },
|
|
33586
|
+
...(colOpts?.extraColumns || []).map((c, i) => ({ key: `x${i}`, label: c.label, pdfW: c.pdfW ?? 40 })),
|
|
33583
33587
|
{ key: "perc", label: "%", pdfW: 20 }
|
|
33584
33588
|
];
|
|
33585
33589
|
return cols.filter(
|
|
33586
33590
|
(c) => !(colOpts?.hideIdentifier && c.key === "identificador") && !(colOpts?.hidePerc && c.key === "perc")
|
|
33587
33591
|
);
|
|
33588
33592
|
}
|
|
33589
|
-
function buildRow(d, idx) {
|
|
33593
|
+
function buildRow(d, idx, colOpts) {
|
|
33594
|
+
const x = extras(d);
|
|
33590
33595
|
const fmtVal = () => {
|
|
33591
|
-
if (d.val === null || d.val === void 0) return "\u2014";
|
|
33596
|
+
if (d.val === null || d.val === void 0) return colOpts?.emptyValueText || "\u2014";
|
|
33592
33597
|
return Number(d.val).toLocaleString("pt-BR", { maximumFractionDigits: 3, useGrouping: false });
|
|
33593
33598
|
};
|
|
33594
33599
|
const pd = resolvePercentDecimals();
|
|
33595
|
-
|
|
33600
|
+
const name = d.labelOrName || d.name || "\u2014";
|
|
33601
|
+
const row = {
|
|
33596
33602
|
idx: String(idx + 1),
|
|
33597
|
-
nome:
|
|
33603
|
+
nome: x.nameNote ? `${name} (${x.nameNote})` : name,
|
|
33598
33604
|
identificador: d.deviceIdentifier || "\u2014",
|
|
33599
33605
|
consumo: fmtVal(),
|
|
33600
33606
|
perc: d.perc !== void 0 ? `${d.perc.toFixed(pd).replace(".", ",")}%` : "\u2014"
|
|
33601
33607
|
};
|
|
33608
|
+
(colOpts?.extraColumns || []).forEach((_, i) => {
|
|
33609
|
+
row[`x${i}`] = x.extraCells?.[i] ?? "\u2014";
|
|
33610
|
+
});
|
|
33611
|
+
return row;
|
|
33612
|
+
}
|
|
33613
|
+
function forEachRow(devices, onGroup, onRow) {
|
|
33614
|
+
let n = 0;
|
|
33615
|
+
devices.forEach((d) => {
|
|
33616
|
+
const g = extras(d).groupHeader;
|
|
33617
|
+
if (g) onGroup(g);
|
|
33618
|
+
else onRow(d, n++);
|
|
33619
|
+
});
|
|
33620
|
+
}
|
|
33621
|
+
function countDataRows(devices) {
|
|
33622
|
+
return devices.filter((d) => !extras(d).groupHeader).length;
|
|
33602
33623
|
}
|
|
33603
33624
|
function fmtPeriod(period) {
|
|
33604
33625
|
if (!period?.startISO) return "";
|
|
@@ -33646,10 +33667,15 @@ function exportGridCsv(devices, label, unit, period, customerName) {
|
|
|
33646
33667
|
].filter((v) => v !== null);
|
|
33647
33668
|
const cols = makeCols(unit);
|
|
33648
33669
|
const header = cols.map((c) => `"${c.label}"`).join(";");
|
|
33649
|
-
const rows =
|
|
33650
|
-
|
|
33651
|
-
|
|
33652
|
-
|
|
33670
|
+
const rows = [];
|
|
33671
|
+
forEachRow(
|
|
33672
|
+
devices,
|
|
33673
|
+
(g) => rows.push(`"${g.replace(/"/g, '""')}"`),
|
|
33674
|
+
(d, i) => {
|
|
33675
|
+
const r = buildRow(d, i);
|
|
33676
|
+
rows.push(cols.map((c) => `"${String(r[c.key]).replace(/"/g, '""')}"`).join(";"));
|
|
33677
|
+
}
|
|
33678
|
+
);
|
|
33653
33679
|
const csv = "\uFEFF" + [...metaRows, header, ...rows].join("\r\n");
|
|
33654
33680
|
triggerDownload(
|
|
33655
33681
|
new Blob([csv], { type: "text/csv;charset=utf-8;" }),
|
|
@@ -33665,14 +33691,23 @@ function exportGridXls(devices, label, unit, period, customerName, options) {
|
|
|
33665
33691
|
const headerCells = cols.map(
|
|
33666
33692
|
(c) => `<Cell ss:StyleID="h"><Data ss:Type="String">${escXml(c.label)}</Data></Cell>`
|
|
33667
33693
|
).join("");
|
|
33668
|
-
const
|
|
33669
|
-
|
|
33670
|
-
|
|
33671
|
-
|
|
33672
|
-
|
|
33673
|
-
|
|
33674
|
-
|
|
33675
|
-
|
|
33694
|
+
const dataRowsArr = [];
|
|
33695
|
+
forEachRow(
|
|
33696
|
+
devices,
|
|
33697
|
+
(g) => dataRowsArr.push(
|
|
33698
|
+
`<Row><Cell ss:StyleID="g" ss:MergeAcross="${cols.length - 1}"><Data ss:Type="String">${escXml(g)}</Data></Cell></Row>`
|
|
33699
|
+
),
|
|
33700
|
+
(d, i) => {
|
|
33701
|
+
const r = buildRow(d, i, options?.columns);
|
|
33702
|
+
const cells = cols.map((c) => {
|
|
33703
|
+
const v = escXml(String(r[c.key]));
|
|
33704
|
+
return `<Cell><Data ss:Type="String">${v}</Data></Cell>`;
|
|
33705
|
+
}).join("");
|
|
33706
|
+
dataRowsArr.push(`<Row>${cells}</Row>`);
|
|
33707
|
+
}
|
|
33708
|
+
);
|
|
33709
|
+
const dataRows = dataRowsArr.join("\n");
|
|
33710
|
+
const kpiRows = (options?.kpis || []).map((k) => metaRow(k.label, k.sub ? `${k.value} (${k.sub})` : k.value)).join("\n");
|
|
33676
33711
|
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
33677
33712
|
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
|
|
33678
33713
|
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
|
|
@@ -33685,6 +33720,10 @@ function exportGridXls(devices, label, unit, period, customerName, options) {
|
|
|
33685
33720
|
<Font ss:Bold="1"/>
|
|
33686
33721
|
<Interior ss:Color="#F0EDF9" ss:Pattern="Solid"/>
|
|
33687
33722
|
</Style>
|
|
33723
|
+
<Style ss:ID="g">
|
|
33724
|
+
<Font ss:Bold="1" ss:Color="#555555"/>
|
|
33725
|
+
<Interior ss:Color="#EEEEEE" ss:Pattern="Solid"/>
|
|
33726
|
+
</Style>
|
|
33688
33727
|
</Styles>
|
|
33689
33728
|
<Worksheet ss:Name="${escXml(label.slice(0, 31))}">
|
|
33690
33729
|
<Table>
|
|
@@ -33692,6 +33731,8 @@ function exportGridXls(devices, label, unit, period, customerName, options) {
|
|
|
33692
33731
|
${customerName ? metaRow("Cliente", customerName) : ""}
|
|
33693
33732
|
${periodLabel2 ? metaRow("Per\xEDodo", periodLabel2) : ""}
|
|
33694
33733
|
${metaRow("Gerado em", (/* @__PURE__ */ new Date()).toLocaleString("pt-BR"))}
|
|
33734
|
+
${kpiRows ? `<Row/>
|
|
33735
|
+
${kpiRows}` : ""}
|
|
33695
33736
|
<Row/>
|
|
33696
33737
|
<Row>${headerCells}</Row>
|
|
33697
33738
|
${dataRows}
|
|
@@ -33746,7 +33787,8 @@ function exportGridPdf(devices, label, unit, period, customerName, options) {
|
|
|
33746
33787
|
doc.setFontSize(8);
|
|
33747
33788
|
const periodLabel2 = fmtPeriod(period);
|
|
33748
33789
|
const periodPart = periodLabel2 ? `Per\xEDodo: ${periodLabel2} \u2022 ` : "";
|
|
33749
|
-
const
|
|
33790
|
+
const countLabel = options?.columns?.countLabel || "dispositivo(s)";
|
|
33791
|
+
const info = `${periodPart}Gerado em: ${generatedAt} \u2022 ${countDataRows(devices)} ${countLabel} \u2022 Unidade: ${unit} \u2022 P\xE1g. ${pageNo2}`;
|
|
33750
33792
|
doc.text(info, PW - MARGIN, HDR_H / 2 + 1.5, { align: "right" });
|
|
33751
33793
|
}
|
|
33752
33794
|
function drawColumnHeaders(y) {
|
|
@@ -33781,6 +33823,14 @@ function exportGridPdf(devices, label, unit, period, customerName, options) {
|
|
|
33781
33823
|
doc.setLineWidth(0.1);
|
|
33782
33824
|
doc.line(MARGIN, y + ROW_H, MARGIN + TABLE_W, y + ROW_H);
|
|
33783
33825
|
}
|
|
33826
|
+
function drawGroupRow(text2, y) {
|
|
33827
|
+
doc.setFillColor(...ACCENT_SOFT);
|
|
33828
|
+
doc.rect(MARGIN, y, TABLE_W, ROW_H, "F");
|
|
33829
|
+
doc.setTextColor(...ACCENT2);
|
|
33830
|
+
doc.setFont("helvetica", "bold");
|
|
33831
|
+
doc.setFontSize(6.5);
|
|
33832
|
+
doc.text(truncate(text2, 160), MARGIN + 1.5, y + ROW_H / 2 + 2.2);
|
|
33833
|
+
}
|
|
33784
33834
|
function drawFooter2() {
|
|
33785
33835
|
doc.setFillColor(250, 249, 255);
|
|
33786
33836
|
doc.rect(0, PH - FTR_H, PW, FTR_H, "F");
|
|
@@ -33827,7 +33877,7 @@ function exportGridPdf(devices, label, unit, period, customerName, options) {
|
|
|
33827
33877
|
}
|
|
33828
33878
|
drawColumnHeaders(currentY);
|
|
33829
33879
|
currentY += HEAD_H;
|
|
33830
|
-
|
|
33880
|
+
const ensureSpace = () => {
|
|
33831
33881
|
if (currentY + ROW_H > MAX_Y) {
|
|
33832
33882
|
drawFooter2();
|
|
33833
33883
|
doc.addPage();
|
|
@@ -33837,9 +33887,20 @@ function exportGridPdf(devices, label, unit, period, customerName, options) {
|
|
|
33837
33887
|
drawColumnHeaders(currentY);
|
|
33838
33888
|
currentY += HEAD_H;
|
|
33839
33889
|
}
|
|
33840
|
-
|
|
33841
|
-
|
|
33842
|
-
|
|
33890
|
+
};
|
|
33891
|
+
forEachRow(
|
|
33892
|
+
devices,
|
|
33893
|
+
(g) => {
|
|
33894
|
+
ensureSpace();
|
|
33895
|
+
drawGroupRow(g, currentY);
|
|
33896
|
+
currentY += ROW_H;
|
|
33897
|
+
},
|
|
33898
|
+
(d, i) => {
|
|
33899
|
+
ensureSpace();
|
|
33900
|
+
drawDataRow(buildRow(d, i, options?.columns), currentY, i % 2 === 0);
|
|
33901
|
+
currentY += ROW_H;
|
|
33902
|
+
}
|
|
33903
|
+
);
|
|
33843
33904
|
drawFooter2();
|
|
33844
33905
|
const chart = options?.chartImage;
|
|
33845
33906
|
if (chart?.dataUrl) {
|
|
@@ -35090,6 +35151,81 @@ function createModalFooter(params = {}) {
|
|
|
35090
35151
|
};
|
|
35091
35152
|
}
|
|
35092
35153
|
|
|
35154
|
+
// src/components/premium-modals/internal/temperatureBarsPng.ts
|
|
35155
|
+
function renderTemperatureBarsPng(opts) {
|
|
35156
|
+
if (typeof document === "undefined" || !opts.items.length) return null;
|
|
35157
|
+
const W = 1400;
|
|
35158
|
+
const ROW = 34;
|
|
35159
|
+
const TOP = 56;
|
|
35160
|
+
const H = TOP + opts.items.length * ROW + 20;
|
|
35161
|
+
const canvas = document.createElement("canvas");
|
|
35162
|
+
canvas.width = W;
|
|
35163
|
+
canvas.height = H;
|
|
35164
|
+
let ctx = null;
|
|
35165
|
+
try {
|
|
35166
|
+
ctx = canvas.getContext("2d");
|
|
35167
|
+
} catch {
|
|
35168
|
+
ctx = null;
|
|
35169
|
+
}
|
|
35170
|
+
if (!ctx) return null;
|
|
35171
|
+
const fmt2 = (v) => v.toLocaleString("pt-BR", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
|
35172
|
+
const ideal = opts.ideal && Number.isFinite(opts.ideal.min) && Number.isFinite(opts.ideal.max) ? opts.ideal : null;
|
|
35173
|
+
const values = opts.items.map((i) => i.value).filter((v) => v !== null && Number.isFinite(v));
|
|
35174
|
+
const base = values.length ? values : [20, 30];
|
|
35175
|
+
const lo = Math.floor(Math.min(...base, ideal ? ideal.min : Infinity) - 1);
|
|
35176
|
+
const hi = Math.ceil(Math.max(...base, ideal ? ideal.max : -Infinity) + 1);
|
|
35177
|
+
const span = Math.max(1, hi - lo);
|
|
35178
|
+
const LABEL_W = 460;
|
|
35179
|
+
const VALUE_W = 120;
|
|
35180
|
+
const trackX = LABEL_W + 20;
|
|
35181
|
+
const trackW = W - trackX - VALUE_W - 20;
|
|
35182
|
+
const xOf = (v) => trackX + (v - lo) / span * trackW;
|
|
35183
|
+
ctx.fillStyle = "#ffffff";
|
|
35184
|
+
ctx.fillRect(0, 0, W, H);
|
|
35185
|
+
ctx.fillStyle = "#6b7280";
|
|
35186
|
+
ctx.font = "20px Helvetica, Arial, sans-serif";
|
|
35187
|
+
ctx.textBaseline = "middle";
|
|
35188
|
+
const scale = `Escala ${fmt2(lo)}\u2013${fmt2(hi)} \xB0C`;
|
|
35189
|
+
ctx.fillText(ideal ? `${scale} \xB7 faixa ideal ${fmt2(ideal.min)}\u2013${fmt2(ideal.max)} \xB0C` : scale, 16, 26);
|
|
35190
|
+
opts.items.forEach((item, i) => {
|
|
35191
|
+
const y = TOP + i * ROW;
|
|
35192
|
+
const cy = y + ROW / 2;
|
|
35193
|
+
ctx.fillStyle = "#1f2937";
|
|
35194
|
+
ctx.font = "20px Helvetica, Arial, sans-serif";
|
|
35195
|
+
ctx.textAlign = "left";
|
|
35196
|
+
let label = item.label;
|
|
35197
|
+
while (ctx.measureText(label).width > LABEL_W - 10 && label.length > 4) label = label.slice(0, -2);
|
|
35198
|
+
if (label !== item.label) label = label.slice(0, -1) + "\u2026";
|
|
35199
|
+
ctx.fillText(label, 16, cy);
|
|
35200
|
+
if (item.value === null || !Number.isFinite(item.value)) {
|
|
35201
|
+
ctx.fillStyle = "#9ca3af";
|
|
35202
|
+
ctx.font = "italic 18px Helvetica, Arial, sans-serif";
|
|
35203
|
+
ctx.fillText("Sem leitura", trackX, cy);
|
|
35204
|
+
ctx.textAlign = "right";
|
|
35205
|
+
ctx.fillText("\u2014", W - 16, cy);
|
|
35206
|
+
return;
|
|
35207
|
+
}
|
|
35208
|
+
ctx.fillStyle = "#e5e7eb";
|
|
35209
|
+
ctx.fillRect(trackX, cy - 9, trackW, 18);
|
|
35210
|
+
if (ideal) {
|
|
35211
|
+
ctx.fillStyle = "rgba(34,197,94,0.18)";
|
|
35212
|
+
ctx.fillRect(xOf(ideal.min), cy - 9, xOf(ideal.max) - xOf(ideal.min), 18);
|
|
35213
|
+
}
|
|
35214
|
+
const v = item.value;
|
|
35215
|
+
ctx.fillStyle = ideal && v > ideal.max ? "#ef4444" : ideal && v < ideal.min ? "#3b82f6" : opts.accent || "#3e1a7d";
|
|
35216
|
+
ctx.fillRect(trackX, cy - 6, Math.max(2, xOf(v) - trackX), 12);
|
|
35217
|
+
ctx.fillStyle = "#111827";
|
|
35218
|
+
ctx.font = "bold 20px Helvetica, Arial, sans-serif";
|
|
35219
|
+
ctx.textAlign = "right";
|
|
35220
|
+
ctx.fillText(fmt2(v), W - 16, cy);
|
|
35221
|
+
});
|
|
35222
|
+
try {
|
|
35223
|
+
return { dataUrl: canvas.toDataURL("image/png"), width: W, height: H };
|
|
35224
|
+
} catch {
|
|
35225
|
+
return null;
|
|
35226
|
+
}
|
|
35227
|
+
}
|
|
35228
|
+
|
|
35093
35229
|
// src/components/premium-modals/report-device/DeviceReportModal.ts
|
|
35094
35230
|
var DOMAIN_CONFIG = {
|
|
35095
35231
|
energy: {
|
|
@@ -35883,10 +36019,22 @@ var DeviceReportModal = class {
|
|
|
35883
36019
|
const summaryValue = this.domainConfig.summaryType === "average" ? valid > 0 ? total / valid : 0 : total;
|
|
35884
36020
|
const now = /* @__PURE__ */ new Date();
|
|
35885
36021
|
const timestamp = now.toLocaleDateString("pt-BR") + " - " + now.toLocaleTimeString("pt-BR", { hour: "2-digit", minute: "2-digit" });
|
|
36022
|
+
const p = this.exportPeriod;
|
|
36023
|
+
const fmtD = (iso) => iso ? new Date(iso).toLocaleDateString("pt-BR") : "";
|
|
36024
|
+
const off = Number(this.params.temperatureOffset) || 0;
|
|
36025
|
+
const isTemp = this.domainConfig.summaryType === "average";
|
|
36026
|
+
void summaryValue;
|
|
35886
36027
|
const csvData = [
|
|
35887
36028
|
["Dispositivo/Loja", this.params.identifier || "N/A", this.params.label || ""],
|
|
36029
|
+
...this.params.deviceName ? [["Dispositivo (TB)", this.params.deviceName, ""]] : [],
|
|
36030
|
+
...isTemp && off !== 0 ? [["Offset aplicado (\xB0C)", this.domainConfig.formatter(off), ""]] : [],
|
|
36031
|
+
...this.resolveCustomerName() ? [["Cliente", this.resolveCustomerName(), ""]] : [],
|
|
36032
|
+
...p?.startISO ? [["Per\xEDodo", `${fmtD(p.startISO)} \u2014 ${fmtD(p.endISO)}`, ""]] : [],
|
|
36033
|
+
["Granularidade", this.granularity === "1h" ? "Hora" : "Dia", ""],
|
|
35888
36034
|
["DATA EMISS\xC3O", timestamp, ""],
|
|
35889
|
-
[
|
|
36035
|
+
[],
|
|
36036
|
+
...this.computeKpis().map((k) => [k.label, k.value, k.sub || ""]),
|
|
36037
|
+
[],
|
|
35890
36038
|
[this.granularity === "1h" ? "Data/Hora" : "Data", this.domainConfig.label, ""],
|
|
35891
36039
|
...this.data.map((row) => [this.formatDate(row.date), row.noData ? "Sem leitura" : this.domainConfig.formatter(row.consumption)])
|
|
35892
36040
|
];
|
|
@@ -35907,7 +36055,11 @@ var DeviceReportModal = class {
|
|
|
35907
36055
|
}
|
|
35908
36056
|
// Título dos exports — mesmo da modal (identificador + etiqueta do device).
|
|
35909
36057
|
resolveExportTitle() {
|
|
35910
|
-
|
|
36058
|
+
const base = `Relat\xF3rio - ${this.params.identifier || "SEM IDENTIFICADOR"} - ${this.params.label || "SEM ETIQUETA"}`;
|
|
36059
|
+
const dev = this.params.deviceName ? ` (${this.params.deviceName})` : "";
|
|
36060
|
+
const off = Number(this.params.temperatureOffset) || 0;
|
|
36061
|
+
const offTxt = this.domainConfig.summaryType === "average" && off !== 0 ? ` \xB7 offset ${off > 0 ? "+" : "\u2212"}${Math.abs(off).toLocaleString("pt-BR", { maximumFractionDigits: 2 })} \xB0C` : "";
|
|
36062
|
+
return `${base}${dev}${offTxt}`;
|
|
35911
36063
|
}
|
|
35912
36064
|
// Mapeia as linhas do relatório (dia/hora × consumo) para o shape TelemetryDevice
|
|
35913
36065
|
// dos exporters compartilhados do grid: labelOrName = data formatada, val = consumo,
|
|
@@ -35926,16 +36078,41 @@ var DeviceReportModal = class {
|
|
|
35926
36078
|
// Opções de coluna dos exporters: relatório single-device → Data | Consumo | %
|
|
35927
36079
|
// (Identificador redundante — o device já está no título/filename).
|
|
35928
36080
|
exportColumnOptions() {
|
|
36081
|
+
const isTemp = this.domainConfig.summaryType === "average";
|
|
35929
36082
|
return {
|
|
35930
36083
|
nameLabel: this.granularity === "1h" ? "Data/Hora" : "Data",
|
|
35931
|
-
hideIdentifier: true
|
|
36084
|
+
hideIdentifier: true,
|
|
36085
|
+
// Mesmo rótulo da coluna da tela (antes saía "Consumo (°C)" em temperatura)
|
|
36086
|
+
valueLabel: this.domainConfig.label,
|
|
36087
|
+
countLabel: this.granularity === "1h" ? "hora(s)" : "dia(s)",
|
|
36088
|
+
...isTemp ? { hidePerc: true, emptyValueText: "Sem leitura" } : {}
|
|
35932
36089
|
};
|
|
35933
36090
|
}
|
|
36091
|
+
// Gráfico da página final do PDF = painel da direita da tela
|
|
36092
|
+
async buildExportChart() {
|
|
36093
|
+
if (this.domainConfig.summaryType === "average") {
|
|
36094
|
+
const isHourly = this.granularity === "1h";
|
|
36095
|
+
const ranked = [...this.validRows].sort((a, b) => b.consumption - a.consumption);
|
|
36096
|
+
const shown = isHourly ? ranked.slice(0, 24) : ranked;
|
|
36097
|
+
const noData = isHourly ? [] : this.data.filter((r) => r.noData);
|
|
36098
|
+
const png = renderTemperatureBarsPng({
|
|
36099
|
+
items: [
|
|
36100
|
+
...shown.map((r) => ({ label: this.formatDate(r.date), value: r.consumption })),
|
|
36101
|
+
...noData.map((r) => ({ label: this.formatDate(r.date), value: null }))
|
|
36102
|
+
],
|
|
36103
|
+
ideal: this.params.temperatureIdealRange ?? null,
|
|
36104
|
+
accent: this.resolveAccentHex()
|
|
36105
|
+
});
|
|
36106
|
+
const title = isHourly ? `Horas mais quentes${ranked.length > 24 ? " (top 24)" : ""}` : "Dias mais quentes (m\xE9dia do dia)";
|
|
36107
|
+
return png ? { ...png, title } : null;
|
|
36108
|
+
}
|
|
36109
|
+
const chartPng = await this.participationChart?.toPngDataUrl?.().catch(() => null);
|
|
36110
|
+
return chartPng ? { ...chartPng, title: "Participa\xE7\xE3o por Dia" } : null;
|
|
36111
|
+
}
|
|
35934
36112
|
// PDF export — layout premium do grid + paleta do dashboard + faixa de KPIs +
|
|
35935
36113
|
// página dedicada com o gráfico "Participação por Dia" da modal.
|
|
35936
36114
|
async exportPDF() {
|
|
35937
36115
|
if (!this.data.length) return;
|
|
35938
|
-
const chartPng = await this.participationChart?.toPngDataUrl?.().catch(() => null);
|
|
35939
36116
|
exportGridPdf(
|
|
35940
36117
|
this.buildExportDevices(),
|
|
35941
36118
|
this.resolveExportTitle(),
|
|
@@ -35945,7 +36122,7 @@ var DeviceReportModal = class {
|
|
|
35945
36122
|
{
|
|
35946
36123
|
accentColor: this.resolveAccentHex(),
|
|
35947
36124
|
kpis: this.computeKpis(),
|
|
35948
|
-
chartImage:
|
|
36125
|
+
chartImage: await this.buildExportChart(),
|
|
35949
36126
|
columns: this.exportColumnOptions()
|
|
35950
36127
|
}
|
|
35951
36128
|
);
|
|
@@ -35961,7 +36138,8 @@ var DeviceReportModal = class {
|
|
|
35961
36138
|
this.resolveCustomerName() || null,
|
|
35962
36139
|
{
|
|
35963
36140
|
accentColor: this.resolveAccentHex(),
|
|
35964
|
-
columns: this.exportColumnOptions()
|
|
36141
|
+
columns: this.exportColumnOptions(),
|
|
36142
|
+
kpis: this.computeKpis()
|
|
35965
36143
|
}
|
|
35966
36144
|
);
|
|
35967
36145
|
}
|
|
@@ -38952,21 +39130,36 @@ var AllReportModal = class {
|
|
|
38952
39130
|
}
|
|
38953
39131
|
// Temperatura: valores já com offset e sem leituras inválidas (mesma base da tabela).
|
|
38954
39132
|
// 1d → uma linha por sensor por DIA; 1h → uma linha por sensor por HORA.
|
|
38955
|
-
exportTemperatureCSV(
|
|
39133
|
+
exportTemperatureCSV(_rows) {
|
|
39134
|
+
const rows = this.getFilteredData();
|
|
38956
39135
|
const showId = this.showIdentifierColumn(rows);
|
|
38957
39136
|
const idCol = (r) => showId ? [r.identifier] : [];
|
|
38958
39137
|
const idHead = showId ? ["Identificador"] : [];
|
|
38959
39138
|
const n2 = (v) => v === null || v === void 0 || !Number.isFinite(v) ? "" : v.toFixed(2);
|
|
38960
39139
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
39140
|
+
const gran = this.granularity;
|
|
38961
39141
|
const fmtStamp = (ts) => {
|
|
38962
39142
|
const { date: d, time } = this.formatSpDateTime(ts);
|
|
38963
39143
|
return `${d} ${time}`;
|
|
38964
39144
|
};
|
|
39145
|
+
const p = this.exportPeriod;
|
|
39146
|
+
const fmtD = (iso) => iso ? new Date(iso).toLocaleDateString("pt-BR") : "";
|
|
39147
|
+
const meta = [
|
|
39148
|
+
["Relat\xF3rio", this.resolveTitle()],
|
|
39149
|
+
...this.params.customerName ? [["Cliente", this.params.customerName]] : [],
|
|
39150
|
+
...p?.startISO ? [["Per\xEDodo", `${fmtD(p.startISO)} \u2014 ${fmtD(p.endISO)}`]] : [],
|
|
39151
|
+
["Granularidade", gran === "1h" ? "Hora" : "Dia"],
|
|
39152
|
+
["Gerado em", (/* @__PURE__ */ new Date()).toLocaleString("pt-BR")],
|
|
39153
|
+
[],
|
|
39154
|
+
...this.computeKpis().map((k) => [k.label, k.value, k.sub || ""]),
|
|
39155
|
+
[]
|
|
39156
|
+
];
|
|
38965
39157
|
const summary = [
|
|
38966
|
-
[...idHead, "Sensor", "M\xE9dia (\xB0C)", "M\xEDn (\xB0C)", "Data/Hora M\xEDn", "M\xE1x (\xB0C)", "Data/Hora M\xE1x", "Leituras", "Leituras descartadas"],
|
|
39158
|
+
[...idHead, "Sensor", "Offset (\xB0C)", "M\xE9dia (\xB0C)", "M\xEDn (\xB0C)", "Data/Hora M\xEDn", "M\xE1x (\xB0C)", "Data/Hora M\xE1x", "Leituras", "Leituras descartadas"],
|
|
38967
39159
|
...rows.map((r) => [
|
|
38968
39160
|
...idCol(r),
|
|
38969
39161
|
r.name,
|
|
39162
|
+
r.id ? n2(this.temperatureOffsetFor(r.id)) : "",
|
|
38970
39163
|
r.noData ? "Sem leitura" : n2(r.consumption),
|
|
38971
39164
|
r.noData ? "" : n2(r.min),
|
|
38972
39165
|
r.noData || !r.minTs ? "" : fmtStamp(r.minTs),
|
|
@@ -38976,7 +39169,6 @@ var AllReportModal = class {
|
|
|
38976
39169
|
String(r.discarded ?? "")
|
|
38977
39170
|
])
|
|
38978
39171
|
];
|
|
38979
|
-
const gran = this.granularity;
|
|
38980
39172
|
const series = [[...idHead, "Sensor", gran === "1h" ? "Data/Hora" : "Dia", "Temperatura (\xB0C)"]];
|
|
38981
39173
|
const fmtTs = (ts) => gran === "1h" ? new Date(ts).toLocaleString("pt-BR", {
|
|
38982
39174
|
day: "2-digit",
|
|
@@ -38989,9 +39181,9 @@ var AllReportModal = class {
|
|
|
38989
39181
|
for (const r of rows) {
|
|
38990
39182
|
const raw = r.id && this.temperatureStats.get(r.id)?.series || [];
|
|
38991
39183
|
const pts = gran === "1h" ? this.aggregateHourly(raw, "avg") : this.aggregateDaily(raw, "avg");
|
|
38992
|
-
for (const
|
|
39184
|
+
for (const p2 of pts) series.push([...idCol(r), r.name, fmtTs(p2.timestamp), p2.value.toFixed(2)]);
|
|
38993
39185
|
}
|
|
38994
|
-
const csvContent = toCsv([...summary, [], ...series]);
|
|
39186
|
+
const csvContent = toCsv([...meta, ...summary, [], ...series]);
|
|
38995
39187
|
this.downloadCSV(csvContent, `relatorio-temperatura-${gran}-${date}.csv`);
|
|
38996
39188
|
}
|
|
38997
39189
|
// 1h CSV: one row per device × hour. The series comes from per-device requests
|
|
@@ -39080,28 +39272,73 @@ var AllReportModal = class {
|
|
|
39080
39272
|
}
|
|
39081
39273
|
// Maps the report rows to the TelemetryDevice shape consumed by the shared
|
|
39082
39274
|
// TELEMETRY grid exporters (only labelOrName/name/deviceIdentifier/val/perc are read).
|
|
39275
|
+
// Linhas dos exports = a TABELA DA TELA: mesma ordem/filtros (getFilteredData), cabeçalhos
|
|
39276
|
+
// de grupo com o mesmo resumo, e — em temperatura — Mín/Máx, offset e "Sem leitura".
|
|
39083
39277
|
buildExportDevices() {
|
|
39084
|
-
const
|
|
39085
|
-
|
|
39086
|
-
)
|
|
39087
|
-
|
|
39088
|
-
|
|
39089
|
-
|
|
39090
|
-
|
|
39091
|
-
|
|
39092
|
-
|
|
39093
|
-
|
|
39094
|
-
|
|
39095
|
-
|
|
39278
|
+
const rows = this.getFilteredData();
|
|
39279
|
+
const grandTotal = this.calculateTotalConsumption();
|
|
39280
|
+
const toDevice = (r) => {
|
|
39281
|
+
const base = {
|
|
39282
|
+
labelOrName: r.name,
|
|
39283
|
+
name: r.name,
|
|
39284
|
+
deviceIdentifier: r.identifier,
|
|
39285
|
+
val: this.isTemperature && r.noData ? null : r.consumption,
|
|
39286
|
+
perc: this.isTemperature ? void 0 : grandTotal > 0 ? r.consumption / grandTotal * 100 : 0
|
|
39287
|
+
};
|
|
39288
|
+
if (!this.isTemperature) return base;
|
|
39289
|
+
const off = r.id && !r.noData ? this.temperatureOffsetFor(r.id) : 0;
|
|
39290
|
+
return {
|
|
39291
|
+
...base,
|
|
39292
|
+
extraCells: [this.fmtTemp(r.noData ? null : r.min), this.fmtTemp(r.noData ? null : r.max)],
|
|
39293
|
+
...off ? { nameNote: `offset ${off > 0 ? "+" : "\u2212"}${fmtPt(Math.abs(off))} \xB0C` } : {}
|
|
39294
|
+
};
|
|
39295
|
+
};
|
|
39296
|
+
const out = [];
|
|
39297
|
+
if (rows.some((r) => r.groupLabel)) {
|
|
39298
|
+
const order = [];
|
|
39299
|
+
const byGroup = /* @__PURE__ */ new Map();
|
|
39300
|
+
for (const r of rows) {
|
|
39301
|
+
const g = r.groupLabel || "\u2014";
|
|
39302
|
+
if (!byGroup.has(g)) {
|
|
39303
|
+
byGroup.set(g, []);
|
|
39304
|
+
order.push(g);
|
|
39305
|
+
}
|
|
39306
|
+
byGroup.get(g).push(r);
|
|
39307
|
+
}
|
|
39308
|
+
for (const g of order) {
|
|
39309
|
+
const items = byGroup.get(g);
|
|
39310
|
+
out.push({ groupHeader: `${g.toUpperCase()} \xB7 ${this.groupSummaryText(items)}` });
|
|
39311
|
+
items.forEach((r) => out.push(toDevice(r)));
|
|
39312
|
+
}
|
|
39313
|
+
} else {
|
|
39314
|
+
rows.forEach((r) => out.push(toDevice(r)));
|
|
39315
|
+
}
|
|
39316
|
+
return out;
|
|
39317
|
+
}
|
|
39318
|
+
// Mesmo texto do cabeçalho de grupo da tabela
|
|
39319
|
+
groupSummaryText(items) {
|
|
39320
|
+
if (this.isTemperature) {
|
|
39321
|
+
const withData = items.filter((r) => !r.noData);
|
|
39322
|
+
const avg = withData.length ? withData.reduce((s, r) => s + r.consumption, 0) / withData.length : null;
|
|
39323
|
+
return `${items.length} sensores \xB7 m\xE9dia ${this.fmtTemp(avg)} \xB0C`;
|
|
39324
|
+
}
|
|
39325
|
+
const total = items.reduce((s, r) => s + r.consumption, 0);
|
|
39326
|
+
return `${items.length} dispositivos \xB7 ${fmtPt(total)} ${this.domainConfig.unit}`;
|
|
39096
39327
|
}
|
|
39097
|
-
// Colunas do PDF/XLS
|
|
39328
|
+
// Colunas do PDF/XLS = colunas da tela. Temperatura: Sensor | Média | Mín | Máx (sem %).
|
|
39098
39329
|
exportColumns() {
|
|
39099
39330
|
if (!this.isTemperature) return null;
|
|
39100
39331
|
return {
|
|
39101
39332
|
nameLabel: "Sensor",
|
|
39102
|
-
valueLabel: "
|
|
39333
|
+
valueLabel: "M\xE9dia (\xB0C)",
|
|
39103
39334
|
hidePerc: true,
|
|
39104
|
-
hideIdentifier: !this.showIdentifierColumn()
|
|
39335
|
+
hideIdentifier: !this.showIdentifierColumn(),
|
|
39336
|
+
extraColumns: [
|
|
39337
|
+
{ label: "M\xEDn (\xB0C)", pdfW: 35 },
|
|
39338
|
+
{ label: "M\xE1x (\xB0C)", pdfW: 35 }
|
|
39339
|
+
],
|
|
39340
|
+
emptyValueText: "Sem leitura",
|
|
39341
|
+
countLabel: "sensor(es)"
|
|
39105
39342
|
};
|
|
39106
39343
|
}
|
|
39107
39344
|
// Accent hex da paleta do dashboard (tema efetivo) para o PDF/XLS — cai no
|
|
@@ -39115,26 +39352,38 @@ var AllReportModal = class {
|
|
|
39115
39352
|
const vars = typeof theme.cssVars === "function" ? theme.cssVars() : theme;
|
|
39116
39353
|
return vars?.["--myio-brand-700"];
|
|
39117
39354
|
}
|
|
39355
|
+
// Gráfico da página final do PDF = o painel da direita da tela
|
|
39356
|
+
async buildExportChart() {
|
|
39357
|
+
if (this.isTemperature) {
|
|
39358
|
+
const png = renderTemperatureBarsPng({
|
|
39359
|
+
items: this.getFilteredData().map((r) => ({ label: r.name, value: r.noData ? null : r.consumption })),
|
|
39360
|
+
ideal: this.temperatureIdealRange,
|
|
39361
|
+
accent: this.resolveAccentHex()
|
|
39362
|
+
});
|
|
39363
|
+
return png ? { ...png, title: "Temperatura m\xE9dia por sensor" } : null;
|
|
39364
|
+
}
|
|
39365
|
+
const chartPng = await this.participationChart?.toPngDataUrl?.().catch(() => null);
|
|
39366
|
+
return chartPng ? { ...chartPng, title: "Participa\xE7\xE3o por Dispositivo" } : null;
|
|
39367
|
+
}
|
|
39118
39368
|
// PDF export — layout premium do grid + paleta do dashboard + faixa de KPIs +
|
|
39119
|
-
// página dedicada com o gráfico
|
|
39369
|
+
// página dedicada com o mesmo gráfico do painel da modal.
|
|
39120
39370
|
async exportPDF() {
|
|
39121
39371
|
if (!this.data.length) return;
|
|
39122
|
-
const chartPng = await this.participationChart?.toPngDataUrl?.().catch(() => null);
|
|
39123
39372
|
exportGridPdf(
|
|
39124
39373
|
this.buildExportDevices(),
|
|
39125
39374
|
this.resolveTitle(),
|
|
39126
39375
|
this.domainConfig.unit,
|
|
39127
39376
|
this.exportPeriod,
|
|
39128
|
-
null,
|
|
39377
|
+
this.params.customerName || null,
|
|
39129
39378
|
{
|
|
39130
39379
|
accentColor: this.resolveAccentHex(),
|
|
39131
39380
|
kpis: this.computeKpis(),
|
|
39132
39381
|
columns: this.exportColumns(),
|
|
39133
|
-
chartImage:
|
|
39382
|
+
chartImage: await this.buildExportChart()
|
|
39134
39383
|
}
|
|
39135
39384
|
);
|
|
39136
39385
|
}
|
|
39137
|
-
// XLS export (XML Spreadsheet) —
|
|
39386
|
+
// XLS export (XML Spreadsheet) — mesma tabela + KPIs da tela.
|
|
39138
39387
|
exportXLS() {
|
|
39139
39388
|
if (!this.data.length) return;
|
|
39140
39389
|
exportGridXls(
|
|
@@ -39142,8 +39391,8 @@ var AllReportModal = class {
|
|
|
39142
39391
|
this.resolveTitle(),
|
|
39143
39392
|
this.domainConfig.unit,
|
|
39144
39393
|
this.exportPeriod,
|
|
39145
|
-
null,
|
|
39146
|
-
{ accentColor: this.resolveAccentHex(), columns: this.exportColumns() }
|
|
39394
|
+
this.params.customerName || null,
|
|
39395
|
+
{ accentColor: this.resolveAccentHex(), columns: this.exportColumns(), kpis: this.computeKpis() }
|
|
39147
39396
|
);
|
|
39148
39397
|
}
|
|
39149
39398
|
async fetchCustomerTotals(startISO, endISO) {
|
|
@@ -79616,7 +79865,10 @@ async function openTemperatureModal(params) {
|
|
|
79616
79865
|
document.body.appendChild(modalContainer);
|
|
79617
79866
|
renderModal3(modalContainer, state6, modalId);
|
|
79618
79867
|
try {
|
|
79619
|
-
state6.data =
|
|
79868
|
+
state6.data = discardInvalid(
|
|
79869
|
+
await (state6.dataFetcher ? state6.dataFetcher(state6.startTs, state6.endTs) : fetchTemperatureData(state6.token, state6.deviceId, state6.startTs, state6.endTs)),
|
|
79870
|
+
state6
|
|
79871
|
+
);
|
|
79620
79872
|
state6.stats = calculateStats(state6.data, state6.clampRange, state6.temperatureOffset);
|
|
79621
79873
|
state6.isLoading = false;
|
|
79622
79874
|
renderModal3(modalContainer, state6, modalId);
|
|
@@ -79641,7 +79893,10 @@ async function openTemperatureModal(params) {
|
|
|
79641
79893
|
state6.isLoading = true;
|
|
79642
79894
|
renderModal3(modalContainer, state6, modalId);
|
|
79643
79895
|
try {
|
|
79644
|
-
state6.data =
|
|
79896
|
+
state6.data = discardInvalid(
|
|
79897
|
+
await (state6.dataFetcher ? state6.dataFetcher(state6.startTs, state6.endTs) : fetchTemperatureData(state6.token, state6.deviceId, state6.startTs, state6.endTs)),
|
|
79898
|
+
state6
|
|
79899
|
+
);
|
|
79645
79900
|
state6.stats = calculateStats(state6.data, state6.clampRange, state6.temperatureOffset);
|
|
79646
79901
|
state6.isLoading = false;
|
|
79647
79902
|
renderModal3(modalContainer, state6, modalId);
|
|
@@ -80269,7 +80524,10 @@ async function setupEventListeners3(container, state6, modalId, onClose) {
|
|
|
80269
80524
|
state6.dateRangePicker = null;
|
|
80270
80525
|
renderModal3(container, state6, modalId);
|
|
80271
80526
|
try {
|
|
80272
|
-
state6.data =
|
|
80527
|
+
state6.data = discardInvalid(
|
|
80528
|
+
await (state6.dataFetcher ? state6.dataFetcher(state6.startTs, state6.endTs) : fetchTemperatureData(state6.token, state6.deviceId, state6.startTs, state6.endTs)),
|
|
80529
|
+
state6
|
|
80530
|
+
);
|
|
80273
80531
|
state6.stats = calculateStats(state6.data, state6.clampRange, state6.temperatureOffset);
|
|
80274
80532
|
state6.isLoading = false;
|
|
80275
80533
|
renderModal3(container, state6, modalId);
|
|
@@ -80283,19 +80541,64 @@ async function setupEventListeners3(container, state6, modalId, onClose) {
|
|
|
80283
80541
|
}
|
|
80284
80542
|
});
|
|
80285
80543
|
}
|
|
80544
|
+
function discardInvalid(data, state6) {
|
|
80545
|
+
const off = Number(state6.temperatureOffset) || 0;
|
|
80546
|
+
return data.filter((p) => {
|
|
80547
|
+
const v = Number(p.value) + off;
|
|
80548
|
+
return Number.isFinite(v) && v >= state6.clampRange.min && v <= state6.clampRange.max;
|
|
80549
|
+
});
|
|
80550
|
+
}
|
|
80286
80551
|
function exportCsv(state6) {
|
|
80287
80552
|
if (state6.data.length === 0) return;
|
|
80288
|
-
const
|
|
80289
|
-
const
|
|
80290
|
-
|
|
80291
|
-
|
|
80292
|
-
|
|
80293
|
-
|
|
80294
|
-
|
|
80295
|
-
|
|
80296
|
-
|
|
80297
|
-
state6.
|
|
80298
|
-
);
|
|
80553
|
+
const off = Number(state6.temperatureOffset) || 0;
|
|
80554
|
+
const n2 = (v) => v.toFixed(2).replace(".", ",");
|
|
80555
|
+
const q = (v) => `"${String(v).replace(/"/g, '""')}"`;
|
|
80556
|
+
const dt = (ts) => new Date(ts).toLocaleString("pt-BR", {
|
|
80557
|
+
day: "2-digit",
|
|
80558
|
+
month: "2-digit",
|
|
80559
|
+
year: "numeric",
|
|
80560
|
+
hour: "2-digit",
|
|
80561
|
+
minute: "2-digit",
|
|
80562
|
+
timeZone: state6.timezone || "America/Sao_Paulo"
|
|
80563
|
+
});
|
|
80564
|
+
const fmtD = (ts) => new Date(ts).toLocaleDateString("pt-BR", { timeZone: state6.timezone || "America/Sao_Paulo" });
|
|
80565
|
+
const filtered = filterByDayPeriods(state6.data, state6.selectedPeriods);
|
|
80566
|
+
const lines = [
|
|
80567
|
+
["Relat\xF3rio", `${state6.label} - Hist\xF3rico de Temperatura`],
|
|
80568
|
+
...state6.deviceName ? [["Dispositivo (TB)", state6.deviceName]] : [],
|
|
80569
|
+
...state6.customerName ? [["Cliente", state6.customerName]] : [],
|
|
80570
|
+
["Per\xEDodo", `${fmtD(state6.startTs)} \u2014 ${fmtD(state6.endTs)}`],
|
|
80571
|
+
["Granularidade", state6.granularity === "hour" ? "Hora (m\xE9dia da hora fechada)" : "Dia (m\xE9dia do dia)"],
|
|
80572
|
+
["Per\xEDodos do dia", getSelectedPeriodsLabel(state6.selectedPeriods)],
|
|
80573
|
+
...off !== 0 ? [["Offset aplicado (\xB0C)", n2(off)]] : [],
|
|
80574
|
+
["Gerado em", (/* @__PURE__ */ new Date()).toLocaleString("pt-BR")],
|
|
80575
|
+
[],
|
|
80576
|
+
["Temperatura atual (\xB0C)", state6.currentTemperature !== null ? n2(state6.currentTemperature) : "N/A"],
|
|
80577
|
+
["M\xE9dia do per\xEDodo (\xB0C)", state6.stats.count > 0 ? n2(state6.stats.avg) : "N/A"],
|
|
80578
|
+
["M\xEDnima (\xB0C)", state6.stats.count > 0 ? n2(state6.stats.min) : "N/A"],
|
|
80579
|
+
["M\xE1xima (\xB0C)", state6.stats.count > 0 ? n2(state6.stats.max) : "N/A"],
|
|
80580
|
+
["Leituras", String(state6.stats.count)],
|
|
80581
|
+
[]
|
|
80582
|
+
];
|
|
80583
|
+
if (state6.granularity === "hour") {
|
|
80584
|
+
lines.push(["Data/Hora", "Temperatura (\xB0C)"]);
|
|
80585
|
+
aggregateByHour(filtered, state6.clampRange, off).forEach((p) => lines.push([dt(p.ts), n2(Number(p.value))]));
|
|
80586
|
+
} else {
|
|
80587
|
+
lines.push(["Dia", "M\xE9dia (\xB0C)", "M\xEDn (\xB0C)", "M\xE1x (\xB0C)", "Leituras"]);
|
|
80588
|
+
aggregateByDay(filtered, state6.clampRange, off).forEach(
|
|
80589
|
+
(d) => lines.push([fmtD(d.dateTs), n2(d.avg), n2(d.min), n2(d.max), String(d.count)])
|
|
80590
|
+
);
|
|
80591
|
+
}
|
|
80592
|
+
const csv = "\uFEFF" + lines.map((r) => r.map(q).join(";")).join("\r\n");
|
|
80593
|
+
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
|
|
80594
|
+
const url = URL.createObjectURL(blob);
|
|
80595
|
+
const a = document.createElement("a");
|
|
80596
|
+
a.href = url;
|
|
80597
|
+
a.download = `temperatura_${state6.label.replace(/\s+/g, "_")}_${state6.granularity === "hour" ? "1h" : "1d"}_${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.csv`;
|
|
80598
|
+
document.body.appendChild(a);
|
|
80599
|
+
a.click();
|
|
80600
|
+
document.body.removeChild(a);
|
|
80601
|
+
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
80299
80602
|
}
|
|
80300
80603
|
function buildHeaderTitle(state6) {
|
|
80301
80604
|
const base = `${escHtml3(state6.label)} - Hist\xF3rico de Temperatura`;
|
|
@@ -81202,43 +81505,63 @@ function mountCmpFooter(container, state6, modalId) {
|
|
|
81202
81505
|
_cmpFooters.set(modalId, footer);
|
|
81203
81506
|
}
|
|
81204
81507
|
function exportComparisonCSV2(state6) {
|
|
81205
|
-
const
|
|
81206
|
-
const
|
|
81207
|
-
const
|
|
81208
|
-
|
|
81209
|
-
|
|
81210
|
-
|
|
81211
|
-
|
|
81212
|
-
|
|
81213
|
-
|
|
81214
|
-
|
|
81215
|
-
csvContent += "\n";
|
|
81216
|
-
csvContent += "Estat\xEDsticas por Sensor:\n";
|
|
81217
|
-
csvContent += "Sensor,M\xE9dia (\xB0C),Min (\xB0C),Max (\xB0C),Leituras\n";
|
|
81218
|
-
state6.deviceData.forEach((dd) => {
|
|
81219
|
-
csvContent += `"${dd.device.label}",${dd.stats.avg.toFixed(2)},${dd.stats.min.toFixed(2)},${dd.stats.max.toFixed(2)},${dd.stats.count}
|
|
81220
|
-
`;
|
|
81508
|
+
const n2 = (v) => v.toFixed(2).replace(".", ",");
|
|
81509
|
+
const q = (v) => `"${String(v).replace(/"/g, '""')}"`;
|
|
81510
|
+
const fmtD = (ts) => new Date(ts).toLocaleDateString("pt-BR", { timeZone: "America/Sao_Paulo" });
|
|
81511
|
+
const dt = (ts) => new Date(ts).toLocaleString("pt-BR", {
|
|
81512
|
+
day: "2-digit",
|
|
81513
|
+
month: "2-digit",
|
|
81514
|
+
year: "numeric",
|
|
81515
|
+
hour: "2-digit",
|
|
81516
|
+
minute: "2-digit",
|
|
81517
|
+
timeZone: "America/Sao_Paulo"
|
|
81221
81518
|
});
|
|
81222
|
-
|
|
81223
|
-
|
|
81224
|
-
|
|
81225
|
-
|
|
81226
|
-
|
|
81227
|
-
|
|
81228
|
-
|
|
81229
|
-
|
|
81230
|
-
|
|
81231
|
-
|
|
81519
|
+
const offTxt = (d) => {
|
|
81520
|
+
const o = Number(d.temperatureOffset) || 0;
|
|
81521
|
+
return o !== 0 ? n2(o) : "";
|
|
81522
|
+
};
|
|
81523
|
+
const lines = [
|
|
81524
|
+
["Relat\xF3rio", `Compara\xE7\xE3o de Temperatura - ${state6.devices.length} sensores`],
|
|
81525
|
+
...state6.customerName ? [["Cliente", state6.customerName]] : [],
|
|
81526
|
+
["Per\xEDodo", `${fmtD(state6.startTs)} \u2014 ${fmtD(state6.endTs)}`],
|
|
81527
|
+
["Granularidade", state6.granularity === "hour" ? "Hora (m\xE9dia da hora fechada)" : "Dia (m\xE9dia do dia)"],
|
|
81528
|
+
["Per\xEDodos do dia", getSelectedPeriodsLabel(state6.selectedPeriods)],
|
|
81529
|
+
["Gerado em", (/* @__PURE__ */ new Date()).toLocaleString("pt-BR")],
|
|
81530
|
+
[],
|
|
81531
|
+
["Sensor", "Offset (\xB0C)", "M\xE9dia (\xB0C)", "M\xEDn (\xB0C)", "M\xE1x (\xB0C)", "Leituras"],
|
|
81532
|
+
...state6.deviceData.map((dd) => [
|
|
81533
|
+
dd.device.label,
|
|
81534
|
+
offTxt(dd.device),
|
|
81535
|
+
dd.stats.count > 0 ? n2(dd.stats.avg) : "N/A",
|
|
81536
|
+
dd.stats.count > 0 ? n2(dd.stats.min) : "N/A",
|
|
81537
|
+
dd.stats.count > 0 ? n2(dd.stats.max) : "N/A",
|
|
81538
|
+
String(dd.stats.count)
|
|
81539
|
+
]),
|
|
81540
|
+
[]
|
|
81541
|
+
];
|
|
81542
|
+
const perDevice = state6.deviceData.map((dd) => {
|
|
81543
|
+
const filtered = filterByDayPeriods(dd.data, state6.selectedPeriods);
|
|
81544
|
+
const pts = state6.granularity === "hour" ? aggregateByHour(filtered, state6.clampRange).map((p) => [p.ts, Number(p.value)]) : aggregateByDay(filtered, state6.clampRange).map((d) => [d.dateTs, d.avg]);
|
|
81545
|
+
return new Map(pts);
|
|
81232
81546
|
});
|
|
81233
|
-
const
|
|
81547
|
+
const allTs = [...new Set(perDevice.flatMap((m) => [...m.keys()]))].sort((a, b) => a - b);
|
|
81548
|
+
lines.push([state6.granularity === "hour" ? "Data/Hora" : "Dia", ...state6.deviceData.map((dd) => `${dd.device.label} (\xB0C)`)]);
|
|
81549
|
+
allTs.forEach(
|
|
81550
|
+
(ts) => lines.push([
|
|
81551
|
+
state6.granularity === "hour" ? dt(ts) : fmtD(ts),
|
|
81552
|
+
...perDevice.map((m) => m.has(ts) ? n2(m.get(ts)) : "")
|
|
81553
|
+
])
|
|
81554
|
+
);
|
|
81555
|
+
const csv = "\uFEFF" + lines.map((r) => r.map(q).join(";")).join("\r\n");
|
|
81556
|
+
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
|
|
81234
81557
|
const url = URL.createObjectURL(blob);
|
|
81235
81558
|
const link = document.createElement("a");
|
|
81236
81559
|
link.href = url;
|
|
81237
|
-
link.download = `comparacao_temperatura_${
|
|
81560
|
+
link.download = `comparacao_temperatura_${state6.granularity === "hour" ? "1h" : "1d"}_${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.csv`;
|
|
81238
81561
|
document.body.appendChild(link);
|
|
81239
81562
|
link.click();
|
|
81240
81563
|
document.body.removeChild(link);
|
|
81241
|
-
URL.revokeObjectURL(url);
|
|
81564
|
+
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
81242
81565
|
}
|
|
81243
81566
|
|
|
81244
81567
|
// src/components/temperature/TemperatureSettingsModal.ts
|