myio-js-library 0.1.522 → 0.1.523
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 +214 -1
- package/dist/index.js +214 -1
- package/dist/myio-js-library.umd.js +214 -1
- package/dist/myio-js-library.umd.min.js +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1240,7 +1240,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
1240
1240
|
// package.json
|
|
1241
1241
|
var package_default = {
|
|
1242
1242
|
name: "myio-js-library",
|
|
1243
|
-
version: "0.1.
|
|
1243
|
+
version: "0.1.523",
|
|
1244
1244
|
description: "A clean, standalone JS SDK for MYIO projects",
|
|
1245
1245
|
license: "MIT",
|
|
1246
1246
|
repository: "github:gh-myio/myio-js-library",
|
|
@@ -67230,10 +67230,41 @@ function openGoalsPanel(params) {
|
|
|
67230
67230
|
zIndex: styles.zIndex || 1e4
|
|
67231
67231
|
};
|
|
67232
67232
|
const i18n2 = locale === "en-US" ? getEnglishStrings(entityLabel) : getPortugueseStrings(entityLabel);
|
|
67233
|
+
const GLABELS = locale === "en-US" ? {
|
|
67234
|
+
title: "Granularity",
|
|
67235
|
+
month: "Monthly",
|
|
67236
|
+
day: "Daily",
|
|
67237
|
+
hour: "Hourly (soon)",
|
|
67238
|
+
template: "Download template",
|
|
67239
|
+
importBtn: "Import spreadsheet",
|
|
67240
|
+
hintMonth: "12 values",
|
|
67241
|
+
hintDay: "365 values",
|
|
67242
|
+
hintHour: "8,760 \u2014 soon",
|
|
67243
|
+
derived: "Monthly values derived from imported daily detail (read-only).",
|
|
67244
|
+
imported: "Spreadsheet imported.",
|
|
67245
|
+
importErr: "Import error: ",
|
|
67246
|
+
readErr: "Failed to read file."
|
|
67247
|
+
} : {
|
|
67248
|
+
title: "Granularidade",
|
|
67249
|
+
month: "M\xEAs",
|
|
67250
|
+
day: "Di\xE1ria",
|
|
67251
|
+
hour: "Hora (em breve)",
|
|
67252
|
+
template: "Baixar template",
|
|
67253
|
+
importBtn: "Importar planilha",
|
|
67254
|
+
hintMonth: "12 valores",
|
|
67255
|
+
hintDay: "365 valores",
|
|
67256
|
+
hintHour: "8.760 \u2014 em breve",
|
|
67257
|
+
derived: "Valores mensais derivados do detalhe di\xE1rio importado (somente leitura).",
|
|
67258
|
+
imported: "Planilha importada.",
|
|
67259
|
+
importErr: "Erro ao importar: ",
|
|
67260
|
+
readErr: "Falha ao ler o arquivo."
|
|
67261
|
+
};
|
|
67233
67262
|
const MODAL_ID4 = "goals-modal";
|
|
67234
67263
|
let modalState = {
|
|
67235
67264
|
currentTab: "shopping",
|
|
67236
67265
|
// 'shopping' | 'assets'
|
|
67266
|
+
granularity: "month",
|
|
67267
|
+
// 'month' | 'day' ('hour' = em breve)
|
|
67237
67268
|
currentYear: (/* @__PURE__ */ new Date()).getFullYear(),
|
|
67238
67269
|
selectedShoppingId: shoppingList.length > 0 ? shoppingList[0].value : null,
|
|
67239
67270
|
goalsData: data || null,
|
|
@@ -67422,6 +67453,30 @@ function openGoalsPanel(params) {
|
|
|
67422
67453
|
</div>
|
|
67423
67454
|
</div>
|
|
67424
67455
|
|
|
67456
|
+
<!-- Granularity selector (M\xEAs | Di\xE1ria | Hora em breve) + template/import -->
|
|
67457
|
+
<div class="myio-goals-section">
|
|
67458
|
+
<div class="myio-goals-section-header">
|
|
67459
|
+
<h3 class="myio-goals-section-title">${GLABELS.title}</h3>
|
|
67460
|
+
<div>
|
|
67461
|
+
<button class="myio-goals-btn-link" data-action="goals-template">\u2B07\uFE0F ${GLABELS.template}</button>
|
|
67462
|
+
<button class="myio-goals-btn-link" data-action="goals-import">\u{1F4E4} ${GLABELS.importBtn}</button>
|
|
67463
|
+
<input type="file" id="goals-import-file" accept=".csv,text/csv" style="display:none" />
|
|
67464
|
+
</div>
|
|
67465
|
+
</div>
|
|
67466
|
+
<div class="myio-goals-gran-chips" style="display:flex;gap:8px;margin-top:8px;flex-wrap:wrap;">
|
|
67467
|
+
${["month", "day", "hour"].map((g) => {
|
|
67468
|
+
const active = modalState.granularity === g;
|
|
67469
|
+
const disabled = g === "hour";
|
|
67470
|
+
const label = g === "month" ? GLABELS.month : g === "day" ? GLABELS.day : GLABELS.hour;
|
|
67471
|
+
const hint = g === "month" ? GLABELS.hintMonth : g === "day" ? GLABELS.hintDay : GLABELS.hintHour;
|
|
67472
|
+
return `<button type="button" class="myio-goals-gran-chip" data-gran="${g}" ${disabled ? "disabled" : ""}
|
|
67473
|
+
style="padding:6px 12px;border:1px solid ${theme.primaryColor};border-radius:16px;cursor:${disabled ? "not-allowed" : "pointer"};font-size:12px;${active ? `background:${theme.primaryColor};color:#fff;` : `background:#fff;color:${theme.primaryColor};`}${disabled ? "opacity:.5;" : ""}"
|
|
67474
|
+
title="${disabled ? GLABELS.hour : label}">${label} <small>(${hint})</small></button>`;
|
|
67475
|
+
}).join("")}
|
|
67476
|
+
</div>
|
|
67477
|
+
${modalState.granularity === "day" && Object.keys((getYearData(modalState.currentYear) || {}).daily || {}).length > 0 ? `<div class="myio-goals-gran-note" style="margin-top:8px;font-size:12px;color:#666;">${GLABELS.derived}</div>` : ""}
|
|
67478
|
+
</div>
|
|
67479
|
+
|
|
67425
67480
|
<!-- Monthly Distribution Section -->
|
|
67426
67481
|
<div class="myio-goals-section">
|
|
67427
67482
|
<div class="myio-goals-section-header">
|
|
@@ -67473,6 +67528,7 @@ function openGoalsPanel(params) {
|
|
|
67473
67528
|
value="${monthly[month.key] || ""}"
|
|
67474
67529
|
min="0"
|
|
67475
67530
|
step="0.01"
|
|
67531
|
+
${modalState.granularity === "day" ? 'disabled title="Derivado do detalhe di\xE1rio \u2014 edite via planilha"' : ""}
|
|
67476
67532
|
placeholder="0">
|
|
67477
67533
|
<span class="myio-goals-unit">${unit}</span>
|
|
67478
67534
|
</div>
|
|
@@ -67674,6 +67730,27 @@ function openGoalsPanel(params) {
|
|
|
67674
67730
|
container.querySelector('[data-action="auto-fill"]')?.addEventListener("click", () => {
|
|
67675
67731
|
autoFillMonthly();
|
|
67676
67732
|
});
|
|
67733
|
+
container.querySelectorAll("[data-gran]").forEach((chip) => {
|
|
67734
|
+
chip.addEventListener("click", () => {
|
|
67735
|
+
if (chip.disabled) return;
|
|
67736
|
+
const g = chip.getAttribute("data-gran");
|
|
67737
|
+
if (g === "hour" || g === modalState.granularity) return;
|
|
67738
|
+
modalState.granularity = g;
|
|
67739
|
+
renderTabContent();
|
|
67740
|
+
});
|
|
67741
|
+
});
|
|
67742
|
+
container.querySelector('[data-action="goals-template"]')?.addEventListener("click", () => {
|
|
67743
|
+
_downloadGoalsTemplate();
|
|
67744
|
+
});
|
|
67745
|
+
const fileInput = container.querySelector("#goals-import-file");
|
|
67746
|
+
container.querySelector('[data-action="goals-import"]')?.addEventListener("click", () => {
|
|
67747
|
+
fileInput?.click();
|
|
67748
|
+
});
|
|
67749
|
+
fileInput?.addEventListener("change", (e) => {
|
|
67750
|
+
const file = e.target.files && e.target.files[0];
|
|
67751
|
+
if (file) _handleGoalsImport(file);
|
|
67752
|
+
e.target.value = "";
|
|
67753
|
+
});
|
|
67677
67754
|
}
|
|
67678
67755
|
function attachAssetsTabListeners() {
|
|
67679
67756
|
const container = document.getElementById("tab-content-area");
|
|
@@ -67723,6 +67800,139 @@ function openGoalsPanel(params) {
|
|
|
67723
67800
|
}
|
|
67724
67801
|
modalState.goalsData.years[year.toString()] = data2;
|
|
67725
67802
|
}
|
|
67803
|
+
function _eachDateOfYear(year) {
|
|
67804
|
+
const out = [];
|
|
67805
|
+
const d = new Date(Date.UTC(year, 0, 1));
|
|
67806
|
+
while (d.getUTCFullYear() === year) {
|
|
67807
|
+
out.push(d.toISOString().slice(0, 10));
|
|
67808
|
+
d.setUTCDate(d.getUTCDate() + 1);
|
|
67809
|
+
}
|
|
67810
|
+
return out;
|
|
67811
|
+
}
|
|
67812
|
+
function _buildGoalsTemplateCsv(granularity, year) {
|
|
67813
|
+
const yd = getYearData(year) || {};
|
|
67814
|
+
if (granularity === "day") {
|
|
67815
|
+
const daily = yd.daily || {};
|
|
67816
|
+
const rows2 = _eachDateOfYear(year).map((iso) => `${iso};${daily[iso] != null ? daily[iso] : 0}`);
|
|
67817
|
+
return "data;valor\n" + rows2.join("\n");
|
|
67818
|
+
}
|
|
67819
|
+
const monthly = yd.monthly || {};
|
|
67820
|
+
const rows = [];
|
|
67821
|
+
for (let i = 1; i <= 12; i++) {
|
|
67822
|
+
const k = String(i).padStart(2, "0");
|
|
67823
|
+
rows.push(`${year}-${k};${monthly[k] != null ? monthly[k] : 0}`);
|
|
67824
|
+
}
|
|
67825
|
+
return "mes;valor\n" + rows.join("\n");
|
|
67826
|
+
}
|
|
67827
|
+
function _downloadGoalsTemplate() {
|
|
67828
|
+
const g = modalState.granularity === "day" ? "day" : "month";
|
|
67829
|
+
const year = modalState.currentYear;
|
|
67830
|
+
const csv = "\uFEFF" + _buildGoalsTemplateCsv(g, year);
|
|
67831
|
+
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
|
|
67832
|
+
const url = URL.createObjectURL(blob);
|
|
67833
|
+
const a = document.createElement("a");
|
|
67834
|
+
a.href = url;
|
|
67835
|
+
a.download = `metas-${g === "day" ? "diaria" : "mensal"}-${year}.csv`;
|
|
67836
|
+
document.body.appendChild(a);
|
|
67837
|
+
a.click();
|
|
67838
|
+
document.body.removeChild(a);
|
|
67839
|
+
URL.revokeObjectURL(url);
|
|
67840
|
+
}
|
|
67841
|
+
function _parseCsvNumber(s) {
|
|
67842
|
+
let t = String(s == null ? "" : s).trim();
|
|
67843
|
+
if (t.includes(",")) t = t.replace(/\./g, "").replace(",", ".");
|
|
67844
|
+
const v = parseFloat(t);
|
|
67845
|
+
return Number.isFinite(v) ? v : NaN;
|
|
67846
|
+
}
|
|
67847
|
+
function _parseGoalsCsv(text, granularity, year) {
|
|
67848
|
+
const errors = [];
|
|
67849
|
+
const lines = String(text).replace(/^/, "").split(/\r?\n/).map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
67850
|
+
if (lines.length && !Number.isFinite(_parseCsvNumber(lines[0].split(";")[1] || ""))) lines.shift();
|
|
67851
|
+
if (granularity === "day") {
|
|
67852
|
+
const daily = {};
|
|
67853
|
+
const valid = new Set(_eachDateOfYear(year));
|
|
67854
|
+
lines.forEach((line, idx) => {
|
|
67855
|
+
const [d, v] = line.split(";");
|
|
67856
|
+
const iso = (d || "").trim();
|
|
67857
|
+
const num = _parseCsvNumber(v);
|
|
67858
|
+
if (!valid.has(iso)) {
|
|
67859
|
+
errors.push(`Linha ${idx + 1}: data inv\xE1lida ou fora de ${year}: "${iso}"`);
|
|
67860
|
+
return;
|
|
67861
|
+
}
|
|
67862
|
+
if (!Number.isFinite(num) || num < 0) {
|
|
67863
|
+
errors.push(`Linha ${idx + 1}: valor inv\xE1lido: "${v}"`);
|
|
67864
|
+
return;
|
|
67865
|
+
}
|
|
67866
|
+
daily[iso] = num;
|
|
67867
|
+
});
|
|
67868
|
+
return { granularity: "day", daily, errors };
|
|
67869
|
+
}
|
|
67870
|
+
const monthly = {};
|
|
67871
|
+
lines.forEach((line, idx) => {
|
|
67872
|
+
const [m, v] = line.split(";");
|
|
67873
|
+
const raw = (m || "").trim();
|
|
67874
|
+
const key = raw.includes("-") ? raw.split("-").pop().padStart(2, "0") : raw.padStart(2, "0");
|
|
67875
|
+
const num = _parseCsvNumber(v);
|
|
67876
|
+
if (!/^(0[1-9]|1[0-2])$/.test(key)) {
|
|
67877
|
+
errors.push(`Linha ${idx + 1}: m\xEAs inv\xE1lido: "${raw}"`);
|
|
67878
|
+
return;
|
|
67879
|
+
}
|
|
67880
|
+
if (!Number.isFinite(num) || num < 0) {
|
|
67881
|
+
errors.push(`Linha ${idx + 1}: valor inv\xE1lido: "${v}"`);
|
|
67882
|
+
return;
|
|
67883
|
+
}
|
|
67884
|
+
monthly[key] = num;
|
|
67885
|
+
});
|
|
67886
|
+
return { granularity: "month", monthly, errors };
|
|
67887
|
+
}
|
|
67888
|
+
function _aggregateDailyToMonthly(daily) {
|
|
67889
|
+
const monthly = {};
|
|
67890
|
+
Object.entries(daily || {}).forEach(([iso, val]) => {
|
|
67891
|
+
const mk = iso.slice(5, 7);
|
|
67892
|
+
monthly[mk] = (monthly[mk] || 0) + (parseFloat(val) || 0);
|
|
67893
|
+
});
|
|
67894
|
+
Object.keys(monthly).forEach((k) => {
|
|
67895
|
+
monthly[k] = Math.round(monthly[k] * 100) / 100;
|
|
67896
|
+
});
|
|
67897
|
+
return monthly;
|
|
67898
|
+
}
|
|
67899
|
+
function _handleGoalsImport(file) {
|
|
67900
|
+
const reader = new FileReader();
|
|
67901
|
+
reader.onload = () => {
|
|
67902
|
+
try {
|
|
67903
|
+
const parsed = _parseGoalsCsv(reader.result, modalState.granularity, modalState.currentYear);
|
|
67904
|
+
if (parsed.errors.length) {
|
|
67905
|
+
displayValidationErrors(parsed.errors.slice(0, 8));
|
|
67906
|
+
return;
|
|
67907
|
+
}
|
|
67908
|
+
const yd = getYearData(modalState.currentYear) || { annual: { total: 0, unit: "kWh" }, monthly: {}, assets: {} };
|
|
67909
|
+
if (parsed.granularity === "day") {
|
|
67910
|
+
yd.daily = parsed.daily;
|
|
67911
|
+
yd.granularity = "day";
|
|
67912
|
+
yd.monthly = _aggregateDailyToMonthly(parsed.daily);
|
|
67913
|
+
} else {
|
|
67914
|
+
yd.monthly = parsed.monthly;
|
|
67915
|
+
yd.granularity = "month";
|
|
67916
|
+
delete yd.daily;
|
|
67917
|
+
}
|
|
67918
|
+
if (!yd.annual) yd.annual = { total: 0, unit: "kWh" };
|
|
67919
|
+
if (!yd.annual.total) {
|
|
67920
|
+
const sum = Object.values(yd.monthly).reduce((s, v) => s + (parseFloat(v) || 0), 0);
|
|
67921
|
+
yd.annual.total = Math.round(sum * 100) / 100;
|
|
67922
|
+
}
|
|
67923
|
+
setYearData(modalState.currentYear, yd);
|
|
67924
|
+
modalState.isDirty = true;
|
|
67925
|
+
const errBox = document.getElementById("validation-errors");
|
|
67926
|
+
if (errBox) errBox.style.display = "none";
|
|
67927
|
+
renderTabContent();
|
|
67928
|
+
showSuccessMessage(GLABELS.imported);
|
|
67929
|
+
} catch (err) {
|
|
67930
|
+
displayValidationErrors([GLABELS.importErr + err.message]);
|
|
67931
|
+
}
|
|
67932
|
+
};
|
|
67933
|
+
reader.onerror = () => displayValidationErrors([GLABELS.readErr]);
|
|
67934
|
+
reader.readAsText(file, "utf-8");
|
|
67935
|
+
}
|
|
67726
67936
|
function loadGoalsData() {
|
|
67727
67937
|
if (!modalState.goalsData) {
|
|
67728
67938
|
modalState.goalsData = {
|
|
@@ -67808,6 +68018,9 @@ function openGoalsPanel(params) {
|
|
|
67808
68018
|
annual: { total, unit },
|
|
67809
68019
|
monthly,
|
|
67810
68020
|
assets: yearData.assets || {},
|
|
68021
|
+
// Preserva granularidade e detalhe diário importado (CSV) — não some no save.
|
|
68022
|
+
granularity: yearData.granularity || modalState.granularity || "month",
|
|
68023
|
+
...yearData.daily ? { daily: yearData.daily } : {},
|
|
67811
68024
|
metaTag: `${(/* @__PURE__ */ new Date()).toISOString()}|user`
|
|
67812
68025
|
};
|
|
67813
68026
|
}
|
package/dist/index.js
CHANGED
|
@@ -535,7 +535,7 @@ var init_template_card = __esm({
|
|
|
535
535
|
// package.json
|
|
536
536
|
var package_default = {
|
|
537
537
|
name: "myio-js-library",
|
|
538
|
-
version: "0.1.
|
|
538
|
+
version: "0.1.523",
|
|
539
539
|
description: "A clean, standalone JS SDK for MYIO projects",
|
|
540
540
|
license: "MIT",
|
|
541
541
|
repository: "github:gh-myio/myio-js-library",
|
|
@@ -65800,10 +65800,41 @@ function openGoalsPanel(params) {
|
|
|
65800
65800
|
zIndex: styles.zIndex || 1e4
|
|
65801
65801
|
};
|
|
65802
65802
|
const i18n2 = locale === "en-US" ? getEnglishStrings(entityLabel) : getPortugueseStrings(entityLabel);
|
|
65803
|
+
const GLABELS = locale === "en-US" ? {
|
|
65804
|
+
title: "Granularity",
|
|
65805
|
+
month: "Monthly",
|
|
65806
|
+
day: "Daily",
|
|
65807
|
+
hour: "Hourly (soon)",
|
|
65808
|
+
template: "Download template",
|
|
65809
|
+
importBtn: "Import spreadsheet",
|
|
65810
|
+
hintMonth: "12 values",
|
|
65811
|
+
hintDay: "365 values",
|
|
65812
|
+
hintHour: "8,760 \u2014 soon",
|
|
65813
|
+
derived: "Monthly values derived from imported daily detail (read-only).",
|
|
65814
|
+
imported: "Spreadsheet imported.",
|
|
65815
|
+
importErr: "Import error: ",
|
|
65816
|
+
readErr: "Failed to read file."
|
|
65817
|
+
} : {
|
|
65818
|
+
title: "Granularidade",
|
|
65819
|
+
month: "M\xEAs",
|
|
65820
|
+
day: "Di\xE1ria",
|
|
65821
|
+
hour: "Hora (em breve)",
|
|
65822
|
+
template: "Baixar template",
|
|
65823
|
+
importBtn: "Importar planilha",
|
|
65824
|
+
hintMonth: "12 valores",
|
|
65825
|
+
hintDay: "365 valores",
|
|
65826
|
+
hintHour: "8.760 \u2014 em breve",
|
|
65827
|
+
derived: "Valores mensais derivados do detalhe di\xE1rio importado (somente leitura).",
|
|
65828
|
+
imported: "Planilha importada.",
|
|
65829
|
+
importErr: "Erro ao importar: ",
|
|
65830
|
+
readErr: "Falha ao ler o arquivo."
|
|
65831
|
+
};
|
|
65803
65832
|
const MODAL_ID4 = "goals-modal";
|
|
65804
65833
|
let modalState = {
|
|
65805
65834
|
currentTab: "shopping",
|
|
65806
65835
|
// 'shopping' | 'assets'
|
|
65836
|
+
granularity: "month",
|
|
65837
|
+
// 'month' | 'day' ('hour' = em breve)
|
|
65807
65838
|
currentYear: (/* @__PURE__ */ new Date()).getFullYear(),
|
|
65808
65839
|
selectedShoppingId: shoppingList.length > 0 ? shoppingList[0].value : null,
|
|
65809
65840
|
goalsData: data || null,
|
|
@@ -65992,6 +66023,30 @@ function openGoalsPanel(params) {
|
|
|
65992
66023
|
</div>
|
|
65993
66024
|
</div>
|
|
65994
66025
|
|
|
66026
|
+
<!-- Granularity selector (M\xEAs | Di\xE1ria | Hora em breve) + template/import -->
|
|
66027
|
+
<div class="myio-goals-section">
|
|
66028
|
+
<div class="myio-goals-section-header">
|
|
66029
|
+
<h3 class="myio-goals-section-title">${GLABELS.title}</h3>
|
|
66030
|
+
<div>
|
|
66031
|
+
<button class="myio-goals-btn-link" data-action="goals-template">\u2B07\uFE0F ${GLABELS.template}</button>
|
|
66032
|
+
<button class="myio-goals-btn-link" data-action="goals-import">\u{1F4E4} ${GLABELS.importBtn}</button>
|
|
66033
|
+
<input type="file" id="goals-import-file" accept=".csv,text/csv" style="display:none" />
|
|
66034
|
+
</div>
|
|
66035
|
+
</div>
|
|
66036
|
+
<div class="myio-goals-gran-chips" style="display:flex;gap:8px;margin-top:8px;flex-wrap:wrap;">
|
|
66037
|
+
${["month", "day", "hour"].map((g) => {
|
|
66038
|
+
const active = modalState.granularity === g;
|
|
66039
|
+
const disabled = g === "hour";
|
|
66040
|
+
const label = g === "month" ? GLABELS.month : g === "day" ? GLABELS.day : GLABELS.hour;
|
|
66041
|
+
const hint = g === "month" ? GLABELS.hintMonth : g === "day" ? GLABELS.hintDay : GLABELS.hintHour;
|
|
66042
|
+
return `<button type="button" class="myio-goals-gran-chip" data-gran="${g}" ${disabled ? "disabled" : ""}
|
|
66043
|
+
style="padding:6px 12px;border:1px solid ${theme.primaryColor};border-radius:16px;cursor:${disabled ? "not-allowed" : "pointer"};font-size:12px;${active ? `background:${theme.primaryColor};color:#fff;` : `background:#fff;color:${theme.primaryColor};`}${disabled ? "opacity:.5;" : ""}"
|
|
66044
|
+
title="${disabled ? GLABELS.hour : label}">${label} <small>(${hint})</small></button>`;
|
|
66045
|
+
}).join("")}
|
|
66046
|
+
</div>
|
|
66047
|
+
${modalState.granularity === "day" && Object.keys((getYearData(modalState.currentYear) || {}).daily || {}).length > 0 ? `<div class="myio-goals-gran-note" style="margin-top:8px;font-size:12px;color:#666;">${GLABELS.derived}</div>` : ""}
|
|
66048
|
+
</div>
|
|
66049
|
+
|
|
65995
66050
|
<!-- Monthly Distribution Section -->
|
|
65996
66051
|
<div class="myio-goals-section">
|
|
65997
66052
|
<div class="myio-goals-section-header">
|
|
@@ -66043,6 +66098,7 @@ function openGoalsPanel(params) {
|
|
|
66043
66098
|
value="${monthly[month.key] || ""}"
|
|
66044
66099
|
min="0"
|
|
66045
66100
|
step="0.01"
|
|
66101
|
+
${modalState.granularity === "day" ? 'disabled title="Derivado do detalhe di\xE1rio \u2014 edite via planilha"' : ""}
|
|
66046
66102
|
placeholder="0">
|
|
66047
66103
|
<span class="myio-goals-unit">${unit}</span>
|
|
66048
66104
|
</div>
|
|
@@ -66244,6 +66300,27 @@ function openGoalsPanel(params) {
|
|
|
66244
66300
|
container.querySelector('[data-action="auto-fill"]')?.addEventListener("click", () => {
|
|
66245
66301
|
autoFillMonthly();
|
|
66246
66302
|
});
|
|
66303
|
+
container.querySelectorAll("[data-gran]").forEach((chip) => {
|
|
66304
|
+
chip.addEventListener("click", () => {
|
|
66305
|
+
if (chip.disabled) return;
|
|
66306
|
+
const g = chip.getAttribute("data-gran");
|
|
66307
|
+
if (g === "hour" || g === modalState.granularity) return;
|
|
66308
|
+
modalState.granularity = g;
|
|
66309
|
+
renderTabContent();
|
|
66310
|
+
});
|
|
66311
|
+
});
|
|
66312
|
+
container.querySelector('[data-action="goals-template"]')?.addEventListener("click", () => {
|
|
66313
|
+
_downloadGoalsTemplate();
|
|
66314
|
+
});
|
|
66315
|
+
const fileInput = container.querySelector("#goals-import-file");
|
|
66316
|
+
container.querySelector('[data-action="goals-import"]')?.addEventListener("click", () => {
|
|
66317
|
+
fileInput?.click();
|
|
66318
|
+
});
|
|
66319
|
+
fileInput?.addEventListener("change", (e) => {
|
|
66320
|
+
const file = e.target.files && e.target.files[0];
|
|
66321
|
+
if (file) _handleGoalsImport(file);
|
|
66322
|
+
e.target.value = "";
|
|
66323
|
+
});
|
|
66247
66324
|
}
|
|
66248
66325
|
function attachAssetsTabListeners() {
|
|
66249
66326
|
const container = document.getElementById("tab-content-area");
|
|
@@ -66293,6 +66370,139 @@ function openGoalsPanel(params) {
|
|
|
66293
66370
|
}
|
|
66294
66371
|
modalState.goalsData.years[year.toString()] = data2;
|
|
66295
66372
|
}
|
|
66373
|
+
function _eachDateOfYear(year) {
|
|
66374
|
+
const out = [];
|
|
66375
|
+
const d = new Date(Date.UTC(year, 0, 1));
|
|
66376
|
+
while (d.getUTCFullYear() === year) {
|
|
66377
|
+
out.push(d.toISOString().slice(0, 10));
|
|
66378
|
+
d.setUTCDate(d.getUTCDate() + 1);
|
|
66379
|
+
}
|
|
66380
|
+
return out;
|
|
66381
|
+
}
|
|
66382
|
+
function _buildGoalsTemplateCsv(granularity, year) {
|
|
66383
|
+
const yd = getYearData(year) || {};
|
|
66384
|
+
if (granularity === "day") {
|
|
66385
|
+
const daily = yd.daily || {};
|
|
66386
|
+
const rows2 = _eachDateOfYear(year).map((iso) => `${iso};${daily[iso] != null ? daily[iso] : 0}`);
|
|
66387
|
+
return "data;valor\n" + rows2.join("\n");
|
|
66388
|
+
}
|
|
66389
|
+
const monthly = yd.monthly || {};
|
|
66390
|
+
const rows = [];
|
|
66391
|
+
for (let i = 1; i <= 12; i++) {
|
|
66392
|
+
const k = String(i).padStart(2, "0");
|
|
66393
|
+
rows.push(`${year}-${k};${monthly[k] != null ? monthly[k] : 0}`);
|
|
66394
|
+
}
|
|
66395
|
+
return "mes;valor\n" + rows.join("\n");
|
|
66396
|
+
}
|
|
66397
|
+
function _downloadGoalsTemplate() {
|
|
66398
|
+
const g = modalState.granularity === "day" ? "day" : "month";
|
|
66399
|
+
const year = modalState.currentYear;
|
|
66400
|
+
const csv = "\uFEFF" + _buildGoalsTemplateCsv(g, year);
|
|
66401
|
+
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
|
|
66402
|
+
const url = URL.createObjectURL(blob);
|
|
66403
|
+
const a = document.createElement("a");
|
|
66404
|
+
a.href = url;
|
|
66405
|
+
a.download = `metas-${g === "day" ? "diaria" : "mensal"}-${year}.csv`;
|
|
66406
|
+
document.body.appendChild(a);
|
|
66407
|
+
a.click();
|
|
66408
|
+
document.body.removeChild(a);
|
|
66409
|
+
URL.revokeObjectURL(url);
|
|
66410
|
+
}
|
|
66411
|
+
function _parseCsvNumber(s) {
|
|
66412
|
+
let t = String(s == null ? "" : s).trim();
|
|
66413
|
+
if (t.includes(",")) t = t.replace(/\./g, "").replace(",", ".");
|
|
66414
|
+
const v = parseFloat(t);
|
|
66415
|
+
return Number.isFinite(v) ? v : NaN;
|
|
66416
|
+
}
|
|
66417
|
+
function _parseGoalsCsv(text, granularity, year) {
|
|
66418
|
+
const errors = [];
|
|
66419
|
+
const lines = String(text).replace(/^/, "").split(/\r?\n/).map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
66420
|
+
if (lines.length && !Number.isFinite(_parseCsvNumber(lines[0].split(";")[1] || ""))) lines.shift();
|
|
66421
|
+
if (granularity === "day") {
|
|
66422
|
+
const daily = {};
|
|
66423
|
+
const valid = new Set(_eachDateOfYear(year));
|
|
66424
|
+
lines.forEach((line, idx) => {
|
|
66425
|
+
const [d, v] = line.split(";");
|
|
66426
|
+
const iso = (d || "").trim();
|
|
66427
|
+
const num = _parseCsvNumber(v);
|
|
66428
|
+
if (!valid.has(iso)) {
|
|
66429
|
+
errors.push(`Linha ${idx + 1}: data inv\xE1lida ou fora de ${year}: "${iso}"`);
|
|
66430
|
+
return;
|
|
66431
|
+
}
|
|
66432
|
+
if (!Number.isFinite(num) || num < 0) {
|
|
66433
|
+
errors.push(`Linha ${idx + 1}: valor inv\xE1lido: "${v}"`);
|
|
66434
|
+
return;
|
|
66435
|
+
}
|
|
66436
|
+
daily[iso] = num;
|
|
66437
|
+
});
|
|
66438
|
+
return { granularity: "day", daily, errors };
|
|
66439
|
+
}
|
|
66440
|
+
const monthly = {};
|
|
66441
|
+
lines.forEach((line, idx) => {
|
|
66442
|
+
const [m, v] = line.split(";");
|
|
66443
|
+
const raw = (m || "").trim();
|
|
66444
|
+
const key = raw.includes("-") ? raw.split("-").pop().padStart(2, "0") : raw.padStart(2, "0");
|
|
66445
|
+
const num = _parseCsvNumber(v);
|
|
66446
|
+
if (!/^(0[1-9]|1[0-2])$/.test(key)) {
|
|
66447
|
+
errors.push(`Linha ${idx + 1}: m\xEAs inv\xE1lido: "${raw}"`);
|
|
66448
|
+
return;
|
|
66449
|
+
}
|
|
66450
|
+
if (!Number.isFinite(num) || num < 0) {
|
|
66451
|
+
errors.push(`Linha ${idx + 1}: valor inv\xE1lido: "${v}"`);
|
|
66452
|
+
return;
|
|
66453
|
+
}
|
|
66454
|
+
monthly[key] = num;
|
|
66455
|
+
});
|
|
66456
|
+
return { granularity: "month", monthly, errors };
|
|
66457
|
+
}
|
|
66458
|
+
function _aggregateDailyToMonthly(daily) {
|
|
66459
|
+
const monthly = {};
|
|
66460
|
+
Object.entries(daily || {}).forEach(([iso, val]) => {
|
|
66461
|
+
const mk = iso.slice(5, 7);
|
|
66462
|
+
monthly[mk] = (monthly[mk] || 0) + (parseFloat(val) || 0);
|
|
66463
|
+
});
|
|
66464
|
+
Object.keys(monthly).forEach((k) => {
|
|
66465
|
+
monthly[k] = Math.round(monthly[k] * 100) / 100;
|
|
66466
|
+
});
|
|
66467
|
+
return monthly;
|
|
66468
|
+
}
|
|
66469
|
+
function _handleGoalsImport(file) {
|
|
66470
|
+
const reader = new FileReader();
|
|
66471
|
+
reader.onload = () => {
|
|
66472
|
+
try {
|
|
66473
|
+
const parsed = _parseGoalsCsv(reader.result, modalState.granularity, modalState.currentYear);
|
|
66474
|
+
if (parsed.errors.length) {
|
|
66475
|
+
displayValidationErrors(parsed.errors.slice(0, 8));
|
|
66476
|
+
return;
|
|
66477
|
+
}
|
|
66478
|
+
const yd = getYearData(modalState.currentYear) || { annual: { total: 0, unit: "kWh" }, monthly: {}, assets: {} };
|
|
66479
|
+
if (parsed.granularity === "day") {
|
|
66480
|
+
yd.daily = parsed.daily;
|
|
66481
|
+
yd.granularity = "day";
|
|
66482
|
+
yd.monthly = _aggregateDailyToMonthly(parsed.daily);
|
|
66483
|
+
} else {
|
|
66484
|
+
yd.monthly = parsed.monthly;
|
|
66485
|
+
yd.granularity = "month";
|
|
66486
|
+
delete yd.daily;
|
|
66487
|
+
}
|
|
66488
|
+
if (!yd.annual) yd.annual = { total: 0, unit: "kWh" };
|
|
66489
|
+
if (!yd.annual.total) {
|
|
66490
|
+
const sum = Object.values(yd.monthly).reduce((s, v) => s + (parseFloat(v) || 0), 0);
|
|
66491
|
+
yd.annual.total = Math.round(sum * 100) / 100;
|
|
66492
|
+
}
|
|
66493
|
+
setYearData(modalState.currentYear, yd);
|
|
66494
|
+
modalState.isDirty = true;
|
|
66495
|
+
const errBox = document.getElementById("validation-errors");
|
|
66496
|
+
if (errBox) errBox.style.display = "none";
|
|
66497
|
+
renderTabContent();
|
|
66498
|
+
showSuccessMessage(GLABELS.imported);
|
|
66499
|
+
} catch (err) {
|
|
66500
|
+
displayValidationErrors([GLABELS.importErr + err.message]);
|
|
66501
|
+
}
|
|
66502
|
+
};
|
|
66503
|
+
reader.onerror = () => displayValidationErrors([GLABELS.readErr]);
|
|
66504
|
+
reader.readAsText(file, "utf-8");
|
|
66505
|
+
}
|
|
66296
66506
|
function loadGoalsData() {
|
|
66297
66507
|
if (!modalState.goalsData) {
|
|
66298
66508
|
modalState.goalsData = {
|
|
@@ -66378,6 +66588,9 @@ function openGoalsPanel(params) {
|
|
|
66378
66588
|
annual: { total, unit },
|
|
66379
66589
|
monthly,
|
|
66380
66590
|
assets: yearData.assets || {},
|
|
66591
|
+
// Preserva granularidade e detalhe diário importado (CSV) — não some no save.
|
|
66592
|
+
granularity: yearData.granularity || modalState.granularity || "month",
|
|
66593
|
+
...yearData.daily ? { daily: yearData.daily } : {},
|
|
66381
66594
|
metaTag: `${(/* @__PURE__ */ new Date()).toISOString()}|user`
|
|
66382
66595
|
};
|
|
66383
66596
|
}
|