@optifye/dashboard-core 6.5.1 → 6.5.2

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.d.mts CHANGED
@@ -1232,6 +1232,14 @@ declare class S3ClipsService {
1232
1232
  * Fetch clips (main method for compatibility)
1233
1233
  */
1234
1234
  fetchClips(params: S3ClipsAPIParams): Promise<BottleneckVideoData[] | VideoSummary>;
1235
+ /**
1236
+ * Batch fetch multiple videos in parallel
1237
+ */
1238
+ batchFetchVideos(workspaceId: string, date: string, shiftId: string | number, requests: Array<{
1239
+ category: string;
1240
+ index: number;
1241
+ includeMetadata?: boolean;
1242
+ }>): Promise<BottleneckVideoData[]>;
1235
1243
  /**
1236
1244
  * Get videos page using pagination API
1237
1245
  */
package/dist/index.d.ts CHANGED
@@ -1232,6 +1232,14 @@ declare class S3ClipsService {
1232
1232
  * Fetch clips (main method for compatibility)
1233
1233
  */
1234
1234
  fetchClips(params: S3ClipsAPIParams): Promise<BottleneckVideoData[] | VideoSummary>;
1235
+ /**
1236
+ * Batch fetch multiple videos in parallel
1237
+ */
1238
+ batchFetchVideos(workspaceId: string, date: string, shiftId: string | number, requests: Array<{
1239
+ category: string;
1240
+ index: number;
1241
+ includeMetadata?: boolean;
1242
+ }>): Promise<BottleneckVideoData[]>;
1235
1243
  /**
1236
1244
  * Get videos page using pagination API
1237
1245
  */
package/dist/index.js CHANGED
@@ -4152,6 +4152,23 @@ var S3ClipsAPIClient = class {
4152
4152
  };
4153
4153
  });
4154
4154
  }
4155
+ /**
4156
+ * Batch fetch multiple videos in parallel
4157
+ */
4158
+ async batchFetchVideos(workspaceId, date, shiftId, requests) {
4159
+ const batchKey = `batch:${workspaceId}:${date}:${shiftId}:${requests.length}`;
4160
+ return this.deduplicate(batchKey, async () => {
4161
+ const response = await this.fetchWithAuth("/api/clips/batch", {
4162
+ workspaceId,
4163
+ date,
4164
+ shift: shiftId.toString(),
4165
+ requests,
4166
+ sopCategories: this.sopCategories
4167
+ });
4168
+ console.log(`[S3ClipsAPIClient] Batch fetched ${response.videos.length} videos in ${response.performance.duration}ms`);
4169
+ return response.videos;
4170
+ });
4171
+ }
4155
4172
  /**
4156
4173
  * Convert S3 URI to CloudFront URL
4157
4174
  * In the API client, URLs are already signed from the server
@@ -4435,6 +4452,23 @@ var S3ClipsService = class {
4435
4452
  );
4436
4453
  return result.videos;
4437
4454
  }
4455
+ /**
4456
+ * Batch fetch multiple videos in parallel
4457
+ */
4458
+ async batchFetchVideos(workspaceId, date, shiftId, requests) {
4459
+ try {
4460
+ const results = await this.apiClient.batchFetchVideos(
4461
+ workspaceId,
4462
+ date,
4463
+ shiftId,
4464
+ requests
4465
+ );
4466
+ return results.map((r2) => r2.video).filter((v) => v !== null);
4467
+ } catch (error) {
4468
+ console.error("[S3ClipsService] Error batch fetching videos:", error);
4469
+ return [];
4470
+ }
4471
+ }
4438
4472
  /**
4439
4473
  * Get videos page using pagination API
4440
4474
  */
