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
package/dist/index.js
CHANGED
|
@@ -644,6 +644,10 @@ function formatNumberReadable(value, locale = "pt-BR", minimumFractionDigits = 2
|
|
|
644
644
|
}
|
|
645
645
|
|
|
646
646
|
// src/format/water.ts
|
|
647
|
+
function formatWater(value) {
|
|
648
|
+
const num = Number(value) || 0;
|
|
649
|
+
return `${num.toFixed(2)} m\xB3`;
|
|
650
|
+
}
|
|
647
651
|
function formatWaterVolumeM3(value, locale = "pt-BR") {
|
|
648
652
|
if (value === null || value === void 0 || isNaN(value)) {
|
|
649
653
|
return "-";
|
|
@@ -721,6 +725,76 @@ function formatAllInSameWaterUnit(values) {
|
|
|
721
725
|
};
|
|
722
726
|
}
|
|
723
727
|
|
|
728
|
+
// src/format/time.ts
|
|
729
|
+
function formatRelativeTime(timestamp) {
|
|
730
|
+
if (!timestamp || timestamp <= 0) {
|
|
731
|
+
return "\u2014";
|
|
732
|
+
}
|
|
733
|
+
const now = Date.now();
|
|
734
|
+
const diffSeconds = Math.round((now - timestamp) / 1e3);
|
|
735
|
+
if (diffSeconds < 10) {
|
|
736
|
+
return "agora";
|
|
737
|
+
}
|
|
738
|
+
if (diffSeconds < 60) {
|
|
739
|
+
return `h\xE1 ${diffSeconds}s`;
|
|
740
|
+
}
|
|
741
|
+
const diffMinutes = Math.round(diffSeconds / 60);
|
|
742
|
+
if (diffMinutes === 1) {
|
|
743
|
+
return "h\xE1 1 min";
|
|
744
|
+
}
|
|
745
|
+
if (diffMinutes < 60) {
|
|
746
|
+
return `h\xE1 ${diffMinutes} mins`;
|
|
747
|
+
}
|
|
748
|
+
const diffHours = Math.round(diffMinutes / 60);
|
|
749
|
+
if (diffHours === 1) {
|
|
750
|
+
return "h\xE1 1 hora";
|
|
751
|
+
}
|
|
752
|
+
if (diffHours < 24) {
|
|
753
|
+
return `h\xE1 ${diffHours} horas`;
|
|
754
|
+
}
|
|
755
|
+
const diffDays = Math.round(diffHours / 24);
|
|
756
|
+
if (diffDays === 1) {
|
|
757
|
+
return "ontem";
|
|
758
|
+
}
|
|
759
|
+
if (diffDays <= 30) {
|
|
760
|
+
return `h\xE1 ${diffDays} dias`;
|
|
761
|
+
}
|
|
762
|
+
return new Date(timestamp).toLocaleDateString("pt-BR");
|
|
763
|
+
}
|
|
764
|
+
function formatarDuracao(ms) {
|
|
765
|
+
if (typeof ms !== "number" || ms < 0 || !isFinite(ms)) {
|
|
766
|
+
return "0s";
|
|
767
|
+
}
|
|
768
|
+
if (ms === 0) {
|
|
769
|
+
return "0s";
|
|
770
|
+
}
|
|
771
|
+
const segundos = Math.floor(ms / 1e3 % 60);
|
|
772
|
+
const minutos = Math.floor(ms / (1e3 * 60) % 60);
|
|
773
|
+
const horas = Math.floor(ms / (1e3 * 60 * 60) % 24);
|
|
774
|
+
const dias = Math.floor(ms / (1e3 * 60 * 60 * 24));
|
|
775
|
+
const parts = [];
|
|
776
|
+
if (dias > 0) {
|
|
777
|
+
parts.push(`${dias}d`);
|
|
778
|
+
if (horas > 0) {
|
|
779
|
+
parts.push(`${horas}h`);
|
|
780
|
+
}
|
|
781
|
+
} else if (horas > 0) {
|
|
782
|
+
parts.push(`${horas}h`);
|
|
783
|
+
if (minutos > 0) {
|
|
784
|
+
parts.push(`${minutos}m`);
|
|
785
|
+
}
|
|
786
|
+
} else if (minutos > 0) {
|
|
787
|
+
parts.push(`${minutos}m`);
|
|
788
|
+
if (segundos > 0) {
|
|
789
|
+
parts.push(`${segundos}s`);
|
|
790
|
+
}
|
|
791
|
+
} else {
|
|
792
|
+
parts.push(`${segundos}s`);
|
|
793
|
+
}
|
|
794
|
+
return parts.length > 0 ? parts.join(" ") : "0s";
|
|
795
|
+
}
|
|
796
|
+
var formatDuration = formatarDuracao;
|
|
797
|
+
|
|
724
798
|
// src/date/ymd.ts
|
|
725
799
|
function formatDateToYMD(date) {
|
|
726
800
|
if (!date) {
|
|
@@ -1229,6 +1303,11 @@ function findValue(data, keyOrPath, legacyDataKey) {
|
|
|
1229
1303
|
}
|
|
1230
1304
|
return getValueByDatakey(data, keyOrPath);
|
|
1231
1305
|
}
|
|
1306
|
+
function findValueWithDefault(values, key, defaultValue = null) {
|
|
1307
|
+
if (!Array.isArray(values)) return defaultValue;
|
|
1308
|
+
const found = values.find((v) => v.key === key || v.dataType === key);
|
|
1309
|
+
return found ? found.value : defaultValue;
|
|
1310
|
+
}
|
|
1232
1311
|
|
|
1233
1312
|
// src/utils/deviceStatus.js
|
|
1234
1313
|
var DeviceStatusType = {
|
|
@@ -1285,6 +1364,16 @@ function mapDeviceToConnectionStatus(deviceStatus) {
|
|
|
1285
1364
|
}
|
|
1286
1365
|
return ConnectionStatusType.CONNECTED;
|
|
1287
1366
|
}
|
|
1367
|
+
function mapConnectionStatus(rawStatus) {
|
|
1368
|
+
const statusLower = String(rawStatus || "").toLowerCase().trim();
|
|
1369
|
+
if (statusLower === "online" || statusLower === "ok" || statusLower === "running") {
|
|
1370
|
+
return "online";
|
|
1371
|
+
}
|
|
1372
|
+
if (statusLower === "waiting" || statusLower === "connecting" || statusLower === "pending") {
|
|
1373
|
+
return "waiting";
|
|
1374
|
+
}
|
|
1375
|
+
return "offline";
|
|
1376
|
+
}
|
|
1288
1377
|
function mapDeviceStatusToCardStatus(deviceStatus) {
|
|
1289
1378
|
const statusMap = {
|
|
1290
1379
|
[DeviceStatusType.POWER_ON]: "ok",
|
|
@@ -2053,8 +2142,9 @@ var MyIOSelectionStoreClass = class _MyIOSelectionStoreClass {
|
|
|
2053
2142
|
lastValue: Number(entity.lastValue) || 0,
|
|
2054
2143
|
unit: entity.unit || "",
|
|
2055
2144
|
status: entity.status || "unknown",
|
|
2056
|
-
ingestionId: entity.ingestionId || entity.id
|
|
2145
|
+
ingestionId: entity.ingestionId || entity.id,
|
|
2057
2146
|
// ⭐ ADD: Store ingestionId for API calls
|
|
2147
|
+
customerName: entity.customerName || ""
|
|
2058
2148
|
};
|
|
2059
2149
|
this.entities.set(entity.id, normalizedEntity);
|
|
2060
2150
|
}
|
|
@@ -4682,6 +4772,13 @@ var Icons = {
|
|
|
4682
4772
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
|
|
4683
4773
|
viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">
|
|
4684
4774
|
<circle cx="12" cy="12" r="5"/>
|
|
4775
|
+
</svg>`,
|
|
4776
|
+
waterDrop: `
|
|
4777
|
+
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
4778
|
+
<g id="water" transform="translate(-4 -2)">
|
|
4779
|
+
<path id="secondary" fill="#2ca9bc" d="M19,14A7,7,0,1,1,5,14C5,8,12,3,12,3S19,8,19,14Z"/>
|
|
4780
|
+
<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"/>
|
|
4781
|
+
</g>
|
|
4685
4782
|
</svg>`,
|
|
4686
4783
|
// ⋮ Kebab (for actions menu)
|
|
4687
4784
|
kebab: `
|
|
@@ -4710,6 +4807,7 @@ var ICON_MAP = {
|
|
|
4710
4807
|
var DEFAULT_I18N = {
|
|
4711
4808
|
// Status labels with icons
|
|
4712
4809
|
in_operation: "\u26A1 Normal",
|
|
4810
|
+
in_operation_water: "\u{1F4A7} Normal",
|
|
4713
4811
|
standby: "\u{1F4A4} Em standby",
|
|
4714
4812
|
alert: "\u26A0\uFE0F Alerta",
|
|
4715
4813
|
failure: "\u{1F6A8} Falha",
|
|
@@ -4766,7 +4864,13 @@ function normalizeParams(params) {
|
|
|
4766
4864
|
}
|
|
4767
4865
|
};
|
|
4768
4866
|
}
|
|
4769
|
-
function getIconSvg(deviceType) {
|
|
4867
|
+
function getIconSvg(deviceType, domain) {
|
|
4868
|
+
if (domain === "water") {
|
|
4869
|
+
return Icons.waterDrop;
|
|
4870
|
+
}
|
|
4871
|
+
if (domain === "temperature") {
|
|
4872
|
+
return Icons.thermometer;
|
|
4873
|
+
}
|
|
4770
4874
|
return ICON_MAP[deviceType] || ICON_MAP.DEFAULT;
|
|
4771
4875
|
}
|
|
4772
4876
|
function formatPower(valueInWatts) {
|
|
@@ -4806,9 +4910,23 @@ function calculateConsumptionPercentage(target, consumption) {
|
|
|
4806
4910
|
const percentage = numericConsumption / numericTarget * 100;
|
|
4807
4911
|
return percentage;
|
|
4808
4912
|
}
|
|
4809
|
-
function getStatusInfo(deviceStatus, i18n) {
|
|
4913
|
+
function getStatusInfo(deviceStatus, i18n, domain) {
|
|
4810
4914
|
switch (deviceStatus) {
|
|
4915
|
+
// --- Novos Status de Temperatura ---
|
|
4916
|
+
case "normal":
|
|
4917
|
+
return { chipClass: "chip--ok", label: "Normal" };
|
|
4918
|
+
// Verde/Azul
|
|
4919
|
+
case "cold":
|
|
4920
|
+
return { chipClass: "chip--standby", label: "Frio" };
|
|
4921
|
+
// Azul claro/Ciano
|
|
4922
|
+
case "hot":
|
|
4923
|
+
return { chipClass: "chip--alert", label: "Quente" };
|
|
4924
|
+
// Laranja/Amarelo
|
|
4925
|
+
// --- Status Existentes ---
|
|
4811
4926
|
case DeviceStatusType.POWER_ON:
|
|
4927
|
+
if (domain === "water") {
|
|
4928
|
+
return { chipClass: "chip--ok", label: i18n.in_operation_water };
|
|
4929
|
+
}
|
|
4812
4930
|
return { chipClass: "chip--ok", label: i18n.in_operation };
|
|
4813
4931
|
case DeviceStatusType.STANDBY:
|
|
4814
4932
|
return { chipClass: "chip--standby", label: i18n.standby };
|
|
@@ -4821,6 +4939,7 @@ function getStatusInfo(deviceStatus, i18n) {
|
|
|
4821
4939
|
return { chipClass: "chip--alert", label: i18n.maintenance };
|
|
4822
4940
|
case DeviceStatusType.NOT_INSTALLED:
|
|
4823
4941
|
return { chipClass: "chip--offline", label: i18n.not_installed };
|
|
4942
|
+
// Default (Cai aqui se não achar 'normal', 'hot' etc)
|
|
4824
4943
|
case DeviceStatusType.NO_INFO:
|
|
4825
4944
|
default:
|
|
4826
4945
|
return { chipClass: "chip--offline", label: i18n.offline };
|
|
@@ -4852,6 +4971,14 @@ function getCardStateClass(deviceStatus) {
|
|
|
4852
4971
|
}
|
|
4853
4972
|
function getStatusDotClass(deviceStatus) {
|
|
4854
4973
|
switch (deviceStatus) {
|
|
4974
|
+
// --- Novos Status de Temperatura ---
|
|
4975
|
+
case "normal":
|
|
4976
|
+
return "dot--ok";
|
|
4977
|
+
case "cold":
|
|
4978
|
+
return "dot--standby";
|
|
4979
|
+
case "hot":
|
|
4980
|
+
return "dot--alert";
|
|
4981
|
+
// --- Status Existentes ---
|
|
4855
4982
|
case DeviceStatusType.POWER_ON:
|
|
4856
4983
|
return "dot--ok";
|
|
4857
4984
|
case DeviceStatusType.STANDBY:
|
|
@@ -4879,7 +5006,7 @@ function buildDOM(state) {
|
|
|
4879
5006
|
header.className = "myio-ho-card__header";
|
|
4880
5007
|
const iconContainer = document.createElement("div");
|
|
4881
5008
|
iconContainer.className = "myio-ho-card__icon";
|
|
4882
|
-
iconContainer.innerHTML = getIconSvg(entityObject.deviceType);
|
|
5009
|
+
iconContainer.innerHTML = getIconSvg(entityObject.deviceType, entityObject.domain);
|
|
4883
5010
|
header.appendChild(iconContainer);
|
|
4884
5011
|
const titleSection = document.createElement("div");
|
|
4885
5012
|
titleSection.className = "myio-ho-card__title";
|
|
@@ -5001,7 +5128,11 @@ function buildDOM(state) {
|
|
|
5001
5128
|
powerMetric.appendChild(statusDot);
|
|
5002
5129
|
const powerLabel = document.createElement("div");
|
|
5003
5130
|
powerLabel.className = "label";
|
|
5004
|
-
|
|
5131
|
+
if (entityObject.domain === "water") {
|
|
5132
|
+
powerLabel.textContent = "Leitura";
|
|
5133
|
+
} else {
|
|
5134
|
+
powerLabel.textContent = i18n.instantaneous_power || "Pot\xEAncia";
|
|
5135
|
+
}
|
|
5005
5136
|
powerMetric.appendChild(powerLabel);
|
|
5006
5137
|
const powerVal = document.createElement("div");
|
|
5007
5138
|
powerVal.className = "val";
|
|
@@ -5025,7 +5156,7 @@ function verifyOfflineStatus(entityObject, delayTimeInMins = 15) {
|
|
|
5025
5156
|
return true;
|
|
5026
5157
|
}
|
|
5027
5158
|
function paint(root, state) {
|
|
5028
|
-
const { entityObject, i18n, delayTimeConnectionInMins } = state;
|
|
5159
|
+
const { entityObject, i18n, delayTimeConnectionInMins, isSelected } = state;
|
|
5029
5160
|
if (entityObject.connectionStatus) {
|
|
5030
5161
|
if (entityObject.connectionStatus === "offline") {
|
|
5031
5162
|
entityObject.deviceStatus = DeviceStatusType.NO_INFO;
|
|
@@ -5037,16 +5168,23 @@ function paint(root, state) {
|
|
|
5037
5168
|
}
|
|
5038
5169
|
const stateClass = getCardStateClass(entityObject.deviceStatus);
|
|
5039
5170
|
root.className = `myio-ho-card ${stateClass}`;
|
|
5040
|
-
const statusInfo = getStatusInfo(entityObject.deviceStatus, i18n);
|
|
5171
|
+
const statusInfo = getStatusInfo(entityObject.deviceStatus, i18n, entityObject.domain);
|
|
5041
5172
|
const chip = root.querySelector(".chip");
|
|
5042
5173
|
chip.className = `chip ${statusInfo.chipClass}`;
|
|
5043
|
-
chip.
|
|
5174
|
+
chip.innerHTML = statusInfo.label;
|
|
5044
5175
|
const primaryValue = formatValueByDomain(entityObject.val, entityObject.domain);
|
|
5045
5176
|
const numSpan = root.querySelector(".myio-ho-card__value .num");
|
|
5046
5177
|
const unitSpan = root.querySelector(".myio-ho-card__value .unit");
|
|
5047
5178
|
numSpan.textContent = primaryValue;
|
|
5048
5179
|
const barContainer = root.querySelector(".bar");
|
|
5049
5180
|
const effContainer = root.querySelector(".myio-ho-card__eff");
|
|
5181
|
+
if (state.enableSelection) {
|
|
5182
|
+
const checkbox = root.querySelector('.myio-ho-card__select input[type="checkbox"]');
|
|
5183
|
+
if (checkbox) {
|
|
5184
|
+
checkbox.checked = !!isSelected;
|
|
5185
|
+
}
|
|
5186
|
+
root.classList.toggle("is-selected", !!isSelected);
|
|
5187
|
+
}
|
|
5050
5188
|
const targetValue = entityObject.consumptionTargetValue;
|
|
5051
5189
|
if (targetValue) {
|
|
5052
5190
|
barContainer.style.display = "";
|
|
@@ -5068,9 +5206,14 @@ function paint(root, state) {
|
|
|
5068
5206
|
}
|
|
5069
5207
|
const powerVal = root.querySelector(".myio-ho-card__footer .metric:nth-child(2) .val");
|
|
5070
5208
|
if (powerVal) {
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5209
|
+
if (entityObject.domain === "water") {
|
|
5210
|
+
const pulses = entityObject.pulses ?? 0;
|
|
5211
|
+
powerVal.textContent = `${pulses} L`;
|
|
5212
|
+
} else {
|
|
5213
|
+
const instantPower = entityObject.instantaneousPower ?? entityObject.consumption_power ?? null;
|
|
5214
|
+
const powerFormatted = formatPower(instantPower);
|
|
5215
|
+
powerVal.textContent = instantPower !== null ? `${powerFormatted.num} ${powerFormatted.unit}` : "-";
|
|
5216
|
+
}
|
|
5074
5217
|
}
|
|
5075
5218
|
const statusDot = root.querySelector(".myio-ho-card__footer .metric:nth-child(2) .status-dot");
|
|
5076
5219
|
if (statusDot) {
|
|
@@ -5130,6 +5273,26 @@ function bindEvents(root, state, callbacks) {
|
|
|
5130
5273
|
callbacks.handleActionSettings(e, entityObject);
|
|
5131
5274
|
});
|
|
5132
5275
|
}
|
|
5276
|
+
const MyIOSelectionStore2 = window.MyIOLibrary?.MyIOSelectionStore || window.MyIOSelectionStore;
|
|
5277
|
+
if (MyIOSelectionStore2) {
|
|
5278
|
+
const onSelectionChange = () => {
|
|
5279
|
+
const selectedIds = MyIOSelectionStore2.getSelectedIds();
|
|
5280
|
+
const isSelected = selectedIds.includes(entityObject.entityId);
|
|
5281
|
+
if (state.isSelected !== isSelected) {
|
|
5282
|
+
state.isSelected = isSelected;
|
|
5283
|
+
paint(root, state);
|
|
5284
|
+
}
|
|
5285
|
+
};
|
|
5286
|
+
MyIOSelectionStore2.on("selection:change", onSelectionChange);
|
|
5287
|
+
root._selectionListener = onSelectionChange;
|
|
5288
|
+
}
|
|
5289
|
+
root._cleanup = () => {
|
|
5290
|
+
document.removeEventListener("click", closeMenu);
|
|
5291
|
+
document.removeEventListener("keydown", closeMenu);
|
|
5292
|
+
if (MyIOSelectionStore2 && root._selectionListener) {
|
|
5293
|
+
MyIOSelectionStore2.off("selection:change", root._selectionListener);
|
|
5294
|
+
}
|
|
5295
|
+
};
|
|
5133
5296
|
const checkbox = root.querySelector('.myio-ho-card__select input[type="checkbox"]');
|
|
5134
5297
|
if (checkbox && callbacks.handleSelect) {
|
|
5135
5298
|
checkbox.addEventListener("change", (e) => {
|
|
@@ -5197,6 +5360,7 @@ function renderCardComponentHeadOffice(containerEl, params) {
|
|
|
5197
5360
|
ensureCss();
|
|
5198
5361
|
const state = normalizeParams(params);
|
|
5199
5362
|
const root = buildDOM(state);
|
|
5363
|
+
state.isSelected = params.isSelected || false;
|
|
5200
5364
|
containerEl.appendChild(root);
|
|
5201
5365
|
bindEvents(root, state, state.callbacks);
|
|
5202
5366
|
paint(root, state);
|
|
@@ -7623,14 +7787,14 @@ async function openRealTimeTelemetryModal(params) {
|
|
|
7623
7787
|
return `${value.toFixed(config.decimals)} ${config.unit}`;
|
|
7624
7788
|
}
|
|
7625
7789
|
function initializeChart() {
|
|
7626
|
-
const
|
|
7627
|
-
if (!
|
|
7790
|
+
const Chart2 = window.Chart;
|
|
7791
|
+
if (!Chart2) {
|
|
7628
7792
|
console.warn("[RealTimeTelemetry] Chart.js not loaded");
|
|
7629
7793
|
return;
|
|
7630
7794
|
}
|
|
7631
7795
|
chartContainer.style.display = "block";
|
|
7632
7796
|
const config = TELEMETRY_CONFIG[selectedChartKey] || { label: selectedChartKey, unit: "" };
|
|
7633
|
-
chart = new
|
|
7797
|
+
chart = new Chart2(chartCanvas, {
|
|
7634
7798
|
type: "line",
|
|
7635
7799
|
data: {
|
|
7636
7800
|
datasets: [{
|
|
@@ -10399,7 +10563,7 @@ async function openDemandModal(params) {
|
|
|
10399
10563
|
params.timezoneOffset
|
|
10400
10564
|
);
|
|
10401
10565
|
if (!newChartData.isEmpty && chart && chartData) {
|
|
10402
|
-
const
|
|
10566
|
+
const Chart2 = window.Chart;
|
|
10403
10567
|
newChartData.series.forEach((newSeries, seriesIndex) => {
|
|
10404
10568
|
if (newSeries.points.length > 0 && chart.data.datasets[seriesIndex]) {
|
|
10405
10569
|
newSeries.points.forEach((point) => {
|
|
@@ -10628,8 +10792,8 @@ async function openDemandModal(params) {
|
|
|
10628
10792
|
peakEl.textContent = `${strings.maximum}: ${peak.formattedValue} kW ${peak.key ? `(${peak.key}) ` : ""}${strings.at} ${dateStr}`;
|
|
10629
10793
|
peakEl.style.display = "block";
|
|
10630
10794
|
}
|
|
10631
|
-
const
|
|
10632
|
-
|
|
10795
|
+
const Chart2 = window.Chart;
|
|
10796
|
+
Chart2.register(window.ChartZoom);
|
|
10633
10797
|
if (chart) {
|
|
10634
10798
|
chart.data.datasets = chartData.series.map((series) => ({
|
|
10635
10799
|
label: series.label,
|
|
@@ -10681,7 +10845,7 @@ async function openDemandModal(params) {
|
|
|
10681
10845
|
};
|
|
10682
10846
|
chart.update();
|
|
10683
10847
|
} else {
|
|
10684
|
-
chart = new
|
|
10848
|
+
chart = new Chart2(chartCanvas, {
|
|
10685
10849
|
type: "line",
|
|
10686
10850
|
data: {
|
|
10687
10851
|
datasets: chartData.series.map((series) => ({
|
|
@@ -10940,11 +11104,13 @@ var EnergyModalView = class {
|
|
|
10940
11104
|
console.log("[EnergyModalView] Bar mode toggled to:", this.currentBarMode);
|
|
10941
11105
|
}
|
|
10942
11106
|
/**
|
|
10943
|
-
|
|
10944
|
-
|
|
11107
|
+
* Applies the current theme to the modal and charts (Updated with #root override)
|
|
11108
|
+
*/
|
|
10945
11109
|
applyTheme() {
|
|
10946
11110
|
const themeToggleBtn = document.getElementById("theme-toggle-btn");
|
|
10947
|
-
const modalContent =
|
|
11111
|
+
const modalContent = this.container;
|
|
11112
|
+
const externalBody = this.container?.closest(".myio-modal-body") || document.querySelector(".myio-modal-body");
|
|
11113
|
+
const rootDiv = document.querySelector("#root > div");
|
|
10948
11114
|
if (themeToggleBtn) {
|
|
10949
11115
|
const sunIcon = themeToggleBtn.querySelector(".myio-theme-icon-sun");
|
|
10950
11116
|
const moonIcon = themeToggleBtn.querySelector(".myio-theme-icon-moon");
|
|
@@ -10957,6 +11123,14 @@ var EnergyModalView = class {
|
|
|
10957
11123
|
moonIcon.style.opacity = "0";
|
|
10958
11124
|
moonIcon.style.transform = "translate(-50%, -50%) rotate(90deg) scale(0)";
|
|
10959
11125
|
}
|
|
11126
|
+
if (externalBody) {
|
|
11127
|
+
externalBody.style.backgroundColor = "#ffffff";
|
|
11128
|
+
externalBody.style.color = "#1f2937";
|
|
11129
|
+
}
|
|
11130
|
+
if (rootDiv) {
|
|
11131
|
+
rootDiv.style.backgroundColor = "#ffffff";
|
|
11132
|
+
rootDiv.style.color = "#1f2937";
|
|
11133
|
+
}
|
|
10960
11134
|
} else {
|
|
10961
11135
|
if (sunIcon) {
|
|
10962
11136
|
sunIcon.style.opacity = "0";
|
|
@@ -10966,6 +11140,14 @@ var EnergyModalView = class {
|
|
|
10966
11140
|
moonIcon.style.opacity = "1";
|
|
10967
11141
|
moonIcon.style.transform = "translate(-50%, -50%) rotate(0deg) scale(1)";
|
|
10968
11142
|
}
|
|
11143
|
+
if (externalBody) {
|
|
11144
|
+
externalBody.style.backgroundColor = "#1f1f1f";
|
|
11145
|
+
externalBody.style.color = "#f3f4f6";
|
|
11146
|
+
}
|
|
11147
|
+
if (rootDiv) {
|
|
11148
|
+
rootDiv.style.backgroundColor = "#1f1f1f";
|
|
11149
|
+
rootDiv.style.color = "#f3f4f6";
|
|
11150
|
+
}
|
|
10969
11151
|
}
|
|
10970
11152
|
}
|
|
10971
11153
|
if (modalContent) {
|
|
@@ -11194,7 +11376,7 @@ var EnergyModalView = class {
|
|
|
11194
11376
|
${this.config.params.mode === "comparison" ? `
|
|
11195
11377
|
<!-- RFC-0097: Granularity Selector (only 1h and 1d supported) -->
|
|
11196
11378
|
<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;">
|
|
11197
|
-
<span style="font-size: 11px;
|
|
11379
|
+
<span class="myio-label-secondary" style="font-size: 11px; margin-right: 4px; white-space: nowrap;">Granularidade:</span>
|
|
11198
11380
|
<button class="myio-btn myio-btn-granularity ${this.currentGranularity === "1h" ? "active" : ""}" data-granularity="1h" title="Hora">1h</button>
|
|
11199
11381
|
<button class="myio-btn myio-btn-granularity ${this.currentGranularity === "1d" ? "active" : ""}" data-granularity="1d" title="Dia">1d</button>
|
|
11200
11382
|
</div>
|
|
@@ -11876,93 +12058,159 @@ var EnergyModalView = class {
|
|
|
11876
12058
|
return i18n[key] || DEFAULT_I18N2[key];
|
|
11877
12059
|
}
|
|
11878
12060
|
/**
|
|
11879
|
-
|
|
11880
|
-
|
|
12061
|
+
* Gets modal styles with fixed Dark/Light contrast
|
|
12062
|
+
*/
|
|
12063
|
+
/**
|
|
12064
|
+
* Gets modal styles with fixed Chart Border for Light Mode
|
|
12065
|
+
*/
|
|
11881
12066
|
getModalStyles() {
|
|
11882
12067
|
const styles = this.config.params.styles || {};
|
|
12068
|
+
const defaultPrimary = styles.primaryColor || "#4A148C";
|
|
12069
|
+
const defaultFont = styles.fontFamily || '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
|
11883
12070
|
return `
|
|
12071
|
+
/* --- VARI\xC1VEIS DE TEMA (LIGHT MODE - PADR\xC3O) --- */
|
|
11884
12072
|
.myio-energy-modal-scope {
|
|
11885
|
-
--myio-energy-primary: ${
|
|
11886
|
-
--myio-energy-
|
|
11887
|
-
--myio-energy-
|
|
11888
|
-
|
|
11889
|
-
|
|
11890
|
-
--myio-energy-
|
|
12073
|
+
--myio-energy-primary: ${defaultPrimary};
|
|
12074
|
+
--myio-energy-font: ${defaultFont};
|
|
12075
|
+
--myio-energy-radius: 8px;
|
|
12076
|
+
|
|
12077
|
+
/* Cores Gerais (Light Mode) */
|
|
12078
|
+
--myio-energy-bg: #ffffff;
|
|
12079
|
+
--myio-energy-text: #1f2937;
|
|
12080
|
+
--myio-energy-text-secondary: #6b7280;
|
|
12081
|
+
|
|
12082
|
+
/* Borda GERAL (para bot\xF5es e inputs) - Cinza suave */
|
|
12083
|
+
--myio-energy-border: #e5e7eb;
|
|
12084
|
+
|
|
12085
|
+
/* Borda ESPEC\xCDFICA DO GR\xC1FICO (Aqui est\xE1 a corre\xE7\xE3o) */
|
|
12086
|
+
/* No Light Mode, definimos como transparente ou branco para "sumir" */
|
|
12087
|
+
--myio-chart-border: transparent;
|
|
12088
|
+
|
|
12089
|
+
--myio-energy-btn-bg: #f3f4f6;
|
|
12090
|
+
--myio-energy-btn-hover: #e5e7eb;
|
|
12091
|
+
--myio-energy-input-bg: #ffffff;
|
|
12092
|
+
--myio-granularity-bg: #f9fafb;
|
|
11891
12093
|
|
|
11892
12094
|
font-family: var(--myio-energy-font);
|
|
12095
|
+
background-color: var(--myio-energy-bg);
|
|
11893
12096
|
color: var(--myio-energy-text);
|
|
11894
12097
|
height: -webkit-fill-available;
|
|
12098
|
+
transition: background-color 0.3s ease, color 0.3s ease;
|
|
11895
12099
|
}
|
|
11896
12100
|
|
|
11897
|
-
|
|
11898
|
-
|
|
11899
|
-
|
|
11900
|
-
|
|
11901
|
-
|
|
11902
|
-
|
|
11903
|
-
|
|
11904
|
-
|
|
11905
|
-
|
|
11906
|
-
}
|
|
12101
|
+
/* --- DARK MODE OVERRIDES --- */
|
|
12102
|
+
.myio-energy-modal-scope[data-theme="dark"] {
|
|
12103
|
+
--myio-energy-bg: #1f1f1f;
|
|
12104
|
+
--myio-energy-text: #f3f4f6;
|
|
12105
|
+
--myio-energy-text-secondary: #9ca3af;
|
|
12106
|
+
|
|
12107
|
+
/* No Dark Mode, as bordas precisam aparecer */
|
|
12108
|
+
--myio-energy-border: #374151;
|
|
12109
|
+
--myio-chart-border: #374151; /* Borda vis\xEDvel no escuro */
|
|
11907
12110
|
|
|
11908
|
-
|
|
11909
|
-
|
|
12111
|
+
--myio-energy-btn-bg: #374151;
|
|
12112
|
+
--myio-energy-btn-hover: #4b5563;
|
|
12113
|
+
--myio-energy-input-bg: #111827;
|
|
12114
|
+
--myio-granularity-bg: #111827;
|
|
11910
12115
|
}
|
|
11911
12116
|
|
|
11912
|
-
|
|
11913
|
-
opacity: 0.5;
|
|
11914
|
-
cursor: not-allowed;
|
|
11915
|
-
}
|
|
12117
|
+
/* --- COMPONENTES --- */
|
|
11916
12118
|
|
|
11917
|
-
.myio-
|
|
11918
|
-
|
|
11919
|
-
|
|
11920
|
-
|
|
12119
|
+
.myio-energy-chart-container {
|
|
12120
|
+
flex: 1 !important;
|
|
12121
|
+
min-height: 353px !important;
|
|
12122
|
+
height: 353px !important;
|
|
12123
|
+
background: var(--myio-energy-bg);
|
|
12124
|
+
border-radius: var(--myio-energy-radius);
|
|
12125
|
+
|
|
12126
|
+
/* USO DA VARI\xC1VEL ESPEC\xCDFICA AQUI */
|
|
12127
|
+
border: 1px solid var(--myio-chart-border);
|
|
12128
|
+
|
|
12129
|
+
padding: 10px !important;
|
|
12130
|
+
display: block !important;
|
|
12131
|
+
overflow: hidden !important;
|
|
11921
12132
|
}
|
|
11922
12133
|
|
|
11923
|
-
.myio-
|
|
11924
|
-
|
|
12134
|
+
.myio-chart-container {
|
|
12135
|
+
/* Aplica a mesma l\xF3gica para o gr\xE1fico de fallback */
|
|
12136
|
+
border: 1px solid var(--myio-chart-border);
|
|
12137
|
+
border-radius: var(--myio-energy-radius);
|
|
12138
|
+
overflow: hidden;
|
|
11925
12139
|
}
|
|
11926
12140
|
|
|
11927
|
-
.
|
|
11928
|
-
|
|
11929
|
-
|
|
11930
|
-
|
|
12141
|
+
/* --- Resto dos estilos (Bot\xF5es, Labels, etc.) --- */
|
|
12142
|
+
|
|
12143
|
+
.myio-label-secondary {
|
|
12144
|
+
color: var(--myio-energy-text-secondary);
|
|
12145
|
+
font-weight: 500;
|
|
11931
12146
|
}
|
|
11932
12147
|
|
|
11933
|
-
.myio-
|
|
11934
|
-
|
|
12148
|
+
.myio-granularity-selector {
|
|
12149
|
+
display: flex;
|
|
12150
|
+
align-items: center;
|
|
12151
|
+
gap: 4px;
|
|
12152
|
+
margin-left: 8px;
|
|
12153
|
+
padding: 4px 8px;
|
|
12154
|
+
border-radius: 8px;
|
|
12155
|
+
background: var(--myio-granularity-bg);
|
|
12156
|
+
border: 1px solid var(--myio-energy-border); /* Granularidade mant\xE9m borda suave */
|
|
11935
12157
|
}
|
|
11936
12158
|
|
|
11937
|
-
/* RFC-0097: Granularity selector buttons */
|
|
11938
12159
|
.myio-btn-granularity {
|
|
11939
12160
|
padding: 4px 10px;
|
|
11940
12161
|
font-size: 12px;
|
|
11941
12162
|
font-weight: 600;
|
|
11942
12163
|
border-radius: 6px;
|
|
11943
|
-
border: 1px solid var(--myio-energy-border);
|
|
11944
|
-
background: var(--myio-energy-bg);
|
|
11945
|
-
color: var(--myio-energy-text);
|
|
11946
12164
|
cursor: pointer;
|
|
11947
12165
|
transition: all 0.2s ease;
|
|
11948
12166
|
min-width: 36px;
|
|
12167
|
+
background: transparent;
|
|
12168
|
+
border: 1px solid transparent;
|
|
12169
|
+
color: var(--myio-energy-text-secondary);
|
|
11949
12170
|
}
|
|
11950
12171
|
|
|
11951
12172
|
.myio-btn-granularity:hover:not(.active) {
|
|
11952
|
-
background:
|
|
11953
|
-
|
|
11954
|
-
color: var(--myio-energy-primary);
|
|
12173
|
+
background: var(--myio-energy-btn-hover);
|
|
12174
|
+
color: var(--myio-energy-text);
|
|
11955
12175
|
}
|
|
11956
12176
|
|
|
11957
12177
|
.myio-btn-granularity.active {
|
|
11958
12178
|
background: var(--myio-energy-primary);
|
|
11959
12179
|
color: white;
|
|
11960
12180
|
border-color: var(--myio-energy-primary);
|
|
11961
|
-
box-shadow: 0 2px
|
|
12181
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
|
11962
12182
|
}
|
|
11963
12183
|
|
|
11964
|
-
.myio-
|
|
12184
|
+
.myio-btn {
|
|
12185
|
+
padding: 8px 16px;
|
|
12186
|
+
border-radius: var(--myio-energy-radius);
|
|
11965
12187
|
border: 1px solid var(--myio-energy-border);
|
|
12188
|
+
background: var(--myio-energy-btn-bg);
|
|
12189
|
+
color: var(--myio-energy-text);
|
|
12190
|
+
cursor: pointer;
|
|
12191
|
+
font-size: 14px;
|
|
12192
|
+
transition: all 0.2s;
|
|
12193
|
+
}
|
|
12194
|
+
|
|
12195
|
+
.myio-btn:hover:not(:disabled) {
|
|
12196
|
+
background: var(--myio-energy-btn-hover);
|
|
12197
|
+
border-color: var(--myio-energy-border);
|
|
12198
|
+
}
|
|
12199
|
+
|
|
12200
|
+
.myio-btn-primary {
|
|
12201
|
+
background: var(--myio-energy-primary);
|
|
12202
|
+
color: white;
|
|
12203
|
+
border-color: var(--myio-energy-primary);
|
|
12204
|
+
}
|
|
12205
|
+
|
|
12206
|
+
.myio-btn-primary:hover:not(:disabled) {
|
|
12207
|
+
opacity: 0.9;
|
|
12208
|
+
}
|
|
12209
|
+
|
|
12210
|
+
.myio-btn-secondary {
|
|
12211
|
+
background: var(--myio-energy-btn-bg);
|
|
12212
|
+
color: var(--myio-energy-text);
|
|
12213
|
+
border-color: var(--myio-energy-border);
|
|
11966
12214
|
}
|
|
11967
12215
|
|
|
11968
12216
|
.myio-modal-scope {
|
|
@@ -11971,42 +12219,30 @@ var EnergyModalView = class {
|
|
|
11971
12219
|
flex-direction: column !important;
|
|
11972
12220
|
}
|
|
11973
12221
|
|
|
11974
|
-
.myio-
|
|
11975
|
-
|
|
11976
|
-
|
|
11977
|
-
height: 353px !important;
|
|
11978
|
-
background: var(--myio-energy-bg);
|
|
11979
|
-
border-radius: var(--myio-energy-radius);
|
|
11980
|
-
border: 1px solid var(--myio-energy-border);
|
|
11981
|
-
padding: 10px !important;
|
|
11982
|
-
display: block !important;
|
|
11983
|
-
overflow: hidden !important;
|
|
11984
|
-
}
|
|
11985
|
-
|
|
11986
|
-
.myio-energy-chart-container > iframe {
|
|
11987
|
-
width: 100% !important;
|
|
11988
|
-
height: 100% !important;
|
|
11989
|
-
min-height: 408px !important;
|
|
11990
|
-
border: none !important;
|
|
12222
|
+
.myio-form-group {
|
|
12223
|
+
display: flex;
|
|
12224
|
+
flex-direction: column;
|
|
11991
12225
|
}
|
|
11992
12226
|
|
|
11993
|
-
.myio-
|
|
11994
|
-
|
|
11995
|
-
|
|
11996
|
-
|
|
11997
|
-
min-height: 408px !important;
|
|
12227
|
+
.myio-label {
|
|
12228
|
+
font-weight: 500;
|
|
12229
|
+
margin-bottom: 5px;
|
|
12230
|
+
color: var(--myio-energy-text);
|
|
11998
12231
|
}
|
|
11999
12232
|
|
|
12000
|
-
.myio-
|
|
12001
|
-
|
|
12002
|
-
|
|
12003
|
-
|
|
12004
|
-
|
|
12005
|
-
|
|
12233
|
+
.myio-input {
|
|
12234
|
+
padding: 8px 12px;
|
|
12235
|
+
border: 1px solid var(--myio-energy-border);
|
|
12236
|
+
border-radius: var(--myio-energy-radius);
|
|
12237
|
+
font-size: 14px;
|
|
12238
|
+
background: var(--myio-energy-input-bg);
|
|
12239
|
+
color: var(--myio-energy-text);
|
|
12006
12240
|
}
|
|
12007
12241
|
|
|
12008
|
-
.myio-
|
|
12009
|
-
|
|
12242
|
+
.myio-input:focus {
|
|
12243
|
+
outline: none;
|
|
12244
|
+
border-color: var(--myio-energy-primary);
|
|
12245
|
+
box-shadow: 0 0 0 1px var(--myio-energy-primary);
|
|
12010
12246
|
}
|
|
12011
12247
|
|
|
12012
12248
|
.myio-spinner {
|
|
@@ -12018,78 +12254,40 @@ var EnergyModalView = class {
|
|
|
12018
12254
|
animation: spin 1s linear infinite;
|
|
12019
12255
|
margin: 0 auto 16px;
|
|
12020
12256
|
}
|
|
12021
|
-
|
|
12257
|
+
|
|
12022
12258
|
@keyframes spin {
|
|
12023
12259
|
0% { transform: rotate(0deg); }
|
|
12024
12260
|
100% { transform: rotate(360deg); }
|
|
12025
12261
|
}
|
|
12026
12262
|
|
|
12263
|
+
.myio-loading-state p {
|
|
12264
|
+
color: var(--myio-energy-text);
|
|
12265
|
+
}
|
|
12266
|
+
|
|
12027
12267
|
.myio-energy-error {
|
|
12028
|
-
background:
|
|
12029
|
-
border: 1px solid
|
|
12268
|
+
background: rgba(254, 202, 202, 0.15);
|
|
12269
|
+
border: 1px solid rgba(248, 113, 113, 0.5);
|
|
12030
12270
|
border-radius: var(--myio-energy-radius);
|
|
12031
12271
|
padding: 16px;
|
|
12032
12272
|
}
|
|
12033
|
-
|
|
12034
|
-
.myio-error-content {
|
|
12035
|
-
display: flex;
|
|
12036
|
-
align-items: center;
|
|
12037
|
-
gap: 12px;
|
|
12038
|
-
}
|
|
12039
|
-
|
|
12040
|
-
.myio-error-icon {
|
|
12041
|
-
font-size: 24px;
|
|
12042
|
-
}
|
|
12043
|
-
|
|
12273
|
+
|
|
12044
12274
|
.myio-error-message {
|
|
12045
|
-
color: #
|
|
12275
|
+
color: #ef4444;
|
|
12046
12276
|
font-weight: 500;
|
|
12047
12277
|
}
|
|
12048
12278
|
|
|
12049
|
-
|
|
12050
|
-
|
|
12051
|
-
.myio-fallback-chart h4 {
|
|
12052
|
-
margin: 0 0 16px 0;
|
|
12053
|
-
text-align: center;
|
|
12054
|
-
}
|
|
12055
|
-
|
|
12056
|
-
.myio-chart-container {
|
|
12057
|
-
border: 1px solid var(--myio-energy-border);
|
|
12058
|
-
border-radius: var(--myio-energy-radius);
|
|
12059
|
-
overflow: hidden;
|
|
12060
|
-
}
|
|
12061
|
-
|
|
12062
12279
|
.myio-chart-note {
|
|
12063
12280
|
margin: 12px 0 0 0;
|
|
12064
12281
|
font-size: 12px;
|
|
12065
|
-
color: #666;
|
|
12066
|
-
text-align: center;
|
|
12067
|
-
}
|
|
12068
|
-
|
|
12069
|
-
.myio-form-group {
|
|
12070
|
-
display: flex;
|
|
12071
|
-
flex-direction: column;
|
|
12072
|
-
}
|
|
12073
|
-
|
|
12074
|
-
.myio-label {
|
|
12075
|
-
font-weight: 500;
|
|
12076
|
-
margin-bottom: 5px;
|
|
12077
|
-
color: var(--myio-energy-text);
|
|
12078
|
-
}
|
|
12079
|
-
|
|
12080
|
-
.myio-input {
|
|
12081
|
-
padding: 8px 12px;
|
|
12082
|
-
border: 1px solid var(--myio-energy-border);
|
|
12083
|
-
border-radius: var(--myio-energy-radius);
|
|
12084
|
-
font-size: 14px;
|
|
12085
|
-
background: var(--myio-energy-bg);
|
|
12086
12282
|
color: var(--myio-energy-text);
|
|
12283
|
+
opacity: 0.7;
|
|
12284
|
+
text-align: center;
|
|
12087
12285
|
}
|
|
12088
12286
|
|
|
12089
|
-
.myio-
|
|
12090
|
-
|
|
12091
|
-
|
|
12092
|
-
|
|
12287
|
+
.myio-energy-chart-container > iframe {
|
|
12288
|
+
width: 100% !important;
|
|
12289
|
+
height: 100% !important;
|
|
12290
|
+
border: none !important;
|
|
12093
12291
|
}
|
|
12094
12292
|
|
|
12095
12293
|
@media (max-width: 768px) {
|
|
@@ -12097,12 +12295,6 @@ var EnergyModalView = class {
|
|
|
12097
12295
|
grid-template-columns: 1fr;
|
|
12098
12296
|
grid-template-rows: auto 1fr;
|
|
12099
12297
|
}
|
|
12100
|
-
|
|
12101
|
-
.myio-energy-header {
|
|
12102
|
-
flex-direction: column;
|
|
12103
|
-
gap: 12px;
|
|
12104
|
-
align-items: stretch;
|
|
12105
|
-
}
|
|
12106
12298
|
}
|
|
12107
12299
|
`;
|
|
12108
12300
|
}
|
|
@@ -18826,8 +19018,8 @@ function openGoalsPanel(params) {
|
|
|
18826
19018
|
<div class="myio-goals-progress-fill" style="width: ${Math.min(progress, 100)}%"></div>
|
|
18827
19019
|
</div>
|
|
18828
19020
|
<div class="myio-goals-progress-text">
|
|
18829
|
-
<span>${
|
|
18830
|
-
<span>${
|
|
19021
|
+
<span>${formatNumber3(monthlySum, locale)} ${annual.unit}</span>
|
|
19022
|
+
<span>${formatNumber3(annual.total, locale)} ${annual.unit}</span>
|
|
18831
19023
|
</div>
|
|
18832
19024
|
|
|
18833
19025
|
<!-- Monthly Grid -->
|
|
@@ -18901,7 +19093,7 @@ function openGoalsPanel(params) {
|
|
|
18901
19093
|
<span>${assetData.label || assetId}</span>
|
|
18902
19094
|
</div>
|
|
18903
19095
|
<div class="myio-goals-asset-total">
|
|
18904
|
-
${
|
|
19096
|
+
${formatNumber3(assetData.annual?.total || 0, locale)} ${assetData.annual?.unit || "kWh"}
|
|
18905
19097
|
</div>
|
|
18906
19098
|
<button class="myio-goals-btn-icon" data-action="delete-asset" data-asset-id="${assetId}" aria-label="${i18n.deleteAsset}">
|
|
18907
19099
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
@@ -19216,7 +19408,7 @@ function openGoalsPanel(params) {
|
|
|
19216
19408
|
monthlySum += value;
|
|
19217
19409
|
}
|
|
19218
19410
|
if (monthlySum > annualTotal && annualTotal > 0) {
|
|
19219
|
-
errors.push(`${i18n.errorMonthlyExceedsAnnual} (${
|
|
19411
|
+
errors.push(`${i18n.errorMonthlyExceedsAnnual} (${formatNumber3(monthlySum, locale)} > ${formatNumber3(annualTotal, locale)})`);
|
|
19220
19412
|
}
|
|
19221
19413
|
}
|
|
19222
19414
|
return errors;
|
|
@@ -19307,8 +19499,8 @@ function openGoalsPanel(params) {
|
|
|
19307
19499
|
}
|
|
19308
19500
|
if (progressTexts.length === 2) {
|
|
19309
19501
|
const unit = document.getElementById("unit-select")?.value || "kWh";
|
|
19310
|
-
progressTexts[0].textContent = `${
|
|
19311
|
-
progressTexts[1].textContent = `${
|
|
19502
|
+
progressTexts[0].textContent = `${formatNumber3(monthlySum, locale)} ${unit}`;
|
|
19503
|
+
progressTexts[1].textContent = `${formatNumber3(annualTotal, locale)} ${unit}`;
|
|
19312
19504
|
}
|
|
19313
19505
|
}
|
|
19314
19506
|
function updateMonthlyUnits(unit) {
|
|
@@ -19406,7 +19598,7 @@ function openGoalsPanel(params) {
|
|
|
19406
19598
|
modal.addEventListener("keydown", handleTab);
|
|
19407
19599
|
firstElement.focus();
|
|
19408
19600
|
}
|
|
19409
|
-
function
|
|
19601
|
+
function formatNumber3(value, locale2) {
|
|
19410
19602
|
return new Intl.NumberFormat(locale2, {
|
|
19411
19603
|
minimumFractionDigits: 0,
|
|
19412
19604
|
maximumFractionDigits: 2
|
|
@@ -22356,11 +22548,1742 @@ function openTemperatureSettingsModal(params) {
|
|
|
22356
22548
|
});
|
|
22357
22549
|
return { destroy };
|
|
22358
22550
|
}
|
|
22551
|
+
|
|
22552
|
+
// src/components/ModalHeader/index.ts
|
|
22553
|
+
var DEFAULT_BG_COLOR = "#3e1a7d";
|
|
22554
|
+
var DEFAULT_TEXT_COLOR = "white";
|
|
22555
|
+
var DEFAULT_BORDER_RADIUS = "10px 10px 0 0";
|
|
22556
|
+
var EXPORT_FORMAT_LABELS = {
|
|
22557
|
+
csv: "CSV",
|
|
22558
|
+
xls: "Excel (XLS)",
|
|
22559
|
+
pdf: "PDF"
|
|
22560
|
+
};
|
|
22561
|
+
var EXPORT_FORMAT_ICONS = {
|
|
22562
|
+
csv: "\u{1F4C4}",
|
|
22563
|
+
xls: "\u{1F4CA}",
|
|
22564
|
+
pdf: "\u{1F4D1}"
|
|
22565
|
+
};
|
|
22566
|
+
function createModalHeader(config) {
|
|
22567
|
+
let currentTheme = config.theme || "light";
|
|
22568
|
+
let currentIsMaximized = config.isMaximized || false;
|
|
22569
|
+
let currentTitle = config.title;
|
|
22570
|
+
let themeBtn = null;
|
|
22571
|
+
let maximizeBtn = null;
|
|
22572
|
+
let closeBtn = null;
|
|
22573
|
+
let exportBtn = null;
|
|
22574
|
+
let exportDropdown = null;
|
|
22575
|
+
const cleanupHandlers = [];
|
|
22576
|
+
const handleThemeClick = () => {
|
|
22577
|
+
currentTheme = currentTheme === "light" ? "dark" : "light";
|
|
22578
|
+
config.onThemeToggle?.(currentTheme);
|
|
22579
|
+
updateButtonIcons();
|
|
22580
|
+
};
|
|
22581
|
+
const handleMaximizeClick = () => {
|
|
22582
|
+
currentIsMaximized = !currentIsMaximized;
|
|
22583
|
+
config.onMaximize?.(currentIsMaximized);
|
|
22584
|
+
updateButtonIcons();
|
|
22585
|
+
};
|
|
22586
|
+
const handleCloseClick = () => {
|
|
22587
|
+
config.onClose?.();
|
|
22588
|
+
};
|
|
22589
|
+
const handleExportClick = (format) => {
|
|
22590
|
+
config.onExport?.(format);
|
|
22591
|
+
if (exportDropdown) {
|
|
22592
|
+
exportDropdown.style.display = "none";
|
|
22593
|
+
}
|
|
22594
|
+
};
|
|
22595
|
+
function updateButtonIcons() {
|
|
22596
|
+
if (themeBtn) {
|
|
22597
|
+
themeBtn.textContent = currentTheme === "dark" ? "\u2600\uFE0F" : "\u{1F319}";
|
|
22598
|
+
themeBtn.title = currentTheme === "dark" ? "Modo claro" : "Modo escuro";
|
|
22599
|
+
}
|
|
22600
|
+
if (maximizeBtn) {
|
|
22601
|
+
maximizeBtn.textContent = currentIsMaximized ? "\u{1F5D7}" : "\u{1F5D6}";
|
|
22602
|
+
maximizeBtn.title = currentIsMaximized ? "Restaurar" : "Maximizar";
|
|
22603
|
+
}
|
|
22604
|
+
}
|
|
22605
|
+
function getButtonStyle() {
|
|
22606
|
+
return `
|
|
22607
|
+
background: none;
|
|
22608
|
+
border: none;
|
|
22609
|
+
font-size: 16px;
|
|
22610
|
+
cursor: pointer;
|
|
22611
|
+
padding: 4px 8px;
|
|
22612
|
+
border-radius: 6px;
|
|
22613
|
+
color: rgba(255, 255, 255, 0.8);
|
|
22614
|
+
transition: background-color 0.2s, color 0.2s;
|
|
22615
|
+
`.replace(/\s+/g, " ").trim();
|
|
22616
|
+
}
|
|
22617
|
+
function renderExportDropdown() {
|
|
22618
|
+
const formats = config.exportFormats || [];
|
|
22619
|
+
if (formats.length === 0) return "";
|
|
22620
|
+
if (formats.length === 1) {
|
|
22621
|
+
return `
|
|
22622
|
+
<button id="${config.id}-export" title="Exportar ${EXPORT_FORMAT_LABELS[formats[0]]}" style="${getButtonStyle()}">
|
|
22623
|
+
\u{1F4E5}
|
|
22624
|
+
</button>
|
|
22625
|
+
`;
|
|
22626
|
+
}
|
|
22627
|
+
return `
|
|
22628
|
+
<div style="position: relative; display: inline-block;">
|
|
22629
|
+
<button id="${config.id}-export-btn" title="Exportar" style="${getButtonStyle()}">
|
|
22630
|
+
\u{1F4E5}
|
|
22631
|
+
</button>
|
|
22632
|
+
<div id="${config.id}-export-dropdown" style="
|
|
22633
|
+
display: none;
|
|
22634
|
+
position: absolute;
|
|
22635
|
+
top: 100%;
|
|
22636
|
+
right: 0;
|
|
22637
|
+
background: white;
|
|
22638
|
+
border-radius: 6px;
|
|
22639
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
22640
|
+
min-width: 140px;
|
|
22641
|
+
z-index: 10001;
|
|
22642
|
+
margin-top: 4px;
|
|
22643
|
+
overflow: hidden;
|
|
22644
|
+
">
|
|
22645
|
+
${formats.map((format) => `
|
|
22646
|
+
<button
|
|
22647
|
+
id="${config.id}-export-${format}"
|
|
22648
|
+
class="myio-export-option"
|
|
22649
|
+
data-format="${format}"
|
|
22650
|
+
style="
|
|
22651
|
+
display: flex;
|
|
22652
|
+
align-items: center;
|
|
22653
|
+
gap: 8px;
|
|
22654
|
+
width: 100%;
|
|
22655
|
+
padding: 10px 14px;
|
|
22656
|
+
border: none;
|
|
22657
|
+
background: white;
|
|
22658
|
+
cursor: pointer;
|
|
22659
|
+
font-size: 13px;
|
|
22660
|
+
color: #333;
|
|
22661
|
+
text-align: left;
|
|
22662
|
+
transition: background-color 0.2s;
|
|
22663
|
+
"
|
|
22664
|
+
>
|
|
22665
|
+
${EXPORT_FORMAT_ICONS[format]} ${EXPORT_FORMAT_LABELS[format]}
|
|
22666
|
+
</button>
|
|
22667
|
+
`).join("")}
|
|
22668
|
+
</div>
|
|
22669
|
+
</div>
|
|
22670
|
+
`;
|
|
22671
|
+
}
|
|
22672
|
+
const instance = {
|
|
22673
|
+
render() {
|
|
22674
|
+
const bgColor = config.backgroundColor || DEFAULT_BG_COLOR;
|
|
22675
|
+
const textColor = config.textColor || DEFAULT_TEXT_COLOR;
|
|
22676
|
+
const borderRadius = currentIsMaximized ? "0" : config.borderRadius || DEFAULT_BORDER_RADIUS;
|
|
22677
|
+
const showTheme = config.showThemeToggle !== false;
|
|
22678
|
+
const showMax = config.showMaximize !== false;
|
|
22679
|
+
const showClose = config.showClose !== false;
|
|
22680
|
+
const showExport = config.exportFormats && config.exportFormats.length > 0;
|
|
22681
|
+
const iconHtml = config.icon ? `<span style="margin-right: 8px;">${config.icon}</span>` : "";
|
|
22682
|
+
const buttonStyle = getButtonStyle();
|
|
22683
|
+
return `
|
|
22684
|
+
<div class="myio-modal-header" style="
|
|
22685
|
+
padding: 4px 8px;
|
|
22686
|
+
display: flex;
|
|
22687
|
+
align-items: center;
|
|
22688
|
+
justify-content: space-between;
|
|
22689
|
+
background: ${bgColor};
|
|
22690
|
+
color: ${textColor};
|
|
22691
|
+
border-radius: ${borderRadius};
|
|
22692
|
+
min-height: 20px;
|
|
22693
|
+
font-family: 'Roboto', Arial, sans-serif;
|
|
22694
|
+
">
|
|
22695
|
+
<h2 id="${config.id}-header-title" style="
|
|
22696
|
+
margin: 6px;
|
|
22697
|
+
font-size: 18px;
|
|
22698
|
+
font-weight: 600;
|
|
22699
|
+
color: ${textColor};
|
|
22700
|
+
line-height: 2;
|
|
22701
|
+
display: flex;
|
|
22702
|
+
align-items: center;
|
|
22703
|
+
">
|
|
22704
|
+
${iconHtml}${currentTitle}
|
|
22705
|
+
</h2>
|
|
22706
|
+
<div style="display: flex; gap: 4px; align-items: center;">
|
|
22707
|
+
${showExport ? renderExportDropdown() : ""}
|
|
22708
|
+
${showTheme ? `
|
|
22709
|
+
<button id="${config.id}-theme-toggle" title="${currentTheme === "dark" ? "Modo claro" : "Modo escuro"}" style="${buttonStyle}">
|
|
22710
|
+
${currentTheme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}
|
|
22711
|
+
</button>
|
|
22712
|
+
` : ""}
|
|
22713
|
+
${showMax ? `
|
|
22714
|
+
<button id="${config.id}-maximize" title="${currentIsMaximized ? "Restaurar" : "Maximizar"}" style="${buttonStyle}">
|
|
22715
|
+
${currentIsMaximized ? "\u{1F5D7}" : "\u{1F5D6}"}
|
|
22716
|
+
</button>
|
|
22717
|
+
` : ""}
|
|
22718
|
+
${showClose ? `
|
|
22719
|
+
<button id="${config.id}-close" title="Fechar" style="${buttonStyle}; font-size: 20px;">
|
|
22720
|
+
\xD7
|
|
22721
|
+
</button>
|
|
22722
|
+
` : ""}
|
|
22723
|
+
</div>
|
|
22724
|
+
</div>
|
|
22725
|
+
`;
|
|
22726
|
+
},
|
|
22727
|
+
attachListeners() {
|
|
22728
|
+
themeBtn = document.getElementById(`${config.id}-theme-toggle`);
|
|
22729
|
+
maximizeBtn = document.getElementById(`${config.id}-maximize`);
|
|
22730
|
+
closeBtn = document.getElementById(`${config.id}-close`);
|
|
22731
|
+
const singleExportBtn = document.getElementById(`${config.id}-export`);
|
|
22732
|
+
if (singleExportBtn && config.exportFormats?.length === 1) {
|
|
22733
|
+
exportBtn = singleExportBtn;
|
|
22734
|
+
const format = config.exportFormats[0];
|
|
22735
|
+
const clickHandler = () => handleExportClick(format);
|
|
22736
|
+
exportBtn.addEventListener("click", clickHandler);
|
|
22737
|
+
cleanupHandlers.push(() => exportBtn?.removeEventListener("click", clickHandler));
|
|
22738
|
+
const enterHandler = () => {
|
|
22739
|
+
exportBtn.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
|
|
22740
|
+
};
|
|
22741
|
+
const leaveHandler = () => {
|
|
22742
|
+
exportBtn.style.backgroundColor = "transparent";
|
|
22743
|
+
};
|
|
22744
|
+
exportBtn.addEventListener("mouseenter", enterHandler);
|
|
22745
|
+
exportBtn.addEventListener("mouseleave", leaveHandler);
|
|
22746
|
+
cleanupHandlers.push(() => {
|
|
22747
|
+
exportBtn?.removeEventListener("mouseenter", enterHandler);
|
|
22748
|
+
exportBtn?.removeEventListener("mouseleave", leaveHandler);
|
|
22749
|
+
});
|
|
22750
|
+
}
|
|
22751
|
+
const exportDropdownBtn = document.getElementById(`${config.id}-export-btn`);
|
|
22752
|
+
exportDropdown = document.getElementById(`${config.id}-export-dropdown`);
|
|
22753
|
+
if (exportDropdownBtn && exportDropdown) {
|
|
22754
|
+
exportBtn = exportDropdownBtn;
|
|
22755
|
+
const toggleHandler = (e) => {
|
|
22756
|
+
e.stopPropagation();
|
|
22757
|
+
if (exportDropdown) {
|
|
22758
|
+
exportDropdown.style.display = exportDropdown.style.display === "none" ? "block" : "none";
|
|
22759
|
+
}
|
|
22760
|
+
};
|
|
22761
|
+
exportDropdownBtn.addEventListener("click", toggleHandler);
|
|
22762
|
+
cleanupHandlers.push(() => exportDropdownBtn.removeEventListener("click", toggleHandler));
|
|
22763
|
+
const outsideClickHandler = (e) => {
|
|
22764
|
+
if (exportDropdown && !exportDropdown.contains(e.target) && e.target !== exportDropdownBtn) {
|
|
22765
|
+
exportDropdown.style.display = "none";
|
|
22766
|
+
}
|
|
22767
|
+
};
|
|
22768
|
+
document.addEventListener("click", outsideClickHandler);
|
|
22769
|
+
cleanupHandlers.push(() => document.removeEventListener("click", outsideClickHandler));
|
|
22770
|
+
config.exportFormats?.forEach((format) => {
|
|
22771
|
+
const btn = document.getElementById(`${config.id}-export-${format}`);
|
|
22772
|
+
if (btn) {
|
|
22773
|
+
const clickHandler = () => handleExportClick(format);
|
|
22774
|
+
btn.addEventListener("click", clickHandler);
|
|
22775
|
+
cleanupHandlers.push(() => btn.removeEventListener("click", clickHandler));
|
|
22776
|
+
const enterHandler2 = () => {
|
|
22777
|
+
btn.style.backgroundColor = "#f0f0f0";
|
|
22778
|
+
};
|
|
22779
|
+
const leaveHandler2 = () => {
|
|
22780
|
+
btn.style.backgroundColor = "white";
|
|
22781
|
+
};
|
|
22782
|
+
btn.addEventListener("mouseenter", enterHandler2);
|
|
22783
|
+
btn.addEventListener("mouseleave", leaveHandler2);
|
|
22784
|
+
cleanupHandlers.push(() => {
|
|
22785
|
+
btn.removeEventListener("mouseenter", enterHandler2);
|
|
22786
|
+
btn.removeEventListener("mouseleave", leaveHandler2);
|
|
22787
|
+
});
|
|
22788
|
+
}
|
|
22789
|
+
});
|
|
22790
|
+
const enterHandler = () => {
|
|
22791
|
+
exportDropdownBtn.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
|
|
22792
|
+
};
|
|
22793
|
+
const leaveHandler = () => {
|
|
22794
|
+
exportDropdownBtn.style.backgroundColor = "transparent";
|
|
22795
|
+
};
|
|
22796
|
+
exportDropdownBtn.addEventListener("mouseenter", enterHandler);
|
|
22797
|
+
exportDropdownBtn.addEventListener("mouseleave", leaveHandler);
|
|
22798
|
+
cleanupHandlers.push(() => {
|
|
22799
|
+
exportDropdownBtn.removeEventListener("mouseenter", enterHandler);
|
|
22800
|
+
exportDropdownBtn.removeEventListener("mouseleave", leaveHandler);
|
|
22801
|
+
});
|
|
22802
|
+
}
|
|
22803
|
+
if (themeBtn && config.showThemeToggle !== false) {
|
|
22804
|
+
themeBtn.addEventListener("click", handleThemeClick);
|
|
22805
|
+
cleanupHandlers.push(() => themeBtn?.removeEventListener("click", handleThemeClick));
|
|
22806
|
+
const enterHandler = () => {
|
|
22807
|
+
themeBtn.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
|
|
22808
|
+
};
|
|
22809
|
+
const leaveHandler = () => {
|
|
22810
|
+
themeBtn.style.backgroundColor = "transparent";
|
|
22811
|
+
};
|
|
22812
|
+
themeBtn.addEventListener("mouseenter", enterHandler);
|
|
22813
|
+
themeBtn.addEventListener("mouseleave", leaveHandler);
|
|
22814
|
+
cleanupHandlers.push(() => {
|
|
22815
|
+
themeBtn?.removeEventListener("mouseenter", enterHandler);
|
|
22816
|
+
themeBtn?.removeEventListener("mouseleave", leaveHandler);
|
|
22817
|
+
});
|
|
22818
|
+
}
|
|
22819
|
+
if (maximizeBtn && config.showMaximize !== false) {
|
|
22820
|
+
maximizeBtn.addEventListener("click", handleMaximizeClick);
|
|
22821
|
+
cleanupHandlers.push(() => maximizeBtn?.removeEventListener("click", handleMaximizeClick));
|
|
22822
|
+
const enterHandler = () => {
|
|
22823
|
+
maximizeBtn.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
|
|
22824
|
+
};
|
|
22825
|
+
const leaveHandler = () => {
|
|
22826
|
+
maximizeBtn.style.backgroundColor = "transparent";
|
|
22827
|
+
};
|
|
22828
|
+
maximizeBtn.addEventListener("mouseenter", enterHandler);
|
|
22829
|
+
maximizeBtn.addEventListener("mouseleave", leaveHandler);
|
|
22830
|
+
cleanupHandlers.push(() => {
|
|
22831
|
+
maximizeBtn?.removeEventListener("mouseenter", enterHandler);
|
|
22832
|
+
maximizeBtn?.removeEventListener("mouseleave", leaveHandler);
|
|
22833
|
+
});
|
|
22834
|
+
}
|
|
22835
|
+
if (closeBtn && config.showClose !== false) {
|
|
22836
|
+
closeBtn.addEventListener("click", handleCloseClick);
|
|
22837
|
+
cleanupHandlers.push(() => closeBtn?.removeEventListener("click", handleCloseClick));
|
|
22838
|
+
const enterHandler = () => {
|
|
22839
|
+
closeBtn.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
|
|
22840
|
+
};
|
|
22841
|
+
const leaveHandler = () => {
|
|
22842
|
+
closeBtn.style.backgroundColor = "transparent";
|
|
22843
|
+
};
|
|
22844
|
+
closeBtn.addEventListener("mouseenter", enterHandler);
|
|
22845
|
+
closeBtn.addEventListener("mouseleave", leaveHandler);
|
|
22846
|
+
cleanupHandlers.push(() => {
|
|
22847
|
+
closeBtn?.removeEventListener("mouseenter", enterHandler);
|
|
22848
|
+
closeBtn?.removeEventListener("mouseleave", leaveHandler);
|
|
22849
|
+
});
|
|
22850
|
+
}
|
|
22851
|
+
},
|
|
22852
|
+
update(updates) {
|
|
22853
|
+
if (updates.theme !== void 0) {
|
|
22854
|
+
currentTheme = updates.theme;
|
|
22855
|
+
updateButtonIcons();
|
|
22856
|
+
}
|
|
22857
|
+
if (updates.isMaximized !== void 0) {
|
|
22858
|
+
currentIsMaximized = updates.isMaximized;
|
|
22859
|
+
updateButtonIcons();
|
|
22860
|
+
}
|
|
22861
|
+
if (updates.title !== void 0) {
|
|
22862
|
+
currentTitle = updates.title;
|
|
22863
|
+
const titleEl = document.getElementById(`${config.id}-header-title`);
|
|
22864
|
+
if (titleEl) {
|
|
22865
|
+
const iconHtml = config.icon ? `<span style="margin-right: 8px;">${config.icon}</span>` : "";
|
|
22866
|
+
titleEl.innerHTML = `${iconHtml}${currentTitle}`;
|
|
22867
|
+
}
|
|
22868
|
+
}
|
|
22869
|
+
},
|
|
22870
|
+
getState() {
|
|
22871
|
+
return {
|
|
22872
|
+
theme: currentTheme,
|
|
22873
|
+
isMaximized: currentIsMaximized
|
|
22874
|
+
};
|
|
22875
|
+
},
|
|
22876
|
+
destroy() {
|
|
22877
|
+
cleanupHandlers.forEach((handler) => handler());
|
|
22878
|
+
cleanupHandlers.length = 0;
|
|
22879
|
+
themeBtn = null;
|
|
22880
|
+
maximizeBtn = null;
|
|
22881
|
+
closeBtn = null;
|
|
22882
|
+
exportBtn = null;
|
|
22883
|
+
exportDropdown = null;
|
|
22884
|
+
}
|
|
22885
|
+
};
|
|
22886
|
+
return instance;
|
|
22887
|
+
}
|
|
22888
|
+
function getModalHeaderStyles() {
|
|
22889
|
+
return `
|
|
22890
|
+
.myio-modal-header button:hover {
|
|
22891
|
+
background-color: rgba(255, 255, 255, 0.2) !important;
|
|
22892
|
+
}
|
|
22893
|
+
.myio-modal-header button:active {
|
|
22894
|
+
background-color: rgba(255, 255, 255, 0.3) !important;
|
|
22895
|
+
}
|
|
22896
|
+
.myio-export-option:hover {
|
|
22897
|
+
background-color: #f0f0f0 !important;
|
|
22898
|
+
}
|
|
22899
|
+
`;
|
|
22900
|
+
}
|
|
22901
|
+
|
|
22902
|
+
// src/components/Consumption7DaysChart/types.ts
|
|
22903
|
+
var DEFAULT_COLORS = {
|
|
22904
|
+
energy: {
|
|
22905
|
+
primary: "#2563eb",
|
|
22906
|
+
background: "rgba(37, 99, 235, 0.1)",
|
|
22907
|
+
gradient: ["#f0fdf4", "#dcfce7"],
|
|
22908
|
+
pointBackground: "#2563eb",
|
|
22909
|
+
pointBorder: "#ffffff"
|
|
22910
|
+
},
|
|
22911
|
+
water: {
|
|
22912
|
+
primary: "#0288d1",
|
|
22913
|
+
background: "rgba(2, 136, 209, 0.1)",
|
|
22914
|
+
gradient: ["#f0f9ff", "#bae6fd"],
|
|
22915
|
+
pointBackground: "#0288d1",
|
|
22916
|
+
pointBorder: "#ffffff"
|
|
22917
|
+
},
|
|
22918
|
+
gas: {
|
|
22919
|
+
primary: "#ea580c",
|
|
22920
|
+
background: "rgba(234, 88, 12, 0.1)",
|
|
22921
|
+
gradient: ["#fff7ed", "#fed7aa"],
|
|
22922
|
+
pointBackground: "#ea580c",
|
|
22923
|
+
pointBorder: "#ffffff"
|
|
22924
|
+
},
|
|
22925
|
+
temperature: {
|
|
22926
|
+
primary: "#dc2626",
|
|
22927
|
+
background: "rgba(220, 38, 38, 0.1)",
|
|
22928
|
+
gradient: ["#fef2f2", "#fecaca"],
|
|
22929
|
+
pointBackground: "#dc2626",
|
|
22930
|
+
pointBorder: "#ffffff"
|
|
22931
|
+
}
|
|
22932
|
+
};
|
|
22933
|
+
var THEME_COLORS = {
|
|
22934
|
+
light: {
|
|
22935
|
+
chartBackground: "#ffffff",
|
|
22936
|
+
text: "#1f2937",
|
|
22937
|
+
textMuted: "#6b7280",
|
|
22938
|
+
grid: "rgba(0, 0, 0, 0.1)",
|
|
22939
|
+
border: "#e5e7eb",
|
|
22940
|
+
tooltipBackground: "#ffffff",
|
|
22941
|
+
tooltipText: "#1f2937"
|
|
22942
|
+
},
|
|
22943
|
+
dark: {
|
|
22944
|
+
chartBackground: "#1f2937",
|
|
22945
|
+
text: "#f9fafb",
|
|
22946
|
+
textMuted: "#9ca3af",
|
|
22947
|
+
grid: "rgba(255, 255, 255, 0.1)",
|
|
22948
|
+
border: "#374151",
|
|
22949
|
+
tooltipBackground: "#374151",
|
|
22950
|
+
tooltipText: "#f9fafb"
|
|
22951
|
+
}
|
|
22952
|
+
};
|
|
22953
|
+
var DEFAULT_CONFIG = {
|
|
22954
|
+
defaultPeriod: 7,
|
|
22955
|
+
defaultChartType: "line",
|
|
22956
|
+
defaultVizMode: "total",
|
|
22957
|
+
defaultTheme: "light",
|
|
22958
|
+
cacheTTL: 3e5,
|
|
22959
|
+
// 5 minutes
|
|
22960
|
+
decimalPlaces: 1,
|
|
22961
|
+
lineTension: 0.4,
|
|
22962
|
+
pointRadius: 4,
|
|
22963
|
+
borderWidth: 2,
|
|
22964
|
+
fill: true,
|
|
22965
|
+
showLegend: false,
|
|
22966
|
+
enableExport: true
|
|
22967
|
+
};
|
|
22968
|
+
|
|
22969
|
+
// src/components/Consumption7DaysChart/createConsumption7DaysChart.ts
|
|
22970
|
+
function createConsumption7DaysChart(config) {
|
|
22971
|
+
let chartInstance = null;
|
|
22972
|
+
let cachedData = null;
|
|
22973
|
+
let currentPeriod = config.defaultPeriod ?? DEFAULT_CONFIG.defaultPeriod;
|
|
22974
|
+
let currentChartType = config.defaultChartType ?? DEFAULT_CONFIG.defaultChartType;
|
|
22975
|
+
let currentVizMode = config.defaultVizMode ?? DEFAULT_CONFIG.defaultVizMode;
|
|
22976
|
+
let currentTheme = config.theme ?? DEFAULT_CONFIG.defaultTheme;
|
|
22977
|
+
let currentIdealRange = config.idealRange ?? null;
|
|
22978
|
+
let isRendered = false;
|
|
22979
|
+
let autoRefreshTimer = null;
|
|
22980
|
+
const colors = {
|
|
22981
|
+
...DEFAULT_COLORS[config.domain] ?? DEFAULT_COLORS.energy,
|
|
22982
|
+
...config.colors
|
|
22983
|
+
};
|
|
22984
|
+
function $id(id) {
|
|
22985
|
+
if (config.$container && config.$container[0]) {
|
|
22986
|
+
return config.$container[0].querySelector(`#${id}`);
|
|
22987
|
+
}
|
|
22988
|
+
return document.getElementById(id);
|
|
22989
|
+
}
|
|
22990
|
+
function log(level, ...args) {
|
|
22991
|
+
const prefix = `[${config.domain.toUpperCase()}]`;
|
|
22992
|
+
console[level](prefix, ...args);
|
|
22993
|
+
}
|
|
22994
|
+
function calculateYAxisMax(values) {
|
|
22995
|
+
const maxValue = Math.max(...values, 0);
|
|
22996
|
+
if (config.domain === "temperature") {
|
|
22997
|
+
const tempConfig = config.temperatureConfig;
|
|
22998
|
+
if (tempConfig?.clampRange) {
|
|
22999
|
+
return tempConfig.clampRange.max;
|
|
23000
|
+
}
|
|
23001
|
+
const maxWithThreshold = Math.max(
|
|
23002
|
+
maxValue,
|
|
23003
|
+
tempConfig?.maxThreshold?.value ?? 0,
|
|
23004
|
+
tempConfig?.idealRange?.max ?? 0
|
|
23005
|
+
);
|
|
23006
|
+
return Math.ceil(maxWithThreshold + 5);
|
|
23007
|
+
}
|
|
23008
|
+
if (maxValue === 0) {
|
|
23009
|
+
return config.thresholdForLargeUnit ? config.thresholdForLargeUnit / 2 : 500;
|
|
23010
|
+
}
|
|
23011
|
+
let roundTo;
|
|
23012
|
+
if (config.thresholdForLargeUnit && maxValue >= config.thresholdForLargeUnit) {
|
|
23013
|
+
roundTo = config.thresholdForLargeUnit / 10;
|
|
23014
|
+
} else if (maxValue >= 1e3) {
|
|
23015
|
+
roundTo = 100;
|
|
23016
|
+
} else if (maxValue >= 100) {
|
|
23017
|
+
roundTo = 50;
|
|
23018
|
+
} else if (maxValue >= 10) {
|
|
23019
|
+
roundTo = 10;
|
|
23020
|
+
} else {
|
|
23021
|
+
roundTo = 5;
|
|
23022
|
+
}
|
|
23023
|
+
return Math.ceil(maxValue * 1.1 / roundTo) * roundTo;
|
|
23024
|
+
}
|
|
23025
|
+
function calculateYAxisMin(values) {
|
|
23026
|
+
if (config.domain !== "temperature") {
|
|
23027
|
+
return 0;
|
|
23028
|
+
}
|
|
23029
|
+
const tempConfig = config.temperatureConfig;
|
|
23030
|
+
if (tempConfig?.clampRange) {
|
|
23031
|
+
return tempConfig.clampRange.min;
|
|
23032
|
+
}
|
|
23033
|
+
const minValue = Math.min(...values);
|
|
23034
|
+
const minWithThreshold = Math.min(
|
|
23035
|
+
minValue,
|
|
23036
|
+
tempConfig?.minThreshold?.value ?? minValue,
|
|
23037
|
+
tempConfig?.idealRange?.min ?? minValue
|
|
23038
|
+
);
|
|
23039
|
+
return Math.floor(minWithThreshold - 5);
|
|
23040
|
+
}
|
|
23041
|
+
function buildTemperatureAnnotations() {
|
|
23042
|
+
const tempConfig = config.temperatureConfig;
|
|
23043
|
+
if (!tempConfig || config.domain !== "temperature") {
|
|
23044
|
+
return {};
|
|
23045
|
+
}
|
|
23046
|
+
const annotations = {};
|
|
23047
|
+
const createLineAnnotation = (line, id) => {
|
|
23048
|
+
const borderDash = line.lineStyle === "dashed" ? [6, 6] : line.lineStyle === "dotted" ? [2, 2] : [];
|
|
23049
|
+
return {
|
|
23050
|
+
type: "line",
|
|
23051
|
+
yMin: line.value,
|
|
23052
|
+
yMax: line.value,
|
|
23053
|
+
borderColor: line.color,
|
|
23054
|
+
borderWidth: line.lineWidth ?? 2,
|
|
23055
|
+
borderDash,
|
|
23056
|
+
label: {
|
|
23057
|
+
display: true,
|
|
23058
|
+
content: line.label,
|
|
23059
|
+
position: "end",
|
|
23060
|
+
backgroundColor: line.color,
|
|
23061
|
+
color: "#fff",
|
|
23062
|
+
font: { size: 10, weight: "bold" },
|
|
23063
|
+
padding: { x: 4, y: 2 }
|
|
23064
|
+
}
|
|
23065
|
+
};
|
|
23066
|
+
};
|
|
23067
|
+
if (tempConfig.minThreshold) {
|
|
23068
|
+
annotations["minThreshold"] = createLineAnnotation(tempConfig.minThreshold, "minThreshold");
|
|
23069
|
+
}
|
|
23070
|
+
if (tempConfig.maxThreshold) {
|
|
23071
|
+
annotations["maxThreshold"] = createLineAnnotation(tempConfig.maxThreshold, "maxThreshold");
|
|
23072
|
+
}
|
|
23073
|
+
if (tempConfig.idealRange) {
|
|
23074
|
+
annotations["idealRange"] = {
|
|
23075
|
+
type: "box",
|
|
23076
|
+
yMin: tempConfig.idealRange.min,
|
|
23077
|
+
yMax: tempConfig.idealRange.max,
|
|
23078
|
+
backgroundColor: tempConfig.idealRange.color,
|
|
23079
|
+
borderWidth: 0,
|
|
23080
|
+
label: tempConfig.idealRange.label ? {
|
|
23081
|
+
display: true,
|
|
23082
|
+
content: tempConfig.idealRange.label,
|
|
23083
|
+
position: { x: "start", y: "center" },
|
|
23084
|
+
color: "#666",
|
|
23085
|
+
font: { size: 10 }
|
|
23086
|
+
} : void 0
|
|
23087
|
+
};
|
|
23088
|
+
}
|
|
23089
|
+
return annotations;
|
|
23090
|
+
}
|
|
23091
|
+
function buildIdealRangeAnnotation() {
|
|
23092
|
+
if (!currentIdealRange) {
|
|
23093
|
+
return {};
|
|
23094
|
+
}
|
|
23095
|
+
const { min, max, enabled = true } = currentIdealRange;
|
|
23096
|
+
if (!enabled || min === 0 && max === 0 || min >= max) {
|
|
23097
|
+
return {};
|
|
23098
|
+
}
|
|
23099
|
+
const defaultColors = {
|
|
23100
|
+
temperature: { bg: "rgba(34, 197, 94, 0.15)", border: "rgba(34, 197, 94, 0.4)" },
|
|
23101
|
+
energy: { bg: "rgba(37, 99, 235, 0.1)", border: "rgba(37, 99, 235, 0.3)" },
|
|
23102
|
+
water: { bg: "rgba(2, 136, 209, 0.1)", border: "rgba(2, 136, 209, 0.3)" },
|
|
23103
|
+
gas: { bg: "rgba(234, 88, 12, 0.1)", border: "rgba(234, 88, 12, 0.3)" }
|
|
23104
|
+
};
|
|
23105
|
+
const domainDefaults = defaultColors[config.domain] || defaultColors.energy;
|
|
23106
|
+
return {
|
|
23107
|
+
idealRangeBox: {
|
|
23108
|
+
type: "box",
|
|
23109
|
+
yMin: min,
|
|
23110
|
+
yMax: max,
|
|
23111
|
+
backgroundColor: currentIdealRange.color || domainDefaults.bg,
|
|
23112
|
+
borderColor: currentIdealRange.borderColor || domainDefaults.border,
|
|
23113
|
+
borderWidth: 1,
|
|
23114
|
+
label: currentIdealRange.label ? {
|
|
23115
|
+
display: true,
|
|
23116
|
+
content: currentIdealRange.label,
|
|
23117
|
+
position: { x: "start", y: "center" },
|
|
23118
|
+
color: "#666",
|
|
23119
|
+
font: { size: 10, style: "italic" },
|
|
23120
|
+
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
|
23121
|
+
padding: { x: 4, y: 2 }
|
|
23122
|
+
} : void 0
|
|
23123
|
+
}
|
|
23124
|
+
};
|
|
23125
|
+
}
|
|
23126
|
+
function formatValue(value, includeUnit = true) {
|
|
23127
|
+
const decimals = config.decimalPlaces ?? DEFAULT_CONFIG.decimalPlaces;
|
|
23128
|
+
if (config.unitLarge && config.thresholdForLargeUnit && value >= config.thresholdForLargeUnit) {
|
|
23129
|
+
const converted = value / config.thresholdForLargeUnit;
|
|
23130
|
+
return includeUnit ? `${converted.toFixed(decimals)} ${config.unitLarge}` : converted.toFixed(decimals);
|
|
23131
|
+
}
|
|
23132
|
+
return includeUnit ? `${value.toFixed(decimals)} ${config.unit}` : value.toFixed(decimals);
|
|
23133
|
+
}
|
|
23134
|
+
function formatTickValue(value) {
|
|
23135
|
+
if (config.unitLarge && config.thresholdForLargeUnit && value >= config.thresholdForLargeUnit) {
|
|
23136
|
+
return `${(value / config.thresholdForLargeUnit).toFixed(1)}`;
|
|
23137
|
+
}
|
|
23138
|
+
return value.toFixed(0);
|
|
23139
|
+
}
|
|
23140
|
+
function buildChartConfig(data) {
|
|
23141
|
+
const yAxisMax = calculateYAxisMax(data.dailyTotals);
|
|
23142
|
+
const yAxisMin = calculateYAxisMin(data.dailyTotals);
|
|
23143
|
+
const tension = config.lineTension ?? DEFAULT_CONFIG.lineTension;
|
|
23144
|
+
const pointRadius = config.pointRadius ?? DEFAULT_CONFIG.pointRadius;
|
|
23145
|
+
const borderWidth = config.borderWidth ?? DEFAULT_CONFIG.borderWidth;
|
|
23146
|
+
const fill = config.fill ?? DEFAULT_CONFIG.fill;
|
|
23147
|
+
const showLegend = config.showLegend ?? DEFAULT_CONFIG.showLegend;
|
|
23148
|
+
const themeColors = THEME_COLORS[currentTheme];
|
|
23149
|
+
const isTemperature = config.domain === "temperature";
|
|
23150
|
+
let datasets;
|
|
23151
|
+
if (currentVizMode === "separate" && data.shoppingData && data.shoppingNames) {
|
|
23152
|
+
const shoppingColors = [
|
|
23153
|
+
"#2563eb",
|
|
23154
|
+
"#16a34a",
|
|
23155
|
+
"#ea580c",
|
|
23156
|
+
"#dc2626",
|
|
23157
|
+
"#8b5cf6",
|
|
23158
|
+
"#0891b2",
|
|
23159
|
+
"#65a30d",
|
|
23160
|
+
"#d97706",
|
|
23161
|
+
"#be185d",
|
|
23162
|
+
"#0d9488"
|
|
23163
|
+
];
|
|
23164
|
+
datasets = Object.entries(data.shoppingData).map(([shoppingId, values], index) => ({
|
|
23165
|
+
label: data.shoppingNames?.[shoppingId] || shoppingId,
|
|
23166
|
+
data: values,
|
|
23167
|
+
borderColor: shoppingColors[index % shoppingColors.length],
|
|
23168
|
+
backgroundColor: currentChartType === "line" ? `${shoppingColors[index % shoppingColors.length]}20` : shoppingColors[index % shoppingColors.length],
|
|
23169
|
+
fill: currentChartType === "line" && fill,
|
|
23170
|
+
tension,
|
|
23171
|
+
borderWidth,
|
|
23172
|
+
pointRadius: currentChartType === "line" ? pointRadius : 0,
|
|
23173
|
+
pointBackgroundColor: shoppingColors[index % shoppingColors.length],
|
|
23174
|
+
pointBorderColor: "#fff",
|
|
23175
|
+
pointBorderWidth: 2
|
|
23176
|
+
}));
|
|
23177
|
+
} else {
|
|
23178
|
+
const datasetLabel = isTemperature ? `Temperatura (${config.unit})` : `Consumo (${config.unit})`;
|
|
23179
|
+
datasets = [
|
|
23180
|
+
{
|
|
23181
|
+
label: datasetLabel,
|
|
23182
|
+
data: data.dailyTotals,
|
|
23183
|
+
borderColor: colors.primary,
|
|
23184
|
+
backgroundColor: currentChartType === "line" ? colors.background : colors.primary,
|
|
23185
|
+
fill: currentChartType === "line" && fill,
|
|
23186
|
+
tension,
|
|
23187
|
+
borderWidth,
|
|
23188
|
+
pointRadius: currentChartType === "line" ? pointRadius : 0,
|
|
23189
|
+
pointBackgroundColor: colors.pointBackground || colors.primary,
|
|
23190
|
+
pointBorderColor: colors.pointBorder || "#fff",
|
|
23191
|
+
pointBorderWidth: 2,
|
|
23192
|
+
borderRadius: currentChartType === "bar" ? 4 : 0
|
|
23193
|
+
}
|
|
23194
|
+
];
|
|
23195
|
+
}
|
|
23196
|
+
const temperatureAnnotations = buildTemperatureAnnotations();
|
|
23197
|
+
const idealRangeAnnotations = buildIdealRangeAnnotation();
|
|
23198
|
+
const allAnnotations = { ...temperatureAnnotations, ...idealRangeAnnotations };
|
|
23199
|
+
const yAxisLabel = config.unitLarge && config.thresholdForLargeUnit && yAxisMax >= config.thresholdForLargeUnit ? config.unitLarge : config.unit;
|
|
23200
|
+
return {
|
|
23201
|
+
type: currentChartType,
|
|
23202
|
+
data: {
|
|
23203
|
+
labels: data.labels,
|
|
23204
|
+
datasets
|
|
23205
|
+
},
|
|
23206
|
+
options: {
|
|
23207
|
+
responsive: true,
|
|
23208
|
+
maintainAspectRatio: false,
|
|
23209
|
+
animation: false,
|
|
23210
|
+
// CRITICAL: Prevents infinite growth bug
|
|
23211
|
+
plugins: {
|
|
23212
|
+
legend: {
|
|
23213
|
+
display: showLegend || currentVizMode === "separate",
|
|
23214
|
+
position: "bottom",
|
|
23215
|
+
labels: {
|
|
23216
|
+
color: themeColors.text
|
|
23217
|
+
}
|
|
23218
|
+
},
|
|
23219
|
+
tooltip: {
|
|
23220
|
+
backgroundColor: themeColors.tooltipBackground,
|
|
23221
|
+
titleColor: themeColors.tooltipText,
|
|
23222
|
+
bodyColor: themeColors.tooltipText,
|
|
23223
|
+
borderColor: themeColors.border,
|
|
23224
|
+
borderWidth: 1,
|
|
23225
|
+
callbacks: {
|
|
23226
|
+
label: function(context) {
|
|
23227
|
+
const value = context.parsed.y || 0;
|
|
23228
|
+
const label = context.dataset.label || "";
|
|
23229
|
+
return `${label}: ${formatValue(value)}`;
|
|
23230
|
+
}
|
|
23231
|
+
}
|
|
23232
|
+
},
|
|
23233
|
+
// Reference lines and ideal range (requires chartjs-plugin-annotation)
|
|
23234
|
+
annotation: Object.keys(allAnnotations).length > 0 ? {
|
|
23235
|
+
annotations: allAnnotations
|
|
23236
|
+
} : void 0
|
|
23237
|
+
},
|
|
23238
|
+
scales: {
|
|
23239
|
+
y: {
|
|
23240
|
+
beginAtZero: !isTemperature,
|
|
23241
|
+
// Temperature can have negative values
|
|
23242
|
+
min: yAxisMin,
|
|
23243
|
+
max: yAxisMax,
|
|
23244
|
+
// CRITICAL: Fixed max prevents animation loop
|
|
23245
|
+
grid: {
|
|
23246
|
+
color: themeColors.grid
|
|
23247
|
+
},
|
|
23248
|
+
title: {
|
|
23249
|
+
display: true,
|
|
23250
|
+
text: yAxisLabel,
|
|
23251
|
+
font: { size: 12 },
|
|
23252
|
+
color: themeColors.text
|
|
23253
|
+
},
|
|
23254
|
+
ticks: {
|
|
23255
|
+
font: { size: 11 },
|
|
23256
|
+
color: themeColors.textMuted,
|
|
23257
|
+
callback: function(value) {
|
|
23258
|
+
return formatTickValue(value);
|
|
23259
|
+
}
|
|
23260
|
+
}
|
|
23261
|
+
},
|
|
23262
|
+
x: {
|
|
23263
|
+
grid: {
|
|
23264
|
+
color: themeColors.grid
|
|
23265
|
+
},
|
|
23266
|
+
ticks: {
|
|
23267
|
+
font: { size: 11 },
|
|
23268
|
+
color: themeColors.textMuted
|
|
23269
|
+
}
|
|
23270
|
+
}
|
|
23271
|
+
}
|
|
23272
|
+
}
|
|
23273
|
+
};
|
|
23274
|
+
}
|
|
23275
|
+
function validateChartJs() {
|
|
23276
|
+
if (typeof Chart === "undefined") {
|
|
23277
|
+
log("error", "Chart.js not loaded. Cannot initialize chart.");
|
|
23278
|
+
config.onError?.(new Error("Chart.js not loaded"));
|
|
23279
|
+
return false;
|
|
23280
|
+
}
|
|
23281
|
+
return true;
|
|
23282
|
+
}
|
|
23283
|
+
function validateCanvas() {
|
|
23284
|
+
const canvas = $id(config.containerId);
|
|
23285
|
+
if (!canvas) {
|
|
23286
|
+
log("error", `Canvas #${config.containerId} not found`);
|
|
23287
|
+
config.onError?.(new Error(`Canvas #${config.containerId} not found`));
|
|
23288
|
+
return null;
|
|
23289
|
+
}
|
|
23290
|
+
return canvas;
|
|
23291
|
+
}
|
|
23292
|
+
function setupAutoRefresh() {
|
|
23293
|
+
if (config.autoRefreshInterval && config.autoRefreshInterval > 0) {
|
|
23294
|
+
if (autoRefreshTimer) {
|
|
23295
|
+
clearInterval(autoRefreshTimer);
|
|
23296
|
+
}
|
|
23297
|
+
autoRefreshTimer = setInterval(async () => {
|
|
23298
|
+
log("log", "Auto-refreshing data...");
|
|
23299
|
+
await instance.refresh(true);
|
|
23300
|
+
}, config.autoRefreshInterval);
|
|
23301
|
+
}
|
|
23302
|
+
}
|
|
23303
|
+
function cleanupAutoRefresh() {
|
|
23304
|
+
if (autoRefreshTimer) {
|
|
23305
|
+
clearInterval(autoRefreshTimer);
|
|
23306
|
+
autoRefreshTimer = null;
|
|
23307
|
+
}
|
|
23308
|
+
}
|
|
23309
|
+
function setupButtonHandlers() {
|
|
23310
|
+
if (config.settingsButtonId && config.onSettingsClick) {
|
|
23311
|
+
const settingsBtn = $id(config.settingsButtonId);
|
|
23312
|
+
if (settingsBtn) {
|
|
23313
|
+
settingsBtn.addEventListener("click", () => {
|
|
23314
|
+
log("log", "Settings button clicked");
|
|
23315
|
+
config.onSettingsClick?.();
|
|
23316
|
+
});
|
|
23317
|
+
log("log", "Settings button handler attached");
|
|
23318
|
+
}
|
|
23319
|
+
}
|
|
23320
|
+
if (config.maximizeButtonId && config.onMaximizeClick) {
|
|
23321
|
+
const maximizeBtn = $id(config.maximizeButtonId);
|
|
23322
|
+
if (maximizeBtn) {
|
|
23323
|
+
maximizeBtn.addEventListener("click", () => {
|
|
23324
|
+
log("log", "Maximize button clicked");
|
|
23325
|
+
config.onMaximizeClick?.();
|
|
23326
|
+
});
|
|
23327
|
+
log("log", "Maximize button handler attached");
|
|
23328
|
+
}
|
|
23329
|
+
}
|
|
23330
|
+
const enableExport = config.enableExport ?? DEFAULT_CONFIG.enableExport;
|
|
23331
|
+
if (enableExport && config.exportButtonId) {
|
|
23332
|
+
const exportBtn = $id(config.exportButtonId);
|
|
23333
|
+
if (exportBtn) {
|
|
23334
|
+
exportBtn.addEventListener("click", () => {
|
|
23335
|
+
log("log", "Export button clicked");
|
|
23336
|
+
if (config.onExportCSV && cachedData) {
|
|
23337
|
+
config.onExportCSV(cachedData);
|
|
23338
|
+
} else {
|
|
23339
|
+
instance.exportCSV();
|
|
23340
|
+
}
|
|
23341
|
+
});
|
|
23342
|
+
log("log", "Export button handler attached");
|
|
23343
|
+
}
|
|
23344
|
+
}
|
|
23345
|
+
}
|
|
23346
|
+
function generateCSVContent(data) {
|
|
23347
|
+
const rows = [];
|
|
23348
|
+
const decimals = config.decimalPlaces ?? DEFAULT_CONFIG.decimalPlaces;
|
|
23349
|
+
if (currentVizMode === "separate" && data.shoppingData && data.shoppingNames) {
|
|
23350
|
+
const shoppingHeaders = Object.keys(data.shoppingData).map(
|
|
23351
|
+
(id) => data.shoppingNames?.[id] || id
|
|
23352
|
+
);
|
|
23353
|
+
rows.push(["Data", ...shoppingHeaders, "Total"].join(";"));
|
|
23354
|
+
data.labels.forEach((label, index) => {
|
|
23355
|
+
const shoppingValues = Object.keys(data.shoppingData).map(
|
|
23356
|
+
(id) => data.shoppingData[id][index].toFixed(decimals)
|
|
23357
|
+
);
|
|
23358
|
+
rows.push([label, ...shoppingValues, data.dailyTotals[index].toFixed(decimals)].join(";"));
|
|
23359
|
+
});
|
|
23360
|
+
} else {
|
|
23361
|
+
rows.push(["Data", `Consumo (${config.unit})`].join(";"));
|
|
23362
|
+
data.labels.forEach((label, index) => {
|
|
23363
|
+
rows.push([label, data.dailyTotals[index].toFixed(decimals)].join(";"));
|
|
23364
|
+
});
|
|
23365
|
+
}
|
|
23366
|
+
const total = data.dailyTotals.reduce((sum, v) => sum + v, 0);
|
|
23367
|
+
const avg = total / data.dailyTotals.length;
|
|
23368
|
+
rows.push("");
|
|
23369
|
+
rows.push(["Total", total.toFixed(decimals)].join(";"));
|
|
23370
|
+
rows.push(["M\xE9dia", avg.toFixed(decimals)].join(";"));
|
|
23371
|
+
return rows.join("\n");
|
|
23372
|
+
}
|
|
23373
|
+
function downloadCSV(content, filename) {
|
|
23374
|
+
const BOM = "\uFEFF";
|
|
23375
|
+
const blob = new Blob([BOM + content], { type: "text/csv;charset=utf-8" });
|
|
23376
|
+
const url = URL.createObjectURL(blob);
|
|
23377
|
+
const link = document.createElement("a");
|
|
23378
|
+
link.href = url;
|
|
23379
|
+
link.download = `${filename}.csv`;
|
|
23380
|
+
document.body.appendChild(link);
|
|
23381
|
+
link.click();
|
|
23382
|
+
document.body.removeChild(link);
|
|
23383
|
+
URL.revokeObjectURL(url);
|
|
23384
|
+
log("log", `CSV exported: ${filename}.csv`);
|
|
23385
|
+
}
|
|
23386
|
+
function updateTitle() {
|
|
23387
|
+
if (config.titleElementId) {
|
|
23388
|
+
const titleEl = $id(config.titleElementId);
|
|
23389
|
+
if (titleEl) {
|
|
23390
|
+
if (currentPeriod === 0) {
|
|
23391
|
+
titleEl.textContent = `Consumo - Per\xEDodo Personalizado`;
|
|
23392
|
+
} else {
|
|
23393
|
+
titleEl.textContent = `Consumo dos \xFAltimos ${currentPeriod} dias`;
|
|
23394
|
+
}
|
|
23395
|
+
}
|
|
23396
|
+
}
|
|
23397
|
+
}
|
|
23398
|
+
const instance = {
|
|
23399
|
+
async render() {
|
|
23400
|
+
log("log", "Rendering chart...");
|
|
23401
|
+
if (!validateChartJs()) return;
|
|
23402
|
+
const canvas = validateCanvas();
|
|
23403
|
+
if (!canvas) return;
|
|
23404
|
+
try {
|
|
23405
|
+
log("log", `Fetching ${currentPeriod} days of data...`);
|
|
23406
|
+
cachedData = await config.fetchData(currentPeriod);
|
|
23407
|
+
cachedData.fetchTimestamp = Date.now();
|
|
23408
|
+
if (config.onBeforeRender) {
|
|
23409
|
+
cachedData = config.onBeforeRender(cachedData);
|
|
23410
|
+
}
|
|
23411
|
+
if (chartInstance) {
|
|
23412
|
+
chartInstance.destroy();
|
|
23413
|
+
chartInstance = null;
|
|
23414
|
+
}
|
|
23415
|
+
const ctx = canvas.getContext("2d");
|
|
23416
|
+
const chartConfig = buildChartConfig(cachedData);
|
|
23417
|
+
chartInstance = new Chart(ctx, chartConfig);
|
|
23418
|
+
isRendered = true;
|
|
23419
|
+
const yAxisMax = calculateYAxisMax(cachedData.dailyTotals);
|
|
23420
|
+
log("log", `Chart initialized with yAxisMax: ${yAxisMax}`);
|
|
23421
|
+
config.onDataLoaded?.(cachedData);
|
|
23422
|
+
config.onAfterRender?.(chartInstance);
|
|
23423
|
+
setupButtonHandlers();
|
|
23424
|
+
updateTitle();
|
|
23425
|
+
setupAutoRefresh();
|
|
23426
|
+
} catch (error) {
|
|
23427
|
+
log("error", "Failed to render chart:", error);
|
|
23428
|
+
config.onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
23429
|
+
}
|
|
23430
|
+
},
|
|
23431
|
+
async update(data) {
|
|
23432
|
+
if (data) {
|
|
23433
|
+
cachedData = data;
|
|
23434
|
+
cachedData.fetchTimestamp = Date.now();
|
|
23435
|
+
}
|
|
23436
|
+
if (!chartInstance || !cachedData) {
|
|
23437
|
+
log("warn", "Cannot update: chart not initialized or no data");
|
|
23438
|
+
return;
|
|
23439
|
+
}
|
|
23440
|
+
let renderData = cachedData;
|
|
23441
|
+
if (config.onBeforeRender) {
|
|
23442
|
+
renderData = config.onBeforeRender(cachedData);
|
|
23443
|
+
}
|
|
23444
|
+
const chartConfig = buildChartConfig(renderData);
|
|
23445
|
+
chartInstance.data = chartConfig.data;
|
|
23446
|
+
chartInstance.options = chartConfig.options;
|
|
23447
|
+
chartInstance.update("none");
|
|
23448
|
+
log("log", "Chart updated");
|
|
23449
|
+
},
|
|
23450
|
+
setChartType(type) {
|
|
23451
|
+
if (currentChartType === type) return;
|
|
23452
|
+
log("log", `Changing chart type to: ${type}`);
|
|
23453
|
+
currentChartType = type;
|
|
23454
|
+
if (cachedData && chartInstance) {
|
|
23455
|
+
const canvas = validateCanvas();
|
|
23456
|
+
if (canvas) {
|
|
23457
|
+
chartInstance.destroy();
|
|
23458
|
+
const ctx = canvas.getContext("2d");
|
|
23459
|
+
chartInstance = new Chart(ctx, buildChartConfig(cachedData));
|
|
23460
|
+
}
|
|
23461
|
+
}
|
|
23462
|
+
},
|
|
23463
|
+
setVizMode(mode) {
|
|
23464
|
+
if (currentVizMode === mode) return;
|
|
23465
|
+
log("log", `Changing viz mode to: ${mode}`);
|
|
23466
|
+
currentVizMode = mode;
|
|
23467
|
+
if (cachedData) {
|
|
23468
|
+
this.update();
|
|
23469
|
+
}
|
|
23470
|
+
},
|
|
23471
|
+
async setPeriod(days) {
|
|
23472
|
+
if (currentPeriod === days) return;
|
|
23473
|
+
log("log", `Changing period to: ${days} days`);
|
|
23474
|
+
currentPeriod = days;
|
|
23475
|
+
updateTitle();
|
|
23476
|
+
await this.refresh(true);
|
|
23477
|
+
},
|
|
23478
|
+
async refresh(forceRefresh = false) {
|
|
23479
|
+
if (!forceRefresh && cachedData?.fetchTimestamp) {
|
|
23480
|
+
const age = Date.now() - cachedData.fetchTimestamp;
|
|
23481
|
+
const ttl = config.cacheTTL ?? DEFAULT_CONFIG.cacheTTL;
|
|
23482
|
+
if (age < ttl) {
|
|
23483
|
+
log("log", `Using cached data (age: ${Math.round(age / 1e3)}s)`);
|
|
23484
|
+
return;
|
|
23485
|
+
}
|
|
23486
|
+
}
|
|
23487
|
+
log("log", "Refreshing data...");
|
|
23488
|
+
await this.render();
|
|
23489
|
+
},
|
|
23490
|
+
destroy() {
|
|
23491
|
+
log("log", "Destroying chart...");
|
|
23492
|
+
cleanupAutoRefresh();
|
|
23493
|
+
if (chartInstance) {
|
|
23494
|
+
chartInstance.destroy();
|
|
23495
|
+
chartInstance = null;
|
|
23496
|
+
}
|
|
23497
|
+
cachedData = null;
|
|
23498
|
+
isRendered = false;
|
|
23499
|
+
},
|
|
23500
|
+
getChartInstance() {
|
|
23501
|
+
return chartInstance;
|
|
23502
|
+
},
|
|
23503
|
+
getCachedData() {
|
|
23504
|
+
return cachedData;
|
|
23505
|
+
},
|
|
23506
|
+
getState() {
|
|
23507
|
+
return {
|
|
23508
|
+
period: currentPeriod,
|
|
23509
|
+
chartType: currentChartType,
|
|
23510
|
+
vizMode: currentVizMode,
|
|
23511
|
+
theme: currentTheme,
|
|
23512
|
+
isRendered
|
|
23513
|
+
};
|
|
23514
|
+
},
|
|
23515
|
+
exportCSV(filename) {
|
|
23516
|
+
if (!cachedData) {
|
|
23517
|
+
log("warn", "Cannot export: no data available");
|
|
23518
|
+
return;
|
|
23519
|
+
}
|
|
23520
|
+
const defaultFilename = config.exportFilename || `${config.domain}-consumo-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
|
|
23521
|
+
const csvContent = generateCSVContent(cachedData);
|
|
23522
|
+
downloadCSV(csvContent, filename || defaultFilename);
|
|
23523
|
+
},
|
|
23524
|
+
setTheme(theme) {
|
|
23525
|
+
if (currentTheme === theme) return;
|
|
23526
|
+
log("log", `Changing theme to: ${theme}`);
|
|
23527
|
+
currentTheme = theme;
|
|
23528
|
+
if (cachedData && chartInstance) {
|
|
23529
|
+
const canvas = validateCanvas();
|
|
23530
|
+
if (canvas) {
|
|
23531
|
+
chartInstance.destroy();
|
|
23532
|
+
const ctx = canvas.getContext("2d");
|
|
23533
|
+
chartInstance = new Chart(ctx, buildChartConfig(cachedData));
|
|
23534
|
+
}
|
|
23535
|
+
}
|
|
23536
|
+
},
|
|
23537
|
+
setIdealRange(range) {
|
|
23538
|
+
const rangeChanged = JSON.stringify(currentIdealRange) !== JSON.stringify(range);
|
|
23539
|
+
if (!rangeChanged) return;
|
|
23540
|
+
if (range) {
|
|
23541
|
+
log("log", `Setting ideal range: ${range.min} - ${range.max}`);
|
|
23542
|
+
} else {
|
|
23543
|
+
log("log", "Clearing ideal range");
|
|
23544
|
+
}
|
|
23545
|
+
currentIdealRange = range;
|
|
23546
|
+
if (cachedData && chartInstance) {
|
|
23547
|
+
const canvas = validateCanvas();
|
|
23548
|
+
if (canvas) {
|
|
23549
|
+
chartInstance.destroy();
|
|
23550
|
+
const ctx = canvas.getContext("2d");
|
|
23551
|
+
chartInstance = new Chart(ctx, buildChartConfig(cachedData));
|
|
23552
|
+
}
|
|
23553
|
+
}
|
|
23554
|
+
},
|
|
23555
|
+
getIdealRange() {
|
|
23556
|
+
return currentIdealRange;
|
|
23557
|
+
}
|
|
23558
|
+
};
|
|
23559
|
+
return instance;
|
|
23560
|
+
}
|
|
23561
|
+
|
|
23562
|
+
// src/components/Consumption7DaysChart/createConsumptionModal.ts
|
|
23563
|
+
var DOMAIN_CONFIG3 = {
|
|
23564
|
+
energy: { name: "Energia", icon: "\u26A1" },
|
|
23565
|
+
water: { name: "\xC1gua", icon: "\u{1F4A7}" },
|
|
23566
|
+
gas: { name: "G\xE1s", icon: "\u{1F525}" },
|
|
23567
|
+
temperature: { name: "Temperatura", icon: "\u{1F321}\uFE0F" }
|
|
23568
|
+
};
|
|
23569
|
+
function createConsumptionModal(config) {
|
|
23570
|
+
const modalId = `myio-consumption-modal-${Date.now()}`;
|
|
23571
|
+
let modalElement = null;
|
|
23572
|
+
let chartInstance = null;
|
|
23573
|
+
let headerInstance = null;
|
|
23574
|
+
let currentTheme = config.theme ?? "light";
|
|
23575
|
+
let currentChartType = config.defaultChartType ?? "line";
|
|
23576
|
+
let currentVizMode = config.defaultVizMode ?? "total";
|
|
23577
|
+
let isMaximized = false;
|
|
23578
|
+
const domainCfg = DOMAIN_CONFIG3[config.domain] || { name: config.domain, icon: "\u{1F4CA}" };
|
|
23579
|
+
const title = config.title || `${domainCfg.name} - Hist\xF3rico de Consumo`;
|
|
23580
|
+
function getThemeColors2() {
|
|
23581
|
+
return THEME_COLORS[currentTheme];
|
|
23582
|
+
}
|
|
23583
|
+
function renderModal4() {
|
|
23584
|
+
const colors = getThemeColors2();
|
|
23585
|
+
const exportFormats = config.exportFormats || ["csv"];
|
|
23586
|
+
headerInstance = createModalHeader({
|
|
23587
|
+
id: modalId,
|
|
23588
|
+
title,
|
|
23589
|
+
icon: domainCfg.icon,
|
|
23590
|
+
theme: currentTheme,
|
|
23591
|
+
isMaximized,
|
|
23592
|
+
exportFormats,
|
|
23593
|
+
onExport: (format) => {
|
|
23594
|
+
if (config.onExport) {
|
|
23595
|
+
config.onExport(format);
|
|
23596
|
+
} else {
|
|
23597
|
+
if (format === "csv") {
|
|
23598
|
+
chartInstance?.exportCSV();
|
|
23599
|
+
} else {
|
|
23600
|
+
console.warn(`[ConsumptionModal] Export format "${format}" requires custom onExport handler`);
|
|
23601
|
+
}
|
|
23602
|
+
}
|
|
23603
|
+
},
|
|
23604
|
+
onThemeToggle: (theme) => {
|
|
23605
|
+
currentTheme = theme;
|
|
23606
|
+
chartInstance?.setTheme(currentTheme);
|
|
23607
|
+
updateModal();
|
|
23608
|
+
},
|
|
23609
|
+
onMaximize: (maximized) => {
|
|
23610
|
+
isMaximized = maximized;
|
|
23611
|
+
updateModal();
|
|
23612
|
+
},
|
|
23613
|
+
onClose: () => {
|
|
23614
|
+
instance.close();
|
|
23615
|
+
}
|
|
23616
|
+
});
|
|
23617
|
+
return `
|
|
23618
|
+
<div class="myio-consumption-modal-overlay" style="
|
|
23619
|
+
position: fixed;
|
|
23620
|
+
top: 0;
|
|
23621
|
+
left: 0;
|
|
23622
|
+
width: 100%;
|
|
23623
|
+
height: 100%;
|
|
23624
|
+
background: rgba(0, 0, 0, 0.5);
|
|
23625
|
+
backdrop-filter: blur(2px);
|
|
23626
|
+
z-index: 99998;
|
|
23627
|
+
display: flex;
|
|
23628
|
+
justify-content: center;
|
|
23629
|
+
align-items: center;
|
|
23630
|
+
">
|
|
23631
|
+
<div class="myio-consumption-modal-content" style="
|
|
23632
|
+
background: ${colors.chartBackground};
|
|
23633
|
+
border-radius: ${isMaximized ? "0" : "10px"};
|
|
23634
|
+
width: ${isMaximized ? "100%" : "95%"};
|
|
23635
|
+
max-width: ${isMaximized ? "100%" : "1200px"};
|
|
23636
|
+
height: ${isMaximized ? "100%" : "85vh"};
|
|
23637
|
+
display: flex;
|
|
23638
|
+
flex-direction: column;
|
|
23639
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
23640
|
+
overflow: hidden;
|
|
23641
|
+
">
|
|
23642
|
+
<!-- MyIO Premium Header (using ModalHeader component) -->
|
|
23643
|
+
${headerInstance.render()}
|
|
23644
|
+
|
|
23645
|
+
<!-- Controls Bar -->
|
|
23646
|
+
<div class="myio-consumption-modal-controls" style="
|
|
23647
|
+
display: flex;
|
|
23648
|
+
gap: 16px;
|
|
23649
|
+
padding: 12px 16px;
|
|
23650
|
+
background: ${currentTheme === "dark" ? "#374151" : "#f7f7f7"};
|
|
23651
|
+
border-bottom: 1px solid ${colors.border};
|
|
23652
|
+
align-items: center;
|
|
23653
|
+
flex-wrap: wrap;
|
|
23654
|
+
">
|
|
23655
|
+
<!-- Viz Mode Tabs -->
|
|
23656
|
+
<div style="display: flex; gap: 2px; background: ${currentTheme === "dark" ? "#4b5563" : "#e5e7eb"}; border-radius: 8px; padding: 2px;">
|
|
23657
|
+
<button id="${modalId}-viz-total" style="
|
|
23658
|
+
padding: 6px 12px;
|
|
23659
|
+
border: none;
|
|
23660
|
+
border-radius: 6px;
|
|
23661
|
+
font-size: 13px;
|
|
23662
|
+
cursor: pointer;
|
|
23663
|
+
transition: all 0.2s;
|
|
23664
|
+
background: ${currentVizMode === "total" ? "#3e1a7d" : "transparent"};
|
|
23665
|
+
color: ${currentVizMode === "total" ? "white" : colors.text};
|
|
23666
|
+
">Consolidado</button>
|
|
23667
|
+
<button id="${modalId}-viz-separate" style="
|
|
23668
|
+
padding: 6px 12px;
|
|
23669
|
+
border: none;
|
|
23670
|
+
border-radius: 6px;
|
|
23671
|
+
font-size: 13px;
|
|
23672
|
+
cursor: pointer;
|
|
23673
|
+
transition: all 0.2s;
|
|
23674
|
+
background: ${currentVizMode === "separate" ? "#3e1a7d" : "transparent"};
|
|
23675
|
+
color: ${currentVizMode === "separate" ? "white" : colors.text};
|
|
23676
|
+
">Por Shopping</button>
|
|
23677
|
+
</div>
|
|
23678
|
+
|
|
23679
|
+
<!-- Chart Type Tabs -->
|
|
23680
|
+
<div style="display: flex; gap: 2px; background: ${currentTheme === "dark" ? "#4b5563" : "#e5e7eb"}; border-radius: 8px; padding: 2px;">
|
|
23681
|
+
<button id="${modalId}-type-line" style="
|
|
23682
|
+
padding: 6px 12px;
|
|
23683
|
+
border: none;
|
|
23684
|
+
border-radius: 6px;
|
|
23685
|
+
font-size: 13px;
|
|
23686
|
+
cursor: pointer;
|
|
23687
|
+
transition: all 0.2s;
|
|
23688
|
+
background: ${currentChartType === "line" ? "#3e1a7d" : "transparent"};
|
|
23689
|
+
color: ${currentChartType === "line" ? "white" : colors.text};
|
|
23690
|
+
">Linhas</button>
|
|
23691
|
+
<button id="${modalId}-type-bar" style="
|
|
23692
|
+
padding: 6px 12px;
|
|
23693
|
+
border: none;
|
|
23694
|
+
border-radius: 6px;
|
|
23695
|
+
font-size: 13px;
|
|
23696
|
+
cursor: pointer;
|
|
23697
|
+
transition: all 0.2s;
|
|
23698
|
+
background: ${currentChartType === "bar" ? "#3e1a7d" : "transparent"};
|
|
23699
|
+
color: ${currentChartType === "bar" ? "white" : colors.text};
|
|
23700
|
+
">Barras</button>
|
|
23701
|
+
</div>
|
|
23702
|
+
</div>
|
|
23703
|
+
|
|
23704
|
+
<!-- Chart Container -->
|
|
23705
|
+
<div style="
|
|
23706
|
+
flex: 1;
|
|
23707
|
+
padding: 16px;
|
|
23708
|
+
min-height: 0;
|
|
23709
|
+
position: relative;
|
|
23710
|
+
background: ${colors.chartBackground};
|
|
23711
|
+
">
|
|
23712
|
+
<canvas id="${modalId}-chart" style="width: 100%; height: 100%;"></canvas>
|
|
23713
|
+
</div>
|
|
23714
|
+
</div>
|
|
23715
|
+
</div>
|
|
23716
|
+
`;
|
|
23717
|
+
}
|
|
23718
|
+
function setupListeners() {
|
|
23719
|
+
if (!modalElement) return;
|
|
23720
|
+
headerInstance?.attachListeners();
|
|
23721
|
+
document.getElementById(`${modalId}-viz-total`)?.addEventListener("click", () => {
|
|
23722
|
+
currentVizMode = "total";
|
|
23723
|
+
chartInstance?.setVizMode("total");
|
|
23724
|
+
updateControlStyles();
|
|
23725
|
+
});
|
|
23726
|
+
document.getElementById(`${modalId}-viz-separate`)?.addEventListener("click", () => {
|
|
23727
|
+
currentVizMode = "separate";
|
|
23728
|
+
chartInstance?.setVizMode("separate");
|
|
23729
|
+
updateControlStyles();
|
|
23730
|
+
});
|
|
23731
|
+
document.getElementById(`${modalId}-type-line`)?.addEventListener("click", () => {
|
|
23732
|
+
currentChartType = "line";
|
|
23733
|
+
chartInstance?.setChartType("line");
|
|
23734
|
+
updateControlStyles();
|
|
23735
|
+
});
|
|
23736
|
+
document.getElementById(`${modalId}-type-bar`)?.addEventListener("click", () => {
|
|
23737
|
+
currentChartType = "bar";
|
|
23738
|
+
chartInstance?.setChartType("bar");
|
|
23739
|
+
updateControlStyles();
|
|
23740
|
+
});
|
|
23741
|
+
modalElement.querySelector(".myio-consumption-modal-overlay")?.addEventListener("click", (e) => {
|
|
23742
|
+
if (e.target.classList.contains("myio-consumption-modal-overlay")) {
|
|
23743
|
+
instance.close();
|
|
23744
|
+
}
|
|
23745
|
+
});
|
|
23746
|
+
const handleKeydown = (e) => {
|
|
23747
|
+
if (e.key === "Escape") {
|
|
23748
|
+
instance.close();
|
|
23749
|
+
}
|
|
23750
|
+
};
|
|
23751
|
+
document.addEventListener("keydown", handleKeydown);
|
|
23752
|
+
modalElement.__handleKeydown = handleKeydown;
|
|
23753
|
+
}
|
|
23754
|
+
function updateControlStyles() {
|
|
23755
|
+
const colors = getThemeColors2();
|
|
23756
|
+
const vizTotalBtn = document.getElementById(`${modalId}-viz-total`);
|
|
23757
|
+
const vizSeparateBtn = document.getElementById(`${modalId}-viz-separate`);
|
|
23758
|
+
if (vizTotalBtn) {
|
|
23759
|
+
vizTotalBtn.style.background = currentVizMode === "total" ? "#3e1a7d" : "transparent";
|
|
23760
|
+
vizTotalBtn.style.color = currentVizMode === "total" ? "white" : colors.text;
|
|
23761
|
+
}
|
|
23762
|
+
if (vizSeparateBtn) {
|
|
23763
|
+
vizSeparateBtn.style.background = currentVizMode === "separate" ? "#3e1a7d" : "transparent";
|
|
23764
|
+
vizSeparateBtn.style.color = currentVizMode === "separate" ? "white" : colors.text;
|
|
23765
|
+
}
|
|
23766
|
+
const typeLineBtn = document.getElementById(`${modalId}-type-line`);
|
|
23767
|
+
const typeBarBtn = document.getElementById(`${modalId}-type-bar`);
|
|
23768
|
+
if (typeLineBtn) {
|
|
23769
|
+
typeLineBtn.style.background = currentChartType === "line" ? "#3e1a7d" : "transparent";
|
|
23770
|
+
typeLineBtn.style.color = currentChartType === "line" ? "white" : colors.text;
|
|
23771
|
+
}
|
|
23772
|
+
if (typeBarBtn) {
|
|
23773
|
+
typeBarBtn.style.background = currentChartType === "bar" ? "#3e1a7d" : "transparent";
|
|
23774
|
+
typeBarBtn.style.color = currentChartType === "bar" ? "white" : colors.text;
|
|
23775
|
+
}
|
|
23776
|
+
}
|
|
23777
|
+
function updateModal() {
|
|
23778
|
+
if (!modalElement) return;
|
|
23779
|
+
const cachedData = chartInstance?.getCachedData();
|
|
23780
|
+
headerInstance?.destroy();
|
|
23781
|
+
chartInstance?.destroy();
|
|
23782
|
+
modalElement.innerHTML = renderModal4();
|
|
23783
|
+
setupListeners();
|
|
23784
|
+
if (cachedData) {
|
|
23785
|
+
chartInstance = createConsumption7DaysChart({
|
|
23786
|
+
...config,
|
|
23787
|
+
containerId: `${modalId}-chart`,
|
|
23788
|
+
theme: currentTheme,
|
|
23789
|
+
defaultChartType: currentChartType,
|
|
23790
|
+
defaultVizMode: currentVizMode
|
|
23791
|
+
});
|
|
23792
|
+
chartInstance.update(cachedData);
|
|
23793
|
+
}
|
|
23794
|
+
}
|
|
23795
|
+
const instance = {
|
|
23796
|
+
async open() {
|
|
23797
|
+
modalElement = document.createElement("div");
|
|
23798
|
+
modalElement.id = modalId;
|
|
23799
|
+
modalElement.innerHTML = renderModal4();
|
|
23800
|
+
const container = config.container || document.body;
|
|
23801
|
+
container.appendChild(modalElement);
|
|
23802
|
+
setupListeners();
|
|
23803
|
+
chartInstance = createConsumption7DaysChart({
|
|
23804
|
+
...config,
|
|
23805
|
+
containerId: `${modalId}-chart`,
|
|
23806
|
+
theme: currentTheme,
|
|
23807
|
+
defaultChartType: currentChartType,
|
|
23808
|
+
defaultVizMode: currentVizMode
|
|
23809
|
+
});
|
|
23810
|
+
await chartInstance.render();
|
|
23811
|
+
},
|
|
23812
|
+
close() {
|
|
23813
|
+
if (modalElement) {
|
|
23814
|
+
const handleKeydown = modalElement.__handleKeydown;
|
|
23815
|
+
if (handleKeydown) {
|
|
23816
|
+
document.removeEventListener("keydown", handleKeydown);
|
|
23817
|
+
}
|
|
23818
|
+
headerInstance?.destroy();
|
|
23819
|
+
headerInstance = null;
|
|
23820
|
+
chartInstance?.destroy();
|
|
23821
|
+
chartInstance = null;
|
|
23822
|
+
modalElement.remove();
|
|
23823
|
+
modalElement = null;
|
|
23824
|
+
config.onClose?.();
|
|
23825
|
+
}
|
|
23826
|
+
},
|
|
23827
|
+
getChart() {
|
|
23828
|
+
return chartInstance;
|
|
23829
|
+
},
|
|
23830
|
+
destroy() {
|
|
23831
|
+
instance.close();
|
|
23832
|
+
}
|
|
23833
|
+
};
|
|
23834
|
+
return instance;
|
|
23835
|
+
}
|
|
23836
|
+
|
|
23837
|
+
// src/components/ExportData/index.ts
|
|
23838
|
+
var DEFAULT_COLORS3 = {
|
|
23839
|
+
primary: "#3e1a7d",
|
|
23840
|
+
// MyIO purple
|
|
23841
|
+
secondary: "#6b4c9a",
|
|
23842
|
+
// Light purple
|
|
23843
|
+
accent: "#00bcd4",
|
|
23844
|
+
// Cyan accent
|
|
23845
|
+
background: "#ffffff",
|
|
23846
|
+
// White
|
|
23847
|
+
text: "#333333",
|
|
23848
|
+
// Dark gray
|
|
23849
|
+
chartColors: ["#3e1a7d", "#00bcd4", "#4caf50", "#ff9800", "#e91e63", "#9c27b0"]
|
|
23850
|
+
};
|
|
23851
|
+
var DOMAIN_ICONS = {
|
|
23852
|
+
energy: "\u26A1",
|
|
23853
|
+
// Lightning bolt
|
|
23854
|
+
water: "\u{1F4A7}",
|
|
23855
|
+
// Water drop
|
|
23856
|
+
temperature: "\u{1F321}\uFE0F"
|
|
23857
|
+
// Thermometer
|
|
23858
|
+
};
|
|
23859
|
+
var DOMAIN_LABELS = {
|
|
23860
|
+
energy: "Energia",
|
|
23861
|
+
water: "\xC1gua",
|
|
23862
|
+
temperature: "Temperatura"
|
|
23863
|
+
};
|
|
23864
|
+
var DOMAIN_LABELS_EN = {
|
|
23865
|
+
energy: "Energy",
|
|
23866
|
+
water: "Water",
|
|
23867
|
+
temperature: "Temperature"
|
|
23868
|
+
};
|
|
23869
|
+
var DOMAIN_UNITS = {
|
|
23870
|
+
energy: "kWh",
|
|
23871
|
+
water: "m\xB3",
|
|
23872
|
+
temperature: "\xB0C"
|
|
23873
|
+
};
|
|
23874
|
+
var CSV_SEPARATORS = {
|
|
23875
|
+
"pt-BR": ";",
|
|
23876
|
+
"en-US": ",",
|
|
23877
|
+
"default": ";"
|
|
23878
|
+
};
|
|
23879
|
+
function formatDateForFilename(date) {
|
|
23880
|
+
const pad = (n) => n.toString().padStart(2, "0");
|
|
23881
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}-${pad(date.getHours())}-${pad(date.getMinutes())}-${pad(date.getSeconds())}`;
|
|
23882
|
+
}
|
|
23883
|
+
function sanitizeFilename(str) {
|
|
23884
|
+
return str.replace(/[<>:"/\\|?*]/g, "").replace(/\s+/g, "_").substring(0, 50);
|
|
23885
|
+
}
|
|
23886
|
+
function generateFilename(data, config) {
|
|
23887
|
+
const timestamp = formatDateForFilename(/* @__PURE__ */ new Date());
|
|
23888
|
+
const domainLabel = DOMAIN_LABELS_EN[config.domain].toUpperCase();
|
|
23889
|
+
const ext = config.formatExport;
|
|
23890
|
+
let baseName = "export";
|
|
23891
|
+
if ("device" in data && data.device) {
|
|
23892
|
+
const device = data.device;
|
|
23893
|
+
const label = device.label || device.name || "device";
|
|
23894
|
+
const identifier = device.identifier ? `-${device.identifier}` : "";
|
|
23895
|
+
baseName = `${sanitizeFilename(label)}${identifier}`;
|
|
23896
|
+
} else if ("customer" in data && data.customer?.customerName) {
|
|
23897
|
+
baseName = sanitizeFilename(data.customer.customerName);
|
|
23898
|
+
} else if ("groupName" in data) {
|
|
23899
|
+
baseName = sanitizeFilename(data.groupName);
|
|
23900
|
+
}
|
|
23901
|
+
return `${baseName}-${domainLabel}-${timestamp}.${ext}`;
|
|
23902
|
+
}
|
|
23903
|
+
function normalizeTimestamp(ts) {
|
|
23904
|
+
if (ts instanceof Date) return ts;
|
|
23905
|
+
if (typeof ts === "number") return new Date(ts);
|
|
23906
|
+
return new Date(ts);
|
|
23907
|
+
}
|
|
23908
|
+
function calculateStats2(dataPoints) {
|
|
23909
|
+
if (dataPoints.length === 0) {
|
|
23910
|
+
return { min: 0, max: 0, average: 0, sum: 0, count: 0 };
|
|
23911
|
+
}
|
|
23912
|
+
const values = dataPoints.map((d) => d.value);
|
|
23913
|
+
const sum = values.reduce((a, b) => a + b, 0);
|
|
23914
|
+
return {
|
|
23915
|
+
min: Math.min(...values),
|
|
23916
|
+
max: Math.max(...values),
|
|
23917
|
+
average: sum / values.length,
|
|
23918
|
+
sum,
|
|
23919
|
+
count: values.length
|
|
23920
|
+
};
|
|
23921
|
+
}
|
|
23922
|
+
function formatNumber2(value, locale, decimals = 2) {
|
|
23923
|
+
return new Intl.NumberFormat(locale, {
|
|
23924
|
+
minimumFractionDigits: decimals,
|
|
23925
|
+
maximumFractionDigits: decimals
|
|
23926
|
+
}).format(value);
|
|
23927
|
+
}
|
|
23928
|
+
function formatDate3(date, locale) {
|
|
23929
|
+
return new Intl.DateTimeFormat(locale, {
|
|
23930
|
+
year: "numeric",
|
|
23931
|
+
month: "2-digit",
|
|
23932
|
+
day: "2-digit",
|
|
23933
|
+
hour: "2-digit",
|
|
23934
|
+
minute: "2-digit"
|
|
23935
|
+
}).format(date);
|
|
23936
|
+
}
|
|
23937
|
+
function generateCSV(data, config) {
|
|
23938
|
+
const sep = CSV_SEPARATORS[config.locale] || CSV_SEPARATORS["default"];
|
|
23939
|
+
const rows = [];
|
|
23940
|
+
const escapeCSV = (val) => {
|
|
23941
|
+
const str = String(val ?? "");
|
|
23942
|
+
if (str.includes(sep) || str.includes('"') || str.includes("\n")) {
|
|
23943
|
+
return `"${str.replace(/"/g, '""')}"`;
|
|
23944
|
+
}
|
|
23945
|
+
return str;
|
|
23946
|
+
};
|
|
23947
|
+
const formatNumCSV = (val) => {
|
|
23948
|
+
return formatNumber2(val, config.locale);
|
|
23949
|
+
};
|
|
23950
|
+
if ("device" in data && "data" in data && Array.isArray(data.data)) {
|
|
23951
|
+
const deviceData = data;
|
|
23952
|
+
rows.push(["Timestamp", config.domainLabel, `Unit (${config.domainUnit})`]);
|
|
23953
|
+
for (const point of deviceData.data) {
|
|
23954
|
+
const ts = normalizeTimestamp(point.timestamp);
|
|
23955
|
+
rows.push([
|
|
23956
|
+
formatDate3(ts, config.locale),
|
|
23957
|
+
formatNumCSV(point.value),
|
|
23958
|
+
point.unit || config.domainUnit
|
|
23959
|
+
]);
|
|
23960
|
+
}
|
|
23961
|
+
if (config.includeStats) {
|
|
23962
|
+
const stats = calculateStats2(deviceData.data);
|
|
23963
|
+
rows.push([]);
|
|
23964
|
+
rows.push(["Statistics", "", ""]);
|
|
23965
|
+
rows.push(["Minimum", formatNumCSV(stats.min), config.domainUnit]);
|
|
23966
|
+
rows.push(["Maximum", formatNumCSV(stats.max), config.domainUnit]);
|
|
23967
|
+
rows.push(["Average", formatNumCSV(stats.average), config.domainUnit]);
|
|
23968
|
+
rows.push(["Total", formatNumCSV(stats.sum), config.domainUnit]);
|
|
23969
|
+
rows.push(["Count", String(stats.count), "points"]);
|
|
23970
|
+
}
|
|
23971
|
+
} else if ("devices" in data && Array.isArray(data.devices)) {
|
|
23972
|
+
const compData = data;
|
|
23973
|
+
const deviceHeaders = compData.devices.map(
|
|
23974
|
+
(d) => d.device.label || d.device.name || "Device"
|
|
23975
|
+
);
|
|
23976
|
+
rows.push(["Timestamp", ...deviceHeaders]);
|
|
23977
|
+
const allTimestamps = /* @__PURE__ */ new Set();
|
|
23978
|
+
compData.devices.forEach((d) => {
|
|
23979
|
+
d.data.forEach((point) => {
|
|
23980
|
+
allTimestamps.add(normalizeTimestamp(point.timestamp).getTime());
|
|
23981
|
+
});
|
|
23982
|
+
});
|
|
23983
|
+
const sortedTimestamps = Array.from(allTimestamps).sort((a, b) => a - b);
|
|
23984
|
+
for (const ts of sortedTimestamps) {
|
|
23985
|
+
const row = [formatDate3(new Date(ts), config.locale)];
|
|
23986
|
+
for (const device of compData.devices) {
|
|
23987
|
+
const point = device.data.find(
|
|
23988
|
+
(p) => normalizeTimestamp(p.timestamp).getTime() === ts
|
|
23989
|
+
);
|
|
23990
|
+
row.push(point ? formatNumCSV(point.value) : "");
|
|
23991
|
+
}
|
|
23992
|
+
rows.push(row);
|
|
23993
|
+
}
|
|
23994
|
+
}
|
|
23995
|
+
return rows.map((row) => row.map(escapeCSV).join(sep)).join("\r\n");
|
|
23996
|
+
}
|
|
23997
|
+
function generateXLSX(data, config) {
|
|
23998
|
+
return generateCSV(data, config);
|
|
23999
|
+
}
|
|
24000
|
+
function generatePDFContent(data, config) {
|
|
24001
|
+
const { colors, domainIcon, domainLabel, domainUnit, locale, includeStats, includeChart } = config;
|
|
24002
|
+
let deviceLabel = "Export";
|
|
24003
|
+
let customerName = "";
|
|
24004
|
+
let identifier = "";
|
|
24005
|
+
let dataPoints = [];
|
|
24006
|
+
if ("device" in data && "data" in data) {
|
|
24007
|
+
const deviceData = data;
|
|
24008
|
+
deviceLabel = deviceData.device.label || deviceData.device.name || "Device";
|
|
24009
|
+
identifier = deviceData.device.identifier || "";
|
|
24010
|
+
customerName = deviceData.customer?.customerName || "";
|
|
24011
|
+
dataPoints = deviceData.data;
|
|
24012
|
+
}
|
|
24013
|
+
const stats = calculateStats2(dataPoints);
|
|
24014
|
+
const tableRows = dataPoints.slice(0, 100).map((point) => {
|
|
24015
|
+
const ts = normalizeTimestamp(point.timestamp);
|
|
24016
|
+
return `
|
|
24017
|
+
<tr>
|
|
24018
|
+
<td style="padding: 8px; border-bottom: 1px solid #eee;">${formatDate3(ts, locale)}</td>
|
|
24019
|
+
<td style="padding: 8px; border-bottom: 1px solid #eee; text-align: right;">${formatNumber2(point.value, locale)}</td>
|
|
24020
|
+
<td style="padding: 8px; border-bottom: 1px solid #eee;">${point.unit || domainUnit}</td>
|
|
24021
|
+
</tr>
|
|
24022
|
+
`;
|
|
24023
|
+
}).join("");
|
|
24024
|
+
const statsSection = includeStats ? `
|
|
24025
|
+
<div style="margin-top: 24px; padding: 16px; background: #f5f5f5; border-radius: 8px;">
|
|
24026
|
+
<h3 style="margin: 0 0 12px 0; color: ${colors.primary};">Statistics</h3>
|
|
24027
|
+
<table style="width: 100%;">
|
|
24028
|
+
<tr>
|
|
24029
|
+
<td><strong>Minimum:</strong></td>
|
|
24030
|
+
<td>${formatNumber2(stats.min, locale)} ${domainUnit}</td>
|
|
24031
|
+
<td><strong>Maximum:</strong></td>
|
|
24032
|
+
<td>${formatNumber2(stats.max, locale)} ${domainUnit}</td>
|
|
24033
|
+
</tr>
|
|
24034
|
+
<tr>
|
|
24035
|
+
<td><strong>Average:</strong></td>
|
|
24036
|
+
<td>${formatNumber2(stats.average, locale)} ${domainUnit}</td>
|
|
24037
|
+
<td><strong>Total:</strong></td>
|
|
24038
|
+
<td>${formatNumber2(stats.sum, locale)} ${domainUnit}</td>
|
|
24039
|
+
</tr>
|
|
24040
|
+
</table>
|
|
24041
|
+
</div>
|
|
24042
|
+
` : "";
|
|
24043
|
+
return `
|
|
24044
|
+
<!DOCTYPE html>
|
|
24045
|
+
<html>
|
|
24046
|
+
<head>
|
|
24047
|
+
<meta charset="UTF-8">
|
|
24048
|
+
<title>${deviceLabel} - ${domainLabel} Report</title>
|
|
24049
|
+
<style>
|
|
24050
|
+
body {
|
|
24051
|
+
font-family: 'Roboto', Arial, sans-serif;
|
|
24052
|
+
margin: 0;
|
|
24053
|
+
padding: 24px;
|
|
24054
|
+
color: ${colors.text};
|
|
24055
|
+
background: ${colors.background};
|
|
24056
|
+
}
|
|
24057
|
+
.header {
|
|
24058
|
+
background: ${colors.primary};
|
|
24059
|
+
color: white;
|
|
24060
|
+
padding: 20px;
|
|
24061
|
+
border-radius: 8px;
|
|
24062
|
+
margin-bottom: 24px;
|
|
24063
|
+
}
|
|
24064
|
+
.header h1 {
|
|
24065
|
+
margin: 0;
|
|
24066
|
+
font-size: 24px;
|
|
24067
|
+
}
|
|
24068
|
+
.header .subtitle {
|
|
24069
|
+
opacity: 0.9;
|
|
24070
|
+
margin-top: 8px;
|
|
24071
|
+
}
|
|
24072
|
+
.device-info {
|
|
24073
|
+
display: flex;
|
|
24074
|
+
gap: 16px;
|
|
24075
|
+
margin-bottom: 16px;
|
|
24076
|
+
padding: 12px;
|
|
24077
|
+
background: #f5f5f5;
|
|
24078
|
+
border-radius: 8px;
|
|
24079
|
+
}
|
|
24080
|
+
.device-info span {
|
|
24081
|
+
padding: 4px 12px;
|
|
24082
|
+
background: ${colors.secondary};
|
|
24083
|
+
color: white;
|
|
24084
|
+
border-radius: 4px;
|
|
24085
|
+
font-size: 14px;
|
|
24086
|
+
}
|
|
24087
|
+
table {
|
|
24088
|
+
width: 100%;
|
|
24089
|
+
border-collapse: collapse;
|
|
24090
|
+
}
|
|
24091
|
+
th {
|
|
24092
|
+
background: ${colors.primary};
|
|
24093
|
+
color: white;
|
|
24094
|
+
padding: 12px 8px;
|
|
24095
|
+
text-align: left;
|
|
24096
|
+
}
|
|
24097
|
+
th:nth-child(2) {
|
|
24098
|
+
text-align: right;
|
|
24099
|
+
}
|
|
24100
|
+
.footer {
|
|
24101
|
+
margin-top: 24px;
|
|
24102
|
+
padding-top: 16px;
|
|
24103
|
+
border-top: 1px solid #eee;
|
|
24104
|
+
text-align: center;
|
|
24105
|
+
font-size: 12px;
|
|
24106
|
+
color: #999;
|
|
24107
|
+
}
|
|
24108
|
+
@media print {
|
|
24109
|
+
body { padding: 0; }
|
|
24110
|
+
.header { border-radius: 0; }
|
|
24111
|
+
}
|
|
24112
|
+
</style>
|
|
24113
|
+
</head>
|
|
24114
|
+
<body>
|
|
24115
|
+
<div class="header">
|
|
24116
|
+
<h1>${domainIcon} ${deviceLabel}</h1>
|
|
24117
|
+
<div class="subtitle">${domainLabel} Report - Generated ${formatDate3(/* @__PURE__ */ new Date(), locale)}</div>
|
|
24118
|
+
</div>
|
|
24119
|
+
|
|
24120
|
+
${customerName ? `<div class="customer-name" style="margin-bottom: 16px; font-size: 18px;"><strong>Customer:</strong> ${customerName}</div>` : ""}
|
|
24121
|
+
|
|
24122
|
+
${identifier ? `
|
|
24123
|
+
<div class="device-info">
|
|
24124
|
+
<span>ID: ${identifier}</span>
|
|
24125
|
+
<span>Domain: ${domainLabel}</span>
|
|
24126
|
+
<span>Unit: ${domainUnit}</span>
|
|
24127
|
+
</div>
|
|
24128
|
+
` : ""}
|
|
24129
|
+
|
|
24130
|
+
<table>
|
|
24131
|
+
<thead>
|
|
24132
|
+
<tr>
|
|
24133
|
+
<th>Timestamp</th>
|
|
24134
|
+
<th>${domainLabel} (${domainUnit})</th>
|
|
24135
|
+
<th>Unit</th>
|
|
24136
|
+
</tr>
|
|
24137
|
+
</thead>
|
|
24138
|
+
<tbody>
|
|
24139
|
+
${tableRows}
|
|
24140
|
+
${dataPoints.length > 100 ? `<tr><td colspan="3" style="text-align: center; padding: 16px; color: #999;">... and ${dataPoints.length - 100} more rows</td></tr>` : ""}
|
|
24141
|
+
</tbody>
|
|
24142
|
+
</table>
|
|
24143
|
+
|
|
24144
|
+
${statsSection}
|
|
24145
|
+
|
|
24146
|
+
<div class="footer">
|
|
24147
|
+
<p>${config.footerText || "Generated by MyIO Platform"}</p>
|
|
24148
|
+
</div>
|
|
24149
|
+
</body>
|
|
24150
|
+
</html>
|
|
24151
|
+
`;
|
|
24152
|
+
}
|
|
24153
|
+
function buildTemplateExport(params) {
|
|
24154
|
+
const {
|
|
24155
|
+
domain,
|
|
24156
|
+
formatExport,
|
|
24157
|
+
typeExport,
|
|
24158
|
+
colorsPallet,
|
|
24159
|
+
locale = "pt-BR",
|
|
24160
|
+
includeChart = formatExport === "pdf",
|
|
24161
|
+
includeStats = true,
|
|
24162
|
+
headerText,
|
|
24163
|
+
footerText
|
|
24164
|
+
} = params;
|
|
24165
|
+
const colors = {
|
|
24166
|
+
...DEFAULT_COLORS3,
|
|
24167
|
+
...colorsPallet,
|
|
24168
|
+
chartColors: colorsPallet?.chartColors || DEFAULT_COLORS3.chartColors
|
|
24169
|
+
};
|
|
24170
|
+
return {
|
|
24171
|
+
domain,
|
|
24172
|
+
formatExport,
|
|
24173
|
+
typeExport,
|
|
24174
|
+
colors,
|
|
24175
|
+
locale,
|
|
24176
|
+
includeChart,
|
|
24177
|
+
includeStats,
|
|
24178
|
+
headerText: headerText || `${DOMAIN_LABELS[domain]} Report`,
|
|
24179
|
+
footerText: footerText || "Generated by MyIO Platform",
|
|
24180
|
+
domainIcon: DOMAIN_ICONS[domain],
|
|
24181
|
+
domainLabel: DOMAIN_LABELS[domain],
|
|
24182
|
+
domainUnit: DOMAIN_UNITS[domain]
|
|
24183
|
+
};
|
|
24184
|
+
}
|
|
24185
|
+
function myioExportData(data, config, options) {
|
|
24186
|
+
const filename = generateFilename(data, config);
|
|
24187
|
+
let allDataPoints = [];
|
|
24188
|
+
if ("data" in data && Array.isArray(data.data)) {
|
|
24189
|
+
allDataPoints = data.data;
|
|
24190
|
+
} else if ("devices" in data && Array.isArray(data.devices)) {
|
|
24191
|
+
allDataPoints = data.devices.flatMap((d) => d.data);
|
|
24192
|
+
}
|
|
24193
|
+
const stats = calculateStats2(allDataPoints);
|
|
24194
|
+
const instance = {
|
|
24195
|
+
async export() {
|
|
24196
|
+
try {
|
|
24197
|
+
options?.onProgress?.(10, "Generating content...");
|
|
24198
|
+
let content;
|
|
24199
|
+
let mimeType;
|
|
24200
|
+
let finalFilename = filename;
|
|
24201
|
+
switch (config.formatExport) {
|
|
24202
|
+
case "csv":
|
|
24203
|
+
content = generateCSV(data, config);
|
|
24204
|
+
mimeType = "text/csv;charset=utf-8;";
|
|
24205
|
+
break;
|
|
24206
|
+
case "xlsx":
|
|
24207
|
+
content = generateXLSX(data, config);
|
|
24208
|
+
mimeType = "text/csv;charset=utf-8;";
|
|
24209
|
+
finalFilename = filename.replace(".xlsx", ".csv");
|
|
24210
|
+
break;
|
|
24211
|
+
case "pdf":
|
|
24212
|
+
content = generatePDFContent(data, config);
|
|
24213
|
+
mimeType = "text/html;charset=utf-8;";
|
|
24214
|
+
finalFilename = filename.replace(".pdf", ".html");
|
|
24215
|
+
break;
|
|
24216
|
+
default:
|
|
24217
|
+
throw new Error(`Unsupported format: ${config.formatExport}`);
|
|
24218
|
+
}
|
|
24219
|
+
options?.onProgress?.(80, "Creating file...");
|
|
24220
|
+
const bom = config.formatExport === "csv" ? "\uFEFF" : "";
|
|
24221
|
+
const blob = new Blob([bom + content], { type: mimeType });
|
|
24222
|
+
options?.onProgress?.(100, "Export complete");
|
|
24223
|
+
return {
|
|
24224
|
+
success: true,
|
|
24225
|
+
filename: finalFilename,
|
|
24226
|
+
blob,
|
|
24227
|
+
dataUrl: URL.createObjectURL(blob)
|
|
24228
|
+
};
|
|
24229
|
+
} catch (error) {
|
|
24230
|
+
return {
|
|
24231
|
+
success: false,
|
|
24232
|
+
filename,
|
|
24233
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
24234
|
+
};
|
|
24235
|
+
}
|
|
24236
|
+
},
|
|
24237
|
+
async download() {
|
|
24238
|
+
const result = await this.export();
|
|
24239
|
+
if (!result.success || !result.blob) {
|
|
24240
|
+
console.error("Export failed:", result.error);
|
|
24241
|
+
return;
|
|
24242
|
+
}
|
|
24243
|
+
const link = document.createElement("a");
|
|
24244
|
+
link.href = URL.createObjectURL(result.blob);
|
|
24245
|
+
link.download = result.filename;
|
|
24246
|
+
link.style.display = "none";
|
|
24247
|
+
document.body.appendChild(link);
|
|
24248
|
+
link.click();
|
|
24249
|
+
document.body.removeChild(link);
|
|
24250
|
+
URL.revokeObjectURL(link.href);
|
|
24251
|
+
},
|
|
24252
|
+
async preview() {
|
|
24253
|
+
if (config.formatExport !== "pdf") {
|
|
24254
|
+
return null;
|
|
24255
|
+
}
|
|
24256
|
+
const result = await this.export();
|
|
24257
|
+
return result.dataUrl || null;
|
|
24258
|
+
},
|
|
24259
|
+
getStats() {
|
|
24260
|
+
return stats;
|
|
24261
|
+
},
|
|
24262
|
+
getFilename() {
|
|
24263
|
+
return filename;
|
|
24264
|
+
}
|
|
24265
|
+
};
|
|
24266
|
+
if (options?.autoDownload) {
|
|
24267
|
+
instance.download();
|
|
24268
|
+
}
|
|
24269
|
+
return instance;
|
|
24270
|
+
}
|
|
24271
|
+
var EXPORT_DEFAULT_COLORS = DEFAULT_COLORS3;
|
|
24272
|
+
var EXPORT_DOMAIN_ICONS = DOMAIN_ICONS;
|
|
24273
|
+
var EXPORT_DOMAIN_LABELS = DOMAIN_LABELS;
|
|
24274
|
+
var EXPORT_DOMAIN_UNITS = DOMAIN_UNITS;
|
|
22359
24275
|
export {
|
|
22360
24276
|
CHART_COLORS,
|
|
24277
|
+
DEFAULT_COLORS as CONSUMPTION_CHART_COLORS,
|
|
24278
|
+
DEFAULT_CONFIG as CONSUMPTION_CHART_DEFAULTS,
|
|
24279
|
+
THEME_COLORS as CONSUMPTION_THEME_COLORS,
|
|
22361
24280
|
ConnectionStatusType,
|
|
22362
24281
|
DEFAULT_CLAMP_RANGE,
|
|
22363
24282
|
DeviceStatusType,
|
|
24283
|
+
EXPORT_DEFAULT_COLORS,
|
|
24284
|
+
EXPORT_DOMAIN_ICONS,
|
|
24285
|
+
EXPORT_DOMAIN_LABELS,
|
|
24286
|
+
EXPORT_DOMAIN_UNITS,
|
|
22364
24287
|
MyIOChartModal,
|
|
22365
24288
|
MyIODraggableCard,
|
|
22366
24289
|
MyIOSelectionStore,
|
|
@@ -22372,11 +24295,13 @@ export {
|
|
|
22372
24295
|
averageByDay,
|
|
22373
24296
|
buildListItemsThingsboardByUniqueDatasource,
|
|
22374
24297
|
buildMyioIngestionAuth,
|
|
24298
|
+
buildTemplateExport,
|
|
22375
24299
|
buildWaterReportCSV,
|
|
22376
24300
|
buildWaterStoresCSV,
|
|
22377
24301
|
calcDeltaPercent,
|
|
22378
24302
|
calculateDeviceStatus,
|
|
22379
24303
|
calculateDeviceStatusWithRanges,
|
|
24304
|
+
calculateStats2 as calculateExportStats,
|
|
22380
24305
|
calculateStats,
|
|
22381
24306
|
clampTemperature,
|
|
22382
24307
|
classify,
|
|
@@ -22384,8 +24309,11 @@ export {
|
|
|
22384
24309
|
classifyWaterLabels,
|
|
22385
24310
|
clearAllAuthCaches,
|
|
22386
24311
|
connectionStatusIcons,
|
|
24312
|
+
createConsumption7DaysChart,
|
|
24313
|
+
createConsumptionModal,
|
|
22387
24314
|
createDateRangePicker2 as createDateRangePicker,
|
|
22388
24315
|
createInputDateRangePickerInsideDIV,
|
|
24316
|
+
createModalHeader,
|
|
22389
24317
|
decodePayload,
|
|
22390
24318
|
decodePayloadBase64Xor,
|
|
22391
24319
|
detectDeviceType,
|
|
@@ -22399,6 +24327,7 @@ export {
|
|
|
22399
24327
|
fetchThingsboardCustomerAttrsFromStorage,
|
|
22400
24328
|
fetchThingsboardCustomerServerScopeAttrs,
|
|
22401
24329
|
findValue,
|
|
24330
|
+
findValueWithDefault,
|
|
22402
24331
|
fmtPerc,
|
|
22403
24332
|
fmtPerc2 as fmtPercLegacy,
|
|
22404
24333
|
formatAllInSameUnit,
|
|
@@ -22406,18 +24335,24 @@ export {
|
|
|
22406
24335
|
formatDateForInput,
|
|
22407
24336
|
formatDateToYMD,
|
|
22408
24337
|
formatDateWithTimezoneOffset,
|
|
24338
|
+
formatDuration,
|
|
22409
24339
|
formatEnergy,
|
|
22410
24340
|
formatNumberReadable,
|
|
24341
|
+
formatRelativeTime,
|
|
22411
24342
|
formatTankHeadFromCm,
|
|
22412
24343
|
formatTemperature2 as formatTemperature,
|
|
24344
|
+
formatWater,
|
|
22413
24345
|
formatWaterByGroup,
|
|
22414
24346
|
formatWaterVolumeM3,
|
|
24347
|
+
formatarDuracao,
|
|
24348
|
+
generateFilename as generateExportFilename,
|
|
22415
24349
|
getAuthCacheStats,
|
|
22416
24350
|
getAvailableContexts,
|
|
22417
24351
|
getConnectionStatusIcon,
|
|
22418
24352
|
getDateRangeArray,
|
|
22419
24353
|
getDeviceStatusIcon,
|
|
22420
24354
|
getDeviceStatusInfo,
|
|
24355
|
+
getModalHeaderStyles,
|
|
22421
24356
|
getSaoPauloISOString,
|
|
22422
24357
|
getSaoPauloISOStringFixed,
|
|
22423
24358
|
getValueByDatakey,
|
|
@@ -22429,8 +24364,10 @@ export {
|
|
|
22429
24364
|
isValidConnectionStatus,
|
|
22430
24365
|
isValidDeviceStatus,
|
|
22431
24366
|
isWaterCategory,
|
|
24367
|
+
mapConnectionStatus,
|
|
22432
24368
|
mapDeviceStatusToCardStatus,
|
|
22433
24369
|
mapDeviceToConnectionStatus,
|
|
24370
|
+
myioExportData,
|
|
22434
24371
|
normalizeRecipients,
|
|
22435
24372
|
numbers_exports as numbers,
|
|
22436
24373
|
openDashboardPopup,
|