myio-js-library 0.1.172 → 0.1.173

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.js CHANGED
@@ -16639,6 +16639,1002 @@ function validateOptions2(options) {
16639
16639
  }
16640
16640
  }
16641
16641
 
16642
+ // src/components/premium-modals/power-limits/types.ts
16643
+ var DEVICE_TYPES = [
16644
+ { value: "ELEVADOR", label: "Elevator" },
16645
+ { value: "ESCADA_ROLANTE", label: "Escalator" },
16646
+ { value: "MOTOR", label: "Motor" },
16647
+ { value: "BOMBA", label: "Pump" },
16648
+ { value: "CHILLER", label: "Chiller" },
16649
+ { value: "AR_CONDICIONADO", label: "Air Conditioner" },
16650
+ { value: "HVAC", label: "HVAC" },
16651
+ { value: "FANCOIL", label: "Fancoil" },
16652
+ { value: "3F_MEDIDOR", label: "Three-phase Meter" }
16653
+ ];
16654
+ var TELEMETRY_TYPES2 = [
16655
+ { value: "consumption", label: "Consumption (kW)", unit: "kW" },
16656
+ { value: "voltage_a", label: "Voltage A (V)", unit: "V" },
16657
+ { value: "voltage_b", label: "Voltage B (V)", unit: "V" },
16658
+ { value: "voltage_c", label: "Voltage C (V)", unit: "V" },
16659
+ { value: "current_a", label: "Current A (A)", unit: "A" },
16660
+ { value: "current_b", label: "Current B (A)", unit: "A" },
16661
+ { value: "current_c", label: "Current C (A)", unit: "A" },
16662
+ { value: "total_current", label: "Total Current (A)", unit: "A" },
16663
+ { value: "fp_a", label: "Power Factor A", unit: "" },
16664
+ { value: "fp_b", label: "Power Factor B", unit: "" },
16665
+ { value: "fp_c", label: "Power Factor C", unit: "" }
16666
+ ];
16667
+ var STATUS_CONFIG = {
16668
+ standBy: { label: "StandBy", color: "#22c55e", bgColor: "rgba(34, 197, 94, 0.1)" },
16669
+ normal: { label: "Normal", color: "#3b82f6", bgColor: "rgba(59, 130, 246, 0.1)" },
16670
+ alert: { label: "Alert", color: "#f59e0b", bgColor: "rgba(245, 158, 11, 0.1)" },
16671
+ failure: { label: "Failure", color: "#ef4444", bgColor: "rgba(239, 68, 68, 0.1)" }
16672
+ };
16673
+
16674
+ // src/components/premium-modals/power-limits/PowerLimitsModalView.ts
16675
+ var PowerLimitsModalView = class {
16676
+ container = null;
16677
+ overlayEl = null;
16678
+ config;
16679
+ formData;
16680
+ isLoading = false;
16681
+ isSaving = false;
16682
+ constructor(config) {
16683
+ this.config = config;
16684
+ this.formData = {
16685
+ deviceType: config.deviceType,
16686
+ telemetryType: config.telemetryType,
16687
+ standby: { baseValue: null, topValue: null },
16688
+ normal: { baseValue: null, topValue: null },
16689
+ alert: { baseValue: null, topValue: null },
16690
+ failure: { baseValue: null, topValue: null }
16691
+ };
16692
+ }
16693
+ render(targetContainer) {
16694
+ this.overlayEl = document.createElement("div");
16695
+ this.overlayEl.className = "myio-power-limits-overlay";
16696
+ this.overlayEl.innerHTML = `
16697
+ <style>${this.getStyles()}</style>
16698
+ <div class="myio-power-limits-card">
16699
+ ${this.renderHeader()}
16700
+ ${this.renderSelectors()}
16701
+ ${this.renderStatusCards()}
16702
+ ${this.renderLoadingState()}
16703
+ ${this.renderErrorState()}
16704
+ ${this.renderSuccessState()}
16705
+ </div>
16706
+ `;
16707
+ const target = targetContainer || document.body;
16708
+ target.appendChild(this.overlayEl);
16709
+ this.container = this.overlayEl.querySelector(".myio-power-limits-card");
16710
+ this.setupEventListeners();
16711
+ requestAnimationFrame(() => {
16712
+ this.overlayEl?.classList.add("active");
16713
+ });
16714
+ return this.overlayEl;
16715
+ }
16716
+ renderHeader() {
16717
+ return `
16718
+ <div class="myio-power-limits-header">
16719
+ <div class="myio-power-limits-title-section">
16720
+ <span class="myio-power-limits-icon">&#x2699;</span>
16721
+ <h2 class="myio-power-limits-title">Power Limits Setup</h2>
16722
+ </div>
16723
+ <div class="myio-power-limits-actions">
16724
+ <button class="myio-btn myio-btn-primary" id="plm-save-btn" type="button">
16725
+ <span class="myio-btn-text">Save</span>
16726
+ <span class="myio-btn-spinner" style="display: none;"></span>
16727
+ </button>
16728
+ <button class="myio-btn myio-btn-secondary" id="plm-reset-btn" type="button">Reset</button>
16729
+ <button class="myio-btn myio-btn-close" id="plm-close-btn" type="button" aria-label="Close">&times;</button>
16730
+ </div>
16731
+ </div>
16732
+ `;
16733
+ }
16734
+ renderSelectors() {
16735
+ const deviceOptions = DEVICE_TYPES.map(
16736
+ (dt) => `<option value="${dt.value}" ${dt.value === this.config.deviceType ? "selected" : ""}>${dt.label}</option>`
16737
+ ).join("");
16738
+ const telemetryOptions = TELEMETRY_TYPES2.map(
16739
+ (tt) => `<option value="${tt.value}" ${tt.value === this.config.telemetryType ? "selected" : ""}>${tt.label}</option>`
16740
+ ).join("");
16741
+ return `
16742
+ <div class="myio-power-limits-selectors">
16743
+ <div class="myio-form-group">
16744
+ <label for="plm-device-type">Device Type</label>
16745
+ <select id="plm-device-type" class="myio-select">
16746
+ ${deviceOptions}
16747
+ </select>
16748
+ </div>
16749
+ <div class="myio-form-group">
16750
+ <label for="plm-telemetry-type">Telemetry Type</label>
16751
+ <select id="plm-telemetry-type" class="myio-select">
16752
+ ${telemetryOptions}
16753
+ </select>
16754
+ </div>
16755
+ </div>
16756
+ `;
16757
+ }
16758
+ renderStatusCards() {
16759
+ const statuses = ["standby", "normal", "alert", "failure"];
16760
+ const cards = statuses.map((status) => {
16761
+ const config = STATUS_CONFIG[status === "standby" ? "standBy" : status];
16762
+ const formValues = this.formData[status];
16763
+ return `
16764
+ <div class="myio-power-limits-card-item myio-status-${status}" style="--status-color: ${config.color}; --status-bg: ${config.bgColor};">
16765
+ <div class="myio-card-header">
16766
+ <span class="myio-status-indicator"></span>
16767
+ <span class="myio-status-label">${config.label}</span>
16768
+ </div>
16769
+ <div class="myio-card-inputs">
16770
+ <div class="myio-input-group">
16771
+ <label for="plm-${status}-base">Base Value</label>
16772
+ <input
16773
+ type="number"
16774
+ id="plm-${status}-base"
16775
+ class="myio-input"
16776
+ min="0"
16777
+ step="0.01"
16778
+ value="${formValues.baseValue ?? ""}"
16779
+ placeholder="0"
16780
+ >
16781
+ </div>
16782
+ <div class="myio-input-group">
16783
+ <label for="plm-${status}-top">Top Value</label>
16784
+ <input
16785
+ type="number"
16786
+ id="plm-${status}-top"
16787
+ class="myio-input"
16788
+ min="0"
16789
+ step="0.01"
16790
+ value="${formValues.topValue ?? ""}"
16791
+ placeholder="0"
16792
+ >
16793
+ </div>
16794
+ </div>
16795
+ </div>
16796
+ `;
16797
+ }).join("");
16798
+ return `
16799
+ <div class="myio-power-limits-grid" id="plm-grid">
16800
+ ${cards}
16801
+ </div>
16802
+ `;
16803
+ }
16804
+ renderLoadingState() {
16805
+ return `
16806
+ <div class="myio-power-limits-loading" id="plm-loading" style="display: none;">
16807
+ <div class="myio-spinner"></div>
16808
+ <span>Loading configuration...</span>
16809
+ </div>
16810
+ `;
16811
+ }
16812
+ renderErrorState() {
16813
+ return `
16814
+ <div class="myio-power-limits-error" id="plm-error" style="display: none;">
16815
+ <span class="myio-error-icon">&#x26A0;</span>
16816
+ <span class="myio-error-message" id="plm-error-msg"></span>
16817
+ </div>
16818
+ `;
16819
+ }
16820
+ renderSuccessState() {
16821
+ return `
16822
+ <div class="myio-power-limits-success" id="plm-success" style="display: none;">
16823
+ <span class="myio-success-icon">&#x2713;</span>
16824
+ <span class="myio-success-message">Configuration saved successfully!</span>
16825
+ </div>
16826
+ `;
16827
+ }
16828
+ setupEventListeners() {
16829
+ if (!this.overlayEl) return;
16830
+ const closeBtn = this.overlayEl.querySelector("#plm-close-btn");
16831
+ closeBtn?.addEventListener("click", () => this.close());
16832
+ this.overlayEl.addEventListener("click", (e) => {
16833
+ if (e.target === this.overlayEl) {
16834
+ this.close();
16835
+ }
16836
+ });
16837
+ document.addEventListener("keydown", this.handleKeyDown);
16838
+ const saveBtn = this.overlayEl.querySelector("#plm-save-btn");
16839
+ saveBtn?.addEventListener("click", () => this.handleSave());
16840
+ const resetBtn = this.overlayEl.querySelector("#plm-reset-btn");
16841
+ resetBtn?.addEventListener("click", () => this.handleReset());
16842
+ const deviceSelect = this.overlayEl.querySelector("#plm-device-type");
16843
+ deviceSelect?.addEventListener("change", (e) => {
16844
+ const value = e.target.value;
16845
+ this.formData.deviceType = value;
16846
+ this.config.onDeviceTypeChange(value);
16847
+ });
16848
+ const telemetrySelect = this.overlayEl.querySelector("#plm-telemetry-type");
16849
+ telemetrySelect?.addEventListener("change", (e) => {
16850
+ const value = e.target.value;
16851
+ this.formData.telemetryType = value;
16852
+ this.config.onTelemetryTypeChange(value);
16853
+ });
16854
+ const statuses = ["standby", "normal", "alert", "failure"];
16855
+ statuses.forEach((status) => {
16856
+ const baseInput = this.overlayEl?.querySelector(`#plm-${status}-base`);
16857
+ const topInput = this.overlayEl?.querySelector(`#plm-${status}-top`);
16858
+ baseInput?.addEventListener("input", (e) => {
16859
+ const value = e.target.value;
16860
+ this.formData[status].baseValue = value ? parseFloat(value) : null;
16861
+ });
16862
+ topInput?.addEventListener("input", (e) => {
16863
+ const value = e.target.value;
16864
+ this.formData[status].topValue = value ? parseFloat(value) : null;
16865
+ });
16866
+ });
16867
+ }
16868
+ handleKeyDown = (e) => {
16869
+ if (e.key === "Escape") {
16870
+ this.close();
16871
+ }
16872
+ };
16873
+ async handleSave() {
16874
+ if (this.isSaving) return;
16875
+ const validationError = this.validateForm();
16876
+ if (validationError) {
16877
+ this.showError(validationError);
16878
+ return;
16879
+ }
16880
+ this.isSaving = true;
16881
+ this.showSaveLoading(true);
16882
+ this.hideError();
16883
+ this.hideSuccess();
16884
+ try {
16885
+ await this.config.onSave();
16886
+ this.showSuccess();
16887
+ setTimeout(() => this.hideSuccess(), 3e3);
16888
+ } catch (error) {
16889
+ this.showError(error.message || "Failed to save configuration");
16890
+ } finally {
16891
+ this.isSaving = false;
16892
+ this.showSaveLoading(false);
16893
+ }
16894
+ }
16895
+ handleReset() {
16896
+ const statuses = ["standby", "normal", "alert", "failure"];
16897
+ statuses.forEach((status) => {
16898
+ const baseInput = this.overlayEl?.querySelector(`#plm-${status}-base`);
16899
+ const topInput = this.overlayEl?.querySelector(`#plm-${status}-top`);
16900
+ if (baseInput) baseInput.value = "";
16901
+ if (topInput) topInput.value = "";
16902
+ this.formData[status] = { baseValue: null, topValue: null };
16903
+ });
16904
+ this.hideError();
16905
+ this.hideSuccess();
16906
+ }
16907
+ validateForm() {
16908
+ const statuses = ["standby", "normal", "alert", "failure"];
16909
+ for (const status of statuses) {
16910
+ const base = this.formData[status].baseValue;
16911
+ const top = this.formData[status].topValue;
16912
+ if (base !== null && base < 0) {
16913
+ return `${STATUS_CONFIG[status === "standby" ? "standBy" : status].label}: Base value cannot be negative`;
16914
+ }
16915
+ if (top !== null && top < 0) {
16916
+ return `${STATUS_CONFIG[status === "standby" ? "standBy" : status].label}: Top value cannot be negative`;
16917
+ }
16918
+ if (base !== null && top !== null && base > top) {
16919
+ return `${STATUS_CONFIG[status === "standby" ? "standBy" : status].label}: Base value should not exceed top value`;
16920
+ }
16921
+ }
16922
+ return null;
16923
+ }
16924
+ close() {
16925
+ document.removeEventListener("keydown", this.handleKeyDown);
16926
+ if (this.overlayEl) {
16927
+ this.overlayEl.classList.remove("active");
16928
+ setTimeout(() => {
16929
+ this.overlayEl?.remove();
16930
+ this.overlayEl = null;
16931
+ this.container = null;
16932
+ this.config.onClose();
16933
+ }, 300);
16934
+ }
16935
+ }
16936
+ destroy() {
16937
+ document.removeEventListener("keydown", this.handleKeyDown);
16938
+ this.overlayEl?.remove();
16939
+ this.overlayEl = null;
16940
+ this.container = null;
16941
+ }
16942
+ showLoading() {
16943
+ this.isLoading = true;
16944
+ const loadingEl = this.overlayEl?.querySelector("#plm-loading");
16945
+ const gridEl = this.overlayEl?.querySelector("#plm-grid");
16946
+ if (loadingEl) loadingEl.style.display = "flex";
16947
+ if (gridEl) gridEl.style.opacity = "0.5";
16948
+ }
16949
+ hideLoading() {
16950
+ this.isLoading = false;
16951
+ const loadingEl = this.overlayEl?.querySelector("#plm-loading");
16952
+ const gridEl = this.overlayEl?.querySelector("#plm-grid");
16953
+ if (loadingEl) loadingEl.style.display = "none";
16954
+ if (gridEl) gridEl.style.opacity = "1";
16955
+ }
16956
+ showSaveLoading(show) {
16957
+ const saveBtn = this.overlayEl?.querySelector("#plm-save-btn");
16958
+ const btnText = saveBtn?.querySelector(".myio-btn-text");
16959
+ const btnSpinner = saveBtn?.querySelector(".myio-btn-spinner");
16960
+ if (saveBtn) saveBtn.disabled = show;
16961
+ if (btnText) btnText.style.display = show ? "none" : "inline";
16962
+ if (btnSpinner) btnSpinner.style.display = show ? "inline-block" : "none";
16963
+ }
16964
+ showError(message) {
16965
+ const errorEl = this.overlayEl?.querySelector("#plm-error");
16966
+ const errorMsg = this.overlayEl?.querySelector("#plm-error-msg");
16967
+ if (errorEl) errorEl.style.display = "flex";
16968
+ if (errorMsg) errorMsg.textContent = message;
16969
+ }
16970
+ hideError() {
16971
+ const errorEl = this.overlayEl?.querySelector("#plm-error");
16972
+ if (errorEl) errorEl.style.display = "none";
16973
+ }
16974
+ showSuccess() {
16975
+ const successEl = this.overlayEl?.querySelector("#plm-success");
16976
+ if (successEl) successEl.style.display = "flex";
16977
+ }
16978
+ hideSuccess() {
16979
+ const successEl = this.overlayEl?.querySelector("#plm-success");
16980
+ if (successEl) successEl.style.display = "none";
16981
+ }
16982
+ getFormData() {
16983
+ return { ...this.formData };
16984
+ }
16985
+ setFormData(data) {
16986
+ if (data.deviceType) this.formData.deviceType = data.deviceType;
16987
+ if (data.telemetryType) this.formData.telemetryType = data.telemetryType;
16988
+ if (data.standby) this.formData.standby = { ...data.standby };
16989
+ if (data.normal) this.formData.normal = { ...data.normal };
16990
+ if (data.alert) this.formData.alert = { ...data.alert };
16991
+ if (data.failure) this.formData.failure = { ...data.failure };
16992
+ this.updateInputValues();
16993
+ }
16994
+ updateInputValues() {
16995
+ const statuses = ["standby", "normal", "alert", "failure"];
16996
+ statuses.forEach((status) => {
16997
+ const baseInput = this.overlayEl?.querySelector(`#plm-${status}-base`);
16998
+ const topInput = this.overlayEl?.querySelector(`#plm-${status}-top`);
16999
+ if (baseInput) {
17000
+ baseInput.value = this.formData[status].baseValue?.toString() ?? "";
17001
+ }
17002
+ if (topInput) {
17003
+ topInput.value = this.formData[status].topValue?.toString() ?? "";
17004
+ }
17005
+ });
17006
+ const deviceSelect = this.overlayEl?.querySelector("#plm-device-type");
17007
+ const telemetrySelect = this.overlayEl?.querySelector("#plm-telemetry-type");
17008
+ if (deviceSelect) deviceSelect.value = this.formData.deviceType;
17009
+ if (telemetrySelect) telemetrySelect.value = this.formData.telemetryType;
17010
+ }
17011
+ getStyles() {
17012
+ const styles = this.config.styles || {};
17013
+ const primaryColor = styles.primaryColor || "#4A148C";
17014
+ const successColor = styles.successColor || "#22c55e";
17015
+ const warningColor = styles.warningColor || "#f59e0b";
17016
+ const dangerColor = styles.dangerColor || "#ef4444";
17017
+ const infoColor = styles.infoColor || "#3b82f6";
17018
+ return `
17019
+ .myio-power-limits-overlay {
17020
+ position: fixed;
17021
+ top: 0;
17022
+ left: 0;
17023
+ right: 0;
17024
+ bottom: 0;
17025
+ background: rgba(0, 0, 0, 0.6);
17026
+ display: flex;
17027
+ align-items: center;
17028
+ justify-content: center;
17029
+ z-index: ${styles.zIndex || 1e4};
17030
+ opacity: 0;
17031
+ visibility: hidden;
17032
+ transition: all 0.3s ease;
17033
+ font-family: ${styles.fontFamily || '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'};
17034
+ }
17035
+
17036
+ .myio-power-limits-overlay.active {
17037
+ opacity: 1;
17038
+ visibility: visible;
17039
+ }
17040
+
17041
+ .myio-power-limits-card {
17042
+ background: ${styles.backgroundColor || "#ffffff"};
17043
+ border-radius: ${styles.borderRadius || "12px"};
17044
+ width: 90%;
17045
+ max-width: 700px;
17046
+ max-height: 90vh;
17047
+ overflow-y: auto;
17048
+ transform: scale(0.9);
17049
+ transition: transform 0.3s ease;
17050
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
17051
+ }
17052
+
17053
+ .myio-power-limits-overlay.active .myio-power-limits-card {
17054
+ transform: scale(1);
17055
+ }
17056
+
17057
+ .myio-power-limits-header {
17058
+ display: flex;
17059
+ align-items: center;
17060
+ justify-content: space-between;
17061
+ padding: 20px 24px;
17062
+ background: linear-gradient(135deg, ${primaryColor}, ${this.lightenColor(primaryColor, 20)});
17063
+ color: white;
17064
+ border-radius: 12px 12px 0 0;
17065
+ }
17066
+
17067
+ .myio-power-limits-title-section {
17068
+ display: flex;
17069
+ align-items: center;
17070
+ gap: 12px;
17071
+ }
17072
+
17073
+ .myio-power-limits-icon {
17074
+ font-size: 24px;
17075
+ }
17076
+
17077
+ .myio-power-limits-title {
17078
+ font-size: 1.25rem;
17079
+ font-weight: 600;
17080
+ margin: 0;
17081
+ }
17082
+
17083
+ .myio-power-limits-actions {
17084
+ display: flex;
17085
+ align-items: center;
17086
+ gap: 8px;
17087
+ }
17088
+
17089
+ .myio-btn {
17090
+ padding: 8px 16px;
17091
+ border-radius: ${styles.buttonRadius || "6px"};
17092
+ font-size: 14px;
17093
+ font-weight: 500;
17094
+ cursor: pointer;
17095
+ border: none;
17096
+ transition: all 0.2s;
17097
+ display: inline-flex;
17098
+ align-items: center;
17099
+ gap: 6px;
17100
+ }
17101
+
17102
+ .myio-btn:disabled {
17103
+ opacity: 0.6;
17104
+ cursor: not-allowed;
17105
+ }
17106
+
17107
+ .myio-btn-primary {
17108
+ background: white;
17109
+ color: ${primaryColor};
17110
+ }
17111
+
17112
+ .myio-btn-primary:hover:not(:disabled) {
17113
+ background: #f3f4f6;
17114
+ }
17115
+
17116
+ .myio-btn-secondary {
17117
+ background: rgba(255, 255, 255, 0.2);
17118
+ color: white;
17119
+ }
17120
+
17121
+ .myio-btn-secondary:hover:not(:disabled) {
17122
+ background: rgba(255, 255, 255, 0.3);
17123
+ }
17124
+
17125
+ .myio-btn-close {
17126
+ background: transparent;
17127
+ color: white;
17128
+ font-size: 24px;
17129
+ padding: 4px 8px;
17130
+ line-height: 1;
17131
+ }
17132
+
17133
+ .myio-btn-close:hover {
17134
+ background: rgba(255, 255, 255, 0.1);
17135
+ }
17136
+
17137
+ .myio-btn-spinner {
17138
+ width: 16px;
17139
+ height: 16px;
17140
+ border: 2px solid ${primaryColor};
17141
+ border-top-color: transparent;
17142
+ border-radius: 50%;
17143
+ animation: spin 0.8s linear infinite;
17144
+ }
17145
+
17146
+ @keyframes spin {
17147
+ to { transform: rotate(360deg); }
17148
+ }
17149
+
17150
+ .myio-power-limits-selectors {
17151
+ display: grid;
17152
+ grid-template-columns: 1fr 1fr;
17153
+ gap: 16px;
17154
+ padding: 20px 24px;
17155
+ background: #f9fafb;
17156
+ border-bottom: 1px solid #e5e7eb;
17157
+ }
17158
+
17159
+ .myio-form-group {
17160
+ display: flex;
17161
+ flex-direction: column;
17162
+ gap: 6px;
17163
+ }
17164
+
17165
+ .myio-form-group label {
17166
+ font-size: 13px;
17167
+ font-weight: 500;
17168
+ color: #374151;
17169
+ }
17170
+
17171
+ .myio-select, .myio-input {
17172
+ padding: 10px 12px;
17173
+ border: 1px solid #d1d5db;
17174
+ border-radius: 6px;
17175
+ font-size: 14px;
17176
+ background: white;
17177
+ color: #1f2937;
17178
+ transition: border-color 0.2s, box-shadow 0.2s;
17179
+ }
17180
+
17181
+ .myio-select:focus, .myio-input:focus {
17182
+ outline: none;
17183
+ border-color: ${primaryColor};
17184
+ box-shadow: 0 0 0 3px ${this.hexToRgba(primaryColor, 0.1)};
17185
+ }
17186
+
17187
+ .myio-power-limits-grid {
17188
+ display: grid;
17189
+ grid-template-columns: repeat(2, 1fr);
17190
+ gap: 16px;
17191
+ padding: 24px;
17192
+ transition: opacity 0.3s;
17193
+ }
17194
+
17195
+ .myio-power-limits-card-item {
17196
+ background: var(--status-bg);
17197
+ border: 1px solid var(--status-color);
17198
+ border-radius: 8px;
17199
+ padding: 16px;
17200
+ transition: transform 0.2s, box-shadow 0.2s;
17201
+ }
17202
+
17203
+ .myio-power-limits-card-item:hover {
17204
+ transform: translateY(-2px);
17205
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
17206
+ }
17207
+
17208
+ .myio-card-header {
17209
+ display: flex;
17210
+ align-items: center;
17211
+ gap: 8px;
17212
+ margin-bottom: 12px;
17213
+ }
17214
+
17215
+ .myio-status-indicator {
17216
+ width: 12px;
17217
+ height: 12px;
17218
+ border-radius: 50%;
17219
+ background: var(--status-color);
17220
+ }
17221
+
17222
+ .myio-status-label {
17223
+ font-weight: 600;
17224
+ font-size: 14px;
17225
+ color: #1f2937;
17226
+ }
17227
+
17228
+ .myio-card-inputs {
17229
+ display: grid;
17230
+ grid-template-columns: 1fr 1fr;
17231
+ gap: 12px;
17232
+ }
17233
+
17234
+ .myio-input-group {
17235
+ display: flex;
17236
+ flex-direction: column;
17237
+ gap: 4px;
17238
+ }
17239
+
17240
+ .myio-input-group label {
17241
+ font-size: 11px;
17242
+ font-weight: 500;
17243
+ color: #6b7280;
17244
+ text-transform: uppercase;
17245
+ }
17246
+
17247
+ .myio-power-limits-loading,
17248
+ .myio-power-limits-error,
17249
+ .myio-power-limits-success {
17250
+ display: flex;
17251
+ align-items: center;
17252
+ justify-content: center;
17253
+ gap: 12px;
17254
+ padding: 16px 24px;
17255
+ margin: 0 24px 24px;
17256
+ border-radius: 8px;
17257
+ }
17258
+
17259
+ .myio-power-limits-loading {
17260
+ background: #f3f4f6;
17261
+ color: #6b7280;
17262
+ }
17263
+
17264
+ .myio-power-limits-error {
17265
+ background: #fef2f2;
17266
+ color: ${dangerColor};
17267
+ border: 1px solid ${dangerColor};
17268
+ }
17269
+
17270
+ .myio-power-limits-success {
17271
+ background: #f0fdf4;
17272
+ color: ${successColor};
17273
+ border: 1px solid ${successColor};
17274
+ }
17275
+
17276
+ .myio-spinner {
17277
+ width: 24px;
17278
+ height: 24px;
17279
+ border: 3px solid #e5e7eb;
17280
+ border-top-color: ${primaryColor};
17281
+ border-radius: 50%;
17282
+ animation: spin 0.8s linear infinite;
17283
+ }
17284
+
17285
+ .myio-error-icon, .myio-success-icon {
17286
+ font-size: 20px;
17287
+ }
17288
+
17289
+ @media (max-width: 600px) {
17290
+ .myio-power-limits-selectors,
17291
+ .myio-power-limits-grid {
17292
+ grid-template-columns: 1fr;
17293
+ }
17294
+
17295
+ .myio-power-limits-header {
17296
+ flex-direction: column;
17297
+ gap: 12px;
17298
+ text-align: center;
17299
+ }
17300
+
17301
+ .myio-power-limits-actions {
17302
+ width: 100%;
17303
+ justify-content: center;
17304
+ }
17305
+ }
17306
+ `;
17307
+ }
17308
+ lightenColor(hex, percent) {
17309
+ const num = parseInt(hex.replace("#", ""), 16);
17310
+ const amt = Math.round(2.55 * percent);
17311
+ const R = (num >> 16) + amt;
17312
+ const G = (num >> 8 & 255) + amt;
17313
+ const B = (num & 255) + amt;
17314
+ return "#" + (16777216 + (R < 255 ? R < 1 ? 0 : R : 255) * 65536 + (G < 255 ? G < 1 ? 0 : G : 255) * 256 + (B < 255 ? B < 1 ? 0 : B : 255)).toString(16).slice(1);
17315
+ }
17316
+ hexToRgba(hex, alpha) {
17317
+ const num = parseInt(hex.replace("#", ""), 16);
17318
+ const R = num >> 16;
17319
+ const G = num >> 8 & 255;
17320
+ const B = num & 255;
17321
+ return `rgba(${R}, ${G}, ${B}, ${alpha})`;
17322
+ }
17323
+ };
17324
+
17325
+ // src/components/premium-modals/power-limits/PowerLimitsPersister.ts
17326
+ var PowerLimitsPersister = class {
17327
+ jwtToken;
17328
+ tbBaseUrl;
17329
+ constructor(jwtToken, tbBaseUrl) {
17330
+ this.jwtToken = jwtToken;
17331
+ this.tbBaseUrl = tbBaseUrl || window.location.origin;
17332
+ }
17333
+ /**
17334
+ * Load existing mapInstantaneousPower from customer server_scope attributes
17335
+ */
17336
+ async loadCustomerPowerLimits(customerId) {
17337
+ try {
17338
+ const url = `${this.tbBaseUrl}/api/plugins/telemetry/CUSTOMER/${customerId}/values/attributes/SERVER_SCOPE?keys=mapInstantaneousPower`;
17339
+ const response = await fetch(url, {
17340
+ method: "GET",
17341
+ headers: {
17342
+ "X-Authorization": `Bearer ${this.jwtToken}`,
17343
+ "Content-Type": "application/json"
17344
+ }
17345
+ });
17346
+ if (!response.ok) {
17347
+ if (response.status === 404) {
17348
+ console.log("[PowerLimitsPersister] No existing mapInstantaneousPower found");
17349
+ return null;
17350
+ }
17351
+ throw this.createHttpError(response.status, await response.text().catch(() => ""));
17352
+ }
17353
+ const data = await response.json();
17354
+ if (!data || data.length === 0) {
17355
+ console.log("[PowerLimitsPersister] No mapInstantaneousPower attribute found");
17356
+ return null;
17357
+ }
17358
+ const attr = data.find((item) => item.key === "mapInstantaneousPower");
17359
+ if (!attr || !attr.value) {
17360
+ return null;
17361
+ }
17362
+ const parsedValue = typeof attr.value === "string" ? JSON.parse(attr.value) : attr.value;
17363
+ console.log("[PowerLimitsPersister] Loaded mapInstantaneousPower:", parsedValue);
17364
+ return parsedValue;
17365
+ } catch (error) {
17366
+ console.error("[PowerLimitsPersister] Error loading power limits:", error);
17367
+ throw this.mapError(error);
17368
+ }
17369
+ }
17370
+ /**
17371
+ * Save mapInstantaneousPower to customer server_scope attributes
17372
+ */
17373
+ async saveCustomerPowerLimits(customerId, limits) {
17374
+ try {
17375
+ const url = `${this.tbBaseUrl}/api/plugins/telemetry/CUSTOMER/${customerId}/attributes/SERVER_SCOPE`;
17376
+ const payload = {
17377
+ mapInstantaneousPower: limits
17378
+ };
17379
+ console.log("[PowerLimitsPersister] Saving power limits:", payload);
17380
+ const response = await fetch(url, {
17381
+ method: "POST",
17382
+ headers: {
17383
+ "X-Authorization": `Bearer ${this.jwtToken}`,
17384
+ "Content-Type": "application/json"
17385
+ },
17386
+ body: JSON.stringify(payload)
17387
+ });
17388
+ if (!response.ok) {
17389
+ throw this.createHttpError(response.status, await response.text().catch(() => ""));
17390
+ }
17391
+ console.log("[PowerLimitsPersister] Successfully saved power limits");
17392
+ return { ok: true };
17393
+ } catch (error) {
17394
+ console.error("[PowerLimitsPersister] Error saving power limits:", error);
17395
+ return { ok: false, error: this.mapError(error) };
17396
+ }
17397
+ }
17398
+ /**
17399
+ * Extract form data for a specific device type and telemetry type
17400
+ */
17401
+ extractFormData(limits, deviceType, telemetryType) {
17402
+ const defaultFormData = {
17403
+ deviceType,
17404
+ telemetryType,
17405
+ standby: { baseValue: null, topValue: null },
17406
+ normal: { baseValue: null, topValue: null },
17407
+ alert: { baseValue: null, topValue: null },
17408
+ failure: { baseValue: null, topValue: null }
17409
+ };
17410
+ if (!limits || !limits.limitsByInstantaneoustPowerType) {
17411
+ return defaultFormData;
17412
+ }
17413
+ const telemetryEntry = limits.limitsByInstantaneoustPowerType.find(
17414
+ (t) => t.telemetryType === telemetryType
17415
+ );
17416
+ if (!telemetryEntry || !telemetryEntry.itemsByDeviceType) {
17417
+ return defaultFormData;
17418
+ }
17419
+ const deviceEntry = telemetryEntry.itemsByDeviceType.find(
17420
+ (d) => d.deviceType === deviceType
17421
+ );
17422
+ if (!deviceEntry || !deviceEntry.limitsByDeviceStatus) {
17423
+ return defaultFormData;
17424
+ }
17425
+ const statusMap = {
17426
+ "standBy": "standby",
17427
+ "normal": "normal",
17428
+ "alert": "alert",
17429
+ "failure": "failure"
17430
+ };
17431
+ deviceEntry.limitsByDeviceStatus.forEach((status) => {
17432
+ const formKey = statusMap[status.deviceStatusName];
17433
+ if (formKey && defaultFormData[formKey]) {
17434
+ defaultFormData[formKey] = {
17435
+ baseValue: status.limitsValues.baseValue,
17436
+ topValue: status.limitsValues.topValue
17437
+ };
17438
+ }
17439
+ });
17440
+ return defaultFormData;
17441
+ }
17442
+ /**
17443
+ * Merge form data into existing limits JSON
17444
+ * Creates new entries if they don't exist
17445
+ */
17446
+ mergeFormDataIntoLimits(existingLimits, formData) {
17447
+ const result = existingLimits ? JSON.parse(JSON.stringify(existingLimits)) : { version: "1.0.0", limitsByInstantaneoustPowerType: [] };
17448
+ const statusLimits = [
17449
+ {
17450
+ deviceStatusName: "standBy",
17451
+ limitsValues: {
17452
+ baseValue: formData.standby.baseValue ?? 0,
17453
+ topValue: formData.standby.topValue ?? 0
17454
+ }
17455
+ },
17456
+ {
17457
+ deviceStatusName: "normal",
17458
+ limitsValues: {
17459
+ baseValue: formData.normal.baseValue ?? 0,
17460
+ topValue: formData.normal.topValue ?? 0
17461
+ }
17462
+ },
17463
+ {
17464
+ deviceStatusName: "alert",
17465
+ limitsValues: {
17466
+ baseValue: formData.alert.baseValue ?? 0,
17467
+ topValue: formData.alert.topValue ?? 0
17468
+ }
17469
+ },
17470
+ {
17471
+ deviceStatusName: "failure",
17472
+ limitsValues: {
17473
+ baseValue: formData.failure.baseValue ?? 0,
17474
+ topValue: formData.failure.topValue ?? 0
17475
+ }
17476
+ }
17477
+ ];
17478
+ let telemetryEntry = result.limitsByInstantaneoustPowerType.find(
17479
+ (t) => t.telemetryType === formData.telemetryType
17480
+ );
17481
+ if (!telemetryEntry) {
17482
+ telemetryEntry = {
17483
+ telemetryType: formData.telemetryType,
17484
+ itemsByDeviceType: []
17485
+ };
17486
+ result.limitsByInstantaneoustPowerType.push(telemetryEntry);
17487
+ }
17488
+ let deviceEntry = telemetryEntry.itemsByDeviceType.find(
17489
+ (d) => d.deviceType === formData.deviceType
17490
+ );
17491
+ if (!deviceEntry) {
17492
+ deviceEntry = {
17493
+ deviceType: formData.deviceType,
17494
+ name: `mapInstantaneousPower${this.formatDeviceTypeName(formData.deviceType)}`,
17495
+ description: `Power limits for ${formData.deviceType}`,
17496
+ limitsByDeviceStatus: []
17497
+ };
17498
+ telemetryEntry.itemsByDeviceType.push(deviceEntry);
17499
+ }
17500
+ deviceEntry.limitsByDeviceStatus = statusLimits;
17501
+ deviceEntry.name = `mapInstantaneousPower${this.formatDeviceTypeName(formData.deviceType)}`;
17502
+ deviceEntry.description = `Power limits for ${formData.deviceType} - ${formData.telemetryType}`;
17503
+ return result;
17504
+ }
17505
+ /**
17506
+ * Format device type name for the JSON name field
17507
+ */
17508
+ formatDeviceTypeName(deviceType) {
17509
+ if (!deviceType) return "";
17510
+ return deviceType.toLowerCase().split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
17511
+ }
17512
+ createHttpError(status, body) {
17513
+ const error = new Error(`HTTP ${status}: ${body}`);
17514
+ error.status = status;
17515
+ error.body = body;
17516
+ return error;
17517
+ }
17518
+ mapError(error) {
17519
+ const status = error.status;
17520
+ if (status === 400) {
17521
+ return {
17522
+ code: "VALIDATION_ERROR",
17523
+ message: "Invalid input data",
17524
+ cause: error
17525
+ };
17526
+ }
17527
+ if (status === 401) {
17528
+ return {
17529
+ code: "TOKEN_EXPIRED",
17530
+ message: "Authentication token has expired",
17531
+ cause: error
17532
+ };
17533
+ }
17534
+ if (status === 403) {
17535
+ return {
17536
+ code: "AUTH_ERROR",
17537
+ message: "Insufficient permissions",
17538
+ cause: error
17539
+ };
17540
+ }
17541
+ if (status === 404) {
17542
+ return {
17543
+ code: "NETWORK_ERROR",
17544
+ message: "Customer not found",
17545
+ cause: error
17546
+ };
17547
+ }
17548
+ if (status >= 500) {
17549
+ return {
17550
+ code: "NETWORK_ERROR",
17551
+ message: "Server error occurred",
17552
+ cause: error
17553
+ };
17554
+ }
17555
+ return {
17556
+ code: "UNKNOWN_ERROR",
17557
+ message: error.message || "Unknown error occurred",
17558
+ cause: error
17559
+ };
17560
+ }
17561
+ };
17562
+
17563
+ // src/components/premium-modals/power-limits/openPowerLimitsSetupModal.ts
17564
+ async function openPowerLimitsSetupModal(params) {
17565
+ if (!params.token) {
17566
+ throw new Error("[PowerLimitsSetupModal] token is required");
17567
+ }
17568
+ if (!params.customerId) {
17569
+ throw new Error("[PowerLimitsSetupModal] customerId is required");
17570
+ }
17571
+ const persister = new PowerLimitsPersister(params.token, params.tbBaseUrl);
17572
+ let currentDeviceType = params.deviceType || "ELEVADOR";
17573
+ let currentTelemetryType = params.telemetryType || "consumption";
17574
+ let existingLimits = params.existingMapPower || null;
17575
+ const view = new PowerLimitsModalView({
17576
+ deviceType: currentDeviceType,
17577
+ telemetryType: currentTelemetryType,
17578
+ styles: params.styles,
17579
+ locale: params.locale,
17580
+ onDeviceTypeChange: async (deviceType) => {
17581
+ currentDeviceType = deviceType;
17582
+ await loadFormData();
17583
+ },
17584
+ onTelemetryTypeChange: async (telemetryType) => {
17585
+ currentTelemetryType = telemetryType;
17586
+ await loadFormData();
17587
+ },
17588
+ onSave: async () => {
17589
+ const formData = view.getFormData();
17590
+ const updatedLimits = persister.mergeFormDataIntoLimits(existingLimits, formData);
17591
+ const result = await persister.saveCustomerPowerLimits(params.customerId, updatedLimits);
17592
+ if (!result.ok) {
17593
+ throw new Error(result.error?.message || "Failed to save configuration");
17594
+ }
17595
+ existingLimits = updatedLimits;
17596
+ if (params.onSave) {
17597
+ params.onSave(updatedLimits);
17598
+ }
17599
+ },
17600
+ onClose: () => {
17601
+ if (params.onClose) {
17602
+ params.onClose();
17603
+ }
17604
+ }
17605
+ });
17606
+ async function loadFormData() {
17607
+ view.showLoading();
17608
+ try {
17609
+ if (!existingLimits) {
17610
+ existingLimits = await persister.loadCustomerPowerLimits(params.customerId);
17611
+ }
17612
+ const formData = persister.extractFormData(existingLimits, currentDeviceType, currentTelemetryType);
17613
+ view.setFormData(formData);
17614
+ } catch (error) {
17615
+ console.error("[PowerLimitsSetupModal] Error loading form data:", error);
17616
+ view.showError(error.message || "Failed to load configuration");
17617
+ } finally {
17618
+ view.hideLoading();
17619
+ }
17620
+ }
17621
+ let container;
17622
+ if (params.container) {
17623
+ if (typeof params.container === "string") {
17624
+ container = document.querySelector(params.container);
17625
+ } else {
17626
+ container = params.container;
17627
+ }
17628
+ }
17629
+ view.render(container);
17630
+ await loadFormData();
17631
+ return {
17632
+ destroy: () => view.destroy(),
17633
+ getFormData: () => view.getFormData(),
17634
+ setFormData: (data) => view.setFormData(data)
17635
+ };
17636
+ }
17637
+
16642
17638
  // src/components/premium-modals/settings/SettingsModalView.ts
16643
17639
  var SettingsModalView = class {
16644
17640
  container;
@@ -26905,6 +27901,9 @@ export {
26905
27901
  MyIOSelectionStore,
26906
27902
  MyIOSelectionStoreClass,
26907
27903
  MyIOToast,
27904
+ DEVICE_TYPES as POWER_LIMITS_DEVICE_TYPES,
27905
+ STATUS_CONFIG as POWER_LIMITS_STATUS_CONFIG,
27906
+ TELEMETRY_TYPES2 as POWER_LIMITS_TELEMETRY_TYPES,
26908
27907
  addDetectionContext,
26909
27908
  addNamespace,
26910
27909
  aggregateByDay,
@@ -27002,6 +28001,7 @@ export {
27002
28001
  openDashboardPopupWaterTank,
27003
28002
  openDemandModal,
27004
28003
  openGoalsPanel,
28004
+ openPowerLimitsSetupModal,
27005
28005
  openRealTimeTelemetryModal,
27006
28006
  openTemperatureComparisonModal,
27007
28007
  openTemperatureModal,