myio-js-library 0.1.160 → 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 +2121 -163
- package/dist/index.d.cts +889 -2
- package/dist/index.js +2100 -163
- package/dist/myio-js-library.umd.js +2099 -162
- package/dist/myio-js-library.umd.min.js +1 -1
- package/package.json +1 -1
|
@@ -650,6 +650,10 @@
|
|
|
650
650
|
}
|
|
651
651
|
|
|
652
652
|
// src/format/water.ts
|
|
653
|
+
function formatWater(value) {
|
|
654
|
+
const num = Number(value) || 0;
|
|
655
|
+
return `${num.toFixed(2)} m\xB3`;
|
|
656
|
+
}
|
|
653
657
|
function formatWaterVolumeM3(value, locale = "pt-BR") {
|
|
654
658
|
if (value === null || value === void 0 || isNaN(value)) {
|
|
655
659
|
return "-";
|
|
@@ -727,6 +731,76 @@
|
|
|
727
731
|
};
|
|
728
732
|
}
|
|
729
733
|
|
|
734
|
+
// src/format/time.ts
|
|
735
|
+
function formatRelativeTime(timestamp) {
|
|
736
|
+
if (!timestamp || timestamp <= 0) {
|
|
737
|
+
return "\u2014";
|
|
738
|
+
}
|
|
739
|
+
const now = Date.now();
|
|
740
|
+
const diffSeconds = Math.round((now - timestamp) / 1e3);
|
|
741
|
+
if (diffSeconds < 10) {
|
|
742
|
+
return "agora";
|
|
743
|
+
}
|
|
744
|
+
if (diffSeconds < 60) {
|
|
745
|
+
return `h\xE1 ${diffSeconds}s`;
|
|
746
|
+
}
|
|
747
|
+
const diffMinutes = Math.round(diffSeconds / 60);
|
|
748
|
+
if (diffMinutes === 1) {
|
|
749
|
+
return "h\xE1 1 min";
|
|
750
|
+
}
|
|
751
|
+
if (diffMinutes < 60) {
|
|
752
|
+
return `h\xE1 ${diffMinutes} mins`;
|
|
753
|
+
}
|
|
754
|
+
const diffHours = Math.round(diffMinutes / 60);
|
|
755
|
+
if (diffHours === 1) {
|
|
756
|
+
return "h\xE1 1 hora";
|
|
757
|
+
}
|
|
758
|
+
if (diffHours < 24) {
|
|
759
|
+
return `h\xE1 ${diffHours} horas`;
|
|
760
|
+
}
|
|
761
|
+
const diffDays = Math.round(diffHours / 24);
|
|
762
|
+
if (diffDays === 1) {
|
|
763
|
+
return "ontem";
|
|
764
|
+
}
|
|
765
|
+
if (diffDays <= 30) {
|
|
766
|
+
return `h\xE1 ${diffDays} dias`;
|
|
767
|
+
}
|
|
768
|
+
return new Date(timestamp).toLocaleDateString("pt-BR");
|
|
769
|
+
}
|
|
770
|
+
function formatarDuracao(ms) {
|
|
771
|
+
if (typeof ms !== "number" || ms < 0 || !isFinite(ms)) {
|
|
772
|
+
return "0s";
|
|
773
|
+
}
|
|
774
|
+
if (ms === 0) {
|
|
775
|
+
return "0s";
|
|
776
|
+
}
|
|
777
|
+
const segundos = Math.floor(ms / 1e3 % 60);
|
|
778
|
+
const minutos = Math.floor(ms / (1e3 * 60) % 60);
|
|
779
|
+
const horas = Math.floor(ms / (1e3 * 60 * 60) % 24);
|
|
780
|
+
const dias = Math.floor(ms / (1e3 * 60 * 60 * 24));
|
|
781
|
+
const parts = [];
|
|
782
|
+
if (dias > 0) {
|
|
783
|
+
parts.push(`${dias}d`);
|
|
784
|
+
if (horas > 0) {
|
|
785
|
+
parts.push(`${horas}h`);
|
|
786
|
+
}
|
|
787
|
+
} else if (horas > 0) {
|
|
788
|
+
parts.push(`${horas}h`);
|
|
789
|
+
if (minutos > 0) {
|
|
790
|
+
parts.push(`${minutos}m`);
|
|
791
|
+
}
|
|
792
|
+
} else if (minutos > 0) {
|
|
793
|
+
parts.push(`${minutos}m`);
|
|
794
|
+
if (segundos > 0) {
|
|
795
|
+
parts.push(`${segundos}s`);
|
|
796
|
+
}
|
|
797
|
+
} else {
|
|
798
|
+
parts.push(`${segundos}s`);
|
|
799
|
+
}
|
|
800
|
+
return parts.length > 0 ? parts.join(" ") : "0s";
|
|
801
|
+
}
|
|
802
|
+
var formatDuration = formatarDuracao;
|
|
803
|
+
|
|
730
804
|
// src/date/ymd.ts
|
|
731
805
|
function formatDateToYMD(date) {
|
|
732
806
|
if (!date) {
|
|
@@ -1235,6 +1309,11 @@
|
|
|
1235
1309
|
}
|
|
1236
1310
|
return getValueByDatakey(data, keyOrPath);
|
|
1237
1311
|
}
|
|
1312
|
+
function findValueWithDefault(values, key, defaultValue = null) {
|
|
1313
|
+
if (!Array.isArray(values)) return defaultValue;
|
|
1314
|
+
const found = values.find((v) => v.key === key || v.dataType === key);
|
|
1315
|
+
return found ? found.value : defaultValue;
|
|
1316
|
+
}
|
|
1238
1317
|
|
|
1239
1318
|
// src/utils/deviceStatus.js
|
|
1240
1319
|
var DeviceStatusType = {
|
|
@@ -1291,6 +1370,16 @@
|
|
|
1291
1370
|
}
|
|
1292
1371
|
return ConnectionStatusType.CONNECTED;
|
|
1293
1372
|
}
|
|
1373
|
+
function mapConnectionStatus(rawStatus) {
|
|
1374
|
+
const statusLower = String(rawStatus || "").toLowerCase().trim();
|
|
1375
|
+
if (statusLower === "online" || statusLower === "ok" || statusLower === "running") {
|
|
1376
|
+
return "online";
|
|
1377
|
+
}
|
|
1378
|
+
if (statusLower === "waiting" || statusLower === "connecting" || statusLower === "pending") {
|
|
1379
|
+
return "waiting";
|
|
1380
|
+
}
|
|
1381
|
+
return "offline";
|
|
1382
|
+
}
|
|
1294
1383
|
function mapDeviceStatusToCardStatus(deviceStatus) {
|
|
1295
1384
|
const statusMap = {
|
|
1296
1385
|
[DeviceStatusType.POWER_ON]: "ok",
|
|
@@ -2059,8 +2148,9 @@
|
|
|
2059
2148
|
lastValue: Number(entity.lastValue) || 0,
|
|
2060
2149
|
unit: entity.unit || "",
|
|
2061
2150
|
status: entity.status || "unknown",
|
|
2062
|
-
ingestionId: entity.ingestionId || entity.id
|
|
2151
|
+
ingestionId: entity.ingestionId || entity.id,
|
|
2063
2152
|
// ⭐ ADD: Store ingestionId for API calls
|
|
2153
|
+
customerName: entity.customerName || ""
|
|
2064
2154
|
};
|
|
2065
2155
|
this.entities.set(entity.id, normalizedEntity);
|
|
2066
2156
|
}
|
|
@@ -4674,6 +4764,13 @@
|
|
|
4674
4764
|
viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">
|
|
4675
4765
|
<rect x="5" y="4" width="14" height="16" rx="2"/>
|
|
4676
4766
|
<path d="M7 12c1.5 1 3 .9 5-.3s3.5-1.3 5 .3v4H7v-4Z" fill="#fff" fill-opacity=".25"/>
|
|
4767
|
+
</svg>`,
|
|
4768
|
+
waterDrop: `
|
|
4769
|
+
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
4770
|
+
<g id="water" transform="translate(-4 -2)">
|
|
4771
|
+
<path id="secondary" fill="#2ca9bc" d="M19,14A7,7,0,1,1,5,14C5,8,12,3,12,3S19,8,19,14Z"/>
|
|
4772
|
+
<path id="primary" d="M19,14A7,7,0,1,1,5,14C5,8,12,3,12,3S19,8,19,14Z" fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
|
|
4773
|
+
</g>
|
|
4677
4774
|
</svg>`,
|
|
4678
4775
|
// ⋮ Kebab (for actions menu)
|
|
4679
4776
|
kebab: `
|
|
@@ -4702,6 +4799,7 @@
|
|
|
4702
4799
|
var DEFAULT_I18N = {
|
|
4703
4800
|
// Status labels with icons
|
|
4704
4801
|
in_operation: "\u26A1 Normal",
|
|
4802
|
+
in_operation_water: "\u{1F4A7} Normal",
|
|
4705
4803
|
standby: "\u{1F4A4} Em standby",
|
|
4706
4804
|
alert: "\u26A0\uFE0F Alerta",
|
|
4707
4805
|
failure: "\u{1F6A8} Falha",
|
|
@@ -4758,7 +4856,13 @@
|
|
|
4758
4856
|
}
|
|
4759
4857
|
};
|
|
4760
4858
|
}
|
|
4761
|
-
function getIconSvg(deviceType) {
|
|
4859
|
+
function getIconSvg(deviceType, domain) {
|
|
4860
|
+
if (domain === "water") {
|
|
4861
|
+
return Icons.waterDrop;
|
|
4862
|
+
}
|
|
4863
|
+
if (domain === "temperature") {
|
|
4864
|
+
return Icons.thermometer;
|
|
4865
|
+
}
|
|
4762
4866
|
return ICON_MAP[deviceType] || ICON_MAP.DEFAULT;
|
|
4763
4867
|
}
|
|
4764
4868
|
function formatPower(valueInWatts) {
|
|
@@ -4798,9 +4902,23 @@
|
|
|
4798
4902
|
const percentage = numericConsumption / numericTarget * 100;
|
|
4799
4903
|
return percentage;
|
|
4800
4904
|
}
|
|
4801
|
-
function getStatusInfo(deviceStatus, i18n) {
|
|
4905
|
+
function getStatusInfo(deviceStatus, i18n, domain) {
|
|
4802
4906
|
switch (deviceStatus) {
|
|
4907
|
+
// --- Novos Status de Temperatura ---
|
|
4908
|
+
case "normal":
|
|
4909
|
+
return { chipClass: "chip--ok", label: "Normal" };
|
|
4910
|
+
// Verde/Azul
|
|
4911
|
+
case "cold":
|
|
4912
|
+
return { chipClass: "chip--standby", label: "Frio" };
|
|
4913
|
+
// Azul claro/Ciano
|
|
4914
|
+
case "hot":
|
|
4915
|
+
return { chipClass: "chip--alert", label: "Quente" };
|
|
4916
|
+
// Laranja/Amarelo
|
|
4917
|
+
// --- Status Existentes ---
|
|
4803
4918
|
case DeviceStatusType.POWER_ON:
|
|
4919
|
+
if (domain === "water") {
|
|
4920
|
+
return { chipClass: "chip--ok", label: i18n.in_operation_water };
|
|
4921
|
+
}
|
|
4804
4922
|
return { chipClass: "chip--ok", label: i18n.in_operation };
|
|
4805
4923
|
case DeviceStatusType.STANDBY:
|
|
4806
4924
|
return { chipClass: "chip--standby", label: i18n.standby };
|
|
@@ -4813,6 +4931,7 @@
|
|
|
4813
4931
|
return { chipClass: "chip--alert", label: i18n.maintenance };
|
|
4814
4932
|
case DeviceStatusType.NOT_INSTALLED:
|
|
4815
4933
|
return { chipClass: "chip--offline", label: i18n.not_installed };
|
|
4934
|
+
// Default (Cai aqui se não achar 'normal', 'hot' etc)
|
|
4816
4935
|
case DeviceStatusType.NO_INFO:
|
|
4817
4936
|
default:
|
|
4818
4937
|
return { chipClass: "chip--offline", label: i18n.offline };
|
|
@@ -4844,6 +4963,14 @@
|
|
|
4844
4963
|
}
|
|
4845
4964
|
function getStatusDotClass(deviceStatus) {
|
|
4846
4965
|
switch (deviceStatus) {
|
|
4966
|
+
// --- Novos Status de Temperatura ---
|
|
4967
|
+
case "normal":
|
|
4968
|
+
return "dot--ok";
|
|
4969
|
+
case "cold":
|
|
4970
|
+
return "dot--standby";
|
|
4971
|
+
case "hot":
|
|
4972
|
+
return "dot--alert";
|
|
4973
|
+
// --- Status Existentes ---
|
|
4847
4974
|
case DeviceStatusType.POWER_ON:
|
|
4848
4975
|
return "dot--ok";
|
|
4849
4976
|
case DeviceStatusType.STANDBY:
|
|
@@ -4871,7 +4998,7 @@
|
|
|
4871
4998
|
header.className = "myio-ho-card__header";
|
|
4872
4999
|
const iconContainer = document.createElement("div");
|
|
4873
5000
|
iconContainer.className = "myio-ho-card__icon";
|
|
4874
|
-
iconContainer.innerHTML = getIconSvg(entityObject.deviceType);
|
|
5001
|
+
iconContainer.innerHTML = getIconSvg(entityObject.deviceType, entityObject.domain);
|
|
4875
5002
|
header.appendChild(iconContainer);
|
|
4876
5003
|
const titleSection = document.createElement("div");
|
|
4877
5004
|
titleSection.className = "myio-ho-card__title";
|
|
@@ -4993,7 +5120,11 @@
|
|
|
4993
5120
|
powerMetric.appendChild(statusDot);
|
|
4994
5121
|
const powerLabel = document.createElement("div");
|
|
4995
5122
|
powerLabel.className = "label";
|
|
4996
|
-
|
|
5123
|
+
if (entityObject.domain === "water") {
|
|
5124
|
+
powerLabel.textContent = "Leitura";
|
|
5125
|
+
} else {
|
|
5126
|
+
powerLabel.textContent = i18n.instantaneous_power || "Pot\xEAncia";
|
|
5127
|
+
}
|
|
4997
5128
|
powerMetric.appendChild(powerLabel);
|
|
4998
5129
|
const powerVal = document.createElement("div");
|
|
4999
5130
|
powerVal.className = "val";
|
|
@@ -5017,7 +5148,7 @@
|
|
|
5017
5148
|
return true;
|
|
5018
5149
|
}
|
|
5019
5150
|
function paint(root, state) {
|
|
5020
|
-
const { entityObject, i18n, delayTimeConnectionInMins } = state;
|
|
5151
|
+
const { entityObject, i18n, delayTimeConnectionInMins, isSelected } = state;
|
|
5021
5152
|
if (entityObject.connectionStatus) {
|
|
5022
5153
|
if (entityObject.connectionStatus === "offline") {
|
|
5023
5154
|
entityObject.deviceStatus = DeviceStatusType.NO_INFO;
|
|
@@ -5029,16 +5160,23 @@
|
|
|
5029
5160
|
}
|
|
5030
5161
|
const stateClass = getCardStateClass(entityObject.deviceStatus);
|
|
5031
5162
|
root.className = `myio-ho-card ${stateClass}`;
|
|
5032
|
-
const statusInfo = getStatusInfo(entityObject.deviceStatus, i18n);
|
|
5163
|
+
const statusInfo = getStatusInfo(entityObject.deviceStatus, i18n, entityObject.domain);
|
|
5033
5164
|
const chip = root.querySelector(".chip");
|
|
5034
5165
|
chip.className = `chip ${statusInfo.chipClass}`;
|
|
5035
|
-
chip.
|
|
5166
|
+
chip.innerHTML = statusInfo.label;
|
|
5036
5167
|
const primaryValue = formatValueByDomain(entityObject.val, entityObject.domain);
|
|
5037
5168
|
const numSpan = root.querySelector(".myio-ho-card__value .num");
|
|
5038
5169
|
root.querySelector(".myio-ho-card__value .unit");
|
|
5039
5170
|
numSpan.textContent = primaryValue;
|
|
5040
5171
|
const barContainer = root.querySelector(".bar");
|
|
5041
5172
|
const effContainer = root.querySelector(".myio-ho-card__eff");
|
|
5173
|
+
if (state.enableSelection) {
|
|
5174
|
+
const checkbox = root.querySelector('.myio-ho-card__select input[type="checkbox"]');
|
|
5175
|
+
if (checkbox) {
|
|
5176
|
+
checkbox.checked = !!isSelected;
|
|
5177
|
+
}
|
|
5178
|
+
root.classList.toggle("is-selected", !!isSelected);
|
|
5179
|
+
}
|
|
5042
5180
|
const targetValue = entityObject.consumptionTargetValue;
|
|
5043
5181
|
if (targetValue) {
|
|
5044
5182
|
barContainer.style.display = "";
|
|
@@ -5060,9 +5198,14 @@
|
|
|
5060
5198
|
}
|
|
5061
5199
|
const powerVal = root.querySelector(".myio-ho-card__footer .metric:nth-child(2) .val");
|
|
5062
5200
|
if (powerVal) {
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5201
|
+
if (entityObject.domain === "water") {
|
|
5202
|
+
const pulses = entityObject.pulses ?? 0;
|
|
5203
|
+
powerVal.textContent = `${pulses} L`;
|
|
5204
|
+
} else {
|
|
5205
|
+
const instantPower = entityObject.instantaneousPower ?? entityObject.consumption_power ?? null;
|
|
5206
|
+
const powerFormatted = formatPower(instantPower);
|
|
5207
|
+
powerVal.textContent = instantPower !== null ? `${powerFormatted.num} ${powerFormatted.unit}` : "-";
|
|
5208
|
+
}
|
|
5066
5209
|
}
|
|
5067
5210
|
const statusDot = root.querySelector(".myio-ho-card__footer .metric:nth-child(2) .status-dot");
|
|
5068
5211
|
if (statusDot) {
|
|
@@ -5122,6 +5265,26 @@
|
|
|
5122
5265
|
callbacks.handleActionSettings(e, entityObject);
|
|
5123
5266
|
});
|
|
5124
5267
|
}
|
|
5268
|
+
const MyIOSelectionStore2 = window.MyIOLibrary?.MyIOSelectionStore || window.MyIOSelectionStore;
|
|
5269
|
+
if (MyIOSelectionStore2) {
|
|
5270
|
+
const onSelectionChange = () => {
|
|
5271
|
+
const selectedIds = MyIOSelectionStore2.getSelectedIds();
|
|
5272
|
+
const isSelected = selectedIds.includes(entityObject.entityId);
|
|
5273
|
+
if (state.isSelected !== isSelected) {
|
|
5274
|
+
state.isSelected = isSelected;
|
|
5275
|
+
paint(root, state);
|
|
5276
|
+
}
|
|
5277
|
+
};
|
|
5278
|
+
MyIOSelectionStore2.on("selection:change", onSelectionChange);
|
|
5279
|
+
root._selectionListener = onSelectionChange;
|
|
5280
|
+
}
|
|
5281
|
+
root._cleanup = () => {
|
|
5282
|
+
document.removeEventListener("click", closeMenu);
|
|
5283
|
+
document.removeEventListener("keydown", closeMenu);
|
|
5284
|
+
if (MyIOSelectionStore2 && root._selectionListener) {
|
|
5285
|
+
MyIOSelectionStore2.off("selection:change", root._selectionListener);
|
|
5286
|
+
}
|
|
5287
|
+
};
|
|
5125
5288
|
const checkbox = root.querySelector('.myio-ho-card__select input[type="checkbox"]');
|
|
5126
5289
|
if (checkbox && callbacks.handleSelect) {
|
|
5127
5290
|
checkbox.addEventListener("change", (e) => {
|
|
@@ -5189,6 +5352,7 @@
|
|
|
5189
5352
|
ensureCss();
|
|
5190
5353
|
const state = normalizeParams(params);
|
|
5191
5354
|
const root = buildDOM(state);
|
|
5355
|
+
state.isSelected = params.isSelected || false;
|
|
5192
5356
|
containerEl.appendChild(root);
|
|
5193
5357
|
bindEvents(root, state, state.callbacks);
|
|
5194
5358
|
paint(root, state);
|
|
@@ -7615,14 +7779,14 @@ ${rangeText}`;
|
|
|
7615
7779
|
return `${value.toFixed(config.decimals)} ${config.unit}`;
|
|
7616
7780
|
}
|
|
7617
7781
|
function initializeChart() {
|
|
7618
|
-
const
|
|
7619
|
-
if (!
|
|
7782
|
+
const Chart2 = window.Chart;
|
|
7783
|
+
if (!Chart2) {
|
|
7620
7784
|
console.warn("[RealTimeTelemetry] Chart.js not loaded");
|
|
7621
7785
|
return;
|
|
7622
7786
|
}
|
|
7623
7787
|
chartContainer.style.display = "block";
|
|
7624
7788
|
const config = TELEMETRY_CONFIG[selectedChartKey] || { label: selectedChartKey, unit: "" };
|
|
7625
|
-
chart = new
|
|
7789
|
+
chart = new Chart2(chartCanvas, {
|
|
7626
7790
|
type: "line",
|
|
7627
7791
|
data: {
|
|
7628
7792
|
datasets: [{
|
|
@@ -10487,8 +10651,8 @@ ${rangeText}`;
|
|
|
10487
10651
|
peakEl.textContent = `${strings.maximum}: ${peak.formattedValue} kW ${peak.key ? `(${peak.key}) ` : ""}${strings.at} ${dateStr}`;
|
|
10488
10652
|
peakEl.style.display = "block";
|
|
10489
10653
|
}
|
|
10490
|
-
const
|
|
10491
|
-
|
|
10654
|
+
const Chart2 = window.Chart;
|
|
10655
|
+
Chart2.register(window.ChartZoom);
|
|
10492
10656
|
if (chart) {
|
|
10493
10657
|
chart.data.datasets = chartData.series.map((series) => ({
|
|
10494
10658
|
label: series.label,
|
|
@@ -10540,7 +10704,7 @@ ${rangeText}`;
|
|
|
10540
10704
|
};
|
|
10541
10705
|
chart.update();
|
|
10542
10706
|
} else {
|
|
10543
|
-
chart = new
|
|
10707
|
+
chart = new Chart2(chartCanvas, {
|
|
10544
10708
|
type: "line",
|
|
10545
10709
|
data: {
|
|
10546
10710
|
datasets: chartData.series.map((series) => ({
|
|
@@ -10799,11 +10963,13 @@ ${rangeText}`;
|
|
|
10799
10963
|
console.log("[EnergyModalView] Bar mode toggled to:", this.currentBarMode);
|
|
10800
10964
|
}
|
|
10801
10965
|
/**
|
|
10802
|
-
|
|
10803
|
-
|
|
10966
|
+
* Applies the current theme to the modal and charts (Updated with #root override)
|
|
10967
|
+
*/
|
|
10804
10968
|
applyTheme() {
|
|
10805
10969
|
const themeToggleBtn = document.getElementById("theme-toggle-btn");
|
|
10806
|
-
const modalContent =
|
|
10970
|
+
const modalContent = this.container;
|
|
10971
|
+
const externalBody = this.container?.closest(".myio-modal-body") || document.querySelector(".myio-modal-body");
|
|
10972
|
+
const rootDiv = document.querySelector("#root > div");
|
|
10807
10973
|
if (themeToggleBtn) {
|
|
10808
10974
|
const sunIcon = themeToggleBtn.querySelector(".myio-theme-icon-sun");
|
|
10809
10975
|
const moonIcon = themeToggleBtn.querySelector(".myio-theme-icon-moon");
|
|
@@ -10816,6 +10982,14 @@ ${rangeText}`;
|
|
|
10816
10982
|
moonIcon.style.opacity = "0";
|
|
10817
10983
|
moonIcon.style.transform = "translate(-50%, -50%) rotate(90deg) scale(0)";
|
|
10818
10984
|
}
|
|
10985
|
+
if (externalBody) {
|
|
10986
|
+
externalBody.style.backgroundColor = "#ffffff";
|
|
10987
|
+
externalBody.style.color = "#1f2937";
|
|
10988
|
+
}
|
|
10989
|
+
if (rootDiv) {
|
|
10990
|
+
rootDiv.style.backgroundColor = "#ffffff";
|
|
10991
|
+
rootDiv.style.color = "#1f2937";
|
|
10992
|
+
}
|
|
10819
10993
|
} else {
|
|
10820
10994
|
if (sunIcon) {
|
|
10821
10995
|
sunIcon.style.opacity = "0";
|
|
@@ -10825,6 +10999,14 @@ ${rangeText}`;
|
|
|
10825
10999
|
moonIcon.style.opacity = "1";
|
|
10826
11000
|
moonIcon.style.transform = "translate(-50%, -50%) rotate(0deg) scale(1)";
|
|
10827
11001
|
}
|
|
11002
|
+
if (externalBody) {
|
|
11003
|
+
externalBody.style.backgroundColor = "#1f1f1f";
|
|
11004
|
+
externalBody.style.color = "#f3f4f6";
|
|
11005
|
+
}
|
|
11006
|
+
if (rootDiv) {
|
|
11007
|
+
rootDiv.style.backgroundColor = "#1f1f1f";
|
|
11008
|
+
rootDiv.style.color = "#f3f4f6";
|
|
11009
|
+
}
|
|
10828
11010
|
}
|
|
10829
11011
|
}
|
|
10830
11012
|
if (modalContent) {
|
|
@@ -11053,7 +11235,7 @@ ${rangeText}`;
|
|
|
11053
11235
|
${this.config.params.mode === "comparison" ? `
|
|
11054
11236
|
<!-- RFC-0097: Granularity Selector (only 1h and 1d supported) -->
|
|
11055
11237
|
<div class="myio-granularity-selector" style="display: flex; align-items: center; gap: 4px; margin-left: 8px; padding: 4px 8px; background: rgba(0,0,0,0.05); border-radius: 8px;">
|
|
11056
|
-
<span style="font-size: 11px;
|
|
11238
|
+
<span class="myio-label-secondary" style="font-size: 11px; margin-right: 4px; white-space: nowrap;">Granularidade:</span>
|
|
11057
11239
|
<button class="myio-btn myio-btn-granularity ${this.currentGranularity === "1h" ? "active" : ""}" data-granularity="1h" title="Hora">1h</button>
|
|
11058
11240
|
<button class="myio-btn myio-btn-granularity ${this.currentGranularity === "1d" ? "active" : ""}" data-granularity="1d" title="Dia">1d</button>
|
|
11059
11241
|
</div>
|
|
@@ -11735,93 +11917,159 @@ ${rangeText}`;
|
|
|
11735
11917
|
return i18n[key] || DEFAULT_I18N2[key];
|
|
11736
11918
|
}
|
|
11737
11919
|
/**
|
|
11738
|
-
|
|
11739
|
-
|
|
11920
|
+
* Gets modal styles with fixed Dark/Light contrast
|
|
11921
|
+
*/
|
|
11922
|
+
/**
|
|
11923
|
+
* Gets modal styles with fixed Chart Border for Light Mode
|
|
11924
|
+
*/
|
|
11740
11925
|
getModalStyles() {
|
|
11741
11926
|
const styles = this.config.params.styles || {};
|
|
11927
|
+
const defaultPrimary = styles.primaryColor || "#4A148C";
|
|
11928
|
+
const defaultFont = styles.fontFamily || '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
|
11742
11929
|
return `
|
|
11930
|
+
/* --- VARI\xC1VEIS DE TEMA (LIGHT MODE - PADR\xC3O) --- */
|
|
11743
11931
|
.myio-energy-modal-scope {
|
|
11744
|
-
--myio-energy-primary: ${
|
|
11745
|
-
--myio-energy-
|
|
11746
|
-
--myio-energy-
|
|
11747
|
-
|
|
11748
|
-
|
|
11749
|
-
--myio-energy-
|
|
11932
|
+
--myio-energy-primary: ${defaultPrimary};
|
|
11933
|
+
--myio-energy-font: ${defaultFont};
|
|
11934
|
+
--myio-energy-radius: 8px;
|
|
11935
|
+
|
|
11936
|
+
/* Cores Gerais (Light Mode) */
|
|
11937
|
+
--myio-energy-bg: #ffffff;
|
|
11938
|
+
--myio-energy-text: #1f2937;
|
|
11939
|
+
--myio-energy-text-secondary: #6b7280;
|
|
11940
|
+
|
|
11941
|
+
/* Borda GERAL (para bot\xF5es e inputs) - Cinza suave */
|
|
11942
|
+
--myio-energy-border: #e5e7eb;
|
|
11943
|
+
|
|
11944
|
+
/* Borda ESPEC\xCDFICA DO GR\xC1FICO (Aqui est\xE1 a corre\xE7\xE3o) */
|
|
11945
|
+
/* No Light Mode, definimos como transparente ou branco para "sumir" */
|
|
11946
|
+
--myio-chart-border: transparent;
|
|
11947
|
+
|
|
11948
|
+
--myio-energy-btn-bg: #f3f4f6;
|
|
11949
|
+
--myio-energy-btn-hover: #e5e7eb;
|
|
11950
|
+
--myio-energy-input-bg: #ffffff;
|
|
11951
|
+
--myio-granularity-bg: #f9fafb;
|
|
11750
11952
|
|
|
11751
11953
|
font-family: var(--myio-energy-font);
|
|
11954
|
+
background-color: var(--myio-energy-bg);
|
|
11752
11955
|
color: var(--myio-energy-text);
|
|
11753
11956
|
height: -webkit-fill-available;
|
|
11957
|
+
transition: background-color 0.3s ease, color 0.3s ease;
|
|
11754
11958
|
}
|
|
11755
11959
|
|
|
11756
|
-
|
|
11757
|
-
|
|
11758
|
-
|
|
11759
|
-
|
|
11760
|
-
|
|
11761
|
-
|
|
11762
|
-
|
|
11763
|
-
|
|
11764
|
-
|
|
11765
|
-
}
|
|
11960
|
+
/* --- DARK MODE OVERRIDES --- */
|
|
11961
|
+
.myio-energy-modal-scope[data-theme="dark"] {
|
|
11962
|
+
--myio-energy-bg: #1f1f1f;
|
|
11963
|
+
--myio-energy-text: #f3f4f6;
|
|
11964
|
+
--myio-energy-text-secondary: #9ca3af;
|
|
11965
|
+
|
|
11966
|
+
/* No Dark Mode, as bordas precisam aparecer */
|
|
11967
|
+
--myio-energy-border: #374151;
|
|
11968
|
+
--myio-chart-border: #374151; /* Borda vis\xEDvel no escuro */
|
|
11766
11969
|
|
|
11767
|
-
|
|
11768
|
-
|
|
11970
|
+
--myio-energy-btn-bg: #374151;
|
|
11971
|
+
--myio-energy-btn-hover: #4b5563;
|
|
11972
|
+
--myio-energy-input-bg: #111827;
|
|
11973
|
+
--myio-granularity-bg: #111827;
|
|
11769
11974
|
}
|
|
11770
11975
|
|
|
11771
|
-
|
|
11772
|
-
opacity: 0.5;
|
|
11773
|
-
cursor: not-allowed;
|
|
11774
|
-
}
|
|
11976
|
+
/* --- COMPONENTES --- */
|
|
11775
11977
|
|
|
11776
|
-
.myio-
|
|
11777
|
-
|
|
11778
|
-
|
|
11779
|
-
|
|
11978
|
+
.myio-energy-chart-container {
|
|
11979
|
+
flex: 1 !important;
|
|
11980
|
+
min-height: 353px !important;
|
|
11981
|
+
height: 353px !important;
|
|
11982
|
+
background: var(--myio-energy-bg);
|
|
11983
|
+
border-radius: var(--myio-energy-radius);
|
|
11984
|
+
|
|
11985
|
+
/* USO DA VARI\xC1VEL ESPEC\xCDFICA AQUI */
|
|
11986
|
+
border: 1px solid var(--myio-chart-border);
|
|
11987
|
+
|
|
11988
|
+
padding: 10px !important;
|
|
11989
|
+
display: block !important;
|
|
11990
|
+
overflow: hidden !important;
|
|
11780
11991
|
}
|
|
11781
11992
|
|
|
11782
|
-
.myio-
|
|
11783
|
-
|
|
11993
|
+
.myio-chart-container {
|
|
11994
|
+
/* Aplica a mesma l\xF3gica para o gr\xE1fico de fallback */
|
|
11995
|
+
border: 1px solid var(--myio-chart-border);
|
|
11996
|
+
border-radius: var(--myio-energy-radius);
|
|
11997
|
+
overflow: hidden;
|
|
11784
11998
|
}
|
|
11785
11999
|
|
|
11786
|
-
.
|
|
11787
|
-
|
|
11788
|
-
|
|
11789
|
-
|
|
12000
|
+
/* --- Resto dos estilos (Bot\xF5es, Labels, etc.) --- */
|
|
12001
|
+
|
|
12002
|
+
.myio-label-secondary {
|
|
12003
|
+
color: var(--myio-energy-text-secondary);
|
|
12004
|
+
font-weight: 500;
|
|
11790
12005
|
}
|
|
11791
12006
|
|
|
11792
|
-
.myio-
|
|
11793
|
-
|
|
12007
|
+
.myio-granularity-selector {
|
|
12008
|
+
display: flex;
|
|
12009
|
+
align-items: center;
|
|
12010
|
+
gap: 4px;
|
|
12011
|
+
margin-left: 8px;
|
|
12012
|
+
padding: 4px 8px;
|
|
12013
|
+
border-radius: 8px;
|
|
12014
|
+
background: var(--myio-granularity-bg);
|
|
12015
|
+
border: 1px solid var(--myio-energy-border); /* Granularidade mant\xE9m borda suave */
|
|
11794
12016
|
}
|
|
11795
12017
|
|
|
11796
|
-
/* RFC-0097: Granularity selector buttons */
|
|
11797
12018
|
.myio-btn-granularity {
|
|
11798
12019
|
padding: 4px 10px;
|
|
11799
12020
|
font-size: 12px;
|
|
11800
12021
|
font-weight: 600;
|
|
11801
12022
|
border-radius: 6px;
|
|
11802
|
-
border: 1px solid var(--myio-energy-border);
|
|
11803
|
-
background: var(--myio-energy-bg);
|
|
11804
|
-
color: var(--myio-energy-text);
|
|
11805
12023
|
cursor: pointer;
|
|
11806
12024
|
transition: all 0.2s ease;
|
|
11807
12025
|
min-width: 36px;
|
|
12026
|
+
background: transparent;
|
|
12027
|
+
border: 1px solid transparent;
|
|
12028
|
+
color: var(--myio-energy-text-secondary);
|
|
11808
12029
|
}
|
|
11809
12030
|
|
|
11810
12031
|
.myio-btn-granularity:hover:not(.active) {
|
|
11811
|
-
background:
|
|
11812
|
-
|
|
11813
|
-
color: var(--myio-energy-primary);
|
|
12032
|
+
background: var(--myio-energy-btn-hover);
|
|
12033
|
+
color: var(--myio-energy-text);
|
|
11814
12034
|
}
|
|
11815
12035
|
|
|
11816
12036
|
.myio-btn-granularity.active {
|
|
11817
12037
|
background: var(--myio-energy-primary);
|
|
11818
12038
|
color: white;
|
|
11819
12039
|
border-color: var(--myio-energy-primary);
|
|
11820
|
-
box-shadow: 0 2px
|
|
12040
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
|
11821
12041
|
}
|
|
11822
12042
|
|
|
11823
|
-
.myio-
|
|
12043
|
+
.myio-btn {
|
|
12044
|
+
padding: 8px 16px;
|
|
12045
|
+
border-radius: var(--myio-energy-radius);
|
|
11824
12046
|
border: 1px solid var(--myio-energy-border);
|
|
12047
|
+
background: var(--myio-energy-btn-bg);
|
|
12048
|
+
color: var(--myio-energy-text);
|
|
12049
|
+
cursor: pointer;
|
|
12050
|
+
font-size: 14px;
|
|
12051
|
+
transition: all 0.2s;
|
|
12052
|
+
}
|
|
12053
|
+
|
|
12054
|
+
.myio-btn:hover:not(:disabled) {
|
|
12055
|
+
background: var(--myio-energy-btn-hover);
|
|
12056
|
+
border-color: var(--myio-energy-border);
|
|
12057
|
+
}
|
|
12058
|
+
|
|
12059
|
+
.myio-btn-primary {
|
|
12060
|
+
background: var(--myio-energy-primary);
|
|
12061
|
+
color: white;
|
|
12062
|
+
border-color: var(--myio-energy-primary);
|
|
12063
|
+
}
|
|
12064
|
+
|
|
12065
|
+
.myio-btn-primary:hover:not(:disabled) {
|
|
12066
|
+
opacity: 0.9;
|
|
12067
|
+
}
|
|
12068
|
+
|
|
12069
|
+
.myio-btn-secondary {
|
|
12070
|
+
background: var(--myio-energy-btn-bg);
|
|
12071
|
+
color: var(--myio-energy-text);
|
|
12072
|
+
border-color: var(--myio-energy-border);
|
|
11825
12073
|
}
|
|
11826
12074
|
|
|
11827
12075
|
.myio-modal-scope {
|
|
@@ -11830,42 +12078,30 @@ ${rangeText}`;
|
|
|
11830
12078
|
flex-direction: column !important;
|
|
11831
12079
|
}
|
|
11832
12080
|
|
|
11833
|
-
.myio-
|
|
11834
|
-
|
|
11835
|
-
|
|
11836
|
-
height: 353px !important;
|
|
11837
|
-
background: var(--myio-energy-bg);
|
|
11838
|
-
border-radius: var(--myio-energy-radius);
|
|
11839
|
-
border: 1px solid var(--myio-energy-border);
|
|
11840
|
-
padding: 10px !important;
|
|
11841
|
-
display: block !important;
|
|
11842
|
-
overflow: hidden !important;
|
|
11843
|
-
}
|
|
11844
|
-
|
|
11845
|
-
.myio-energy-chart-container > iframe {
|
|
11846
|
-
width: 100% !important;
|
|
11847
|
-
height: 100% !important;
|
|
11848
|
-
min-height: 408px !important;
|
|
11849
|
-
border: none !important;
|
|
12081
|
+
.myio-form-group {
|
|
12082
|
+
display: flex;
|
|
12083
|
+
flex-direction: column;
|
|
11850
12084
|
}
|
|
11851
12085
|
|
|
11852
|
-
.myio-
|
|
11853
|
-
|
|
11854
|
-
|
|
11855
|
-
|
|
11856
|
-
min-height: 408px !important;
|
|
12086
|
+
.myio-label {
|
|
12087
|
+
font-weight: 500;
|
|
12088
|
+
margin-bottom: 5px;
|
|
12089
|
+
color: var(--myio-energy-text);
|
|
11857
12090
|
}
|
|
11858
12091
|
|
|
11859
|
-
.myio-
|
|
11860
|
-
|
|
11861
|
-
|
|
11862
|
-
|
|
11863
|
-
|
|
11864
|
-
|
|
12092
|
+
.myio-input {
|
|
12093
|
+
padding: 8px 12px;
|
|
12094
|
+
border: 1px solid var(--myio-energy-border);
|
|
12095
|
+
border-radius: var(--myio-energy-radius);
|
|
12096
|
+
font-size: 14px;
|
|
12097
|
+
background: var(--myio-energy-input-bg);
|
|
12098
|
+
color: var(--myio-energy-text);
|
|
11865
12099
|
}
|
|
11866
12100
|
|
|
11867
|
-
.myio-
|
|
11868
|
-
|
|
12101
|
+
.myio-input:focus {
|
|
12102
|
+
outline: none;
|
|
12103
|
+
border-color: var(--myio-energy-primary);
|
|
12104
|
+
box-shadow: 0 0 0 1px var(--myio-energy-primary);
|
|
11869
12105
|
}
|
|
11870
12106
|
|
|
11871
12107
|
.myio-spinner {
|
|
@@ -11877,78 +12113,40 @@ ${rangeText}`;
|
|
|
11877
12113
|
animation: spin 1s linear infinite;
|
|
11878
12114
|
margin: 0 auto 16px;
|
|
11879
12115
|
}
|
|
11880
|
-
|
|
12116
|
+
|
|
11881
12117
|
@keyframes spin {
|
|
11882
12118
|
0% { transform: rotate(0deg); }
|
|
11883
12119
|
100% { transform: rotate(360deg); }
|
|
11884
12120
|
}
|
|
11885
12121
|
|
|
12122
|
+
.myio-loading-state p {
|
|
12123
|
+
color: var(--myio-energy-text);
|
|
12124
|
+
}
|
|
12125
|
+
|
|
11886
12126
|
.myio-energy-error {
|
|
11887
|
-
background:
|
|
11888
|
-
border: 1px solid
|
|
12127
|
+
background: rgba(254, 202, 202, 0.15);
|
|
12128
|
+
border: 1px solid rgba(248, 113, 113, 0.5);
|
|
11889
12129
|
border-radius: var(--myio-energy-radius);
|
|
11890
12130
|
padding: 16px;
|
|
11891
12131
|
}
|
|
11892
|
-
|
|
11893
|
-
.myio-error-content {
|
|
11894
|
-
display: flex;
|
|
11895
|
-
align-items: center;
|
|
11896
|
-
gap: 12px;
|
|
11897
|
-
}
|
|
11898
|
-
|
|
11899
|
-
.myio-error-icon {
|
|
11900
|
-
font-size: 24px;
|
|
11901
|
-
}
|
|
11902
|
-
|
|
12132
|
+
|
|
11903
12133
|
.myio-error-message {
|
|
11904
|
-
color: #
|
|
12134
|
+
color: #ef4444;
|
|
11905
12135
|
font-weight: 500;
|
|
11906
12136
|
}
|
|
11907
12137
|
|
|
11908
|
-
|
|
11909
|
-
|
|
11910
|
-
.myio-fallback-chart h4 {
|
|
11911
|
-
margin: 0 0 16px 0;
|
|
11912
|
-
text-align: center;
|
|
11913
|
-
}
|
|
11914
|
-
|
|
11915
|
-
.myio-chart-container {
|
|
11916
|
-
border: 1px solid var(--myio-energy-border);
|
|
11917
|
-
border-radius: var(--myio-energy-radius);
|
|
11918
|
-
overflow: hidden;
|
|
11919
|
-
}
|
|
11920
|
-
|
|
11921
12138
|
.myio-chart-note {
|
|
11922
12139
|
margin: 12px 0 0 0;
|
|
11923
12140
|
font-size: 12px;
|
|
11924
|
-
color: #666;
|
|
11925
|
-
text-align: center;
|
|
11926
|
-
}
|
|
11927
|
-
|
|
11928
|
-
.myio-form-group {
|
|
11929
|
-
display: flex;
|
|
11930
|
-
flex-direction: column;
|
|
11931
|
-
}
|
|
11932
|
-
|
|
11933
|
-
.myio-label {
|
|
11934
|
-
font-weight: 500;
|
|
11935
|
-
margin-bottom: 5px;
|
|
11936
|
-
color: var(--myio-energy-text);
|
|
11937
|
-
}
|
|
11938
|
-
|
|
11939
|
-
.myio-input {
|
|
11940
|
-
padding: 8px 12px;
|
|
11941
|
-
border: 1px solid var(--myio-energy-border);
|
|
11942
|
-
border-radius: var(--myio-energy-radius);
|
|
11943
|
-
font-size: 14px;
|
|
11944
|
-
background: var(--myio-energy-bg);
|
|
11945
12141
|
color: var(--myio-energy-text);
|
|
12142
|
+
opacity: 0.7;
|
|
12143
|
+
text-align: center;
|
|
11946
12144
|
}
|
|
11947
12145
|
|
|
11948
|
-
.myio-
|
|
11949
|
-
|
|
11950
|
-
|
|
11951
|
-
|
|
12146
|
+
.myio-energy-chart-container > iframe {
|
|
12147
|
+
width: 100% !important;
|
|
12148
|
+
height: 100% !important;
|
|
12149
|
+
border: none !important;
|
|
11952
12150
|
}
|
|
11953
12151
|
|
|
11954
12152
|
@media (max-width: 768px) {
|
|
@@ -11956,12 +12154,6 @@ ${rangeText}`;
|
|
|
11956
12154
|
grid-template-columns: 1fr;
|
|
11957
12155
|
grid-template-rows: auto 1fr;
|
|
11958
12156
|
}
|
|
11959
|
-
|
|
11960
|
-
.myio-energy-header {
|
|
11961
|
-
flex-direction: column;
|
|
11962
|
-
gap: 12px;
|
|
11963
|
-
align-items: stretch;
|
|
11964
|
-
}
|
|
11965
12157
|
}
|
|
11966
12158
|
`;
|
|
11967
12159
|
}
|
|
@@ -18644,8 +18836,8 @@ ${rangeText}`;
|
|
|
18644
18836
|
<div class="myio-goals-progress-fill" style="width: ${Math.min(progress, 100)}%"></div>
|
|
18645
18837
|
</div>
|
|
18646
18838
|
<div class="myio-goals-progress-text">
|
|
18647
|
-
<span>${
|
|
18648
|
-
<span>${
|
|
18839
|
+
<span>${formatNumber3(monthlySum, locale)} ${annual.unit}</span>
|
|
18840
|
+
<span>${formatNumber3(annual.total, locale)} ${annual.unit}</span>
|
|
18649
18841
|
</div>
|
|
18650
18842
|
|
|
18651
18843
|
<!-- Monthly Grid -->
|
|
@@ -18719,7 +18911,7 @@ ${rangeText}`;
|
|
|
18719
18911
|
<span>${assetData.label || assetId}</span>
|
|
18720
18912
|
</div>
|
|
18721
18913
|
<div class="myio-goals-asset-total">
|
|
18722
|
-
${
|
|
18914
|
+
${formatNumber3(assetData.annual?.total || 0, locale)} ${assetData.annual?.unit || "kWh"}
|
|
18723
18915
|
</div>
|
|
18724
18916
|
<button class="myio-goals-btn-icon" data-action="delete-asset" data-asset-id="${assetId}" aria-label="${i18n.deleteAsset}">
|
|
18725
18917
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
@@ -19034,7 +19226,7 @@ ${rangeText}`;
|
|
|
19034
19226
|
monthlySum += value;
|
|
19035
19227
|
}
|
|
19036
19228
|
if (monthlySum > annualTotal && annualTotal > 0) {
|
|
19037
|
-
errors.push(`${i18n.errorMonthlyExceedsAnnual} (${
|
|
19229
|
+
errors.push(`${i18n.errorMonthlyExceedsAnnual} (${formatNumber3(monthlySum, locale)} > ${formatNumber3(annualTotal, locale)})`);
|
|
19038
19230
|
}
|
|
19039
19231
|
}
|
|
19040
19232
|
return errors;
|
|
@@ -19125,8 +19317,8 @@ ${rangeText}`;
|
|
|
19125
19317
|
}
|
|
19126
19318
|
if (progressTexts.length === 2) {
|
|
19127
19319
|
const unit = document.getElementById("unit-select")?.value || "kWh";
|
|
19128
|
-
progressTexts[0].textContent = `${
|
|
19129
|
-
progressTexts[1].textContent = `${
|
|
19320
|
+
progressTexts[0].textContent = `${formatNumber3(monthlySum, locale)} ${unit}`;
|
|
19321
|
+
progressTexts[1].textContent = `${formatNumber3(annualTotal, locale)} ${unit}`;
|
|
19130
19322
|
}
|
|
19131
19323
|
}
|
|
19132
19324
|
function updateMonthlyUnits(unit) {
|
|
@@ -19224,7 +19416,7 @@ ${rangeText}`;
|
|
|
19224
19416
|
modal.addEventListener("keydown", handleTab);
|
|
19225
19417
|
firstElement.focus();
|
|
19226
19418
|
}
|
|
19227
|
-
function
|
|
19419
|
+
function formatNumber3(value, locale2) {
|
|
19228
19420
|
return new Intl.NumberFormat(locale2, {
|
|
19229
19421
|
minimumFractionDigits: 0,
|
|
19230
19422
|
maximumFractionDigits: 2
|
|
@@ -22175,10 +22367,1741 @@ ${rangeText}`;
|
|
|
22175
22367
|
return { destroy };
|
|
22176
22368
|
}
|
|
22177
22369
|
|
|
22370
|
+
// src/components/ModalHeader/index.ts
|
|
22371
|
+
var DEFAULT_BG_COLOR = "#3e1a7d";
|
|
22372
|
+
var DEFAULT_TEXT_COLOR = "white";
|
|
22373
|
+
var DEFAULT_BORDER_RADIUS = "10px 10px 0 0";
|
|
22374
|
+
var EXPORT_FORMAT_LABELS = {
|
|
22375
|
+
csv: "CSV",
|
|
22376
|
+
xls: "Excel (XLS)",
|
|
22377
|
+
pdf: "PDF"
|
|
22378
|
+
};
|
|
22379
|
+
var EXPORT_FORMAT_ICONS = {
|
|
22380
|
+
csv: "\u{1F4C4}",
|
|
22381
|
+
xls: "\u{1F4CA}",
|
|
22382
|
+
pdf: "\u{1F4D1}"
|
|
22383
|
+
};
|
|
22384
|
+
function createModalHeader(config) {
|
|
22385
|
+
let currentTheme = config.theme || "light";
|
|
22386
|
+
let currentIsMaximized = config.isMaximized || false;
|
|
22387
|
+
let currentTitle = config.title;
|
|
22388
|
+
let themeBtn = null;
|
|
22389
|
+
let maximizeBtn = null;
|
|
22390
|
+
let closeBtn = null;
|
|
22391
|
+
let exportBtn = null;
|
|
22392
|
+
let exportDropdown = null;
|
|
22393
|
+
const cleanupHandlers = [];
|
|
22394
|
+
const handleThemeClick = () => {
|
|
22395
|
+
currentTheme = currentTheme === "light" ? "dark" : "light";
|
|
22396
|
+
config.onThemeToggle?.(currentTheme);
|
|
22397
|
+
updateButtonIcons();
|
|
22398
|
+
};
|
|
22399
|
+
const handleMaximizeClick = () => {
|
|
22400
|
+
currentIsMaximized = !currentIsMaximized;
|
|
22401
|
+
config.onMaximize?.(currentIsMaximized);
|
|
22402
|
+
updateButtonIcons();
|
|
22403
|
+
};
|
|
22404
|
+
const handleCloseClick = () => {
|
|
22405
|
+
config.onClose?.();
|
|
22406
|
+
};
|
|
22407
|
+
const handleExportClick = (format) => {
|
|
22408
|
+
config.onExport?.(format);
|
|
22409
|
+
if (exportDropdown) {
|
|
22410
|
+
exportDropdown.style.display = "none";
|
|
22411
|
+
}
|
|
22412
|
+
};
|
|
22413
|
+
function updateButtonIcons() {
|
|
22414
|
+
if (themeBtn) {
|
|
22415
|
+
themeBtn.textContent = currentTheme === "dark" ? "\u2600\uFE0F" : "\u{1F319}";
|
|
22416
|
+
themeBtn.title = currentTheme === "dark" ? "Modo claro" : "Modo escuro";
|
|
22417
|
+
}
|
|
22418
|
+
if (maximizeBtn) {
|
|
22419
|
+
maximizeBtn.textContent = currentIsMaximized ? "\u{1F5D7}" : "\u{1F5D6}";
|
|
22420
|
+
maximizeBtn.title = currentIsMaximized ? "Restaurar" : "Maximizar";
|
|
22421
|
+
}
|
|
22422
|
+
}
|
|
22423
|
+
function getButtonStyle() {
|
|
22424
|
+
return `
|
|
22425
|
+
background: none;
|
|
22426
|
+
border: none;
|
|
22427
|
+
font-size: 16px;
|
|
22428
|
+
cursor: pointer;
|
|
22429
|
+
padding: 4px 8px;
|
|
22430
|
+
border-radius: 6px;
|
|
22431
|
+
color: rgba(255, 255, 255, 0.8);
|
|
22432
|
+
transition: background-color 0.2s, color 0.2s;
|
|
22433
|
+
`.replace(/\s+/g, " ").trim();
|
|
22434
|
+
}
|
|
22435
|
+
function renderExportDropdown() {
|
|
22436
|
+
const formats = config.exportFormats || [];
|
|
22437
|
+
if (formats.length === 0) return "";
|
|
22438
|
+
if (formats.length === 1) {
|
|
22439
|
+
return `
|
|
22440
|
+
<button id="${config.id}-export" title="Exportar ${EXPORT_FORMAT_LABELS[formats[0]]}" style="${getButtonStyle()}">
|
|
22441
|
+
\u{1F4E5}
|
|
22442
|
+
</button>
|
|
22443
|
+
`;
|
|
22444
|
+
}
|
|
22445
|
+
return `
|
|
22446
|
+
<div style="position: relative; display: inline-block;">
|
|
22447
|
+
<button id="${config.id}-export-btn" title="Exportar" style="${getButtonStyle()}">
|
|
22448
|
+
\u{1F4E5}
|
|
22449
|
+
</button>
|
|
22450
|
+
<div id="${config.id}-export-dropdown" style="
|
|
22451
|
+
display: none;
|
|
22452
|
+
position: absolute;
|
|
22453
|
+
top: 100%;
|
|
22454
|
+
right: 0;
|
|
22455
|
+
background: white;
|
|
22456
|
+
border-radius: 6px;
|
|
22457
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
22458
|
+
min-width: 140px;
|
|
22459
|
+
z-index: 10001;
|
|
22460
|
+
margin-top: 4px;
|
|
22461
|
+
overflow: hidden;
|
|
22462
|
+
">
|
|
22463
|
+
${formats.map((format) => `
|
|
22464
|
+
<button
|
|
22465
|
+
id="${config.id}-export-${format}"
|
|
22466
|
+
class="myio-export-option"
|
|
22467
|
+
data-format="${format}"
|
|
22468
|
+
style="
|
|
22469
|
+
display: flex;
|
|
22470
|
+
align-items: center;
|
|
22471
|
+
gap: 8px;
|
|
22472
|
+
width: 100%;
|
|
22473
|
+
padding: 10px 14px;
|
|
22474
|
+
border: none;
|
|
22475
|
+
background: white;
|
|
22476
|
+
cursor: pointer;
|
|
22477
|
+
font-size: 13px;
|
|
22478
|
+
color: #333;
|
|
22479
|
+
text-align: left;
|
|
22480
|
+
transition: background-color 0.2s;
|
|
22481
|
+
"
|
|
22482
|
+
>
|
|
22483
|
+
${EXPORT_FORMAT_ICONS[format]} ${EXPORT_FORMAT_LABELS[format]}
|
|
22484
|
+
</button>
|
|
22485
|
+
`).join("")}
|
|
22486
|
+
</div>
|
|
22487
|
+
</div>
|
|
22488
|
+
`;
|
|
22489
|
+
}
|
|
22490
|
+
const instance = {
|
|
22491
|
+
render() {
|
|
22492
|
+
const bgColor = config.backgroundColor || DEFAULT_BG_COLOR;
|
|
22493
|
+
const textColor = config.textColor || DEFAULT_TEXT_COLOR;
|
|
22494
|
+
const borderRadius = currentIsMaximized ? "0" : config.borderRadius || DEFAULT_BORDER_RADIUS;
|
|
22495
|
+
const showTheme = config.showThemeToggle !== false;
|
|
22496
|
+
const showMax = config.showMaximize !== false;
|
|
22497
|
+
const showClose = config.showClose !== false;
|
|
22498
|
+
const showExport = config.exportFormats && config.exportFormats.length > 0;
|
|
22499
|
+
const iconHtml = config.icon ? `<span style="margin-right: 8px;">${config.icon}</span>` : "";
|
|
22500
|
+
const buttonStyle = getButtonStyle();
|
|
22501
|
+
return `
|
|
22502
|
+
<div class="myio-modal-header" style="
|
|
22503
|
+
padding: 4px 8px;
|
|
22504
|
+
display: flex;
|
|
22505
|
+
align-items: center;
|
|
22506
|
+
justify-content: space-between;
|
|
22507
|
+
background: ${bgColor};
|
|
22508
|
+
color: ${textColor};
|
|
22509
|
+
border-radius: ${borderRadius};
|
|
22510
|
+
min-height: 20px;
|
|
22511
|
+
font-family: 'Roboto', Arial, sans-serif;
|
|
22512
|
+
">
|
|
22513
|
+
<h2 id="${config.id}-header-title" style="
|
|
22514
|
+
margin: 6px;
|
|
22515
|
+
font-size: 18px;
|
|
22516
|
+
font-weight: 600;
|
|
22517
|
+
color: ${textColor};
|
|
22518
|
+
line-height: 2;
|
|
22519
|
+
display: flex;
|
|
22520
|
+
align-items: center;
|
|
22521
|
+
">
|
|
22522
|
+
${iconHtml}${currentTitle}
|
|
22523
|
+
</h2>
|
|
22524
|
+
<div style="display: flex; gap: 4px; align-items: center;">
|
|
22525
|
+
${showExport ? renderExportDropdown() : ""}
|
|
22526
|
+
${showTheme ? `
|
|
22527
|
+
<button id="${config.id}-theme-toggle" title="${currentTheme === "dark" ? "Modo claro" : "Modo escuro"}" style="${buttonStyle}">
|
|
22528
|
+
${currentTheme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}
|
|
22529
|
+
</button>
|
|
22530
|
+
` : ""}
|
|
22531
|
+
${showMax ? `
|
|
22532
|
+
<button id="${config.id}-maximize" title="${currentIsMaximized ? "Restaurar" : "Maximizar"}" style="${buttonStyle}">
|
|
22533
|
+
${currentIsMaximized ? "\u{1F5D7}" : "\u{1F5D6}"}
|
|
22534
|
+
</button>
|
|
22535
|
+
` : ""}
|
|
22536
|
+
${showClose ? `
|
|
22537
|
+
<button id="${config.id}-close" title="Fechar" style="${buttonStyle}; font-size: 20px;">
|
|
22538
|
+
\xD7
|
|
22539
|
+
</button>
|
|
22540
|
+
` : ""}
|
|
22541
|
+
</div>
|
|
22542
|
+
</div>
|
|
22543
|
+
`;
|
|
22544
|
+
},
|
|
22545
|
+
attachListeners() {
|
|
22546
|
+
themeBtn = document.getElementById(`${config.id}-theme-toggle`);
|
|
22547
|
+
maximizeBtn = document.getElementById(`${config.id}-maximize`);
|
|
22548
|
+
closeBtn = document.getElementById(`${config.id}-close`);
|
|
22549
|
+
const singleExportBtn = document.getElementById(`${config.id}-export`);
|
|
22550
|
+
if (singleExportBtn && config.exportFormats?.length === 1) {
|
|
22551
|
+
exportBtn = singleExportBtn;
|
|
22552
|
+
const format = config.exportFormats[0];
|
|
22553
|
+
const clickHandler = () => handleExportClick(format);
|
|
22554
|
+
exportBtn.addEventListener("click", clickHandler);
|
|
22555
|
+
cleanupHandlers.push(() => exportBtn?.removeEventListener("click", clickHandler));
|
|
22556
|
+
const enterHandler = () => {
|
|
22557
|
+
exportBtn.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
|
|
22558
|
+
};
|
|
22559
|
+
const leaveHandler = () => {
|
|
22560
|
+
exportBtn.style.backgroundColor = "transparent";
|
|
22561
|
+
};
|
|
22562
|
+
exportBtn.addEventListener("mouseenter", enterHandler);
|
|
22563
|
+
exportBtn.addEventListener("mouseleave", leaveHandler);
|
|
22564
|
+
cleanupHandlers.push(() => {
|
|
22565
|
+
exportBtn?.removeEventListener("mouseenter", enterHandler);
|
|
22566
|
+
exportBtn?.removeEventListener("mouseleave", leaveHandler);
|
|
22567
|
+
});
|
|
22568
|
+
}
|
|
22569
|
+
const exportDropdownBtn = document.getElementById(`${config.id}-export-btn`);
|
|
22570
|
+
exportDropdown = document.getElementById(`${config.id}-export-dropdown`);
|
|
22571
|
+
if (exportDropdownBtn && exportDropdown) {
|
|
22572
|
+
exportBtn = exportDropdownBtn;
|
|
22573
|
+
const toggleHandler = (e) => {
|
|
22574
|
+
e.stopPropagation();
|
|
22575
|
+
if (exportDropdown) {
|
|
22576
|
+
exportDropdown.style.display = exportDropdown.style.display === "none" ? "block" : "none";
|
|
22577
|
+
}
|
|
22578
|
+
};
|
|
22579
|
+
exportDropdownBtn.addEventListener("click", toggleHandler);
|
|
22580
|
+
cleanupHandlers.push(() => exportDropdownBtn.removeEventListener("click", toggleHandler));
|
|
22581
|
+
const outsideClickHandler = (e) => {
|
|
22582
|
+
if (exportDropdown && !exportDropdown.contains(e.target) && e.target !== exportDropdownBtn) {
|
|
22583
|
+
exportDropdown.style.display = "none";
|
|
22584
|
+
}
|
|
22585
|
+
};
|
|
22586
|
+
document.addEventListener("click", outsideClickHandler);
|
|
22587
|
+
cleanupHandlers.push(() => document.removeEventListener("click", outsideClickHandler));
|
|
22588
|
+
config.exportFormats?.forEach((format) => {
|
|
22589
|
+
const btn = document.getElementById(`${config.id}-export-${format}`);
|
|
22590
|
+
if (btn) {
|
|
22591
|
+
const clickHandler = () => handleExportClick(format);
|
|
22592
|
+
btn.addEventListener("click", clickHandler);
|
|
22593
|
+
cleanupHandlers.push(() => btn.removeEventListener("click", clickHandler));
|
|
22594
|
+
const enterHandler2 = () => {
|
|
22595
|
+
btn.style.backgroundColor = "#f0f0f0";
|
|
22596
|
+
};
|
|
22597
|
+
const leaveHandler2 = () => {
|
|
22598
|
+
btn.style.backgroundColor = "white";
|
|
22599
|
+
};
|
|
22600
|
+
btn.addEventListener("mouseenter", enterHandler2);
|
|
22601
|
+
btn.addEventListener("mouseleave", leaveHandler2);
|
|
22602
|
+
cleanupHandlers.push(() => {
|
|
22603
|
+
btn.removeEventListener("mouseenter", enterHandler2);
|
|
22604
|
+
btn.removeEventListener("mouseleave", leaveHandler2);
|
|
22605
|
+
});
|
|
22606
|
+
}
|
|
22607
|
+
});
|
|
22608
|
+
const enterHandler = () => {
|
|
22609
|
+
exportDropdownBtn.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
|
|
22610
|
+
};
|
|
22611
|
+
const leaveHandler = () => {
|
|
22612
|
+
exportDropdownBtn.style.backgroundColor = "transparent";
|
|
22613
|
+
};
|
|
22614
|
+
exportDropdownBtn.addEventListener("mouseenter", enterHandler);
|
|
22615
|
+
exportDropdownBtn.addEventListener("mouseleave", leaveHandler);
|
|
22616
|
+
cleanupHandlers.push(() => {
|
|
22617
|
+
exportDropdownBtn.removeEventListener("mouseenter", enterHandler);
|
|
22618
|
+
exportDropdownBtn.removeEventListener("mouseleave", leaveHandler);
|
|
22619
|
+
});
|
|
22620
|
+
}
|
|
22621
|
+
if (themeBtn && config.showThemeToggle !== false) {
|
|
22622
|
+
themeBtn.addEventListener("click", handleThemeClick);
|
|
22623
|
+
cleanupHandlers.push(() => themeBtn?.removeEventListener("click", handleThemeClick));
|
|
22624
|
+
const enterHandler = () => {
|
|
22625
|
+
themeBtn.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
|
|
22626
|
+
};
|
|
22627
|
+
const leaveHandler = () => {
|
|
22628
|
+
themeBtn.style.backgroundColor = "transparent";
|
|
22629
|
+
};
|
|
22630
|
+
themeBtn.addEventListener("mouseenter", enterHandler);
|
|
22631
|
+
themeBtn.addEventListener("mouseleave", leaveHandler);
|
|
22632
|
+
cleanupHandlers.push(() => {
|
|
22633
|
+
themeBtn?.removeEventListener("mouseenter", enterHandler);
|
|
22634
|
+
themeBtn?.removeEventListener("mouseleave", leaveHandler);
|
|
22635
|
+
});
|
|
22636
|
+
}
|
|
22637
|
+
if (maximizeBtn && config.showMaximize !== false) {
|
|
22638
|
+
maximizeBtn.addEventListener("click", handleMaximizeClick);
|
|
22639
|
+
cleanupHandlers.push(() => maximizeBtn?.removeEventListener("click", handleMaximizeClick));
|
|
22640
|
+
const enterHandler = () => {
|
|
22641
|
+
maximizeBtn.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
|
|
22642
|
+
};
|
|
22643
|
+
const leaveHandler = () => {
|
|
22644
|
+
maximizeBtn.style.backgroundColor = "transparent";
|
|
22645
|
+
};
|
|
22646
|
+
maximizeBtn.addEventListener("mouseenter", enterHandler);
|
|
22647
|
+
maximizeBtn.addEventListener("mouseleave", leaveHandler);
|
|
22648
|
+
cleanupHandlers.push(() => {
|
|
22649
|
+
maximizeBtn?.removeEventListener("mouseenter", enterHandler);
|
|
22650
|
+
maximizeBtn?.removeEventListener("mouseleave", leaveHandler);
|
|
22651
|
+
});
|
|
22652
|
+
}
|
|
22653
|
+
if (closeBtn && config.showClose !== false) {
|
|
22654
|
+
closeBtn.addEventListener("click", handleCloseClick);
|
|
22655
|
+
cleanupHandlers.push(() => closeBtn?.removeEventListener("click", handleCloseClick));
|
|
22656
|
+
const enterHandler = () => {
|
|
22657
|
+
closeBtn.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
|
|
22658
|
+
};
|
|
22659
|
+
const leaveHandler = () => {
|
|
22660
|
+
closeBtn.style.backgroundColor = "transparent";
|
|
22661
|
+
};
|
|
22662
|
+
closeBtn.addEventListener("mouseenter", enterHandler);
|
|
22663
|
+
closeBtn.addEventListener("mouseleave", leaveHandler);
|
|
22664
|
+
cleanupHandlers.push(() => {
|
|
22665
|
+
closeBtn?.removeEventListener("mouseenter", enterHandler);
|
|
22666
|
+
closeBtn?.removeEventListener("mouseleave", leaveHandler);
|
|
22667
|
+
});
|
|
22668
|
+
}
|
|
22669
|
+
},
|
|
22670
|
+
update(updates) {
|
|
22671
|
+
if (updates.theme !== void 0) {
|
|
22672
|
+
currentTheme = updates.theme;
|
|
22673
|
+
updateButtonIcons();
|
|
22674
|
+
}
|
|
22675
|
+
if (updates.isMaximized !== void 0) {
|
|
22676
|
+
currentIsMaximized = updates.isMaximized;
|
|
22677
|
+
updateButtonIcons();
|
|
22678
|
+
}
|
|
22679
|
+
if (updates.title !== void 0) {
|
|
22680
|
+
currentTitle = updates.title;
|
|
22681
|
+
const titleEl = document.getElementById(`${config.id}-header-title`);
|
|
22682
|
+
if (titleEl) {
|
|
22683
|
+
const iconHtml = config.icon ? `<span style="margin-right: 8px;">${config.icon}</span>` : "";
|
|
22684
|
+
titleEl.innerHTML = `${iconHtml}${currentTitle}`;
|
|
22685
|
+
}
|
|
22686
|
+
}
|
|
22687
|
+
},
|
|
22688
|
+
getState() {
|
|
22689
|
+
return {
|
|
22690
|
+
theme: currentTheme,
|
|
22691
|
+
isMaximized: currentIsMaximized
|
|
22692
|
+
};
|
|
22693
|
+
},
|
|
22694
|
+
destroy() {
|
|
22695
|
+
cleanupHandlers.forEach((handler) => handler());
|
|
22696
|
+
cleanupHandlers.length = 0;
|
|
22697
|
+
themeBtn = null;
|
|
22698
|
+
maximizeBtn = null;
|
|
22699
|
+
closeBtn = null;
|
|
22700
|
+
exportBtn = null;
|
|
22701
|
+
exportDropdown = null;
|
|
22702
|
+
}
|
|
22703
|
+
};
|
|
22704
|
+
return instance;
|
|
22705
|
+
}
|
|
22706
|
+
function getModalHeaderStyles() {
|
|
22707
|
+
return `
|
|
22708
|
+
.myio-modal-header button:hover {
|
|
22709
|
+
background-color: rgba(255, 255, 255, 0.2) !important;
|
|
22710
|
+
}
|
|
22711
|
+
.myio-modal-header button:active {
|
|
22712
|
+
background-color: rgba(255, 255, 255, 0.3) !important;
|
|
22713
|
+
}
|
|
22714
|
+
.myio-export-option:hover {
|
|
22715
|
+
background-color: #f0f0f0 !important;
|
|
22716
|
+
}
|
|
22717
|
+
`;
|
|
22718
|
+
}
|
|
22719
|
+
|
|
22720
|
+
// src/components/Consumption7DaysChart/types.ts
|
|
22721
|
+
var DEFAULT_COLORS = {
|
|
22722
|
+
energy: {
|
|
22723
|
+
primary: "#2563eb",
|
|
22724
|
+
background: "rgba(37, 99, 235, 0.1)",
|
|
22725
|
+
gradient: ["#f0fdf4", "#dcfce7"],
|
|
22726
|
+
pointBackground: "#2563eb",
|
|
22727
|
+
pointBorder: "#ffffff"
|
|
22728
|
+
},
|
|
22729
|
+
water: {
|
|
22730
|
+
primary: "#0288d1",
|
|
22731
|
+
background: "rgba(2, 136, 209, 0.1)",
|
|
22732
|
+
gradient: ["#f0f9ff", "#bae6fd"],
|
|
22733
|
+
pointBackground: "#0288d1",
|
|
22734
|
+
pointBorder: "#ffffff"
|
|
22735
|
+
},
|
|
22736
|
+
gas: {
|
|
22737
|
+
primary: "#ea580c",
|
|
22738
|
+
background: "rgba(234, 88, 12, 0.1)",
|
|
22739
|
+
gradient: ["#fff7ed", "#fed7aa"],
|
|
22740
|
+
pointBackground: "#ea580c",
|
|
22741
|
+
pointBorder: "#ffffff"
|
|
22742
|
+
},
|
|
22743
|
+
temperature: {
|
|
22744
|
+
primary: "#dc2626",
|
|
22745
|
+
background: "rgba(220, 38, 38, 0.1)",
|
|
22746
|
+
gradient: ["#fef2f2", "#fecaca"],
|
|
22747
|
+
pointBackground: "#dc2626",
|
|
22748
|
+
pointBorder: "#ffffff"
|
|
22749
|
+
}
|
|
22750
|
+
};
|
|
22751
|
+
var THEME_COLORS = {
|
|
22752
|
+
light: {
|
|
22753
|
+
chartBackground: "#ffffff",
|
|
22754
|
+
text: "#1f2937",
|
|
22755
|
+
textMuted: "#6b7280",
|
|
22756
|
+
grid: "rgba(0, 0, 0, 0.1)",
|
|
22757
|
+
border: "#e5e7eb",
|
|
22758
|
+
tooltipBackground: "#ffffff",
|
|
22759
|
+
tooltipText: "#1f2937"
|
|
22760
|
+
},
|
|
22761
|
+
dark: {
|
|
22762
|
+
chartBackground: "#1f2937",
|
|
22763
|
+
text: "#f9fafb",
|
|
22764
|
+
textMuted: "#9ca3af",
|
|
22765
|
+
grid: "rgba(255, 255, 255, 0.1)",
|
|
22766
|
+
border: "#374151",
|
|
22767
|
+
tooltipBackground: "#374151",
|
|
22768
|
+
tooltipText: "#f9fafb"
|
|
22769
|
+
}
|
|
22770
|
+
};
|
|
22771
|
+
var DEFAULT_CONFIG = {
|
|
22772
|
+
defaultPeriod: 7,
|
|
22773
|
+
defaultChartType: "line",
|
|
22774
|
+
defaultVizMode: "total",
|
|
22775
|
+
defaultTheme: "light",
|
|
22776
|
+
cacheTTL: 3e5,
|
|
22777
|
+
// 5 minutes
|
|
22778
|
+
decimalPlaces: 1,
|
|
22779
|
+
lineTension: 0.4,
|
|
22780
|
+
pointRadius: 4,
|
|
22781
|
+
borderWidth: 2,
|
|
22782
|
+
fill: true,
|
|
22783
|
+
showLegend: false,
|
|
22784
|
+
enableExport: true
|
|
22785
|
+
};
|
|
22786
|
+
|
|
22787
|
+
// src/components/Consumption7DaysChart/createConsumption7DaysChart.ts
|
|
22788
|
+
function createConsumption7DaysChart(config) {
|
|
22789
|
+
let chartInstance = null;
|
|
22790
|
+
let cachedData = null;
|
|
22791
|
+
let currentPeriod = config.defaultPeriod ?? DEFAULT_CONFIG.defaultPeriod;
|
|
22792
|
+
let currentChartType = config.defaultChartType ?? DEFAULT_CONFIG.defaultChartType;
|
|
22793
|
+
let currentVizMode = config.defaultVizMode ?? DEFAULT_CONFIG.defaultVizMode;
|
|
22794
|
+
let currentTheme = config.theme ?? DEFAULT_CONFIG.defaultTheme;
|
|
22795
|
+
let currentIdealRange = config.idealRange ?? null;
|
|
22796
|
+
let isRendered = false;
|
|
22797
|
+
let autoRefreshTimer = null;
|
|
22798
|
+
const colors = {
|
|
22799
|
+
...DEFAULT_COLORS[config.domain] ?? DEFAULT_COLORS.energy,
|
|
22800
|
+
...config.colors
|
|
22801
|
+
};
|
|
22802
|
+
function $id(id) {
|
|
22803
|
+
if (config.$container && config.$container[0]) {
|
|
22804
|
+
return config.$container[0].querySelector(`#${id}`);
|
|
22805
|
+
}
|
|
22806
|
+
return document.getElementById(id);
|
|
22807
|
+
}
|
|
22808
|
+
function log(level, ...args) {
|
|
22809
|
+
const prefix = `[${config.domain.toUpperCase()}]`;
|
|
22810
|
+
console[level](prefix, ...args);
|
|
22811
|
+
}
|
|
22812
|
+
function calculateYAxisMax(values) {
|
|
22813
|
+
const maxValue = Math.max(...values, 0);
|
|
22814
|
+
if (config.domain === "temperature") {
|
|
22815
|
+
const tempConfig = config.temperatureConfig;
|
|
22816
|
+
if (tempConfig?.clampRange) {
|
|
22817
|
+
return tempConfig.clampRange.max;
|
|
22818
|
+
}
|
|
22819
|
+
const maxWithThreshold = Math.max(
|
|
22820
|
+
maxValue,
|
|
22821
|
+
tempConfig?.maxThreshold?.value ?? 0,
|
|
22822
|
+
tempConfig?.idealRange?.max ?? 0
|
|
22823
|
+
);
|
|
22824
|
+
return Math.ceil(maxWithThreshold + 5);
|
|
22825
|
+
}
|
|
22826
|
+
if (maxValue === 0) {
|
|
22827
|
+
return config.thresholdForLargeUnit ? config.thresholdForLargeUnit / 2 : 500;
|
|
22828
|
+
}
|
|
22829
|
+
let roundTo;
|
|
22830
|
+
if (config.thresholdForLargeUnit && maxValue >= config.thresholdForLargeUnit) {
|
|
22831
|
+
roundTo = config.thresholdForLargeUnit / 10;
|
|
22832
|
+
} else if (maxValue >= 1e3) {
|
|
22833
|
+
roundTo = 100;
|
|
22834
|
+
} else if (maxValue >= 100) {
|
|
22835
|
+
roundTo = 50;
|
|
22836
|
+
} else if (maxValue >= 10) {
|
|
22837
|
+
roundTo = 10;
|
|
22838
|
+
} else {
|
|
22839
|
+
roundTo = 5;
|
|
22840
|
+
}
|
|
22841
|
+
return Math.ceil(maxValue * 1.1 / roundTo) * roundTo;
|
|
22842
|
+
}
|
|
22843
|
+
function calculateYAxisMin(values) {
|
|
22844
|
+
if (config.domain !== "temperature") {
|
|
22845
|
+
return 0;
|
|
22846
|
+
}
|
|
22847
|
+
const tempConfig = config.temperatureConfig;
|
|
22848
|
+
if (tempConfig?.clampRange) {
|
|
22849
|
+
return tempConfig.clampRange.min;
|
|
22850
|
+
}
|
|
22851
|
+
const minValue = Math.min(...values);
|
|
22852
|
+
const minWithThreshold = Math.min(
|
|
22853
|
+
minValue,
|
|
22854
|
+
tempConfig?.minThreshold?.value ?? minValue,
|
|
22855
|
+
tempConfig?.idealRange?.min ?? minValue
|
|
22856
|
+
);
|
|
22857
|
+
return Math.floor(minWithThreshold - 5);
|
|
22858
|
+
}
|
|
22859
|
+
function buildTemperatureAnnotations() {
|
|
22860
|
+
const tempConfig = config.temperatureConfig;
|
|
22861
|
+
if (!tempConfig || config.domain !== "temperature") {
|
|
22862
|
+
return {};
|
|
22863
|
+
}
|
|
22864
|
+
const annotations = {};
|
|
22865
|
+
const createLineAnnotation = (line, id) => {
|
|
22866
|
+
const borderDash = line.lineStyle === "dashed" ? [6, 6] : line.lineStyle === "dotted" ? [2, 2] : [];
|
|
22867
|
+
return {
|
|
22868
|
+
type: "line",
|
|
22869
|
+
yMin: line.value,
|
|
22870
|
+
yMax: line.value,
|
|
22871
|
+
borderColor: line.color,
|
|
22872
|
+
borderWidth: line.lineWidth ?? 2,
|
|
22873
|
+
borderDash,
|
|
22874
|
+
label: {
|
|
22875
|
+
display: true,
|
|
22876
|
+
content: line.label,
|
|
22877
|
+
position: "end",
|
|
22878
|
+
backgroundColor: line.color,
|
|
22879
|
+
color: "#fff",
|
|
22880
|
+
font: { size: 10, weight: "bold" },
|
|
22881
|
+
padding: { x: 4, y: 2 }
|
|
22882
|
+
}
|
|
22883
|
+
};
|
|
22884
|
+
};
|
|
22885
|
+
if (tempConfig.minThreshold) {
|
|
22886
|
+
annotations["minThreshold"] = createLineAnnotation(tempConfig.minThreshold);
|
|
22887
|
+
}
|
|
22888
|
+
if (tempConfig.maxThreshold) {
|
|
22889
|
+
annotations["maxThreshold"] = createLineAnnotation(tempConfig.maxThreshold);
|
|
22890
|
+
}
|
|
22891
|
+
if (tempConfig.idealRange) {
|
|
22892
|
+
annotations["idealRange"] = {
|
|
22893
|
+
type: "box",
|
|
22894
|
+
yMin: tempConfig.idealRange.min,
|
|
22895
|
+
yMax: tempConfig.idealRange.max,
|
|
22896
|
+
backgroundColor: tempConfig.idealRange.color,
|
|
22897
|
+
borderWidth: 0,
|
|
22898
|
+
label: tempConfig.idealRange.label ? {
|
|
22899
|
+
display: true,
|
|
22900
|
+
content: tempConfig.idealRange.label,
|
|
22901
|
+
position: { x: "start", y: "center" },
|
|
22902
|
+
color: "#666",
|
|
22903
|
+
font: { size: 10 }
|
|
22904
|
+
} : void 0
|
|
22905
|
+
};
|
|
22906
|
+
}
|
|
22907
|
+
return annotations;
|
|
22908
|
+
}
|
|
22909
|
+
function buildIdealRangeAnnotation() {
|
|
22910
|
+
if (!currentIdealRange) {
|
|
22911
|
+
return {};
|
|
22912
|
+
}
|
|
22913
|
+
const { min, max, enabled = true } = currentIdealRange;
|
|
22914
|
+
if (!enabled || min === 0 && max === 0 || min >= max) {
|
|
22915
|
+
return {};
|
|
22916
|
+
}
|
|
22917
|
+
const defaultColors = {
|
|
22918
|
+
temperature: { bg: "rgba(34, 197, 94, 0.15)", border: "rgba(34, 197, 94, 0.4)" },
|
|
22919
|
+
energy: { bg: "rgba(37, 99, 235, 0.1)", border: "rgba(37, 99, 235, 0.3)" },
|
|
22920
|
+
water: { bg: "rgba(2, 136, 209, 0.1)", border: "rgba(2, 136, 209, 0.3)" },
|
|
22921
|
+
gas: { bg: "rgba(234, 88, 12, 0.1)", border: "rgba(234, 88, 12, 0.3)" }
|
|
22922
|
+
};
|
|
22923
|
+
const domainDefaults = defaultColors[config.domain] || defaultColors.energy;
|
|
22924
|
+
return {
|
|
22925
|
+
idealRangeBox: {
|
|
22926
|
+
type: "box",
|
|
22927
|
+
yMin: min,
|
|
22928
|
+
yMax: max,
|
|
22929
|
+
backgroundColor: currentIdealRange.color || domainDefaults.bg,
|
|
22930
|
+
borderColor: currentIdealRange.borderColor || domainDefaults.border,
|
|
22931
|
+
borderWidth: 1,
|
|
22932
|
+
label: currentIdealRange.label ? {
|
|
22933
|
+
display: true,
|
|
22934
|
+
content: currentIdealRange.label,
|
|
22935
|
+
position: { x: "start", y: "center" },
|
|
22936
|
+
color: "#666",
|
|
22937
|
+
font: { size: 10, style: "italic" },
|
|
22938
|
+
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
|
22939
|
+
padding: { x: 4, y: 2 }
|
|
22940
|
+
} : void 0
|
|
22941
|
+
}
|
|
22942
|
+
};
|
|
22943
|
+
}
|
|
22944
|
+
function formatValue(value, includeUnit = true) {
|
|
22945
|
+
const decimals = config.decimalPlaces ?? DEFAULT_CONFIG.decimalPlaces;
|
|
22946
|
+
if (config.unitLarge && config.thresholdForLargeUnit && value >= config.thresholdForLargeUnit) {
|
|
22947
|
+
const converted = value / config.thresholdForLargeUnit;
|
|
22948
|
+
return includeUnit ? `${converted.toFixed(decimals)} ${config.unitLarge}` : converted.toFixed(decimals);
|
|
22949
|
+
}
|
|
22950
|
+
return includeUnit ? `${value.toFixed(decimals)} ${config.unit}` : value.toFixed(decimals);
|
|
22951
|
+
}
|
|
22952
|
+
function formatTickValue(value) {
|
|
22953
|
+
if (config.unitLarge && config.thresholdForLargeUnit && value >= config.thresholdForLargeUnit) {
|
|
22954
|
+
return `${(value / config.thresholdForLargeUnit).toFixed(1)}`;
|
|
22955
|
+
}
|
|
22956
|
+
return value.toFixed(0);
|
|
22957
|
+
}
|
|
22958
|
+
function buildChartConfig(data) {
|
|
22959
|
+
const yAxisMax = calculateYAxisMax(data.dailyTotals);
|
|
22960
|
+
const yAxisMin = calculateYAxisMin(data.dailyTotals);
|
|
22961
|
+
const tension = config.lineTension ?? DEFAULT_CONFIG.lineTension;
|
|
22962
|
+
const pointRadius = config.pointRadius ?? DEFAULT_CONFIG.pointRadius;
|
|
22963
|
+
const borderWidth = config.borderWidth ?? DEFAULT_CONFIG.borderWidth;
|
|
22964
|
+
const fill = config.fill ?? DEFAULT_CONFIG.fill;
|
|
22965
|
+
const showLegend = config.showLegend ?? DEFAULT_CONFIG.showLegend;
|
|
22966
|
+
const themeColors = THEME_COLORS[currentTheme];
|
|
22967
|
+
const isTemperature = config.domain === "temperature";
|
|
22968
|
+
let datasets;
|
|
22969
|
+
if (currentVizMode === "separate" && data.shoppingData && data.shoppingNames) {
|
|
22970
|
+
const shoppingColors = [
|
|
22971
|
+
"#2563eb",
|
|
22972
|
+
"#16a34a",
|
|
22973
|
+
"#ea580c",
|
|
22974
|
+
"#dc2626",
|
|
22975
|
+
"#8b5cf6",
|
|
22976
|
+
"#0891b2",
|
|
22977
|
+
"#65a30d",
|
|
22978
|
+
"#d97706",
|
|
22979
|
+
"#be185d",
|
|
22980
|
+
"#0d9488"
|
|
22981
|
+
];
|
|
22982
|
+
datasets = Object.entries(data.shoppingData).map(([shoppingId, values], index) => ({
|
|
22983
|
+
label: data.shoppingNames?.[shoppingId] || shoppingId,
|
|
22984
|
+
data: values,
|
|
22985
|
+
borderColor: shoppingColors[index % shoppingColors.length],
|
|
22986
|
+
backgroundColor: currentChartType === "line" ? `${shoppingColors[index % shoppingColors.length]}20` : shoppingColors[index % shoppingColors.length],
|
|
22987
|
+
fill: currentChartType === "line" && fill,
|
|
22988
|
+
tension,
|
|
22989
|
+
borderWidth,
|
|
22990
|
+
pointRadius: currentChartType === "line" ? pointRadius : 0,
|
|
22991
|
+
pointBackgroundColor: shoppingColors[index % shoppingColors.length],
|
|
22992
|
+
pointBorderColor: "#fff",
|
|
22993
|
+
pointBorderWidth: 2
|
|
22994
|
+
}));
|
|
22995
|
+
} else {
|
|
22996
|
+
const datasetLabel = isTemperature ? `Temperatura (${config.unit})` : `Consumo (${config.unit})`;
|
|
22997
|
+
datasets = [
|
|
22998
|
+
{
|
|
22999
|
+
label: datasetLabel,
|
|
23000
|
+
data: data.dailyTotals,
|
|
23001
|
+
borderColor: colors.primary,
|
|
23002
|
+
backgroundColor: currentChartType === "line" ? colors.background : colors.primary,
|
|
23003
|
+
fill: currentChartType === "line" && fill,
|
|
23004
|
+
tension,
|
|
23005
|
+
borderWidth,
|
|
23006
|
+
pointRadius: currentChartType === "line" ? pointRadius : 0,
|
|
23007
|
+
pointBackgroundColor: colors.pointBackground || colors.primary,
|
|
23008
|
+
pointBorderColor: colors.pointBorder || "#fff",
|
|
23009
|
+
pointBorderWidth: 2,
|
|
23010
|
+
borderRadius: currentChartType === "bar" ? 4 : 0
|
|
23011
|
+
}
|
|
23012
|
+
];
|
|
23013
|
+
}
|
|
23014
|
+
const temperatureAnnotations = buildTemperatureAnnotations();
|
|
23015
|
+
const idealRangeAnnotations = buildIdealRangeAnnotation();
|
|
23016
|
+
const allAnnotations = { ...temperatureAnnotations, ...idealRangeAnnotations };
|
|
23017
|
+
const yAxisLabel = config.unitLarge && config.thresholdForLargeUnit && yAxisMax >= config.thresholdForLargeUnit ? config.unitLarge : config.unit;
|
|
23018
|
+
return {
|
|
23019
|
+
type: currentChartType,
|
|
23020
|
+
data: {
|
|
23021
|
+
labels: data.labels,
|
|
23022
|
+
datasets
|
|
23023
|
+
},
|
|
23024
|
+
options: {
|
|
23025
|
+
responsive: true,
|
|
23026
|
+
maintainAspectRatio: false,
|
|
23027
|
+
animation: false,
|
|
23028
|
+
// CRITICAL: Prevents infinite growth bug
|
|
23029
|
+
plugins: {
|
|
23030
|
+
legend: {
|
|
23031
|
+
display: showLegend || currentVizMode === "separate",
|
|
23032
|
+
position: "bottom",
|
|
23033
|
+
labels: {
|
|
23034
|
+
color: themeColors.text
|
|
23035
|
+
}
|
|
23036
|
+
},
|
|
23037
|
+
tooltip: {
|
|
23038
|
+
backgroundColor: themeColors.tooltipBackground,
|
|
23039
|
+
titleColor: themeColors.tooltipText,
|
|
23040
|
+
bodyColor: themeColors.tooltipText,
|
|
23041
|
+
borderColor: themeColors.border,
|
|
23042
|
+
borderWidth: 1,
|
|
23043
|
+
callbacks: {
|
|
23044
|
+
label: function(context) {
|
|
23045
|
+
const value = context.parsed.y || 0;
|
|
23046
|
+
const label = context.dataset.label || "";
|
|
23047
|
+
return `${label}: ${formatValue(value)}`;
|
|
23048
|
+
}
|
|
23049
|
+
}
|
|
23050
|
+
},
|
|
23051
|
+
// Reference lines and ideal range (requires chartjs-plugin-annotation)
|
|
23052
|
+
annotation: Object.keys(allAnnotations).length > 0 ? {
|
|
23053
|
+
annotations: allAnnotations
|
|
23054
|
+
} : void 0
|
|
23055
|
+
},
|
|
23056
|
+
scales: {
|
|
23057
|
+
y: {
|
|
23058
|
+
beginAtZero: !isTemperature,
|
|
23059
|
+
// Temperature can have negative values
|
|
23060
|
+
min: yAxisMin,
|
|
23061
|
+
max: yAxisMax,
|
|
23062
|
+
// CRITICAL: Fixed max prevents animation loop
|
|
23063
|
+
grid: {
|
|
23064
|
+
color: themeColors.grid
|
|
23065
|
+
},
|
|
23066
|
+
title: {
|
|
23067
|
+
display: true,
|
|
23068
|
+
text: yAxisLabel,
|
|
23069
|
+
font: { size: 12 },
|
|
23070
|
+
color: themeColors.text
|
|
23071
|
+
},
|
|
23072
|
+
ticks: {
|
|
23073
|
+
font: { size: 11 },
|
|
23074
|
+
color: themeColors.textMuted,
|
|
23075
|
+
callback: function(value) {
|
|
23076
|
+
return formatTickValue(value);
|
|
23077
|
+
}
|
|
23078
|
+
}
|
|
23079
|
+
},
|
|
23080
|
+
x: {
|
|
23081
|
+
grid: {
|
|
23082
|
+
color: themeColors.grid
|
|
23083
|
+
},
|
|
23084
|
+
ticks: {
|
|
23085
|
+
font: { size: 11 },
|
|
23086
|
+
color: themeColors.textMuted
|
|
23087
|
+
}
|
|
23088
|
+
}
|
|
23089
|
+
}
|
|
23090
|
+
}
|
|
23091
|
+
};
|
|
23092
|
+
}
|
|
23093
|
+
function validateChartJs() {
|
|
23094
|
+
if (typeof Chart === "undefined") {
|
|
23095
|
+
log("error", "Chart.js not loaded. Cannot initialize chart.");
|
|
23096
|
+
config.onError?.(new Error("Chart.js not loaded"));
|
|
23097
|
+
return false;
|
|
23098
|
+
}
|
|
23099
|
+
return true;
|
|
23100
|
+
}
|
|
23101
|
+
function validateCanvas() {
|
|
23102
|
+
const canvas = $id(config.containerId);
|
|
23103
|
+
if (!canvas) {
|
|
23104
|
+
log("error", `Canvas #${config.containerId} not found`);
|
|
23105
|
+
config.onError?.(new Error(`Canvas #${config.containerId} not found`));
|
|
23106
|
+
return null;
|
|
23107
|
+
}
|
|
23108
|
+
return canvas;
|
|
23109
|
+
}
|
|
23110
|
+
function setupAutoRefresh() {
|
|
23111
|
+
if (config.autoRefreshInterval && config.autoRefreshInterval > 0) {
|
|
23112
|
+
if (autoRefreshTimer) {
|
|
23113
|
+
clearInterval(autoRefreshTimer);
|
|
23114
|
+
}
|
|
23115
|
+
autoRefreshTimer = setInterval(async () => {
|
|
23116
|
+
log("log", "Auto-refreshing data...");
|
|
23117
|
+
await instance.refresh(true);
|
|
23118
|
+
}, config.autoRefreshInterval);
|
|
23119
|
+
}
|
|
23120
|
+
}
|
|
23121
|
+
function cleanupAutoRefresh() {
|
|
23122
|
+
if (autoRefreshTimer) {
|
|
23123
|
+
clearInterval(autoRefreshTimer);
|
|
23124
|
+
autoRefreshTimer = null;
|
|
23125
|
+
}
|
|
23126
|
+
}
|
|
23127
|
+
function setupButtonHandlers() {
|
|
23128
|
+
if (config.settingsButtonId && config.onSettingsClick) {
|
|
23129
|
+
const settingsBtn = $id(config.settingsButtonId);
|
|
23130
|
+
if (settingsBtn) {
|
|
23131
|
+
settingsBtn.addEventListener("click", () => {
|
|
23132
|
+
log("log", "Settings button clicked");
|
|
23133
|
+
config.onSettingsClick?.();
|
|
23134
|
+
});
|
|
23135
|
+
log("log", "Settings button handler attached");
|
|
23136
|
+
}
|
|
23137
|
+
}
|
|
23138
|
+
if (config.maximizeButtonId && config.onMaximizeClick) {
|
|
23139
|
+
const maximizeBtn = $id(config.maximizeButtonId);
|
|
23140
|
+
if (maximizeBtn) {
|
|
23141
|
+
maximizeBtn.addEventListener("click", () => {
|
|
23142
|
+
log("log", "Maximize button clicked");
|
|
23143
|
+
config.onMaximizeClick?.();
|
|
23144
|
+
});
|
|
23145
|
+
log("log", "Maximize button handler attached");
|
|
23146
|
+
}
|
|
23147
|
+
}
|
|
23148
|
+
const enableExport = config.enableExport ?? DEFAULT_CONFIG.enableExport;
|
|
23149
|
+
if (enableExport && config.exportButtonId) {
|
|
23150
|
+
const exportBtn = $id(config.exportButtonId);
|
|
23151
|
+
if (exportBtn) {
|
|
23152
|
+
exportBtn.addEventListener("click", () => {
|
|
23153
|
+
log("log", "Export button clicked");
|
|
23154
|
+
if (config.onExportCSV && cachedData) {
|
|
23155
|
+
config.onExportCSV(cachedData);
|
|
23156
|
+
} else {
|
|
23157
|
+
instance.exportCSV();
|
|
23158
|
+
}
|
|
23159
|
+
});
|
|
23160
|
+
log("log", "Export button handler attached");
|
|
23161
|
+
}
|
|
23162
|
+
}
|
|
23163
|
+
}
|
|
23164
|
+
function generateCSVContent(data) {
|
|
23165
|
+
const rows = [];
|
|
23166
|
+
const decimals = config.decimalPlaces ?? DEFAULT_CONFIG.decimalPlaces;
|
|
23167
|
+
if (currentVizMode === "separate" && data.shoppingData && data.shoppingNames) {
|
|
23168
|
+
const shoppingHeaders = Object.keys(data.shoppingData).map(
|
|
23169
|
+
(id) => data.shoppingNames?.[id] || id
|
|
23170
|
+
);
|
|
23171
|
+
rows.push(["Data", ...shoppingHeaders, "Total"].join(";"));
|
|
23172
|
+
data.labels.forEach((label, index) => {
|
|
23173
|
+
const shoppingValues = Object.keys(data.shoppingData).map(
|
|
23174
|
+
(id) => data.shoppingData[id][index].toFixed(decimals)
|
|
23175
|
+
);
|
|
23176
|
+
rows.push([label, ...shoppingValues, data.dailyTotals[index].toFixed(decimals)].join(";"));
|
|
23177
|
+
});
|
|
23178
|
+
} else {
|
|
23179
|
+
rows.push(["Data", `Consumo (${config.unit})`].join(";"));
|
|
23180
|
+
data.labels.forEach((label, index) => {
|
|
23181
|
+
rows.push([label, data.dailyTotals[index].toFixed(decimals)].join(";"));
|
|
23182
|
+
});
|
|
23183
|
+
}
|
|
23184
|
+
const total = data.dailyTotals.reduce((sum, v) => sum + v, 0);
|
|
23185
|
+
const avg = total / data.dailyTotals.length;
|
|
23186
|
+
rows.push("");
|
|
23187
|
+
rows.push(["Total", total.toFixed(decimals)].join(";"));
|
|
23188
|
+
rows.push(["M\xE9dia", avg.toFixed(decimals)].join(";"));
|
|
23189
|
+
return rows.join("\n");
|
|
23190
|
+
}
|
|
23191
|
+
function downloadCSV(content, filename) {
|
|
23192
|
+
const BOM = "\uFEFF";
|
|
23193
|
+
const blob = new Blob([BOM + content], { type: "text/csv;charset=utf-8" });
|
|
23194
|
+
const url = URL.createObjectURL(blob);
|
|
23195
|
+
const link = document.createElement("a");
|
|
23196
|
+
link.href = url;
|
|
23197
|
+
link.download = `${filename}.csv`;
|
|
23198
|
+
document.body.appendChild(link);
|
|
23199
|
+
link.click();
|
|
23200
|
+
document.body.removeChild(link);
|
|
23201
|
+
URL.revokeObjectURL(url);
|
|
23202
|
+
log("log", `CSV exported: ${filename}.csv`);
|
|
23203
|
+
}
|
|
23204
|
+
function updateTitle() {
|
|
23205
|
+
if (config.titleElementId) {
|
|
23206
|
+
const titleEl = $id(config.titleElementId);
|
|
23207
|
+
if (titleEl) {
|
|
23208
|
+
if (currentPeriod === 0) {
|
|
23209
|
+
titleEl.textContent = `Consumo - Per\xEDodo Personalizado`;
|
|
23210
|
+
} else {
|
|
23211
|
+
titleEl.textContent = `Consumo dos \xFAltimos ${currentPeriod} dias`;
|
|
23212
|
+
}
|
|
23213
|
+
}
|
|
23214
|
+
}
|
|
23215
|
+
}
|
|
23216
|
+
const instance = {
|
|
23217
|
+
async render() {
|
|
23218
|
+
log("log", "Rendering chart...");
|
|
23219
|
+
if (!validateChartJs()) return;
|
|
23220
|
+
const canvas = validateCanvas();
|
|
23221
|
+
if (!canvas) return;
|
|
23222
|
+
try {
|
|
23223
|
+
log("log", `Fetching ${currentPeriod} days of data...`);
|
|
23224
|
+
cachedData = await config.fetchData(currentPeriod);
|
|
23225
|
+
cachedData.fetchTimestamp = Date.now();
|
|
23226
|
+
if (config.onBeforeRender) {
|
|
23227
|
+
cachedData = config.onBeforeRender(cachedData);
|
|
23228
|
+
}
|
|
23229
|
+
if (chartInstance) {
|
|
23230
|
+
chartInstance.destroy();
|
|
23231
|
+
chartInstance = null;
|
|
23232
|
+
}
|
|
23233
|
+
const ctx = canvas.getContext("2d");
|
|
23234
|
+
const chartConfig = buildChartConfig(cachedData);
|
|
23235
|
+
chartInstance = new Chart(ctx, chartConfig);
|
|
23236
|
+
isRendered = true;
|
|
23237
|
+
const yAxisMax = calculateYAxisMax(cachedData.dailyTotals);
|
|
23238
|
+
log("log", `Chart initialized with yAxisMax: ${yAxisMax}`);
|
|
23239
|
+
config.onDataLoaded?.(cachedData);
|
|
23240
|
+
config.onAfterRender?.(chartInstance);
|
|
23241
|
+
setupButtonHandlers();
|
|
23242
|
+
updateTitle();
|
|
23243
|
+
setupAutoRefresh();
|
|
23244
|
+
} catch (error) {
|
|
23245
|
+
log("error", "Failed to render chart:", error);
|
|
23246
|
+
config.onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
23247
|
+
}
|
|
23248
|
+
},
|
|
23249
|
+
async update(data) {
|
|
23250
|
+
if (data) {
|
|
23251
|
+
cachedData = data;
|
|
23252
|
+
cachedData.fetchTimestamp = Date.now();
|
|
23253
|
+
}
|
|
23254
|
+
if (!chartInstance || !cachedData) {
|
|
23255
|
+
log("warn", "Cannot update: chart not initialized or no data");
|
|
23256
|
+
return;
|
|
23257
|
+
}
|
|
23258
|
+
let renderData = cachedData;
|
|
23259
|
+
if (config.onBeforeRender) {
|
|
23260
|
+
renderData = config.onBeforeRender(cachedData);
|
|
23261
|
+
}
|
|
23262
|
+
const chartConfig = buildChartConfig(renderData);
|
|
23263
|
+
chartInstance.data = chartConfig.data;
|
|
23264
|
+
chartInstance.options = chartConfig.options;
|
|
23265
|
+
chartInstance.update("none");
|
|
23266
|
+
log("log", "Chart updated");
|
|
23267
|
+
},
|
|
23268
|
+
setChartType(type) {
|
|
23269
|
+
if (currentChartType === type) return;
|
|
23270
|
+
log("log", `Changing chart type to: ${type}`);
|
|
23271
|
+
currentChartType = type;
|
|
23272
|
+
if (cachedData && chartInstance) {
|
|
23273
|
+
const canvas = validateCanvas();
|
|
23274
|
+
if (canvas) {
|
|
23275
|
+
chartInstance.destroy();
|
|
23276
|
+
const ctx = canvas.getContext("2d");
|
|
23277
|
+
chartInstance = new Chart(ctx, buildChartConfig(cachedData));
|
|
23278
|
+
}
|
|
23279
|
+
}
|
|
23280
|
+
},
|
|
23281
|
+
setVizMode(mode) {
|
|
23282
|
+
if (currentVizMode === mode) return;
|
|
23283
|
+
log("log", `Changing viz mode to: ${mode}`);
|
|
23284
|
+
currentVizMode = mode;
|
|
23285
|
+
if (cachedData) {
|
|
23286
|
+
this.update();
|
|
23287
|
+
}
|
|
23288
|
+
},
|
|
23289
|
+
async setPeriod(days) {
|
|
23290
|
+
if (currentPeriod === days) return;
|
|
23291
|
+
log("log", `Changing period to: ${days} days`);
|
|
23292
|
+
currentPeriod = days;
|
|
23293
|
+
updateTitle();
|
|
23294
|
+
await this.refresh(true);
|
|
23295
|
+
},
|
|
23296
|
+
async refresh(forceRefresh = false) {
|
|
23297
|
+
if (!forceRefresh && cachedData?.fetchTimestamp) {
|
|
23298
|
+
const age = Date.now() - cachedData.fetchTimestamp;
|
|
23299
|
+
const ttl = config.cacheTTL ?? DEFAULT_CONFIG.cacheTTL;
|
|
23300
|
+
if (age < ttl) {
|
|
23301
|
+
log("log", `Using cached data (age: ${Math.round(age / 1e3)}s)`);
|
|
23302
|
+
return;
|
|
23303
|
+
}
|
|
23304
|
+
}
|
|
23305
|
+
log("log", "Refreshing data...");
|
|
23306
|
+
await this.render();
|
|
23307
|
+
},
|
|
23308
|
+
destroy() {
|
|
23309
|
+
log("log", "Destroying chart...");
|
|
23310
|
+
cleanupAutoRefresh();
|
|
23311
|
+
if (chartInstance) {
|
|
23312
|
+
chartInstance.destroy();
|
|
23313
|
+
chartInstance = null;
|
|
23314
|
+
}
|
|
23315
|
+
cachedData = null;
|
|
23316
|
+
isRendered = false;
|
|
23317
|
+
},
|
|
23318
|
+
getChartInstance() {
|
|
23319
|
+
return chartInstance;
|
|
23320
|
+
},
|
|
23321
|
+
getCachedData() {
|
|
23322
|
+
return cachedData;
|
|
23323
|
+
},
|
|
23324
|
+
getState() {
|
|
23325
|
+
return {
|
|
23326
|
+
period: currentPeriod,
|
|
23327
|
+
chartType: currentChartType,
|
|
23328
|
+
vizMode: currentVizMode,
|
|
23329
|
+
theme: currentTheme,
|
|
23330
|
+
isRendered
|
|
23331
|
+
};
|
|
23332
|
+
},
|
|
23333
|
+
exportCSV(filename) {
|
|
23334
|
+
if (!cachedData) {
|
|
23335
|
+
log("warn", "Cannot export: no data available");
|
|
23336
|
+
return;
|
|
23337
|
+
}
|
|
23338
|
+
const defaultFilename = config.exportFilename || `${config.domain}-consumo-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
|
|
23339
|
+
const csvContent = generateCSVContent(cachedData);
|
|
23340
|
+
downloadCSV(csvContent, filename || defaultFilename);
|
|
23341
|
+
},
|
|
23342
|
+
setTheme(theme) {
|
|
23343
|
+
if (currentTheme === theme) return;
|
|
23344
|
+
log("log", `Changing theme to: ${theme}`);
|
|
23345
|
+
currentTheme = theme;
|
|
23346
|
+
if (cachedData && chartInstance) {
|
|
23347
|
+
const canvas = validateCanvas();
|
|
23348
|
+
if (canvas) {
|
|
23349
|
+
chartInstance.destroy();
|
|
23350
|
+
const ctx = canvas.getContext("2d");
|
|
23351
|
+
chartInstance = new Chart(ctx, buildChartConfig(cachedData));
|
|
23352
|
+
}
|
|
23353
|
+
}
|
|
23354
|
+
},
|
|
23355
|
+
setIdealRange(range) {
|
|
23356
|
+
const rangeChanged = JSON.stringify(currentIdealRange) !== JSON.stringify(range);
|
|
23357
|
+
if (!rangeChanged) return;
|
|
23358
|
+
if (range) {
|
|
23359
|
+
log("log", `Setting ideal range: ${range.min} - ${range.max}`);
|
|
23360
|
+
} else {
|
|
23361
|
+
log("log", "Clearing ideal range");
|
|
23362
|
+
}
|
|
23363
|
+
currentIdealRange = range;
|
|
23364
|
+
if (cachedData && chartInstance) {
|
|
23365
|
+
const canvas = validateCanvas();
|
|
23366
|
+
if (canvas) {
|
|
23367
|
+
chartInstance.destroy();
|
|
23368
|
+
const ctx = canvas.getContext("2d");
|
|
23369
|
+
chartInstance = new Chart(ctx, buildChartConfig(cachedData));
|
|
23370
|
+
}
|
|
23371
|
+
}
|
|
23372
|
+
},
|
|
23373
|
+
getIdealRange() {
|
|
23374
|
+
return currentIdealRange;
|
|
23375
|
+
}
|
|
23376
|
+
};
|
|
23377
|
+
return instance;
|
|
23378
|
+
}
|
|
23379
|
+
|
|
23380
|
+
// src/components/Consumption7DaysChart/createConsumptionModal.ts
|
|
23381
|
+
var DOMAIN_CONFIG3 = {
|
|
23382
|
+
energy: { name: "Energia", icon: "\u26A1" },
|
|
23383
|
+
water: { name: "\xC1gua", icon: "\u{1F4A7}" },
|
|
23384
|
+
gas: { name: "G\xE1s", icon: "\u{1F525}" },
|
|
23385
|
+
temperature: { name: "Temperatura", icon: "\u{1F321}\uFE0F" }
|
|
23386
|
+
};
|
|
23387
|
+
function createConsumptionModal(config) {
|
|
23388
|
+
const modalId = `myio-consumption-modal-${Date.now()}`;
|
|
23389
|
+
let modalElement = null;
|
|
23390
|
+
let chartInstance = null;
|
|
23391
|
+
let headerInstance = null;
|
|
23392
|
+
let currentTheme = config.theme ?? "light";
|
|
23393
|
+
let currentChartType = config.defaultChartType ?? "line";
|
|
23394
|
+
let currentVizMode = config.defaultVizMode ?? "total";
|
|
23395
|
+
let isMaximized = false;
|
|
23396
|
+
const domainCfg = DOMAIN_CONFIG3[config.domain] || { name: config.domain, icon: "\u{1F4CA}" };
|
|
23397
|
+
const title = config.title || `${domainCfg.name} - Hist\xF3rico de Consumo`;
|
|
23398
|
+
function getThemeColors2() {
|
|
23399
|
+
return THEME_COLORS[currentTheme];
|
|
23400
|
+
}
|
|
23401
|
+
function renderModal4() {
|
|
23402
|
+
const colors = getThemeColors2();
|
|
23403
|
+
const exportFormats = config.exportFormats || ["csv"];
|
|
23404
|
+
headerInstance = createModalHeader({
|
|
23405
|
+
id: modalId,
|
|
23406
|
+
title,
|
|
23407
|
+
icon: domainCfg.icon,
|
|
23408
|
+
theme: currentTheme,
|
|
23409
|
+
isMaximized,
|
|
23410
|
+
exportFormats,
|
|
23411
|
+
onExport: (format) => {
|
|
23412
|
+
if (config.onExport) {
|
|
23413
|
+
config.onExport(format);
|
|
23414
|
+
} else {
|
|
23415
|
+
if (format === "csv") {
|
|
23416
|
+
chartInstance?.exportCSV();
|
|
23417
|
+
} else {
|
|
23418
|
+
console.warn(`[ConsumptionModal] Export format "${format}" requires custom onExport handler`);
|
|
23419
|
+
}
|
|
23420
|
+
}
|
|
23421
|
+
},
|
|
23422
|
+
onThemeToggle: (theme) => {
|
|
23423
|
+
currentTheme = theme;
|
|
23424
|
+
chartInstance?.setTheme(currentTheme);
|
|
23425
|
+
updateModal();
|
|
23426
|
+
},
|
|
23427
|
+
onMaximize: (maximized) => {
|
|
23428
|
+
isMaximized = maximized;
|
|
23429
|
+
updateModal();
|
|
23430
|
+
},
|
|
23431
|
+
onClose: () => {
|
|
23432
|
+
instance.close();
|
|
23433
|
+
}
|
|
23434
|
+
});
|
|
23435
|
+
return `
|
|
23436
|
+
<div class="myio-consumption-modal-overlay" style="
|
|
23437
|
+
position: fixed;
|
|
23438
|
+
top: 0;
|
|
23439
|
+
left: 0;
|
|
23440
|
+
width: 100%;
|
|
23441
|
+
height: 100%;
|
|
23442
|
+
background: rgba(0, 0, 0, 0.5);
|
|
23443
|
+
backdrop-filter: blur(2px);
|
|
23444
|
+
z-index: 99998;
|
|
23445
|
+
display: flex;
|
|
23446
|
+
justify-content: center;
|
|
23447
|
+
align-items: center;
|
|
23448
|
+
">
|
|
23449
|
+
<div class="myio-consumption-modal-content" style="
|
|
23450
|
+
background: ${colors.chartBackground};
|
|
23451
|
+
border-radius: ${isMaximized ? "0" : "10px"};
|
|
23452
|
+
width: ${isMaximized ? "100%" : "95%"};
|
|
23453
|
+
max-width: ${isMaximized ? "100%" : "1200px"};
|
|
23454
|
+
height: ${isMaximized ? "100%" : "85vh"};
|
|
23455
|
+
display: flex;
|
|
23456
|
+
flex-direction: column;
|
|
23457
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
23458
|
+
overflow: hidden;
|
|
23459
|
+
">
|
|
23460
|
+
<!-- MyIO Premium Header (using ModalHeader component) -->
|
|
23461
|
+
${headerInstance.render()}
|
|
23462
|
+
|
|
23463
|
+
<!-- Controls Bar -->
|
|
23464
|
+
<div class="myio-consumption-modal-controls" style="
|
|
23465
|
+
display: flex;
|
|
23466
|
+
gap: 16px;
|
|
23467
|
+
padding: 12px 16px;
|
|
23468
|
+
background: ${currentTheme === "dark" ? "#374151" : "#f7f7f7"};
|
|
23469
|
+
border-bottom: 1px solid ${colors.border};
|
|
23470
|
+
align-items: center;
|
|
23471
|
+
flex-wrap: wrap;
|
|
23472
|
+
">
|
|
23473
|
+
<!-- Viz Mode Tabs -->
|
|
23474
|
+
<div style="display: flex; gap: 2px; background: ${currentTheme === "dark" ? "#4b5563" : "#e5e7eb"}; border-radius: 8px; padding: 2px;">
|
|
23475
|
+
<button id="${modalId}-viz-total" style="
|
|
23476
|
+
padding: 6px 12px;
|
|
23477
|
+
border: none;
|
|
23478
|
+
border-radius: 6px;
|
|
23479
|
+
font-size: 13px;
|
|
23480
|
+
cursor: pointer;
|
|
23481
|
+
transition: all 0.2s;
|
|
23482
|
+
background: ${currentVizMode === "total" ? "#3e1a7d" : "transparent"};
|
|
23483
|
+
color: ${currentVizMode === "total" ? "white" : colors.text};
|
|
23484
|
+
">Consolidado</button>
|
|
23485
|
+
<button id="${modalId}-viz-separate" style="
|
|
23486
|
+
padding: 6px 12px;
|
|
23487
|
+
border: none;
|
|
23488
|
+
border-radius: 6px;
|
|
23489
|
+
font-size: 13px;
|
|
23490
|
+
cursor: pointer;
|
|
23491
|
+
transition: all 0.2s;
|
|
23492
|
+
background: ${currentVizMode === "separate" ? "#3e1a7d" : "transparent"};
|
|
23493
|
+
color: ${currentVizMode === "separate" ? "white" : colors.text};
|
|
23494
|
+
">Por Shopping</button>
|
|
23495
|
+
</div>
|
|
23496
|
+
|
|
23497
|
+
<!-- Chart Type Tabs -->
|
|
23498
|
+
<div style="display: flex; gap: 2px; background: ${currentTheme === "dark" ? "#4b5563" : "#e5e7eb"}; border-radius: 8px; padding: 2px;">
|
|
23499
|
+
<button id="${modalId}-type-line" style="
|
|
23500
|
+
padding: 6px 12px;
|
|
23501
|
+
border: none;
|
|
23502
|
+
border-radius: 6px;
|
|
23503
|
+
font-size: 13px;
|
|
23504
|
+
cursor: pointer;
|
|
23505
|
+
transition: all 0.2s;
|
|
23506
|
+
background: ${currentChartType === "line" ? "#3e1a7d" : "transparent"};
|
|
23507
|
+
color: ${currentChartType === "line" ? "white" : colors.text};
|
|
23508
|
+
">Linhas</button>
|
|
23509
|
+
<button id="${modalId}-type-bar" style="
|
|
23510
|
+
padding: 6px 12px;
|
|
23511
|
+
border: none;
|
|
23512
|
+
border-radius: 6px;
|
|
23513
|
+
font-size: 13px;
|
|
23514
|
+
cursor: pointer;
|
|
23515
|
+
transition: all 0.2s;
|
|
23516
|
+
background: ${currentChartType === "bar" ? "#3e1a7d" : "transparent"};
|
|
23517
|
+
color: ${currentChartType === "bar" ? "white" : colors.text};
|
|
23518
|
+
">Barras</button>
|
|
23519
|
+
</div>
|
|
23520
|
+
</div>
|
|
23521
|
+
|
|
23522
|
+
<!-- Chart Container -->
|
|
23523
|
+
<div style="
|
|
23524
|
+
flex: 1;
|
|
23525
|
+
padding: 16px;
|
|
23526
|
+
min-height: 0;
|
|
23527
|
+
position: relative;
|
|
23528
|
+
background: ${colors.chartBackground};
|
|
23529
|
+
">
|
|
23530
|
+
<canvas id="${modalId}-chart" style="width: 100%; height: 100%;"></canvas>
|
|
23531
|
+
</div>
|
|
23532
|
+
</div>
|
|
23533
|
+
</div>
|
|
23534
|
+
`;
|
|
23535
|
+
}
|
|
23536
|
+
function setupListeners() {
|
|
23537
|
+
if (!modalElement) return;
|
|
23538
|
+
headerInstance?.attachListeners();
|
|
23539
|
+
document.getElementById(`${modalId}-viz-total`)?.addEventListener("click", () => {
|
|
23540
|
+
currentVizMode = "total";
|
|
23541
|
+
chartInstance?.setVizMode("total");
|
|
23542
|
+
updateControlStyles();
|
|
23543
|
+
});
|
|
23544
|
+
document.getElementById(`${modalId}-viz-separate`)?.addEventListener("click", () => {
|
|
23545
|
+
currentVizMode = "separate";
|
|
23546
|
+
chartInstance?.setVizMode("separate");
|
|
23547
|
+
updateControlStyles();
|
|
23548
|
+
});
|
|
23549
|
+
document.getElementById(`${modalId}-type-line`)?.addEventListener("click", () => {
|
|
23550
|
+
currentChartType = "line";
|
|
23551
|
+
chartInstance?.setChartType("line");
|
|
23552
|
+
updateControlStyles();
|
|
23553
|
+
});
|
|
23554
|
+
document.getElementById(`${modalId}-type-bar`)?.addEventListener("click", () => {
|
|
23555
|
+
currentChartType = "bar";
|
|
23556
|
+
chartInstance?.setChartType("bar");
|
|
23557
|
+
updateControlStyles();
|
|
23558
|
+
});
|
|
23559
|
+
modalElement.querySelector(".myio-consumption-modal-overlay")?.addEventListener("click", (e) => {
|
|
23560
|
+
if (e.target.classList.contains("myio-consumption-modal-overlay")) {
|
|
23561
|
+
instance.close();
|
|
23562
|
+
}
|
|
23563
|
+
});
|
|
23564
|
+
const handleKeydown = (e) => {
|
|
23565
|
+
if (e.key === "Escape") {
|
|
23566
|
+
instance.close();
|
|
23567
|
+
}
|
|
23568
|
+
};
|
|
23569
|
+
document.addEventListener("keydown", handleKeydown);
|
|
23570
|
+
modalElement.__handleKeydown = handleKeydown;
|
|
23571
|
+
}
|
|
23572
|
+
function updateControlStyles() {
|
|
23573
|
+
const colors = getThemeColors2();
|
|
23574
|
+
const vizTotalBtn = document.getElementById(`${modalId}-viz-total`);
|
|
23575
|
+
const vizSeparateBtn = document.getElementById(`${modalId}-viz-separate`);
|
|
23576
|
+
if (vizTotalBtn) {
|
|
23577
|
+
vizTotalBtn.style.background = currentVizMode === "total" ? "#3e1a7d" : "transparent";
|
|
23578
|
+
vizTotalBtn.style.color = currentVizMode === "total" ? "white" : colors.text;
|
|
23579
|
+
}
|
|
23580
|
+
if (vizSeparateBtn) {
|
|
23581
|
+
vizSeparateBtn.style.background = currentVizMode === "separate" ? "#3e1a7d" : "transparent";
|
|
23582
|
+
vizSeparateBtn.style.color = currentVizMode === "separate" ? "white" : colors.text;
|
|
23583
|
+
}
|
|
23584
|
+
const typeLineBtn = document.getElementById(`${modalId}-type-line`);
|
|
23585
|
+
const typeBarBtn = document.getElementById(`${modalId}-type-bar`);
|
|
23586
|
+
if (typeLineBtn) {
|
|
23587
|
+
typeLineBtn.style.background = currentChartType === "line" ? "#3e1a7d" : "transparent";
|
|
23588
|
+
typeLineBtn.style.color = currentChartType === "line" ? "white" : colors.text;
|
|
23589
|
+
}
|
|
23590
|
+
if (typeBarBtn) {
|
|
23591
|
+
typeBarBtn.style.background = currentChartType === "bar" ? "#3e1a7d" : "transparent";
|
|
23592
|
+
typeBarBtn.style.color = currentChartType === "bar" ? "white" : colors.text;
|
|
23593
|
+
}
|
|
23594
|
+
}
|
|
23595
|
+
function updateModal() {
|
|
23596
|
+
if (!modalElement) return;
|
|
23597
|
+
const cachedData = chartInstance?.getCachedData();
|
|
23598
|
+
headerInstance?.destroy();
|
|
23599
|
+
chartInstance?.destroy();
|
|
23600
|
+
modalElement.innerHTML = renderModal4();
|
|
23601
|
+
setupListeners();
|
|
23602
|
+
if (cachedData) {
|
|
23603
|
+
chartInstance = createConsumption7DaysChart({
|
|
23604
|
+
...config,
|
|
23605
|
+
containerId: `${modalId}-chart`,
|
|
23606
|
+
theme: currentTheme,
|
|
23607
|
+
defaultChartType: currentChartType,
|
|
23608
|
+
defaultVizMode: currentVizMode
|
|
23609
|
+
});
|
|
23610
|
+
chartInstance.update(cachedData);
|
|
23611
|
+
}
|
|
23612
|
+
}
|
|
23613
|
+
const instance = {
|
|
23614
|
+
async open() {
|
|
23615
|
+
modalElement = document.createElement("div");
|
|
23616
|
+
modalElement.id = modalId;
|
|
23617
|
+
modalElement.innerHTML = renderModal4();
|
|
23618
|
+
const container = config.container || document.body;
|
|
23619
|
+
container.appendChild(modalElement);
|
|
23620
|
+
setupListeners();
|
|
23621
|
+
chartInstance = createConsumption7DaysChart({
|
|
23622
|
+
...config,
|
|
23623
|
+
containerId: `${modalId}-chart`,
|
|
23624
|
+
theme: currentTheme,
|
|
23625
|
+
defaultChartType: currentChartType,
|
|
23626
|
+
defaultVizMode: currentVizMode
|
|
23627
|
+
});
|
|
23628
|
+
await chartInstance.render();
|
|
23629
|
+
},
|
|
23630
|
+
close() {
|
|
23631
|
+
if (modalElement) {
|
|
23632
|
+
const handleKeydown = modalElement.__handleKeydown;
|
|
23633
|
+
if (handleKeydown) {
|
|
23634
|
+
document.removeEventListener("keydown", handleKeydown);
|
|
23635
|
+
}
|
|
23636
|
+
headerInstance?.destroy();
|
|
23637
|
+
headerInstance = null;
|
|
23638
|
+
chartInstance?.destroy();
|
|
23639
|
+
chartInstance = null;
|
|
23640
|
+
modalElement.remove();
|
|
23641
|
+
modalElement = null;
|
|
23642
|
+
config.onClose?.();
|
|
23643
|
+
}
|
|
23644
|
+
},
|
|
23645
|
+
getChart() {
|
|
23646
|
+
return chartInstance;
|
|
23647
|
+
},
|
|
23648
|
+
destroy() {
|
|
23649
|
+
instance.close();
|
|
23650
|
+
}
|
|
23651
|
+
};
|
|
23652
|
+
return instance;
|
|
23653
|
+
}
|
|
23654
|
+
|
|
23655
|
+
// src/components/ExportData/index.ts
|
|
23656
|
+
var DEFAULT_COLORS3 = {
|
|
23657
|
+
primary: "#3e1a7d",
|
|
23658
|
+
// MyIO purple
|
|
23659
|
+
secondary: "#6b4c9a",
|
|
23660
|
+
// Light purple
|
|
23661
|
+
accent: "#00bcd4",
|
|
23662
|
+
// Cyan accent
|
|
23663
|
+
background: "#ffffff",
|
|
23664
|
+
// White
|
|
23665
|
+
text: "#333333",
|
|
23666
|
+
// Dark gray
|
|
23667
|
+
chartColors: ["#3e1a7d", "#00bcd4", "#4caf50", "#ff9800", "#e91e63", "#9c27b0"]
|
|
23668
|
+
};
|
|
23669
|
+
var DOMAIN_ICONS = {
|
|
23670
|
+
energy: "\u26A1",
|
|
23671
|
+
// Lightning bolt
|
|
23672
|
+
water: "\u{1F4A7}",
|
|
23673
|
+
// Water drop
|
|
23674
|
+
temperature: "\u{1F321}\uFE0F"
|
|
23675
|
+
// Thermometer
|
|
23676
|
+
};
|
|
23677
|
+
var DOMAIN_LABELS = {
|
|
23678
|
+
energy: "Energia",
|
|
23679
|
+
water: "\xC1gua",
|
|
23680
|
+
temperature: "Temperatura"
|
|
23681
|
+
};
|
|
23682
|
+
var DOMAIN_LABELS_EN = {
|
|
23683
|
+
energy: "Energy",
|
|
23684
|
+
water: "Water",
|
|
23685
|
+
temperature: "Temperature"
|
|
23686
|
+
};
|
|
23687
|
+
var DOMAIN_UNITS = {
|
|
23688
|
+
energy: "kWh",
|
|
23689
|
+
water: "m\xB3",
|
|
23690
|
+
temperature: "\xB0C"
|
|
23691
|
+
};
|
|
23692
|
+
var CSV_SEPARATORS = {
|
|
23693
|
+
"pt-BR": ";",
|
|
23694
|
+
"en-US": ",",
|
|
23695
|
+
"default": ";"
|
|
23696
|
+
};
|
|
23697
|
+
function formatDateForFilename(date) {
|
|
23698
|
+
const pad = (n) => n.toString().padStart(2, "0");
|
|
23699
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}-${pad(date.getHours())}-${pad(date.getMinutes())}-${pad(date.getSeconds())}`;
|
|
23700
|
+
}
|
|
23701
|
+
function sanitizeFilename(str) {
|
|
23702
|
+
return str.replace(/[<>:"/\\|?*]/g, "").replace(/\s+/g, "_").substring(0, 50);
|
|
23703
|
+
}
|
|
23704
|
+
function generateFilename(data, config) {
|
|
23705
|
+
const timestamp = formatDateForFilename(/* @__PURE__ */ new Date());
|
|
23706
|
+
const domainLabel = DOMAIN_LABELS_EN[config.domain].toUpperCase();
|
|
23707
|
+
const ext = config.formatExport;
|
|
23708
|
+
let baseName = "export";
|
|
23709
|
+
if ("device" in data && data.device) {
|
|
23710
|
+
const device = data.device;
|
|
23711
|
+
const label = device.label || device.name || "device";
|
|
23712
|
+
const identifier = device.identifier ? `-${device.identifier}` : "";
|
|
23713
|
+
baseName = `${sanitizeFilename(label)}${identifier}`;
|
|
23714
|
+
} else if ("customer" in data && data.customer?.customerName) {
|
|
23715
|
+
baseName = sanitizeFilename(data.customer.customerName);
|
|
23716
|
+
} else if ("groupName" in data) {
|
|
23717
|
+
baseName = sanitizeFilename(data.groupName);
|
|
23718
|
+
}
|
|
23719
|
+
return `${baseName}-${domainLabel}-${timestamp}.${ext}`;
|
|
23720
|
+
}
|
|
23721
|
+
function normalizeTimestamp(ts) {
|
|
23722
|
+
if (ts instanceof Date) return ts;
|
|
23723
|
+
if (typeof ts === "number") return new Date(ts);
|
|
23724
|
+
return new Date(ts);
|
|
23725
|
+
}
|
|
23726
|
+
function calculateStats2(dataPoints) {
|
|
23727
|
+
if (dataPoints.length === 0) {
|
|
23728
|
+
return { min: 0, max: 0, average: 0, sum: 0, count: 0 };
|
|
23729
|
+
}
|
|
23730
|
+
const values = dataPoints.map((d) => d.value);
|
|
23731
|
+
const sum = values.reduce((a, b) => a + b, 0);
|
|
23732
|
+
return {
|
|
23733
|
+
min: Math.min(...values),
|
|
23734
|
+
max: Math.max(...values),
|
|
23735
|
+
average: sum / values.length,
|
|
23736
|
+
sum,
|
|
23737
|
+
count: values.length
|
|
23738
|
+
};
|
|
23739
|
+
}
|
|
23740
|
+
function formatNumber2(value, locale, decimals = 2) {
|
|
23741
|
+
return new Intl.NumberFormat(locale, {
|
|
23742
|
+
minimumFractionDigits: decimals,
|
|
23743
|
+
maximumFractionDigits: decimals
|
|
23744
|
+
}).format(value);
|
|
23745
|
+
}
|
|
23746
|
+
function formatDate3(date, locale) {
|
|
23747
|
+
return new Intl.DateTimeFormat(locale, {
|
|
23748
|
+
year: "numeric",
|
|
23749
|
+
month: "2-digit",
|
|
23750
|
+
day: "2-digit",
|
|
23751
|
+
hour: "2-digit",
|
|
23752
|
+
minute: "2-digit"
|
|
23753
|
+
}).format(date);
|
|
23754
|
+
}
|
|
23755
|
+
function generateCSV(data, config) {
|
|
23756
|
+
const sep = CSV_SEPARATORS[config.locale] || CSV_SEPARATORS["default"];
|
|
23757
|
+
const rows = [];
|
|
23758
|
+
const escapeCSV = (val) => {
|
|
23759
|
+
const str = String(val ?? "");
|
|
23760
|
+
if (str.includes(sep) || str.includes('"') || str.includes("\n")) {
|
|
23761
|
+
return `"${str.replace(/"/g, '""')}"`;
|
|
23762
|
+
}
|
|
23763
|
+
return str;
|
|
23764
|
+
};
|
|
23765
|
+
const formatNumCSV = (val) => {
|
|
23766
|
+
return formatNumber2(val, config.locale);
|
|
23767
|
+
};
|
|
23768
|
+
if ("device" in data && "data" in data && Array.isArray(data.data)) {
|
|
23769
|
+
const deviceData = data;
|
|
23770
|
+
rows.push(["Timestamp", config.domainLabel, `Unit (${config.domainUnit})`]);
|
|
23771
|
+
for (const point of deviceData.data) {
|
|
23772
|
+
const ts = normalizeTimestamp(point.timestamp);
|
|
23773
|
+
rows.push([
|
|
23774
|
+
formatDate3(ts, config.locale),
|
|
23775
|
+
formatNumCSV(point.value),
|
|
23776
|
+
point.unit || config.domainUnit
|
|
23777
|
+
]);
|
|
23778
|
+
}
|
|
23779
|
+
if (config.includeStats) {
|
|
23780
|
+
const stats = calculateStats2(deviceData.data);
|
|
23781
|
+
rows.push([]);
|
|
23782
|
+
rows.push(["Statistics", "", ""]);
|
|
23783
|
+
rows.push(["Minimum", formatNumCSV(stats.min), config.domainUnit]);
|
|
23784
|
+
rows.push(["Maximum", formatNumCSV(stats.max), config.domainUnit]);
|
|
23785
|
+
rows.push(["Average", formatNumCSV(stats.average), config.domainUnit]);
|
|
23786
|
+
rows.push(["Total", formatNumCSV(stats.sum), config.domainUnit]);
|
|
23787
|
+
rows.push(["Count", String(stats.count), "points"]);
|
|
23788
|
+
}
|
|
23789
|
+
} else if ("devices" in data && Array.isArray(data.devices)) {
|
|
23790
|
+
const compData = data;
|
|
23791
|
+
const deviceHeaders = compData.devices.map(
|
|
23792
|
+
(d) => d.device.label || d.device.name || "Device"
|
|
23793
|
+
);
|
|
23794
|
+
rows.push(["Timestamp", ...deviceHeaders]);
|
|
23795
|
+
const allTimestamps = /* @__PURE__ */ new Set();
|
|
23796
|
+
compData.devices.forEach((d) => {
|
|
23797
|
+
d.data.forEach((point) => {
|
|
23798
|
+
allTimestamps.add(normalizeTimestamp(point.timestamp).getTime());
|
|
23799
|
+
});
|
|
23800
|
+
});
|
|
23801
|
+
const sortedTimestamps = Array.from(allTimestamps).sort((a, b) => a - b);
|
|
23802
|
+
for (const ts of sortedTimestamps) {
|
|
23803
|
+
const row = [formatDate3(new Date(ts), config.locale)];
|
|
23804
|
+
for (const device of compData.devices) {
|
|
23805
|
+
const point = device.data.find(
|
|
23806
|
+
(p) => normalizeTimestamp(p.timestamp).getTime() === ts
|
|
23807
|
+
);
|
|
23808
|
+
row.push(point ? formatNumCSV(point.value) : "");
|
|
23809
|
+
}
|
|
23810
|
+
rows.push(row);
|
|
23811
|
+
}
|
|
23812
|
+
}
|
|
23813
|
+
return rows.map((row) => row.map(escapeCSV).join(sep)).join("\r\n");
|
|
23814
|
+
}
|
|
23815
|
+
function generateXLSX(data, config) {
|
|
23816
|
+
return generateCSV(data, config);
|
|
23817
|
+
}
|
|
23818
|
+
function generatePDFContent(data, config) {
|
|
23819
|
+
const { colors, domainIcon, domainLabel, domainUnit, locale, includeStats, includeChart } = config;
|
|
23820
|
+
let deviceLabel = "Export";
|
|
23821
|
+
let customerName = "";
|
|
23822
|
+
let identifier = "";
|
|
23823
|
+
let dataPoints = [];
|
|
23824
|
+
if ("device" in data && "data" in data) {
|
|
23825
|
+
const deviceData = data;
|
|
23826
|
+
deviceLabel = deviceData.device.label || deviceData.device.name || "Device";
|
|
23827
|
+
identifier = deviceData.device.identifier || "";
|
|
23828
|
+
customerName = deviceData.customer?.customerName || "";
|
|
23829
|
+
dataPoints = deviceData.data;
|
|
23830
|
+
}
|
|
23831
|
+
const stats = calculateStats2(dataPoints);
|
|
23832
|
+
const tableRows = dataPoints.slice(0, 100).map((point) => {
|
|
23833
|
+
const ts = normalizeTimestamp(point.timestamp);
|
|
23834
|
+
return `
|
|
23835
|
+
<tr>
|
|
23836
|
+
<td style="padding: 8px; border-bottom: 1px solid #eee;">${formatDate3(ts, locale)}</td>
|
|
23837
|
+
<td style="padding: 8px; border-bottom: 1px solid #eee; text-align: right;">${formatNumber2(point.value, locale)}</td>
|
|
23838
|
+
<td style="padding: 8px; border-bottom: 1px solid #eee;">${point.unit || domainUnit}</td>
|
|
23839
|
+
</tr>
|
|
23840
|
+
`;
|
|
23841
|
+
}).join("");
|
|
23842
|
+
const statsSection = includeStats ? `
|
|
23843
|
+
<div style="margin-top: 24px; padding: 16px; background: #f5f5f5; border-radius: 8px;">
|
|
23844
|
+
<h3 style="margin: 0 0 12px 0; color: ${colors.primary};">Statistics</h3>
|
|
23845
|
+
<table style="width: 100%;">
|
|
23846
|
+
<tr>
|
|
23847
|
+
<td><strong>Minimum:</strong></td>
|
|
23848
|
+
<td>${formatNumber2(stats.min, locale)} ${domainUnit}</td>
|
|
23849
|
+
<td><strong>Maximum:</strong></td>
|
|
23850
|
+
<td>${formatNumber2(stats.max, locale)} ${domainUnit}</td>
|
|
23851
|
+
</tr>
|
|
23852
|
+
<tr>
|
|
23853
|
+
<td><strong>Average:</strong></td>
|
|
23854
|
+
<td>${formatNumber2(stats.average, locale)} ${domainUnit}</td>
|
|
23855
|
+
<td><strong>Total:</strong></td>
|
|
23856
|
+
<td>${formatNumber2(stats.sum, locale)} ${domainUnit}</td>
|
|
23857
|
+
</tr>
|
|
23858
|
+
</table>
|
|
23859
|
+
</div>
|
|
23860
|
+
` : "";
|
|
23861
|
+
return `
|
|
23862
|
+
<!DOCTYPE html>
|
|
23863
|
+
<html>
|
|
23864
|
+
<head>
|
|
23865
|
+
<meta charset="UTF-8">
|
|
23866
|
+
<title>${deviceLabel} - ${domainLabel} Report</title>
|
|
23867
|
+
<style>
|
|
23868
|
+
body {
|
|
23869
|
+
font-family: 'Roboto', Arial, sans-serif;
|
|
23870
|
+
margin: 0;
|
|
23871
|
+
padding: 24px;
|
|
23872
|
+
color: ${colors.text};
|
|
23873
|
+
background: ${colors.background};
|
|
23874
|
+
}
|
|
23875
|
+
.header {
|
|
23876
|
+
background: ${colors.primary};
|
|
23877
|
+
color: white;
|
|
23878
|
+
padding: 20px;
|
|
23879
|
+
border-radius: 8px;
|
|
23880
|
+
margin-bottom: 24px;
|
|
23881
|
+
}
|
|
23882
|
+
.header h1 {
|
|
23883
|
+
margin: 0;
|
|
23884
|
+
font-size: 24px;
|
|
23885
|
+
}
|
|
23886
|
+
.header .subtitle {
|
|
23887
|
+
opacity: 0.9;
|
|
23888
|
+
margin-top: 8px;
|
|
23889
|
+
}
|
|
23890
|
+
.device-info {
|
|
23891
|
+
display: flex;
|
|
23892
|
+
gap: 16px;
|
|
23893
|
+
margin-bottom: 16px;
|
|
23894
|
+
padding: 12px;
|
|
23895
|
+
background: #f5f5f5;
|
|
23896
|
+
border-radius: 8px;
|
|
23897
|
+
}
|
|
23898
|
+
.device-info span {
|
|
23899
|
+
padding: 4px 12px;
|
|
23900
|
+
background: ${colors.secondary};
|
|
23901
|
+
color: white;
|
|
23902
|
+
border-radius: 4px;
|
|
23903
|
+
font-size: 14px;
|
|
23904
|
+
}
|
|
23905
|
+
table {
|
|
23906
|
+
width: 100%;
|
|
23907
|
+
border-collapse: collapse;
|
|
23908
|
+
}
|
|
23909
|
+
th {
|
|
23910
|
+
background: ${colors.primary};
|
|
23911
|
+
color: white;
|
|
23912
|
+
padding: 12px 8px;
|
|
23913
|
+
text-align: left;
|
|
23914
|
+
}
|
|
23915
|
+
th:nth-child(2) {
|
|
23916
|
+
text-align: right;
|
|
23917
|
+
}
|
|
23918
|
+
.footer {
|
|
23919
|
+
margin-top: 24px;
|
|
23920
|
+
padding-top: 16px;
|
|
23921
|
+
border-top: 1px solid #eee;
|
|
23922
|
+
text-align: center;
|
|
23923
|
+
font-size: 12px;
|
|
23924
|
+
color: #999;
|
|
23925
|
+
}
|
|
23926
|
+
@media print {
|
|
23927
|
+
body { padding: 0; }
|
|
23928
|
+
.header { border-radius: 0; }
|
|
23929
|
+
}
|
|
23930
|
+
</style>
|
|
23931
|
+
</head>
|
|
23932
|
+
<body>
|
|
23933
|
+
<div class="header">
|
|
23934
|
+
<h1>${domainIcon} ${deviceLabel}</h1>
|
|
23935
|
+
<div class="subtitle">${domainLabel} Report - Generated ${formatDate3(/* @__PURE__ */ new Date(), locale)}</div>
|
|
23936
|
+
</div>
|
|
23937
|
+
|
|
23938
|
+
${customerName ? `<div class="customer-name" style="margin-bottom: 16px; font-size: 18px;"><strong>Customer:</strong> ${customerName}</div>` : ""}
|
|
23939
|
+
|
|
23940
|
+
${identifier ? `
|
|
23941
|
+
<div class="device-info">
|
|
23942
|
+
<span>ID: ${identifier}</span>
|
|
23943
|
+
<span>Domain: ${domainLabel}</span>
|
|
23944
|
+
<span>Unit: ${domainUnit}</span>
|
|
23945
|
+
</div>
|
|
23946
|
+
` : ""}
|
|
23947
|
+
|
|
23948
|
+
<table>
|
|
23949
|
+
<thead>
|
|
23950
|
+
<tr>
|
|
23951
|
+
<th>Timestamp</th>
|
|
23952
|
+
<th>${domainLabel} (${domainUnit})</th>
|
|
23953
|
+
<th>Unit</th>
|
|
23954
|
+
</tr>
|
|
23955
|
+
</thead>
|
|
23956
|
+
<tbody>
|
|
23957
|
+
${tableRows}
|
|
23958
|
+
${dataPoints.length > 100 ? `<tr><td colspan="3" style="text-align: center; padding: 16px; color: #999;">... and ${dataPoints.length - 100} more rows</td></tr>` : ""}
|
|
23959
|
+
</tbody>
|
|
23960
|
+
</table>
|
|
23961
|
+
|
|
23962
|
+
${statsSection}
|
|
23963
|
+
|
|
23964
|
+
<div class="footer">
|
|
23965
|
+
<p>${config.footerText || "Generated by MyIO Platform"}</p>
|
|
23966
|
+
</div>
|
|
23967
|
+
</body>
|
|
23968
|
+
</html>
|
|
23969
|
+
`;
|
|
23970
|
+
}
|
|
23971
|
+
function buildTemplateExport(params) {
|
|
23972
|
+
const {
|
|
23973
|
+
domain,
|
|
23974
|
+
formatExport,
|
|
23975
|
+
typeExport,
|
|
23976
|
+
colorsPallet,
|
|
23977
|
+
locale = "pt-BR",
|
|
23978
|
+
includeChart = formatExport === "pdf",
|
|
23979
|
+
includeStats = true,
|
|
23980
|
+
headerText,
|
|
23981
|
+
footerText
|
|
23982
|
+
} = params;
|
|
23983
|
+
const colors = {
|
|
23984
|
+
...DEFAULT_COLORS3,
|
|
23985
|
+
...colorsPallet,
|
|
23986
|
+
chartColors: colorsPallet?.chartColors || DEFAULT_COLORS3.chartColors
|
|
23987
|
+
};
|
|
23988
|
+
return {
|
|
23989
|
+
domain,
|
|
23990
|
+
formatExport,
|
|
23991
|
+
typeExport,
|
|
23992
|
+
colors,
|
|
23993
|
+
locale,
|
|
23994
|
+
includeChart,
|
|
23995
|
+
includeStats,
|
|
23996
|
+
headerText: headerText || `${DOMAIN_LABELS[domain]} Report`,
|
|
23997
|
+
footerText: footerText || "Generated by MyIO Platform",
|
|
23998
|
+
domainIcon: DOMAIN_ICONS[domain],
|
|
23999
|
+
domainLabel: DOMAIN_LABELS[domain],
|
|
24000
|
+
domainUnit: DOMAIN_UNITS[domain]
|
|
24001
|
+
};
|
|
24002
|
+
}
|
|
24003
|
+
function myioExportData(data, config, options) {
|
|
24004
|
+
const filename = generateFilename(data, config);
|
|
24005
|
+
let allDataPoints = [];
|
|
24006
|
+
if ("data" in data && Array.isArray(data.data)) {
|
|
24007
|
+
allDataPoints = data.data;
|
|
24008
|
+
} else if ("devices" in data && Array.isArray(data.devices)) {
|
|
24009
|
+
allDataPoints = data.devices.flatMap((d) => d.data);
|
|
24010
|
+
}
|
|
24011
|
+
const stats = calculateStats2(allDataPoints);
|
|
24012
|
+
const instance = {
|
|
24013
|
+
async export() {
|
|
24014
|
+
try {
|
|
24015
|
+
options?.onProgress?.(10, "Generating content...");
|
|
24016
|
+
let content;
|
|
24017
|
+
let mimeType;
|
|
24018
|
+
let finalFilename = filename;
|
|
24019
|
+
switch (config.formatExport) {
|
|
24020
|
+
case "csv":
|
|
24021
|
+
content = generateCSV(data, config);
|
|
24022
|
+
mimeType = "text/csv;charset=utf-8;";
|
|
24023
|
+
break;
|
|
24024
|
+
case "xlsx":
|
|
24025
|
+
content = generateXLSX(data, config);
|
|
24026
|
+
mimeType = "text/csv;charset=utf-8;";
|
|
24027
|
+
finalFilename = filename.replace(".xlsx", ".csv");
|
|
24028
|
+
break;
|
|
24029
|
+
case "pdf":
|
|
24030
|
+
content = generatePDFContent(data, config);
|
|
24031
|
+
mimeType = "text/html;charset=utf-8;";
|
|
24032
|
+
finalFilename = filename.replace(".pdf", ".html");
|
|
24033
|
+
break;
|
|
24034
|
+
default:
|
|
24035
|
+
throw new Error(`Unsupported format: ${config.formatExport}`);
|
|
24036
|
+
}
|
|
24037
|
+
options?.onProgress?.(80, "Creating file...");
|
|
24038
|
+
const bom = config.formatExport === "csv" ? "\uFEFF" : "";
|
|
24039
|
+
const blob = new Blob([bom + content], { type: mimeType });
|
|
24040
|
+
options?.onProgress?.(100, "Export complete");
|
|
24041
|
+
return {
|
|
24042
|
+
success: true,
|
|
24043
|
+
filename: finalFilename,
|
|
24044
|
+
blob,
|
|
24045
|
+
dataUrl: URL.createObjectURL(blob)
|
|
24046
|
+
};
|
|
24047
|
+
} catch (error) {
|
|
24048
|
+
return {
|
|
24049
|
+
success: false,
|
|
24050
|
+
filename,
|
|
24051
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
24052
|
+
};
|
|
24053
|
+
}
|
|
24054
|
+
},
|
|
24055
|
+
async download() {
|
|
24056
|
+
const result = await this.export();
|
|
24057
|
+
if (!result.success || !result.blob) {
|
|
24058
|
+
console.error("Export failed:", result.error);
|
|
24059
|
+
return;
|
|
24060
|
+
}
|
|
24061
|
+
const link = document.createElement("a");
|
|
24062
|
+
link.href = URL.createObjectURL(result.blob);
|
|
24063
|
+
link.download = result.filename;
|
|
24064
|
+
link.style.display = "none";
|
|
24065
|
+
document.body.appendChild(link);
|
|
24066
|
+
link.click();
|
|
24067
|
+
document.body.removeChild(link);
|
|
24068
|
+
URL.revokeObjectURL(link.href);
|
|
24069
|
+
},
|
|
24070
|
+
async preview() {
|
|
24071
|
+
if (config.formatExport !== "pdf") {
|
|
24072
|
+
return null;
|
|
24073
|
+
}
|
|
24074
|
+
const result = await this.export();
|
|
24075
|
+
return result.dataUrl || null;
|
|
24076
|
+
},
|
|
24077
|
+
getStats() {
|
|
24078
|
+
return stats;
|
|
24079
|
+
},
|
|
24080
|
+
getFilename() {
|
|
24081
|
+
return filename;
|
|
24082
|
+
}
|
|
24083
|
+
};
|
|
24084
|
+
if (options?.autoDownload) {
|
|
24085
|
+
instance.download();
|
|
24086
|
+
}
|
|
24087
|
+
return instance;
|
|
24088
|
+
}
|
|
24089
|
+
var EXPORT_DEFAULT_COLORS = DEFAULT_COLORS3;
|
|
24090
|
+
var EXPORT_DOMAIN_ICONS = DOMAIN_ICONS;
|
|
24091
|
+
var EXPORT_DOMAIN_LABELS = DOMAIN_LABELS;
|
|
24092
|
+
var EXPORT_DOMAIN_UNITS = DOMAIN_UNITS;
|
|
24093
|
+
|
|
22178
24094
|
exports.CHART_COLORS = CHART_COLORS;
|
|
24095
|
+
exports.CONSUMPTION_CHART_COLORS = DEFAULT_COLORS;
|
|
24096
|
+
exports.CONSUMPTION_CHART_DEFAULTS = DEFAULT_CONFIG;
|
|
24097
|
+
exports.CONSUMPTION_THEME_COLORS = THEME_COLORS;
|
|
22179
24098
|
exports.ConnectionStatusType = ConnectionStatusType;
|
|
22180
24099
|
exports.DEFAULT_CLAMP_RANGE = DEFAULT_CLAMP_RANGE;
|
|
22181
24100
|
exports.DeviceStatusType = DeviceStatusType;
|
|
24101
|
+
exports.EXPORT_DEFAULT_COLORS = EXPORT_DEFAULT_COLORS;
|
|
24102
|
+
exports.EXPORT_DOMAIN_ICONS = EXPORT_DOMAIN_ICONS;
|
|
24103
|
+
exports.EXPORT_DOMAIN_LABELS = EXPORT_DOMAIN_LABELS;
|
|
24104
|
+
exports.EXPORT_DOMAIN_UNITS = EXPORT_DOMAIN_UNITS;
|
|
22182
24105
|
exports.MyIOChartModal = MyIOChartModal;
|
|
22183
24106
|
exports.MyIODraggableCard = MyIODraggableCard;
|
|
22184
24107
|
exports.MyIOSelectionStoreClass = MyIOSelectionStoreClass;
|
|
@@ -22189,11 +24112,13 @@ ${rangeText}`;
|
|
|
22189
24112
|
exports.averageByDay = averageByDay;
|
|
22190
24113
|
exports.buildListItemsThingsboardByUniqueDatasource = buildListItemsThingsboardByUniqueDatasource;
|
|
22191
24114
|
exports.buildMyioIngestionAuth = buildMyioIngestionAuth;
|
|
24115
|
+
exports.buildTemplateExport = buildTemplateExport;
|
|
22192
24116
|
exports.buildWaterReportCSV = buildWaterReportCSV;
|
|
22193
24117
|
exports.buildWaterStoresCSV = buildWaterStoresCSV;
|
|
22194
24118
|
exports.calcDeltaPercent = calcDeltaPercent;
|
|
22195
24119
|
exports.calculateDeviceStatus = calculateDeviceStatus;
|
|
22196
24120
|
exports.calculateDeviceStatusWithRanges = calculateDeviceStatusWithRanges;
|
|
24121
|
+
exports.calculateExportStats = calculateStats2;
|
|
22197
24122
|
exports.calculateStats = calculateStats;
|
|
22198
24123
|
exports.clampTemperature = clampTemperature;
|
|
22199
24124
|
exports.classify = classify;
|
|
@@ -22201,8 +24126,11 @@ ${rangeText}`;
|
|
|
22201
24126
|
exports.classifyWaterLabels = classifyWaterLabels;
|
|
22202
24127
|
exports.clearAllAuthCaches = clearAllAuthCaches;
|
|
22203
24128
|
exports.connectionStatusIcons = connectionStatusIcons;
|
|
24129
|
+
exports.createConsumption7DaysChart = createConsumption7DaysChart;
|
|
24130
|
+
exports.createConsumptionModal = createConsumptionModal;
|
|
22204
24131
|
exports.createDateRangePicker = createDateRangePicker2;
|
|
22205
24132
|
exports.createInputDateRangePickerInsideDIV = createInputDateRangePickerInsideDIV;
|
|
24133
|
+
exports.createModalHeader = createModalHeader;
|
|
22206
24134
|
exports.decodePayload = decodePayload;
|
|
22207
24135
|
exports.decodePayloadBase64Xor = decodePayloadBase64Xor;
|
|
22208
24136
|
exports.detectDeviceType = detectDeviceType;
|
|
@@ -22216,6 +24144,7 @@ ${rangeText}`;
|
|
|
22216
24144
|
exports.fetchThingsboardCustomerAttrsFromStorage = fetchThingsboardCustomerAttrsFromStorage;
|
|
22217
24145
|
exports.fetchThingsboardCustomerServerScopeAttrs = fetchThingsboardCustomerServerScopeAttrs;
|
|
22218
24146
|
exports.findValue = findValue;
|
|
24147
|
+
exports.findValueWithDefault = findValueWithDefault;
|
|
22219
24148
|
exports.fmtPerc = fmtPerc;
|
|
22220
24149
|
exports.fmtPercLegacy = fmtPerc2;
|
|
22221
24150
|
exports.formatAllInSameUnit = formatAllInSameUnit;
|
|
@@ -22223,18 +24152,24 @@ ${rangeText}`;
|
|
|
22223
24152
|
exports.formatDateForInput = formatDateForInput;
|
|
22224
24153
|
exports.formatDateToYMD = formatDateToYMD;
|
|
22225
24154
|
exports.formatDateWithTimezoneOffset = formatDateWithTimezoneOffset;
|
|
24155
|
+
exports.formatDuration = formatDuration;
|
|
22226
24156
|
exports.formatEnergy = formatEnergy;
|
|
22227
24157
|
exports.formatNumberReadable = formatNumberReadable;
|
|
24158
|
+
exports.formatRelativeTime = formatRelativeTime;
|
|
22228
24159
|
exports.formatTankHeadFromCm = formatTankHeadFromCm;
|
|
22229
24160
|
exports.formatTemperature = formatTemperature2;
|
|
24161
|
+
exports.formatWater = formatWater;
|
|
22230
24162
|
exports.formatWaterByGroup = formatWaterByGroup;
|
|
22231
24163
|
exports.formatWaterVolumeM3 = formatWaterVolumeM3;
|
|
24164
|
+
exports.formatarDuracao = formatarDuracao;
|
|
24165
|
+
exports.generateExportFilename = generateFilename;
|
|
22232
24166
|
exports.getAuthCacheStats = getAuthCacheStats;
|
|
22233
24167
|
exports.getAvailableContexts = getAvailableContexts;
|
|
22234
24168
|
exports.getConnectionStatusIcon = getConnectionStatusIcon;
|
|
22235
24169
|
exports.getDateRangeArray = getDateRangeArray;
|
|
22236
24170
|
exports.getDeviceStatusIcon = getDeviceStatusIcon;
|
|
22237
24171
|
exports.getDeviceStatusInfo = getDeviceStatusInfo;
|
|
24172
|
+
exports.getModalHeaderStyles = getModalHeaderStyles;
|
|
22238
24173
|
exports.getSaoPauloISOString = getSaoPauloISOString;
|
|
22239
24174
|
exports.getSaoPauloISOStringFixed = getSaoPauloISOStringFixed;
|
|
22240
24175
|
exports.getValueByDatakey = getValueByDatakey;
|
|
@@ -22246,8 +24181,10 @@ ${rangeText}`;
|
|
|
22246
24181
|
exports.isValidConnectionStatus = isValidConnectionStatus;
|
|
22247
24182
|
exports.isValidDeviceStatus = isValidDeviceStatus;
|
|
22248
24183
|
exports.isWaterCategory = isWaterCategory;
|
|
24184
|
+
exports.mapConnectionStatus = mapConnectionStatus;
|
|
22249
24185
|
exports.mapDeviceStatusToCardStatus = mapDeviceStatusToCardStatus;
|
|
22250
24186
|
exports.mapDeviceToConnectionStatus = mapDeviceToConnectionStatus;
|
|
24187
|
+
exports.myioExportData = myioExportData;
|
|
22251
24188
|
exports.normalizeRecipients = normalizeRecipients;
|
|
22252
24189
|
exports.numbers = numbers_exports;
|
|
22253
24190
|
exports.openDashboardPopup = openDashboardPopup;
|