mdas-jsview-sdk 1.0.12-uat.0 → 1.0.14-uat.0

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.
@@ -1189,6 +1189,76 @@ const CombinedMarketTemplate = `
1189
1189
  </div>
1190
1190
  `;
1191
1191
 
1192
+ const IntradayChartTemplate = `
1193
+ <div class="intraday-chart-widget">
1194
+ <div class="chart-header">
1195
+ <div class="chart-title-section">
1196
+ <div class="company-market-info">
1197
+ <span class="intraday-company-name"></span>
1198
+ </div>
1199
+ <h3 class="intraday-chart-symbol editable-symbol"
1200
+ title="Double-click to edit symbol"
1201
+ data-original-symbol="">AAPL</h3>
1202
+ </div>
1203
+ <div class="chart-change positive">+0.00 (+0.00%)</div>
1204
+ </div>
1205
+
1206
+ <div class="chart-controls">
1207
+ <div class="chart-range-selector">
1208
+ <button class="range-btn active" data-range="0">1D</button>
1209
+ <button class="range-btn" data-range="5">5D</button>
1210
+ </div>
1211
+ <div class="chart-type-selector">
1212
+ <button class="type-btn active" data-type="line" title="Line Chart">Line</button>
1213
+ <button class="type-btn" data-type="area" title="Area Chart">Mountain</button>
1214
+ <button class="type-btn" data-type="candlestick" title="Candlestick Chart">Candles</button>
1215
+ </div>
1216
+ <button class="zoom-reset-btn" title="Reset Zoom">
1217
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
1218
+ <path d="M2 8a6 6 0 1 1 12 0A6 6 0 0 1 2 8zm6-7a7 7 0 1 0 0 14A7 7 0 0 0 8 1z" fill="currentColor"/>
1219
+ <path d="M5 8h6M8 5v6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
1220
+ </svg>
1221
+ Reset Zoom
1222
+ </button>
1223
+ </div>
1224
+
1225
+ <div class="chart-container">
1226
+ <canvas id="intradayChart"></canvas>
1227
+ </div>
1228
+
1229
+ <div class="chart-stats">
1230
+ <div class="stats-header">Daily Stats</div>
1231
+ <div class="stats-grid">
1232
+ <div class="stat-item stat-open">
1233
+ <span class="stat-label">Open</span>
1234
+ <span class="stat-value">$0.00</span>
1235
+ </div>
1236
+ <div class="stat-item stat-high">
1237
+ <span class="stat-label">High</span>
1238
+ <span class="stat-value">$0.00</span>
1239
+ </div>
1240
+ <div class="stat-item stat-low">
1241
+ <span class="stat-label">Low</span>
1242
+ <span class="stat-value">$0.00</span>
1243
+ </div>
1244
+ <div class="stat-item stat-close">
1245
+ <span class="stat-label">Close</span>
1246
+ <span class="stat-value">$0.00</span>
1247
+ </div>
1248
+ <div class="stat-item stat-volume">
1249
+ <span class="stat-label">Volume</span>
1250
+ <span class="stat-value">0</span>
1251
+ </div>
1252
+ </div>
1253
+ </div>
1254
+
1255
+ <div class="widget-loading-overlay hidden">
1256
+ <div class="loading-spinner"></div>
1257
+ <div class="loading-text">Loading chart data...</div>
1258
+ </div>
1259
+ </div>
1260
+ `;
1261
+
1192
1262
  // Widget HTML Templates
