myio-js-library 0.1.181 → 0.1.183

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
@@ -1538,11 +1538,11 @@ function buildEntityMapFromDatasource(datasources) {
1538
1538
  const dsArray = Array.isArray(datasources) ? datasources : [];
1539
1539
  const map = /* @__PURE__ */ new Map();
1540
1540
  dsArray.forEach((ds) => {
1541
- const entityId = ds?.entityId;
1541
+ const entityId = ds?.entity?.id?.id || ds?.entity?.id || ds?.entityId || null;
1542
1542
  if (!entityId) return;
1543
1543
  if (!map.has(entityId)) {
1544
1544
  const entity = ds?.entity;
1545
- const draftLabel = entity?.label || entity?.name || ds?.name || null;
1545
+ const draftLabel = entity?.label || entity?.name || ds?.entityLabel || ds?.name || null;
1546
1546
  map.set(entityId, {
1547
1547
  id: entityId,
1548
1548
  identifier: null,
@@ -1564,7 +1564,7 @@ function buildEntityMapFromDatasource(datasources) {
1564
1564
  function hydrateEntityMapWithCtxData(data, map) {
1565
1565
  const rows = Array.isArray(data) ? data : [];
1566
1566
  rows.forEach((row) => {
1567
- const entityId = row?.datasource?.entityId || null;
1567
+ const entityId = row?.datasource?.entity?.id?.id || row?.datasource?.entity?.id || row?.datasource?.entityId || null;
1568
1568
  if (!entityId || !map.has(entityId)) return;
1569
1569
  const rawKey = row?.dataKey?.name || "";
1570
1570
  if (!rawKey) return;
@@ -5919,214 +5919,283 @@ function createLogHelper(debugActive = false) {
5919
5919
  }
5920
5920
  var LogHelper = createLogHelper(false);
5921
5921
 
5922
- // src/thingsboard/main-dashboard-shopping/v-4.0.0/card/head-office/card-head-office.js
5923
- var LABEL_CHAR_LIMIT = 18;
5924
- var DEFAUL_DELAY_TIME_CONNECTION_IN_MINS = 1440;
5925
- var CSS_TAG = "head-office-card-v1";
5926
- function ensureCss() {
5927
- if (!document.querySelector(`style[data-myio-css="${CSS_TAG}"]`)) {
5928
- const style = document.createElement("style");
5929
- style.setAttribute("data-myio-css", CSS_TAG);
5930
- style.textContent = CSS_STRING;
5931
- document.head.appendChild(style);
5932
- }
5922
+ // src/utils/TempRangeTooltip.ts
5923
+ var TOOLTIP_STYLES = `
5924
+ .temp-range-tooltip {
5925
+ position: fixed;
5926
+ z-index: 99999;
5927
+ pointer-events: none;
5928
+ opacity: 0;
5929
+ transition: opacity 0.2s ease, transform 0.2s ease;
5930
+ transform: translateY(5px);
5933
5931
  }
5934
- function normalizeParams(params) {
5935
- if (!params || !params.entityObject) {
5936
- throw new Error("renderCardCompenteHeadOffice: entityObject is required");
5937
- }
5938
- const LogHelper2 = createLogHelper(params.debugActive ?? false);
5939
- const entityObject = params.entityObject;
5940
- if (!entityObject.entityId) {
5941
- LogHelper2.warn("[CardHeadOffice] entityId is missing, generating temporary ID");
5942
- entityObject.entityId = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
5943
- }
5944
- if (!params.delayTimeConnectionInMins) {
5945
- LogHelper2.warn(
5946
- `[CardHeadOffice] delayTimeConnectionInMins is missing, defaulting to ${DEFAUL_DELAY_TIME_CONNECTION_IN_MINS} mins`
5947
- );
5948
- }
5949
- return {
5950
- entityObject,
5951
- i18n: { ...DEFAULT_I18N, ...params.i18n || {} },
5952
- enableSelection: Boolean(params.enableSelection),
5953
- enableDragDrop: Boolean(params.enableDragDrop),
5954
- useNewComponents: Boolean(params.useNewComponents),
5955
- // RFC-0091: Configurable delay time for connection status check (default 15 minutes)
5956
- delayTimeConnectionInMins: params.delayTimeConnectionInMins ?? DEFAUL_DELAY_TIME_CONNECTION_IN_MINS,
5957
- // Debug options
5958
- debugActive: params.debugActive ?? false,
5959
- activeTooltipDebug: params.activeTooltipDebug ?? false,
5960
- // LogHelper instance for this card
5961
- LogHelper: LogHelper2,
5962
- callbacks: {
5963
- handleActionDashboard: params.handleActionDashboard,
5964
- handleActionReport: params.handleActionReport,
5965
- handleActionSettings: params.handleActionSettings,
5966
- handleSelect: params.handleSelect,
5967
- handInfo: params.handInfo,
5968
- handleClickCard: params.handleClickCard
5969
- }
5970
- };
5932
+
5933
+ .temp-range-tooltip.visible {
5934
+ opacity: 1;
5935
+ pointer-events: auto;
5936
+ transform: translateY(0);
5971
5937
  }
5972
- function getIconSvg(deviceType, domain) {
5973
- if (domain === "water") {
5974
- return Icons.waterDrop;
5975
- }
5976
- if (domain === "temperature") {
5977
- return Icons.thermometer;
5978
- }
5979
- return ICON_MAP[deviceType] || ICON_MAP.DEFAULT;
5938
+
5939
+ .temp-range-tooltip__content {
5940
+ background: #ffffff;
5941
+ border: 1px solid #e2e8f0;
5942
+ border-radius: 12px;
5943
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15), 0 2px 10px rgba(0, 0, 0, 0.08);
5944
+ min-width: 280px;
5945
+ max-width: 320px;
5946
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
5947
+ font-size: 12px;
5948
+ color: #1e293b;
5949
+ overflow: hidden;
5980
5950
  }
5981
- function formatPower(valueInWatts) {
5982
- if (valueInWatts === null || valueInWatts === void 0 || isNaN(valueInWatts)) {
5983
- return { num: "-", unit: "" };
5984
- }
5985
- const val = Number(valueInWatts);
5986
- if (val >= 1e3) {
5987
- const kw = Math.ceil(val / 1e3 * 100) / 100;
5988
- return { num: kw.toFixed(2), unit: "kW" };
5989
- } else {
5990
- const w = Math.ceil(val);
5991
- return { num: w.toString(), unit: "W" };
5992
- }
5951
+
5952
+ .temp-range-tooltip__header {
5953
+ display: flex;
5954
+ align-items: center;
5955
+ gap: 8px;
5956
+ padding: 12px 16px;
5957
+ background: linear-gradient(90deg, #fff7ed 0%, #fed7aa 100%);
5958
+ border-bottom: 1px solid #fdba74;
5993
5959
  }
5994
- function formatValueByDomain(value, domain) {
5995
- if (domain === "water") {
5996
- return formatWaterVolumeM3(value);
5997
- }
5998
- if (domain === "temperature") {
5999
- return formatTemperature(value, 0);
6000
- }
6001
- return formatEnergy(value);
5960
+
5961
+ .temp-range-tooltip__icon {
5962
+ font-size: 18px;
6002
5963
  }
6003
- function formatRelativeTime2(timestamp) {
6004
- if (!timestamp || isNaN(timestamp)) return "\u2014";
6005
- const date = new Date(timestamp);
6006
- const hours = String(date.getHours()).padStart(2, "0");
6007
- const minutes = String(date.getMinutes()).padStart(2, "0");
6008
- const day = String(date.getDate()).padStart(2, "0");
6009
- const month = String(date.getMonth() + 1).padStart(2, "0");
6010
- const year = date.getFullYear();
6011
- return `${hours}:${minutes} ${day}/${month}/${year}`;
5964
+
5965
+ .temp-range-tooltip__title {
5966
+ font-weight: 700;
5967
+ font-size: 13px;
5968
+ color: #c2410c;
6012
5969
  }
6013
- function calculateConsumptionPercentage(target, consumption) {
6014
- const numericTarget = Number(target);
6015
- const numericConsumption = Number(consumption);
6016
- if (isNaN(numericTarget) || isNaN(numericConsumption) || numericTarget <= 0) {
6017
- return 0;
6018
- }
6019
- const percentage = numericConsumption / numericTarget * 100;
6020
- return percentage;
5970
+
5971
+ .temp-range-tooltip__body {
5972
+ padding: 16px;
6021
5973
  }
6022
- function getStatusInfo(deviceStatus, i18n, domain) {
6023
- switch (deviceStatus) {
6024
- // --- Novos Status de Temperatura ---
6025
- case "normal":
6026
- return { chipClass: "chip--ok", label: "Normal" };
6027
- case "cold":
6028
- return { chipClass: "chip--standby", label: "Frio" };
6029
- case "hot":
6030
- return { chipClass: "chip--alert", label: "Quente" };
6031
- // --- Status Existentes (aligned with getCardStateClass) ---
6032
- case DeviceStatusType.POWER_ON:
6033
- if (domain === "water") {
6034
- return { chipClass: "chip--power-on", label: i18n.in_operation_water };
6035
- }
6036
- return { chipClass: "chip--power-on", label: i18n.in_operation };
6037
- case DeviceStatusType.STANDBY:
6038
- return { chipClass: "chip--standby", label: i18n.standby };
6039
- case DeviceStatusType.WARNING:
6040
- return { chipClass: "chip--warning", label: i18n.alert };
6041
- case DeviceStatusType.MAINTENANCE:
6042
- return { chipClass: "chip--maintenance", label: i18n.maintenance };
6043
- case DeviceStatusType.FAILURE:
6044
- return { chipClass: "chip--failure", label: i18n.failure };
6045
- case DeviceStatusType.POWER_OFF:
6046
- return { chipClass: "chip--power-off", label: i18n.power_off || i18n.failure };
6047
- case DeviceStatusType.OFFLINE:
6048
- return { chipClass: "chip--offline", label: i18n.offline };
6049
- case DeviceStatusType.NO_INFO:
6050
- return { chipClass: "chip--no-info", label: i18n.no_info || i18n.offline };
6051
- case DeviceStatusType.NOT_INSTALLED:
6052
- return { chipClass: "chip--not-installed", label: i18n.not_installed };
6053
- default:
6054
- return { chipClass: "chip--offline", label: i18n.offline };
6055
- }
5974
+
5975
+ .temp-range-tooltip__value-row {
5976
+ display: flex;
5977
+ justify-content: space-between;
5978
+ align-items: center;
5979
+ margin-bottom: 16px;
6056
5980
  }
6057
- function getCardStateClass(deviceStatus) {
6058
- switch (deviceStatus) {
6059
- case DeviceStatusType.POWER_ON:
6060
- return "is-power-on";
6061
- // Blue border
6062
- case DeviceStatusType.STANDBY:
6063
- return "is-standby";
6064
- // Green border
6065
- case DeviceStatusType.WARNING:
6066
- return "is-warning";
6067
- // Yellow border
6068
- case DeviceStatusType.MAINTENANCE:
6069
- return "is-maintenance";
6070
- // Yellow border
6071
- case DeviceStatusType.FAILURE:
6072
- return "is-failure";
6073
- // Dark Red border
6074
- case DeviceStatusType.POWER_OFF:
6075
- return "is-power-off";
6076
- // Light Red border
6077
- case DeviceStatusType.OFFLINE:
6078
- return "is-offline";
6079
- // Dark Gray border
6080
- case DeviceStatusType.NO_INFO:
6081
- return "is-no-info";
6082
- // Dark Orange border
6083
- case DeviceStatusType.NOT_INSTALLED:
6084
- return "is-not-installed";
6085
- // Purple border
6086
- default:
6087
- return "";
6088
- }
5981
+
5982
+ .temp-range-tooltip__current {
5983
+ font-size: 28px;
5984
+ font-weight: 700;
5985
+ color: #1e293b;
6089
5986
  }
6090
- function getTempRangeClass(entityObject) {
6091
- if (entityObject.domain !== "temperature") return "";
6092
- const currentTemp = Number(entityObject.val ?? entityObject.currentTemperature ?? entityObject.temperature) || 0;
6093
- const tempMin = entityObject.temperatureMin ?? entityObject.minTemperature;
6094
- const tempMax = entityObject.temperatureMax ?? entityObject.maxTemperature;
6095
- if (tempMin === void 0 || tempMax === void 0 || tempMin === null || tempMax === null) {
6096
- return "";
6097
- }
6098
- if (currentTemp > tempMax) return "is-temp-hot";
6099
- if (currentTemp < tempMin) return "is-temp-cold";
6100
- return "is-temp-ok";
5987
+
5988
+ .temp-range-tooltip__current sup {
5989
+ font-size: 14px;
5990
+ color: #64748b;
6101
5991
  }
6102
- var TempRangeTooltip = {
6103
- containerId: "myio-temp-range-tooltip",
6104
- /**
6105
- * Create or get the tooltip container
6106
- */
6107
- getContainer() {
6108
- let container = document.getElementById(this.containerId);
6109
- if (!container) {
6110
- container = document.createElement("div");
6111
- container.id = this.containerId;
6112
- container.className = "temp-range-tooltip";
6113
- document.body.appendChild(container);
6114
- }
6115
- return container;
6116
- },
6117
- /**
6118
- * Calculate temperature status and deviation
6119
- */
6120
- calculateStatus(currentTemp, tempMin, tempMax) {
6121
- if (tempMin == null || tempMax == null) {
6122
- return { status: "unknown", deviation: null, deviationPercent: null };
6123
- }
6124
- const rangeSize = tempMax - tempMin;
6125
- const midPoint = (tempMin + tempMax) / 2;
6126
- if (currentTemp < tempMin) {
6127
- const deviation = tempMin - currentTemp;
6128
- const deviationPercent = rangeSize > 0 ? deviation / rangeSize * 100 : 0;
6129
- return { status: "cold", deviation: -deviation, deviationPercent: -deviationPercent };
5992
+
5993
+ .temp-range-tooltip__deviation {
5994
+ text-align: right;
5995
+ }
5996
+
5997
+ .temp-range-tooltip__deviation-value {
5998
+ font-size: 16px;
5999
+ font-weight: 700;
6000
+ }
6001
+
6002
+ .temp-range-tooltip__deviation-value.cold {
6003
+ color: #2563eb;
6004
+ }
6005
+
6006
+ .temp-range-tooltip__deviation-value.ok {
6007
+ color: #16a34a;
6008
+ }
6009
+
6010
+ .temp-range-tooltip__deviation-value.hot {
6011
+ color: #dc2626;
6012
+ }
6013
+
6014
+ .temp-range-tooltip__deviation-label {
6015
+ font-size: 10px;
6016
+ color: #64748b;
6017
+ text-transform: uppercase;
6018
+ letter-spacing: 0.5px;
6019
+ }
6020
+
6021
+ .temp-range-tooltip__ruler {
6022
+ position: relative;
6023
+ height: 32px;
6024
+ margin: 12px 0;
6025
+ border-radius: 8px;
6026
+ overflow: visible;
6027
+ }
6028
+
6029
+ .temp-range-tooltip__ruler-track {
6030
+ position: absolute;
6031
+ top: 12px;
6032
+ left: 0;
6033
+ right: 0;
6034
+ height: 8px;
6035
+ background: linear-gradient(90deg, #dbeafe 0%, #dcfce7 50%, #fee2e2 100%);
6036
+ border-radius: 4px;
6037
+ border: 1px solid #e2e8f0;
6038
+ }
6039
+
6040
+ .temp-range-tooltip__ruler-range {
6041
+ position: absolute;
6042
+ top: 12px;
6043
+ height: 8px;
6044
+ background: #22c55e;
6045
+ border-radius: 4px;
6046
+ opacity: 0.6;
6047
+ }
6048
+
6049
+ .temp-range-tooltip__ruler-marker {
6050
+ position: absolute;
6051
+ top: 4px;
6052
+ width: 4px;
6053
+ height: 24px;
6054
+ background: #1e293b;
6055
+ border-radius: 2px;
6056
+ transform: translateX(-50%);
6057
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
6058
+ }
6059
+
6060
+ .temp-range-tooltip__ruler-marker::after {
6061
+ content: '';
6062
+ position: absolute;
6063
+ top: -4px;
6064
+ left: 50%;
6065
+ transform: translateX(-50%);
6066
+ width: 12px;
6067
+ height: 12px;
6068
+ background: #1e293b;
6069
+ border-radius: 50%;
6070
+ border: 2px solid #fff;
6071
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
6072
+ }
6073
+
6074
+ .temp-range-tooltip__ruler-labels {
6075
+ display: flex;
6076
+ justify-content: space-between;
6077
+ margin-top: 8px;
6078
+ font-size: 10px;
6079
+ color: #64748b;
6080
+ }
6081
+
6082
+ .temp-range-tooltip__ruler-min,
6083
+ .temp-range-tooltip__ruler-max {
6084
+ font-weight: 600;
6085
+ }
6086
+
6087
+ .temp-range-tooltip__range-info {
6088
+ display: flex;
6089
+ justify-content: space-between;
6090
+ padding: 10px 12px;
6091
+ background: #f8fafc;
6092
+ border-radius: 8px;
6093
+ margin-top: 12px;
6094
+ }
6095
+
6096
+ .temp-range-tooltip__range-item {
6097
+ text-align: center;
6098
+ }
6099
+
6100
+ .temp-range-tooltip__range-label {
6101
+ font-size: 10px;
6102
+ color: #64748b;
6103
+ text-transform: uppercase;
6104
+ letter-spacing: 0.3px;
6105
+ margin-bottom: 2px;
6106
+ }
6107
+
6108
+ .temp-range-tooltip__range-value {
6109
+ font-size: 14px;
6110
+ font-weight: 600;
6111
+ color: #334155;
6112
+ }
6113
+
6114
+ .temp-range-tooltip__status {
6115
+ display: flex;
6116
+ align-items: center;
6117
+ justify-content: center;
6118
+ gap: 6px;
6119
+ margin-top: 12px;
6120
+ padding: 8px 12px;
6121
+ border-radius: 6px;
6122
+ font-size: 11px;
6123
+ font-weight: 600;
6124
+ }
6125
+
6126
+ .temp-range-tooltip__status.cold {
6127
+ background: #dbeafe;
6128
+ color: #1d4ed8;
6129
+ border: 1px solid #93c5fd;
6130
+ }
6131
+
6132
+ .temp-range-tooltip__status.ok {
6133
+ background: #dcfce7;
6134
+ color: #15803d;
6135
+ border: 1px solid #86efac;
6136
+ }
6137
+
6138
+ .temp-range-tooltip__status.hot {
6139
+ background: #fee2e2;
6140
+ color: #b91c1c;
6141
+ border: 1px solid #fca5a5;
6142
+ }
6143
+
6144
+ .temp-range-tooltip__status.unknown {
6145
+ background: #f3f4f6;
6146
+ color: #6b7280;
6147
+ border: 1px solid #d1d5db;
6148
+ }
6149
+ `;
6150
+ function injectStyles() {
6151
+ if (typeof document === "undefined") return;
6152
+ const styleId = "myio-temp-range-tooltip-styles";
6153
+ if (document.getElementById(styleId)) return;
6154
+ const style = document.createElement("style");
6155
+ style.id = styleId;
6156
+ style.textContent = TOOLTIP_STYLES;
6157
+ document.head.appendChild(style);
6158
+ }
6159
+ function extractTemperature(entityData) {
6160
+ return Number(entityData.val ?? entityData.currentTemperature ?? entityData.temperature) || 0;
6161
+ }
6162
+ function extractRange(entityData) {
6163
+ const tempMin = entityData.temperatureMin ?? entityData.minTemperature ?? null;
6164
+ const tempMax = entityData.temperatureMax ?? entityData.maxTemperature ?? null;
6165
+ return { tempMin, tempMax };
6166
+ }
6167
+ function extractLabel(entityData) {
6168
+ return entityData.labelOrName || entityData.name || entityData.label || "Sensor";
6169
+ }
6170
+ var TempRangeTooltip = {
6171
+ containerId: "myio-temp-range-tooltip",
6172
+ /**
6173
+ * Create or get the tooltip container
6174
+ */
6175
+ getContainer() {
6176
+ injectStyles();
6177
+ let container = document.getElementById(this.containerId);
6178
+ if (!container) {
6179
+ container = document.createElement("div");
6180
+ container.id = this.containerId;
6181
+ container.className = "temp-range-tooltip";
6182
+ document.body.appendChild(container);
6183
+ }
6184
+ return container;
6185
+ },
6186
+ /**
6187
+ * Calculate temperature status and deviation
6188
+ */
6189
+ calculateStatus(currentTemp, tempMin, tempMax) {
6190
+ if (tempMin == null || tempMax == null) {
6191
+ return { status: "unknown", deviation: null, deviationPercent: null };
6192
+ }
6193
+ const rangeSize = tempMax - tempMin;
6194
+ const midPoint = (tempMin + tempMax) / 2;
6195
+ if (currentTemp < tempMin) {
6196
+ const deviation = tempMin - currentTemp;
6197
+ const deviationPercent = rangeSize > 0 ? deviation / rangeSize * 100 : 0;
6198
+ return { status: "cold", deviation: -deviation, deviationPercent: -deviationPercent };
6130
6199
  } else if (currentTemp > tempMax) {
6131
6200
  const deviation = currentTemp - tempMax;
6132
6201
  const deviationPercent = rangeSize > 0 ? deviation / rangeSize * 100 : 0;
@@ -6154,20 +6223,20 @@ var TempRangeTooltip = {
6154
6223
  },
6155
6224
  /**
6156
6225
  * Show tooltip for a temperature card
6157
- * @param {HTMLElement} triggerElement - The card element
6158
- * @param {Object} entityObject - Entity data
6159
- * @param {MouseEvent} event - Mouse event for cursor position
6226
+ * @param triggerElement - The card element
6227
+ * @param entityData - Entity data with temperature info
6228
+ * @param event - Mouse event for cursor position
6160
6229
  */
6161
- show(triggerElement, entityObject, event) {
6230
+ show(triggerElement, entityData, event) {
6162
6231
  const container = this.getContainer();
6163
- const currentTemp = Number(entityObject.val ?? entityObject.currentTemperature ?? entityObject.temperature) || 0;
6164
- const tempMin = entityObject.temperatureMin ?? entityObject.minTemperature;
6165
- const tempMax = entityObject.temperatureMax ?? entityObject.maxTemperature;
6232
+ const currentTemp = extractTemperature(entityData);
6233
+ const { tempMin, tempMax } = extractRange(entityData);
6234
+ const label = extractLabel(entityData);
6166
6235
  const hasRange = tempMin != null && tempMax != null;
6167
- const { status, deviation, deviationPercent } = this.calculateStatus(currentTemp, tempMin, tempMax);
6236
+ const { status, deviationPercent } = this.calculateStatus(currentTemp, tempMin, tempMax);
6168
6237
  const markerPos = this.calculateMarkerPosition(currentTemp, tempMin, tempMax);
6169
6238
  let rangeLeft = 0, rangeWidth = 100;
6170
- if (hasRange) {
6239
+ if (hasRange && tempMin != null && tempMax != null) {
6171
6240
  const rangeSize = tempMax - tempMin;
6172
6241
  const extension = rangeSize * 0.3;
6173
6242
  const visibleMin = tempMin - extension;
@@ -6176,28 +6245,23 @@ var TempRangeTooltip = {
6176
6245
  rangeLeft = (tempMin - visibleMin) / visibleRange * 100;
6177
6246
  rangeWidth = rangeSize / visibleRange * 100;
6178
6247
  }
6179
- let deviationText = "";
6180
- let deviationClass = status;
6181
- if (status === "cold") {
6182
- deviationText = `${Math.abs(deviation).toFixed(1)}\xB0C abaixo`;
6183
- } else if (status === "hot") {
6184
- deviationText = `+${deviation.toFixed(1)}\xB0C acima`;
6185
- } else if (status === "ok") {
6186
- deviationText = "Na faixa ideal";
6187
- } else {
6188
- deviationText = "Faixa n\xE3o configurada";
6189
- }
6190
6248
  const statusLabels = {
6191
6249
  cold: "\u2744\uFE0F Abaixo da Faixa Ideal",
6192
6250
  ok: "\u2714\uFE0F Dentro da Faixa Ideal",
6193
6251
  hot: "\u{1F525} Acima da Faixa Ideal",
6194
6252
  unknown: "\u2753 Faixa N\xE3o Configurada"
6195
6253
  };
6254
+ const statusColors = {
6255
+ cold: "#2563eb",
6256
+ ok: "#16a34a",
6257
+ hot: "#dc2626",
6258
+ unknown: "#64748b"
6259
+ };
6196
6260
  container.innerHTML = `
6197
6261
  <div class="temp-range-tooltip__content">
6198
6262
  <div class="temp-range-tooltip__header">
6199
6263
  <span class="temp-range-tooltip__icon">\u{1F321}\uFE0F</span>
6200
- <span class="temp-range-tooltip__title">${entityObject.labelOrName || entityObject.name || "Sensor"}</span>
6264
+ <span class="temp-range-tooltip__title">${label}</span>
6201
6265
  </div>
6202
6266
  <div class="temp-range-tooltip__body">
6203
6267
  <div class="temp-range-tooltip__value-row">
@@ -6205,7 +6269,7 @@ var TempRangeTooltip = {
6205
6269
  ${currentTemp.toFixed(1)}<sup>\xB0C</sup>
6206
6270
  </div>
6207
6271
  <div class="temp-range-tooltip__deviation">
6208
- <div class="temp-range-tooltip__deviation-value ${deviationClass}">
6272
+ <div class="temp-range-tooltip__deviation-value ${status}">
6209
6273
  ${status === "ok" ? "\u2713" : status === "cold" ? "\u2193" : status === "hot" ? "\u2191" : "?"} ${Math.abs(deviationPercent || 0).toFixed(0)}%
6210
6274
  </div>
6211
6275
  <div class="temp-range-tooltip__deviation-label">Desvio</div>
@@ -6232,7 +6296,7 @@ var TempRangeTooltip = {
6232
6296
  </div>
6233
6297
  <div class="temp-range-tooltip__range-item">
6234
6298
  <div class="temp-range-tooltip__range-label">Atual</div>
6235
- <div class="temp-range-tooltip__range-value" style="color: ${status === "ok" ? "#16a34a" : status === "cold" ? "#2563eb" : "#dc2626"}">${currentTemp.toFixed(1)}\xB0C</div>
6299
+ <div class="temp-range-tooltip__range-value" style="color: ${statusColors[status]}">${currentTemp.toFixed(1)}\xB0C</div>
6236
6300
  </div>
6237
6301
  <div class="temp-range-tooltip__range-item">
6238
6302
  <div class="temp-range-tooltip__range-label">M\xE1ximo</div>
@@ -6277,7 +6341,320 @@ var TempRangeTooltip = {
6277
6341
  if (container) {
6278
6342
  container.classList.remove("visible");
6279
6343
  }
6344
+ },
6345
+ /**
6346
+ * Attach tooltip to an element
6347
+ * Returns cleanup function
6348
+ */
6349
+ attach(element, entityData) {
6350
+ const showHandler = (e) => this.show(element, entityData, e);
6351
+ const hideHandler = () => this.hide();
6352
+ element.style.cursor = "help";
6353
+ element.addEventListener("mouseenter", showHandler);
6354
+ element.addEventListener("mouseleave", hideHandler);
6355
+ return () => {
6356
+ element.removeEventListener("mouseenter", showHandler);
6357
+ element.removeEventListener("mouseleave", hideHandler);
6358
+ element.style.cursor = "";
6359
+ this.hide();
6360
+ };
6361
+ }
6362
+ };
6363
+
6364
+ // src/utils/EnergyRangeTooltip.ts
6365
+ var ENERGY_RANGE_TOOLTIP_CSS = `
6366
+ /* ============================================
6367
+ Energy Range Tooltip (for domain=energy)
6368
+ Shows power ruler with current position and status ranges
6369
+ ============================================ */
6370
+ .energy-range-tooltip {
6371
+ position: fixed;
6372
+ z-index: 99999;
6373
+ pointer-events: none;
6374
+ opacity: 0;
6375
+ transition: opacity 0.2s ease, transform 0.2s ease;
6376
+ transform: translateY(5px);
6377
+ }
6378
+
6379
+ .energy-range-tooltip.visible {
6380
+ opacity: 1;
6381
+ pointer-events: auto;
6382
+ transform: translateY(0);
6383
+ }
6384
+
6385
+ .energy-range-tooltip__content {
6386
+ background: #ffffff;
6387
+ border: 1px solid #e2e8f0;
6388
+ border-radius: 12px;
6389
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15), 0 2px 10px rgba(0, 0, 0, 0.08);
6390
+ min-width: 300px;
6391
+ max-width: 360px;
6392
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
6393
+ font-size: 12px;
6394
+ color: #1e293b;
6395
+ overflow: hidden;
6396
+ }
6397
+
6398
+ .energy-range-tooltip__header {
6399
+ display: flex;
6400
+ align-items: center;
6401
+ gap: 8px;
6402
+ padding: 12px 16px;
6403
+ background: linear-gradient(90deg, #ecfdf5 0%, #d1fae5 100%);
6404
+ border-bottom: 1px solid #6ee7b7;
6405
+ }
6406
+
6407
+ .energy-range-tooltip__icon {
6408
+ font-size: 18px;
6409
+ }
6410
+
6411
+ .energy-range-tooltip__title {
6412
+ font-weight: 700;
6413
+ font-size: 13px;
6414
+ color: #047857;
6415
+ }
6416
+
6417
+ .energy-range-tooltip__body {
6418
+ padding: 16px;
6419
+ }
6420
+
6421
+ /* Power value display */
6422
+ .energy-range-tooltip__value-row {
6423
+ display: flex;
6424
+ justify-content: space-between;
6425
+ align-items: center;
6426
+ margin-bottom: 16px;
6427
+ }
6428
+
6429
+ .energy-range-tooltip__current {
6430
+ font-size: 28px;
6431
+ font-weight: 700;
6432
+ color: #1e293b;
6433
+ }
6434
+
6435
+ .energy-range-tooltip__current sup {
6436
+ font-size: 14px;
6437
+ color: #64748b;
6438
+ }
6439
+
6440
+ .energy-range-tooltip__status-badge {
6441
+ text-align: right;
6442
+ }
6443
+
6444
+ .energy-range-tooltip__status-value {
6445
+ font-size: 14px;
6446
+ font-weight: 700;
6447
+ padding: 4px 10px;
6448
+ border-radius: 6px;
6449
+ }
6450
+
6451
+ .energy-range-tooltip__status-value.standby {
6452
+ background: #dbeafe;
6453
+ color: #1d4ed8;
6454
+ }
6455
+
6456
+ .energy-range-tooltip__status-value.normal {
6457
+ background: #dcfce7;
6458
+ color: #15803d;
6459
+ }
6460
+
6461
+ .energy-range-tooltip__status-value.alert {
6462
+ background: #fef3c7;
6463
+ color: #b45309;
6464
+ }
6465
+
6466
+ .energy-range-tooltip__status-value.failure {
6467
+ background: #fee2e2;
6468
+ color: #b91c1c;
6469
+ }
6470
+
6471
+ .energy-range-tooltip__status-value.offline {
6472
+ background: #f3f4f6;
6473
+ color: #6b7280;
6474
+ }
6475
+
6476
+ /* Power ruler/gauge */
6477
+ .energy-range-tooltip__ruler {
6478
+ position: relative;
6479
+ height: 40px;
6480
+ margin: 12px 0;
6481
+ border-radius: 8px;
6482
+ overflow: visible;
6483
+ }
6484
+
6485
+ .energy-range-tooltip__ruler-track {
6486
+ position: absolute;
6487
+ top: 16px;
6488
+ left: 0;
6489
+ right: 0;
6490
+ height: 8px;
6491
+ display: flex;
6492
+ border-radius: 4px;
6493
+ overflow: hidden;
6494
+ border: 1px solid #e2e8f0;
6495
+ }
6496
+
6497
+ .energy-range-tooltip__ruler-segment {
6498
+ height: 100%;
6499
+ }
6500
+
6501
+ .energy-range-tooltip__ruler-segment.standby {
6502
+ background: #dbeafe;
6503
+ }
6504
+
6505
+ .energy-range-tooltip__ruler-segment.normal {
6506
+ background: #dcfce7;
6507
+ }
6508
+
6509
+ .energy-range-tooltip__ruler-segment.alert {
6510
+ background: #fef3c7;
6511
+ }
6512
+
6513
+ .energy-range-tooltip__ruler-segment.failure {
6514
+ background: #fee2e2;
6515
+ }
6516
+
6517
+ .energy-range-tooltip__ruler-marker {
6518
+ position: absolute;
6519
+ top: 8px;
6520
+ width: 4px;
6521
+ height: 24px;
6522
+ background: #1e293b;
6523
+ border-radius: 2px;
6524
+ transform: translateX(-50%);
6525
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
6526
+ }
6527
+
6528
+ .energy-range-tooltip__ruler-marker::after {
6529
+ content: '';
6530
+ position: absolute;
6531
+ top: -4px;
6532
+ left: 50%;
6533
+ transform: translateX(-50%);
6534
+ width: 12px;
6535
+ height: 12px;
6536
+ background: #1e293b;
6537
+ border-radius: 50%;
6538
+ border: 2px solid #fff;
6539
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
6540
+ }
6541
+
6542
+ /* Range info grid */
6543
+ .energy-range-tooltip__ranges {
6544
+ display: grid;
6545
+ grid-template-columns: repeat(4, 1fr);
6546
+ gap: 8px;
6547
+ margin-top: 16px;
6548
+ }
6549
+
6550
+ .energy-range-tooltip__range-item {
6551
+ text-align: center;
6552
+ padding: 8px 4px;
6553
+ border-radius: 6px;
6554
+ background: #f8fafc;
6555
+ }
6556
+
6557
+ .energy-range-tooltip__range-item.standby {
6558
+ border-left: 3px solid #3b82f6;
6559
+ }
6560
+
6561
+ .energy-range-tooltip__range-item.normal {
6562
+ border-left: 3px solid #22c55e;
6563
+ }
6564
+
6565
+ .energy-range-tooltip__range-item.alert {
6566
+ border-left: 3px solid #f59e0b;
6567
+ }
6568
+
6569
+ .energy-range-tooltip__range-item.failure {
6570
+ border-left: 3px solid #ef4444;
6571
+ }
6572
+
6573
+ .energy-range-tooltip__range-label {
6574
+ font-size: 9px;
6575
+ color: #64748b;
6576
+ text-transform: uppercase;
6577
+ letter-spacing: 0.3px;
6578
+ margin-bottom: 2px;
6579
+ }
6580
+
6581
+ .energy-range-tooltip__range-value {
6582
+ font-size: 11px;
6583
+ font-weight: 600;
6584
+ color: #334155;
6585
+ }
6586
+
6587
+ /* Status info */
6588
+ .energy-range-tooltip__status-info {
6589
+ display: flex;
6590
+ align-items: center;
6591
+ justify-content: center;
6592
+ gap: 6px;
6593
+ margin-top: 12px;
6594
+ padding: 8px 12px;
6595
+ border-radius: 6px;
6596
+ font-size: 11px;
6597
+ font-weight: 600;
6598
+ }
6599
+
6600
+ .energy-range-tooltip__status-info.standby {
6601
+ background: #dbeafe;
6602
+ color: #1d4ed8;
6603
+ border: 1px solid #93c5fd;
6604
+ }
6605
+
6606
+ .energy-range-tooltip__status-info.normal {
6607
+ background: #dcfce7;
6608
+ color: #15803d;
6609
+ border: 1px solid #86efac;
6610
+ }
6611
+
6612
+ .energy-range-tooltip__status-info.alert {
6613
+ background: #fef3c7;
6614
+ color: #b45309;
6615
+ border: 1px solid #fcd34d;
6616
+ }
6617
+
6618
+ .energy-range-tooltip__status-info.failure {
6619
+ background: #fee2e2;
6620
+ color: #b91c1c;
6621
+ border: 1px solid #fca5a5;
6622
+ }
6623
+
6624
+ .energy-range-tooltip__status-info.offline {
6625
+ background: #f3f4f6;
6626
+ color: #6b7280;
6627
+ border: 1px solid #d1d5db;
6628
+ }
6629
+ `;
6630
+ var cssInjected = false;
6631
+ function injectCSS() {
6632
+ if (cssInjected) return;
6633
+ if (typeof document === "undefined") return;
6634
+ const styleId = "myio-energy-range-tooltip-styles";
6635
+ if (document.getElementById(styleId)) {
6636
+ cssInjected = true;
6637
+ return;
6280
6638
  }
6639
+ const style = document.createElement("style");
6640
+ style.id = styleId;
6641
+ style.textContent = ENERGY_RANGE_TOOLTIP_CSS;
6642
+ document.head.appendChild(style);
6643
+ cssInjected = true;
6644
+ }
6645
+ var STATUS_LABELS = {
6646
+ standby: "Standby",
6647
+ normal: "Operacao Normal",
6648
+ alert: "Alerta",
6649
+ failure: "Falha",
6650
+ offline: "Fora da faixa"
6651
+ };
6652
+ var STATUS_INFO_LABELS = {
6653
+ standby: "\u{1F535} Standby",
6654
+ normal: "\u2705 Operacao Normal",
6655
+ alert: "\u26A0\uFE0F Alerta",
6656
+ failure: "\u{1F534} Falha",
6657
+ offline: "\u26AB Offline / Sem dados"
6281
6658
  };
6282
6659
  var EnergyRangeTooltip = {
6283
6660
  containerId: "myio-energy-range-tooltip",
@@ -6285,6 +6662,7 @@ var EnergyRangeTooltip = {
6285
6662
  * Create or get the tooltip container
6286
6663
  */
6287
6664
  getContainer() {
6665
+ injectCSS();
6288
6666
  let container = document.getElementById(this.containerId);
6289
6667
  if (!container) {
6290
6668
  container = document.createElement("div");
@@ -6304,18 +6682,18 @@ var EnergyRangeTooltip = {
6304
6682
  const power = Number(powerValue) || 0;
6305
6683
  const { standbyRange, normalRange, alertRange, failureRange } = ranges;
6306
6684
  if (standbyRange && power >= standbyRange.down && power <= standbyRange.up) {
6307
- return { status: "standby", label: "Standby" };
6685
+ return { status: "standby", label: STATUS_LABELS.standby };
6308
6686
  }
6309
6687
  if (normalRange && power >= normalRange.down && power <= normalRange.up) {
6310
- return { status: "normal", label: "Normal" };
6688
+ return { status: "normal", label: STATUS_LABELS.normal };
6311
6689
  }
6312
6690
  if (alertRange && power >= alertRange.down && power <= alertRange.up) {
6313
- return { status: "alert", label: "Alerta" };
6691
+ return { status: "alert", label: STATUS_LABELS.alert };
6314
6692
  }
6315
6693
  if (failureRange && power >= failureRange.down && power <= failureRange.up) {
6316
- return { status: "failure", label: "Falha" };
6694
+ return { status: "failure", label: STATUS_LABELS.failure };
6317
6695
  }
6318
- return { status: "offline", label: "Fora da faixa" };
6696
+ return { status: "offline", label: STATUS_LABELS.offline };
6319
6697
  },
6320
6698
  /**
6321
6699
  * Calculate marker position on ruler (0-100%)
@@ -6346,7 +6724,7 @@ var EnergyRangeTooltip = {
6346
6724
  * Format power value for display
6347
6725
  */
6348
6726
  formatPower(value) {
6349
- if (value == null || isNaN(value)) return "-";
6727
+ if (value == null || isNaN(Number(value))) return "-";
6350
6728
  const num = Number(value);
6351
6729
  if (num >= 1e3) {
6352
6730
  return `${(num / 1e3).toFixed(2)} kW`;
@@ -6364,13 +6742,40 @@ var EnergyRangeTooltip = {
6364
6742
  const { status, label } = this.calculateStatus(powerValue, ranges);
6365
6743
  const markerPos = this.calculateMarkerPosition(powerValue, ranges);
6366
6744
  const segmentWidths = this.calculateSegmentWidths(ranges);
6367
- const statusLabels = {
6368
- standby: "\u{1F535} Standby",
6369
- normal: "\u2705 Opera\xE7\xE3o Normal",
6370
- alert: "\u26A0\uFE0F Alerta",
6371
- failure: "\u{1F534} Falha",
6372
- offline: "\u26AB Offline / Sem dados"
6373
- };
6745
+ const rangesHtml = hasRanges && ranges ? `
6746
+ <div class="energy-range-tooltip__ruler">
6747
+ <div class="energy-range-tooltip__ruler-track">
6748
+ <div class="energy-range-tooltip__ruler-segment standby" style="width: ${segmentWidths.standby}%"></div>
6749
+ <div class="energy-range-tooltip__ruler-segment normal" style="width: ${segmentWidths.normal}%"></div>
6750
+ <div class="energy-range-tooltip__ruler-segment alert" style="width: ${segmentWidths.alert}%"></div>
6751
+ <div class="energy-range-tooltip__ruler-segment failure" style="width: ${segmentWidths.failure}%"></div>
6752
+ </div>
6753
+ <div class="energy-range-tooltip__ruler-marker" style="left: ${markerPos}%;"></div>
6754
+ </div>
6755
+
6756
+ <div class="energy-range-tooltip__ranges">
6757
+ <div class="energy-range-tooltip__range-item standby">
6758
+ <div class="energy-range-tooltip__range-label">Standby</div>
6759
+ <div class="energy-range-tooltip__range-value">${ranges.standbyRange?.down || 0}-${ranges.standbyRange?.up || 0}W</div>
6760
+ </div>
6761
+ <div class="energy-range-tooltip__range-item normal">
6762
+ <div class="energy-range-tooltip__range-label">Normal</div>
6763
+ <div class="energy-range-tooltip__range-value">${ranges.normalRange?.down || 0}-${ranges.normalRange?.up || 0}W</div>
6764
+ </div>
6765
+ <div class="energy-range-tooltip__range-item alert">
6766
+ <div class="energy-range-tooltip__range-label">Alerta</div>
6767
+ <div class="energy-range-tooltip__range-value">${ranges.alertRange?.down || 0}-${ranges.alertRange?.up || 0}W</div>
6768
+ </div>
6769
+ <div class="energy-range-tooltip__range-item failure">
6770
+ <div class="energy-range-tooltip__range-label">Falha</div>
6771
+ <div class="energy-range-tooltip__range-value">&gt;${ranges.failureRange?.down || 0}W</div>
6772
+ </div>
6773
+ </div>
6774
+ ` : `
6775
+ <div style="text-align: center; padding: 16px; color: #64748b; font-size: 12px;">
6776
+ Ranges de potencia nao configurados
6777
+ </div>
6778
+ `;
6374
6779
  container.innerHTML = `
6375
6780
  <div class="energy-range-tooltip__content">
6376
6781
  <div class="energy-range-tooltip__header">
@@ -6387,51 +6792,18 @@ var EnergyRangeTooltip = {
6387
6792
  </div>
6388
6793
  </div>
6389
6794
 
6390
- ${hasRanges ? `
6391
- <div class="energy-range-tooltip__ruler">
6392
- <div class="energy-range-tooltip__ruler-track">
6393
- <div class="energy-range-tooltip__ruler-segment standby" style="width: ${segmentWidths.standby}%"></div>
6394
- <div class="energy-range-tooltip__ruler-segment normal" style="width: ${segmentWidths.normal}%"></div>
6395
- <div class="energy-range-tooltip__ruler-segment alert" style="width: ${segmentWidths.alert}%"></div>
6396
- <div class="energy-range-tooltip__ruler-segment failure" style="width: ${segmentWidths.failure}%"></div>
6397
- </div>
6398
- <div class="energy-range-tooltip__ruler-marker" style="left: ${markerPos}%;"></div>
6399
- </div>
6400
-
6401
- <div class="energy-range-tooltip__ranges">
6402
- <div class="energy-range-tooltip__range-item standby">
6403
- <div class="energy-range-tooltip__range-label">Standby</div>
6404
- <div class="energy-range-tooltip__range-value">${ranges.standbyRange?.down || 0}-${ranges.standbyRange?.up || 0}W</div>
6405
- </div>
6406
- <div class="energy-range-tooltip__range-item normal">
6407
- <div class="energy-range-tooltip__range-label">Normal</div>
6408
- <div class="energy-range-tooltip__range-value">${ranges.normalRange?.down || 0}-${ranges.normalRange?.up || 0}W</div>
6409
- </div>
6410
- <div class="energy-range-tooltip__range-item alert">
6411
- <div class="energy-range-tooltip__range-label">Alerta</div>
6412
- <div class="energy-range-tooltip__range-value">${ranges.alertRange?.down || 0}-${ranges.alertRange?.up || 0}W</div>
6413
- </div>
6414
- <div class="energy-range-tooltip__range-item failure">
6415
- <div class="energy-range-tooltip__range-label">Falha</div>
6416
- <div class="energy-range-tooltip__range-value">&gt;${ranges.failureRange?.down || 0}W</div>
6417
- </div>
6418
- </div>
6419
- ` : `
6420
- <div style="text-align: center; padding: 16px; color: #64748b; font-size: 12px;">
6421
- Ranges de pot\xEAncia n\xE3o configurados
6422
- </div>
6423
- `}
6795
+ ${rangesHtml}
6424
6796
 
6425
6797
  <div class="energy-range-tooltip__status-info ${status}">
6426
- ${statusLabels[status] || statusLabels.offline}
6798
+ ${STATUS_INFO_LABELS[status] || STATUS_INFO_LABELS.offline}
6427
6799
  </div>
6428
6800
  </div>
6429
6801
  </div>
6430
6802
  `;
6431
6803
  let left, top;
6432
6804
  if (event && event.clientX && event.clientY) {
6433
- left = event.clientX + 15;
6434
- top = event.clientY + 15;
6805
+ left = event.clientX + 8;
6806
+ top = event.clientY + 8;
6435
6807
  } else {
6436
6808
  const rect = triggerElement.getBoundingClientRect();
6437
6809
  left = rect.left + rect.width / 2 - 150;
@@ -6440,11 +6812,11 @@ var EnergyRangeTooltip = {
6440
6812
  const tooltipWidth = 320;
6441
6813
  const tooltipHeight = 380;
6442
6814
  if (left + tooltipWidth > window.innerWidth - 10) {
6443
- left = (event?.clientX || left) - tooltipWidth - 15;
6815
+ left = (event?.clientX || left) - tooltipWidth - 8;
6444
6816
  }
6445
6817
  if (left < 10) left = 10;
6446
6818
  if (top + tooltipHeight > window.innerHeight - 10) {
6447
- top = (event?.clientY || top) - tooltipHeight - 15;
6819
+ top = (event?.clientY || top) - tooltipHeight - 8;
6448
6820
  }
6449
6821
  if (top < 10) top = 10;
6450
6822
  container.style.left = left + "px";
@@ -6459,8 +6831,229 @@ var EnergyRangeTooltip = {
6459
6831
  if (container) {
6460
6832
  container.classList.remove("visible");
6461
6833
  }
6834
+ },
6835
+ /**
6836
+ * Attach tooltip to an element with automatic show/hide on hover
6837
+ * Returns cleanup function to remove event listeners
6838
+ */
6839
+ attach(element, entityData) {
6840
+ const handleMouseEnter = (e) => {
6841
+ this.show(element, entityData, e);
6842
+ };
6843
+ const handleMouseLeave = () => {
6844
+ this.hide();
6845
+ };
6846
+ const handleMouseMove = (e) => {
6847
+ const container = document.getElementById(this.containerId);
6848
+ if (container && container.classList.contains("visible")) {
6849
+ let left = e.clientX + 8;
6850
+ let top = e.clientY + 8;
6851
+ const tooltipWidth = 320;
6852
+ const tooltipHeight = 380;
6853
+ if (left + tooltipWidth > window.innerWidth - 10) {
6854
+ left = e.clientX - tooltipWidth - 8;
6855
+ }
6856
+ if (left < 10) left = 10;
6857
+ if (top + tooltipHeight > window.innerHeight - 10) {
6858
+ top = e.clientY - tooltipHeight - 8;
6859
+ }
6860
+ if (top < 10) top = 10;
6861
+ container.style.left = left + "px";
6862
+ container.style.top = top + "px";
6863
+ }
6864
+ };
6865
+ element.addEventListener("mouseenter", handleMouseEnter);
6866
+ element.addEventListener("mouseleave", handleMouseLeave);
6867
+ element.addEventListener("mousemove", handleMouseMove);
6868
+ return () => {
6869
+ element.removeEventListener("mouseenter", handleMouseEnter);
6870
+ element.removeEventListener("mouseleave", handleMouseLeave);
6871
+ element.removeEventListener("mousemove", handleMouseMove);
6872
+ this.hide();
6873
+ };
6462
6874
  }
6463
6875
  };
6876
+
6877
+ // src/thingsboard/main-dashboard-shopping/v-4.0.0/card/head-office/card-head-office.js
6878
+ var LABEL_CHAR_LIMIT = 18;
6879
+ var DEFAUL_DELAY_TIME_CONNECTION_IN_MINS = 1440;
6880
+ var CSS_TAG = "head-office-card-v1";
6881
+ function ensureCss() {
6882
+ if (!document.querySelector(`style[data-myio-css="${CSS_TAG}"]`)) {
6883
+ const style = document.createElement("style");
6884
+ style.setAttribute("data-myio-css", CSS_TAG);
6885
+ style.textContent = CSS_STRING;
6886
+ document.head.appendChild(style);
6887
+ }
6888
+ }
6889
+ function normalizeParams(params) {
6890
+ if (!params || !params.entityObject) {
6891
+ throw new Error("renderCardCompenteHeadOffice: entityObject is required");
6892
+ }
6893
+ const LogHelper2 = createLogHelper(params.debugActive ?? false);
6894
+ const entityObject = params.entityObject;
6895
+ if (!entityObject.entityId) {
6896
+ LogHelper2.warn("[CardHeadOffice] entityId is missing, generating temporary ID");
6897
+ entityObject.entityId = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
6898
+ }
6899
+ if (!params.delayTimeConnectionInMins) {
6900
+ LogHelper2.warn(
6901
+ `[CardHeadOffice] delayTimeConnectionInMins is missing, defaulting to ${DEFAUL_DELAY_TIME_CONNECTION_IN_MINS} mins`
6902
+ );
6903
+ }
6904
+ return {
6905
+ entityObject,
6906
+ i18n: { ...DEFAULT_I18N, ...params.i18n || {} },
6907
+ enableSelection: Boolean(params.enableSelection),
6908
+ enableDragDrop: Boolean(params.enableDragDrop),
6909
+ useNewComponents: Boolean(params.useNewComponents),
6910
+ // RFC-0091: Configurable delay time for connection status check (default 15 minutes)
6911
+ delayTimeConnectionInMins: params.delayTimeConnectionInMins ?? DEFAUL_DELAY_TIME_CONNECTION_IN_MINS,
6912
+ // Debug options
6913
+ debugActive: params.debugActive ?? false,
6914
+ activeTooltipDebug: params.activeTooltipDebug ?? false,
6915
+ // LogHelper instance for this card
6916
+ LogHelper: LogHelper2,
6917
+ callbacks: {
6918
+ handleActionDashboard: params.handleActionDashboard,
6919
+ handleActionReport: params.handleActionReport,
6920
+ handleActionSettings: params.handleActionSettings,
6921
+ handleSelect: params.handleSelect,
6922
+ handInfo: params.handInfo,
6923
+ handleClickCard: params.handleClickCard
6924
+ }
6925
+ };
6926
+ }
6927
+ function getIconSvg(deviceType, domain) {
6928
+ if (domain === "water") {
6929
+ return Icons.waterDrop;
6930
+ }
6931
+ if (domain === "temperature") {
6932
+ return Icons.thermometer;
6933
+ }
6934
+ return ICON_MAP[deviceType] || ICON_MAP.DEFAULT;
6935
+ }
6936
+ function formatPower(valueInWatts) {
6937
+ if (valueInWatts === null || valueInWatts === void 0 || isNaN(valueInWatts)) {
6938
+ return { num: "-", unit: "" };
6939
+ }
6940
+ const val = Number(valueInWatts);
6941
+ if (val >= 1e3) {
6942
+ const kw = Math.ceil(val / 1e3 * 100) / 100;
6943
+ return { num: kw.toFixed(2), unit: "kW" };
6944
+ } else {
6945
+ const w = Math.ceil(val);
6946
+ return { num: w.toString(), unit: "W" };
6947
+ }
6948
+ }
6949
+ function formatValueByDomain(value, domain) {
6950
+ if (domain === "water") {
6951
+ return formatWaterVolumeM3(value);
6952
+ }
6953
+ if (domain === "temperature") {
6954
+ return formatTemperature(value, 0);
6955
+ }
6956
+ return formatEnergy(value);
6957
+ }
6958
+ function formatRelativeTime2(timestamp) {
6959
+ if (!timestamp || isNaN(timestamp)) return "\u2014";
6960
+ const date = new Date(timestamp);
6961
+ const hours = String(date.getHours()).padStart(2, "0");
6962
+ const minutes = String(date.getMinutes()).padStart(2, "0");
6963
+ const day = String(date.getDate()).padStart(2, "0");
6964
+ const month = String(date.getMonth() + 1).padStart(2, "0");
6965
+ const year = date.getFullYear();
6966
+ return `${hours}:${minutes} ${day}/${month}/${year}`;
6967
+ }
6968
+ function calculateConsumptionPercentage(target, consumption) {
6969
+ const numericTarget = Number(target);
6970
+ const numericConsumption = Number(consumption);
6971
+ if (isNaN(numericTarget) || isNaN(numericConsumption) || numericTarget <= 0) {
6972
+ return 0;
6973
+ }
6974
+ const percentage = numericConsumption / numericTarget * 100;
6975
+ return percentage;
6976
+ }
6977
+ function getStatusInfo(deviceStatus, i18n, domain) {
6978
+ switch (deviceStatus) {
6979
+ // --- Novos Status de Temperatura ---
6980
+ case "normal":
6981
+ return { chipClass: "chip--ok", label: "Normal" };
6982
+ case "cold":
6983
+ return { chipClass: "chip--standby", label: "Frio" };
6984
+ case "hot":
6985
+ return { chipClass: "chip--alert", label: "Quente" };
6986
+ // --- Status Existentes (aligned with getCardStateClass) ---
6987
+ case DeviceStatusType.POWER_ON:
6988
+ if (domain === "water") {
6989
+ return { chipClass: "chip--power-on", label: i18n.in_operation_water };
6990
+ }
6991
+ return { chipClass: "chip--power-on", label: i18n.in_operation };
6992
+ case DeviceStatusType.STANDBY:
6993
+ return { chipClass: "chip--standby", label: i18n.standby };
6994
+ case DeviceStatusType.WARNING:
6995
+ return { chipClass: "chip--warning", label: i18n.alert };
6996
+ case DeviceStatusType.MAINTENANCE:
6997
+ return { chipClass: "chip--maintenance", label: i18n.maintenance };
6998
+ case DeviceStatusType.FAILURE:
6999
+ return { chipClass: "chip--failure", label: i18n.failure };
7000
+ case DeviceStatusType.POWER_OFF:
7001
+ return { chipClass: "chip--power-off", label: i18n.power_off || i18n.failure };
7002
+ case DeviceStatusType.OFFLINE:
7003
+ return { chipClass: "chip--offline", label: i18n.offline };
7004
+ case DeviceStatusType.NO_INFO:
7005
+ return { chipClass: "chip--no-info", label: i18n.no_info || i18n.offline };
7006
+ case DeviceStatusType.NOT_INSTALLED:
7007
+ return { chipClass: "chip--not-installed", label: i18n.not_installed };
7008
+ default:
7009
+ return { chipClass: "chip--offline", label: i18n.offline };
7010
+ }
7011
+ }
7012
+ function getCardStateClass(deviceStatus) {
7013
+ switch (deviceStatus) {
7014
+ case DeviceStatusType.POWER_ON:
7015
+ return "is-power-on";
7016
+ // Blue border
7017
+ case DeviceStatusType.STANDBY:
7018
+ return "is-standby";
7019
+ // Green border
7020
+ case DeviceStatusType.WARNING:
7021
+ return "is-warning";
7022
+ // Yellow border
7023
+ case DeviceStatusType.MAINTENANCE:
7024
+ return "is-maintenance";
7025
+ // Yellow border
7026
+ case DeviceStatusType.FAILURE:
7027
+ return "is-failure";
7028
+ // Dark Red border
7029
+ case DeviceStatusType.POWER_OFF:
7030
+ return "is-power-off";
7031
+ // Light Red border
7032
+ case DeviceStatusType.OFFLINE:
7033
+ return "is-offline";
7034
+ // Dark Gray border
7035
+ case DeviceStatusType.NO_INFO:
7036
+ return "is-no-info";
7037
+ // Dark Orange border
7038
+ case DeviceStatusType.NOT_INSTALLED:
7039
+ return "is-not-installed";
7040
+ // Purple border
7041
+ default:
7042
+ return "";
7043
+ }
7044
+ }
7045
+ function getTempRangeClass(entityObject) {
7046
+ if (entityObject.domain !== "temperature") return "";
7047
+ const currentTemp = Number(entityObject.val ?? entityObject.currentTemperature ?? entityObject.temperature) || 0;
7048
+ const tempMin = entityObject.temperatureMin ?? entityObject.minTemperature;
7049
+ const tempMax = entityObject.temperatureMax ?? entityObject.maxTemperature;
7050
+ if (tempMin === void 0 || tempMax === void 0 || tempMin === null || tempMax === null) {
7051
+ return "";
7052
+ }
7053
+ if (currentTemp > tempMax) return "is-temp-hot";
7054
+ if (currentTemp < tempMin) return "is-temp-cold";
7055
+ return "is-temp-ok";
7056
+ }
6464
7057
  function getStatusDotClass(deviceStatus) {
6465
7058
  switch (deviceStatus) {
6466
7059
  // --- Novos Status de Temperatura ---
@@ -7581,6 +8174,7 @@ function renderCardComponentV5({
7581
8174
  };
7582
8175
  const isTankDevice = deviceType === "TANK" || deviceType === "CAIXA_DAGUA";
7583
8176
  const isTermostatoDevice = deviceType?.toUpperCase() === "TERMOSTATO";
8177
+ const isEnergyDeviceFlag = isEnergyDevice(deviceType);
7584
8178
  const percentageForDisplay = isTankDevice ? (waterPercentage || 0) * 100 : perc;
7585
8179
  const calculateTempStatus = () => {
7586
8180
  if (temperatureStatus) return temperatureStatus;
@@ -7597,33 +8191,6 @@ function renderCardComponentV5({
7597
8191
  tempStatus,
7598
8192
  isOffline
7599
8193
  });
7600
- const getTemperatureTooltip = () => {
7601
- if (!isTermostatoDevice) return "";
7602
- const currentTemp = Number(val) || 0;
7603
- const hasRange = temperatureMin !== void 0 && temperatureMax !== void 0 && temperatureMin !== null && temperatureMax !== null;
7604
- if (isOffline) {
7605
- return "Dispositivo offline";
7606
- }
7607
- if (!hasRange) {
7608
- return `Temperatura atual: ${currentTemp.toFixed(1)}\xB0C
7609
- Faixa n\xE3o configurada`;
7610
- }
7611
- const rangeText = `Faixa ideal: ${temperatureMin}\xB0C a ${temperatureMax}\xB0C`;
7612
- if (tempStatus === "above") {
7613
- return `ACIMA da faixa ideal
7614
- Temperatura atual: ${currentTemp.toFixed(1)}\xB0C
7615
- ${rangeText}`;
7616
- } else if (tempStatus === "below") {
7617
- return `ABAIXO da faixa ideal
7618
- Temperatura atual: ${currentTemp.toFixed(1)}\xB0C
7619
- ${rangeText}`;
7620
- } else {
7621
- return `DENTRO da faixa ideal
7622
- Temperatura atual: ${currentTemp.toFixed(1)}\xB0C
7623
- ${rangeText}`;
7624
- }
7625
- };
7626
- const temperatureTooltip = getTemperatureTooltip();
7627
8194
  const cardHTML = `
7628
8195
  <div class="device-card-centered clickable ${cardEntity.status === "offline" ? "offline" : ""}"
7629
8196
  data-entity-id="${entityId}"
@@ -7649,7 +8216,7 @@ ${rangeText}`;
7649
8216
  ` : ""}
7650
8217
  </div>
7651
8218
 
7652
- <img class="device-image" src="${deviceImageUrl}" alt="${deviceType}" ${isTermostatoDevice && temperatureTooltip ? `title="${temperatureTooltip}"` : ""} style="${isTermostatoDevice ? "cursor: help;" : ""}" />
8219
+ <img class="device-image ${isTermostatoDevice ? "temp-tooltip-trigger" : ""}${isEnergyDeviceFlag ? " energy-tooltip-trigger" : ""}" src="${deviceImageUrl}" alt="${deviceType}" style="${isTermostatoDevice || isEnergyDeviceFlag ? "cursor: help;" : ""}" />
7653
8220
 
7654
8221
 
7655
8222
  ${customerName && String(customerName).trim() !== "" ? `
@@ -8213,6 +8780,33 @@ ${rangeText}`;
8213
8780
  }
8214
8781
  });
8215
8782
  }
8783
+ const tempTooltipTrigger = enhancedCardElement.querySelector(".temp-tooltip-trigger");
8784
+ let tempTooltipCleanup = null;
8785
+ if (tempTooltipTrigger && isTermostatoDevice) {
8786
+ const tooltipEntityData = {
8787
+ val,
8788
+ temperatureMin,
8789
+ temperatureMax,
8790
+ labelOrName: cardEntity.name,
8791
+ name: cardEntity.name
8792
+ };
8793
+ tempTooltipCleanup = TempRangeTooltip.attach(tempTooltipTrigger, tooltipEntityData);
8794
+ }
8795
+ const energyTooltipTrigger = enhancedCardElement.querySelector(".energy-tooltip-trigger");
8796
+ let energyTooltipCleanup = null;
8797
+ if (energyTooltipTrigger && isEnergyDeviceFlag) {
8798
+ const energyTooltipData = {
8799
+ labelOrName: cardEntity.name,
8800
+ name: cardEntity.name,
8801
+ instantaneousPower: entityObject.instantaneousPower ?? entityObject.consumption_power ?? val,
8802
+ powerRanges: entityObject.powerRanges || entityObject.ranges
8803
+ };
8804
+ energyTooltipCleanup = EnergyRangeTooltip.attach(energyTooltipTrigger, energyTooltipData);
8805
+ }
8806
+ container._cleanup = () => {
8807
+ if (tempTooltipCleanup) tempTooltipCleanup();
8808
+ if (energyTooltipCleanup) energyTooltipCleanup();
8809
+ };
8216
8810
  const jQueryLikeObject = {
8217
8811
  get: (index) => index === 0 ? container : void 0,
8218
8812
  0: container,
@@ -11197,7 +11791,7 @@ var chartJsLoaded = false;
11197
11791
  var zoomPluginLoaded = false;
11198
11792
  var jsPdfLoaded = false;
11199
11793
  var _jspdfPromise = null;
11200
- var cssInjected = false;
11794
+ var cssInjected2 = false;
11201
11795
  var STRINGS2 = {
11202
11796
  "pt-BR": {
11203
11797
  title: "Demanda",
@@ -11373,8 +11967,8 @@ function ensureRoom(doc, nextY, minRoom = 40) {
11373
11967
  }
11374
11968
  return nextY;
11375
11969
  }
11376
- function injectCSS(styles) {
11377
- if (cssInjected) return;
11970
+ function injectCSS2(styles) {
11971
+ if (cssInjected2) return;
11378
11972
  const css = `
11379
11973
  .myio-demand-modal-overlay {
11380
11974
  position: fixed;
@@ -11769,7 +12363,7 @@ function injectCSS(styles) {
11769
12363
  const style = document.createElement("style");
11770
12364
  style.textContent = css;
11771
12365
  document.head.appendChild(style);
11772
- cssInjected = true;
12366
+ cssInjected2 = true;
11773
12367
  }
11774
12368
  function formatDate2(date, locale) {
11775
12369
  return date.toLocaleDateString(locale, {
@@ -11951,7 +12545,7 @@ async function openDemandModal(params) {
11951
12545
  const locale = params.locale || "pt-BR";
11952
12546
  const strings = STRINGS2[locale] || STRINGS2["pt-BR"];
11953
12547
  await loadExternalLibraries();
11954
- injectCSS(styles);
12548
+ injectCSS2(styles);
11955
12549
  const container = typeof params.container === "string" ? document.querySelector(params.container) : params.container || document.body;
11956
12550
  if (!container) {
11957
12551
  throw new Error("Container element not found");
@@ -21600,7 +22194,7 @@ async function createInputDateRangePickerInsideDIV(params) {
21600
22194
  placeholder = "Clique para selecionar per\xEDodo",
21601
22195
  pickerOptions = {},
21602
22196
  classNames = {},
21603
- injectStyles = true,
22197
+ injectStyles: injectStyles2 = true,
21604
22198
  showHelper = true
21605
22199
  } = params;
21606
22200
  validateId(containerId, "containerId");
@@ -21609,7 +22203,7 @@ async function createInputDateRangePickerInsideDIV(params) {
21609
22203
  if (!container) {
21610
22204
  throw new Error(`[createInputDateRangePickerInsideDIV] Container '#${containerId}' not found`);
21611
22205
  }
21612
- if (injectStyles) {
22206
+ if (injectStyles2) {
21613
22207
  injectPremiumStyles();
21614
22208
  }
21615
22209
  let inputEl = document.getElementById(inputId);
@@ -21804,7 +22398,7 @@ function openGoalsPanel(params) {
21804
22398
  modal.setAttribute("aria-labelledby", "goals-modal-title");
21805
22399
  modal.innerHTML = generateModalHTML();
21806
22400
  document.body.appendChild(modal);
21807
- injectStyles();
22401
+ injectStyles2();
21808
22402
  attachEventListeners();
21809
22403
  trapFocus(modal);
21810
22404
  renderTabContent();
@@ -22557,7 +23151,7 @@ function openGoalsPanel(params) {
22557
23151
  maximumFractionDigits: 2
22558
23152
  }).format(value);
22559
23153
  }
22560
- function injectStyles() {
23154
+ function injectStyles2() {
22561
23155
  const styleId = "myio-goals-panel-styles";
22562
23156
  if (document.getElementById(styleId)) return;
22563
23157
  const style = document.createElement("style");
@@ -24814,7 +25408,7 @@ var LIGHT_THEME2 = {
24814
25408
  function getColors(theme) {
24815
25409
  return theme === "dark" ? DARK_THEME2 : LIGHT_THEME2;
24816
25410
  }
24817
- async function fetchCustomerAttributes(customerId, token) {
25411
+ async function fetchCustomerAttributes(customerId, token, onError) {
24818
25412
  const url = `/api/plugins/telemetry/CUSTOMER/${customerId}/values/attributes/SERVER_SCOPE`;
24819
25413
  const response = await fetch(url, {
24820
25414
  method: "GET",
@@ -24827,6 +25421,10 @@ async function fetchCustomerAttributes(customerId, token) {
24827
25421
  if (response.status === 404 || response.status === 400) {
24828
25422
  return { minTemperature: null, maxTemperature: null };
24829
25423
  }
25424
+ if (onError) {
25425
+ onError({ status: response.status, message: `Failed to fetch attributes: ${response.status}` });
25426
+ return { minTemperature: null, maxTemperature: null };
25427
+ }
24830
25428
  throw new Error(`Failed to fetch attributes: ${response.status}`);
24831
25429
  }
24832
25430
  const attributes = await response.json();
@@ -24843,7 +25441,7 @@ async function fetchCustomerAttributes(customerId, token) {
24843
25441
  }
24844
25442
  return { minTemperature, maxTemperature };
24845
25443
  }
24846
- async function saveCustomerAttributes(customerId, token, minTemperature, maxTemperature) {
25444
+ async function saveCustomerAttributes(customerId, token, minTemperature, maxTemperature, onError) {
24847
25445
  const url = `/api/plugins/telemetry/CUSTOMER/${customerId}/SERVER_SCOPE`;
24848
25446
  const attributes = {
24849
25447
  minTemperature,
@@ -24858,6 +25456,10 @@ async function saveCustomerAttributes(customerId, token, minTemperature, maxTemp
24858
25456
  body: JSON.stringify(attributes)
24859
25457
  });
24860
25458
  if (!response.ok) {
25459
+ if (onError) {
25460
+ onError({ status: response.status, message: `Failed to save attributes: ${response.status}` });
25461
+ return;
25462
+ }
24861
25463
  throw new Error(`Failed to save attributes: ${response.status}`);
24862
25464
  }
24863
25465
  }
@@ -25274,7 +25876,7 @@ function openTemperatureSettingsModal(params) {
25274
25876
  state.successMessage = null;
25275
25877
  renderModal3(container, state, modalId, destroy, handleSave);
25276
25878
  try {
25277
- await saveCustomerAttributes(state.customerId, state.token, min, max);
25879
+ await saveCustomerAttributes(state.customerId, state.token, min, max, params.onError);
25278
25880
  state.minTemperature = min;
25279
25881
  state.maxTemperature = max;
25280
25882
  state.isSaving = false;
@@ -25291,7 +25893,7 @@ function openTemperatureSettingsModal(params) {
25291
25893
  }
25292
25894
  };
25293
25895
  renderModal3(container, state, modalId, destroy, handleSave);
25294
- fetchCustomerAttributes(state.customerId, state.token).then(({ minTemperature, maxTemperature }) => {
25896
+ fetchCustomerAttributes(state.customerId, state.token, params.onError).then(({ minTemperature, maxTemperature }) => {
25295
25897
  state.minTemperature = minTemperature;
25296
25898
  state.maxTemperature = maxTemperature;
25297
25899
  state.isLoading = false;
@@ -27452,7 +28054,7 @@ function createConsumptionChartWidget(config) {
27452
28054
  </div>
27453
28055
  `;
27454
28056
  }
27455
- function injectStyles() {
28057
+ function injectStyles2() {
27456
28058
  if (styleElement) return;
27457
28059
  styleElement = document.createElement("style");
27458
28060
  styleElement.id = `${widgetId}-styles`;
@@ -27934,7 +28536,7 @@ function createConsumptionChartWidget(config) {
27934
28536
  console.error(`[ConsumptionWidget] Container #${config.containerId} not found`);
27935
28537
  return;
27936
28538
  }
27937
- injectStyles();
28539
+ injectStyles2();
27938
28540
  containerElement.innerHTML = renderHTML();
27939
28541
  setupListeners();
27940
28542
  setLoading(true);
@@ -29073,6 +29675,7 @@ export {
29073
29675
  EXPORT_DOMAIN_ICONS,
29074
29676
  EXPORT_DOMAIN_LABELS,
29075
29677
  EXPORT_DOMAIN_UNITS,
29678
+ EnergyRangeTooltip,
29076
29679
  MyIOChartModal,
29077
29680
  MyIODraggableCard,
29078
29681
  MyIOSelectionStore,
@@ -29081,6 +29684,7 @@ export {
29081
29684
  DEVICE_TYPES as POWER_LIMITS_DEVICE_TYPES,
29082
29685
  STATUS_CONFIG as POWER_LIMITS_STATUS_CONFIG,
29083
29686
  TELEMETRY_TYPES2 as POWER_LIMITS_TELEMETRY_TYPES,
29687
+ TempRangeTooltip,
29084
29688
  addDetectionContext,
29085
29689
  addNamespace,
29086
29690
  aggregateByDay,