myio-js-library 0.1.30 → 0.1.32

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/README.md CHANGED
@@ -1081,6 +1081,30 @@ const controller = await MyIOLibrary.createInputDateRangePickerInsideDIV({
1081
1081
 
1082
1082
  The library includes four premium modal components for ThingsBoard dashboards that provide comprehensive device analytics and reporting capabilities.
1083
1083
 
1084
+ #### Token Authentication
1085
+
1086
+ Both AllReportModal and DeviceReportModal use a dual-token authentication system to access different API endpoints:
1087
+
1088
+ - **`api.tbJwtToken`** - Used for ThingsBoard API endpoints (/) for device management and configuration
1089
+ - **`api.ingestionToken`** - Used for Data API endpoints (https://data.apps.myio-bas.com) for telemetry data access
1090
+
1091
+ **Important**: The tokens are automatically applied based on the target endpoint. You must provide the appropriate token in the API configuration:
1092
+
1093
+ ```javascript
1094
+ // Example API configuration with both tokens
1095
+ const apiConfig = {
1096
+ clientId: 'your-client-id',
1097
+ clientSecret: 'your-client-secret',
1098
+ dataApiBaseUrl: 'https://data.apps.myio-bas.com',
1099
+ tbJwtToken: 'jwt-token-for-thingsboard-apis', // For ThingsBoard endpoints
1100
+ ingestionToken: 'ingestion-token-for-data-apis' // For Data API endpoints
1101
+ };
1102
+ ```
1103
+
1104
+ The modals will automatically select the correct token based on the API endpoint being called:
1105
+ - Data API calls (data.apps.myio-bas.com) → Uses `ingestionToken`
1106
+ - ThingsBoard API calls (/) → Uses `tbJwtToken`
1107
+
1084
1108
  #### `openDashboardPopupReport(params: OpenDeviceReportParams): ModalHandle`
1085
1109
 
1086
1110
  Opens a device-specific daily consumption report modal with built-in date range picker, sortable table, and CSV export functionality.
package/dist/index.cjs CHANGED
@@ -2631,19 +2631,19 @@ function renderCardComponentV2({
2631
2631
  </div>
2632
2632
  </div>
2633
2633
 
2634
- <div class="connection-status-icon">
2635
- ${connectionIcon}
2636
- </div>
2634
+ <div class="connection-status-icon" data-conn="${connectionStatus}" data-state="${deviceStatus}" aria-label="${connectionStatus}"></div>
2637
2635
 
2638
2636
  </div>
2639
2637
  `;
2640
2638
  container.innerHTML = cardHTML;
2641
2639
  const enhancedCardElement = container.querySelector(".device-card-centered");
2642
- if (!document.getElementById("myio-enhanced-card-layout-styl\u0111es")) {
2640
+ if (!document.getElementById("myio-enhanced-card-layout-styles")) {
2643
2641
  const layoutStyle = document.createElement("style");
2644
2642
  layoutStyle.id = "myio-enhanced-card-layout-styles";
2645
2643
  layoutStyle.textContent = `
2646
- /* Premium Enhanced Card Design - Reduced Size (10% smaller) */
2644
+ /* ===== MYIO Card v2 \u2014 Clean Piano Keys & Flat Status ===== */
2645
+
2646
+ /* Card shell (kept) */
2647
2647
  .device-card-centered.clickable {
2648
2648
  width: 90% !important;
2649
2649
  max-width: 280px !important;
@@ -2682,8 +2682,8 @@ function renderCardComponentV2({
2682
2682
  .device-card-centered.clickable:hover::before {
2683
2683
  opacity: 1;
2684
2684
  }
2685
-
2686
- /* Premium Selection State */
2685
+
2686
+ /* Selected / Offline (kept) */
2687
2687
  .device-card-centered.selected {
2688
2688
  border: 2px solid #00e09e !important;
2689
2689
  box-shadow: 0 12px 40px rgba(0, 224, 158, 0.25), 0 4px 12px rgba(0, 224, 158, 0.15) !important;
@@ -2696,7 +2696,6 @@ function renderCardComponentV2({
2696
2696
  background: linear-gradient(90deg, #00e09e 0%, #00d4aa 100%);
2697
2697
  }
2698
2698
 
2699
- /* Premium Offline State */
2700
2699
  .device-card-centered.offline {
2701
2700
  border: 2px solid #ef4444 !important;
2702
2701
  background: linear-gradient(145deg, #fef2f2 0%, #fee2e2 50%, #fef2f2 100%) !important;
@@ -2716,8 +2715,8 @@ function renderCardComponentV2({
2716
2715
  box-shadow: 0 12px 40px rgba(239, 68, 68, 0.25), 0 4px 12px rgba(239, 68, 68, 0.2);
2717
2716
  }
2718
2717
  }
2719
-
2720
- /* Premium Device Image - Reduced Size (10% smaller) */
2718
+
2719
+ /* Device image & titles (kept) */
2721
2720
  .device-card-centered .device-image {
2722
2721
  max-height: 47px !important;
2723
2722
  width: auto;
@@ -2733,7 +2732,6 @@ function renderCardComponentV2({
2733
2732
  transform: scale(1.05);
2734
2733
  }
2735
2734
 
2736
- /* Premium Typography - Reduced Size (10% smaller) - FORCED VERTICAL LAYOUT */
2737
2735
  .device-card-centered .device-title-row {
2738
2736
  display: flex !important;
2739
2737
  flex-direction: column !important;
@@ -2770,8 +2768,8 @@ function renderCardComponentV2({
2770
2768
  margin: 0 !important;
2771
2769
  padding: 0 !important;
2772
2770
  }
2773
-
2774
- /* Premium Value Display - Reduced Size (10% smaller) */
2771
+
2772
+ /* Value pill (kept) */
2775
2773
  .device-card-centered .consumption-main {
2776
2774
  background: linear-gradient(135deg, rgba(0, 224, 158, 0.1) 0%, rgba(0, 180, 216, 0.1) 100%);
2777
2775
  border-radius: 10px;
@@ -2795,11 +2793,9 @@ function renderCardComponentV2({
2795
2793
  margin-left: 5px;
2796
2794
  }
2797
2795
 
2798
- /* Premium Status Icons - Reduced Size (10% smaller) */
2799
2796
  .device-card-centered .flash-icon {
2800
2797
  font-size: 1rem !important;
2801
2798
  margin-right: 7px;
2802
- filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
2803
2799
  transition: all 0.3s ease;
2804
2800
  }
2805
2801
 
@@ -2823,8 +2819,8 @@ function renderCardComponentV2({
2823
2819
  filter: drop-shadow(0 4px 8px rgba(239, 68, 68, 0.3));
2824
2820
  }
2825
2821
  }
2826
-
2827
- /* Premium Checkbox - Reduced Size (10% smaller) */
2822
+
2823
+ /* Checkbox (kept) */
2828
2824
  .device-card-centered .card-checkbox {
2829
2825
  width: 16px !important;
2830
2826
  height: 16px !important;
@@ -2861,71 +2857,105 @@ function renderCardComponentV2({
2861
2857
  font-weight: bold;
2862
2858
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
2863
2859
  }
2864
-
2865
- /* Premium Card Actions - Mirrored to Left Side - Reduced Size (10% smaller) */
2860
+
2861
+ /* \u2014\u2014\u2014 NEW: Piano-Key Actions (flat by default, full height) \u2014\u2014\u2014 */
2866
2862
  .device-card-centered .card-actions {
2867
2863
  position: absolute;
2868
- top: 10px;
2869
- left: 10px;
2864
+ left: 12px;
2865
+ top: 12px;
2866
+ bottom: 12px;
2867
+ padding: 0;
2870
2868
  display: flex;
2871
2869
  flex-direction: column;
2872
- gap: 7px;
2870
+ gap: 0;
2871
+ border: 1px solid rgba(226, 232, 240, 0.9);
2872
+ border-radius: 8px;
2873
+ background: #fff;
2874
+ overflow: visible;
2875
+ box-shadow: none !important;
2873
2876
  z-index: 10;
2874
2877
  }
2875
2878
 
2876
2879
  .device-card-centered .card-action {
2877
- width: 32px !important;
2878
- height: 32px !important;
2879
- border-radius: 9px !important;
2880
- background: rgba(255, 255, 255, 0.9) !important;
2881
- border: 1px solid rgba(226, 232, 240, 0.8) !important;
2882
- backdrop-filter: blur(10px);
2883
- box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
2884
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
2880
+ width: 36px !important;
2881
+ height: 36px !important;
2882
+ border: 0;
2883
+ border-bottom: 1px solid rgba(226, 232, 240, 0.9);
2884
+ background: #fff !important;
2885
+ box-shadow: none !important;
2886
+ backdrop-filter: none !important;
2887
+ transform: translateZ(0);
2888
+ transition: transform 0.18s ease, box-shadow 0.18s ease;
2885
2889
  cursor: pointer;
2886
2890
  display: flex;
2887
2891
  align-items: center;
2888
2892
  justify-content: center;
2893
+ margin: 0;
2894
+ border-radius: 0;
2889
2895
  }
2890
2896
 
2891
- .device-card-centered .card-action:hover {
2892
- background: rgba(0, 224, 158, 0.1) !important;
2893
- border-color: rgba(0, 224, 158, 0.3) !important;
2894
- transform: scale(1.1) translateY(-1px);
2895
- box-shadow: 0 3px 10px rgba(0, 224, 158, 0.15);
2897
+ .device-card-centered .card-action:first-child {
2898
+ border-top-left-radius: 7px;
2899
+ border-top-right-radius: 7px;
2900
+ }
2901
+
2902
+ .device-card-centered .card-action:last-child {
2903
+ border-bottom: 0;
2904
+ border-bottom-left-radius: 7px;
2905
+ border-bottom-right-radius: 7px;
2896
2906
  }
2897
2907
 
2898
2908
  .device-card-centered .card-action img {
2899
2909
  width: 16px !important;
2900
2910
  height: 16px !important;
2901
- filter: grayscale(0.2) brightness(0.8);
2902
- transition: all 0.3s ease;
2911
+ filter: grayscale(0.2) brightness(0.85);
2912
+ transition: transform 0.18s ease, filter 0.18s ease;
2913
+ }
2914
+
2915
+ /* Lift on interaction only */
2916
+ .device-card-centered .card-action:hover,
2917
+ .device-card-centered .card-action:focus-visible {
2918
+ transform: translateY(-2px) scale(1.05);
2919
+ box-shadow: 0 6px 14px rgba(16, 24, 40, 0.12), 0 2px 6px rgba(16, 24, 40, 0.08);
2920
+ outline: none;
2903
2921
  }
2904
2922
 
2905
- .device-card-centered .card-action:hover img {
2923
+ .device-card-centered .card-action:hover img,
2924
+ .device-card-centered .card-action:focus-visible img {
2906
2925
  filter: grayscale(0) brightness(1);
2907
- transform: scale(1.1);
2926
+ transform: scale(1.08);
2908
2927
  }
2909
-
2910
- /* Premium Info Panel */
2911
- .myio-enhanced-card-container .info-panel {
2912
- background: rgba(255, 255, 255, 0.95) !important;
2913
- backdrop-filter: blur(20px) !important;
2914
- border-radius: 16px !important;
2915
- border: 1px solid rgba(226, 232, 240, 0.8);
2916
- box-shadow: 0 16px 48px rgba(0, 0, 0, 0.12);
2928
+
2929
+ /* \u2014\u2014\u2014 NEW: Flat Status (CSS dot, never clipped) \u2014\u2014\u2014 */
2930
+ .device-card-centered .connection-status-icon {
2931
+ position: absolute;
2932
+ bottom: 18px;
2933
+ right: 18px;
2934
+ width: 14px;
2935
+ height: 14px;
2936
+ border-radius: 50%;
2937
+ background: #22c55e;
2938
+ border: 1px solid rgba(0, 0, 0, 0.06);
2939
+ box-shadow: none !important;
2940
+ backdrop-filter: none !important;
2941
+ z-index: 5;
2917
2942
  }
2918
-
2919
- .myio-enhanced-card-container .info-close {
2920
- background: rgba(239, 68, 68, 0.1) !important;
2921
- border: 1px solid rgba(239, 68, 68, 0.2) !important;
2922
- border-radius: 8px !important;
2923
- width: 28px;
2924
- height: 28px;
2925
- display: flex;
2926
- align-items: center;
2927
- justify-content: center;
2928
- transition: all 0.3s ease;
2943
+
2944
+ /* Map colors by connection or device state */
2945
+ .device-card-centered .connection-status-icon[data-conn="offline"] {
2946
+ background: #94a3b8;
2947
+ }
2948
+ .device-card-centered .connection-status-icon[data-state="warning"] {
2949
+ background: #f59e0b;
2950
+ }
2951
+ .device-card-centered .connection-status-icon[data-state="danger"] {
2952
+ background: #ef4444;
2953
+ }
2954
+ .device-card-centered .connection-status-icon[data-state="no_info"] {
2955
+ background: #94a3b8;
2956
+ }
2957
+ .device-card-centered .connection-status-icon[data-state="maintenance"] {
2958
+ background: #0ea5e9;
2929
2959
  }
2930
2960
 
2931
2961
  .myio-enhanced-card-container .info-close:hover {
@@ -2933,24 +2963,7 @@ function renderCardComponentV2({
2933
2963
  transform: scale(1.1);
2934
2964
  }
2935
2965
 
2936
- .device-card-centered .connection-status-icon {
2937
- position: absolute;
2938
- bottom: 10px;
2939
- right: 12px;
2940
- width: 22px;
2941
- height: 22px;
2942
- display: flex;
2943
- align-items: center;
2944
- justify-content: center;
2945
- border-radius: 50%;
2946
- background: rgba(255, 255, 255, 0.8);
2947
- box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);
2948
- font-size: 14px;
2949
- z-index: 5;
2950
- backdrop-filter: blur(4px);
2951
- }
2952
-
2953
- /* Premium Responsive Design */
2966
+ /* Responsive / Dark mode (kept) */
2954
2967
  @media (max-width: 768px) {
2955
2968
  .device-card-centered.clickable {
2956
2969
  padding: 16px !important;
@@ -2962,12 +2975,11 @@ function renderCardComponentV2({
2962
2975
  }
2963
2976
 
2964
2977
  .device-card-centered .card-action {
2965
- width: 32px !important;
2966
- height: 32px !important;
2978
+ width: 36px !important;
2979
+ height: 36px !important;
2967
2980
  }
2968
2981
  }
2969
2982
 
2970
- /* Premium Dark Mode Support */
2971
2983
  @media (prefers-color-scheme: dark) {
2972
2984
  .device-card-centered.clickable {
2973
2985
  background: linear-gradient(145deg, #1e293b 0%, #334155 100%) !important;
@@ -2983,9 +2995,14 @@ function renderCardComponentV2({
2983
2995
  color: #94a3b8 !important;
2984
2996
  }
2985
2997
 
2998
+ .device-card-centered .card-actions {
2999
+ border-color: rgba(71, 85, 105, 0.8);
3000
+ background: #1e293b;
3001
+ }
3002
+
2986
3003
  .device-card-centered .card-action {
2987
- background: rgba(30, 41, 59, 0.9) !important;
2988
- border-color: rgba(71, 85, 105, 0.8) !important;
3004
+ background: #1e293b !important;
3005
+ border-bottom: 1px solid rgba(71, 85, 105, 0.8);
2989
3006
  }
2990
3007
  }
2991
3008
  `;
@@ -5759,10 +5776,11 @@ var createDefaultEnergyFetcher = (params) => {
5759
5776
  const url = `${baseUrl}/api/v1/telemetry/devices/${ingestionId}/energy?startTime=${encodeURIComponent(startISO)}&endTime=${encodeURIComponent(endISO)}&granularity=1d&page=1&pageSize=1000&deep=0`;
5760
5777
  const token = params.api.ingestionToken;
5761
5778
  if (!token) {
5762
- throw new Error("ingestionToken is required for Data/Ingestion API calls");
5779
+ throw new Error("ingestionToken is required for Data API calls to data.apps.myio-bas.com");
5763
5780
  }
5764
5781
  const response = await fetch(url, {
5765
5782
  headers: {
5783
+ // Using ingestionToken for Data API endpoints (data.apps.myio-bas.com)
5766
5784
  "Authorization": `Bearer ${token}`,
5767
5785
  "Content-Type": "application/json"
5768
5786
  }
@@ -7327,15 +7345,19 @@ var AllReportModal = class {
7327
7345
  }
7328
7346
  async fetchCustomerTotals(startISO, endISO) {
7329
7347
  if (this.params.fetcher) {
7348
+ const token2 = this.params.api.ingestionToken || await this.authClient.getBearer();
7330
7349
  return await this.params.fetcher({
7331
7350
  baseUrl: this.params.api.dataApiBaseUrl || "https://api.data.apps.myio-bas.com",
7332
- token: await this.authClient.getBearer(),
7351
+ token: token2,
7333
7352
  customerId: this.params.customerId,
7334
7353
  startISO,
7335
7354
  endISO
7336
7355
  });
7337
7356
  }
7338
- const token = await this.authClient.getBearer();
7357
+ const token = this.params.api.ingestionToken;
7358
+ if (!token) {
7359
+ throw new Error("ingestionToken is required for Data API calls to data.apps.myio-bas.com");
7360
+ }
7339
7361
  const baseUrl = this.params.api.dataApiBaseUrl || "https://api.data.apps.myio-bas.com";
7340
7362
  const startTime = encodeURIComponent(startISO);
7341
7363
  const endTime = encodeURIComponent(endISO);
@@ -7344,26 +7366,12 @@ var AllReportModal = class {
7344
7366
  const response = await fetch(url, {
7345
7367
  method: "GET",
7346
7368
  headers: {
7369
+ // Using ingestionToken for Data API endpoints (data.apps.myio-bas.com)
7347
7370
  "Authorization": `Bearer ${token}`,
7348
7371
  "Content-Type": "application/json"
7349
7372
  }
7350
7373
  });
7351
7374
  if (!response.ok) {
7352
- if (response.status === 401) {
7353
- this.authClient.clearCache();
7354
- const newToken = await this.authClient.getBearer();
7355
- const retryResponse = await fetch(url, {
7356
- method: "GET",
7357
- headers: {
7358
- "Authorization": `Bearer ${newToken}`,
7359
- "Content-Type": "application/json"
7360
- }
7361
- });
7362
- if (!retryResponse.ok) {
7363
- throw new Error(`Erro de autentica\xE7\xE3o: ${retryResponse.status} ${retryResponse.statusText}`);
7364
- }
7365
- return await retryResponse.json();
7366
- }
7367
7375
  throw new Error(`Erro na API: ${response.status} ${response.statusText}`);
7368
7376
  }
7369
7377
  const data = await response.json();