1193
1263
  const NightSessionTemplate = `
1194
1264
  <div class="night-session-widget widget">
@@ -3967,9 +4037,11 @@ class NightSessionModel {
3967
4037
  } else {
3968
4038
  return 'BlueOcean 20 mins delayed';
3969
4039
  }
3970
- } else if (this.source.toLowerCase().includes('bruce') || this.source.toLowerCase().includes('blueocean')) {
4040
+ } else if (this.source.toLowerCase().includes('bruce') || this.source.toLowerCase().includes('blueocean') || this.source.toLowerCase().includes('onbbo')) {
3971
4041
  if (this.source.toLowerCase() === 'bruce') {
3972
4042
  return 'Bruce Real-time';
4043
+ } else if (this.source.toLowerCase() === 'onbbo') {
4044
+ return 'ONBBO Real-time';
3973
4045
  } else {
3974
4046
  return 'BlueOcean Real-time';
3975
4047
  }
@@ -4005,8 +4077,8 @@ class NightSessionWidget extends BaseWidget {
4005
4077
  if (!options.wsManager) {
4006
4078
  throw new Error('WebSocketManager is required for NightSessionWidget');
4007
4079
  }
4008
- if (!options.source || options.source.toLowerCase() !== 'blueocean' && options.source.toLowerCase() !== 'bruce') {
4009
- throw new Error('Source should be either "blueocean" or "bruce"');
4080
+ if (!options.source || options.source.toLowerCase() !== 'blueocean' && options.source.toLowerCase() !== 'bruce' && options.source.toLowerCase() !== 'onbbo') {
4081
+ throw new Error('Source should be either "blueocean", "bruce", or "onbbo"');
4010
4082
  }
4011
4083
  this.type = 'nightsession ' + options.source;
4012
4084
 
@@ -4226,7 +4298,14 @@ class NightSessionWidget extends BaseWidget {
4226
4298
  }
4227
4299
  }
4228
4300
  subscribeToData() {
4229
- const subscriptionType = this.source === 'bruce' ? 'querybrucel1' : 'queryblueoceanl1';
4301
+ let subscriptionType;
4302
+ if (this.source === 'bruce') {
4303
+ subscriptionType = 'querybrucel1';
4304
+ } else if (this.source === 'onbbo') {
4305
+ subscriptionType = 'queryonbbol1';
4306
+ } else {
4307
+ subscriptionType = 'queryblueoceanl1';
4308
+ }
4230
4309
 
4231
4310
  // Subscribe with symbol for routing
4232
4311
  this.unsubscribe = this.wsManager.subscribe(this.widgetId, [subscriptionType], this.handleMessage.bind(this), this.symbol // Pass symbol for routing
@@ -4340,9 +4419,12 @@ class NightSessionWidget extends BaseWidget {
4340
4419
  }
4341
4420
  }
4342
4421
  // Handle wrapped format
4343
- else if (message.type === 'queryblueoceanl1' || message.type === 'querybrucel1') {
4422
+ else if (message.type === 'queryblueoceanl1' || message.type === 'querybrucel1' || message.type === 'queryonbbol1') {
4344
4423
  if (message['0']?.Symbol === this.symbol) {
4345
- if (message['0'].NotFound === true || !message['0'].MarketName || message['0'].MarketName !== 'BLUE') {
4424
+ // For onbbo source, skip MarketName check
4425
+ const isOnbbo = message.type === 'queryonbbol1';
4426
+ const shouldShowNoData = message['0'].NotFound === true || !isOnbbo && (!message['0'].MarketName || message['0'].MarketName !== 'BLUE');
4427
+ if (shouldShowNoData) {
4346
4428
  // Only show no data state if we don't have cached data
4347
4429
  if (!this.data) {
4348
4430
  this.showNoDataState(message['0']);
@@ -7911,73 +7993,6 @@ class IntradayChartModel {
7911
7993
  }
7912
7994
  }
7913
7995
 
7914
- const IntradayChartTemplate = `
7915
- <div class="intraday-chart-widget">
7916
- <div class="chart-header">
7917
- <div class="chart-title-section">
7918
- <div class="company-market-info">
7919
- <span class="intraday-company-name"></span>
7920
- </div>
7921
- <h3 class="intraday-chart-symbol editable-symbol"
7922
- title="Double-click to edit symbol"
7923
- data-original-symbol="">AAPL</h3>
7924
- </div>
7925
- <div class="chart-change positive">+0.00 (+0.00%)</div>
7926
- </div>
7927
-
7928
- <div class="chart-controls">
7929
- <div class="chart-range-selector">
7930
- <button class="range-btn active" data-range="0">1D</button>
7931
- <button class="range-btn" data-range="5">5D</button>
7932
- </div>
7933
- <div class="chart-type-selector">
7934
- <button class="type-btn active" data-type="line" title="Line Chart">Line</button>
7935
- <button class="type-btn" data-type="area" title="Area Chart">Mountain</button>
7936
- <button class="type-btn" data-type="candlestick" title="Candlestick Chart">Candles</button>
7937
- </div>
7938
- <button class="zoom-reset-btn" title="Reset Zoom">
7939
- <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
7940
- <path d="M2 8a6 6 0 1 1 12 0A6 6 0 0 1 2 8zm6-7a7 7 0 1 0 0 14A7 7 0 0 0 8 1z" fill="currentColor"/>
7941
- <path d="M5 8h6M8 5v6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
7942
- </svg>
7943
- Reset Zoom
7944
- </button>
7945
- </div>
7946
-
7947
- <div class="chart-container">
7948
- <canvas id="intradayChart"></canvas>
7949
- </div>
7950
-
7951
- <div class="chart-stats">
7952
- <div class="stat-item stat-open">
7953
- <span class="stat-label">Open</span>
7954
- <span class="stat-value">$0.00</span>
7955
- </div>
7956
- <div class="stat-item stat-high">
7957
- <span class="stat-label">High</span>
7958
- <span class="stat-value">$0.00</span>
7959
- </div>
7960
- <div class="stat-item stat-low">
7961
- <span class="stat-label">Low</span>
7962
- <span class="stat-value">$0.00</span>
7963
- </div>
7964
- <div class="stat-item stat-close">
7965
- <span class="stat-label">Close</span>
7966
- <span class="stat-value">$0.00</span>
7967
- </div>
7968
- <div class="stat-item stat-volume">
7969
- <span class="stat-label">Volume</span>
7970
- <span class="stat-value">0</span>
7971
- </div>
7972
- </div>
7973
-
7974
- <div class="widget-loading-overlay hidden">
7975
- <div class="loading-spinner"></div>
7976
- <div class="loading-text">Loading chart data...</div>
7977
- </div>
7978
- </div>
7979
- `;
7980
-
7981
7996
  const IntradayChartStyles = `
