@optifye/dashboard-core 6.1.7 → 6.1.8

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
@@ -23095,7 +23095,14 @@ var S3ClipsService = class {
23095
23095
  }
23096
23096
  const limitPerCategory = limit ? Math.min(Math.max(limit, 1), this.maxLimitPerCategory) : this.defaultLimitPerCategory;
23097
23097
  const shouldFetchAll = category === "missing_quality_check" || category === "low_value";
23098
- const initialFetchLimit = shouldFetchAll ? void 0 : category ? Math.min(limitPerCategory * 3, this.maxInitialFetch) : void 0;
23098
+ let initialFetchLimit;
23099
+ if (shouldFetchAll) {
23100
+ initialFetchLimit = void 0;
23101
+ } else if (category) {
23102
+ initialFetchLimit = Math.min(limitPerCategory * 3, this.maxInitialFetch);
23103
+ } else {
23104
+ initialFetchLimit = this.maxInitialFetch;
23105
+ }
23099
23106
  const s3Uris = await this.listS3Clips({ workspaceId, date, shiftId, maxKeys: initialFetchLimit });
23100
23107
  if (s3Uris.length === 0) {
23101
23108
  console.log(`S3ClipsService: No HLS playlists found for workspace ${workspaceId} on date ${date}, shift ${shiftId}`);
@@ -23221,6 +23228,9 @@ var S3ClipsService = class {
23221
23228
  return videos;
23222
23229
  }
23223
23230
  };