@@ -27024,44 +27058,73 @@ var BottlenecksContent = ({
27024
27058
  if (indicesToLoad.length === 0) return;
27025
27059
  console.log(`[ensureVideosLoaded] Preloading ${indicesToLoad.length} videos around index ${centerIndex}: [${indicesToLoad.join(", ")}]`);
27026
27060
  indicesToLoad.forEach((idx) => loadingVideosRef.current.add(idx));
27027
- const loadPromises = indicesToLoad.map(async (index) => {
27028
- try {
27029
- let video = null;
27030
- if (!video) {
27031
- const operationalDate = date || getOperationalDate();
27032
- const shiftStr = effectiveShift;
27033
- video = await s3ClipsService.getClipByIndex(
27061
+ const operationalDate = date || getOperationalDate();
27062
+ const shiftStr = effectiveShift;
27063
+ console.log(`[ensureVideosLoaded] Batch fetching ${indicesToLoad.length} videos in parallel`);
27064
+ try {
27065
+ const batchRequests = indicesToLoad.map((index) => ({
27066
+ category: effectiveFilter,
27067
+ index,
27068
+ includeMetadata: false
27069
+ // No metadata during bulk preloading
27070
+ }));
27071
+ const videos = await s3ClipsService.batchFetchVideos(
27072
+ workspaceId,
27073
+ operationalDate,
27074
+ shiftStr,
27075
+ batchRequests
27076
+ );
27077
+ if (videos.length > 0 && isMountedRef.current) {
27078
+ videos.forEach((video, idx) => {
27079
+ if (video) {
27080
+ setAllVideos((prev) => {
27081
+ const exists = prev.some((v) => v.id === video.id);
27082
+ if (!exists) {
27083
+ return [...prev, video];
27084
+ }
27085
+ return prev;
27086
+ });
27087
+ const originalIndex = indicesToLoad[idx];
27088
+ loadedIndices.add(originalIndex);
27089
+ preloadVideoUrl(video.src);
27090
+ }
27091
+ });
27092
+ console.log(`[ensureVideosLoaded] Successfully loaded ${videos.length} videos in batch`);
27093
+ }
27094
+ } catch (error2) {
27095
+ console.error("[ensureVideosLoaded] Batch fetch failed:", error2);
27096
+ const loadPromises = indicesToLoad.map(async (index) => {
27097
+ try {
27098
+ const video = await s3ClipsService.getClipByIndex(
27034
27099
  workspaceId,
27035
27100
  operationalDate,
27036
27101
  shiftStr,
27037
27102
  effectiveFilter,
27038
27103
  index,
27039
27104
  true,
27040
- // includeCycleTime - OK for preloading
27105
+ // includeCycleTime
27041
27106
  false
27042
- // includeMetadata - NO metadata during bulk preloading to prevent flooding
27107
+ // includeMetadata
27043
27108
  );
27109
+ if (video && isMountedRef.current) {
27110
+ setAllVideos((prev) => {
27111
+ const exists = prev.some((v) => v.id === video.id);
27112
+ if (!exists) {
27113
+ return [...prev, video];
27114
+ }
27115
+ return prev;
27116
+ });
27117
+ loadedIndices.add(index);
27118
+ preloadVideoUrl(video.src);
27119
+ }
27120
+ } catch (err) {
27121
+ console.warn(`[ensureVideosLoaded] Failed to load video at index ${index}:`, err);
27044
27122
  }
27045
- if (video && isMountedRef.current) {
27046
- setAllVideos((prev) => {
27047
- const exists = prev.some((v) => v.id === video.id);
27048
- if (!exists) {
27049
- return [...prev, video];
27050
- }
27051
- return prev;
27052
- });
27053
- loadedIndices.add(index);
27054
- preloadVideoUrl(video.src);
27055
- }
27056
- } catch (error2) {
27057
- console.warn(`[ensureVideosLoaded] Failed to load video at index ${index}:`, error2);
27058
- } finally {
27059
- loadingVideosRef.current.delete(index);
27060
- }
27061
- });
27062
- Promise.all(loadPromises).catch((err) => {
27063
- console.warn("[ensureVideosLoaded] Some videos failed to preload:", err);
27064
- });
27123
+ });
27124
+ await Promise.all(loadPromises);
27125
+ } finally {
27126
+ indicesToLoad.forEach((idx) => loadingVideosRef.current.delete(idx));
27127
+ }
27065
27128
  }, [s3ClipsService, workspaceId, clipCounts, sopCategories, date, effectiveShift]);
27066
27129
  const loadFirstVideoForCategory = React19.useCallback(async (category) => {
27067
27130
  if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
package/dist/index.mjs CHANGED
@@ -4122,6 +4122,23 @@ var S3ClipsAPIClient = class {
4122
4122
  };
4123
4123
  });
4124
4124
  }
4125
+ /**
4126
+ * Batch fetch multiple videos in parallel
4127
+ */
4128
+ async batchFetchVideos(workspaceId, date, shiftId, requests) {
4129
+ const batchKey = `batch:${workspaceId}:${date}:${shiftId}:${requests.length}`;
4130
+ return this.deduplicate(batchKey, async () => {
4131
+ const response = await this.fetchWithAuth("/api/clips/batch", {
4132
+ workspaceId,
4133
+ date,
4134
+ shift: shiftId.toString(),
4135
+ requests,
4136
+ sopCategories: this.sopCategories
4137
+ });
4138
+ console.log(`[S3ClipsAPIClient] Batch fetched ${response.videos.length} videos in ${response.performance.duration}ms`);
4139
+ return response.videos;
4140
+ });
4141
+ }
4125
4142
  /**
4126
4143
  * Convert S3 URI to CloudFront URL
4127
4144
  * In the API client, URLs are already signed from the server
@@ -4405,6 +4422,23 @@ var S3ClipsService = class {
4405
4422
  );
4406
4423
  return result.videos;
4407
4424
  }
4425
+ /**
4426
+ * Batch fetch multiple videos in parallel
4427
+ */
4428
+ async batchFetchVideos(workspaceId, date, shiftId, requests) {
4429
+ try {
4430
+ const results = await this.apiClient.batchFetchVideos(
4431
+ workspaceId,
4432
+ date,
4433
+ shiftId,
4434
+ requests
4435
+ );
4436
+ return results.map((r2) => r2.video).filter((v) => v !== null);
4437
+ } catch (error) {
4438
+ console.error("[S3ClipsService] Error batch fetching videos:", error);
4439
+ return [];
4440
+ }
4441
+ }
4408
4442
  /**
4409
4443
  * Get videos page using pagination API
4410
4444
  */
@@ -26994,44 +27028,73 @@ var BottlenecksContent = ({
26994
27028
  if (indicesToLoad.length === 0) return;
26995
27029
  console.log(`[ensureVideosLoaded] Preloading ${indicesToLoad.length} videos around index ${centerIndex}: [${indicesToLoad.join(", ")}]`);
26996
27030
  indicesToLoad.forEach((idx) => loadingVideosRef.current.add(idx));
26997
- const loadPromises = indicesToLoad.map(async (index) => {
26998
- try {
26999
- let video = null;
27000
- if (!video) {
27001
- const operationalDate = date || getOperationalDate();
27002
- const shiftStr = effectiveShift;
27003
- video = await s3ClipsService.getClipByIndex(
27031
+ const operationalDate = date || getOperationalDate();
27032
+ const shiftStr = effectiveShift;
27033
+ console.log(`[ensureVideosLoaded] Batch fetching ${indicesToLoad.length} videos in parallel`);
27034
+ try {
27035
+ const batchRequests = indicesToLoad.map((index) => ({
27036
+ category: effectiveFilter,
27037
+ index,
27038
+ includeMetadata: false
27039
+ // No metadata during bulk preloading
27040
+ }));
27041
+ const videos = await s3ClipsService.batchFetchVideos(
27042
+ workspaceId,
27043
+ operationalDate,
27044
+ shiftStr,
27045
+ batchRequests
27046
+ );
27047
+ if (videos.length > 0 && isMountedRef.current) {
27048
+ videos.forEach((video, idx) => {
27049
+ if (video) {
27050
+ setAllVideos((prev) => {
27051
+ const exists = prev.some((v) => v.id === video.id);
27052
+ if (!exists) {
27053
+ return [...prev, video];
27054
+ }
27055
+ return prev;
27056
+ });
27057
+ const originalIndex = indicesToLoad[idx];
27058
+ loadedIndices.add(originalIndex);
27059
+ preloadVideoUrl(video.src);
27060
+ }
27061
+ });
27062
+ console.log(`[ensureVideosLoaded] Successfully loaded ${videos.length} videos in batch`);
27063
+ }
27064
+ } catch (error2) {
27065
+ console.error("[ensureVideosLoaded] Batch fetch failed:", error2);
27066
+ const loadPromises = indicesToLoad.map(async (index) => {
27067
+ try {
27068
+ const video = await s3ClipsService.getClipByIndex(
27004
27069
  workspaceId,
27005
27070
  operationalDate,
27006
27071
  shiftStr,
27007
27072
  effectiveFilter,
27008
27073
  index,
27009
27074
  true,
27010
- // includeCycleTime - OK for preloading
27075
+ // includeCycleTime
27011
27076
  false
27012
- // includeMetadata - NO metadata during bulk preloading to prevent flooding
27077
+ // includeMetadata
27013
27078
  );
27079
+ if (video && isMountedRef.current) {
27080
+ setAllVideos((prev) => {
27081
+ const exists = prev.some((v) => v.id === video.id);
27082
+ if (!exists) {
27083
+ return [...prev, video];
27084
+ }
27085
+ return prev;
27086
+ });
27087
+ loadedIndices.add(index);
27088
+ preloadVideoUrl(video.src);
27089
+ }
27090
+ } catch (err) {
27091
+ console.warn(`[ensureVideosLoaded] Failed to load video at index ${index}:`, err);
27014
27092
  }
27015
- if (video && isMountedRef.current) {
27016
- setAllVideos((prev) => {
27017
- const exists = prev.some((v) => v.id === video.id);
27018
- if (!exists) {
27019
- return [...prev, video];
27020
- }
27021
- return prev;
27022
- });
27023
- loadedIndices.add(index);
27024
- preloadVideoUrl(video.src);
27025
- }
27026
- } catch (error2) {
27027
- console.warn(`[ensureVideosLoaded] Failed to load video at index ${index}:`, error2);
27028
- } finally {
27029
- loadingVideosRef.current.delete(index);
27030
- }
27031
- });
27032
- Promise.all(loadPromises).catch((err) => {
27033
- console.warn("[ensureVideosLoaded] Some videos failed to preload:", err);
27034
- });
27093
+ });
27094
+ await Promise.all(loadPromises);
27095
+ } finally {
27096
+ indicesToLoad.forEach((idx) => loadingVideosRef.current.delete(idx));
27097
+ }
27035
27098
  }, [s3ClipsService, workspaceId, clipCounts, sopCategories, date, effectiveShift]);
27036
27099
  const loadFirstVideoForCategory = useCallback(async (category) => {
27037
27100
  if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optifye/dashboard-core",
3
- "version": "6.5.1",
3
+ "version": "6.5.2",
4
4
  "description": "Reusable UI & logic for Optifye dashboard",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",