myio-js-library 0.1.402 → 0.1.403

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1121,7 +1121,7 @@ module.exports = __toCommonJS(index_exports);
1121
1121
  // package.json
1122
1122
  var package_default = {
1123
1123
  name: "myio-js-library",
1124
- version: "0.1.402",
1124
+ version: "0.1.403",
1125
1125
  description: "A clean, standalone JS SDK for MYIO projects",
1126
1126
  license: "MIT",
1127
1127
  repository: "github:gh-myio/myio-js-library",
@@ -99747,6 +99747,15 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
99747
99747
  transform: translateY(0) scale(1);
99748
99748
  }
99749
99749
 
99750
+ /* ---- Maximized state ---- */
99751
+ .adm-drawer--maximized {
99752
+ width: 100% !important;
99753
+ max-width: 100% !important;
99754
+ max-height: 100vh !important;
99755
+ height: 100vh !important;
99756
+ border-radius: 0 !important;
99757
+ }
99758
+
99750
99759
  /* ---- Header ---- */
99751
99760
  .adm-header {
99752
99761
  padding: 14px 18px 12px;
@@ -99762,6 +99771,13 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
99762
99771
  margin-bottom: 8px;
99763
99772
  }
99764
99773
 
99774
+ .adm-header-actions {
99775
+ display: flex;
99776
+ align-items: center;
99777
+ gap: 4px;
99778
+ flex-shrink: 0;
99779
+ }
99780
+
99765
99781
  .adm-title {
99766
99782
  display: block;
99767
99783
  margin: 0;
@@ -99776,6 +99792,25 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
99776
99792
  letter-spacing: -0.1px;
99777
99793
  }
99778
99794
 
