@optifye/dashboard-core 6.3.3 → 6.3.5

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
@@ -424,11 +424,11 @@ var actionService = {
424
424
  };
425
425
  function createMemoizedFunction(fn, getCacheKey) {
426
426
  const cache = /* @__PURE__ */ new Map();
427
- const CACHE_DURATION3 = 5 * 60 * 1e3;
427
+ const CACHE_DURATION = 5 * 60 * 1e3;
428
428
  return (...args) => {
429
429
  const key = getCacheKey(...args);
430
430
  const cached = cache.get(key);
431
- if (cached && Date.now() - cached.timestamp < CACHE_DURATION3) {
431
+ if (cached && Date.now() - cached.timestamp < CACHE_DURATION) {
432
432
  return cached.result;
433
433
  }
434
434
  const result = fn(...args);
@@ -2479,199 +2479,6 @@ async function deleteThread(threadId) {
2479
2479
  if (error) throw error;
2480
2480
  }
2481
2481
 
2482
- // src/lib/services/cacheService.ts
2483
- var CacheService = class {
2484
- constructor() {
2485
- this.memoryCache = /* @__PURE__ */ new Map();
2486
- this.DEFAULT_DURATION = 5 * 60 * 1e3;
2487
- }
2488
- // 5 minutes
2489
- /**
2490
- * Generate a cache key from multiple parts
2491
- */
2492
- generateKey(...parts) {
2493
- return parts.filter((p) => p !== void 0 && p !== null).join(":");
2494
- }
2495
- /**
2496
- * Get item from cache
2497
- */
2498
- get(key, options) {
2499
- const storage = options?.storage || "memory";
2500
- try {
2501
- let cacheItem = null;
2502
- if (storage === "memory") {
2503
- cacheItem = this.memoryCache.get(key) || null;
2504
- } else if (storage === "localStorage" || storage === "sessionStorage") {
2505
- const stored = window[storage].getItem(key);
2506
- if (stored) {
2507
- cacheItem = JSON.parse(stored);
2508
- }
2509
- }
2510
- if (!cacheItem) {
2511
- return null;
2512
- }
2513
- if (Date.now() > cacheItem.expiresAt) {
2514
- this.delete(key, options);
2515
- return null;
2516
- }
2517
- return cacheItem.data;
2518
- } catch (error) {
2519
- console.error(`Error getting cache item ${key}:`, error);
2520
- return null;
2521
- }
2522
- }
2523
- /**
2524
- * Set item in cache
2525
- */
2526
- set(key, data, options) {
2527
- const storage = options?.storage || "memory";
2528
- const duration = options?.duration || this.DEFAULT_DURATION;
2529
- const cacheItem = {
2530
- data,
2531
- timestamp: Date.now(),
2532
- expiresAt: Date.now() + duration
2533
- };
2534
- try {
2535
- if (storage === "memory") {
2536
- this.memoryCache.set(key, cacheItem);
2537
- if (this.memoryCache.size > 100) {
2538
- const firstKey = this.memoryCache.keys().next().value;
2539
- if (firstKey) {
2540
- this.memoryCache.delete(firstKey);
2541
- }
2542
- }
2543
- } else if (storage === "localStorage" || storage === "sessionStorage") {
2544
- window[storage].setItem(key, JSON.stringify(cacheItem));
2545
- }
2546
- } catch (error) {
2547
- console.error(`Error setting cache item ${key}:`, error);
2548
- }
2549
- }
2550
- /**
2551
- * Delete item from cache
2552
- */
2553
- delete(key, options) {
2554
- const storage = options?.storage || "memory";
2555
- try {
2556
- if (storage === "memory") {
2557
- this.memoryCache.delete(key);
2558
- } else if (storage === "localStorage" || storage === "sessionStorage") {
2559
- window[storage].removeItem(key);
2560
- }
2561
- } catch (error) {
2562
- console.error(`Error deleting cache item ${key}:`, error);
2563
- }
2564
- }
2565
- /**
2566
- * Clear all items from cache
2567
- */
2568
- clear(options) {
2569
- const storage = options?.storage || "memory";
2570
- try {
2571
- if (storage === "memory") {
2572
- this.memoryCache.clear();
2573
- } else if (storage === "localStorage" || storage === "sessionStorage") {
2574
- const keys = Object.keys(window[storage]);
2575
- keys.forEach((key) => {
2576
- try {
2577
- const item = window[storage].getItem(key);
2578
- if (item) {
2579
- const parsed = JSON.parse(item);
2580
- if (parsed.timestamp && parsed.expiresAt && parsed.data !== void 0) {
2581
- window[storage].removeItem(key);
2582
- }
2583
- }
2584
- } catch {
2585
- }
2586
- });
2587
- }
2588
- } catch (error) {
2589
- console.error("Error clearing cache:", error);
2590
- }
2591
- }
2592
- /**
2593
- * Get or set item in cache with a factory function
2594
- */
2595
- async getOrSet(key, factory, options) {
2596
- const cached = this.get(key, options);
2597
- if (cached !== null) {
2598
- return cached;
2599
- }
2600
- const data = await factory();
2601
- this.set(key, data, options);
2602
- return data;
2603
- }
2604
- /**
2605
- * Invalidate cache entries matching a pattern
2606
- */
2607
- invalidatePattern(pattern, options) {
2608
- const storage = options?.storage || "memory";
2609
- const regex = typeof pattern === "string" ? new RegExp(pattern) : pattern;
2610
- try {
2611
- if (storage === "memory") {
2612
- const keysToDelete = [];
2613
- this.memoryCache.forEach((_, key) => {
2614
- if (regex.test(key)) {
2615
- keysToDelete.push(key);
2616
- }
2617
- });
2618
- keysToDelete.forEach((key) => this.memoryCache.delete(key));
2619
- } else if (storage === "localStorage" || storage === "sessionStorage") {
2620
- const keys = Object.keys(window[storage]);
2621
- keys.forEach((key) => {
2622
- if (regex.test(key)) {
2623
- window[storage].removeItem(key);
2624
- }
2625
- });
2626
- }
2627
- } catch (error) {
2628
- console.error("Error invalidating cache pattern:", error);
2629
- }
2630
- }
2631
- /**
2632
- * Clean up expired items
2633
- */
2634
- cleanup(options) {
2635
- const storage = options?.storage || "memory";
2636
- const now2 = Date.now();
2637
- try {
2638
- if (storage === "memory") {
2639
- const keysToDelete = [];
2640
- this.memoryCache.forEach((item, key) => {
2641
- if (now2 > item.expiresAt) {
2642
- keysToDelete.push(key);
2643
- }
2644
- });
2645
- keysToDelete.forEach((key) => this.memoryCache.delete(key));
2646
- } else if (storage === "localStorage" || storage === "sessionStorage") {
2647
- const keys = Object.keys(window[storage]);
2648
- keys.forEach((key) => {
2649
- try {
2650
- const item = window[storage].getItem(key);
2651
- if (item) {
2652
- const parsed = JSON.parse(item);
2653
- if (parsed.expiresAt && now2 > parsed.expiresAt) {
2654
- window[storage].removeItem(key);
2655
- }
2656
- }
2657
- } catch {
2658
- }
2659
- });
2660
- }
2661
- } catch (error) {
2662
- console.error("Error cleaning up cache:", error);
2663
- }
2664
- }
2665
- };
2666
- var cacheService = new CacheService();
2667
- if (typeof window !== "undefined") {
2668
- setInterval(() => {
2669
- cacheService.cleanup({ storage: "localStorage" });
2670
- cacheService.cleanup({ storage: "sessionStorage" });
2671
- cacheService.cleanup({ storage: "memory" });
2672
- }, 60 * 1e3);
2673
- }
2674
-
2675
2482
  // src/lib/services/subscriptionManager.ts
2676
2483
  var SubscriptionManager = class {
2677
2484
  constructor(supabase) {
@@ -4000,6 +3807,8 @@ var S3ClipsService = class {
4000
3807
  constructor(config) {
4001
3808
  // Request deduplication cache
4002
3809
  this.requestCache = new RequestDeduplicationCache();
3810
+ // Flag to prevent metadata fetching during index building
3811
+ this.isIndexBuilding = false;
4003
3812
  this.config = config;
4004
3813
  if (!config.s3Config) {
4005
3814
  throw new Error("S3 configuration is required");
@@ -4151,6 +3960,10 @@ var S3ClipsService = class {
4151
3960
  * Fetches full metadata including timestamps with deduplication
4152
3961
  */
4153
3962
  async getFullMetadata(playlistUri) {
3963
+ if (this.isIndexBuilding) {
3964
+ console.warn(`[S3ClipsService] Skipping metadata fetch during index building for: ${playlistUri}`);
3965
+ return null;
3966
+ }
4154
3967
  const deduplicationKey = `full-metadata:${playlistUri}`;
4155
3968
  return this.requestCache.deduplicate(
4156
3969
  deduplicationKey,
@@ -4217,141 +4030,152 @@ var S3ClipsService = class {
4217
4030
  * Internal implementation of clip counts fetching
4218
4031
  */
4219
4032
  async executeGetClipCounts(workspaceId, date, shiftId, buildIndex) {
4220
- const basePrefix = `sop_violations/${workspaceId}/${date}/${shiftId}/`;
4221
- const counts = { total: 0 };
4222
- const categoryFolders = [
4223
- "idle_time",
4224
- "low_value",
4225
- "sop_deviation",
4226
- "missing_quality_check",
4227
- "best_cycle_time",
4228
- "worst_cycle_time",
4229
- "long_cycle_time",
4230
- "cycle_completion",
4231
- "bottleneck"
4232
- ];
4233
- console.log(`[S3ClipsService] ${buildIndex ? "Building video index and counting" : "Fast counting"} clips for ${workspaceId} on ${date}, shift ${shiftId}`);
4234
- const startTime = performance.now();
4235
- const videoIndex = buildIndex ? {
4236
- byCategory: /* @__PURE__ */ new Map(),
4237
- allVideos: [],
4238
- counts: {},
4239
- workspaceId,
4240
- date,
4241
- shiftId: shiftId.toString(),
4242
- lastUpdated: /* @__PURE__ */ new Date(),
4243
- _debugId: `vid_${Date.now()}_${Math.random().toString(36).substring(7)}`
4244
- } : null;
4245
- const countPromises = categoryFolders.map(async (category) => {
4246
- const categoryPrefix = `${basePrefix}${category}/videos/`;
4247
- const categoryVideos = [];
4248
- try {
4249
- if (buildIndex) {
4250
- const command = new clientS3.ListObjectsV2Command({
4251
- Bucket: this.config.s3Config.bucketName,
4252
- Prefix: categoryPrefix,
4253
- MaxKeys: 1e3
4254
- });
4255
- let continuationToken;
4256
- do {
4257
- if (continuationToken) {
4258
- command.input.ContinuationToken = continuationToken;
4259
- }
4260
- const response = await this.s3Client.send(command);
4261
- if (response.Contents) {
4262
- for (const obj of response.Contents) {
4263
- if (obj.Key && obj.Key.endsWith("playlist.m3u8")) {
4264
- if (obj.Key.includes("missed_qchecks")) {
4265
- continue;
4266
- }
4267
- const s3Uri = `s3://${this.config.s3Config.bucketName}/${obj.Key}`;
4268
- const sopCategories = this.getSOPCategories(workspaceId);
4269
- const parsedInfo = parseS3Uri(s3Uri, sopCategories);
4270
- const belongsToCategory = parsedInfo && (parsedInfo.type === category || // Handle specific mismatches between folder names and parsed types
4271
- category === "cycle_completion" && parsedInfo.type === "cycle_completion" || category === "sop_deviation" && parsedInfo.type === "missing_quality_check" || category === "missing_quality_check" && parsedInfo.type === "missing_quality_check" || category === "idle_time" && parsedInfo.type === "low_value" || category === "low_value" && parsedInfo.type === "low_value");
4272
- if (belongsToCategory) {
4273
- const videoEntry = {
4274
- uri: s3Uri,
4275
- category: parsedInfo.type,
4276
- // Use the parsed type, not the folder name
4277
- timestamp: parsedInfo.timestamp,
4278
- videoId: `${workspaceId}-${parsedInfo.timestamp}`,
4279
- workspaceId,
4280
- date,
4281
- shiftId: shiftId.toString()
4282
- };
4283
- categoryVideos.push(videoEntry);
4033
+ if (buildIndex) {
4034
+ this.isIndexBuilding = true;
4035
+ console.log(`[S3ClipsService] Starting index building - metadata fetching disabled`);
4036
+ }
4037
+ try {
4038
+ const basePrefix = `sop_violations/${workspaceId}/${date}/${shiftId}/`;
4039
+ const counts = { total: 0 };
4040
+ const categoryFolders = [
4041
+ "idle_time",
4042
+ "low_value",
4043
+ "sop_deviation",
4044
+ "missing_quality_check",
4045
+ "best_cycle_time",
4046
+ "worst_cycle_time",
4047
+ "long_cycle_time",
4048
+ "cycle_completion",
4049
+ "bottleneck"
4050
+ ];
4051
+ console.log(`[S3ClipsService] ${buildIndex ? "Building video index and counting" : "Fast counting"} clips for ${workspaceId} on ${date}, shift ${shiftId}`);
4052
+ const startTime = performance.now();
4053
+ const videoIndex = buildIndex ? {
4054
+ byCategory: /* @__PURE__ */ new Map(),
4055
+ allVideos: [],
4056
+ counts: {},
4057
+ workspaceId,
4058
+ date,
4059
+ shiftId: shiftId.toString(),
4060
+ lastUpdated: /* @__PURE__ */ new Date(),
4061
+ _debugId: `vid_${Date.now()}_${Math.random().toString(36).substring(7)}`
4062
+ } : null;
4063
+ const countPromises = categoryFolders.map(async (category) => {
4064
+ const categoryPrefix = `${basePrefix}${category}/videos/`;
4065
+ const categoryVideos = [];
4066
+ try {
4067
+ if (buildIndex) {
4068
+ const command = new clientS3.ListObjectsV2Command({
4069
+ Bucket: this.config.s3Config.bucketName,
4070
+ Prefix: categoryPrefix,
4071
+ MaxKeys: 1e3
4072
+ });
4073
+ let continuationToken;
4074
+ do {
4075
+ if (continuationToken) {
4076
+ command.input.ContinuationToken = continuationToken;
4077
+ }
4078
+ const response = await this.s3Client.send(command);
4079
+ if (response.Contents) {
4080
+ for (const obj of response.Contents) {
4081
+ if (obj.Key && obj.Key.endsWith("playlist.m3u8")) {
4082
+ if (obj.Key.includes("missed_qchecks")) {
4083
+ continue;
4084
+ }
4085
+ const s3Uri = `s3://${this.config.s3Config.bucketName}/${obj.Key}`;
4086
+ const sopCategories = this.getSOPCategories(workspaceId);
4087
+ const parsedInfo = parseS3Uri(s3Uri, sopCategories);
4088
+ const belongsToCategory = parsedInfo && (parsedInfo.type === category || // Handle specific mismatches between folder names and parsed types
4089
+ category === "cycle_completion" && parsedInfo.type === "cycle_completion" || category === "sop_deviation" && parsedInfo.type === "missing_quality_check" || category === "missing_quality_check" && parsedInfo.type === "missing_quality_check" || category === "idle_time" && parsedInfo.type === "low_value" || category === "low_value" && parsedInfo.type === "low_value");
4090
+ if (belongsToCategory) {
4091
+ const videoEntry = {
4092
+ uri: s3Uri,
4093
+ category: parsedInfo.type,
4094
+ // Use the parsed type, not the folder name
4095
+ timestamp: parsedInfo.timestamp,
4096
+ videoId: `${workspaceId}-${parsedInfo.timestamp}`,
4097
+ workspaceId,
4098
+ date,
4099
+ shiftId: shiftId.toString()
4100
+ };
4101
+ categoryVideos.push(videoEntry);
4102
+ }
4284
4103
  }
4285
4104
  }
4286
4105
  }
4106
+ continuationToken = response.NextContinuationToken;
4107
+ } while (continuationToken);
4108
+ categoryVideos.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
4109
+ if (categoryVideos.length > 0) {
4110
+ console.log(`[S3ClipsService] Found ${categoryVideos.length} videos for category '${category}' (parsed types: ${[...new Set(categoryVideos.map((v) => v.category))].join(", ")})`);
4287
4111
  }
4288
- continuationToken = response.NextContinuationToken;
4289
- } while (continuationToken);
4290
- categoryVideos.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
4291
- if (categoryVideos.length > 0) {
4292
- console.log(`[S3ClipsService] Found ${categoryVideos.length} videos for category '${category}' (parsed types: ${[...new Set(categoryVideos.map((v) => v.category))].join(", ")})`);
4112
+ return { category, count: categoryVideos.length, videos: categoryVideos };
4113
+ } else {
4114
+ const command = new clientS3.ListObjectsV2Command({
4115
+ Bucket: this.config.s3Config.bucketName,
4116
+ Prefix: categoryPrefix,
4117
+ Delimiter: "/",
4118
+ MaxKeys: 1e3
4119
+ });
4120
+ let folderCount = 0;
4121
+ let continuationToken;
4122
+ do {
4123
+ if (continuationToken) {
4124
+ command.input.ContinuationToken = continuationToken;
4125
+ }
4126
+ const response = await this.s3Client.send(command);
4127
+ if (response.CommonPrefixes) {
4128
+ folderCount += response.CommonPrefixes.length;
4129
+ }
4130
+ continuationToken = response.NextContinuationToken;
4131
+ } while (continuationToken);
4132
+ return { category, count: folderCount, videos: [] };
4293
4133
  }
4294
- return { category, count: categoryVideos.length, videos: categoryVideos };
4295
- } else {
4296
- const command = new clientS3.ListObjectsV2Command({
4297
- Bucket: this.config.s3Config.bucketName,
4298
- Prefix: categoryPrefix,
4299
- Delimiter: "/",
4300
- MaxKeys: 1e3
4301
- });
4302
- let folderCount = 0;
4303
- let continuationToken;
4304
- do {
4305
- if (continuationToken) {
4306
- command.input.ContinuationToken = continuationToken;
4307
- }
4308
- const response = await this.s3Client.send(command);
4309
- if (response.CommonPrefixes) {
4310
- folderCount += response.CommonPrefixes.length;
4134
+ } catch (error) {
4135
+ console.error(`Error ${buildIndex ? "building index for" : "counting folders for"} category ${category}:`, error);
4136
+ return { category, count: 0, videos: [] };
4137
+ }
4138
+ });
4139
+ const results = await Promise.all(countPromises);
4140
+ for (const { category, count, videos } of results) {
4141
+ counts[category] = count;
4142
+ counts.total += count;
4143
+ if (buildIndex && videoIndex && videos) {
4144
+ if (videos.length > 0) {
4145
+ const parsedType = videos[0].category;
4146
+ videoIndex.byCategory.set(parsedType, videos);
4147
+ console.log(`[S3ClipsService] Indexed ${videos.length} videos under parsed type '${parsedType}'`);
4148
+ if (category !== parsedType) {
4149
+ videoIndex.byCategory.set(category, videos);
4150
+ console.log(`[S3ClipsService] Created alias: S3 folder '${category}' -> parsed type '${parsedType}' (${videos.length} videos)`);
4311
4151
  }
4312
- continuationToken = response.NextContinuationToken;
4313
- } while (continuationToken);
4314
- return { category, count: folderCount, videos: [] };
4152
+ }
4153
+ videoIndex.allVideos.push(...videos);
4315
4154
  }
4316
- } catch (error) {
4317
- console.error(`Error ${buildIndex ? "building index for" : "counting folders for"} category ${category}:`, error);
4318
- return { category, count: 0, videos: [] };
4319
4155
  }
4320
- });
4321
- const results = await Promise.all(countPromises);
4322
- for (const { category, count, videos } of results) {
4323
- counts[category] = count;
4324
- counts.total += count;
4325
- if (buildIndex && videoIndex && videos) {
4326
- if (videos.length > 0) {
4327
- const parsedType = videos[0].category;
4328
- videoIndex.byCategory.set(parsedType, videos);
4329
- console.log(`[S3ClipsService] Indexed ${videos.length} videos under parsed type '${parsedType}'`);
4330
- if (category !== parsedType) {
4331
- videoIndex.byCategory.set(category, videos);
4332
- console.log(`[S3ClipsService] Created alias: S3 folder '${category}' -> parsed type '${parsedType}' (${videos.length} videos)`);
4333
- }
4156
+ if (buildIndex && videoIndex) {
4157
+ videoIndex.allVideos.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
4158
+ videoIndex.counts = { ...counts };
4159
+ }
4160
+ const elapsed = performance.now() - startTime;
4161
+ console.log(`[S3ClipsService] ${buildIndex ? "Video index and counts" : "Clip counts"} completed in ${elapsed.toFixed(2)}ms - Total: ${counts.total}`);
4162
+ if (buildIndex && videoIndex) {
4163
+ console.log(`[S3ClipsService] Final video index summary:`);
4164
+ console.log(` - VideoIndex ID: ${videoIndex._debugId}`);
4165
+ console.log(` - Total videos in allVideos: ${videoIndex.allVideos.length}`);
4166
+ console.log(` - Categories in byCategory Map: ${Array.from(videoIndex.byCategory.keys()).join(", ")}`);
4167
+ for (const [cat, vids] of videoIndex.byCategory.entries()) {
4168
+ console.log(` - '${cat}': ${vids.length} videos`);
4334
4169
  }
4335
- videoIndex.allVideos.push(...videos);
4170
+ return { counts, videoIndex };
4336
4171
  }
4337
- }
4338
- if (buildIndex && videoIndex) {
4339
- videoIndex.allVideos.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
4340
- videoIndex.counts = { ...counts };
4341
- }
4342
- const elapsed = performance.now() - startTime;
4343
- console.log(`[S3ClipsService] ${buildIndex ? "Video index and counts" : "Clip counts"} completed in ${elapsed.toFixed(2)}ms - Total: ${counts.total}`);
4344
- if (buildIndex && videoIndex) {
4345
- console.log(`[S3ClipsService] Final video index summary:`);
4346
- console.log(` - VideoIndex ID: ${videoIndex._debugId}`);
4347
- console.log(` - Total videos in allVideos: ${videoIndex.allVideos.length}`);
4348
- console.log(` - Categories in byCategory Map: ${Array.from(videoIndex.byCategory.keys()).join(", ")}`);
4349
- for (const [cat, vids] of videoIndex.byCategory.entries()) {
4350
- console.log(` - '${cat}': ${vids.length} videos`);
4172
+ return counts;
4173
+ } finally {
4174
+ if (buildIndex) {
4175
+ this.isIndexBuilding = false;
4176
+ console.log(`[S3ClipsService] Index building complete - metadata fetching re-enabled`);
4351
4177
  }
4352
- return { counts, videoIndex };
4353
4178
  }
4354
- return counts;
4355
4179
  }
4356
4180
  async getClipCountsCacheFirst(workspaceId, date, shiftId, buildIndex) {
4357
4181
  const cacheKey = `clip-counts:${workspaceId}:${date}:${shiftId}`;
@@ -4445,18 +4269,18 @@ var S3ClipsService = class {
4445
4269
  * Get a specific clip by index for a category with deduplication
4446
4270
  * @deprecated Use getVideoFromIndex with a pre-built VideoIndex for better performance
4447
4271
  */
4448
- async getClipByIndex(workspaceId, date, shiftId, category, index) {
4449
- const deduplicationKey = `clip-by-index:${workspaceId}:${date}:${shiftId}:${category}:${index}`;
4272
+ async getClipByIndex(workspaceId, date, shiftId, category, index, includeCycleTime = true, includeMetadata = false) {
4273
+ const deduplicationKey = `clip-by-index:${workspaceId}:${date}:${shiftId}:${category}:${index}:${includeCycleTime}:${includeMetadata}`;
4450
4274
  return this.requestCache.deduplicate(
4451
4275
  deduplicationKey,
4452
- () => this.executeGetClipByIndex(workspaceId, date, shiftId, category, index),
4276
+ () => this.executeGetClipByIndex(workspaceId, date, shiftId, category, index, includeCycleTime, includeMetadata),
4453
4277
  "ClipByIndex"
4454
4278
  );
4455
4279
  }
4456
4280
  /**
4457
4281
  * Internal implementation of clip by index fetching
4458
4282
  */
4459
- async executeGetClipByIndex(workspaceId, date, shiftId, category, index) {
4283
+ async executeGetClipByIndex(workspaceId, date, shiftId, category, index, includeCycleTime = true, includeMetadata = false) {
4460
4284
  const categoryPrefix = `sop_violations/${workspaceId}/${date}/${shiftId}/${category}/videos/`;
4461
4285
  try {
4462
4286
  const command = new clientS3.ListObjectsV2Command({
@@ -4494,10 +4318,8 @@ var S3ClipsService = class {
4494
4318
  workspaceId,
4495
4319
  date,
4496
4320
  shiftId.toString(),
4497
- true,
4498
- // includeCycleTime
4499
- true
4500
- // includeMetadata
4321
+ includeCycleTime,
4322
+ includeMetadata
4501
4323
  );
4502
4324
  }
4503
4325
  } catch (error) {
@@ -4568,7 +4390,7 @@ var S3ClipsService = class {
4568
4390
  }
4569
4391
  let cycleTimeSeconds = null;
4570
4392
  let creationTimestamp = void 0;
4571
- if (includeMetadata || includeCycleTime && (parsedInfo.type === "bottleneck" && parsedInfo.description.toLowerCase().includes("cycle time") || parsedInfo.type === "best_cycle_time" || parsedInfo.type === "worst_cycle_time" || parsedInfo.type === "cycle_completion")) {
4393
+ if (includeMetadata) {
4572
4394
  const metadata = await this.getFullMetadata(uri);
4573
4395
  if (metadata) {
4574
4396
  if (metadata.original_task_metadata?.cycle_time) {
@@ -4576,6 +4398,8 @@ var S3ClipsService = class {
4576
4398
  }
4577
4399
  creationTimestamp = metadata.upload_timestamp || metadata.original_task_metadata?.timestamp || metadata.creation_timestamp || metadata[""];
4578
4400
  }
4401
+ } else if (includeCycleTime && (parsedInfo.type === "bottleneck" && parsedInfo.description.toLowerCase().includes("cycle time") || parsedInfo.type === "best_cycle_time" || parsedInfo.type === "worst_cycle_time" || parsedInfo.type === "cycle_completion")) {
4402
+ cycleTimeSeconds = null;
4579
4403
  }
4580
4404
  const cloudfrontPlaylistUrl = this.s3UriToCloudfront(uri);
4581
4405
  const { type: videoType, timestamp: videoTimestamp } = parsedInfo;
@@ -5597,7 +5421,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5597
5421
  const [error, setError] = React19.useState(null);
5598
5422
  const updateQueueRef = React19.useRef(false);
5599
5423
  const isFetchingRef = React19.useRef(false);
5600
- const timeoutRef = React19.useRef(null);
5601
5424
  const channelRef = React19.useRef(null);
5602
5425
  const schema = databaseConfig.schema ?? "public";
5603
5426
  const companyId = entityConfig.companyId || "";
@@ -5606,7 +5429,7 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5606
5429
  const defaultTimezone = dateTimeConfig.defaultTimezone;
5607
5430
  const workspaceMetricsBaseTable = databaseConfig.tables?.workspaces ?? "workspace_metrics";
5608
5431
  const workspaceActionsTable = databaseConfig.tables?.actions ?? "workspace_actions";
5609
- const fetchMetrics = React19.useCallback(async (skipCache = false) => {
5432
+ const fetchMetrics = React19.useCallback(async () => {
5610
5433
  if (!workspaceId || isFetchingRef.current) return;
5611
5434
  try {
5612
5435
  isFetchingRef.current = true;
@@ -5615,28 +5438,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5615
5438
  const queryShiftId = shiftId !== void 0 ? shiftId : currentShift.shiftId;
5616
5439
  console.log("[useWorkspaceDetailedMetrics] Using shift ID:", queryShiftId, "from input shift:", shiftId);
5617
5440
  console.log("[useWorkspaceDetailedMetrics] Using date:", queryDate, "from input date:", date);
5618
- const cacheKey = cacheService.generateKey(
5619
- "workspace-detailed-metrics",
5620
- workspaceId,
5621
- queryDate,
5622
- queryShiftId,
5623
- companyId
5624
- );
5625
- if (!skipCache) {
5626
- const cachedData = cacheService.get(cacheKey, {
5627
- storage: "memory",
5628
- duration: 5 * 60 * 1e3
5629
- // 5 minutes
5630
- });
5631
- if (cachedData) {
5632
- console.log("[useWorkspaceDetailedMetrics] Using cached data for:", cacheKey);
5633
- setMetrics(cachedData);
5634
- setIsLoading(false);
5635
- updateQueueRef.current = false;
5636
- isFetchingRef.current = false;
5637
- return;
5638
- }
5639
- }
5640
5441
  console.log(`[useWorkspaceDetailedMetrics] Querying ${metricsTable} for workspace: ${workspaceId}, date: ${queryDate}, shift: ${queryShiftId}`);
5641
5442
  const { data, error: fetchError } = await supabase.from(metricsTable).select("*").eq("workspace_id", workspaceId).eq("date", queryDate).eq("shift_id", queryShiftId).maybeSingle();
5642
5443
  if (fetchError) throw fetchError;
@@ -5739,18 +5540,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5739
5540
  setIsLoading(false);
5740
5541
  updateQueueRef.current = false;
5741
5542
  isFetchingRef.current = false;
5742
- const fallbackCacheKey = cacheService.generateKey(
5743
- "workspace-detailed-metrics",
5744
- workspaceId,
5745
- recentData.date,
5746
- recentData.shift_id,
5747
- companyId
5748
- );
5749
- cacheService.set(fallbackCacheKey, transformedData2, {
5750
- storage: "memory",
5751
- duration: 2 * 60 * 1e3
5752
- // 2 minutes for fallback data
5753
- });
5754
5543
  return;
5755
5544
  } else {
5756
5545
  console.warn("[useWorkspaceDetailedMetrics] No data found for workspace:", workspaceId, "at all");
@@ -5864,11 +5653,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5864
5653
  ...data.sop_check !== void 0 && { sop_check: data.sop_check }
5865
5654
  };
5866
5655
  setMetrics(transformedData);
5867
- cacheService.set(cacheKey, transformedData, {
5868
- storage: "memory",
5869
- duration: 5 * 60 * 1e3
5870
- // 5 minutes
5871
- });
5872
5656
  } catch (err) {
5873
5657
  console.error("Error fetching workspace metrics:", err);
5874
5658
  setError({ message: err.message, code: err.code });
@@ -5881,12 +5665,7 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5881
5665
  const queueUpdate = React19.useCallback(() => {
5882
5666
  if (!workspaceId || updateQueueRef.current) return;
5883
5667
  updateQueueRef.current = true;
5884
- if (timeoutRef.current) {
5885
- clearTimeout(timeoutRef.current);
5886
- }
5887
- timeoutRef.current = setTimeout(() => {
5888
- fetchMetrics();
5889
- }, 500);
5668
+ fetchMetrics();
5890
5669
  }, [fetchMetrics, workspaceId]);
5891
5670
  const setupSubscription = React19.useCallback(() => {
5892
5671
  if (!workspaceId) return;
@@ -5903,7 +5682,7 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5903
5682
  },
5904
5683
  async (payload) => {
5905
5684
  console.log(`Received ${metricsTablePrefix} update:`, payload);
5906
- await fetchMetrics(true);
5685
+ await fetchMetrics();
5907
5686
  }
5908
5687
  ).subscribe((status) => {
5909
5688
  console.log(`Workspace detailed metrics subscription status:`, status);
@@ -5937,14 +5716,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5937
5716
  matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
5938
5717
  });
5939
5718
  if (payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId) {
5940
- const cacheKey = cacheService.generateKey(
5941
- "workspace-detailed-metrics",
5942
- workspaceId,
5943
- operationalDate,
5944
- queryShiftId,
5945
- companyId
5946
- );
5947
- cacheService.delete(cacheKey, { storage: "memory" });
5948
5719
  queueUpdate();
5949
5720
  }
5950
5721
  }
@@ -5970,14 +5741,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5970
5741
  matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
5971
5742
  });
5972
5743
  if (payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId) {
5973
- const cacheKey = cacheService.generateKey(
5974
- "workspace-detailed-metrics",
5975
- workspaceId,
5976
- operationalDate,
5977
- queryShiftId,
5978
- companyId
5979
- );
5980
- cacheService.delete(cacheKey, { storage: "memory" });
5981
5744
  queueUpdate();
5982
5745
  }
5983
5746
  }
@@ -6003,14 +5766,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
6003
5766
  matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
6004
5767
  });
6005
5768
  if (payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId) {
6006
- const cacheKey = cacheService.generateKey(
6007
- "workspace-detailed-metrics",
6008
- workspaceId,
6009
- operationalDate,
6010
- queryShiftId,
6011
- companyId
6012
- );
6013
- cacheService.delete(cacheKey, { storage: "memory" });
6014
5769
  queueUpdate();
6015
5770
  }
6016
5771
  }
@@ -6021,9 +5776,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
6021
5776
  fetchMetrics();
6022
5777
  setupSubscription();
6023
5778
  return () => {
6024
- if (timeoutRef.current) {
6025
- clearTimeout(timeoutRef.current);
6026
- }
6027
5779
  channels.forEach((channel) => {
6028
5780
  console.log("Cleaning up channel subscription");
6029
5781
  supabase.removeChannel(channel);
@@ -6037,7 +5789,7 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
6037
5789
  metrics: metrics2,
6038
5790
  isLoading,
6039
5791
  error,
6040
- refetch: () => fetchMetrics(true)
5792
+ refetch: () => fetchMetrics()
6041
5793
  // Force refresh without cache
6042
5794
  };
6043
5795
  };
@@ -6482,33 +6234,6 @@ var useLeaderboardMetrics = (lineId, topCount = 10) => {
6482
6234
  refetch: fetchLeaderboardData
6483
6235
  };
6484
6236
  };
6485
- var CACHE_KEY_PREFIX = "dashboard_metrics_cache_";
6486
- var CACHE_DURATION = 5 * 60 * 1e3;
6487
- var getCache = (lineId) => {
6488
- if (typeof window === "undefined") return null;
6489
- try {
6490
- const cached = localStorage.getItem(`${CACHE_KEY_PREFIX}${lineId}`);
6491
- if (!cached) return null;
6492
- const parsedCache = JSON.parse(cached);
6493
- if (!parsedCache.lastUpdated || Date.now() - parsedCache.lastUpdated > CACHE_DURATION) {
6494
- localStorage.removeItem(`${CACHE_KEY_PREFIX}${lineId}`);
6495
- return null;
6496
- }
6497
- return parsedCache;
6498
- } catch {
6499
- return null;
6500
- }
6501
- };
6502
- var setCache = (lineId, metrics2) => {
6503
- if (typeof window === "undefined") return;
6504
- try {
6505
- localStorage.setItem(`${CACHE_KEY_PREFIX}${lineId}`, JSON.stringify({
6506
- ...metrics2,
6507
- lastUpdated: Date.now()
6508
- }));
6509
- } catch {
6510
- }
6511
- };
6512
6237
  var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6513
6238
  const { supabaseUrl, supabaseKey } = useDashboardConfig();
6514
6239
  const entityConfig = useEntityConfig();
@@ -6519,22 +6244,20 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6519
6244
  const configuredLineMetricsTable = databaseConfig?.tables?.lineMetrics ?? "line_metrics";
6520
6245
  const schema = databaseConfig?.schema ?? "public";
6521
6246
  const supabase = useSupabase();
6522
- const [metrics2, setMetrics] = React19.useState(() => getCache(lineId) || { workspaceMetrics: [], lineMetrics: [] });
6523
- const [isLoading, setIsLoading] = React19.useState(() => !getCache(lineId));
6247
+ const [metrics2, setMetrics] = React19.useState({ workspaceMetrics: [], lineMetrics: [] });
6248
+ const [isLoading, setIsLoading] = React19.useState(true);
6524
6249
  const [error, setError] = React19.useState(null);
6525
6250
  const lineIdRef = React19.useRef(lineId);
6526
6251
  const isFetchingRef = React19.useRef(false);
6527
6252
  const updateQueueRef = React19.useRef(false);
6528
- const timeoutRef = React19.useRef(null);
6529
6253
  const companySpecificMetricsTable = React19.useMemo(
6530
6254
  () => getCompanyMetricsTableName(entityConfig.companyId, "performance_metrics"),
6531
6255
  [entityConfig.companyId]
6532
6256
  );
6533
6257
  React19.useEffect(() => {
6534
6258
  lineIdRef.current = lineId;
6535
- const cachedData = getCache(lineId);
6536
- setMetrics(cachedData || { workspaceMetrics: [], lineMetrics: [] });
6537
- setIsLoading(!cachedData);
6259
+ setMetrics({ workspaceMetrics: [], lineMetrics: [] });
6260
+ setIsLoading(true);
6538
6261
  }, [lineId]);
6539
6262
  const fetchAllMetrics = React19.useCallback(async () => {
6540
6263
  const currentLineIdToUse = lineIdRef.current;
@@ -6546,9 +6269,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6546
6269
  return;
6547
6270
  }
6548
6271
  isFetchingRef.current = true;
6549
- if (!getCache(currentLineIdToUse)) {
6550
- setIsLoading(true);
6551
- }
6272
+ setIsLoading(true);
6552
6273
  setError(null);
6553
6274
  try {
6554
6275
  const currentShiftDetails = getCurrentShift(defaultTimezone, shiftConfig);
@@ -6573,7 +6294,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6573
6294
  lineMetrics: []
6574
6295
  };
6575
6296
  setMetrics(newMetricsState2);
6576
- setCache(currentLineIdToUse, newMetricsState2);
6577
6297
  return;
6578
6298
  }
6579
6299
  let workspaceQuery = supabase.from(companySpecificMetricsTable).select("company_id,line_id,shift_id,date,workspace_id,workspace_name,total_output,avg_pph,performance_score,avg_cycle_time,trend_score,ideal_output,efficiency,total_day_output").eq("date", operationalDate).eq("shift_id", currentShiftDetails.shiftId).in("workspace_id", enabledWorkspaceIds);
@@ -6625,7 +6345,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6625
6345
  lineMetrics: lineData || []
6626
6346
  };
6627
6347
  setMetrics(newMetricsState);
6628
- setCache(currentLineIdToUse, newMetricsState);
6629
6348
  } catch (err) {
6630
6349
  setError({ message: err.message, code: err.code || "FETCH_ERROR" });
6631
6350
  } finally {
@@ -6651,12 +6370,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6651
6370
  return;
6652
6371
  }
6653
6372
  updateQueueRef.current = true;
6654
- if (timeoutRef.current) {
6655
- clearTimeout(timeoutRef.current);
6656
- }
6657
- timeoutRef.current = setTimeout(() => {
6658
- fetchAllMetrics();
6659
- }, 500);
6373
+ fetchAllMetrics();
6660
6374
  }, [fetchAllMetrics, supabase]);
6661
6375
  React19.useEffect(() => {
6662
6376
  if (lineId && supabase) {
@@ -6701,9 +6415,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6701
6415
  onLineMetricsUpdate?.();
6702
6416
  });
6703
6417
  return () => {
6704
- if (timeoutRef.current) {
6705
- clearTimeout(timeoutRef.current);
6706
- }
6707
6418
  channels.forEach((channel) => {
6708
6419
  supabase?.removeChannel(channel).catch((err) => console.error("[useDashboardMetrics] Error removing channel:", err.message));
6709
6420
  });
@@ -6732,30 +6443,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6732
6443
  refetch: fetchAllMetrics
6733
6444
  };
6734
6445
  };
6735
- var CACHE_KEY_PREFIX2 = "line_kpis_cache_";
6736
- var CACHE_DURATION2 = 5 * 60 * 1e3;
6737
- var getCache2 = (lineId) => {
6738
- if (typeof window === "undefined") return null;
6739
- try {
6740
- const cached = localStorage.getItem(`${CACHE_KEY_PREFIX2}${lineId}`);
6741
- if (!cached) return null;
6742
- const parsedCache = JSON.parse(cached);
6743
- if (!parsedCache.timestamp || Date.now() - parsedCache.timestamp > CACHE_DURATION2) {
6744
- localStorage.removeItem(`${CACHE_KEY_PREFIX2}${lineId}`);
6745
- return null;
6746
- }
6747
- return parsedCache.data;
6748
- } catch (error) {
6749
- return null;
6750
- }
6751
- };
6752
- var setCache2 = (lineId, data) => {
6753
- if (typeof window === "undefined") return;
6754
- try {
6755
- localStorage.setItem(`${CACHE_KEY_PREFIX2}${lineId}`, JSON.stringify({ data, timestamp: Date.now() }));
6756
- } catch (error) {
6757
- }
6758
- };
6759
6446
  var useLineKPIs = ({ lineId }) => {
6760
6447
  useDashboardConfig();
6761
6448
  const entityConfig = useEntityConfig();
@@ -6767,13 +6454,12 @@ var useLineKPIs = ({ lineId }) => {
6767
6454
  const dashboardServiceInstance = React19.useMemo(() => {
6768
6455
  return dashboardService;
6769
6456
  }, []);
6770
- const [kpis, setKPIs] = React19.useState(() => getCache2(lineId));
6771
- const [isLoading, setIsLoading] = React19.useState(!getCache2(lineId));
6457
+ const [kpis, setKPIs] = React19.useState(null);
6458
+ const [isLoading, setIsLoading] = React19.useState(true);
6772
6459
  const [error, setError] = React19.useState(null);
6773
6460
  const lineIdRef = React19.useRef(lineId);
6774
6461
  const isFetchingRef = React19.useRef(false);
6775
6462
  const updateQueueRef = React19.useRef(false);
6776
- const timeoutRef = React19.useRef(null);
6777
6463
  const defaultTimezone = dateTimeConfig.defaultTimezone;
6778
6464
  const schema = databaseConfig.schema ?? "public";
6779
6465
  const lineMetricsTable = databaseConfig.tables?.lineMetrics ?? "line_metrics";
@@ -6804,7 +6490,6 @@ var useLineKPIs = ({ lineId }) => {
6804
6490
  if (lineInfo) {
6805
6491
  const newKPIs = dashboardServiceInstance.calculateKPIs(lineInfo);
6806
6492
  setKPIs(newKPIs);
6807
- setCache2(currentLineId, newKPIs);
6808
6493
  } else {
6809
6494
  setKPIs(null);
6810
6495
  }
@@ -6821,12 +6506,7 @@ var useLineKPIs = ({ lineId }) => {
6821
6506
  const queueUpdate = React19.useCallback(() => {
6822
6507
  if (updateQueueRef.current) return;
6823
6508
  updateQueueRef.current = true;
6824
- if (timeoutRef.current) {
6825
- clearTimeout(timeoutRef.current);
6826
- }
6827
- timeoutRef.current = setTimeout(() => {
6828
- fetchKPIs();
6829
- }, 500);
6509
+ fetchKPIs();
6830
6510
  }, [fetchKPIs]);
6831
6511
  React19.useEffect(() => {
6832
6512
  const currentLineId = lineIdRef.current;
@@ -6884,9 +6564,6 @@ var useLineKPIs = ({ lineId }) => {
6884
6564
  activeChannels.push(csChannel);
6885
6565
  }
6886
6566
  return () => {
6887
- if (timeoutRef.current) {
6888
- clearTimeout(timeoutRef.current);
6889
- }
6890
6567
  activeChannels.forEach((ch) => supabase.removeChannel(ch).catch((err) => console.error("[useLineKPIs] Error removing KPI channel:", err)));
6891
6568
  };
6892
6569
  }, [supabase, lineId, fetchKPIs, queueUpdate, dashboardServiceInstance, entityConfig, schema, lineMetricsTable, companySpecificMetricsTable, defaultTimezone, shiftConfig, kpis, isFactoryView]);
@@ -6915,7 +6592,6 @@ var useRealtimeLineMetrics = ({
6915
6592
  const [initialized, setInitialized] = React19.useState(false);
6916
6593
  const lineIdRef = React19.useRef(null);
6917
6594
  const updateQueueRef = React19.useRef(false);
6918
- const timeoutRef = React19.useRef(null);
6919
6595
  const isFetchingRef = React19.useRef(false);
6920
6596
  const channelsRef = React19.useRef([]);
6921
6597
  const currentShift = React19.useMemo(() => getCurrentShift(dateTimeConfig.defaultTimezone || "Asia/Kolkata", shiftConfig), [dateTimeConfig.defaultTimezone, shiftConfig]);
@@ -7128,12 +6804,7 @@ var useRealtimeLineMetrics = ({
7128
6804
  const queueUpdate = React19.useCallback(() => {
7129
6805
  if (updateQueueRef.current) return;
7130
6806
  updateQueueRef.current = true;
7131
- if (timeoutRef.current) {
7132
- clearTimeout(timeoutRef.current);
7133
- }
7134
- timeoutRef.current = setTimeout(() => {
7135
- fetchData();
7136
- }, 500);
6807
+ fetchData();
7137
6808
  }, [fetchData]);
7138
6809
  const setupSubscriptions = React19.useCallback(() => {
7139
6810
  if (channelsRef.current.length > 0) {
@@ -7211,9 +6882,6 @@ var useRealtimeLineMetrics = ({
7211
6882
  }
7212
6883
  setupSubscriptions();
7213
6884
  return () => {
7214
- if (timeoutRef.current) {
7215
- clearTimeout(timeoutRef.current);
7216
- }
7217
6885
  if (channelsRef.current.length > 0) {
7218
6886
  channelsRef.current.forEach((channel) => {
7219
6887
  if (process.env.NODE_ENV === "development") {
@@ -8501,32 +8169,12 @@ var useAllWorkspaceMetrics = (options) => {
8501
8169
  return `${metricsTablePrefix}_${companyId.replace(/-/g, "_")}`;
8502
8170
  }, [entityConfig.companyId]);
8503
8171
  const schema = databaseConfig.schema ?? "public";
8504
- const fetchWorkspaceMetrics = React19.useCallback(async (skipCache = false) => {
8172
+ const fetchWorkspaceMetrics = React19.useCallback(async () => {
8505
8173
  if (!initialized) {
8506
8174
  setLoading(true);
8507
8175
  }
8508
8176
  setError(null);
8509
8177
  try {
8510
- const cacheKey = cacheService.generateKey(
8511
- "all-workspace-metrics",
8512
- entityConfig.companyId,
8513
- queryDate,
8514
- queryShiftId
8515
- );
8516
- if (!skipCache && !loading) {
8517
- const cachedData = cacheService.get(cacheKey, {
8518
- storage: "memory",
8519
- duration: 5 * 60 * 1e3
8520
- // 5 minutes
8521
- });
8522
- if (cachedData) {
8523
- console.log("[useAllWorkspaceMetrics] Using cached data for:", cacheKey);
8524
- setWorkspaces(cachedData);
8525
- setInitialized(true);
8526
- setLoading(false);
8527
- return;
8528
- }
8529
- }
8530
8178
  console.log("Fetching all workspace metrics with params:", {
8531
8179
  queryDate,
8532
8180
  queryShiftId,
@@ -8576,11 +8224,6 @@ var useAllWorkspaceMetrics = (options) => {
8576
8224
  }));
8577
8225
  setWorkspaces(transformedData);
8578
8226
  setInitialized(true);
8579
- cacheService.set(cacheKey, transformedData, {
8580
- storage: "memory",
8581
- duration: 5 * 60 * 1e3
8582
- // 5 minutes
8583
- });
8584
8227
  } catch (err) {
8585
8228
  console.error("Error fetching all workspace metrics:", err);
8586
8229
  setError({ message: err.message, code: err.code || "FETCH_ERROR" });
@@ -8605,14 +8248,7 @@ var useAllWorkspaceMetrics = (options) => {
8605
8248
  },
8606
8249
  async (payload) => {
8607
8250
  console.log("All workspace metrics update received:", payload);
8608
- const cacheKey = cacheService.generateKey(
8609
- "all-workspace-metrics",
8610
- entityConfig.companyId,
8611
- queryDate,
8612
- queryShiftId
8613
- );
8614
- cacheService.delete(cacheKey, { storage: "memory" });
8615
- await fetchWorkspaceMetrics(true);
8251
+ await fetchWorkspaceMetrics();
8616
8252
  }
8617
8253
  ).subscribe();
8618
8254
  return channel2;
@@ -8627,7 +8263,7 @@ var useAllWorkspaceMetrics = (options) => {
8627
8263
  React19.useEffect(() => {
8628
8264
  setInitialized(false);
8629
8265
  }, [queryDate, queryShiftId]);
8630
- const refreshWorkspaces = React19.useCallback(() => fetchWorkspaceMetrics(true), [fetchWorkspaceMetrics]);
8266
+ const refreshWorkspaces = React19.useCallback(() => fetchWorkspaceMetrics(), [fetchWorkspaceMetrics]);
8631
8267
  return React19.useMemo(
8632
8268
  () => ({ workspaces, loading, error, refreshWorkspaces }),
8633
8269
  [workspaces, loading, error, refreshWorkspaces]
@@ -12080,8 +11716,11 @@ var useHourlyTargetAchievements = ({
12080
11716
  targetThreshold,
12081
11717
  enabled
12082
11718
  });
12083
- const currentHourIndex = hourlyData.length - 1;
12084
- if (currentHourIndex < 0 || hourlyData[currentHourIndex] === void 0) {
11719
+ let currentHourIndex = hourlyData.length - 1;
11720
+ while (currentHourIndex >= 0 && (hourlyData[currentHourIndex] === void 0 || hourlyData[currentHourIndex] === 0)) {
11721
+ currentHourIndex--;
11722
+ }
11723
+ if (currentHourIndex < 0) {
12085
11724
  console.log("[HOURLY TARGET CHECK] No hourly data available");
12086
11725
  return;
12087
11726
  }
@@ -12179,8 +11818,11 @@ var useHourlyTargetMisses = ({
12179
11818
  targetThreshold,
12180
11819
  enabled
12181
11820
  });
12182
- const currentHourIndex = hourlyData.length - 1;
12183
- if (currentHourIndex < 0 || hourlyData[currentHourIndex] === void 0) {
11821
+ let currentHourIndex = hourlyData.length - 1;
11822
+ while (currentHourIndex >= 0 && (hourlyData[currentHourIndex] === void 0 || hourlyData[currentHourIndex] === 0)) {
11823
+ currentHourIndex--;
11824
+ }
11825
+ if (currentHourIndex < 0) {
12184
11826
  console.log("[HOURLY TARGET MISS CHECK] No hourly data available");
12185
11827
  return;
12186
11828
  }
@@ -26629,6 +26271,85 @@ var BottlenecksContent = ({
26629
26271
  }, [workspaceId, date, s3ClipsService, shift, dashboardConfig, updateClipCounts, updateVideoIndex]);
26630
26272
  const loadingCategoryRef = React19.useRef(null);
26631
26273
  const videoRetryCountRef = React19.useRef(0);
26274
+ const loadingVideosRef = React19.useRef(/* @__PURE__ */ new Set());
26275
+ const loadedVideosMapRef = React19.useRef(/* @__PURE__ */ new Map());
26276
+ const ensureVideosLoaded = React19.useCallback(async (centerIndex) => {
26277
+ if (!s3ClipsService || !workspaceId || !isMountedRef.current) return;
26278
+ const currentFilter = activeFilterRef.current;
26279
+ const currentVideoIndex = videoIndexRef.current;
26280
+ let effectiveFilter = currentFilter;
26281
+ if (sopCategories && sopCategories.length > 0) {
26282
+ const category = sopCategories.find((cat) => cat.id === currentFilter);
26283
+ if (category && category.s3FolderName) {
26284
+ effectiveFilter = category.s3FolderName;
26285
+ }
26286
+ }
26287
+ const cacheKey = `${effectiveFilter}:${date}:${shift}`;
26288
+ if (!loadedVideosMapRef.current.has(cacheKey)) {
26289
+ loadedVideosMapRef.current.set(cacheKey, /* @__PURE__ */ new Set());
26290
+ }
26291
+ const loadedIndices = loadedVideosMapRef.current.get(cacheKey);
26292
+ const indicesToLoad = [];
26293
+ const rangeBefore = 1;
26294
+ const rangeAfter = 3;
26295
+ for (let i = Math.max(0, centerIndex - rangeBefore); i <= Math.min(clipCounts[effectiveFilter] - 1, centerIndex + rangeAfter); i++) {
26296
+ if (!loadedIndices.has(i) && !loadingVideosRef.current.has(i)) {
26297
+ indicesToLoad.push(i);
26298
+ }
26299
+ }
26300
+ if (indicesToLoad.length === 0) return;
26301
+ console.log(`[ensureVideosLoaded] Preloading ${indicesToLoad.length} videos around index ${centerIndex}: [${indicesToLoad.join(", ")}]`);
26302
+ indicesToLoad.forEach((idx) => loadingVideosRef.current.add(idx));
26303
+ const loadPromises = indicesToLoad.map(async (index) => {
26304
+ try {
26305
+ let video = null;
26306
+ if (currentVideoIndex && currentVideoIndex.byCategory && currentVideoIndex.allVideos.length > 0) {
26307
+ video = await s3ClipsService.getVideoFromIndex(
26308
+ currentVideoIndex,
26309
+ effectiveFilter,
26310
+ index,
26311
+ true,
26312
+ // includeCycleTime - OK for preloading
26313
+ true
26314
+ // includeMetadata - OK for just +3/-1 videos, not ALL videos
26315
+ );
26316
+ }
26317
+ if (!video) {
26318
+ const operationalDate = date || getOperationalDate();
26319
+ const shiftStr = shift?.toString() || "0";
26320
+ video = await s3ClipsService.getClipByIndex(
26321
+ workspaceId,
26322
+ operationalDate,
26323
+ shiftStr,
26324
+ effectiveFilter,
26325
+ index,
26326
+ true,
26327
+ // includeCycleTime - OK for preloading
26328
+ true
26329
+ // includeMetadata - OK for just +3/-1 videos, not ALL videos
26330
+ );
26331
+ }
26332
+ if (video && isMountedRef.current) {
26333
+ setAllVideos((prev) => {
26334
+ const exists = prev.some((v) => v.id === video.id);
26335
+ if (!exists) {
26336
+ return [...prev, video];
26337
+ }
26338
+ return prev;
26339
+ });
26340
+ loadedIndices.add(index);
26341
+ preloadVideoUrl(video.src);
26342
+ }
26343
+ } catch (error2) {
26344
+ console.warn(`[ensureVideosLoaded] Failed to load video at index ${index}:`, error2);
26345
+ } finally {
26346
+ loadingVideosRef.current.delete(index);
26347
+ }
26348
+ });
26349
+ Promise.all(loadPromises).catch((err) => {
26350
+ console.warn("[ensureVideosLoaded] Some videos failed to preload:", err);
26351
+ });
26352
+ }, [s3ClipsService, workspaceId, clipCounts, sopCategories, date, shift]);
26632
26353
  const loadFirstVideoForCategory = React19.useCallback(async (category) => {
26633
26354
  if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
26634
26355
  const targetCategory = category || activeFilterRef.current;
@@ -26731,9 +26452,11 @@ var BottlenecksContent = ({
26731
26452
  }, [prefetchData, prefetchStatus, updateClipCounts, updateVideoIndex, hasInitialLoad]);
26732
26453
  React19.useEffect(() => {
26733
26454
  if (s3ClipsService && videoIndex && clipCounts[activeFilter] > 0) {
26734
- loadFirstVideoForCategory(activeFilter);
26455
+ if (allVideos.length === 0) {
26456
+ loadFirstVideoForCategory(activeFilter);
26457
+ }
26735
26458
  }
26736
- }, [activeFilter, s3ClipsService, videoIndex, clipCounts, loadFirstVideoForCategory]);
26459
+ }, [activeFilter, s3ClipsService, videoIndex, clipCounts, loadFirstVideoForCategory, allVideos.length]);
26737
26460
  React19.useEffect(() => {
26738
26461
  if (previousFilterRef.current !== activeFilter) {
26739
26462
  console.log(`Filter changed from ${previousFilterRef.current} to ${activeFilter} - resetting to first video`);
@@ -26802,26 +26525,25 @@ var BottlenecksContent = ({
26802
26525
  return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
26803
26526
  });
26804
26527
  }, [activeFilter, allVideos, sopCategories]);
26805
- React19.useEffect(() => {
26806
- if (filteredVideos.length === 0) return;
26807
- const upcoming = [];
26808
- if (currentIndex + 1 < filteredVideos.length && filteredVideos[currentIndex + 1]) {
26809
- upcoming.push(filteredVideos[currentIndex + 1].src);
26810
- }
26811
- if (currentIndex - 1 >= 0 && currentIndex - 1 < filteredVideos.length && filteredVideos[currentIndex - 1]) {
26812
- upcoming.push(filteredVideos[currentIndex - 1].src);
26813
- }
26814
- if (upcoming.length > 0) {
26815
- preloadVideosUrl(upcoming);
26816
- }
26817
- }, [currentIndex, filteredVideos]);
26818
26528
  React19.useEffect(() => {
26819
26529
  if (isNavigating && currentIndex < filteredVideos.length) {
26820
26530
  setIsNavigating(false);
26821
26531
  setError(null);
26822
26532
  videoRetryCountRef.current = 0;
26533
+ ensureVideosLoaded(currentIndex);
26823
26534
  }
26824
26535
  }, [isNavigating, currentIndex, filteredVideos.length]);
26536
+ React19.useEffect(() => {
26537
+ if (!isPlaying || !isMountedRef.current) return;
26538
+ const preloadInterval = setInterval(() => {
26539
+ if (isMountedRef.current) {
26540
+ const currentIdx = currentIndexRef.current;
26541
+ console.log(`[Background Preloader] Ensuring videos loaded around index ${currentIdx}`);
26542
+ ensureVideosLoaded(currentIdx);
26543
+ }
26544
+ }, 2e3);
26545
+ return () => clearInterval(preloadInterval);
26546
+ }, [isPlaying]);
26825
26547
  const handleNext = React19.useCallback(async () => {
26826
26548
  if (!isMountedRef.current) return;
26827
26549
  const currentIdx = currentIndexRef.current;
@@ -26842,6 +26564,7 @@ var BottlenecksContent = ({
26842
26564
  }
26843
26565
  if (nextIndex < filteredVideos.length) {
26844
26566
  setIsNavigating(false);
26567
+ ensureVideosLoaded(nextIndex);
26845
26568
  return;
26846
26569
  }
26847
26570
  if (isMountedRef.current) {
@@ -26859,8 +26582,8 @@ var BottlenecksContent = ({
26859
26582
  nextIndex,
26860
26583
  true,
26861
26584
  // includeCycleTime
26862
- true
26863
- // includeMetadata
26585
+ false
26586
+ // includeMetadata - DON'T fetch metadata during navigation to prevent flooding!
26864
26587
  );
26865
26588
  } else {
26866
26589
  console.warn(`[BottlenecksContent] Video index not ready for navigation: ID: ${currentVideoIndex?._debugId || "NO_ID"}, byCategory exists = ${!!currentVideoIndex?.byCategory}, allVideos = ${currentVideoIndex?.allVideos?.length || 0}`);
@@ -26873,7 +26596,11 @@ var BottlenecksContent = ({
26873
26596
  operationalDate,
26874
26597
  shiftStr,
26875
26598
  effectiveFilter,
26876
- nextIndex
26599
+ nextIndex,
26600
+ true,
26601
+ // includeCycleTime - needed for main video display
26602
+ false
26603
+ // includeMetadata - DON'T fetch metadata during navigation to prevent flooding!
26877
26604
  );
26878
26605
  }
26879
26606
  if (video && isMountedRef.current) {
@@ -26886,26 +26613,7 @@ var BottlenecksContent = ({
26886
26613
  return prev;
26887
26614
  });
26888
26615
  preloadVideoUrl(video.src);
26889
- if (nextIndex + 1 < clipCounts[effectiveFilter]) {
26890
- setTimeout(() => {
26891
- const videoIndexForPreload = videoIndexRef.current;
26892
- if (videoIndexForPreload && s3ClipsService && isMountedRef.current) {
26893
- s3ClipsService.getVideoFromIndex(videoIndexForPreload, effectiveFilter, nextIndex + 1, true, true).then((nextVideo) => {
26894
- if (nextVideo && isMountedRef.current) {
26895
- setAllVideos((prev) => {
26896
- if (!prev.some((v) => v.id === nextVideo.id)) {
26897
- const newVideos = [...prev, nextVideo];
26898
- return newVideos;
26899
- }
26900
- return prev;
26901
- });
26902
- preloadVideoUrl(nextVideo.src);
26903
- }
26904
- }).catch(() => {
26905
- });
26906
- }
26907
- }, 100);
26908
- }
26616
+ ensureVideosLoaded(nextIndex);
26909
26617
  }
26910
26618
  } catch (error2) {
26911
26619
  console.error("Error loading next video:", error2);
@@ -26927,6 +26635,7 @@ var BottlenecksContent = ({
26927
26635
  if (prevIndex < filteredVideos.length) {
26928
26636
  setCurrentIndex(prevIndex);
26929
26637
  setError(null);
26638
+ ensureVideosLoaded(prevIndex);
26930
26639
  }
26931
26640
  }
26932
26641
  }, [filteredVideos.length]);
@@ -26935,6 +26644,8 @@ var BottlenecksContent = ({
26935
26644
  }, []);
26936
26645
  const handleVideoPlay = React19.useCallback((player) => {
26937
26646
  setIsPlaying(true);
26647
+ const currentIdx = currentIndexRef.current;
26648
+ ensureVideosLoaded(currentIdx);
26938
26649
  }, []);
26939
26650
  const handleVideoPause = React19.useCallback((player) => {
26940
26651
  setIsPlaying(false);
@@ -35500,6 +35211,10 @@ var TargetsViewUI = ({
35500
35211
  ),
35501
35212
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 w-px bg-gray-200/80" }),
35502
35213
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ml-4 text-sm font-medium text-gray-600", children: [
35214
+ line.shiftStartTime,
35215
+ " \u2013 ",
35216
+ line.shiftEndTime,
35217
+ " \u2022 ",
35503
35218
  line.shiftHours,
35504
35219
  " hours"
35505
35220
  ] }) })
@@ -36162,7 +35877,7 @@ var TargetsView = ({
36162
35877
  const handleShiftChange = (shiftId) => {
36163
35878
  setSelectedShift(shiftId);
36164
35879
  const loadShiftHours = async () => {
36165
- const updatedLineWorkspaces = { ...lineWorkspaces };
35880
+ const updatedLineWorkspaces = { ...allShiftsData[shiftId] };
36166
35881
  let hasUpdates = false;
36167
35882
  for (const lineId of lineIds) {
36168
35883
  try {
@@ -36210,7 +35925,10 @@ var TargetsView = ({
36210
35925
  }
36211
35926
  }
36212
35927
  if (hasUpdates) {
36213
- setLineWorkspaces(updatedLineWorkspaces);
35928
+ setAllShiftsData((prev) => ({
35929
+ ...prev,
35930
+ [shiftId]: updatedLineWorkspaces
35931
+ }));
36214
35932
  }
36215
35933
  };
36216
35934
  loadShiftHours();
@@ -38007,7 +37725,6 @@ exports.apiUtils = apiUtils;
38007
37725
  exports.authCoreService = authCoreService;
38008
37726
  exports.authOTPService = authOTPService;
38009
37727
  exports.authRateLimitService = authRateLimitService;
38010
- exports.cacheService = cacheService;
38011
37728
  exports.checkRateLimit = checkRateLimit2;
38012
37729
  exports.clearAllRateLimits = clearAllRateLimits2;
38013
37730
  exports.clearRateLimit = clearRateLimit2;