7982
7997
  .intraday-chart-widget {
7983
7998
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
@@ -8162,14 +8177,28 @@ const IntradayChartStyles = `
8162
8177
  }
8163
8178
 
8164
8179
  .chart-stats {
8165
- display: grid;
8166
- grid-template-columns: repeat(5, 1fr);
8167
- gap: 15px;
8168
8180
  padding: 15px;
8169
8181
  background: #f9fafb;
8170
8182
  border-radius: 8px;
8171
8183
  }
8172
8184
 
8185
+ .stats-header {
8186
+ font-size: 0.875em;
8187
+ font-weight: 700;
8188
+ color: #374151;
8189
+ text-transform: uppercase;
8190
+ letter-spacing: 0.5px;
8191
+ margin-bottom: 12px;
8192
+ padding-bottom: 8px;
8193
+ border-bottom: 2px solid #e5e7eb;
8194
+ }
8195
+
8196
+ .stats-grid {
8197
+ display: grid;
8198
+ grid-template-columns: repeat(5, 1fr);
8199
+ gap: 15px;
8200
+ }
8201
+
8173
8202
  .stat-item {
8174
8203
  display: flex;
8175
8204
  flex-direction: column;
@@ -8238,11 +8267,16 @@ const IntradayChartStyles = `
8238
8267
  padding: 15px;
8239
8268
  }
8240
8269
 
8241
- .chart-stats {
8270
+ .stats-grid {
8242
8271
  grid-template-columns: repeat(3, 1fr);
8243
8272
  gap: 10px;
8244
8273
  }
8245
8274
 
8275
+ .stats-header {
8276
+ font-size: 0.8em;
8277
+ margin-bottom: 10px;
8278
+ }
8279
+
8246
8280
  .chart-container {
8247
8281
  height: 350px;
8248
8282
  }
@@ -37493,7 +37527,8 @@ class IntradayChartWidget extends BaseWidget {
37493
37527
  // Symbol editor
37494
37528
  this.symbolEditor = null;
37495
37529
 
37496
- // Lazy loading cache for 5D data
37530
+ // Lazy loading cache for chart data
37531
+ this.cached1DData = null;
37497
37532
  this.cached5DData = null;
37498
37533
  this.is5DDataLoading = false;
37499
37534
  this.createWidgetStructure();
@@ -37597,7 +37632,8 @@ class IntradayChartWidget extends BaseWidget {
37597
37632
  this.chartData = null;
37598
37633
  this.livePrice = null;
37599
37634
 
37600
- // Clear cached 5D data for old symbol
37635
+ // Clear all cached data for old symbol
37636
+ this.cached1DData = null;
37601
37637
  this.cached5DData = null;
37602
37638
  this.is5DDataLoading = false;
37603
37639
 
@@ -37715,7 +37751,7 @@ class IntradayChartWidget extends BaseWidget {
37715
37751
  // Update chart type and re-render
37716
37752
  this.chartType = type;
37717
37753
  this.renderChart();
37718
- this.updateStats();
37754
+ // Stats are managed by WebSocket updates only
37719
37755
  });
37720
37756
  });
37721
37757
  }
