myio-js-library 0.1.161 → 0.1.162
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 +1869 -14
- package/dist/index.d.cts +889 -2
- package/dist/index.js +1848 -14
- package/dist/myio-js-library.umd.js +1847 -13
- package/dist/myio-js-library.umd.min.js +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -569,9 +569,16 @@ var init_template_card = __esm({
|
|
|
569
569
|
var index_exports = {};
|
|
570
570
|
__export(index_exports, {
|
|
571
571
|
CHART_COLORS: () => CHART_COLORS,
|
|
572
|
+
CONSUMPTION_CHART_COLORS: () => DEFAULT_COLORS,
|
|
573
|
+
CONSUMPTION_CHART_DEFAULTS: () => DEFAULT_CONFIG,
|
|
574
|
+
CONSUMPTION_THEME_COLORS: () => THEME_COLORS,
|
|
572
575
|
ConnectionStatusType: () => ConnectionStatusType,
|
|
573
576
|
DEFAULT_CLAMP_RANGE: () => DEFAULT_CLAMP_RANGE,
|
|
574
577
|
DeviceStatusType: () => DeviceStatusType,
|
|
578
|
+
EXPORT_DEFAULT_COLORS: () => EXPORT_DEFAULT_COLORS,
|
|
579
|
+
EXPORT_DOMAIN_ICONS: () => EXPORT_DOMAIN_ICONS,
|
|
580
|
+
EXPORT_DOMAIN_LABELS: () => EXPORT_DOMAIN_LABELS,
|
|
581
|
+
EXPORT_DOMAIN_UNITS: () => EXPORT_DOMAIN_UNITS,
|
|
575
582
|
MyIOChartModal: () => MyIOChartModal,
|
|
576
583
|
MyIODraggableCard: () => MyIODraggableCard,
|
|
577
584
|
MyIOSelectionStore: () => MyIOSelectionStore,
|
|
@@ -583,11 +590,13 @@ __export(index_exports, {
|
|
|
583
590
|
averageByDay: () => averageByDay,
|
|
584
591
|
buildListItemsThingsboardByUniqueDatasource: () => buildListItemsThingsboardByUniqueDatasource,
|
|
585
592
|
buildMyioIngestionAuth: () => buildMyioIngestionAuth,
|
|
593
|
+
buildTemplateExport: () => buildTemplateExport,
|
|
586
594
|
buildWaterReportCSV: () => buildWaterReportCSV,
|
|
587
595
|
buildWaterStoresCSV: () => buildWaterStoresCSV,
|
|
588
596
|
calcDeltaPercent: () => calcDeltaPercent,
|
|
589
597
|
calculateDeviceStatus: () => calculateDeviceStatus,
|
|
590
598
|
calculateDeviceStatusWithRanges: () => calculateDeviceStatusWithRanges,
|
|
599
|
+
calculateExportStats: () => calculateStats2,
|
|
591
600
|
calculateStats: () => calculateStats,
|
|
592
601
|
clampTemperature: () => clampTemperature,
|
|
593
602
|
classify: () => classify,
|
|
@@ -595,8 +604,11 @@ __export(index_exports, {
|
|
|
595
604
|
classifyWaterLabels: () => classifyWaterLabels,
|
|
596
605
|
clearAllAuthCaches: () => clearAllAuthCaches,
|
|
597
606
|
connectionStatusIcons: () => connectionStatusIcons,
|
|
607
|
+
createConsumption7DaysChart: () => createConsumption7DaysChart,
|
|
608
|
+
createConsumptionModal: () => createConsumptionModal,
|
|
598
609
|
createDateRangePicker: () => createDateRangePicker2,
|
|
599
610
|
createInputDateRangePickerInsideDIV: () => createInputDateRangePickerInsideDIV,
|
|
611
|
+
createModalHeader: () => createModalHeader,
|
|
600
612
|
decodePayload: () => decodePayload,
|
|
601
613
|
decodePayloadBase64Xor: () => decodePayloadBase64Xor,
|
|
602
614
|
detectDeviceType: () => detectDeviceType,
|
|
@@ -610,6 +622,7 @@ __export(index_exports, {
|
|
|
610
622
|
fetchThingsboardCustomerAttrsFromStorage: () => fetchThingsboardCustomerAttrsFromStorage,
|
|
611
623
|
fetchThingsboardCustomerServerScopeAttrs: () => fetchThingsboardCustomerServerScopeAttrs,
|
|
612
624
|
findValue: () => findValue,
|
|
625
|
+
findValueWithDefault: () => findValueWithDefault,
|
|
613
626
|
fmtPerc: () => fmtPerc,
|
|
614
627
|
fmtPercLegacy: () => fmtPerc2,
|
|
615
628
|
formatAllInSameUnit: () => formatAllInSameUnit,
|
|
@@ -617,18 +630,24 @@ __export(index_exports, {
|
|
|
617
630
|
formatDateForInput: () => formatDateForInput,
|
|
618
631
|
formatDateToYMD: () => formatDateToYMD,
|
|
619
632
|
formatDateWithTimezoneOffset: () => formatDateWithTimezoneOffset,
|
|
633
|
+
formatDuration: () => formatDuration,
|
|
620
634
|
formatEnergy: () => formatEnergy,
|
|
621
635
|
formatNumberReadable: () => formatNumberReadable,
|
|
636
|
+
formatRelativeTime: () => formatRelativeTime,
|
|
622
637
|
formatTankHeadFromCm: () => formatTankHeadFromCm,
|
|
623
638
|
formatTemperature: () => formatTemperature2,
|
|
639
|
+
formatWater: () => formatWater,
|
|
624
640
|
formatWaterByGroup: () => formatWaterByGroup,
|
|
625
641
|
formatWaterVolumeM3: () => formatWaterVolumeM3,
|
|
642
|
+
formatarDuracao: () => formatarDuracao,
|
|
643
|
+
generateExportFilename: () => generateFilename,
|
|
626
644
|
getAuthCacheStats: () => getAuthCacheStats,
|
|
627
645
|
getAvailableContexts: () => getAvailableContexts,
|
|
628
646
|
getConnectionStatusIcon: () => getConnectionStatusIcon,
|
|
629
647
|
getDateRangeArray: () => getDateRangeArray,
|
|
630
648
|
getDeviceStatusIcon: () => getDeviceStatusIcon,
|
|
631
649
|
getDeviceStatusInfo: () => getDeviceStatusInfo,
|
|
650
|
+
getModalHeaderStyles: () => getModalHeaderStyles,
|
|
632
651
|
getSaoPauloISOString: () => getSaoPauloISOString,
|
|
633
652
|
getSaoPauloISOStringFixed: () => getSaoPauloISOStringFixed,
|
|
634
653
|
getValueByDatakey: () => getValueByDatakey,
|
|
@@ -640,8 +659,10 @@ __export(index_exports, {
|
|
|
640
659
|
isValidConnectionStatus: () => isValidConnectionStatus,
|
|
641
660
|
isValidDeviceStatus: () => isValidDeviceStatus,
|
|
642
661
|
isWaterCategory: () => isWaterCategory,
|
|
662
|
+
mapConnectionStatus: () => mapConnectionStatus,
|
|
643
663
|
mapDeviceStatusToCardStatus: () => mapDeviceStatusToCardStatus,
|
|
644
664
|
mapDeviceToConnectionStatus: () => mapDeviceToConnectionStatus,
|
|
665
|
+
myioExportData: () => myioExportData,
|
|
645
666
|
normalizeRecipients: () => normalizeRecipients,
|
|
646
667
|
numbers: () => numbers_exports,
|
|
647
668
|
openDashboardPopup: () => openDashboardPopup,
|
|
@@ -752,6 +773,10 @@ function formatNumberReadable(value, locale = "pt-BR", minimumFractionDigits = 2
|
|
|
752
773
|
}
|
|
753
774
|
|
|
754
775
|
// src/format/water.ts
|
|
776
|
+
function formatWater(value) {
|
|
777
|
+
const num = Number(value) || 0;
|
|
778
|
+
return `${num.toFixed(2)} m\xB3`;
|
|
779
|
+
}
|
|
755
780
|
function formatWaterVolumeM3(value, locale = "pt-BR") {
|
|
756
781
|
if (value === null || value === void 0 || isNaN(value)) {
|
|
757
782
|
return "-";
|
|
@@ -829,6 +854,76 @@ function formatAllInSameWaterUnit(values) {
|
|
|
829
854
|
};
|
|
830
855
|
}
|
|
831
856
|
|
|
857
|
+
// src/format/time.ts
|
|
858
|
+
function formatRelativeTime(timestamp) {
|
|
859
|
+
if (!timestamp || timestamp <= 0) {
|
|
860
|
+
return "\u2014";
|
|
861
|
+
}
|
|
862
|
+
const now = Date.now();
|
|
863
|
+
const diffSeconds = Math.round((now - timestamp) / 1e3);
|
|
864
|
+
if (diffSeconds < 10) {
|
|
865
|
+
return "agora";
|
|
866
|
+
}
|
|
867
|
+
if (diffSeconds < 60) {
|
|
868
|
+
return `h\xE1 ${diffSeconds}s`;
|
|
869
|
+
}
|
|
870
|
+
const diffMinutes = Math.round(diffSeconds / 60);
|
|
871
|
+
if (diffMinutes === 1) {
|
|
872
|
+
return "h\xE1 1 min";
|
|
873
|
+
}
|
|
874
|
+
if (diffMinutes < 60) {
|
|
875
|
+
return `h\xE1 ${diffMinutes} mins`;
|
|
876
|
+
}
|
|
877
|
+
const diffHours = Math.round(diffMinutes / 60);
|
|
878
|
+
if (diffHours === 1) {
|
|
879
|
+
return "h\xE1 1 hora";
|
|
880
|
+
}
|
|
881
|
+
if (diffHours < 24) {
|
|
882
|
+
return `h\xE1 ${diffHours} horas`;
|
|
883
|
+
}
|
|
884
|
+
const diffDays = Math.round(diffHours / 24);
|
|
885
|
+
if (diffDays === 1) {
|
|
886
|
+
return "ontem";
|
|
887
|
+
}
|
|
888
|
+
if (diffDays <= 30) {
|
|
889
|
+
return `h\xE1 ${diffDays} dias`;
|
|
890
|
+
}
|
|
891
|
+
return new Date(timestamp).toLocaleDateString("pt-BR");
|
|
892
|
+
}
|
|
893
|
+
function formatarDuracao(ms) {
|
|
894
|
+
if (typeof ms !== "number" || ms < 0 || !isFinite(ms)) {
|
|
895
|
+
return "0s";
|
|
896
|
+
}
|
|
897
|
+
if (ms === 0) {
|
|
898
|
+
return "0s";
|
|
899
|
+
}
|
|
900
|
+
const segundos = Math.floor(ms / 1e3 % 60);
|
|
901
|
+
const minutos = Math.floor(ms / (1e3 * 60) % 60);
|
|
902
|
+
const horas = Math.floor(ms / (1e3 * 60 * 60) % 24);
|
|
903
|
+
const dias = Math.floor(ms / (1e3 * 60 * 60 * 24));
|
|
904
|
+
const parts = [];
|
|
905
|
+
if (dias > 0) {
|
|
906
|
+
parts.push(`${dias}d`);
|
|
907
|
+
if (horas > 0) {
|
|
908
|
+
parts.push(`${horas}h`);
|
|
909
|
+
}
|
|
910
|
+
} else if (horas > 0) {
|
|
911
|
+
parts.push(`${horas}h`);
|
|
912
|
+
if (minutos > 0) {
|
|
913
|
+
parts.push(`${minutos}m`);
|
|
914
|
+
}
|
|
915
|
+
} else if (minutos > 0) {
|
|
916
|
+
parts.push(`${minutos}m`);
|
|
917
|
+
if (segundos > 0) {
|
|
918
|
+
parts.push(`${segundos}s`);
|
|
919
|
+
}
|
|
920
|
+
} else {
|
|
921
|
+
parts.push(`${segundos}s`);
|
|
922
|
+
}
|
|
923
|
+
return parts.length > 0 ? parts.join(" ") : "0s";
|
|
924
|
+
}
|
|
925
|
+
var formatDuration = formatarDuracao;
|
|
926
|
+
|
|
832
927
|
// src/date/ymd.ts
|
|
833
928
|
function formatDateToYMD(date) {
|
|
834
929
|
if (!date) {
|
|
@@ -1337,6 +1432,11 @@ function findValue(data, keyOrPath, legacyDataKey) {
|
|
|
1337
1432
|
}
|
|
1338
1433
|
return getValueByDatakey(data, keyOrPath);
|
|
1339
1434
|
}
|
|
1435
|
+
function findValueWithDefault(values, key, defaultValue = null) {
|
|
1436
|
+
if (!Array.isArray(values)) return defaultValue;
|
|
1437
|
+
const found = values.find((v) => v.key === key || v.dataType === key);
|
|
1438
|
+
return found ? found.value : defaultValue;
|
|
1439
|
+
}
|
|
1340
1440
|
|
|
1341
1441
|
// src/utils/deviceStatus.js
|
|
1342
1442
|
var DeviceStatusType = {
|
|
@@ -1393,6 +1493,16 @@ function mapDeviceToConnectionStatus(deviceStatus) {
|
|
|
1393
1493
|
}
|
|
1394
1494
|
return ConnectionStatusType.CONNECTED;
|
|
1395
1495
|
}
|
|
1496
|
+
function mapConnectionStatus(rawStatus) {
|
|
1497
|
+
const statusLower = String(rawStatus || "").toLowerCase().trim();
|
|
1498
|
+
if (statusLower === "online" || statusLower === "ok" || statusLower === "running") {
|
|
1499
|
+
return "online";
|
|
1500
|
+
}
|
|
1501
|
+
if (statusLower === "waiting" || statusLower === "connecting" || statusLower === "pending") {
|
|
1502
|
+
return "waiting";
|
|
1503
|
+
}
|
|
1504
|
+
return "offline";
|
|
1505
|
+
}
|
|
1396
1506
|
function mapDeviceStatusToCardStatus(deviceStatus) {
|
|
1397
1507
|
const statusMap = {
|
|
1398
1508
|
[DeviceStatusType.POWER_ON]: "ok",
|
|
@@ -7806,14 +7916,14 @@ async function openRealTimeTelemetryModal(params) {
|
|
|
7806
7916
|
return `${value.toFixed(config.decimals)} ${config.unit}`;
|
|
7807
7917
|
}
|
|
7808
7918
|
function initializeChart() {
|
|
7809
|
-
const
|
|
7810
|
-
if (!
|
|
7919
|
+
const Chart2 = window.Chart;
|
|
7920
|
+
if (!Chart2) {
|
|
7811
7921
|
console.warn("[RealTimeTelemetry] Chart.js not loaded");
|
|
7812
7922
|
return;
|
|
7813
7923
|
}
|
|
7814
7924
|
chartContainer.style.display = "block";
|
|
7815
7925
|
const config = TELEMETRY_CONFIG[selectedChartKey] || { label: selectedChartKey, unit: "" };
|
|
7816
|
-
chart = new
|
|
7926
|
+
chart = new Chart2(chartCanvas, {
|
|
7817
7927
|
type: "line",
|
|
7818
7928
|
data: {
|
|
7819
7929
|
datasets: [{
|
|
@@ -10582,7 +10692,7 @@ async function openDemandModal(params) {
|
|
|
10582
10692
|
params.timezoneOffset
|
|
10583
10693
|
);
|
|
10584
10694
|
if (!newChartData.isEmpty && chart && chartData) {
|
|
10585
|
-
const
|
|
10695
|
+
const Chart2 = window.Chart;
|
|
10586
10696
|
newChartData.series.forEach((newSeries, seriesIndex) => {
|
|
10587
10697
|
if (newSeries.points.length > 0 && chart.data.datasets[seriesIndex]) {
|
|
10588
10698
|
newSeries.points.forEach((point) => {
|
|
@@ -10811,8 +10921,8 @@ async function openDemandModal(params) {
|
|
|
10811
10921
|
peakEl.textContent = `${strings.maximum}: ${peak.formattedValue} kW ${peak.key ? `(${peak.key}) ` : ""}${strings.at} ${dateStr}`;
|
|
10812
10922
|
peakEl.style.display = "block";
|
|
10813
10923
|
}
|
|
10814
|
-
const
|
|
10815
|
-
|
|
10924
|
+
const Chart2 = window.Chart;
|
|
10925
|
+
Chart2.register(window.ChartZoom);
|
|
10816
10926
|
if (chart) {
|
|
10817
10927
|
chart.data.datasets = chartData.series.map((series) => ({
|
|
10818
10928
|
label: series.label,
|
|
@@ -10864,7 +10974,7 @@ async function openDemandModal(params) {
|
|
|
10864
10974
|
};
|
|
10865
10975
|
chart.update();
|
|
10866
10976
|
} else {
|
|
10867
|
-
chart = new
|
|
10977
|
+
chart = new Chart2(chartCanvas, {
|
|
10868
10978
|
type: "line",
|
|
10869
10979
|
data: {
|
|
10870
10980
|
datasets: chartData.series.map((series) => ({
|
|
@@ -19037,8 +19147,8 @@ function openGoalsPanel(params) {
|
|
|
19037
19147
|
<div class="myio-goals-progress-fill" style="width: ${Math.min(progress, 100)}%"></div>
|
|
19038
19148
|
</div>
|
|
19039
19149
|
<div class="myio-goals-progress-text">
|
|
19040
|
-
<span>${
|
|
19041
|
-
<span>${
|
|
19150
|
+
<span>${formatNumber3(monthlySum, locale)} ${annual.unit}</span>
|
|
19151
|
+
<span>${formatNumber3(annual.total, locale)} ${annual.unit}</span>
|
|
19042
19152
|
</div>
|
|
19043
19153
|
|
|
19044
19154
|
<!-- Monthly Grid -->
|
|
@@ -19112,7 +19222,7 @@ function openGoalsPanel(params) {
|
|
|
19112
19222
|
<span>${assetData.label || assetId}</span>
|
|
19113
19223
|
</div>
|
|
19114
19224
|
<div class="myio-goals-asset-total">
|
|
19115
|
-
${
|
|
19225
|
+
${formatNumber3(assetData.annual?.total || 0, locale)} ${assetData.annual?.unit || "kWh"}
|
|
19116
19226
|
</div>
|
|
19117
19227
|
<button class="myio-goals-btn-icon" data-action="delete-asset" data-asset-id="${assetId}" aria-label="${i18n.deleteAsset}">
|
|
19118
19228
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
@@ -19427,7 +19537,7 @@ function openGoalsPanel(params) {
|
|
|
19427
19537
|
monthlySum += value;
|
|
19428
19538
|
}
|
|
19429
19539
|
if (monthlySum > annualTotal && annualTotal > 0) {
|
|
19430
|
-
errors.push(`${i18n.errorMonthlyExceedsAnnual} (${
|
|
19540
|
+
errors.push(`${i18n.errorMonthlyExceedsAnnual} (${formatNumber3(monthlySum, locale)} > ${formatNumber3(annualTotal, locale)})`);
|
|
19431
19541
|
}
|
|
19432
19542
|
}
|
|
19433
19543
|
return errors;
|
|
@@ -19518,8 +19628,8 @@ function openGoalsPanel(params) {
|
|
|
19518
19628
|
}
|
|
19519
19629
|
if (progressTexts.length === 2) {
|
|
19520
19630
|
const unit = document.getElementById("unit-select")?.value || "kWh";
|
|
19521
|
-
progressTexts[0].textContent = `${
|
|
19522
|
-
progressTexts[1].textContent = `${
|
|
19631
|
+
progressTexts[0].textContent = `${formatNumber3(monthlySum, locale)} ${unit}`;
|
|
19632
|
+
progressTexts[1].textContent = `${formatNumber3(annualTotal, locale)} ${unit}`;
|
|
19523
19633
|
}
|
|
19524
19634
|
}
|
|
19525
19635
|
function updateMonthlyUnits(unit) {
|
|
@@ -19617,7 +19727,7 @@ function openGoalsPanel(params) {
|
|
|
19617
19727
|
modal.addEventListener("keydown", handleTab);
|
|
19618
19728
|
firstElement.focus();
|
|
19619
19729
|
}
|
|
19620
|
-
function
|
|
19730
|
+
function formatNumber3(value, locale2) {
|
|
19621
19731
|
return new Intl.NumberFormat(locale2, {
|
|
19622
19732
|
minimumFractionDigits: 0,
|
|
19623
19733
|
maximumFractionDigits: 2
|
|
@@ -22567,12 +22677,1743 @@ function openTemperatureSettingsModal(params) {
|
|
|
22567
22677
|
});
|
|
22568
22678
|
return { destroy };
|
|
22569
22679
|
}
|
|
22680
|
+
|
|
22681
|
+
// src/components/ModalHeader/index.ts
|
|
22682
|
+
var DEFAULT_BG_COLOR = "#3e1a7d";
|
|
22683
|
+
var DEFAULT_TEXT_COLOR = "white";
|
|
22684
|
+
var DEFAULT_BORDER_RADIUS = "10px 10px 0 0";
|
|
22685
|
+
var EXPORT_FORMAT_LABELS = {
|
|
22686
|
+
csv: "CSV",
|
|
22687
|
+
xls: "Excel (XLS)",
|
|
22688
|
+
pdf: "PDF"
|
|
22689
|
+
};
|
|
22690
|
+
var EXPORT_FORMAT_ICONS = {
|
|
22691
|
+
csv: "\u{1F4C4}",
|
|
22692
|
+
xls: "\u{1F4CA}",
|
|
22693
|
+
pdf: "\u{1F4D1}"
|
|
22694
|
+
};
|
|
22695
|
+
function createModalHeader(config) {
|
|
22696
|
+
let currentTheme = config.theme || "light";
|
|
22697
|
+
let currentIsMaximized = config.isMaximized || false;
|
|
22698
|
+
let currentTitle = config.title;
|
|
22699
|
+
let themeBtn = null;
|
|
22700
|
+
let maximizeBtn = null;
|
|
22701
|
+
let closeBtn = null;
|
|
22702
|
+
let exportBtn = null;
|
|
22703
|
+
let exportDropdown = null;
|
|
22704
|
+
const cleanupHandlers = [];
|
|
22705
|
+
const handleThemeClick = () => {
|
|
22706
|
+
currentTheme = currentTheme === "light" ? "dark" : "light";
|
|
22707
|
+
config.onThemeToggle?.(currentTheme);
|
|
22708
|
+
updateButtonIcons();
|
|
22709
|
+
};
|
|
22710
|
+
const handleMaximizeClick = () => {
|
|
22711
|
+
currentIsMaximized = !currentIsMaximized;
|
|
22712
|
+
config.onMaximize?.(currentIsMaximized);
|
|
22713
|
+
updateButtonIcons();
|
|
22714
|
+
};
|
|
22715
|
+
const handleCloseClick = () => {
|
|
22716
|
+
config.onClose?.();
|
|
22717
|
+
};
|
|
22718
|
+
const handleExportClick = (format) => {
|
|
22719
|
+
config.onExport?.(format);
|
|
22720
|
+
if (exportDropdown) {
|
|
22721
|
+
exportDropdown.style.display = "none";
|
|
22722
|
+
}
|
|
22723
|
+
};
|
|
22724
|
+
function updateButtonIcons() {
|
|
22725
|
+
if (themeBtn) {
|
|
22726
|
+
themeBtn.textContent = currentTheme === "dark" ? "\u2600\uFE0F" : "\u{1F319}";
|
|
22727
|
+
themeBtn.title = currentTheme === "dark" ? "Modo claro" : "Modo escuro";
|
|
22728
|
+
}
|
|
22729
|
+
if (maximizeBtn) {
|
|
22730
|
+
maximizeBtn.textContent = currentIsMaximized ? "\u{1F5D7}" : "\u{1F5D6}";
|
|
22731
|
+
maximizeBtn.title = currentIsMaximized ? "Restaurar" : "Maximizar";
|
|
22732
|
+
}
|
|
22733
|
+
}
|
|
22734
|
+
function getButtonStyle() {
|
|
22735
|
+
return `
|
|
22736
|
+
background: none;
|
|
22737
|
+
border: none;
|
|
22738
|
+
font-size: 16px;
|
|
22739
|
+
cursor: pointer;
|
|
22740
|
+
padding: 4px 8px;
|
|
22741
|
+
border-radius: 6px;
|
|
22742
|
+
color: rgba(255, 255, 255, 0.8);
|
|
22743
|
+
transition: background-color 0.2s, color 0.2s;
|
|
22744
|
+
`.replace(/\s+/g, " ").trim();
|
|
22745
|
+
}
|
|
22746
|
+
function renderExportDropdown() {
|
|
22747
|
+
const formats = config.exportFormats || [];
|
|
22748
|
+
if (formats.length === 0) return "";
|
|
22749
|
+
if (formats.length === 1) {
|
|
22750
|
+
return `
|
|
22751
|
+
<button id="${config.id}-export" title="Exportar ${EXPORT_FORMAT_LABELS[formats[0]]}" style="${getButtonStyle()}">
|
|
22752
|
+
\u{1F4E5}
|
|
22753
|
+
</button>
|
|
22754
|
+
`;
|
|
22755
|
+
}
|
|
22756
|
+
return `
|
|
22757
|
+
<div style="position: relative; display: inline-block;">
|
|
22758
|
+
<button id="${config.id}-export-btn" title="Exportar" style="${getButtonStyle()}">
|
|
22759
|
+
\u{1F4E5}
|
|
22760
|
+
</button>
|
|
22761
|
+
<div id="${config.id}-export-dropdown" style="
|
|
22762
|
+
display: none;
|
|
22763
|
+
position: absolute;
|
|
22764
|
+
top: 100%;
|
|
22765
|
+
right: 0;
|
|
22766
|
+
background: white;
|
|
22767
|
+
border-radius: 6px;
|
|
22768
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
22769
|
+
min-width: 140px;
|
|
22770
|
+
z-index: 10001;
|
|
22771
|
+
margin-top: 4px;
|
|
22772
|
+
overflow: hidden;
|
|
22773
|
+
">
|
|
22774
|
+
${formats.map((format) => `
|
|
22775
|
+
<button
|
|
22776
|
+
id="${config.id}-export-${format}"
|
|
22777
|
+
class="myio-export-option"
|
|
22778
|
+
data-format="${format}"
|
|
22779
|
+
style="
|
|
22780
|
+
display: flex;
|
|
22781
|
+
align-items: center;
|
|
22782
|
+
gap: 8px;
|
|
22783
|
+
width: 100%;
|
|
22784
|
+
padding: 10px 14px;
|
|
22785
|
+
border: none;
|
|
22786
|
+
background: white;
|
|
22787
|
+
cursor: pointer;
|
|
22788
|
+
font-size: 13px;
|
|
22789
|
+
color: #333;
|
|
22790
|
+
text-align: left;
|
|
22791
|
+
transition: background-color 0.2s;
|
|
22792
|
+
"
|
|
22793
|
+
>
|
|
22794
|
+
${EXPORT_FORMAT_ICONS[format]} ${EXPORT_FORMAT_LABELS[format]}
|
|
22795
|
+
</button>
|
|
22796
|
+
`).join("")}
|
|
22797
|
+
</div>
|
|
22798
|
+
</div>
|
|
22799
|
+
`;
|
|
22800
|
+
}
|
|
22801
|
+
const instance = {
|
|
22802
|
+
render() {
|
|
22803
|
+
const bgColor = config.backgroundColor || DEFAULT_BG_COLOR;
|
|
22804
|
+
const textColor = config.textColor || DEFAULT_TEXT_COLOR;
|
|
22805
|
+
const borderRadius = currentIsMaximized ? "0" : config.borderRadius || DEFAULT_BORDER_RADIUS;
|
|
22806
|
+
const showTheme = config.showThemeToggle !== false;
|
|
22807
|
+
const showMax = config.showMaximize !== false;
|
|
22808
|
+
const showClose = config.showClose !== false;
|
|
22809
|
+
const showExport = config.exportFormats && config.exportFormats.length > 0;
|
|
22810
|
+
const iconHtml = config.icon ? `<span style="margin-right: 8px;">${config.icon}</span>` : "";
|
|
22811
|
+
const buttonStyle = getButtonStyle();
|
|
22812
|
+
return `
|
|
22813
|
+
<div class="myio-modal-header" style="
|
|
22814
|
+
padding: 4px 8px;
|
|
22815
|
+
display: flex;
|
|
22816
|
+
align-items: center;
|
|
22817
|
+
justify-content: space-between;
|
|
22818
|
+
background: ${bgColor};
|
|
22819
|
+
color: ${textColor};
|
|
22820
|
+
border-radius: ${borderRadius};
|
|
22821
|
+
min-height: 20px;
|
|
22822
|
+
font-family: 'Roboto', Arial, sans-serif;
|
|
22823
|
+
">
|
|
22824
|
+
<h2 id="${config.id}-header-title" style="
|
|
22825
|
+
margin: 6px;
|
|
22826
|
+
font-size: 18px;
|
|
22827
|
+
font-weight: 600;
|
|
22828
|
+
color: ${textColor};
|
|
22829
|
+
line-height: 2;
|
|
22830
|
+
display: flex;
|
|
22831
|
+
align-items: center;
|
|
22832
|
+
">
|
|
22833
|
+
${iconHtml}${currentTitle}
|
|
22834
|
+
</h2>
|
|
22835
|
+
<div style="display: flex; gap: 4px; align-items: center;">
|
|
22836
|
+
${showExport ? renderExportDropdown() : ""}
|
|
22837
|
+
${showTheme ? `
|
|
22838
|
+
<button id="${config.id}-theme-toggle" title="${currentTheme === "dark" ? "Modo claro" : "Modo escuro"}" style="${buttonStyle}">
|
|
22839
|
+
${currentTheme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}
|
|
22840
|
+
</button>
|
|
22841
|
+
` : ""}
|
|
22842
|
+
${showMax ? `
|
|
22843
|
+
<button id="${config.id}-maximize" title="${currentIsMaximized ? "Restaurar" : "Maximizar"}" style="${buttonStyle}">
|
|
22844
|
+
${currentIsMaximized ? "\u{1F5D7}" : "\u{1F5D6}"}
|
|
22845
|
+
</button>
|
|
22846
|
+
` : ""}
|
|
22847
|
+
${showClose ? `
|
|
22848
|
+
<button id="${config.id}-close" title="Fechar" style="${buttonStyle}; font-size: 20px;">
|
|
22849
|
+
\xD7
|
|
22850
|
+
</button>
|
|
22851
|
+
` : ""}
|
|
22852
|
+
</div>
|
|
22853
|
+
</div>
|
|
22854
|
+
`;
|
|
22855
|
+
},
|
|
22856
|
+
attachListeners() {
|
|
22857
|
+
themeBtn = document.getElementById(`${config.id}-theme-toggle`);
|
|
22858
|
+
maximizeBtn = document.getElementById(`${config.id}-maximize`);
|
|
22859
|
+
closeBtn = document.getElementById(`${config.id}-close`);
|
|
22860
|
+
const singleExportBtn = document.getElementById(`${config.id}-export`);
|
|
22861
|
+
if (singleExportBtn && config.exportFormats?.length === 1) {
|
|
22862
|
+
exportBtn = singleExportBtn;
|
|
22863
|
+
const format = config.exportFormats[0];
|
|
22864
|
+
const clickHandler = () => handleExportClick(format);
|
|
22865
|
+
exportBtn.addEventListener("click", clickHandler);
|
|
22866
|
+
cleanupHandlers.push(() => exportBtn?.removeEventListener("click", clickHandler));
|
|
22867
|
+
const enterHandler = () => {
|
|
22868
|
+
exportBtn.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
|
|
22869
|
+
};
|
|
22870
|
+
const leaveHandler = () => {
|
|
22871
|
+
exportBtn.style.backgroundColor = "transparent";
|
|
22872
|
+
};
|
|
22873
|
+
exportBtn.addEventListener("mouseenter", enterHandler);
|
|
22874
|
+
exportBtn.addEventListener("mouseleave", leaveHandler);
|
|
22875
|
+
cleanupHandlers.push(() => {
|
|
22876
|
+
exportBtn?.removeEventListener("mouseenter", enterHandler);
|
|
22877
|
+
exportBtn?.removeEventListener("mouseleave", leaveHandler);
|
|
22878
|
+
});
|
|
22879
|
+
}
|
|
22880
|
+
const exportDropdownBtn = document.getElementById(`${config.id}-export-btn`);
|
|
22881
|
+
exportDropdown = document.getElementById(`${config.id}-export-dropdown`);
|
|
22882
|
+
if (exportDropdownBtn && exportDropdown) {
|
|
22883
|
+
exportBtn = exportDropdownBtn;
|
|
22884
|
+
const toggleHandler = (e) => {
|
|
22885
|
+
e.stopPropagation();
|
|
22886
|
+
if (exportDropdown) {
|
|
22887
|
+
exportDropdown.style.display = exportDropdown.style.display === "none" ? "block" : "none";
|
|
22888
|
+
}
|
|
22889
|
+
};
|
|
22890
|
+
exportDropdownBtn.addEventListener("click", toggleHandler);
|
|
22891
|
+
cleanupHandlers.push(() => exportDropdownBtn.removeEventListener("click", toggleHandler));
|
|
22892
|
+
const outsideClickHandler = (e) => {
|
|
22893
|
+
if (exportDropdown && !exportDropdown.contains(e.target) && e.target !== exportDropdownBtn) {
|
|
22894
|
+
exportDropdown.style.display = "none";
|
|
22895
|
+
}
|
|
22896
|
+
};
|
|
22897
|
+
document.addEventListener("click", outsideClickHandler);
|
|
22898
|
+
cleanupHandlers.push(() => document.removeEventListener("click", outsideClickHandler));
|
|
22899
|
+
config.exportFormats?.forEach((format) => {
|
|
22900
|
+
const btn = document.getElementById(`${config.id}-export-${format}`);
|
|
22901
|
+
if (btn) {
|
|
22902
|
+
const clickHandler = () => handleExportClick(format);
|
|
22903
|
+
btn.addEventListener("click", clickHandler);
|
|
22904
|
+
cleanupHandlers.push(() => btn.removeEventListener("click", clickHandler));
|
|
22905
|
+
const enterHandler2 = () => {
|
|
22906
|
+
btn.style.backgroundColor = "#f0f0f0";
|
|
22907
|
+
};
|
|
22908
|
+
const leaveHandler2 = () => {
|
|
22909
|
+
btn.style.backgroundColor = "white";
|
|
22910
|
+
};
|
|
22911
|
+
btn.addEventListener("mouseenter", enterHandler2);
|
|
22912
|
+
btn.addEventListener("mouseleave", leaveHandler2);
|
|
22913
|
+
cleanupHandlers.push(() => {
|
|
22914
|
+
btn.removeEventListener("mouseenter", enterHandler2);
|
|
22915
|
+
btn.removeEventListener("mouseleave", leaveHandler2);
|
|
22916
|
+
});
|
|
22917
|
+
}
|
|
22918
|
+
});
|
|
22919
|
+
const enterHandler = () => {
|
|
22920
|
+
exportDropdownBtn.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
|
|
22921
|
+
};
|
|
22922
|
+
const leaveHandler = () => {
|
|
22923
|
+
exportDropdownBtn.style.backgroundColor = "transparent";
|
|
22924
|
+
};
|
|
22925
|
+
exportDropdownBtn.addEventListener("mouseenter", enterHandler);
|
|
22926
|
+
exportDropdownBtn.addEventListener("mouseleave", leaveHandler);
|
|
22927
|
+
cleanupHandlers.push(() => {
|
|
22928
|
+
exportDropdownBtn.removeEventListener("mouseenter", enterHandler);
|
|
22929
|
+
exportDropdownBtn.removeEventListener("mouseleave", leaveHandler);
|
|
22930
|
+
});
|
|
22931
|
+
}
|
|
22932
|
+
if (themeBtn && config.showThemeToggle !== false) {
|
|
22933
|
+
themeBtn.addEventListener("click", handleThemeClick);
|
|
22934
|
+
cleanupHandlers.push(() => themeBtn?.removeEventListener("click", handleThemeClick));
|
|
22935
|
+
const enterHandler = () => {
|
|
22936
|
+
themeBtn.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
|
|
22937
|
+
};
|
|
22938
|
+
const leaveHandler = () => {
|
|
22939
|
+
themeBtn.style.backgroundColor = "transparent";
|
|
22940
|
+
};
|
|
22941
|
+
themeBtn.addEventListener("mouseenter", enterHandler);
|
|
22942
|
+
themeBtn.addEventListener("mouseleave", leaveHandler);
|
|
22943
|
+
cleanupHandlers.push(() => {
|
|
22944
|
+
themeBtn?.removeEventListener("mouseenter", enterHandler);
|
|
22945
|
+
themeBtn?.removeEventListener("mouseleave", leaveHandler);
|
|
22946
|
+
});
|
|
22947
|
+
}
|
|
22948
|
+
if (maximizeBtn && config.showMaximize !== false) {
|
|
22949
|
+
maximizeBtn.addEventListener("click", handleMaximizeClick);
|
|
22950
|
+
cleanupHandlers.push(() => maximizeBtn?.removeEventListener("click", handleMaximizeClick));
|
|
22951
|
+
const enterHandler = () => {
|
|
22952
|
+
maximizeBtn.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
|
|
22953
|
+
};
|
|
22954
|
+
const leaveHandler = () => {
|
|
22955
|
+
maximizeBtn.style.backgroundColor = "transparent";
|
|
22956
|
+
};
|
|
22957
|
+
maximizeBtn.addEventListener("mouseenter", enterHandler);
|
|
22958
|
+
maximizeBtn.addEventListener("mouseleave", leaveHandler);
|
|
22959
|
+
cleanupHandlers.push(() => {
|
|
22960
|
+
maximizeBtn?.removeEventListener("mouseenter", enterHandler);
|
|
22961
|
+
maximizeBtn?.removeEventListener("mouseleave", leaveHandler);
|
|
22962
|
+
});
|
|
22963
|
+
}
|
|
22964
|
+
if (closeBtn && config.showClose !== false) {
|
|
22965
|
+
closeBtn.addEventListener("click", handleCloseClick);
|
|
22966
|
+
cleanupHandlers.push(() => closeBtn?.removeEventListener("click", handleCloseClick));
|
|
22967
|
+
const enterHandler = () => {
|
|
22968
|
+
closeBtn.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
|
|
22969
|
+
};
|
|
22970
|
+
const leaveHandler = () => {
|
|
22971
|
+
closeBtn.style.backgroundColor = "transparent";
|
|
22972
|
+
};
|
|
22973
|
+
closeBtn.addEventListener("mouseenter", enterHandler);
|
|
22974
|
+
closeBtn.addEventListener("mouseleave", leaveHandler);
|
|
22975
|
+
cleanupHandlers.push(() => {
|
|
22976
|
+
closeBtn?.removeEventListener("mouseenter", enterHandler);
|
|
22977
|
+
closeBtn?.removeEventListener("mouseleave", leaveHandler);
|
|
22978
|
+
});
|
|
22979
|
+
}
|
|
22980
|
+
},
|
|
22981
|
+
update(updates) {
|
|
22982
|
+
if (updates.theme !== void 0) {
|
|
22983
|
+
currentTheme = updates.theme;
|
|
22984
|
+
updateButtonIcons();
|
|
22985
|
+
}
|
|
22986
|
+
if (updates.isMaximized !== void 0) {
|
|
22987
|
+
currentIsMaximized = updates.isMaximized;
|
|
22988
|
+
updateButtonIcons();
|
|
22989
|
+
}
|
|
22990
|
+
if (updates.title !== void 0) {
|
|
22991
|
+
currentTitle = updates.title;
|
|
22992
|
+
const titleEl = document.getElementById(`${config.id}-header-title`);
|
|
22993
|
+
if (titleEl) {
|
|
22994
|
+
const iconHtml = config.icon ? `<span style="margin-right: 8px;">${config.icon}</span>` : "";
|
|
22995
|
+
titleEl.innerHTML = `${iconHtml}${currentTitle}`;
|
|
22996
|
+
}
|
|
22997
|
+
}
|
|
22998
|
+
},
|
|
22999
|
+
getState() {
|
|
23000
|
+
return {
|
|
23001
|
+
theme: currentTheme,
|
|
23002
|
+
isMaximized: currentIsMaximized
|
|
23003
|
+
};
|
|
23004
|
+
},
|
|
23005
|
+
destroy() {
|
|
23006
|
+
cleanupHandlers.forEach((handler) => handler());
|
|
23007
|
+
cleanupHandlers.length = 0;
|
|
23008
|
+
themeBtn = null;
|
|
23009
|
+
maximizeBtn = null;
|
|
23010
|
+
closeBtn = null;
|
|
23011
|
+
exportBtn = null;
|
|
23012
|
+
exportDropdown = null;
|
|
23013
|
+
}
|
|
23014
|
+
};
|
|
23015
|
+
return instance;
|
|
23016
|
+
}
|
|
23017
|
+
function getModalHeaderStyles() {
|
|
23018
|
+
return `
|
|
23019
|
+
.myio-modal-header button:hover {
|
|
23020
|
+
background-color: rgba(255, 255, 255, 0.2) !important;
|
|
23021
|
+
}
|
|
23022
|
+
.myio-modal-header button:active {
|
|
23023
|
+
background-color: rgba(255, 255, 255, 0.3) !important;
|
|
23024
|
+
}
|
|
23025
|
+
.myio-export-option:hover {
|
|
23026
|
+
background-color: #f0f0f0 !important;
|
|
23027
|
+
}
|
|
23028
|
+
`;
|
|
23029
|
+
}
|
|
23030
|
+
|
|
23031
|
+
// src/components/Consumption7DaysChart/types.ts
|
|
23032
|
+
var DEFAULT_COLORS = {
|
|
23033
|
+
energy: {
|
|
23034
|
+
primary: "#2563eb",
|
|
23035
|
+
background: "rgba(37, 99, 235, 0.1)",
|
|
23036
|
+
gradient: ["#f0fdf4", "#dcfce7"],
|
|
23037
|
+
pointBackground: "#2563eb",
|
|
23038
|
+
pointBorder: "#ffffff"
|
|
23039
|
+
},
|
|
23040
|
+
water: {
|
|
23041
|
+
primary: "#0288d1",
|
|
23042
|
+
background: "rgba(2, 136, 209, 0.1)",
|
|
23043
|
+
gradient: ["#f0f9ff", "#bae6fd"],
|
|
23044
|
+
pointBackground: "#0288d1",
|
|
23045
|
+
pointBorder: "#ffffff"
|
|
23046
|
+
},
|
|
23047
|
+
gas: {
|
|
23048
|
+
primary: "#ea580c",
|
|
23049
|
+
background: "rgba(234, 88, 12, 0.1)",
|
|
23050
|
+
gradient: ["#fff7ed", "#fed7aa"],
|
|
23051
|
+
pointBackground: "#ea580c",
|
|
23052
|
+
pointBorder: "#ffffff"
|
|
23053
|
+
},
|
|
23054
|
+
temperature: {
|
|
23055
|
+
primary: "#dc2626",
|
|
23056
|
+
background: "rgba(220, 38, 38, 0.1)",
|
|
23057
|
+
gradient: ["#fef2f2", "#fecaca"],
|
|
23058
|
+
pointBackground: "#dc2626",
|
|
23059
|
+
pointBorder: "#ffffff"
|
|
23060
|
+
}
|
|
23061
|
+
};
|
|
23062
|
+
var THEME_COLORS = {
|
|
23063
|
+
light: {
|
|
23064
|
+
chartBackground: "#ffffff",
|
|
23065
|
+
text: "#1f2937",
|
|
23066
|
+
textMuted: "#6b7280",
|
|
23067
|
+
grid: "rgba(0, 0, 0, 0.1)",
|
|
23068
|
+
border: "#e5e7eb",
|
|
23069
|
+
tooltipBackground: "#ffffff",
|
|
23070
|
+
tooltipText: "#1f2937"
|
|
23071
|
+
},
|
|
23072
|
+
dark: {
|
|
23073
|
+
chartBackground: "#1f2937",
|
|
23074
|
+
text: "#f9fafb",
|
|
23075
|
+
textMuted: "#9ca3af",
|
|
23076
|
+
grid: "rgba(255, 255, 255, 0.1)",
|
|
23077
|
+
border: "#374151",
|
|
23078
|
+
tooltipBackground: "#374151",
|
|
23079
|
+
tooltipText: "#f9fafb"
|
|
23080
|
+
}
|
|
23081
|
+
};
|
|
23082
|
+
var DEFAULT_CONFIG = {
|
|
23083
|
+
defaultPeriod: 7,
|
|
23084
|
+
defaultChartType: "line",
|
|
23085
|
+
defaultVizMode: "total",
|
|
23086
|
+
defaultTheme: "light",
|
|
23087
|
+
cacheTTL: 3e5,
|
|
23088
|
+
// 5 minutes
|
|
23089
|
+
decimalPlaces: 1,
|
|
23090
|
+
lineTension: 0.4,
|
|
23091
|
+
pointRadius: 4,
|
|
23092
|
+
borderWidth: 2,
|
|
23093
|
+
fill: true,
|
|
23094
|
+
showLegend: false,
|
|
23095
|
+
enableExport: true
|
|
23096
|
+
};
|
|
23097
|
+
|
|
23098
|
+
// src/components/Consumption7DaysChart/createConsumption7DaysChart.ts
|
|
23099
|
+
function createConsumption7DaysChart(config) {
|
|
23100
|
+
let chartInstance = null;
|
|
23101
|
+
let cachedData = null;
|
|
23102
|
+
let currentPeriod = config.defaultPeriod ?? DEFAULT_CONFIG.defaultPeriod;
|
|
23103
|
+
let currentChartType = config.defaultChartType ?? DEFAULT_CONFIG.defaultChartType;
|
|
23104
|
+
let currentVizMode = config.defaultVizMode ?? DEFAULT_CONFIG.defaultVizMode;
|
|
23105
|
+
let currentTheme = config.theme ?? DEFAULT_CONFIG.defaultTheme;
|
|
23106
|
+
let currentIdealRange = config.idealRange ?? null;
|
|
23107
|
+
let isRendered = false;
|
|
23108
|
+
let autoRefreshTimer = null;
|
|
23109
|
+
const colors = {
|
|
23110
|
+
...DEFAULT_COLORS[config.domain] ?? DEFAULT_COLORS.energy,
|
|
23111
|
+
...config.colors
|
|
23112
|
+
};
|
|
23113
|
+
function $id(id) {
|
|
23114
|
+
if (config.$container && config.$container[0]) {
|
|
23115
|
+
return config.$container[0].querySelector(`#${id}`);
|
|
23116
|
+
}
|
|
23117
|
+
return document.getElementById(id);
|
|
23118
|
+
}
|
|
23119
|
+
function log(level, ...args) {
|
|
23120
|
+
const prefix = `[${config.domain.toUpperCase()}]`;
|
|
23121
|
+
console[level](prefix, ...args);
|
|
23122
|
+
}
|
|
23123
|
+
function calculateYAxisMax(values) {
|
|
23124
|
+
const maxValue = Math.max(...values, 0);
|
|
23125
|
+
if (config.domain === "temperature") {
|
|
23126
|
+
const tempConfig = config.temperatureConfig;
|
|
23127
|
+
if (tempConfig?.clampRange) {
|
|
23128
|
+
return tempConfig.clampRange.max;
|
|
23129
|
+
}
|
|
23130
|
+
const maxWithThreshold = Math.max(
|
|
23131
|
+
maxValue,
|
|
23132
|
+
tempConfig?.maxThreshold?.value ?? 0,
|
|
23133
|
+
tempConfig?.idealRange?.max ?? 0
|
|
23134
|
+
);
|
|
23135
|
+
return Math.ceil(maxWithThreshold + 5);
|
|
23136
|
+
}
|
|
23137
|
+
if (maxValue === 0) {
|
|
23138
|
+
return config.thresholdForLargeUnit ? config.thresholdForLargeUnit / 2 : 500;
|
|
23139
|
+
}
|
|
23140
|
+
let roundTo;
|
|
23141
|
+
if (config.thresholdForLargeUnit && maxValue >= config.thresholdForLargeUnit) {
|
|
23142
|
+
roundTo = config.thresholdForLargeUnit / 10;
|
|
23143
|
+
} else if (maxValue >= 1e3) {
|
|
23144
|
+
roundTo = 100;
|
|
23145
|
+
} else if (maxValue >= 100) {
|
|
23146
|
+
roundTo = 50;
|
|
23147
|
+
} else if (maxValue >= 10) {
|
|
23148
|
+
roundTo = 10;
|
|
23149
|
+
} else {
|
|
23150
|
+
roundTo = 5;
|
|
23151
|
+
}
|
|
23152
|
+
return Math.ceil(maxValue * 1.1 / roundTo) * roundTo;
|
|
23153
|
+
}
|
|
23154
|
+
function calculateYAxisMin(values) {
|
|
23155
|
+
if (config.domain !== "temperature") {
|
|
23156
|
+
return 0;
|
|
23157
|
+
}
|
|
23158
|
+
const tempConfig = config.temperatureConfig;
|
|
23159
|
+
if (tempConfig?.clampRange) {
|
|
23160
|
+
return tempConfig.clampRange.min;
|
|
23161
|
+
}
|
|
23162
|
+
const minValue = Math.min(...values);
|
|
23163
|
+
const minWithThreshold = Math.min(
|
|
23164
|
+
minValue,
|
|
23165
|
+
tempConfig?.minThreshold?.value ?? minValue,
|
|
23166
|
+
tempConfig?.idealRange?.min ?? minValue
|
|
23167
|
+
);
|
|
23168
|
+
return Math.floor(minWithThreshold - 5);
|
|
23169
|
+
}
|
|
23170
|
+
function buildTemperatureAnnotations() {
|
|
23171
|
+
const tempConfig = config.temperatureConfig;
|
|
23172
|
+
if (!tempConfig || config.domain !== "temperature") {
|
|
23173
|
+
return {};
|
|
23174
|
+
}
|
|
23175
|
+
const annotations = {};
|
|
23176
|
+
const createLineAnnotation = (line, id) => {
|
|
23177
|
+
const borderDash = line.lineStyle === "dashed" ? [6, 6] : line.lineStyle === "dotted" ? [2, 2] : [];
|
|
23178
|
+
return {
|
|
23179
|
+
type: "line",
|
|
23180
|
+
yMin: line.value,
|
|
23181
|
+
yMax: line.value,
|
|
23182
|
+
borderColor: line.color,
|
|
23183
|
+
borderWidth: line.lineWidth ?? 2,
|
|
23184
|
+
borderDash,
|
|
23185
|
+
label: {
|
|
23186
|
+
display: true,
|
|
23187
|
+
content: line.label,
|
|
23188
|
+
position: "end",
|
|
23189
|
+
backgroundColor: line.color,
|
|
23190
|
+
color: "#fff",
|
|
23191
|
+
font: { size: 10, weight: "bold" },
|
|
23192
|
+
padding: { x: 4, y: 2 }
|
|
23193
|
+
}
|
|
23194
|
+
};
|
|
23195
|
+
};
|
|
23196
|
+
if (tempConfig.minThreshold) {
|
|
23197
|
+
annotations["minThreshold"] = createLineAnnotation(tempConfig.minThreshold, "minThreshold");
|
|
23198
|
+
}
|
|
23199
|
+
if (tempConfig.maxThreshold) {
|
|
23200
|
+
annotations["maxThreshold"] = createLineAnnotation(tempConfig.maxThreshold, "maxThreshold");
|
|
23201
|
+
}
|
|
23202
|
+
if (tempConfig.idealRange) {
|
|
23203
|
+
annotations["idealRange"] = {
|
|
23204
|
+
type: "box",
|
|
23205
|
+
yMin: tempConfig.idealRange.min,
|
|
23206
|
+
yMax: tempConfig.idealRange.max,
|
|
23207
|
+
backgroundColor: tempConfig.idealRange.color,
|
|
23208
|
+
borderWidth: 0,
|
|
23209
|
+
label: tempConfig.idealRange.label ? {
|
|
23210
|
+
display: true,
|
|
23211
|
+
content: tempConfig.idealRange.label,
|
|
23212
|
+
position: { x: "start", y: "center" },
|
|
23213
|
+
color: "#666",
|
|
23214
|
+
font: { size: 10 }
|
|
23215
|
+
} : void 0
|
|
23216
|
+
};
|
|
23217
|
+
}
|
|
23218
|
+
return annotations;
|
|
23219
|
+
}
|
|
23220
|
+
function buildIdealRangeAnnotation() {
|
|
23221
|
+
if (!currentIdealRange) {
|
|
23222
|
+
return {};
|
|
23223
|
+
}
|
|
23224
|
+
const { min, max, enabled = true } = currentIdealRange;
|
|
23225
|
+
if (!enabled || min === 0 && max === 0 || min >= max) {
|
|
23226
|
+
return {};
|
|
23227
|
+
}
|
|
23228
|
+
const defaultColors = {
|
|
23229
|
+
temperature: { bg: "rgba(34, 197, 94, 0.15)", border: "rgba(34, 197, 94, 0.4)" },
|
|
23230
|
+
energy: { bg: "rgba(37, 99, 235, 0.1)", border: "rgba(37, 99, 235, 0.3)" },
|
|
23231
|
+
water: { bg: "rgba(2, 136, 209, 0.1)", border: "rgba(2, 136, 209, 0.3)" },
|
|
23232
|
+
gas: { bg: "rgba(234, 88, 12, 0.1)", border: "rgba(234, 88, 12, 0.3)" }
|
|
23233
|
+
};
|
|
23234
|
+
const domainDefaults = defaultColors[config.domain] || defaultColors.energy;
|
|
23235
|
+
return {
|
|
23236
|
+
idealRangeBox: {
|
|
23237
|
+
type: "box",
|
|
23238
|
+
yMin: min,
|
|
23239
|
+
yMax: max,
|
|
23240
|
+
backgroundColor: currentIdealRange.color || domainDefaults.bg,
|
|
23241
|
+
borderColor: currentIdealRange.borderColor || domainDefaults.border,
|
|
23242
|
+
borderWidth: 1,
|
|
23243
|
+
label: currentIdealRange.label ? {
|
|
23244
|
+
display: true,
|
|
23245
|
+
content: currentIdealRange.label,
|
|
23246
|
+
position: { x: "start", y: "center" },
|
|
23247
|
+
color: "#666",
|
|
23248
|
+
font: { size: 10, style: "italic" },
|
|
23249
|
+
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
|
23250
|
+
padding: { x: 4, y: 2 }
|
|
23251
|
+
} : void 0
|
|
23252
|
+
}
|
|
23253
|
+
};
|
|
23254
|
+
}
|
|
23255
|
+
function formatValue(value, includeUnit = true) {
|
|
23256
|
+
const decimals = config.decimalPlaces ?? DEFAULT_CONFIG.decimalPlaces;
|
|
23257
|
+
if (config.unitLarge && config.thresholdForLargeUnit && value >= config.thresholdForLargeUnit) {
|
|
23258
|
+
const converted = value / config.thresholdForLargeUnit;
|
|
23259
|
+
return includeUnit ? `${converted.toFixed(decimals)} ${config.unitLarge}` : converted.toFixed(decimals);
|
|
23260
|
+
}
|
|
23261
|
+
return includeUnit ? `${value.toFixed(decimals)} ${config.unit}` : value.toFixed(decimals);
|
|
23262
|
+
}
|
|
23263
|
+
function formatTickValue(value) {
|
|
23264
|
+
if (config.unitLarge && config.thresholdForLargeUnit && value >= config.thresholdForLargeUnit) {
|
|
23265
|
+
return `${(value / config.thresholdForLargeUnit).toFixed(1)}`;
|
|
23266
|
+
}
|
|
23267
|
+
return value.toFixed(0);
|
|
23268
|
+
}
|
|
23269
|
+
function buildChartConfig(data) {
|
|
23270
|
+
const yAxisMax = calculateYAxisMax(data.dailyTotals);
|
|
23271
|
+
const yAxisMin = calculateYAxisMin(data.dailyTotals);
|
|
23272
|
+
const tension = config.lineTension ?? DEFAULT_CONFIG.lineTension;
|
|
23273
|
+
const pointRadius = config.pointRadius ?? DEFAULT_CONFIG.pointRadius;
|
|
23274
|
+
const borderWidth = config.borderWidth ?? DEFAULT_CONFIG.borderWidth;
|
|
23275
|
+
const fill = config.fill ?? DEFAULT_CONFIG.fill;
|
|
23276
|
+
const showLegend = config.showLegend ?? DEFAULT_CONFIG.showLegend;
|
|
23277
|
+
const themeColors = THEME_COLORS[currentTheme];
|
|
23278
|
+
const isTemperature = config.domain === "temperature";
|
|
23279
|
+
let datasets;
|
|
23280
|
+
if (currentVizMode === "separate" && data.shoppingData && data.shoppingNames) {
|
|
23281
|
+
const shoppingColors = [
|
|
23282
|
+
"#2563eb",
|
|
23283
|
+
"#16a34a",
|
|
23284
|
+
"#ea580c",
|
|
23285
|
+
"#dc2626",
|
|
23286
|
+
"#8b5cf6",
|
|
23287
|
+
"#0891b2",
|
|
23288
|
+
"#65a30d",
|
|
23289
|
+
"#d97706",
|
|
23290
|
+
"#be185d",
|
|
23291
|
+
"#0d9488"
|
|
23292
|
+
];
|
|
23293
|
+
datasets = Object.entries(data.shoppingData).map(([shoppingId, values], index) => ({
|
|
23294
|
+
label: data.shoppingNames?.[shoppingId] || shoppingId,
|
|
23295
|
+
data: values,
|
|
23296
|
+
borderColor: shoppingColors[index % shoppingColors.length],
|
|
23297
|
+
backgroundColor: currentChartType === "line" ? `${shoppingColors[index % shoppingColors.length]}20` : shoppingColors[index % shoppingColors.length],
|
|
23298
|
+
fill: currentChartType === "line" && fill,
|
|
23299
|
+
tension,
|
|
23300
|
+
borderWidth,
|
|
23301
|
+
pointRadius: currentChartType === "line" ? pointRadius : 0,
|
|
23302
|
+
pointBackgroundColor: shoppingColors[index % shoppingColors.length],
|
|
23303
|
+
pointBorderColor: "#fff",
|
|
23304
|
+
pointBorderWidth: 2
|
|
23305
|
+
}));
|
|
23306
|
+
} else {
|
|
23307
|
+
const datasetLabel = isTemperature ? `Temperatura (${config.unit})` : `Consumo (${config.unit})`;
|
|
23308
|
+
datasets = [
|
|
23309
|
+
{
|
|
23310
|
+
label: datasetLabel,
|
|
23311
|
+
data: data.dailyTotals,
|
|
23312
|
+
borderColor: colors.primary,
|
|
23313
|
+
backgroundColor: currentChartType === "line" ? colors.background : colors.primary,
|
|
23314
|
+
fill: currentChartType === "line" && fill,
|
|
23315
|
+
tension,
|
|
23316
|
+
borderWidth,
|
|
23317
|
+
pointRadius: currentChartType === "line" ? pointRadius : 0,
|
|
23318
|
+
pointBackgroundColor: colors.pointBackground || colors.primary,
|
|
23319
|
+
pointBorderColor: colors.pointBorder || "#fff",
|
|
23320
|
+
pointBorderWidth: 2,
|
|
23321
|
+
borderRadius: currentChartType === "bar" ? 4 : 0
|
|
23322
|
+
}
|
|
23323
|
+
];
|
|
23324
|
+
}
|
|
23325
|
+
const temperatureAnnotations = buildTemperatureAnnotations();
|
|
23326
|
+
const idealRangeAnnotations = buildIdealRangeAnnotation();
|
|
23327
|
+
const allAnnotations = { ...temperatureAnnotations, ...idealRangeAnnotations };
|
|
23328
|
+
const yAxisLabel = config.unitLarge && config.thresholdForLargeUnit && yAxisMax >= config.thresholdForLargeUnit ? config.unitLarge : config.unit;
|
|
23329
|
+
return {
|
|
23330
|
+
type: currentChartType,
|
|
23331
|
+
data: {
|
|
23332
|
+
labels: data.labels,
|
|
23333
|
+
datasets
|
|
23334
|
+
},
|
|
23335
|
+
options: {
|
|
23336
|
+
responsive: true,
|
|
23337
|
+
maintainAspectRatio: false,
|
|
23338
|
+
animation: false,
|
|
23339
|
+
// CRITICAL: Prevents infinite growth bug
|
|
23340
|
+
plugins: {
|
|
23341
|
+
legend: {
|
|
23342
|
+
display: showLegend || currentVizMode === "separate",
|
|
23343
|
+
position: "bottom",
|
|
23344
|
+
labels: {
|
|
23345
|
+
color: themeColors.text
|
|
23346
|
+
}
|
|
23347
|
+
},
|
|
23348
|
+
tooltip: {
|
|
23349
|
+
backgroundColor: themeColors.tooltipBackground,
|
|
23350
|
+
titleColor: themeColors.tooltipText,
|
|
23351
|
+
bodyColor: themeColors.tooltipText,
|
|
23352
|
+
borderColor: themeColors.border,
|
|
23353
|
+
borderWidth: 1,
|
|
23354
|
+
callbacks: {
|
|
23355
|
+
label: function(context) {
|
|
23356
|
+
const value = context.parsed.y || 0;
|
|
23357
|
+
const label = context.dataset.label || "";
|
|
23358
|
+
return `${label}: ${formatValue(value)}`;
|
|
23359
|
+
}
|
|
23360
|
+
}
|
|
23361
|
+
},
|
|
23362
|
+
// Reference lines and ideal range (requires chartjs-plugin-annotation)
|
|
23363
|
+
annotation: Object.keys(allAnnotations).length > 0 ? {
|
|
23364
|
+
annotations: allAnnotations
|
|
23365
|
+
} : void 0
|
|
23366
|
+
},
|
|
23367
|
+
scales: {
|
|
23368
|
+
y: {
|
|
23369
|
+
beginAtZero: !isTemperature,
|
|
23370
|
+
// Temperature can have negative values
|
|
23371
|
+
min: yAxisMin,
|
|
23372
|
+
max: yAxisMax,
|
|
23373
|
+
// CRITICAL: Fixed max prevents animation loop
|
|
23374
|
+
grid: {
|
|
23375
|
+
color: themeColors.grid
|
|
23376
|
+
},
|
|
23377
|
+
title: {
|
|
23378
|
+
display: true,
|
|
23379
|
+
text: yAxisLabel,
|
|
23380
|
+
font: { size: 12 },
|
|
23381
|
+
color: themeColors.text
|
|
23382
|
+
},
|
|
23383
|
+
ticks: {
|
|
23384
|
+
font: { size: 11 },
|
|
23385
|
+
color: themeColors.textMuted,
|
|
23386
|
+
callback: function(value) {
|
|
23387
|
+
return formatTickValue(value);
|
|
23388
|
+
}
|
|
23389
|
+
}
|
|
23390
|
+
},
|
|
23391
|
+
x: {
|
|
23392
|
+
grid: {
|
|
23393
|
+
color: themeColors.grid
|
|
23394
|
+
},
|
|
23395
|
+
ticks: {
|
|
23396
|
+
font: { size: 11 },
|
|
23397
|
+
color: themeColors.textMuted
|
|
23398
|
+
}
|
|
23399
|
+
}
|
|
23400
|
+
}
|
|
23401
|
+
}
|
|
23402
|
+
};
|
|
23403
|
+
}
|
|
23404
|
+
function validateChartJs() {
|
|
23405
|
+
if (typeof Chart === "undefined") {
|
|
23406
|
+
log("error", "Chart.js not loaded. Cannot initialize chart.");
|
|
23407
|
+
config.onError?.(new Error("Chart.js not loaded"));
|
|
23408
|
+
return false;
|
|
23409
|
+
}
|
|
23410
|
+
return true;
|
|
23411
|
+
}
|
|
23412
|
+
function validateCanvas() {
|
|
23413
|
+
const canvas = $id(config.containerId);
|
|
23414
|
+
if (!canvas) {
|
|
23415
|
+
log("error", `Canvas #${config.containerId} not found`);
|
|
23416
|
+
config.onError?.(new Error(`Canvas #${config.containerId} not found`));
|
|
23417
|
+
return null;
|
|
23418
|
+
}
|
|
23419
|
+
return canvas;
|
|
23420
|
+
}
|
|
23421
|
+
function setupAutoRefresh() {
|
|
23422
|
+
if (config.autoRefreshInterval && config.autoRefreshInterval > 0) {
|
|
23423
|
+
if (autoRefreshTimer) {
|
|
23424
|
+
clearInterval(autoRefreshTimer);
|
|
23425
|
+
}
|
|
23426
|
+
autoRefreshTimer = setInterval(async () => {
|
|
23427
|
+
log("log", "Auto-refreshing data...");
|
|
23428
|
+
await instance.refresh(true);
|
|
23429
|
+
}, config.autoRefreshInterval);
|
|
23430
|
+
}
|
|
23431
|
+
}
|
|
23432
|
+
function cleanupAutoRefresh() {
|
|
23433
|
+
if (autoRefreshTimer) {
|
|
23434
|
+
clearInterval(autoRefreshTimer);
|
|
23435
|
+
autoRefreshTimer = null;
|
|
23436
|
+
}
|
|
23437
|
+
}
|
|
23438
|
+
function setupButtonHandlers() {
|
|
23439
|
+
if (config.settingsButtonId && config.onSettingsClick) {
|
|
23440
|
+
const settingsBtn = $id(config.settingsButtonId);
|
|
23441
|
+
if (settingsBtn) {
|
|
23442
|
+
settingsBtn.addEventListener("click", () => {
|
|
23443
|
+
log("log", "Settings button clicked");
|
|
23444
|
+
config.onSettingsClick?.();
|
|
23445
|
+
});
|
|
23446
|
+
log("log", "Settings button handler attached");
|
|
23447
|
+
}
|
|
23448
|
+
}
|
|
23449
|
+
if (config.maximizeButtonId && config.onMaximizeClick) {
|
|
23450
|
+
const maximizeBtn = $id(config.maximizeButtonId);
|
|
23451
|
+
if (maximizeBtn) {
|
|
23452
|
+
maximizeBtn.addEventListener("click", () => {
|
|
23453
|
+
log("log", "Maximize button clicked");
|
|
23454
|
+
config.onMaximizeClick?.();
|
|
23455
|
+
});
|
|
23456
|
+
log("log", "Maximize button handler attached");
|
|
23457
|
+
}
|
|
23458
|
+
}
|
|
23459
|
+
const enableExport = config.enableExport ?? DEFAULT_CONFIG.enableExport;
|
|
23460
|
+
if (enableExport && config.exportButtonId) {
|
|
23461
|
+
const exportBtn = $id(config.exportButtonId);
|
|
23462
|
+
if (exportBtn) {
|
|
23463
|
+
exportBtn.addEventListener("click", () => {
|
|
23464
|
+
log("log", "Export button clicked");
|
|
23465
|
+
if (config.onExportCSV && cachedData) {
|
|
23466
|
+
config.onExportCSV(cachedData);
|
|
23467
|
+
} else {
|
|
23468
|
+
instance.exportCSV();
|
|
23469
|
+
}
|
|
23470
|
+
});
|
|
23471
|
+
log("log", "Export button handler attached");
|
|
23472
|
+
}
|
|
23473
|
+
}
|
|
23474
|
+
}
|
|
23475
|
+
function generateCSVContent(data) {
|
|
23476
|
+
const rows = [];
|
|
23477
|
+
const decimals = config.decimalPlaces ?? DEFAULT_CONFIG.decimalPlaces;
|
|
23478
|
+
if (currentVizMode === "separate" && data.shoppingData && data.shoppingNames) {
|
|
23479
|
+
const shoppingHeaders = Object.keys(data.shoppingData).map(
|
|
23480
|
+
(id) => data.shoppingNames?.[id] || id
|
|
23481
|
+
);
|
|
23482
|
+
rows.push(["Data", ...shoppingHeaders, "Total"].join(";"));
|
|
23483
|
+
data.labels.forEach((label, index) => {
|
|
23484
|
+
const shoppingValues = Object.keys(data.shoppingData).map(
|
|
23485
|
+
(id) => data.shoppingData[id][index].toFixed(decimals)
|
|
23486
|
+
);
|
|
23487
|
+
rows.push([label, ...shoppingValues, data.dailyTotals[index].toFixed(decimals)].join(";"));
|
|
23488
|
+
});
|
|
23489
|
+
} else {
|
|
23490
|
+
rows.push(["Data", `Consumo (${config.unit})`].join(";"));
|
|
23491
|
+
data.labels.forEach((label, index) => {
|
|
23492
|
+
rows.push([label, data.dailyTotals[index].toFixed(decimals)].join(";"));
|
|
23493
|
+
});
|
|
23494
|
+
}
|
|
23495
|
+
const total = data.dailyTotals.reduce((sum, v) => sum + v, 0);
|
|
23496
|
+
const avg = total / data.dailyTotals.length;
|
|
23497
|
+
rows.push("");
|
|
23498
|
+
rows.push(["Total", total.toFixed(decimals)].join(";"));
|
|
23499
|
+
rows.push(["M\xE9dia", avg.toFixed(decimals)].join(";"));
|
|
23500
|
+
return rows.join("\n");
|
|
23501
|
+
}
|
|
23502
|
+
function downloadCSV(content, filename) {
|
|
23503
|
+
const BOM = "\uFEFF";
|
|
23504
|
+
const blob = new Blob([BOM + content], { type: "text/csv;charset=utf-8" });
|
|
23505
|
+
const url = URL.createObjectURL(blob);
|
|
23506
|
+
const link = document.createElement("a");
|
|
23507
|
+
link.href = url;
|
|
23508
|
+
link.download = `${filename}.csv`;
|
|
23509
|
+
document.body.appendChild(link);
|
|
23510
|
+
link.click();
|
|
23511
|
+
document.body.removeChild(link);
|
|
23512
|
+
URL.revokeObjectURL(url);
|
|
23513
|
+
log("log", `CSV exported: ${filename}.csv`);
|
|
23514
|
+
}
|
|
23515
|
+
function updateTitle() {
|
|
23516
|
+
if (config.titleElementId) {
|
|
23517
|
+
const titleEl = $id(config.titleElementId);
|
|
23518
|
+
if (titleEl) {
|
|
23519
|
+
if (currentPeriod === 0) {
|
|
23520
|
+
titleEl.textContent = `Consumo - Per\xEDodo Personalizado`;
|
|
23521
|
+
} else {
|
|
23522
|
+
titleEl.textContent = `Consumo dos \xFAltimos ${currentPeriod} dias`;
|
|
23523
|
+
}
|
|
23524
|
+
}
|
|
23525
|
+
}
|
|
23526
|
+
}
|
|
23527
|
+
const instance = {
|
|
23528
|
+
async render() {
|
|
23529
|
+
log("log", "Rendering chart...");
|
|
23530
|
+
if (!validateChartJs()) return;
|
|
23531
|
+
const canvas = validateCanvas();
|
|
23532
|
+
if (!canvas) return;
|
|
23533
|
+
try {
|
|
23534
|
+
log("log", `Fetching ${currentPeriod} days of data...`);
|
|
23535
|
+
cachedData = await config.fetchData(currentPeriod);
|
|
23536
|
+
cachedData.fetchTimestamp = Date.now();
|
|
23537
|
+
if (config.onBeforeRender) {
|
|
23538
|
+
cachedData = config.onBeforeRender(cachedData);
|
|
23539
|
+
}
|
|
23540
|
+
if (chartInstance) {
|
|
23541
|
+
chartInstance.destroy();
|
|
23542
|
+
chartInstance = null;
|
|
23543
|
+
}
|
|
23544
|
+
const ctx = canvas.getContext("2d");
|
|
23545
|
+
const chartConfig = buildChartConfig(cachedData);
|
|
23546
|
+
chartInstance = new Chart(ctx, chartConfig);
|
|
23547
|
+
isRendered = true;
|
|
23548
|
+
const yAxisMax = calculateYAxisMax(cachedData.dailyTotals);
|
|
23549
|
+
log("log", `Chart initialized with yAxisMax: ${yAxisMax}`);
|
|
23550
|
+
config.onDataLoaded?.(cachedData);
|
|
23551
|
+
config.onAfterRender?.(chartInstance);
|
|
23552
|
+
setupButtonHandlers();
|
|
23553
|
+
updateTitle();
|
|
23554
|
+
setupAutoRefresh();
|
|
23555
|
+
} catch (error) {
|
|
23556
|
+
log("error", "Failed to render chart:", error);
|
|
23557
|
+
config.onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
23558
|
+
}
|
|
23559
|
+
},
|
|
23560
|
+
async update(data) {
|
|
23561
|
+
if (data) {
|
|
23562
|
+
cachedData = data;
|
|
23563
|
+
cachedData.fetchTimestamp = Date.now();
|
|
23564
|
+
}
|
|
23565
|
+
if (!chartInstance || !cachedData) {
|
|
23566
|
+
log("warn", "Cannot update: chart not initialized or no data");
|
|
23567
|
+
return;
|
|
23568
|
+
}
|
|
23569
|
+
let renderData = cachedData;
|
|
23570
|
+
if (config.onBeforeRender) {
|
|
23571
|
+
renderData = config.onBeforeRender(cachedData);
|
|
23572
|
+
}
|
|
23573
|
+
const chartConfig = buildChartConfig(renderData);
|
|
23574
|
+
chartInstance.data = chartConfig.data;
|
|
23575
|
+
chartInstance.options = chartConfig.options;
|
|
23576
|
+
chartInstance.update("none");
|
|
23577
|
+
log("log", "Chart updated");
|
|
23578
|
+
},
|
|
23579
|
+
setChartType(type) {
|
|
23580
|
+
if (currentChartType === type) return;
|
|
23581
|
+
log("log", `Changing chart type to: ${type}`);
|
|
23582
|
+
currentChartType = type;
|
|
23583
|
+
if (cachedData && chartInstance) {
|
|
23584
|
+
const canvas = validateCanvas();
|
|
23585
|
+
if (canvas) {
|
|
23586
|
+
chartInstance.destroy();
|
|
23587
|
+
const ctx = canvas.getContext("2d");
|
|
23588
|
+
chartInstance = new Chart(ctx, buildChartConfig(cachedData));
|
|
23589
|
+
}
|
|
23590
|
+
}
|
|
23591
|
+
},
|
|
23592
|
+
setVizMode(mode) {
|
|
23593
|
+
if (currentVizMode === mode) return;
|
|
23594
|
+
log("log", `Changing viz mode to: ${mode}`);
|
|
23595
|
+
currentVizMode = mode;
|
|
23596
|
+
if (cachedData) {
|
|
23597
|
+
this.update();
|
|
23598
|
+
}
|
|
23599
|
+
},
|
|
23600
|
+
async setPeriod(days) {
|
|
23601
|
+
if (currentPeriod === days) return;
|
|
23602
|
+
log("log", `Changing period to: ${days} days`);
|
|
23603
|
+
currentPeriod = days;
|
|
23604
|
+
updateTitle();
|
|
23605
|
+
await this.refresh(true);
|
|
23606
|
+
},
|
|
23607
|
+
async refresh(forceRefresh = false) {
|
|
23608
|
+
if (!forceRefresh && cachedData?.fetchTimestamp) {
|
|
23609
|
+
const age = Date.now() - cachedData.fetchTimestamp;
|
|
23610
|
+
const ttl = config.cacheTTL ?? DEFAULT_CONFIG.cacheTTL;
|
|
23611
|
+
if (age < ttl) {
|
|
23612
|
+
log("log", `Using cached data (age: ${Math.round(age / 1e3)}s)`);
|
|
23613
|
+
return;
|
|
23614
|
+
}
|
|
23615
|
+
}
|
|
23616
|
+
log("log", "Refreshing data...");
|
|
23617
|
+
await this.render();
|
|
23618
|
+
},
|
|
23619
|
+
destroy() {
|
|
23620
|
+
log("log", "Destroying chart...");
|
|
23621
|
+
cleanupAutoRefresh();
|
|
23622
|
+
if (chartInstance) {
|
|
23623
|
+
chartInstance.destroy();
|
|
23624
|
+
chartInstance = null;
|
|
23625
|
+
}
|
|
23626
|
+
cachedData = null;
|
|
23627
|
+
isRendered = false;
|
|
23628
|
+
},
|
|
23629
|
+
getChartInstance() {
|
|
23630
|
+
return chartInstance;
|
|
23631
|
+
},
|
|
23632
|
+
getCachedData() {
|
|
23633
|
+
return cachedData;
|
|
23634
|
+
},
|
|
23635
|
+
getState() {
|
|
23636
|
+
return {
|
|
23637
|
+
period: currentPeriod,
|
|
23638
|
+
chartType: currentChartType,
|
|
23639
|
+
vizMode: currentVizMode,
|
|
23640
|
+
theme: currentTheme,
|
|
23641
|
+
isRendered
|
|
23642
|
+
};
|
|
23643
|
+
},
|
|
23644
|
+
exportCSV(filename) {
|
|
23645
|
+
if (!cachedData) {
|
|
23646
|
+
log("warn", "Cannot export: no data available");
|
|
23647
|
+
return;
|
|
23648
|
+
}
|
|
23649
|
+
const defaultFilename = config.exportFilename || `${config.domain}-consumo-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
|
|
23650
|
+
const csvContent = generateCSVContent(cachedData);
|
|
23651
|
+
downloadCSV(csvContent, filename || defaultFilename);
|
|
23652
|
+
},
|
|
23653
|
+
setTheme(theme) {
|
|
23654
|
+
if (currentTheme === theme) return;
|
|
23655
|
+
log("log", `Changing theme to: ${theme}`);
|
|
23656
|
+
currentTheme = theme;
|
|
23657
|
+
if (cachedData && chartInstance) {
|
|
23658
|
+
const canvas = validateCanvas();
|
|
23659
|
+
if (canvas) {
|
|
23660
|
+
chartInstance.destroy();
|
|
23661
|
+
const ctx = canvas.getContext("2d");
|
|
23662
|
+
chartInstance = new Chart(ctx, buildChartConfig(cachedData));
|
|
23663
|
+
}
|
|
23664
|
+
}
|
|
23665
|
+
},
|
|
23666
|
+
setIdealRange(range) {
|
|
23667
|
+
const rangeChanged = JSON.stringify(currentIdealRange) !== JSON.stringify(range);
|
|
23668
|
+
if (!rangeChanged) return;
|
|
23669
|
+
if (range) {
|
|
23670
|
+
log("log", `Setting ideal range: ${range.min} - ${range.max}`);
|
|
23671
|
+
} else {
|
|
23672
|
+
log("log", "Clearing ideal range");
|
|
23673
|
+
}
|
|
23674
|
+
currentIdealRange = range;
|
|
23675
|
+
if (cachedData && chartInstance) {
|
|
23676
|
+
const canvas = validateCanvas();
|
|
23677
|
+
if (canvas) {
|
|
23678
|
+
chartInstance.destroy();
|
|
23679
|
+
const ctx = canvas.getContext("2d");
|
|
23680
|
+
chartInstance = new Chart(ctx, buildChartConfig(cachedData));
|
|
23681
|
+
}
|
|
23682
|
+
}
|
|
23683
|
+
},
|
|
23684
|
+
getIdealRange() {
|
|
23685
|
+
return currentIdealRange;
|
|
23686
|
+
}
|
|
23687
|
+
};
|
|
23688
|
+
return instance;
|
|
23689
|
+
}
|
|
23690
|
+
|
|
23691
|
+
// src/components/Consumption7DaysChart/createConsumptionModal.ts
|
|
23692
|
+
var DOMAIN_CONFIG3 = {
|
|
23693
|
+
energy: { name: "Energia", icon: "\u26A1" },
|
|
23694
|
+
water: { name: "\xC1gua", icon: "\u{1F4A7}" },
|
|
23695
|
+
gas: { name: "G\xE1s", icon: "\u{1F525}" },
|
|
23696
|
+
temperature: { name: "Temperatura", icon: "\u{1F321}\uFE0F" }
|
|
23697
|
+
};
|
|
23698
|
+
function createConsumptionModal(config) {
|
|
23699
|
+
const modalId = `myio-consumption-modal-${Date.now()}`;
|
|
23700
|
+
let modalElement = null;
|
|
23701
|
+
let chartInstance = null;
|
|
23702
|
+
let headerInstance = null;
|
|
23703
|
+
let currentTheme = config.theme ?? "light";
|
|
23704
|
+
let currentChartType = config.defaultChartType ?? "line";
|
|
23705
|
+
let currentVizMode = config.defaultVizMode ?? "total";
|
|
23706
|
+
let isMaximized = false;
|
|
23707
|
+
const domainCfg = DOMAIN_CONFIG3[config.domain] || { name: config.domain, icon: "\u{1F4CA}" };
|
|
23708
|
+
const title = config.title || `${domainCfg.name} - Hist\xF3rico de Consumo`;
|
|
23709
|
+
function getThemeColors2() {
|
|
23710
|
+
return THEME_COLORS[currentTheme];
|
|
23711
|
+
}
|
|
23712
|
+
function renderModal4() {
|
|
23713
|
+
const colors = getThemeColors2();
|
|
23714
|
+
const exportFormats = config.exportFormats || ["csv"];
|
|
23715
|
+
headerInstance = createModalHeader({
|
|
23716
|
+
id: modalId,
|
|
23717
|
+
title,
|
|
23718
|
+
icon: domainCfg.icon,
|
|
23719
|
+
theme: currentTheme,
|
|
23720
|
+
isMaximized,
|
|
23721
|
+
exportFormats,
|
|
23722
|
+
onExport: (format) => {
|
|
23723
|
+
if (config.onExport) {
|
|
23724
|
+
config.onExport(format);
|
|
23725
|
+
} else {
|
|
23726
|
+
if (format === "csv") {
|
|
23727
|
+
chartInstance?.exportCSV();
|
|
23728
|
+
} else {
|
|
23729
|
+
console.warn(`[ConsumptionModal] Export format "${format}" requires custom onExport handler`);
|
|
23730
|
+
}
|
|
23731
|
+
}
|
|
23732
|
+
},
|
|
23733
|
+
onThemeToggle: (theme) => {
|
|
23734
|
+
currentTheme = theme;
|
|
23735
|
+
chartInstance?.setTheme(currentTheme);
|
|
23736
|
+
updateModal();
|
|
23737
|
+
},
|
|
23738
|
+
onMaximize: (maximized) => {
|
|
23739
|
+
isMaximized = maximized;
|
|
23740
|
+
updateModal();
|
|
23741
|
+
},
|
|
23742
|
+
onClose: () => {
|
|
23743
|
+
instance.close();
|
|
23744
|
+
}
|
|
23745
|
+
});
|
|
23746
|
+
return `
|
|
23747
|
+
<div class="myio-consumption-modal-overlay" style="
|
|
23748
|
+
position: fixed;
|
|
23749
|
+
top: 0;
|
|
23750
|
+
left: 0;
|
|
23751
|
+
width: 100%;
|
|
23752
|
+
height: 100%;
|
|
23753
|
+
background: rgba(0, 0, 0, 0.5);
|
|
23754
|
+
backdrop-filter: blur(2px);
|
|
23755
|
+
z-index: 99998;
|
|
23756
|
+
display: flex;
|
|
23757
|
+
justify-content: center;
|
|
23758
|
+
align-items: center;
|
|
23759
|
+
">
|
|
23760
|
+
<div class="myio-consumption-modal-content" style="
|
|
23761
|
+
background: ${colors.chartBackground};
|
|
23762
|
+
border-radius: ${isMaximized ? "0" : "10px"};
|
|
23763
|
+
width: ${isMaximized ? "100%" : "95%"};
|
|
23764
|
+
max-width: ${isMaximized ? "100%" : "1200px"};
|
|
23765
|
+
height: ${isMaximized ? "100%" : "85vh"};
|
|
23766
|
+
display: flex;
|
|
23767
|
+
flex-direction: column;
|
|
23768
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
23769
|
+
overflow: hidden;
|
|
23770
|
+
">
|
|
23771
|
+
<!-- MyIO Premium Header (using ModalHeader component) -->
|
|
23772
|
+
${headerInstance.render()}
|
|
23773
|
+
|
|
23774
|
+
<!-- Controls Bar -->
|
|
23775
|
+
<div class="myio-consumption-modal-controls" style="
|
|
23776
|
+
display: flex;
|
|
23777
|
+
gap: 16px;
|
|
23778
|
+
padding: 12px 16px;
|
|
23779
|
+
background: ${currentTheme === "dark" ? "#374151" : "#f7f7f7"};
|
|
23780
|
+
border-bottom: 1px solid ${colors.border};
|
|
23781
|
+
align-items: center;
|
|
23782
|
+
flex-wrap: wrap;
|
|
23783
|
+
">
|
|
23784
|
+
<!-- Viz Mode Tabs -->
|
|
23785
|
+
<div style="display: flex; gap: 2px; background: ${currentTheme === "dark" ? "#4b5563" : "#e5e7eb"}; border-radius: 8px; padding: 2px;">
|
|
23786
|
+
<button id="${modalId}-viz-total" style="
|
|
23787
|
+
padding: 6px 12px;
|
|
23788
|
+
border: none;
|
|
23789
|
+
border-radius: 6px;
|
|
23790
|
+
font-size: 13px;
|
|
23791
|
+
cursor: pointer;
|
|
23792
|
+
transition: all 0.2s;
|
|
23793
|
+
background: ${currentVizMode === "total" ? "#3e1a7d" : "transparent"};
|
|
23794
|
+
color: ${currentVizMode === "total" ? "white" : colors.text};
|
|
23795
|
+
">Consolidado</button>
|
|
23796
|
+
<button id="${modalId}-viz-separate" style="
|
|
23797
|
+
padding: 6px 12px;
|
|
23798
|
+
border: none;
|
|
23799
|
+
border-radius: 6px;
|
|
23800
|
+
font-size: 13px;
|
|
23801
|
+
cursor: pointer;
|
|
23802
|
+
transition: all 0.2s;
|
|
23803
|
+
background: ${currentVizMode === "separate" ? "#3e1a7d" : "transparent"};
|
|
23804
|
+
color: ${currentVizMode === "separate" ? "white" : colors.text};
|
|
23805
|
+
">Por Shopping</button>
|
|
23806
|
+
</div>
|
|
23807
|
+
|
|
23808
|
+
<!-- Chart Type Tabs -->
|
|
23809
|
+
<div style="display: flex; gap: 2px; background: ${currentTheme === "dark" ? "#4b5563" : "#e5e7eb"}; border-radius: 8px; padding: 2px;">
|
|
23810
|
+
<button id="${modalId}-type-line" style="
|
|
23811
|
+
padding: 6px 12px;
|
|
23812
|
+
border: none;
|
|
23813
|
+
border-radius: 6px;
|
|
23814
|
+
font-size: 13px;
|
|
23815
|
+
cursor: pointer;
|
|
23816
|
+
transition: all 0.2s;
|
|
23817
|
+
background: ${currentChartType === "line" ? "#3e1a7d" : "transparent"};
|
|
23818
|
+
color: ${currentChartType === "line" ? "white" : colors.text};
|
|
23819
|
+
">Linhas</button>
|
|
23820
|
+
<button id="${modalId}-type-bar" style="
|
|
23821
|
+
padding: 6px 12px;
|
|
23822
|
+
border: none;
|
|
23823
|
+
border-radius: 6px;
|
|
23824
|
+
font-size: 13px;
|
|
23825
|
+
cursor: pointer;
|
|
23826
|
+
transition: all 0.2s;
|
|
23827
|
+
background: ${currentChartType === "bar" ? "#3e1a7d" : "transparent"};
|
|
23828
|
+
color: ${currentChartType === "bar" ? "white" : colors.text};
|
|
23829
|
+
">Barras</button>
|
|
23830
|
+
</div>
|
|
23831
|
+
</div>
|
|
23832
|
+
|
|
23833
|
+
<!-- Chart Container -->
|
|
23834
|
+
<div style="
|
|
23835
|
+
flex: 1;
|
|
23836
|
+
padding: 16px;
|
|
23837
|
+
min-height: 0;
|
|
23838
|
+
position: relative;
|
|
23839
|
+
background: ${colors.chartBackground};
|
|
23840
|
+
">
|
|
23841
|
+
<canvas id="${modalId}-chart" style="width: 100%; height: 100%;"></canvas>
|
|
23842
|
+
</div>
|
|
23843
|
+
</div>
|
|
23844
|
+
</div>
|
|
23845
|
+
`;
|
|
23846
|
+
}
|
|
23847
|
+
function setupListeners() {
|
|
23848
|
+
if (!modalElement) return;
|
|
23849
|
+
headerInstance?.attachListeners();
|
|
23850
|
+
document.getElementById(`${modalId}-viz-total`)?.addEventListener("click", () => {
|
|
23851
|
+
currentVizMode = "total";
|
|
23852
|
+
chartInstance?.setVizMode("total");
|
|
23853
|
+
updateControlStyles();
|
|
23854
|
+
});
|
|
23855
|
+
document.getElementById(`${modalId}-viz-separate`)?.addEventListener("click", () => {
|
|
23856
|
+
currentVizMode = "separate";
|
|
23857
|
+
chartInstance?.setVizMode("separate");
|
|
23858
|
+
updateControlStyles();
|
|
23859
|
+
});
|
|
23860
|
+
document.getElementById(`${modalId}-type-line`)?.addEventListener("click", () => {
|
|
23861
|
+
currentChartType = "line";
|
|
23862
|
+
chartInstance?.setChartType("line");
|
|
23863
|
+
updateControlStyles();
|
|
23864
|
+
});
|
|
23865
|
+
document.getElementById(`${modalId}-type-bar`)?.addEventListener("click", () => {
|
|
23866
|
+
currentChartType = "bar";
|
|
23867
|
+
chartInstance?.setChartType("bar");
|
|
23868
|
+
updateControlStyles();
|
|
23869
|
+
});
|
|
23870
|
+
modalElement.querySelector(".myio-consumption-modal-overlay")?.addEventListener("click", (e) => {
|
|
23871
|
+
if (e.target.classList.contains("myio-consumption-modal-overlay")) {
|
|
23872
|
+
instance.close();
|
|
23873
|
+
}
|
|
23874
|
+
});
|
|
23875
|
+
const handleKeydown = (e) => {
|
|
23876
|
+
if (e.key === "Escape") {
|
|
23877
|
+
instance.close();
|
|
23878
|
+
}
|
|
23879
|
+
};
|
|
23880
|
+
document.addEventListener("keydown", handleKeydown);
|
|
23881
|
+
modalElement.__handleKeydown = handleKeydown;
|
|
23882
|
+
}
|
|
23883
|
+
function updateControlStyles() {
|
|
23884
|
+
const colors = getThemeColors2();
|
|
23885
|
+
const vizTotalBtn = document.getElementById(`${modalId}-viz-total`);
|
|
23886
|
+
const vizSeparateBtn = document.getElementById(`${modalId}-viz-separate`);
|
|
23887
|
+
if (vizTotalBtn) {
|
|
23888
|
+
vizTotalBtn.style.background = currentVizMode === "total" ? "#3e1a7d" : "transparent";
|
|
23889
|
+
vizTotalBtn.style.color = currentVizMode === "total" ? "white" : colors.text;
|
|
23890
|
+
}
|
|
23891
|
+
if (vizSeparateBtn) {
|
|
23892
|
+
vizSeparateBtn.style.background = currentVizMode === "separate" ? "#3e1a7d" : "transparent";
|
|
23893
|
+
vizSeparateBtn.style.color = currentVizMode === "separate" ? "white" : colors.text;
|
|
23894
|
+
}
|
|
23895
|
+
const typeLineBtn = document.getElementById(`${modalId}-type-line`);
|
|
23896
|
+
const typeBarBtn = document.getElementById(`${modalId}-type-bar`);
|
|
23897
|
+
if (typeLineBtn) {
|
|
23898
|
+
typeLineBtn.style.background = currentChartType === "line" ? "#3e1a7d" : "transparent";
|
|
23899
|
+
typeLineBtn.style.color = currentChartType === "line" ? "white" : colors.text;
|
|
23900
|
+
}
|
|
23901
|
+
if (typeBarBtn) {
|
|
23902
|
+
typeBarBtn.style.background = currentChartType === "bar" ? "#3e1a7d" : "transparent";
|
|
23903
|
+
typeBarBtn.style.color = currentChartType === "bar" ? "white" : colors.text;
|
|
23904
|
+
}
|
|
23905
|
+
}
|
|
23906
|
+
function updateModal() {
|
|
23907
|
+
if (!modalElement) return;
|
|
23908
|
+
const cachedData = chartInstance?.getCachedData();
|
|
23909
|
+
headerInstance?.destroy();
|
|
23910
|
+
chartInstance?.destroy();
|
|
23911
|
+
modalElement.innerHTML = renderModal4();
|
|
23912
|
+
setupListeners();
|
|
23913
|
+
if (cachedData) {
|
|
23914
|
+
chartInstance = createConsumption7DaysChart({
|
|
23915
|
+
...config,
|
|
23916
|
+
containerId: `${modalId}-chart`,
|
|
23917
|
+
theme: currentTheme,
|
|
23918
|
+
defaultChartType: currentChartType,
|
|
23919
|
+
defaultVizMode: currentVizMode
|
|
23920
|
+
});
|
|
23921
|
+
chartInstance.update(cachedData);
|
|
23922
|
+
}
|
|
23923
|
+
}
|
|
23924
|
+
const instance = {
|
|
23925
|
+
async open() {
|
|
23926
|
+
modalElement = document.createElement("div");
|
|
23927
|
+
modalElement.id = modalId;
|
|
23928
|
+
modalElement.innerHTML = renderModal4();
|
|
23929
|
+
const container = config.container || document.body;
|
|
23930
|
+
container.appendChild(modalElement);
|
|
23931
|
+
setupListeners();
|
|
23932
|
+
chartInstance = createConsumption7DaysChart({
|
|
23933
|
+
...config,
|
|
23934
|
+
containerId: `${modalId}-chart`,
|
|
23935
|
+
theme: currentTheme,
|
|
23936
|
+
defaultChartType: currentChartType,
|
|
23937
|
+
defaultVizMode: currentVizMode
|
|
23938
|
+
});
|
|
23939
|
+
await chartInstance.render();
|
|
23940
|
+
},
|
|
23941
|
+
close() {
|
|
23942
|
+
if (modalElement) {
|
|
23943
|
+
const handleKeydown = modalElement.__handleKeydown;
|
|
23944
|
+
if (handleKeydown) {
|
|
23945
|
+
document.removeEventListener("keydown", handleKeydown);
|
|
23946
|
+
}
|
|
23947
|
+
headerInstance?.destroy();
|
|
23948
|
+
headerInstance = null;
|
|
23949
|
+
chartInstance?.destroy();
|
|
23950
|
+
chartInstance = null;
|
|
23951
|
+
modalElement.remove();
|
|
23952
|
+
modalElement = null;
|
|
23953
|
+
config.onClose?.();
|
|
23954
|
+
}
|
|
23955
|
+
},
|
|
23956
|
+
getChart() {
|
|
23957
|
+
return chartInstance;
|
|
23958
|
+
},
|
|
23959
|
+
destroy() {
|
|
23960
|
+
instance.close();
|
|
23961
|
+
}
|
|
23962
|
+
};
|
|
23963
|
+
return instance;
|
|
23964
|
+
}
|
|
23965
|
+
|
|
23966
|
+
// src/components/ExportData/index.ts
|
|
23967
|
+
var DEFAULT_COLORS3 = {
|
|
23968
|
+
primary: "#3e1a7d",
|
|
23969
|
+
// MyIO purple
|
|
23970
|
+
secondary: "#6b4c9a",
|
|
23971
|
+
// Light purple
|
|
23972
|
+
accent: "#00bcd4",
|
|
23973
|
+
// Cyan accent
|
|
23974
|
+
background: "#ffffff",
|
|
23975
|
+
// White
|
|
23976
|
+
text: "#333333",
|
|
23977
|
+
// Dark gray
|
|
23978
|
+
chartColors: ["#3e1a7d", "#00bcd4", "#4caf50", "#ff9800", "#e91e63", "#9c27b0"]
|
|
23979
|
+
};
|
|
23980
|
+
var DOMAIN_ICONS = {
|
|
23981
|
+
energy: "\u26A1",
|
|
23982
|
+
// Lightning bolt
|
|
23983
|
+
water: "\u{1F4A7}",
|
|
23984
|
+
// Water drop
|
|
23985
|
+
temperature: "\u{1F321}\uFE0F"
|
|
23986
|
+
// Thermometer
|
|
23987
|
+
};
|
|
23988
|
+
var DOMAIN_LABELS = {
|
|
23989
|
+
energy: "Energia",
|
|
23990
|
+
water: "\xC1gua",
|
|
23991
|
+
temperature: "Temperatura"
|
|
23992
|
+
};
|
|
23993
|
+
var DOMAIN_LABELS_EN = {
|
|
23994
|
+
energy: "Energy",
|
|
23995
|
+
water: "Water",
|
|
23996
|
+
temperature: "Temperature"
|
|
23997
|
+
};
|
|
23998
|
+
var DOMAIN_UNITS = {
|
|
23999
|
+
energy: "kWh",
|
|
24000
|
+
water: "m\xB3",
|
|
24001
|
+
temperature: "\xB0C"
|
|
24002
|
+
};
|
|
24003
|
+
var CSV_SEPARATORS = {
|
|
24004
|
+
"pt-BR": ";",
|
|
24005
|
+
"en-US": ",",
|
|
24006
|
+
"default": ";"
|
|
24007
|
+
};
|
|
24008
|
+
function formatDateForFilename(date) {
|
|
24009
|
+
const pad = (n) => n.toString().padStart(2, "0");
|
|
24010
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}-${pad(date.getHours())}-${pad(date.getMinutes())}-${pad(date.getSeconds())}`;
|
|
24011
|
+
}
|
|
24012
|
+
function sanitizeFilename(str) {
|
|
24013
|
+
return str.replace(/[<>:"/\\|?*]/g, "").replace(/\s+/g, "_").substring(0, 50);
|
|
24014
|
+
}
|
|
24015
|
+
function generateFilename(data, config) {
|
|
24016
|
+
const timestamp = formatDateForFilename(/* @__PURE__ */ new Date());
|
|
24017
|
+
const domainLabel = DOMAIN_LABELS_EN[config.domain].toUpperCase();
|
|
24018
|
+
const ext = config.formatExport;
|
|
24019
|
+
let baseName = "export";
|
|
24020
|
+
if ("device" in data && data.device) {
|
|
24021
|
+
const device = data.device;
|
|
24022
|
+
const label = device.label || device.name || "device";
|
|
24023
|
+
const identifier = device.identifier ? `-${device.identifier}` : "";
|
|
24024
|
+
baseName = `${sanitizeFilename(label)}${identifier}`;
|
|
24025
|
+
} else if ("customer" in data && data.customer?.customerName) {
|
|
24026
|
+
baseName = sanitizeFilename(data.customer.customerName);
|
|
24027
|
+
} else if ("groupName" in data) {
|
|
24028
|
+
baseName = sanitizeFilename(data.groupName);
|
|
24029
|
+
}
|
|
24030
|
+
return `${baseName}-${domainLabel}-${timestamp}.${ext}`;
|
|
24031
|
+
}
|
|
24032
|
+
function normalizeTimestamp(ts) {
|
|
24033
|
+
if (ts instanceof Date) return ts;
|
|
24034
|
+
if (typeof ts === "number") return new Date(ts);
|
|
24035
|
+
return new Date(ts);
|
|
24036
|
+
}
|
|
24037
|
+
function calculateStats2(dataPoints) {
|
|
24038
|
+
if (dataPoints.length === 0) {
|
|
24039
|
+
return { min: 0, max: 0, average: 0, sum: 0, count: 0 };
|
|
24040
|
+
}
|
|
24041
|
+
const values = dataPoints.map((d) => d.value);
|
|
24042
|
+
const sum = values.reduce((a, b) => a + b, 0);
|
|
24043
|
+
return {
|
|
24044
|
+
min: Math.min(...values),
|
|
24045
|
+
max: Math.max(...values),
|
|
24046
|
+
average: sum / values.length,
|
|
24047
|
+
sum,
|
|
24048
|
+
count: values.length
|
|
24049
|
+
};
|
|
24050
|
+
}
|
|
24051
|
+
function formatNumber2(value, locale, decimals = 2) {
|
|
24052
|
+
return new Intl.NumberFormat(locale, {
|
|
24053
|
+
minimumFractionDigits: decimals,
|
|
24054
|
+
maximumFractionDigits: decimals
|
|
24055
|
+
}).format(value);
|
|
24056
|
+
}
|
|
24057
|
+
function formatDate3(date, locale) {
|
|
24058
|
+
return new Intl.DateTimeFormat(locale, {
|
|
24059
|
+
year: "numeric",
|
|
24060
|
+
month: "2-digit",
|
|
24061
|
+
day: "2-digit",
|
|
24062
|
+
hour: "2-digit",
|
|
24063
|
+
minute: "2-digit"
|
|
24064
|
+
}).format(date);
|
|
24065
|
+
}
|
|
24066
|
+
function generateCSV(data, config) {
|
|
24067
|
+
const sep = CSV_SEPARATORS[config.locale] || CSV_SEPARATORS["default"];
|
|
24068
|
+
const rows = [];
|
|
24069
|
+
const escapeCSV = (val) => {
|
|
24070
|
+
const str = String(val ?? "");
|
|
24071
|
+
if (str.includes(sep) || str.includes('"') || str.includes("\n")) {
|
|
24072
|
+
return `"${str.replace(/"/g, '""')}"`;
|
|
24073
|
+
}
|
|
24074
|
+
return str;
|
|
24075
|
+
};
|
|
24076
|
+
const formatNumCSV = (val) => {
|
|
24077
|
+
return formatNumber2(val, config.locale);
|
|
24078
|
+
};
|
|
24079
|
+
if ("device" in data && "data" in data && Array.isArray(data.data)) {
|
|
24080
|
+
const deviceData = data;
|
|
24081
|
+
rows.push(["Timestamp", config.domainLabel, `Unit (${config.domainUnit})`]);
|
|
24082
|
+
for (const point of deviceData.data) {
|
|
24083
|
+
const ts = normalizeTimestamp(point.timestamp);
|
|
24084
|
+
rows.push([
|
|
24085
|
+
formatDate3(ts, config.locale),
|
|
24086
|
+
formatNumCSV(point.value),
|
|
24087
|
+
point.unit || config.domainUnit
|
|
24088
|
+
]);
|
|
24089
|
+
}
|
|
24090
|
+
if (config.includeStats) {
|
|
24091
|
+
const stats = calculateStats2(deviceData.data);
|
|
24092
|
+
rows.push([]);
|
|
24093
|
+
rows.push(["Statistics", "", ""]);
|
|
24094
|
+
rows.push(["Minimum", formatNumCSV(stats.min), config.domainUnit]);
|
|
24095
|
+
rows.push(["Maximum", formatNumCSV(stats.max), config.domainUnit]);
|
|
24096
|
+
rows.push(["Average", formatNumCSV(stats.average), config.domainUnit]);
|
|
24097
|
+
rows.push(["Total", formatNumCSV(stats.sum), config.domainUnit]);
|
|
24098
|
+
rows.push(["Count", String(stats.count), "points"]);
|
|
24099
|
+
}
|
|
24100
|
+
} else if ("devices" in data && Array.isArray(data.devices)) {
|
|
24101
|
+
const compData = data;
|
|
24102
|
+
const deviceHeaders = compData.devices.map(
|
|
24103
|
+
(d) => d.device.label || d.device.name || "Device"
|
|
24104
|
+
);
|
|
24105
|
+
rows.push(["Timestamp", ...deviceHeaders]);
|
|
24106
|
+
const allTimestamps = /* @__PURE__ */ new Set();
|
|
24107
|
+
compData.devices.forEach((d) => {
|
|
24108
|
+
d.data.forEach((point) => {
|
|
24109
|
+
allTimestamps.add(normalizeTimestamp(point.timestamp).getTime());
|
|
24110
|
+
});
|
|
24111
|
+
});
|
|
24112
|
+
const sortedTimestamps = Array.from(allTimestamps).sort((a, b) => a - b);
|
|
24113
|
+
for (const ts of sortedTimestamps) {
|
|
24114
|
+
const row = [formatDate3(new Date(ts), config.locale)];
|
|
24115
|
+
for (const device of compData.devices) {
|
|
24116
|
+
const point = device.data.find(
|
|
24117
|
+
(p) => normalizeTimestamp(p.timestamp).getTime() === ts
|
|
24118
|
+
);
|
|
24119
|
+
row.push(point ? formatNumCSV(point.value) : "");
|
|
24120
|
+
}
|
|
24121
|
+
rows.push(row);
|
|
24122
|
+
}
|
|
24123
|
+
}
|
|
24124
|
+
return rows.map((row) => row.map(escapeCSV).join(sep)).join("\r\n");
|
|
24125
|
+
}
|
|
24126
|
+
function generateXLSX(data, config) {
|
|
24127
|
+
return generateCSV(data, config);
|
|
24128
|
+
}
|
|
24129
|
+
function generatePDFContent(data, config) {
|
|
24130
|
+
const { colors, domainIcon, domainLabel, domainUnit, locale, includeStats, includeChart } = config;
|
|
24131
|
+
let deviceLabel = "Export";
|
|
24132
|
+
let customerName = "";
|
|
24133
|
+
let identifier = "";
|
|
24134
|
+
let dataPoints = [];
|
|
24135
|
+
if ("device" in data && "data" in data) {
|
|
24136
|
+
const deviceData = data;
|
|
24137
|
+
deviceLabel = deviceData.device.label || deviceData.device.name || "Device";
|
|
24138
|
+
identifier = deviceData.device.identifier || "";
|
|
24139
|
+
customerName = deviceData.customer?.customerName || "";
|
|
24140
|
+
dataPoints = deviceData.data;
|
|
24141
|
+
}
|
|
24142
|
+
const stats = calculateStats2(dataPoints);
|
|
24143
|
+
const tableRows = dataPoints.slice(0, 100).map((point) => {
|
|
24144
|
+
const ts = normalizeTimestamp(point.timestamp);
|
|
24145
|
+
return `
|
|
24146
|
+
<tr>
|
|
24147
|
+
<td style="padding: 8px; border-bottom: 1px solid #eee;">${formatDate3(ts, locale)}</td>
|
|
24148
|
+
<td style="padding: 8px; border-bottom: 1px solid #eee; text-align: right;">${formatNumber2(point.value, locale)}</td>
|
|
24149
|
+
<td style="padding: 8px; border-bottom: 1px solid #eee;">${point.unit || domainUnit}</td>
|
|
24150
|
+
</tr>
|
|
24151
|
+
`;
|
|
24152
|
+
}).join("");
|
|
24153
|
+
const statsSection = includeStats ? `
|
|
24154
|
+
<div style="margin-top: 24px; padding: 16px; background: #f5f5f5; border-radius: 8px;">
|
|
24155
|
+
<h3 style="margin: 0 0 12px 0; color: ${colors.primary};">Statistics</h3>
|
|
24156
|
+
<table style="width: 100%;">
|
|
24157
|
+
<tr>
|
|
24158
|
+
<td><strong>Minimum:</strong></td>
|
|
24159
|
+
<td>${formatNumber2(stats.min, locale)} ${domainUnit}</td>
|
|
24160
|
+
<td><strong>Maximum:</strong></td>
|
|
24161
|
+
<td>${formatNumber2(stats.max, locale)} ${domainUnit}</td>
|
|
24162
|
+
</tr>
|
|
24163
|
+
<tr>
|
|
24164
|
+
<td><strong>Average:</strong></td>
|
|
24165
|
+
<td>${formatNumber2(stats.average, locale)} ${domainUnit}</td>
|
|
24166
|
+
<td><strong>Total:</strong></td>
|
|
24167
|
+
<td>${formatNumber2(stats.sum, locale)} ${domainUnit}</td>
|
|
24168
|
+
</tr>
|
|
24169
|
+
</table>
|
|
24170
|
+
</div>
|
|
24171
|
+
` : "";
|
|
24172
|
+
return `
|
|
24173
|
+
<!DOCTYPE html>
|
|
24174
|
+
<html>
|
|
24175
|
+
<head>
|
|
24176
|
+
<meta charset="UTF-8">
|
|
24177
|
+
<title>${deviceLabel} - ${domainLabel} Report</title>
|
|
24178
|
+
<style>
|
|
24179
|
+
body {
|
|
24180
|
+
font-family: 'Roboto', Arial, sans-serif;
|
|
24181
|
+
margin: 0;
|
|
24182
|
+
padding: 24px;
|
|
24183
|
+
color: ${colors.text};
|
|
24184
|
+
background: ${colors.background};
|
|
24185
|
+
}
|
|
24186
|
+
.header {
|
|
24187
|
+
background: ${colors.primary};
|
|
24188
|
+
color: white;
|
|
24189
|
+
padding: 20px;
|
|
24190
|
+
border-radius: 8px;
|
|
24191
|
+
margin-bottom: 24px;
|
|
24192
|
+
}
|
|
24193
|
+
.header h1 {
|
|
24194
|
+
margin: 0;
|
|
24195
|
+
font-size: 24px;
|
|
24196
|
+
}
|
|
24197
|
+
.header .subtitle {
|
|
24198
|
+
opacity: 0.9;
|
|
24199
|
+
margin-top: 8px;
|
|
24200
|
+
}
|
|
24201
|
+
.device-info {
|
|
24202
|
+
display: flex;
|
|
24203
|
+
gap: 16px;
|
|
24204
|
+
margin-bottom: 16px;
|
|
24205
|
+
padding: 12px;
|
|
24206
|
+
background: #f5f5f5;
|
|
24207
|
+
border-radius: 8px;
|
|
24208
|
+
}
|
|
24209
|
+
.device-info span {
|
|
24210
|
+
padding: 4px 12px;
|
|
24211
|
+
background: ${colors.secondary};
|
|
24212
|
+
color: white;
|
|
24213
|
+
border-radius: 4px;
|
|
24214
|
+
font-size: 14px;
|
|
24215
|
+
}
|
|
24216
|
+
table {
|
|
24217
|
+
width: 100%;
|
|
24218
|
+
border-collapse: collapse;
|
|
24219
|
+
}
|
|
24220
|
+
th {
|
|
24221
|
+
background: ${colors.primary};
|
|
24222
|
+
color: white;
|
|
24223
|
+
padding: 12px 8px;
|
|
24224
|
+
text-align: left;
|
|
24225
|
+
}
|
|
24226
|
+
th:nth-child(2) {
|
|
24227
|
+
text-align: right;
|
|
24228
|
+
}
|
|
24229
|
+
.footer {
|
|
24230
|
+
margin-top: 24px;
|
|
24231
|
+
padding-top: 16px;
|
|
24232
|
+
border-top: 1px solid #eee;
|
|
24233
|
+
text-align: center;
|
|
24234
|
+
font-size: 12px;
|
|
24235
|
+
color: #999;
|
|
24236
|
+
}
|
|
24237
|
+
@media print {
|
|
24238
|
+
body { padding: 0; }
|
|
24239
|
+
.header { border-radius: 0; }
|
|
24240
|
+
}
|
|
24241
|
+
</style>
|
|
24242
|
+
</head>
|
|
24243
|
+
<body>
|
|
24244
|
+
<div class="header">
|
|
24245
|
+
<h1>${domainIcon} ${deviceLabel}</h1>
|
|
24246
|
+
<div class="subtitle">${domainLabel} Report - Generated ${formatDate3(/* @__PURE__ */ new Date(), locale)}</div>
|
|
24247
|
+
</div>
|
|
24248
|
+
|
|
24249
|
+
${customerName ? `<div class="customer-name" style="margin-bottom: 16px; font-size: 18px;"><strong>Customer:</strong> ${customerName}</div>` : ""}
|
|
24250
|
+
|
|
24251
|
+
${identifier ? `
|
|
24252
|
+
<div class="device-info">
|
|
24253
|
+
<span>ID: ${identifier}</span>
|
|
24254
|
+
<span>Domain: ${domainLabel}</span>
|
|
24255
|
+
<span>Unit: ${domainUnit}</span>
|
|
24256
|
+
</div>
|
|
24257
|
+
` : ""}
|
|
24258
|
+
|
|
24259
|
+
<table>
|
|
24260
|
+
<thead>
|
|
24261
|
+
<tr>
|
|
24262
|
+
<th>Timestamp</th>
|
|
24263
|
+
<th>${domainLabel} (${domainUnit})</th>
|
|
24264
|
+
<th>Unit</th>
|
|
24265
|
+
</tr>
|
|
24266
|
+
</thead>
|
|
24267
|
+
<tbody>
|
|
24268
|
+
${tableRows}
|
|
24269
|
+
${dataPoints.length > 100 ? `<tr><td colspan="3" style="text-align: center; padding: 16px; color: #999;">... and ${dataPoints.length - 100} more rows</td></tr>` : ""}
|
|
24270
|
+
</tbody>
|
|
24271
|
+
</table>
|
|
24272
|
+
|
|
24273
|
+
${statsSection}
|
|
24274
|
+
|
|
24275
|
+
<div class="footer">
|
|
24276
|
+
<p>${config.footerText || "Generated by MyIO Platform"}</p>
|
|
24277
|
+
</div>
|
|
24278
|
+
</body>
|
|
24279
|
+
</html>
|
|
24280
|
+
`;
|
|
24281
|
+
}
|
|
24282
|
+
function buildTemplateExport(params) {
|
|
24283
|
+
const {
|
|
24284
|
+
domain,
|
|
24285
|
+
formatExport,
|
|
24286
|
+
typeExport,
|
|
24287
|
+
colorsPallet,
|
|
24288
|
+
locale = "pt-BR",
|
|
24289
|
+
includeChart = formatExport === "pdf",
|
|
24290
|
+
includeStats = true,
|
|
24291
|
+
headerText,
|
|
24292
|
+
footerText
|
|
24293
|
+
} = params;
|
|
24294
|
+
const colors = {
|
|
24295
|
+
...DEFAULT_COLORS3,
|
|
24296
|
+
...colorsPallet,
|
|
24297
|
+
chartColors: colorsPallet?.chartColors || DEFAULT_COLORS3.chartColors
|
|
24298
|
+
};
|
|
24299
|
+
return {
|
|
24300
|
+
domain,
|
|
24301
|
+
formatExport,
|
|
24302
|
+
typeExport,
|
|
24303
|
+
colors,
|
|
24304
|
+
locale,
|
|
24305
|
+
includeChart,
|
|
24306
|
+
includeStats,
|
|
24307
|
+
headerText: headerText || `${DOMAIN_LABELS[domain]} Report`,
|
|
24308
|
+
footerText: footerText || "Generated by MyIO Platform",
|
|
24309
|
+
domainIcon: DOMAIN_ICONS[domain],
|
|
24310
|
+
domainLabel: DOMAIN_LABELS[domain],
|
|
24311
|
+
domainUnit: DOMAIN_UNITS[domain]
|
|
24312
|
+
};
|
|
24313
|
+
}
|
|
24314
|
+
function myioExportData(data, config, options) {
|
|
24315
|
+
const filename = generateFilename(data, config);
|
|
24316
|
+
let allDataPoints = [];
|
|
24317
|
+
if ("data" in data && Array.isArray(data.data)) {
|
|
24318
|
+
allDataPoints = data.data;
|
|
24319
|
+
} else if ("devices" in data && Array.isArray(data.devices)) {
|
|
24320
|
+
allDataPoints = data.devices.flatMap((d) => d.data);
|
|
24321
|
+
}
|
|
24322
|
+
const stats = calculateStats2(allDataPoints);
|
|
24323
|
+
const instance = {
|
|
24324
|
+
async export() {
|
|
24325
|
+
try {
|
|
24326
|
+
options?.onProgress?.(10, "Generating content...");
|
|
24327
|
+
let content;
|
|
24328
|
+
let mimeType;
|
|
24329
|
+
let finalFilename = filename;
|
|
24330
|
+
switch (config.formatExport) {
|
|
24331
|
+
case "csv":
|
|
24332
|
+
content = generateCSV(data, config);
|
|
24333
|
+
mimeType = "text/csv;charset=utf-8;";
|
|
24334
|
+
break;
|
|
24335
|
+
case "xlsx":
|
|
24336
|
+
content = generateXLSX(data, config);
|
|
24337
|
+
mimeType = "text/csv;charset=utf-8;";
|
|
24338
|
+
finalFilename = filename.replace(".xlsx", ".csv");
|
|
24339
|
+
break;
|
|
24340
|
+
case "pdf":
|
|
24341
|
+
content = generatePDFContent(data, config);
|
|
24342
|
+
mimeType = "text/html;charset=utf-8;";
|
|
24343
|
+
finalFilename = filename.replace(".pdf", ".html");
|
|
24344
|
+
break;
|
|
24345
|
+
default:
|
|
24346
|
+
throw new Error(`Unsupported format: ${config.formatExport}`);
|
|
24347
|
+
}
|
|
24348
|
+
options?.onProgress?.(80, "Creating file...");
|
|
24349
|
+
const bom = config.formatExport === "csv" ? "\uFEFF" : "";
|
|
24350
|
+
const blob = new Blob([bom + content], { type: mimeType });
|
|
24351
|
+
options?.onProgress?.(100, "Export complete");
|
|
24352
|
+
return {
|
|
24353
|
+
success: true,
|
|
24354
|
+
filename: finalFilename,
|
|
24355
|
+
blob,
|
|
24356
|
+
dataUrl: URL.createObjectURL(blob)
|
|
24357
|
+
};
|
|
24358
|
+
} catch (error) {
|
|
24359
|
+
return {
|
|
24360
|
+
success: false,
|
|
24361
|
+
filename,
|
|
24362
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
24363
|
+
};
|
|
24364
|
+
}
|
|
24365
|
+
},
|
|
24366
|
+
async download() {
|
|
24367
|
+
const result = await this.export();
|
|
24368
|
+
if (!result.success || !result.blob) {
|
|
24369
|
+
console.error("Export failed:", result.error);
|
|
24370
|
+
return;
|
|
24371
|
+
}
|
|
24372
|
+
const link = document.createElement("a");
|
|
24373
|
+
link.href = URL.createObjectURL(result.blob);
|
|
24374
|
+
link.download = result.filename;
|
|
24375
|
+
link.style.display = "none";
|
|
24376
|
+
document.body.appendChild(link);
|
|
24377
|
+
link.click();
|
|
24378
|
+
document.body.removeChild(link);
|
|
24379
|
+
URL.revokeObjectURL(link.href);
|
|
24380
|
+
},
|
|
24381
|
+
async preview() {
|
|
24382
|
+
if (config.formatExport !== "pdf") {
|
|
24383
|
+
return null;
|
|
24384
|
+
}
|
|
24385
|
+
const result = await this.export();
|
|
24386
|
+
return result.dataUrl || null;
|
|
24387
|
+
},
|
|
24388
|
+
getStats() {
|
|
24389
|
+
return stats;
|
|
24390
|
+
},
|
|
24391
|
+
getFilename() {
|
|
24392
|
+
return filename;
|
|
24393
|
+
}
|
|
24394
|
+
};
|
|
24395
|
+
if (options?.autoDownload) {
|
|
24396
|
+
instance.download();
|
|
24397
|
+
}
|
|
24398
|
+
return instance;
|
|
24399
|
+
}
|
|
24400
|
+
var EXPORT_DEFAULT_COLORS = DEFAULT_COLORS3;
|
|
24401
|
+
var EXPORT_DOMAIN_ICONS = DOMAIN_ICONS;
|
|
24402
|
+
var EXPORT_DOMAIN_LABELS = DOMAIN_LABELS;
|
|
24403
|
+
var EXPORT_DOMAIN_UNITS = DOMAIN_UNITS;
|
|
22570
24404
|
// Annotate the CommonJS export names for ESM import in node:
|
|
22571
24405
|
0 && (module.exports = {
|
|
22572
24406
|
CHART_COLORS,
|
|
24407
|
+
CONSUMPTION_CHART_COLORS,
|
|
24408
|
+
CONSUMPTION_CHART_DEFAULTS,
|
|
24409
|
+
CONSUMPTION_THEME_COLORS,
|
|
22573
24410
|
ConnectionStatusType,
|
|
22574
24411
|
DEFAULT_CLAMP_RANGE,
|
|
22575
24412
|
DeviceStatusType,
|
|
24413
|
+
EXPORT_DEFAULT_COLORS,
|
|
24414
|
+
EXPORT_DOMAIN_ICONS,
|
|
24415
|
+
EXPORT_DOMAIN_LABELS,
|
|
24416
|
+
EXPORT_DOMAIN_UNITS,
|
|
22576
24417
|
MyIOChartModal,
|
|
22577
24418
|
MyIODraggableCard,
|
|
22578
24419
|
MyIOSelectionStore,
|
|
@@ -22584,11 +24425,13 @@ function openTemperatureSettingsModal(params) {
|
|
|
22584
24425
|
averageByDay,
|
|
22585
24426
|
buildListItemsThingsboardByUniqueDatasource,
|
|
22586
24427
|
buildMyioIngestionAuth,
|
|
24428
|
+
buildTemplateExport,
|
|
22587
24429
|
buildWaterReportCSV,
|
|
22588
24430
|
buildWaterStoresCSV,
|
|
22589
24431
|
calcDeltaPercent,
|
|
22590
24432
|
calculateDeviceStatus,
|
|
22591
24433
|
calculateDeviceStatusWithRanges,
|
|
24434
|
+
calculateExportStats,
|
|
22592
24435
|
calculateStats,
|
|
22593
24436
|
clampTemperature,
|
|
22594
24437
|
classify,
|
|
@@ -22596,8 +24439,11 @@ function openTemperatureSettingsModal(params) {
|
|
|
22596
24439
|
classifyWaterLabels,
|
|
22597
24440
|
clearAllAuthCaches,
|
|
22598
24441
|
connectionStatusIcons,
|
|
24442
|
+
createConsumption7DaysChart,
|
|
24443
|
+
createConsumptionModal,
|
|
22599
24444
|
createDateRangePicker,
|
|
22600
24445
|
createInputDateRangePickerInsideDIV,
|
|
24446
|
+
createModalHeader,
|
|
22601
24447
|
decodePayload,
|
|
22602
24448
|
decodePayloadBase64Xor,
|
|
22603
24449
|
detectDeviceType,
|
|
@@ -22611,6 +24457,7 @@ function openTemperatureSettingsModal(params) {
|
|
|
22611
24457
|
fetchThingsboardCustomerAttrsFromStorage,
|
|
22612
24458
|
fetchThingsboardCustomerServerScopeAttrs,
|
|
22613
24459
|
findValue,
|
|
24460
|
+
findValueWithDefault,
|
|
22614
24461
|
fmtPerc,
|
|
22615
24462
|
fmtPercLegacy,
|
|
22616
24463
|
formatAllInSameUnit,
|
|
@@ -22618,18 +24465,24 @@ function openTemperatureSettingsModal(params) {
|
|
|
22618
24465
|
formatDateForInput,
|
|
22619
24466
|
formatDateToYMD,
|
|
22620
24467
|
formatDateWithTimezoneOffset,
|
|
24468
|
+
formatDuration,
|
|
22621
24469
|
formatEnergy,
|
|
22622
24470
|
formatNumberReadable,
|
|
24471
|
+
formatRelativeTime,
|
|
22623
24472
|
formatTankHeadFromCm,
|
|
22624
24473
|
formatTemperature,
|
|
24474
|
+
formatWater,
|
|
22625
24475
|
formatWaterByGroup,
|
|
22626
24476
|
formatWaterVolumeM3,
|
|
24477
|
+
formatarDuracao,
|
|
24478
|
+
generateExportFilename,
|
|
22627
24479
|
getAuthCacheStats,
|
|
22628
24480
|
getAvailableContexts,
|
|
22629
24481
|
getConnectionStatusIcon,
|
|
22630
24482
|
getDateRangeArray,
|
|
22631
24483
|
getDeviceStatusIcon,
|
|
22632
24484
|
getDeviceStatusInfo,
|
|
24485
|
+
getModalHeaderStyles,
|
|
22633
24486
|
getSaoPauloISOString,
|
|
22634
24487
|
getSaoPauloISOStringFixed,
|
|
22635
24488
|
getValueByDatakey,
|
|
@@ -22641,8 +24494,10 @@ function openTemperatureSettingsModal(params) {
|
|
|
22641
24494
|
isValidConnectionStatus,
|
|
22642
24495
|
isValidDeviceStatus,
|
|
22643
24496
|
isWaterCategory,
|
|
24497
|
+
mapConnectionStatus,
|
|
22644
24498
|
mapDeviceStatusToCardStatus,
|
|
22645
24499
|
mapDeviceToConnectionStatus,
|
|
24500
|
+
myioExportData,
|
|
22646
24501
|
normalizeRecipients,
|
|
22647
24502
|
numbers,
|
|
22648
24503
|
openDashboardPopup,
|