23231
+
23232
+ // src/lib/cache/clipsCache.ts
23233
+ var clipsCache = {};
23224
23234
  var BottlenecksContent = ({
23225
23235
  workspaceId,
23226
23236
  workspaceName,
@@ -23240,6 +23250,7 @@ var BottlenecksContent = ({
23240
23250
  const videoRef = React19.useRef(null);
23241
23251
  const fullscreenContainerRef = React19.useRef(null);
23242
23252
  const timestampFilterRef = React19.useRef(null);
23253
+ const hlsRef = React19.useRef(null);
23243
23254
  const [isPlaying, setIsPlaying] = React19.useState(false);
23244
23255
  const [currentTime, setCurrentTime] = React19.useState(0);
23245
23256
  const [duration, setDuration] = React19.useState(0);
@@ -23288,21 +23299,36 @@ var BottlenecksContent = ({
23288
23299
  if (timestampEnd) {
23289
23300
  timestampEndFull = `${operationalDate}T${timestampEnd}:00`;
23290
23301
  }
23291
- const videos = await s3ClipsService.fetchClips({
23302
+ const cacheKey = `${workspaceId}-${operationalDate}-${shift || "all"}-all-meta-1000-${timestampStartFull || ""}-${timestampEndFull || ""}`;
23303
+ const existingEntry = clipsCache[cacheKey];
23304
+ if (existingEntry?.status === "resolved" && existingEntry.data) {
23305
+ setAllVideos(existingEntry.data);
23306
+ return;
23307
+ }
23308
+ if (existingEntry?.status === "pending" && existingEntry.promise) {
23309
+ setIsLoading(true);
23310
+ existingEntry.promise.then((data) => {
23311
+ setAllVideos(data);
23312
+ setIsLoading(false);
23313
+ }).catch(() => setIsLoading(false));
23314
+ return;
23315
+ }
23316
+ setIsLoading(true);
23317
+ const fetchPromise = s3ClipsService.fetchClips({
23292
23318
  workspaceId,
23293
23319
  date: operationalDate,
23294
23320
  shift: shift?.toString(),
23295
- // Pass the shift parameter
23296
23321
  mode: "full",
23297
23322
  includeCycleTime: true,
23298
23323
  includeMetadata: true,
23299
- // Always include metadata for timestamp info
23300
23324
  limit: 1e3,
23301
- // Reasonable limit for UI performance
23302
23325
  timestampStart: timestampStartFull,
23303
23326
  timestampEnd: timestampEndFull
23304
23327
  });
23305
- if (Array.isArray(videos) && videos.length > 0) {
23328
+ clipsCache[cacheKey] = { status: "pending", promise: fetchPromise };
23329
+ const videos = await fetchPromise;
23330
+ clipsCache[cacheKey] = { status: "resolved", data: videos };
23331
+ if (videos.length > 0) {
23306
23332
  preloadVideoUrl2(videos[0].src);
23307
23333
  const firstHigh = videos.find((v) => v.type === "bottleneck" && v.severity === "high");
23308
23334
  const firstMed = videos.find((v) => v.type === "bottleneck" && v.severity === "medium");
@@ -23326,6 +23352,8 @@ var BottlenecksContent = ({
23326
23352
  ].filter(Boolean));
23327
23353
  }
23328
23354
  setAllVideos(videos);
23355
+ setIsLoading(false);
23356
+ return;
23329
23357
  } catch (err) {
23330
23358
  console.error("Error fetching HLS clips from S3:", err);
23331
23359
  setError("Failed to load clips from storage. Please try again.");
@@ -23445,6 +23473,14 @@ var BottlenecksContent = ({
23445
23473
  } else {
23446
23474
  import('hls.js').then(({ default: Hls3 }) => {
23447
23475
  if (Hls3.isSupported()) {
23476
+ if (hlsRef.current) {
23477
+ try {
23478
+ hlsRef.current.destroy();
23479
+ } catch (err) {
23480
+ console.warn("Failed to destroy previous HLS instance", err);
23481
+ }
23482
+ hlsRef.current = null;
23483
+ }
23448
23484
  const hls = new Hls3({
23449
23485
  // Add CORS and error handling configuration
23450
23486
  xhrSetup: (xhr, url) => {
@@ -23487,7 +23523,8 @@ var BottlenecksContent = ({
23487
23523
  testBandwidth: false,
23488
23524
  // Error recovery configuration
23489
23525
  capLevelOnFPSDrop: false,
23490
- capLevelToPlayerSize: false
23526
+ capLevelToPlayerSize: true,
23527
+ abrEwmaDefaultEstimate: 5e5
23491
23528
  });
23492
23529
  hls.on(Hls3.Events.ERROR, (event, data) => {
23493
23530
  console.error("HLS.js error:", data);
@@ -23615,6 +23652,13 @@ var BottlenecksContent = ({
23615
23652
  video.removeEventListener("loadedmetadata", onLoadedMetadata);
23616
23653
  video.removeEventListener("ended", onEnded);
23617
23654
  video.removeEventListener("error", onError);
23655
+ if (hlsRef.current) {
23656
+ try {
23657
+ hlsRef.current.destroy();
23658
+ } catch (e) {
23659
+ }
23660
+ hlsRef.current = null;
23661
+ }
23618
23662
  };
23619
23663
  } else if (videoRef.current) {
23620
23664
  videoRef.current.pause();
@@ -33423,6 +33467,29 @@ var WorkspaceDetailView = ({
33423
33467
  const isHistoricView = Boolean(date && parsedShiftId !== void 0);
33424
33468
  const initialTab = getInitialTab(sourceType, defaultTab, fromMonthly, date);
33425
33469
  const [activeTab, setActiveTab] = React19.useState(initialTab);
33470
+ const dashboardConfig = useDashboardConfig();
33471
+ React19.useEffect(() => {
33472
+ if (!dashboardConfig?.s3Config) return;
33473
+ const operationalDate2 = date || getOperationalDate();
33474
+ const cacheKey = `${workspaceId}-${operationalDate2}-${shift || "all"}-all-meta-1000--`;
33475
+ if (clipsCache[cacheKey]?.status) return;
33476
+ const service = new S3ClipsService(dashboardConfig);
33477
+ const promise = service.fetchClips({
33478
+ workspaceId,
33479
+ date: operationalDate2,
33480
+ shift: shift?.toString(),
33481
+ mode: "full",
33482
+ includeCycleTime: true,
33483
+ includeMetadata: true,
33484
+ limit: 1e3
33485
+ });
33486
+ clipsCache[cacheKey] = { status: "pending", promise };
33487
+ promise.then((videos) => {
33488
+ clipsCache[cacheKey] = { status: "resolved", data: videos };
33489
+ }).catch((err) => {
33490
+ clipsCache[cacheKey] = { status: "rejected", error: err };
33491
+ });
33492
+ }, [workspaceId, date, shift, dashboardConfig]);
33426
33493
  const [isTransitioning, setIsTransitioning] = React19.useState(false);
33427
33494
  const [usingFallbackData, setUsingFallbackData] = React19.useState(false);
33428
33495
  const [showIdleTime, setShowIdleTime] = React19.useState(false);
package/dist/index.mjs CHANGED
@@ -23066,7 +23066,14 @@ var S3ClipsService = class {
23066
23066
  }
23067
23067
  const limitPerCategory = limit ? Math.min(Math.max(limit, 1), this.maxLimitPerCategory) : this.defaultLimitPerCategory;
23068
23068
  const shouldFetchAll = category === "missing_quality_check" || category === "low_value";
23069
- const initialFetchLimit = shouldFetchAll ? void 0 : category ? Math.min(limitPerCategory * 3, this.maxInitialFetch) : void 0;
23069
+ let initialFetchLimit;
23070
+ if (shouldFetchAll) {
23071
+ initialFetchLimit = void 0;
23072
+ } else if (category) {
23073
+ initialFetchLimit = Math.min(limitPerCategory * 3, this.maxInitialFetch);
23074
+ } else {
23075
+ initialFetchLimit = this.maxInitialFetch;
23076
+ }
23070
23077
  const s3Uris = await this.listS3Clips({ workspaceId, date, shiftId, maxKeys: initialFetchLimit });
23071
23078
  if (s3Uris.length === 0) {
23072
23079
  console.log(`S3ClipsService: No HLS playlists found for workspace ${workspaceId} on date ${date}, shift ${shiftId}`);
@@ -23192,6 +23199,9 @@ var S3ClipsService = class {
23192
23199
  return videos;
23193
23200
  }
23194
23201
  };
23202
+
23203
+ // src/lib/cache/clipsCache.ts
23204
+ var clipsCache = {};
23195
23205
  var BottlenecksContent = ({
23196
23206
  workspaceId,
23197
23207
  workspaceName,
@@ -23211,6 +23221,7 @@ var BottlenecksContent = ({
23211
23221
  const videoRef = useRef(null);
23212
23222
  const fullscreenContainerRef = useRef(null);
23213
23223
  const timestampFilterRef = useRef(null);
23224
+ const hlsRef = useRef(null);
23214
23225
  const [isPlaying, setIsPlaying] = useState(false);
23215
23226
  const [currentTime, setCurrentTime] = useState(0);
23216
23227
  const [duration, setDuration] = useState(0);
@@ -23259,21 +23270,36 @@ var BottlenecksContent = ({
23259
23270
  if (timestampEnd) {
23260
23271
  timestampEndFull = `${operationalDate}T${timestampEnd}:00`;
23261
23272
  }
23262
- const videos = await s3ClipsService.fetchClips({
23273
+ const cacheKey = `${workspaceId}-${operationalDate}-${shift || "all"}-all-meta-1000-${timestampStartFull || ""}-${timestampEndFull || ""}`;
23274
+ const existingEntry = clipsCache[cacheKey];
23275
+ if (existingEntry?.status === "resolved" && existingEntry.data) {
23276
+ setAllVideos(existingEntry.data);
23277
+ return;
23278
+ }
23279
+ if (existingEntry?.status === "pending" && existingEntry.promise) {
23280
+ setIsLoading(true);
23281
+ existingEntry.promise.then((data) => {
23282
+ setAllVideos(data);
23283
+ setIsLoading(false);
23284
+ }).catch(() => setIsLoading(false));
23285
+ return;
23286
+ }
23287
+ setIsLoading(true);
23288
+ const fetchPromise = s3ClipsService.fetchClips({
23263
23289
  workspaceId,
23264
23290
  date: operationalDate,
23265
23291
  shift: shift?.toString(),
23266
- // Pass the shift parameter
23267
23292
  mode: "full",
23268
23293
  includeCycleTime: true,
23269
23294
  includeMetadata: true,
23270
- // Always include metadata for timestamp info
23271
23295
  limit: 1e3,
23272
- // Reasonable limit for UI performance
23273
23296
  timestampStart: timestampStartFull,
23274
23297
  timestampEnd: timestampEndFull
23275
23298
  });
23276
- if (Array.isArray(videos) && videos.length > 0) {
23299
+ clipsCache[cacheKey] = { status: "pending", promise: fetchPromise };
23300
+ const videos = await fetchPromise;
23301
+ clipsCache[cacheKey] = { status: "resolved", data: videos };
23302
+ if (videos.length > 0) {
23277
23303
  preloadVideoUrl2(videos[0].src);
23278
23304
  const firstHigh = videos.find((v) => v.type === "bottleneck" && v.severity === "high");
23279
23305
  const firstMed = videos.find((v) => v.type === "bottleneck" && v.severity === "medium");
@@ -23297,6 +23323,8 @@ var BottlenecksContent = ({
23297
23323
  ].filter(Boolean));
23298
23324
  }
23299
23325
  setAllVideos(videos);
23326
+ setIsLoading(false);
23327
+ return;
23300
23328
  } catch (err) {
23301
23329
  console.error("Error fetching HLS clips from S3:", err);
23302
23330
  setError("Failed to load clips from storage. Please try again.");
@@ -23416,6 +23444,14 @@ var BottlenecksContent = ({
23416
23444
  } else {
23417
23445
  import('hls.js').then(({ default: Hls3 }) => {
23418
23446
  if (Hls3.isSupported()) {
23447
+ if (hlsRef.current) {
23448
+ try {
23449
+ hlsRef.current.destroy();
23450
+ } catch (err) {
23451
+ console.warn("Failed to destroy previous HLS instance", err);
23452
+ }
23453
+ hlsRef.current = null;
23454
+ }
23419
23455
  const hls = new Hls3({
23420
23456
  // Add CORS and error handling configuration
23421
23457
  xhrSetup: (xhr, url) => {
@@ -23458,7 +23494,8 @@ var BottlenecksContent = ({
23458
23494
  testBandwidth: false,
23459
23495
  // Error recovery configuration
23460
23496
  capLevelOnFPSDrop: false,
23461
- capLevelToPlayerSize: false
23497
+ capLevelToPlayerSize: true,
23498
+ abrEwmaDefaultEstimate: 5e5
23462
23499
  });
23463
23500
  hls.on(Hls3.Events.ERROR, (event, data) => {
23464
23501
  console.error("HLS.js error:", data);
@@ -23586,6 +23623,13 @@ var BottlenecksContent = ({
23586
23623
  video.removeEventListener("loadedmetadata", onLoadedMetadata);
23587
23624
  video.removeEventListener("ended", onEnded);
23588
23625
  video.removeEventListener("error", onError);
23626
+ if (hlsRef.current) {
23627
+ try {
23628
+ hlsRef.current.destroy();
23629
+ } catch (e) {
23630
+ }
23631
+ hlsRef.current = null;
23632
+ }
23589
23633
  };
23590
23634
  } else if (videoRef.current) {
23591
23635
  videoRef.current.pause();
@@ -33394,6 +33438,29 @@ var WorkspaceDetailView = ({
33394
33438
  const isHistoricView = Boolean(date && parsedShiftId !== void 0);
33395
33439
  const initialTab = getInitialTab(sourceType, defaultTab, fromMonthly, date);
33396
33440
  const [activeTab, setActiveTab] = useState(initialTab);
33441
+ const dashboardConfig = useDashboardConfig();
33442
+ useEffect(() => {
33443
+ if (!dashboardConfig?.s3Config) return;
33444
+ const operationalDate2 = date || getOperationalDate();
33445
+ const cacheKey = `${workspaceId}-${operationalDate2}-${shift || "all"}-all-meta-1000--`;
33446
+ if (clipsCache[cacheKey]?.status) return;
33447
+ const service = new S3ClipsService(dashboardConfig);
33448
+ const promise = service.fetchClips({
33449
+ workspaceId,
33450
+ date: operationalDate2,
33451
+ shift: shift?.toString(),
33452
+ mode: "full",
33453
+ includeCycleTime: true,
33454
+ includeMetadata: true,
33455
+ limit: 1e3
33456
+ });
33457
+ clipsCache[cacheKey] = { status: "pending", promise };
33458
+ promise.then((videos) => {
33459
+ clipsCache[cacheKey] = { status: "resolved", data: videos };
33460
+ }).catch((err) => {
33461
+ clipsCache[cacheKey] = { status: "rejected", error: err };
33462
+ });
33463
+ }, [workspaceId, date, shift, dashboardConfig]);
33397
33464
  const [isTransitioning, setIsTransitioning] = useState(false);
33398
33465
  const [usingFallbackData, setUsingFallbackData] = useState(false);
33399
33466
  const [showIdleTime, setShowIdleTime] = useState(false);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optifye/dashboard-core",
3
- "version": "6.1.7",
3
+ "version": "6.1.8",
4
4
  "description": "Reusable UI & logic for Optifye dashboard",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",