@@ -37751,12 +37787,30 @@ class IntradayChartWidget extends BaseWidget {
37751
37787
  throw new Error('API service not available');
37752
37788
  }
37753
37789
 
37754
- // For 5D chart: Load data from multiple days (rangeback 0-5)
37790
+ // For 5D chart: Load data from multiple days (rangeback 0-6)
37755
37791
  if (this.rangeBack === 5) {
37756
37792
  await this.load5DayChartData(apiService);
37757
37793
  return;
37758
37794
  }
37759
37795
 
37796
+ // For 1D chart: Check if we have cached data first
37797
+ if (this.rangeBack === 0 && this.cached1DData) {
37798
+ if (this.debug) {
37799
+ console.log('[IntradayChartWidget] Using cached 1D data');
37800
+ }
37801
+
37802
+ // Use cached data
37803
+ this.chartData = this.cached1DData;
37804
+ this.renderChart();
37805
+ // Skip updateStats() - stats will be updated from WebSocket data only
37806
+ this.hideLoading();
37807
+
37808
+ // Restart auto-refresh and live price subscription
37809
+ await this.startAutoRefresh();
37810
+ this.subscribeToLivePrice();
37811
+ return;
37812
+ }
37813
+
37760
37814
  // FALLBACK LOGIC: Try to find most recent available data (for 1D)
37761
37815
  const MAX_LOOKBACK_DAYS = 7;
37762
37816
  let attemptRangeBack = this.rangeBack;
@@ -37832,8 +37886,16 @@ class IntradayChartWidget extends BaseWidget {
37832
37886
  console.error('[IntradayChartWidget] Model has no data points after processing');
37833
37887
  throw new Error(`No valid data points for ${this.symbol}`);
37834
37888
  }
37889
+
37890
+ // Cache 1D data for instant switching
37891
+ if (this.rangeBack === 0) {
37892
+ this.cached1DData = this.chartData;
37893
+ if (this.debug) {
37894
+ console.log('[IntradayChartWidget] Cached 1D data for instant switching');
37895
+ }
37896
+ }
37835
37897
  this.renderChart();
37836
- this.updateStats();
37898
+ // Skip updateStats() - stats will be updated from WebSocket data only
37837
37899
  this.hideLoading();