99795
+ .adm-header-btn {
99796
+ display: flex;
99797
+ align-items: center;
99798
+ justify-content: center;
99799
+ width: 28px;
99800
+ height: 28px;
99801
+ border: none;
99802
+ background: #f3f4f6;
99803
+ border-radius: 6px;
99804
+ cursor: pointer;
99805
+ color: #6b7280;
99806
+ transition: background 0.15s, color 0.15s;
99807
+ }
99808
+
99809
+ .adm-header-btn:hover {
99810
+ background: #e5e7eb;
99811
+ color: #111827;
99812
+ }
99813
+
99779
99814
  .adm-close {
99780
99815
  display: flex;
99781
99816
  align-items: center;
@@ -100041,56 +100076,58 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
100041
100076
 
100042
100077
  .adm-timeline-item {
100043
100078
  position: relative;
100044
- margin-bottom: 16px;
100079
+ display: flex !important;
100080
+ align-items: center;
100081
+ flex-wrap: nowrap;
100082
+ gap: 5px;
100083
+ margin-bottom: 10px;
100084
+ min-height: 20px;
100085
+ overflow: hidden;
100045
100086
  }
100046
100087
 
100047
100088
  .adm-timeline-dot {
100048
100089
  position: absolute;
100049
100090
  left: -23px;
100050
- top: 3px;
100091
+ top: 50%;
100092
+ transform: translateY(-50%);
100051
100093
  width: 12px;
100052
100094
  height: 12px;
100053
100095
  border-radius: 50%;
100054
100096
  background: #8b5cf6;
100055
100097
  border: 2px solid #fff;
100056
100098
  box-shadow: 0 0 0 2px #8b5cf6;
100099
+ flex-shrink: 0;
100057
100100
  }
100058
100101
 
100059
100102
  .adm-timeline-item.is-last .adm-timeline-dot { background: #3b82f6; box-shadow: 0 0 0 2px #3b82f6; }
100060
100103
  .adm-timeline-item.is-first .adm-timeline-dot { background: #ef4444; box-shadow: 0 0 0 2px #ef4444; }
100061
100104
  .adm-timeline-item.is-single .adm-timeline-dot { background: #7c3aed; box-shadow: 0 0 0 2px #7c3aed; }
100062
100105
 
100063
- .adm-timeline-content { }
100064
-
100065
100106
  .adm-timeline-num {
100066
- font-size: 9px;
100107
+ font-size: 11px;
100067
100108
  font-weight: 700;
100068
- text-transform: uppercase;
100069
- letter-spacing: 0.4px;
100070
- color: #9ca3af;
100071
- margin-bottom: 2px;
100072
- }
100073
-
100074
- .adm-timeline-time {
100075
- font-size: 13px;
100076
- font-weight: 600;
100077
100109
  color: #111827;
100110
+ white-space: nowrap;
100111
+ flex-shrink: 0;
100078
100112
  }
100079
100113
 
100080
- .adm-timeline-meta {
100081
- font-size: 11px;
100082
- color: #9ca3af;
100083
- margin-top: 2px;
100114
+ .adm-timeline-sep {
100115
+ color: #d1d5db;
100116
+ font-size: 10px;
100117
+ flex-shrink: 0;
100118
+ user-select: none;
100084
100119
  }
100085
100120
 
100086
- .adm-timeline-row {
100087
- display: flex;
100088
- align-items: center;
100089
- justify-content: space-between;
100090
- gap: 6px;
100121
+ .adm-timeline-time {
100122
+ font-size: 12px;
100123
+ font-weight: 600;
100124
+ color: #374151;
100125
+ white-space: nowrap;
100126
+ flex-shrink: 0;
100091
100127
  }
100092
100128
 
100093
100129
  .adm-timeline-value-pill {
100130
+ display: inline-block;
100094
100131
  font-size: 10px;
100095
100132
  font-weight: 600;
100096
100133
  background: #f0fdf4;
@@ -100099,17 +100136,18 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
100099
100136
  border-radius: 4px;
100100
100137
  padding: 1px 6px;
100101
100138
  white-space: nowrap;
100139
+ vertical-align: middle;
100102
100140
  flex-shrink: 0;
100103
100141
  }
100104
100142
 
100105
100143
  .adm-timeline-device {
100106
100144
  font-size: 11px;
100107
100145
  color: #6b7280;
100108
- margin-top: 3px;
100109
100146
  white-space: nowrap;
100110
100147
  overflow: hidden;
100111
100148
  text-overflow: ellipsis;
100112
- max-width: 220px;
100149
+ min-width: 0;
100150
+ flex: 0 1 auto;
100113
100151
  }
100114
100152
 
100115
100153
  .adm-timeline-ellipsis {
@@ -100201,7 +100239,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
100201
100239
 
100202
100240
  .adm-matrix-header {
100203
100241
  display: grid;
100204
- grid-template-columns: 52px 1fr 1fr;
100242
+ grid-template-columns: 52px 1fr 1fr 64px;
100205
100243
  gap: 0;
100206
100244
  background: #f9fafb;
100207
100245
  border-bottom: 1px solid #e5e7eb;
@@ -100218,7 +100256,7 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
100218
100256
 
100219
100257
  .adm-matrix-row {
100220
100258
  display: grid;
100221
- grid-template-columns: 52px 1fr 1fr;
100259
+ grid-template-columns: 52px 1fr 1fr 64px;
100222
100260
  gap: 0;
100223
100261
  padding: 7px 10px;
100224
100262
  border-bottom: 1px solid #f3f4f6;
@@ -100256,6 +100294,14 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
100256
100294
  cursor: help;
100257
100295
  }
100258
100296
 
100297
+ .adm-matrix-trigger {
100298
+ font-family: 'SF Mono', Monaco, monospace;
100299
+ font-size: 10px;
100300
+ font-weight: 600;
100301
+ color: #374151;
100302
+ white-space: nowrap;
100303
+ }
100304
+
100259
100305
  .adm-device-chip {
100260
100306
  display: inline-flex;
100261
100307
  align-items: center;
@@ -100478,21 +100524,32 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
100478
100524
  color: #fff;
100479
100525
  }
100480
100526
 
100481
- /* Chart wrapper + variants */
100527
+ /* Chart wrapper + canvas */
100482
100528
  .adm-chart-wrapper {
100483
100529
  position: relative;
100484
- min-height: 80px;
100530
+ height: 210px;
100485
100531
  }
100486
100532
 
100487
- .adm-chart-variant {
100488
- display: none;
100533
+ .adm-chart-canvas {
100534
+ display: block;
100535
+ width: 100% !important;
100536
+ height: 100% !important;
100489
100537
  }
100490
100538
 
100491
- .adm-chart-variant.is-active {
100492
- display: block;
100539
+ /* Secondary chart wrappers (DOW + HOD) */
100540
+ .adm-chart-canvas-wrap {
100541
+ position: relative;
100542
+ }
100543
+
100544
+ .adm-chart-canvas-wrap--dow {
100545
+ height: 148px;
100493
100546
  }
100494
100547
 
100495
- /* Legend */
100548
+ .adm-chart-canvas-wrap--hod {
100549
+ height: 120px;
100550
+ }
100551
+
100552
+ /* Legend (kept for Chart.js built-in legend) */
100496
100553
  .adm-chart-legend {
100497
100554
  display: flex;
100498
100555
  flex-wrap: wrap;
@@ -100929,6 +100986,283 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
100929
100986
 
100930
100987
  .adm-annot-archived-list { margin-top: 8px; }
100931
100988
 
100989
+ /* =====================================================================
100990
+ Alarm Details Modal \u2014 Dark Mode
100991
+ Activated by data-theme="dark" on .adm-overlay
100992
+ ===================================================================== */
100993
+
100994
+ .adm-overlay[data-theme="dark"] .adm-drawer {
100995
+ background: #1e1f2e;
100996
+ box-shadow: 0 24px 80px rgba(0, 0, 0, 0.6);
100997
+ }
100998
+
100999
+ .adm-overlay[data-theme="dark"] .adm-header {
101000
+ border-color: #2a2b3d;
101001
+ }
101002
+
101003
+ .adm-overlay[data-theme="dark"] .adm-title {
101004
+ color: #e2e8f0;
101005
+ }
101006
+
101007
+ .adm-overlay[data-theme="dark"] .adm-header-btn,
101008
+ .adm-overlay[data-theme="dark"] .adm-close {
101009
+ background: #2a2b3d;
101010
+ color: #94a3b8;
101011
+ }
101012
+
101013
+ .adm-overlay[data-theme="dark"] .adm-header-btn:hover,
101014
+ .adm-overlay[data-theme="dark"] .adm-close:hover {
101015
+ background: #363750;
101016
+ color: #e2e8f0;
101017
+ }
101018
+
101019
+ .adm-overlay[data-theme="dark"] .adm-badge-state {
101020
+ background: #2a2b3d;
101021
+ color: #94a3b8;
101022
+ }
101023
+
101024
+ .adm-overlay[data-theme="dark"] .adm-badge-source {
101025
+ background: #252636;
101026
+ border-color: #2a2b3d;
101027
+ color: #94a3b8;
101028
+ }
101029
+
101030
+ .adm-overlay[data-theme="dark"] .adm-tabs {
101031
+ border-color: #2a2b3d;
101032
+ }
101033
+
101034
+ .adm-overlay[data-theme="dark"] .adm-tab {
101035
+ color: #64748b;
101036
+ }
101037
+
101038
+ .adm-overlay[data-theme="dark"] .adm-tab:hover {
101039
+ color: #e2e8f0;
101040
+ }
101041
+
101042
+ .adm-overlay[data-theme="dark"] .adm-tab.is-active {
101043
+ color: #a78bfa;
101044
+ border-bottom-color: #a78bfa;
101045
+ }
101046
+
101047
+ .adm-overlay[data-theme="dark"] .adm-tab-badge {
101048
+ background: #2d1b69;
101049
+ color: #a78bfa;
101050
+ }
101051
+
101052
+ .adm-overlay[data-theme="dark"] .adm-body {
101053
+ scrollbar-color: #2a2b3d transparent;
101054
+ }
101055
+
101056
+ .adm-overlay[data-theme="dark"] .adm-body::-webkit-scrollbar-thumb {
101057
+ background: #2a2b3d;
101058
+ }
101059
+
101060
+ .adm-overlay[data-theme="dark"] .adm-kpi {
101061
+ background: #252636;
101062
+ border-color: #2a2b3d;
101063
+ }
101064
+
101065
+ .adm-overlay[data-theme="dark"] .adm-kpi-value {
101066
+ color: #e2e8f0;
101067
+ }
101068
+
101069
+ .adm-overlay[data-theme="dark"] .adm-kpi-label {
101070
+ color: #64748b;
101071
+ }
101072
+
101073
+ .adm-overlay[data-theme="dark"] .adm-section-title {
101074
+ color: #94a3b8;
101075
+ border-color: #2a2b3d;
101076
+ }
101077
+
101078
+ .adm-overlay[data-theme="dark"] .adm-row {
101079
+ border-color: #2a2b3d;
101080
+ }
101081
+
101082
+ .adm-overlay[data-theme="dark"] .adm-row-label {
101083
+ color: #64748b;
101084
+ }
101085
+
101086
+ .adm-overlay[data-theme="dark"] .adm-row-value {
101087
+ color: #cbd5e1;
101088
+ }
101089
+
101090
+ .adm-overlay[data-theme="dark"] .adm-description {
101091
+ background: #252636;
101092
+ border-color: #2a2b3d;
101093
+ color: #94a3b8;
101094
+ }
101095
+
101096
+ .adm-overlay[data-theme="dark"] .adm-timeline::before {
101097
+ background: #2a2b3d;
101098
+ }
101099
+
101100
+ .adm-overlay[data-theme="dark"] .adm-timeline-item {
101101
+ color: #94a3b8;
101102
+ }
101103
+
101104
+ .adm-overlay[data-theme="dark"] .adm-timeline-dot {
101105
+ border-color: #1e1f2e;
101106
+ }
101107
+
101108
+ .adm-overlay[data-theme="dark"] .adm-timeline-num {
101109
+ color: #cbd5e1;
101110
+ }
101111
+
101112
+ .adm-overlay[data-theme="dark"] .adm-timeline-device {
101113
+ color: #64748b;
101114
+ }
101115
+
101116
+ .adm-overlay[data-theme="dark"] .adm-devices-list {
101117
+ border-color: #2a2b3d;
101118
+ }
101119
+
101120
+ .adm-overlay[data-theme="dark"] .adm-device-row {
101121
+ border-color: #2a2b3d;
101122
+ color: #94a3b8;
101123
+ }
101124
+
101125
+ .adm-overlay[data-theme="dark"] .adm-device-row:nth-child(odd) {
101126
+ background: #252636;
101127
+ }
101128
+
101129
+ .adm-overlay[data-theme="dark"] .adm-device-chip {
101130
+ background: #252636;
101131
+ border-color: #2a2b3d;
101132
+ color: #94a3b8;
101133
+ }
101134
+
101135
+ .adm-overlay[data-theme="dark"] .adm-chart-btn {
101136
+ background: #252636;
101137
+ border-color: #2a2b3d;
101138
+ color: #64748b;
101139
+ }
101140
+
101141
+ .adm-overlay[data-theme="dark"] .adm-chart-btn.is-active {
101142
+ background: #2d1b69;
101143
+ border-color: #6d28d9;
101144
+ color: #a78bfa;
101145
+ }
101146
+
101147
+ .adm-overlay[data-theme="dark"] .adm-chart-btn:hover:not(.is-active) {
101148
+ background: #2a2b3d;
101149
+ color: #e2e8f0;
101150
+ }
101151
+
101152
+ /* Chart.js canvas \u2014 dark mode tick/grid colours are set via plugin options */
101153
+
101154
+ .adm-overlay[data-theme="dark"] .adm-dow-chart {
101155
+ border-color: #2a2b3d;
101156
+ color: #64748b;
101157
+ }
101158
+
101159
+ .adm-overlay[data-theme="dark"] .adm-hod-chart {
101160
+ color: #64748b;
101161
+ }
101162
+
101163
+ .adm-overlay[data-theme="dark"] .adm-matrix {
101164
+ border-color: #2a2b3d;
101165
+ }
101166
+
101167
+ .adm-overlay[data-theme="dark"] .adm-matrix-header,
101168
+ .adm-overlay[data-theme="dark"] .adm-matrix-row {
101169
+ border-color: #2a2b3d;
101170
+ color: #64748b;
101171
+ }
101172
+
101173
+ .adm-overlay[data-theme="dark"] .adm-matrix-row:hover {
101174
+ background: #252636;
101175
+ }
101176
+
101177
+ .adm-overlay[data-theme="dark"] .adm-matrix-trigger {
101178
+ color: #94a3b8;
101179
+ }
101180
+
101181
+ .adm-overlay[data-theme="dark"] .adm-report-toolbar input[type="date"],
101182
+ .adm-overlay[data-theme="dark"] .adm-report-toolbar select {
101183
+ background: #252636;
101184
+ border-color: #2a2b3d;
101185
+ color: #e2e8f0;
101186
+ color-scheme: dark;
101187
+ }
101188
+
101189
+ .adm-overlay[data-theme="dark"] .adm-rpt-table {
101190
+ border-color: #2a2b3d;
101191
+ }
101192
+
101193
+ .adm-overlay[data-theme="dark"] .adm-rpt-th {
101194
+ background: #252636;
101195
+ color: #64748b;
101196
+ border-color: #2a2b3d;
101197
+ }
101198
+
101199
+ .adm-overlay[data-theme="dark"] .adm-rpt-cell {
101200
+ border-color: #2a2b3d;
101201
+ color: #cbd5e1;
101202
+ }
101203
+
101204
+ .adm-overlay[data-theme="dark"] .adm-rpt-row:nth-child(even) .adm-rpt-cell {
101205
+ background: #252636;
101206
+ }
101207
+
101208
+ .adm-overlay[data-theme="dark"] .adm-annot-card {
101209
+ background: #252636;
101210
+ border-color: #2a2b3d;
101211
+ }
101212
+
101213
+ .adm-overlay[data-theme="dark"] .adm-annot-card:hover {
101214
+ border-color: #4c1d95;
101215
+ }
101216
+
101217
+ .adm-overlay[data-theme="dark"] .adm-annot-card--archived {
101218
+ background: #1e1f2e;
101219
+ }
101220
+
101221
+ .adm-overlay[data-theme="dark"] .adm-annot-card-text {
101222
+ color: #cbd5e1;
101223
+ }
101224
+
101225
+ .adm-overlay[data-theme="dark"] .adm-annot-author {
101226
+ color: #e2e8f0;
101227
+ }
101228
+
101229
+ .adm-overlay[data-theme="dark"] .adm-annot-date {
101230
+ color: #64748b;
101231
+ }
101232
+
101233
+ .adm-overlay[data-theme="dark"] .adm-annot-btn {
101234
+ border-color: #2a2b3d;
101235
+ color: #94a3b8;
101236
+ }
101237
+
101238
+ .adm-overlay[data-theme="dark"] .adm-annot-btn:hover {
101239
+ background: #2a2b3d;
101240
+ }
101241
+
101242
+ .adm-overlay[data-theme="dark"] .adm-annot-archived-summary {
101243
+ color: #64748b;
101244
+ }
101245
+
101246
+ .adm-overlay[data-theme="dark"] .adm-annot-form {
101247
+ background: #252636;
101248
+ border-color: #2a2b3d;
101249
+ }
101250
+
101251
+ .adm-overlay[data-theme="dark"] .adm-annot-form textarea,
101252
+ .adm-overlay[data-theme="dark"] .adm-annot-form select {
101253
+ background: #1e1f2e;
101254
+ border-color: #2a2b3d;
101255
+ color: #e2e8f0;
101256
+ }
101257
+
101258
+ .adm-overlay[data-theme="dark"] .adm-annot-toolbar {
101259
+ border-color: #2a2b3d;
101260
+ }
101261
+
101262
+ .adm-overlay[data-theme="dark"] .adm-annot-empty {
101263
+ color: #64748b;
101264
+ }
101265
+
100932
101266
  /* =====================================================================
100933
101267
  Bulk Action Picker Modal (abm-*)
100934
101268
  ===================================================================== */
@@ -101221,8 +101555,39 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
101221
101555
  .atbl-th--sel { width: 28px; text-align: center; }
101222
101556
  .atbl-th--num { width: 48px; text-align: center; }
101223
101557
  .atbl-th--date { width: 90px; }
101558
+ .atbl-th--device { width: 130px; }
101224
101559
  .atbl-th--actions { width: 100px; }
101225
101560
 
101561
+ .atbl-th--sortable {
101562
+ cursor: pointer;
101563
+ gap: 4px;
101564
+ }
101565
+
101566
+ .atbl-th--sortable:hover {
101567
+ color: var(--alarms-text-primary);
101568
+ background: var(--alarms-card-hover);
101569
+ }
101570
+
101571
+ .atbl-th--sortable.is-sorted {
101572
+ color: var(--alarms-primary);
101573
+ background: var(--alarms-primary-light);
101574
+ }
101575
+
101576
+ .atbl-sort-icon {
101577
+ display: inline-block;
101578
+ font-size: 8px;
101579
+ opacity: 0.35;
101580
+ margin-left: 3px;
101581
+ vertical-align: middle;
101582
+ font-style: normal;
101583
+ line-height: 1;
101584
+ }
101585
+
101586
+ .atbl-sort-icon.is-active {
101587
+ opacity: 1;
101588
+ color: var(--alarms-primary);
101589
+ }
101590
+
101226
101591
  .atbl-row {
101227
101592
  border-bottom: 1px solid var(--alarms-border-light);
101228
101593
  cursor: pointer;
@@ -101250,6 +101615,14 @@ var ALARMS_NOTIFICATIONS_PANEL_STYLES = `
101250
101615
  font-weight: 600;
101251
101616
  }
101252
101617
 
101618
+ .atbl-cell--device {
101619
+ max-width: 130px;
101620
+ white-space: nowrap;
101621
+ overflow: hidden;
101622
+ text-overflow: ellipsis;
101623
+ font-size: 11px;
101624
+ }
101625
+
101253
101626
  .atbl-cell--customer {
101254
101627
  max-width: 120px;
101255
101628
  white-space: nowrap;
@@ -102689,21 +103062,20 @@ function buildTimeline(alarm) {
102689
103062
  const lastMs = new Date(alarm.lastOccurrence).getTime();
102690
103063
  const interval = count > 1 ? (lastMs - firstMs) / (count - 1) : 0;
102691
103064
  const deviceTokens = alarm.source ? alarm.source.split(/[,;]+/).map((s) => s.trim()).filter(Boolean) : [];
102692
- const deviceLabel = deviceTokens.length === 1 ? `<div class="adm-timeline-device">${escHtml(deviceTokens[0])}</div>` : "";
102693
- const valuePill = alarm.triggerValue != null ? `<span class="adm-timeline-value-pill">${escHtml(String(alarm.triggerValue))}</span>` : "";
103065
+ const deviceSpan = deviceTokens.length === 1 ? `<span class="adm-timeline-sep">|</span><span class="adm-timeline-device">${escHtml(deviceTokens[0])}</span>` : "";
103066
+ const valuePillHtml = alarm.triggerValue != null ? `<span class="adm-timeline-value-pill">${escHtml(String(alarm.triggerValue))}</span>` : "";
102694
103067
  function occItem(n, tsMs, meta, extraClass = "") {
103068
+ const isLatest = extraClass.includes("is-last") || extraClass.includes("is-single");
103069
+ const isEstimated = meta === "Estimado";
103070
+ const timeStr = (isEstimated ? "~" : "") + fmt(tsMs);
102695
103071
  return `
102696
103072
  <div class="adm-timeline-item ${extraClass}">
102697
103073
  <div class="adm-timeline-dot"></div>
102698
- <div class="adm-timeline-content">
102699
- <div class="adm-timeline-row">
102700
- <div class="adm-timeline-num">Ocorr\xEAncia #${n}</div>
102701
- ${valuePill}
102702
- </div>
102703
- <div class="adm-timeline-time">${fmt(tsMs)}</div>
102704
- ${meta ? `<div class="adm-timeline-meta">${meta}</div>` : ""}
102705
- ${deviceLabel}
102706
- </div>
103074
+ <span class="adm-timeline-num">#${n}</span>
103075
+ <span class="adm-timeline-sep">|</span>
103076
+ <span class="adm-timeline-time">${timeStr}</span>
103077
+ ${deviceSpan}
103078
+ ${isLatest ? valuePillHtml : ""}
102707
103079
  </div>`;
102708
103080
  }
102709
103081
  const MAX_SHOWN = 30;
@@ -102847,148 +103219,164 @@ var DEV_COLORS = [
102847
103219
  "#9333ea",
102848
103220
  "#65a30d"
102849
103221
  ];
102850
- function chartGrid(cW, cH, maxVal) {
102851
- return [0, 0.25, 0.5, 0.75, 1].map((pct) => {
102852
- const y = (cH * pct).toFixed(1);
102853
- const val = Math.round(maxVal * (1 - pct));
102854
- return `<line x1="0" y1="${y}" x2="${cW}" y2="${y}" stroke="#f3f4f6" stroke-width="1"/>
102855
- <text x="-5" y="${(cH * pct + 3).toFixed(1)}" text-anchor="end" font-size="9" fill="#9ca3af">${val}</text>`;
102856
- }).join("");
102857
- }
102858
- function chartXLabels(buckets, cW, cH) {
102859
- const step = buckets.length > 12 ? Math.ceil(buckets.length / 12) : 1;
102860
- return buckets.map((b, i) => {
102861
- if (i % step !== 0 && i !== buckets.length - 1) return "";
102862
- const x = ((i + 0.5) * cW / buckets.length).toFixed(1);
102863
- return `<text x="${x}" y="${(cH + 18).toFixed(1)}" text-anchor="middle" font-size="8" fill="#9ca3af">${b.label}</text>`;
102864
- }).join("");
103222
+ function getChartLib() {
103223
+ return window.Chart ?? null;
103224
+ }
103225
+ function createMainChart(canvas, buckets, devices, chartType, vizMode, singleColor) {
103226
+ const Chart2 = getChartLib();
103227
+ if (!Chart2) return null;
103228
+ const labels = buckets.map((b) => b.label);
103229
+ const isMulti = vizMode === "separate" && devices.length > 1;
103230
+ const datasets = isMulti ? devices.map((dev, di) => {
103231
+ const color = DEV_COLORS[di % DEV_COLORS.length];
103232
+ return {
103233
+ label: dev,
103234
+ data: buckets.map((b) => b.byDevice[dev] ?? 0),
103235
+ backgroundColor: chartType === "bar" ? color + "bb" : color + "22",
103236
+ borderColor: color,
103237
+ borderWidth: chartType === "line" ? 2 : 1,
103238
+ borderRadius: chartType === "bar" ? 3 : 0,
103239
+ tension: chartType === "line" ? 0.4 : 0,
103240
+ fill: chartType === "line",
103241
+ pointRadius: chartType === "line" ? 3 : 0,
103242
+ pointHoverRadius: 6
103243
+ };
103244
+ }) : [{
103245
+ label: "Ocorr\xEAncias",
103246
+ data: buckets.map((b) => b.total),
103247
+ backgroundColor: chartType === "bar" ? singleColor + "bb" : singleColor + "22",
103248
+ borderColor: singleColor,
103249
+ borderWidth: chartType === "line" ? 2.5 : 1,
103250
+ borderRadius: chartType === "bar" ? 3 : 0,
103251
+ tension: chartType === "line" ? 0.4 : 0,
103252
+ fill: chartType === "line",
103253
+ pointRadius: chartType === "line" ? 3 : 0,
103254
+ pointHoverRadius: 6
103255
+ }];
103256
+ return new Chart2(canvas, {
103257
+ type: chartType,
103258
+ data: { labels, datasets },
103259
+ options: {
103260
+ responsive: true,
103261
+ maintainAspectRatio: false,
103262
+ animation: { duration: 250 },
103263
+ plugins: {
103264
+ legend: {
103265
+ display: isMulti,
103266
+ position: "bottom",
103267
+ labels: { font: { size: 10 }, boxWidth: 10, padding: 10 }
103268
+ },
103269
+ tooltip: {
103270
+ callbacks: {
103271
+ title: (items) => `Per\xEDodo: ${items[0]?.label ?? ""}`,
103272
+ label: (item) => ` ${item.dataset.label}: ${item.parsed.y} ocorr\xEAncias`
103273
+ }
103274
+ }
103275
+ },
103276
+ scales: {
103277
+ x: {
103278
+ grid: { color: "#f3f4f6" },
103279
+ ticks: { font: { size: 9 }, color: "#9ca3af", maxRotation: 45 }
103280
+ },
103281
+ y: {
103282
+ beginAtZero: true,
103283
+ grid: { color: "#f3f4f6" },
103284
+ ticks: { font: { size: 9 }, color: "#9ca3af", precision: 0 }
103285
+ }
103286
+ }
103287
+ }
103288
+ });
102865
103289
  }
102866
- function chartLegend(devices) {
102867
- return `<div class="adm-chart-legend">${devices.map(
102868
- (dev, di) => `<span class="adm-chart-legend-item">
102869
- <span class="adm-chart-legend-dot" style="background:${DEV_COLORS[di % DEV_COLORS.length]}"></span>
102870
- ${escHtml(dev)}
102871
- </span>`
102872
- ).join("")}</div>`;
102873
- }
102874
- function buildBarChartHtml(buckets, devices, singleColor, vizMode) {
102875
- const W = 560;
102876
- const H = 160;
102877
- const PAD = { top: 14, right: 8, bottom: 30, left: 32 };
102878
- const cW = W - PAD.left - PAD.right;
102879
- const cH = H - PAD.top - PAD.bottom;
102880
- const multi = vizMode === "separate" && devices.length > 1;
102881
- const maxVal = Math.max(...buckets.map((b) => b.total), 1);
102882
- const barSlot = cW / buckets.length;
102883
- const barW = Math.max(3, Math.min(28, barSlot * 0.65));
102884
- const bars = buckets.map((b, i) => {
102885
- const groupCenterX = i * barSlot + barSlot / 2;
102886
- let rects;
102887
- let labelX;
102888
- if (multi) {
102889
- const subW = Math.max(2, Math.min(barSlot * 0.82 / devices.length, 18));
102890
- const gap = Math.min(1.5, subW * 0.18);
102891
- const groupW = devices.length * subW + (devices.length - 1) * gap;
102892
- const gx0 = groupCenterX - groupW / 2;
102893
- rects = devices.map((dev, di) => {
102894
- const v = b.byDevice[dev] ?? 0;
102895
- const bh = Math.max(0, v / maxVal * cH);
102896
- const bx = gx0 + di * (subW + gap);
102897
- return `<rect x="${bx.toFixed(1)}" y="${(cH - bh).toFixed(1)}" width="${subW.toFixed(1)}" height="${bh.toFixed(1)}" fill="${DEV_COLORS[di % DEV_COLORS.length]}" rx="1" opacity="0.88"/>`;
102898
- }).join("");
102899
- labelX = groupCenterX;
102900
- } else {
102901
- const bh = Math.max(0, b.total / maxVal * cH);
102902
- const bx = groupCenterX - barW / 2;
102903
- rects = `<rect x="${bx.toFixed(1)}" y="${(cH - bh).toFixed(1)}" width="${barW.toFixed(1)}" height="${bh.toFixed(1)}" fill="${singleColor}" rx="1.5" opacity="0.85"/>`;
102904
- labelX = groupCenterX;
103290
+ function createDowChart(canvas, vals) {
103291
+ const Chart2 = getChartLib();
103292
+ if (!Chart2) return null;
103293
+ return new Chart2(canvas, {
103294
+ type: "bar",
103295
+ data: {
103296
+ labels: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "S\xE1b"],
103297
+ datasets: [{
103298
+ label: "Ocorr\xEAncias",
103299
+ data: vals,
103300
+ backgroundColor: "#8b5cf6bb",
103301
+ borderColor: "#7c3aed",
103302
+ borderWidth: 1,
103303
+ borderRadius: 3
103304
+ }]
103305
+ },
103306
+ options: {
103307
+ indexAxis: "y",
103308
+ responsive: true,
103309
+ maintainAspectRatio: false,
103310
+ animation: { duration: 250 },
103311
+ plugins: {
103312
+ legend: { display: false },
103313
+ tooltip: {
103314
+ callbacks: {
103315
+ label: (item) => ` ${item.parsed.x} ocorr\xEAncias`
103316
+ }
103317
+ }
103318
+ },
103319
+ scales: {
103320
+ x: {
103321
+ beginAtZero: true,
103322
+ grid: { color: "#f3f4f6" },
103323
+ ticks: { font: { size: 9 }, color: "#9ca3af", precision: 0 }
103324
+ },
103325
+ y: {
103326
+ grid: { display: false },
103327
+ ticks: { font: { size: 10 }, color: "#374151" }
103328
+ }
103329
+ }
102905
103330
  }
102906
- return rects + `<text x="${labelX.toFixed(1)}" y="${(cH + 18).toFixed(1)}" text-anchor="middle" font-size="8" fill="#9ca3af">${b.label}</text>`;
102907
- }).join("");
102908
- const trendPts = buckets.map((b, i) => `${((i + 0.5) * barSlot).toFixed(1)},${(cH - b.total / maxVal * cH).toFixed(1)}`).join(" ");
102909
- const svg = `<svg viewBox="0 0 ${W} ${H}" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;display:block">
102910
- <g transform="translate(${PAD.left},${PAD.top})">
102911
- ${chartGrid(cW, cH, maxVal)}
102912
- ${bars}
102913
- <polyline points="${trendPts}" fill="none" stroke="#1e293b" stroke-width="1.5" stroke-linejoin="round" opacity="0.35"/>
102914
- </g>
102915
- </svg>`;
102916
- return svg + (multi ? chartLegend(devices) : "");
102917
- }
102918
- function buildLineChartHtml(buckets, devices, singleColor, vizMode) {
102919
- const W = 560;
102920
- const H = 160;
102921
- const PAD = { top: 14, right: 8, bottom: 30, left: 32 };
102922
- const cW = W - PAD.left - PAD.right;
102923
- const cH = H - PAD.top - PAD.bottom;
102924
- const multi = vizMode === "separate" && devices.length > 1;
102925
- const maxVal = Math.max(...buckets.map((b) => b.total), 1);
102926
- const xOf = (i) => ((i + 0.5) * cW / buckets.length).toFixed(1);
102927
- const yOf = (v) => (cH - v / maxVal * cH).toFixed(1);
102928
- let paths = "";
102929
- if (multi) {
102930
- for (let di = 0; di < devices.length; di++) {
102931
- const color = DEV_COLORS[di % DEV_COLORS.length];
102932
- const pts = buckets.map((b, i) => `${xOf(i)},${yOf(b.byDevice[devices[di]] ?? 0)}`);
102933
- const areaD = `M ${xOf(0)},${cH} L ${pts.join(" L ")} L ${xOf(buckets.length - 1)},${cH} Z`;
102934
- paths += `<path d="${areaD}" fill="${color}" opacity="0.1"/>`;
102935
- paths += `<polyline points="${pts.join(" ")}" fill="none" stroke="${color}" stroke-width="2" stroke-linejoin="round" stroke-linecap="round"/>`;
102936
- buckets.forEach((b, i) => {
102937
- paths += `<circle cx="${xOf(i)}" cy="${yOf(b.byDevice[devices[di]] ?? 0)}" r="3" fill="${color}" stroke="#fff" stroke-width="1.5"/>`;
102938
- });
103331
+ });
103332
+ }
103333
+ function createHodChart(canvas, vals) {
103334
+ const Chart2 = getChartLib();
103335
+ if (!Chart2) return null;
103336
+ return new Chart2(canvas, {
103337
+ type: "bar",
103338
+ data: {
103339
+ labels: Array.from({ length: 24 }, (_, h) => `${h}h`),
103340
+ datasets: [{
103341
+ label: "Ocorr\xEAncias",
103342
+ data: vals,
103343
+ backgroundColor: "#3b82f6bb",
103344
+ borderColor: "#2563eb",
103345
+ borderWidth: 1,
103346
+ borderRadius: 2
103347
+ }]
103348
+ },
103349
+ options: {
103350
+ responsive: true,
103351
+ maintainAspectRatio: false,
103352
+ animation: { duration: 250 },
103353
+ plugins: {
103354
+ legend: { display: false },
103355
+ tooltip: {
103356
+ callbacks: {
103357
+ title: (items) => `${items[0]?.label}`,
103358
+ label: (item) => ` ${item.parsed.y} ocorr\xEAncias`
103359
+ }
103360
+ }
103361
+ },
103362
+ scales: {
103363
+ x: {
103364
+ grid: { display: false },
103365
+ ticks: { font: { size: 9 }, color: "#9ca3af" }
103366
+ },
103367
+ y: {
103368
+ beginAtZero: true,
103369
+ grid: { color: "#f3f4f6" },
103370
+ ticks: { font: { size: 9 }, color: "#9ca3af", precision: 0 }
103371
+ }
103372
+ }
102939
103373
  }
102940
- } else {
102941
- const pts = buckets.map((b, i) => `${xOf(i)},${yOf(b.total)}`);
102942
- const areaD = `M ${xOf(0)},${cH} L ${pts.join(" L ")} L ${xOf(buckets.length - 1)},${cH} Z`;
102943
- paths += `<path d="${areaD}" fill="${singleColor}" opacity="0.12"/>`;
102944
- paths += `<polyline points="${pts.join(" ")}" fill="none" stroke="${singleColor}" stroke-width="2.5" stroke-linejoin="round" stroke-linecap="round"/>`;
102945
- buckets.forEach((b, i) => {
102946
- paths += `<circle cx="${xOf(i)}" cy="${yOf(b.total)}" r="3.5" fill="${singleColor}" stroke="#fff" stroke-width="1.5"/>`;
102947
- });
102948
- }
102949
- const svg = `<svg viewBox="0 0 ${W} ${H}" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;display:block">
102950
- <g transform="translate(${PAD.left},${PAD.top})">
102951
- ${chartGrid(cW, cH, maxVal)}
102952
- ${chartXLabels(buckets, cW, cH)}
102953
- ${paths}
102954
- </g>
102955
- </svg>`;
102956
- return svg + (multi ? chartLegend(devices) : "");
102957
- }
102958
- function buildDowChart(vals) {
102959
- const LABELS = ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "S\xE1b"];
102960
- const maxV = Math.max(...vals, 1);
102961
- const barMaxW = 200;
102962
- const rows = LABELS.map((lbl, i) => {
102963
- const pct = vals[i] / maxV;
102964
- const w = Math.round(pct * barMaxW);
102965
- const opacity = 0.3 + pct * 0.7;
102966
- return `<div class="adm-dow-row">
102967
- <span class="adm-dow-label">${lbl}</span>
102968
- <div class="adm-dow-track">
102969
- <div class="adm-dow-bar" style="width:${w}px;opacity:${opacity.toFixed(2)}"></div>
102970
- </div>
102971
- <span class="adm-dow-val">${vals[i]}</span>
102972
- </div>`;
102973
- }).join("");
102974
- return `<div class="adm-dow-chart">${rows}</div>`;
102975
- }
102976
- function buildHodChart(vals) {
102977
- const maxV = Math.max(...vals, 1);
102978
- const cells = vals.map((v, h) => {
102979
- const pct = v / maxV;
102980
- const opacity = 0.08 + pct * 0.92;
102981
- return `<div class="adm-hod-cell" title="${h}:00 \u2014 ${v} ocorr\xEAncias" style="opacity:${opacity.toFixed(2)}">
102982
- <div class="adm-hod-bar" style="height:${Math.max(4, Math.round(pct * 40))}px"></div>
102983
- <span class="adm-hod-label">${h}</span>
102984
- </div>`;
102985
- }).join("");
102986
- return `<div class="adm-hod-chart">${cells}</div>`;
103374
+ });
102987
103375
  }
102988
103376
  function parseDevices(source) {
102989
103377
  return source.split(/[,;]+/).map((s) => s.trim()).filter(Boolean);
102990
103378
  }
102991
- function openAlarmDetailsModal(alarm) {
103379
+ function openAlarmDetailsModal(alarm, themeMode = "light") {
102992
103380
  const sev = SEVERITY_CONFIG[alarm.severity];
102993
103381
  const st = STATE_CONFIG[alarm.state];
102994
103382
  const count = alarm.occurrenceCount || 1;
@@ -103014,20 +103402,14 @@ function openAlarmDetailsModal(alarm) {
103014
103402
  </div>`
103015
103403
  ).join("");
103016
103404
  const initialPeriod = durMs < 864e5 ? "hora" : durMs < 7 * 864e5 ? "dia" : durMs < 30 * 864e5 ? "semana" : "mes";
103017
- const buckets = generateBuckets(alarm, devices, initialPeriod);
103018
103405
  const dowVals = generateDowDistribution(alarm);
103019
103406
  const hodVals = generateHodDistribution(alarm);
103020
103407
  const chartSingleColor = sev.text || "#7c3aed";
103021
- const barTotalHtml = buildBarChartHtml(buckets, devices, chartSingleColor, "total");
103022
- const barSepHtml = devices.length > 1 ? buildBarChartHtml(buckets, devices, chartSingleColor, "separate") : barTotalHtml;
103023
- const lineTotalHtml = buildLineChartHtml(buckets, devices, chartSingleColor, "total");
103024
- const lineSepHtml = devices.length > 1 ? buildLineChartHtml(buckets, devices, chartSingleColor, "separate") : lineTotalHtml;
103025
- const dowChartHtml = buildDowChart(dowVals);
103026
- const hodChartHtml = buildHodChart(hodVals);
103027
103408
  const annotCount = getActiveAnnotationCount(alarm.id);
103028
103409
  const sourceDisplay = deviceCount > 1 ? `${deviceCount} dispositivos` : escHtml(alarm.source);
103029
103410
  const overlay = document.createElement("div");
103030
103411
  overlay.className = "adm-overlay";
103412
+ overlay.setAttribute("data-theme", themeMode);
103031
103413
  overlay.innerHTML = `
103032
103414
  <div class="adm-drawer" role="dialog" aria-modal="true" aria-label="Detalhes do alarme">
103033
103415
 
@@ -103035,11 +103417,23 @@ function openAlarmDetailsModal(alarm) {
103035
103417
  <div class="adm-header">
103036
103418
  <div class="adm-header-top">
103037
103419
  <div class="adm-title" title="${escHtml(alarm.title)}">${escHtml(alarm.title)}</div>
103038
- <button class="adm-close" id="admClose" title="Fechar (Esc)">
103039
- <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5">
103040
- <path d="M18 6L6 18M6 6l12 12"/>
103041
- </svg>
103042
- </button>
103420
+ <div class="adm-header-actions">
103421
+ <button class="adm-header-btn" id="admMaximize" title="Maximizar">
103422
+ <svg class="adm-icon-expand" viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
103423
+ <polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/>
103424
+ <line x1="21" y1="3" x2="14" y2="10"/><line x1="3" y1="21" x2="10" y2="14"/>
103425
+ </svg>
103426
+ <svg class="adm-icon-compress" viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display:none">
103427
+ <polyline points="4 14 10 14 10 20"/><polyline points="20 10 14 10 14 4"/>
103428
+ <line x1="10" y1="14" x2="3" y2="21"/><line x1="21" y1="3" x2="14" y2="10"/>
103429
+ </svg>
103430
+ </button>
103431
+ <button class="adm-header-btn adm-close" id="admClose" title="Fechar (Esc)">
103432
+ <svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.5">
103433
+ <path d="M18 6L6 18M6 6l12 12"/>
103434
+ </svg>
103435
+ </button>
103436
+ </div>
103043
103437
  </div>
103044
103438
  <div class="adm-badges">
103045
103439
  <span class="adm-badge-severity" style="background:${sev.bg};color:${sev.text}">${sev.icon} ${sev.label}</span>
@@ -103151,7 +103545,7 @@ function openAlarmDetailsModal(alarm) {
103151
103545
  <div class="adm-section-title">Mapa ocorr\xEAncias \xD7 dispositivos</div>
103152
103546
  <div class="adm-matrix">
103153
103547
  <div class="adm-matrix-header">
103154
- <span>ID</span><span>Timestamp</span><span>Dispositivos</span>
103548
+ <span>ID</span><span>Timestamp</span><span>Dispositivos</span><span>Trigger</span>
103155
103549
  </div>
103156
103550
  ${buildOccurrenceMatrix(alarm, devices)}
103157
103551
  </div>
@@ -103218,21 +103612,18 @@ function openAlarmDetailsModal(alarm) {
103218
103612
  <div class="adm-section">
103219
103613
  <div class="adm-section-title">Tend\xEAncia de ocorr\xEAncias no per\xEDodo</div>
103220
103614
  <div class="adm-chart-wrapper">
103221
- <div class="adm-chart-variant is-active" data-chart-type="bar" data-viz-mode="total">${barTotalHtml}</div>
103222
- <div class="adm-chart-variant" data-chart-type="bar" data-viz-mode="separate">${barSepHtml}</div>
103223
- <div class="adm-chart-variant" data-chart-type="line" data-viz-mode="total">${lineTotalHtml}</div>
103224
- <div class="adm-chart-variant" data-chart-type="line" data-viz-mode="separate">${lineSepHtml}</div>
103615
+ <canvas id="admMainChart" class="adm-chart-canvas"></canvas>
103225
103616
  </div>
103226
103617
  </div>
103227
103618
 
103228
103619
  <div class="adm-chart-secondary-grid">
103229
103620
  <div class="adm-section">
103230
103621
  <div class="adm-section-title">Por dia da semana</div>
103231
- ${dowChartHtml}
103622
+ <div class="adm-chart-canvas-wrap adm-chart-canvas-wrap--dow"><canvas id="admDowChart"></canvas></div>
103232
103623
  </div>
103233
103624
  <div class="adm-section">
103234
103625
  <div class="adm-section-title">Por hora do dia</div>
103235
- ${hodChartHtml}
103626
+ <div class="adm-chart-canvas-wrap adm-chart-canvas-wrap--hod"><canvas id="admHodChart"></canvas></div>
103236
103627
  </div>
103237
103628
  </div>
103238
103629
  </div>
@@ -103256,58 +103647,58 @@ function openAlarmDetailsModal(alarm) {
103256
103647
  overlay.querySelector(`.adm-panel[data-panel="${panel}"]`)?.classList.add("is-active");
103257
103648
  });
103258
103649
  });
103650
+ const chartInstances = [];
103259
103651
  const graficoPanel = overlay.querySelector('.adm-panel[data-panel="grafico"]');
103260
103652
  if (graficoPanel) {
103261
103653
  let activeChartType = "bar";
103262
103654
  let activeVizMode = "total";
103263
103655
  let activePeriod = initialPeriod;
103264
- const updateChartVariant = () => {
103265
- graficoPanel.querySelectorAll(".adm-chart-variant").forEach((el) => {
103266
- const show = el.dataset.chartType === activeChartType && el.dataset.vizMode === activeVizMode;
103267
- el.classList.toggle("is-active", show);
103268
- });
103656
+ const buildMain = () => {
103657
+ const nb = generateBuckets(alarm, devices, activePeriod);
103658
+ const canvas = graficoPanel.querySelector("#admMainChart");
103659
+ if (!canvas) return;
103660
+ if (chartInstances[0]) {
103661
+ chartInstances[0].destroy();
103662
+ chartInstances[0] = null;
103663
+ }
103664
+ chartInstances[0] = createMainChart(canvas, nb, devices, activeChartType, activeVizMode, chartSingleColor);
103269
103665
  };
103270
- const rebuildCharts = (period) => {
103271
- const nb = generateBuckets(alarm, devices, period);
103272
- const bT = buildBarChartHtml(nb, devices, chartSingleColor, "total");
103273
- const bS = devices.length > 1 ? buildBarChartHtml(nb, devices, chartSingleColor, "separate") : bT;
103274
- const lT = buildLineChartHtml(nb, devices, chartSingleColor, "total");
103275
- const lS = devices.length > 1 ? buildLineChartHtml(nb, devices, chartSingleColor, "separate") : lT;
103276
- const map = {
103277
- "bar-total": bT,
103278
- "bar-separate": bS,
103279
- "line-total": lT,
103280
- "line-separate": lS
103281
- };
103282
- graficoPanel.querySelectorAll(".adm-chart-variant").forEach((el) => {
103283
- el.innerHTML = map[`${el.dataset.chartType}-${el.dataset.vizMode}`] ?? "";
103284
- });
103666
+ const initSecondary = () => {
103667
+ const dowCanvas = graficoPanel.querySelector("#admDowChart");
103668
+ if (dowCanvas && !chartInstances[1]) chartInstances[1] = createDowChart(dowCanvas, dowVals);
103669
+ const hodCanvas = graficoPanel.querySelector("#admHodChart");
103670
+ if (hodCanvas && !chartInstances[2]) chartInstances[2] = createHodChart(hodCanvas, hodVals);
103285
103671
  };
103286
- graficoPanel.querySelectorAll("[data-chart-type]").forEach((btn) => {
103672
+ graficoPanel.querySelectorAll("button[data-chart-type]").forEach((btn) => {
103287
103673
  btn.addEventListener("click", () => {
103288
103674
  activeChartType = btn.dataset.chartType;
103289
- graficoPanel.querySelectorAll("[data-chart-type]").forEach((b) => b.classList.remove("is-active"));
103675
+ graficoPanel.querySelectorAll("button[data-chart-type]").forEach((b) => b.classList.remove("is-active"));
103290
103676
  btn.classList.add("is-active");
103291
- updateChartVariant();
103677
+ buildMain();
103292
103678
  });
103293
103679
  });
103294
- graficoPanel.querySelectorAll("[data-viz-mode]").forEach((btn) => {
103680
+ graficoPanel.querySelectorAll("button[data-viz-mode]").forEach((btn) => {
103295
103681
  btn.addEventListener("click", () => {
103296
103682
  activeVizMode = btn.dataset.vizMode;
103297
- graficoPanel.querySelectorAll("[data-viz-mode]").forEach((b) => b.classList.remove("is-active"));
103683
+ graficoPanel.querySelectorAll("button[data-viz-mode]").forEach((b) => b.classList.remove("is-active"));
103298
103684
  btn.classList.add("is-active");
103299
- updateChartVariant();
103685
+ buildMain();
103300
103686
  });
103301
103687
  });
103302
- graficoPanel.querySelectorAll("[data-period]").forEach((btn) => {
103688
+ graficoPanel.querySelectorAll("button[data-period]").forEach((btn) => {
103303
103689
  btn.addEventListener("click", () => {
103304
103690
  activePeriod = btn.dataset.period;
103305
- graficoPanel.querySelectorAll("[data-period]").forEach((b) => b.classList.remove("is-active"));
103691
+ graficoPanel.querySelectorAll("button[data-period]").forEach((b) => b.classList.remove("is-active"));
103306
103692
  btn.classList.add("is-active");
103307
- rebuildCharts(activePeriod);
103308
- updateChartVariant();
103693
+ buildMain();
103309
103694
  });
103310
103695
  });
103696
+ overlay.querySelectorAll('.adm-tab[data-panel="grafico"]').forEach((tabBtn) => {
103697
+ tabBtn.addEventListener("click", () => setTimeout(() => {
103698
+ buildMain();
103699
+ initSecondary();
103700
+ }, 30));
103701
+ });
103311
103702
  }
103312
103703
  const rptPanel = overlay.querySelector('.adm-panel[data-panel="relatorio"]');
103313
103704
  if (rptPanel) {
@@ -103331,14 +103722,17 @@ function openAlarmDetailsModal(alarm) {
103331
103722
  const rptAlarm = { ...alarm, firstOccurrence: new Date(startMs).toISOString(), lastOccurrence: new Date(endMs).toISOString() };
103332
103723
  const nb = generateBuckets(rptAlarm, devices, rptPeriod);
103333
103724
  const totalOcc = nb.reduce((s, b) => s + b.total, 0);
103334
- const csvRows = [["Per\xEDodo", "Ocorr\xEAncias", "Dispositivos"]];
103335
- const tableRows = nb.map((b) => {
103725
+ const csvRows = [["Per\xEDodo", "Ocorr\xEAncias", "Dispositivos", "Trigger"]];
103726
+ const tableRows = nb.map((b, bIdx) => {
103336
103727
  const devList = devices.length === 1 ? devices[0] : devices.filter((d) => (b.byDevice[d] ?? 0) > 0).join(", ") || "-";
103337
- csvRows.push([b.label, String(b.total), devList]);
103728
+ const isLastBucket = bIdx === nb.length - 1;
103729
+ const trigStr = isLastBucket && alarm.triggerValue != null ? String(alarm.triggerValue) : "\u2014";
103730
+ csvRows.push([b.label, String(b.total), devList, trigStr]);
103338
103731
  return `<tr>
103339
103732
  <td class="adm-rpt-cell">${b.label}</td>
103340
103733
  <td class="adm-rpt-cell adm-rpt-cell--num">${b.total}</td>
103341
103734
  <td class="adm-rpt-cell adm-rpt-cell--dev">${escHtml(devList)}</td>
103735
+ <td class="adm-rpt-cell adm-rpt-cell--num">${escHtml(trigStr)}</td>
103342
103736
  </tr>`;
103343
103737
  }).join("");
103344
103738
  if (!grid) return;
@@ -103350,6 +103744,7 @@ function openAlarmDetailsModal(alarm) {
103350
103744
  <th class="adm-rpt-th">Per\xEDodo</th>
103351
103745
  <th class="adm-rpt-th adm-rpt-th--num">Ocorr\xEAncias</th>
103352
103746
  <th class="adm-rpt-th">Dispositivos</th>
103747
+ <th class="adm-rpt-th adm-rpt-th--num">Trigger</th>
103353
103748
  </tr>
103354
103749
  </thead>
103355
103750
  <tbody>${tableRows}</tbody>
@@ -103358,6 +103753,7 @@ function openAlarmDetailsModal(alarm) {
103358
103753
  <td class="adm-rpt-cell adm-rpt-cell--total">Total</td>
103359
103754
  <td class="adm-rpt-cell adm-rpt-cell--num adm-rpt-cell--total">${totalOcc}</td>
103360
103755
  <td class="adm-rpt-cell adm-rpt-cell--total">${deviceCount} dispositivo${deviceCount !== 1 ? "s" : ""}</td>
103756
+ <td class="adm-rpt-cell adm-rpt-cell--total">\u2014</td>
103361
103757
  </tr>
103362
103758
  </tfoot>
103363
103759
  </table>
@@ -103428,9 +103824,27 @@ function openAlarmDetailsModal(alarm) {
103428
103824
  }
103429
103825
  });
103430
103826
  }
103827
+ const drawer = overlay.querySelector(".adm-drawer");
103828
+ const maximizeBtn = overlay.querySelector("#admMaximize");
103829
+ const iconExpand = maximizeBtn?.querySelector(".adm-icon-expand");
103830
+ const iconCompress = maximizeBtn?.querySelector(".adm-icon-compress");
103831
+ maximizeBtn?.addEventListener("click", () => {
103832
+ const isMax = drawer?.classList.toggle("adm-drawer--maximized");
103833
+ if (iconExpand) iconExpand.style.display = isMax ? "none" : "";
103834
+ if (iconCompress) iconCompress.style.display = isMax ? "" : "none";
103835
+ maximizeBtn.title = isMax ? "Restaurar (Esc)" : "Maximizar";
103836
+ });
103431
103837
  const closeModal2 = () => {
103432
103838
  overlay.classList.remove("adm-overlay--visible");
103433
- setTimeout(() => overlay.remove(), 300);
103839
+ setTimeout(() => {
103840
+ overlay.remove();
103841
+ chartInstances.forEach((c) => {
103842
+ try {
103843
+ c?.destroy();
103844
+ } catch (_) {
103845
+ }
103846
+ });
103847
+ }, 300);
103434
103848
  document.removeEventListener("keydown", onKey);
103435
103849
  };
103436
103850
  const onKey = (e) => {
@@ -103465,10 +103879,12 @@ function buildOccurrenceMatrix(alarm, devices) {
103465
103879
  const devIndex = (item.n - 1) % devices.length;
103466
103880
  const rowDevices = devices.length === 1 ? [devices[0]] : [devices[devIndex]];
103467
103881
  const chipsHtml = rowDevices.map(deviceChip).join("");
103882
+ const trigVal = item.n === count && alarm.triggerValue != null ? escHtml(String(alarm.triggerValue)) : "\u2014";
103468
103883
  return `<div class="adm-matrix-row">
103469
103884
  <span class="adm-matrix-n">#${item.n}</span>
103470
103885
  <span class="adm-matrix-ts">${fmt(item.tsMs)}${item.estimated ? '<sup title="Estimado">~</sup>' : ""}</span>
103471
103886
  <span class="adm-matrix-devices">${chipsHtml}</span>
103887
+ <span class="adm-matrix-trigger">${trigVal}</span>
103472
103888
  </div>`;
103473
103889
  }).join("");
103474
103890
  }
@@ -103487,6 +103903,9 @@ var AlarmsNotificationsPanelView = class {
103487
103903
  viewMode = "card";
103488
103904
  // Group mode: 'consolidado' (default) groups same-title alarms; 'separado' one row per device
103489
103905
  groupMode = "consolidado";
103906
+ // Table sort state
103907
+ sortCol = "";
103908
+ sortDir = "asc";
103490
103909
  constructor(params, controller) {
103491
103910
  this.params = params;
103492
103911
  this.controller = controller;
@@ -103686,6 +104105,18 @@ var AlarmsNotificationsPanelView = class {
103686
104105
  const state6 = this.controller.getState();
103687
104106
  this.renderListContent(state6);
103688
104107
  });
104108
+ this.root.addEventListener("click", (e) => {
104109
+ const th = e.target.closest("[data-sort-col]");
104110
+ if (!th) return;
104111
+ const col = th.getAttribute("data-sort-col");
104112
+ if (this.sortCol === col) {
104113
+ this.sortDir = this.sortDir === "asc" ? "desc" : "asc";
104114
+ } else {
104115
+ this.sortCol = col;
104116
+ this.sortDir = "asc";
104117
+ }
104118
+ this.renderListContent(this.controller.getState());
104119
+ });
103689
104120
  this.root.addEventListener("click", (e) => {
103690
104121
  if (e.target.closest("#exportBtn")) this.openExportModal();
103691
104122
  });
@@ -103808,12 +104239,16 @@ var AlarmsNotificationsPanelView = class {
103808
104239
  }
103809
104240
  if (emptyState) emptyState.style.display = "none";
103810
104241
  this.groupedAlarms = this.groupMode === "consolidado" ? this.groupAlarmsByTitle(state6.filteredAlarms) : this.explodeAlarmsByDevice(state6.filteredAlarms);
104242
+ const isSeparado = this.groupMode === "separado";
103811
104243
  if (this.viewMode === "list") {
103812
104244
  grid.className = "alarms-table-container";
103813
- grid.innerHTML = this.renderAlarmsTable(this.groupedAlarms);
104245
+ grid.innerHTML = this.renderAlarmsTable(this.groupedAlarms, isSeparado);
103814
104246
  } else {
104247
+ const cards = isSeparado ? [...this.groupedAlarms].sort(
104248
+ (a, b) => (a.source || "").localeCompare(b.source || "", "pt-BR", { sensitivity: "base" })
104249
+ ) : this.groupedAlarms;
103815
104250
  grid.className = "alarms-grid";
103816
- this.groupedAlarms.forEach((alarm) => {
104251
+ cards.forEach((alarm) => {
103817
104252
  const card = createAlarmCardElement(alarm, {
103818
104253
  onCardClick: (a) => this.handleAlarmClick(a),
103819
104254
  onAcknowledge: (id) => this.openAlarmActionModal("acknowledge", id),
@@ -103822,7 +104257,7 @@ var AlarmsNotificationsPanelView = class {
103822
104257
  themeMode: state6.themeMode,
103823
104258
  showCustomerName: this.params.showCustomerName ?? true,
103824
104259
  selected: this.selectedTitles.has(alarm.title),
103825
- showDeviceBadge: this.groupMode === "separado"
104260
+ showDeviceBadge: isSeparado
103826
104261
  });
103827
104262
  grid.appendChild(card);
103828
104263
  });
@@ -103985,7 +104420,7 @@ var AlarmsNotificationsPanelView = class {
103985
104420
  // =====================================================================
103986
104421
  // Table View
103987
104422
  // =====================================================================
103988
- renderAlarmsTable(alarms) {
104423
+ renderAlarmsTable(alarms, showDevice = false) {
103989
104424
  const showCustomer = this.params.showCustomerName ?? true;
103990
104425
  const fmtDt3 = (iso) => {
103991
104426
  if (!iso) return "-";
@@ -103993,6 +104428,38 @@ var AlarmsNotificationsPanelView = class {
103993
104428
  if (isNaN(d.getTime())) return "-";
103994
104429
  return String(d.getDate()).padStart(2, "0") + "/" + String(d.getMonth() + 1).padStart(2, "0") + " " + String(d.getHours()).padStart(2, "0") + ":" + String(d.getMinutes()).padStart(2, "0");
103995
104430
  };
104431
+ const SEVERITY_ORDER3 = { CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3, INFO: 4 };
104432
+ const STATE_ORDER2 = { OPEN: 0, ESCALATED: 1, ACK: 2, SNOOZED: 3, CLOSED: 4 };
104433
+ if (this.sortCol) {
104434
+ const dir = this.sortDir === "asc" ? 1 : -1;
104435
+ alarms = [...alarms].sort((a, b) => {
104436
+ switch (this.sortCol) {
104437
+ case "title":
104438
+ return dir * (a.title || "").localeCompare(b.title || "", "pt-BR", { sensitivity: "base" });
104439
+ case "device":
104440
+ return dir * (a.source || "").localeCompare(b.source || "", "pt-BR", { sensitivity: "base" });
104441
+ case "severity":
104442
+ return dir * ((SEVERITY_ORDER3[a.severity] ?? 9) - (SEVERITY_ORDER3[b.severity] ?? 9));
104443
+ case "state":
104444
+ return dir * ((STATE_ORDER2[a.state] ?? 9) - (STATE_ORDER2[b.state] ?? 9));
104445
+ case "customer":
104446
+ return dir * (a.customerName || "").localeCompare(b.customerName || "", "pt-BR", { sensitivity: "base" });
104447
+ case "count":
104448
+ return dir * ((a.occurrenceCount || 1) - (b.occurrenceCount || 1));
104449
+ case "first":
104450
+ return dir * (new Date(a.firstOccurrence ?? 0).getTime() - new Date(b.firstOccurrence ?? 0).getTime());
104451
+ case "last":
104452
+ return dir * (new Date(a.lastOccurrence ?? 0).getTime() - new Date(b.lastOccurrence ?? 0).getTime());
104453
+ default:
104454
+ return 0;
104455
+ }
104456
+ });
104457
+ }
104458
+ const th = (label, col, extraClass = "") => {
104459
+ const active = this.sortCol === col;
104460
+ const icon = active ? `<span class="atbl-sort-icon is-active">${this.sortDir === "asc" ? "\u25B2" : "\u25BC"}</span>` : `<span class="atbl-sort-icon">\u21C5</span>`;
104461
+ return `<th class="atbl-th atbl-th--sortable${extraClass ? " " + extraClass : ""}${active ? " is-sorted" : ""}" data-sort-col="${col}">${label}${icon}</th>`;
104462
+ };
103996
104463
  const rows = alarms.map((alarm) => {
103997
104464
  const sev = SEVERITY_CONFIG[alarm.severity];
103998
104465
  const st = STATE_CONFIG[alarm.state];
@@ -104013,6 +104480,7 @@ var AlarmsNotificationsPanelView = class {
104013
104480
  <input type="checkbox" class="alarm-card-select" data-alarm-id="${alarm.id}" data-alarm-title="${escTitle}"${sel ? " checked" : ""}>
104014
104481
  </td>
104015
104482
  <td class="atbl-cell atbl-cell--title" title="${escTitle}">${escTitle}</td>
104483
+ ${showDevice ? `<td class="atbl-cell atbl-cell--device" title="${escSource}">${escSource}</td>` : ""}
104016
104484
  <td class="atbl-cell atbl-cell--sev">
104017
104485
  <span class="atbl-sev-badge" style="background:${sev.bg};color:${sev.text}">${sev.icon} ${sev.label}</span>
104018
104486
  </td>
@@ -104032,13 +104500,14 @@ var AlarmsNotificationsPanelView = class {
104032
104500
  <thead>
104033
104501
  <tr class="atbl-head-row">
104034
104502
  <th class="atbl-th atbl-th--sel"><input type="checkbox" id="tblSelectAll"${allSelected ? " checked" : ""}></th>
104035
- <th class="atbl-th">Tipo</th>
104036
- <th class="atbl-th">Severidade</th>
104037
- <th class="atbl-th">Estado</th>
104038
- ${showCustomer ? '<th class="atbl-th">Shopping</th>' : ""}
104039
- <th class="atbl-th atbl-th--num">Qte.</th>
104040
- <th class="atbl-th atbl-th--date">1a Ocorr\xEAncia</th>
104041
- <th class="atbl-th atbl-th--date">\xDAlt. Ocorr\xEAncia</th>
104503
+ ${th("Tipo", "title")}
104504
+ ${showDevice ? th("Dispositivo", "device", "atbl-th--device") : ""}
104505
+ ${th("Severidade", "severity")}
104506
+ ${th("Estado", "state")}
104507
+ ${showCustomer ? th("Shopping", "customer") : ""}
104508
+ ${th("Qte.", "count", "atbl-th--num")}
104509
+ ${th("1a Ocorr\xEAncia", "first", "atbl-th--date")}
104510
+ ${th("\xDAlt. Ocorr\xEAncia", "last", "atbl-th--date")}
104042
104511
  <th class="atbl-th atbl-th--actions">A\xE7\xF5es</th>
104043
104512
  </tr>
104044
104513
  </thead>
@@ -104488,7 +104957,7 @@ var AlarmsNotificationsPanelView = class {
104488
104957
  this.log("Details", alarmId);
104489
104958
  const alarm = this.groupedAlarms.find((a) => a.id === alarmId) ?? this.controller.getAlarms().find((a) => a.id === this.stripSeparadoId(alarmId));
104490
104959
  if (alarm) {
104491
- openAlarmDetailsModal(alarm);
104960
+ openAlarmDetailsModal(alarm, this.controller.getState().themeMode);
104492
104961
  this.emit("alarm-click", alarm);
104493
104962
  }
104494
104963
  }