37838
37900
  if (this.debug) {
37839
37901
  console.log(`[IntradayChartWidget] Loaded ${dataArray.length} data points`);
@@ -37887,9 +37949,12 @@ class IntradayChartWidget extends BaseWidget {
37887
37949
  // Use cached data
37888
37950
  this.chartData = this.cached5DData;
37889
37951
  this.renderChart();
37890
- this.updateStats();
37952
+ // Skip updateStats() - stats will be updated from WebSocket data only
37891
37953
  this.hideLoading();
37892
37954
 
37955
+ // Subscribe to live price for stats updates (WebSocket only)
37956
+ this.subscribeToLivePrice();
37957
+
37893
37958
  // Don't auto-refresh for 5D chart
37894
37959
  this.stopAutoRefresh();
37895
37960
  return;
@@ -37907,12 +37972,15 @@ class IntradayChartWidget extends BaseWidget {
37907
37972
  // Cache the processed data
37908
37973
  this.cached5DData = this.chartData;
37909
37974
  this.renderChart();
37910
- this.updateStats();
37975
+ // Skip updateStats() - stats will be updated from WebSocket data only
37911
37976
  this.hideLoading();
37912
37977
  if (this.debug) {
37913
37978
  console.log(`[IntradayChartWidget] 5D chart loaded with ${this.chartData.dataPoints.length} data points`);
37914
37979
  }
37915
37980
 
37981
+ // Subscribe to live price for stats updates (WebSocket only)
37982
+ this.subscribeToLivePrice();
37983
+
37916
37984
  // Don't auto-refresh for 5D chart
37917
37985
  this.stopAutoRefresh();
37918
37986
  } catch (error) {
@@ -38113,6 +38181,64 @@ class IntradayChartWidget extends BaseWidget {
38113
38181
  } else {
38114
38182
  console.log('[IntradayChartWidget] Invalid price, not updating:', price);
38115
38183
  }
38184
+
38185
+ // Update chart stats with live WebSocket data for both 1D and 5D charts
38186
+ // This shows the current day's stats from live data
38187
+ this.updateStatsFromLiveData(priceData);
38188
+ }
38189
+ updateStatsFromLiveData(data) {
38190
+ if (!data) return;
38191
+
38192
+ // Extract stats from WebSocket data
38193
+ const stats = {
38194
+ high: data.HighPx !== undefined && data.HighPx !== null ? parseFloat(data.HighPx) : null,
38195
+ low: data.LowPx !== undefined && data.LowPx !== null ? parseFloat(data.LowPx) : null,
38196
+ open: data.OpenPx !== undefined && data.OpenPx !== null ? parseFloat(data.OpenPx) : null,
38197
+ close: data.LastPx !== undefined && data.LastPx !== null ? parseFloat(data.LastPx) : data.TradePx !== undefined && data.TradePx !== null ? parseFloat(data.TradePx) : null,
38198
+ volume: data.Volume !== undefined && data.Volume !== null ? parseInt(data.Volume) : null,
38199
+ change: data.Change !== undefined && data.Change !== null ? parseFloat(data.Change) : null,
38200
+ changePercent: data.ChangePercent !== undefined && data.ChangePercent !== null ? parseFloat(data.ChangePercent) * 100 : null
38201
+ };
38202
+ if (this.debug) {
38203
+ console.log('[IntradayChartWidget] Updating stats from live data:', stats);
38204
+ }
38205
+
38206
+ // Update stats display
38207
+ const highElement = this.container.querySelector('.stat-high .stat-value');
38208
+ const lowElement = this.container.querySelector('.stat-low .stat-value');
38209
+ const openElement = this.container.querySelector('.stat-open .stat-value');
38210
+ const closeElement = this.container.querySelector('.stat-close .stat-value');
38211
+ const volumeElement = this.container.querySelector('.stat-volume .stat-value');
38212
+ const changeElement = this.container.querySelector('.chart-change');
38213
+ if (highElement && stats.high !== null) {
38214
+ highElement.textContent = `$${stats.high.toFixed(2)}`;
38215
+ }
38216
+ if (lowElement && stats.low !== null) {
38217
+ lowElement.textContent = `$${stats.low.toFixed(2)}`;
38218
+ }
38219
+ if (openElement && stats.open !== null) {
38220
+ openElement.textContent = `$${stats.open.toFixed(2)}`;
38221
+ }
38222
+ if (closeElement && stats.close !== null) {
38223
+ closeElement.textContent = `$${stats.close.toFixed(2)}`;
38224
+ }
38225
+ if (volumeElement && stats.volume !== null) {
38226
+ volumeElement.textContent = this.formatVolume(stats.volume);
38227
+ }
38228
+ if (changeElement && stats.change !== null && stats.changePercent !== null) {
38229
+ const changeClass = stats.change >= 0 ? 'positive' : 'negative';
38230
+ changeElement.className = `chart-change ${changeClass}`;
38231
+ const sign = stats.change >= 0 ? '+' : '';
38232
+ changeElement.textContent = `${sign}${stats.change.toFixed(2)} (${sign}${stats.changePercent.toFixed(2)}%)`;
38233
+ }
38234
+ }
38235
+ formatVolume(volume) {
38236
+ if (volume >= 1000000) {
38237
+ return (volume / 1000000).toFixed(2) + 'M';
38238
+ } else if (volume >= 1000) {
38239
+ return (volume / 1000).toFixed(2) + 'K';
38240
+ }
38241
+ return volume.toString();
38116
38242
  }
38117
38243
  updateLivePriceLine() {
38118
38244
  if (!this.chartInstance || !this.livePrice) {
@@ -38271,7 +38397,8 @@ class IntradayChartWidget extends BaseWidget {
38271
38397
  if (response && Array.isArray(response)) {
38272
38398
  this.chartData = new IntradayChartModel(response);
38273
38399
  this.renderChart();
38274
- this.updateStats();
38400
+ // Stats are managed by WebSocket updates only
38401
+
38275
38402
  if (this.debug) {
38276
38403
  console.log(`[IntradayChartWidget] Auto-refresh complete - ${response.length} data points`);
38277
38404
  }
@@ -38723,6 +38850,13 @@ class IntradayChartWidget extends BaseWidget {
38723
38850
  });
38724
38851
  }
38725
38852
  }
38853
+
38854
+ /**
38855
+ * DEPRECATED: This method is no longer used. Stats are now exclusively
38856
+ * updated from WebSocket data via updateStatsFromLiveData() to ensure
38857
+ * consistency between 1D and 5D charts (both showing today's stats only).
38858
+ * Kept for reference only.
38859
+ */
38726
38860
  updateStats() {
38727
38861
  if (!this.chartData) return;
38728
38862
  const stats = this.chartData.getStats();