@optifye/dashboard-core 6.3.4 → 6.4.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.
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,13 @@ 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;
3812
+ // Flag to prevent metadata fetching during entire prefetch operation
3813
+ this.isPrefetching = false;
3814
+ // Global safeguard: limit concurrent metadata fetches to prevent flooding
3815
+ this.currentMetadataFetches = 0;
3816
+ this.MAX_CONCURRENT_METADATA = 3;
4003
3817
  this.config = config;
4004
3818
  if (!config.s3Config) {
4005
3819
  throw new Error("S3 configuration is required");
@@ -4147,16 +3961,36 @@ var S3ClipsService = class {
4147
3961
  return null;
4148
3962
  }
4149
3963
  }
3964
+ /**
3965
+ * Control prefetch mode to prevent metadata fetching during background operations
3966
+ */
3967
+ setPrefetchMode(enabled) {
3968
+ this.isPrefetching = enabled;
3969
+ console.log(`[S3ClipsService] Prefetch mode ${enabled ? "enabled" : "disabled"} - metadata fetching ${enabled ? "blocked" : "allowed"}`);
3970
+ }
4150
3971
  /**
4151
3972
  * Fetches full metadata including timestamps with deduplication
4152
3973
  */
4153
3974
  async getFullMetadata(playlistUri) {
4154
- const deduplicationKey = `full-metadata:${playlistUri}`;
4155
- return this.requestCache.deduplicate(
4156
- deduplicationKey,
4157
- () => this.executeGetFullMetadata(playlistUri),
4158
- "FullMetadata"
4159
- );
3975
+ if (this.isIndexBuilding || this.isPrefetching) {
3976
+ console.warn(`[S3ClipsService] Skipping metadata fetch - building: ${this.isIndexBuilding}, prefetching: ${this.isPrefetching}`);
3977
+ return null;
3978
+ }
3979
+ if (this.currentMetadataFetches >= this.MAX_CONCURRENT_METADATA) {
3980
+ console.warn(`[S3ClipsService] Skipping metadata - max concurrent fetches (${this.MAX_CONCURRENT_METADATA}) reached`);
3981
+ return null;
3982
+ }
3983
+ this.currentMetadataFetches++;
3984
+ try {
3985
+ const deduplicationKey = `full-metadata:${playlistUri}`;
3986
+ return await this.requestCache.deduplicate(
3987
+ deduplicationKey,
3988
+ () => this.executeGetFullMetadata(playlistUri),
3989
+ "FullMetadata"
3990
+ );
3991
+ } finally {
3992
+ this.currentMetadataFetches--;
3993
+ }
4160
3994
  }
4161
3995
  /**
4162
3996
  * Internal implementation of full metadata fetching
@@ -4217,141 +4051,152 @@ var S3ClipsService = class {
4217
4051
  * Internal implementation of clip counts fetching
4218
4052
  */
4219
4053
  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);
4054
+ if (buildIndex) {
4055
+ this.isIndexBuilding = true;
4056
+ console.log(`[S3ClipsService] Starting index building - metadata fetching disabled`);
4057
+ }
4058
+ try {
4059
+ const basePrefix = `sop_violations/${workspaceId}/${date}/${shiftId}/`;
4060
+ const counts = { total: 0 };
4061
+ const categoryFolders = [
4062
+ "idle_time",
4063
+ "low_value",
4064
+ "sop_deviation",
4065
+ "missing_quality_check",
4066
+ "best_cycle_time",
4067
+ "worst_cycle_time",
4068
+ "long_cycle_time",
4069
+ "cycle_completion",
4070
+ "bottleneck"
4071
+ ];
4072
+ console.log(`[S3ClipsService] ${buildIndex ? "Building video index and counting" : "Fast counting"} clips for ${workspaceId} on ${date}, shift ${shiftId}`);
4073
+ const startTime = performance.now();
4074
+ const videoIndex = buildIndex ? {
4075
+ byCategory: /* @__PURE__ */ new Map(),
4076
+ allVideos: [],
4077
+ counts: {},
4078
+ workspaceId,
4079
+ date,
4080
+ shiftId: shiftId.toString(),
4081
+ lastUpdated: /* @__PURE__ */ new Date(),
4082
+ _debugId: `vid_${Date.now()}_${Math.random().toString(36).substring(7)}`
4083
+ } : null;
4084
+ const countPromises = categoryFolders.map(async (category) => {
4085
+ const categoryPrefix = `${basePrefix}${category}/videos/`;
4086
+ const categoryVideos = [];
4087
+ try {
4088
+ if (buildIndex) {
4089
+ const command = new clientS3.ListObjectsV2Command({
4090
+ Bucket: this.config.s3Config.bucketName,
4091
+ Prefix: categoryPrefix,
4092
+ MaxKeys: 1e3
4093
+ });
4094
+ let continuationToken;
4095
+ do {
4096
+ if (continuationToken) {
4097
+ command.input.ContinuationToken = continuationToken;
4098
+ }
4099
+ const response = await this.s3Client.send(command);
4100
+ if (response.Contents) {
4101
+ for (const obj of response.Contents) {
4102
+ if (obj.Key && obj.Key.endsWith("playlist.m3u8")) {
4103
+ if (obj.Key.includes("missed_qchecks")) {
4104
+ continue;
4105
+ }
4106
+ const s3Uri = `s3://${this.config.s3Config.bucketName}/${obj.Key}`;
4107
+ const sopCategories = this.getSOPCategories(workspaceId);
4108
+ const parsedInfo = parseS3Uri(s3Uri, sopCategories);
4109
+ const belongsToCategory = parsedInfo && (parsedInfo.type === category || // Handle specific mismatches between folder names and parsed types
4110
+ 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");
4111
+ if (belongsToCategory) {
4112
+ const videoEntry = {
4113
+ uri: s3Uri,
4114
+ category: parsedInfo.type,
4115
+ // Use the parsed type, not the folder name
4116
+ timestamp: parsedInfo.timestamp,
4117
+ videoId: `${workspaceId}-${parsedInfo.timestamp}`,
4118
+ workspaceId,
4119
+ date,
4120
+ shiftId: shiftId.toString()
4121
+ };
4122
+ categoryVideos.push(videoEntry);
4123
+ }
4284
4124
  }
4285
4125
  }
4286
4126
  }
4127
+ continuationToken = response.NextContinuationToken;
4128
+ } while (continuationToken);
4129
+ categoryVideos.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
4130
+ if (categoryVideos.length > 0) {
4131
+ console.log(`[S3ClipsService] Found ${categoryVideos.length} videos for category '${category}' (parsed types: ${[...new Set(categoryVideos.map((v) => v.category))].join(", ")})`);
4287
4132
  }
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(", ")})`);
4133
+ return { category, count: categoryVideos.length, videos: categoryVideos };
4134
+ } else {
4135
+ const command = new clientS3.ListObjectsV2Command({
4136
+ Bucket: this.config.s3Config.bucketName,
4137
+ Prefix: categoryPrefix,
4138
+ Delimiter: "/",
4139
+ MaxKeys: 1e3
4140
+ });
4141
+ let folderCount = 0;
4142
+ let continuationToken;
4143
+ do {
4144
+ if (continuationToken) {
4145
+ command.input.ContinuationToken = continuationToken;
4146
+ }
4147
+ const response = await this.s3Client.send(command);
4148
+ if (response.CommonPrefixes) {
4149
+ folderCount += response.CommonPrefixes.length;
4150
+ }
4151
+ continuationToken = response.NextContinuationToken;
4152
+ } while (continuationToken);
4153
+ return { category, count: folderCount, videos: [] };
4293
4154
  }
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;
4155
+ } catch (error) {
4156
+ console.error(`Error ${buildIndex ? "building index for" : "counting folders for"} category ${category}:`, error);
4157
+ return { category, count: 0, videos: [] };
4158
+ }
4159
+ });
4160
+ const results = await Promise.all(countPromises);
4161
+ for (const { category, count, videos } of results) {
4162
+ counts[category] = count;
4163
+ counts.total += count;
4164
+ if (buildIndex && videoIndex && videos) {
4165
+ if (videos.length > 0) {
4166
+ const parsedType = videos[0].category;
4167
+ videoIndex.byCategory.set(parsedType, videos);
4168
+ console.log(`[S3ClipsService] Indexed ${videos.length} videos under parsed type '${parsedType}'`);
4169
+ if (category !== parsedType) {
4170
+ videoIndex.byCategory.set(category, videos);
4171
+ console.log(`[S3ClipsService] Created alias: S3 folder '${category}' -> parsed type '${parsedType}' (${videos.length} videos)`);
4311
4172
  }
4312
- continuationToken = response.NextContinuationToken;
4313
- } while (continuationToken);
4314
- return { category, count: folderCount, videos: [] };
4173
+ }
4174
+ videoIndex.allVideos.push(...videos);
4315
4175
  }
4316
- } catch (error) {
4317
- console.error(`Error ${buildIndex ? "building index for" : "counting folders for"} category ${category}:`, error);
4318
- return { category, count: 0, videos: [] };
4319
4176
  }
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
- }
4177
+ if (buildIndex && videoIndex) {
4178
+ videoIndex.allVideos.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
4179
+ videoIndex.counts = { ...counts };
4180
+ }
4181
+ const elapsed = performance.now() - startTime;
4182
+ console.log(`[S3ClipsService] ${buildIndex ? "Video index and counts" : "Clip counts"} completed in ${elapsed.toFixed(2)}ms - Total: ${counts.total}`);
4183
+ if (buildIndex && videoIndex) {
4184
+ console.log(`[S3ClipsService] Final video index summary:`);
4185
+ console.log(` - VideoIndex ID: ${videoIndex._debugId}`);
4186
+ console.log(` - Total videos in allVideos: ${videoIndex.allVideos.length}`);
4187
+ console.log(` - Categories in byCategory Map: ${Array.from(videoIndex.byCategory.keys()).join(", ")}`);
4188
+ for (const [cat, vids] of videoIndex.byCategory.entries()) {
4189
+ console.log(` - '${cat}': ${vids.length} videos`);
4334
4190
  }
4335
- videoIndex.allVideos.push(...videos);
4191
+ return { counts, videoIndex };
4336
4192
  }
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`);
4193
+ return counts;
4194
+ } finally {
4195
+ if (buildIndex) {
4196
+ this.isIndexBuilding = false;
4197
+ console.log(`[S3ClipsService] Index building complete - metadata fetching re-enabled`);
4351
4198
  }
4352
- return { counts, videoIndex };
4353
4199
  }
4354
- return counts;
4355
4200
  }
4356
4201
  async getClipCountsCacheFirst(workspaceId, date, shiftId, buildIndex) {
4357
4202
  const cacheKey = `clip-counts:${workspaceId}:${date}:${shiftId}`;
@@ -4445,18 +4290,18 @@ var S3ClipsService = class {
4445
4290
  * Get a specific clip by index for a category with deduplication
4446
4291
  * @deprecated Use getVideoFromIndex with a pre-built VideoIndex for better performance
4447
4292
  */
4448
- async getClipByIndex(workspaceId, date, shiftId, category, index) {
4449
- const deduplicationKey = `clip-by-index:${workspaceId}:${date}:${shiftId}:${category}:${index}`;
4293
+ async getClipByIndex(workspaceId, date, shiftId, category, index, includeCycleTime = true, includeMetadata = false) {
4294
+ const deduplicationKey = `clip-by-index:${workspaceId}:${date}:${shiftId}:${category}:${index}:${includeCycleTime}:${includeMetadata}`;
4450
4295
  return this.requestCache.deduplicate(
4451
4296
  deduplicationKey,
4452
- () => this.executeGetClipByIndex(workspaceId, date, shiftId, category, index),
4297
+ () => this.executeGetClipByIndex(workspaceId, date, shiftId, category, index, includeCycleTime, includeMetadata),
4453
4298
  "ClipByIndex"
4454
4299
  );
4455
4300
  }
4456
4301
  /**
4457
4302
  * Internal implementation of clip by index fetching
4458
4303
  */
4459
- async executeGetClipByIndex(workspaceId, date, shiftId, category, index) {
4304
+ async executeGetClipByIndex(workspaceId, date, shiftId, category, index, includeCycleTime = true, includeMetadata = false) {
4460
4305
  const categoryPrefix = `sop_violations/${workspaceId}/${date}/${shiftId}/${category}/videos/`;
4461
4306
  try {
4462
4307
  const command = new clientS3.ListObjectsV2Command({
@@ -4494,10 +4339,8 @@ var S3ClipsService = class {
4494
4339
  workspaceId,
4495
4340
  date,
4496
4341
  shiftId.toString(),
4497
- true,
4498
- // includeCycleTime
4499
- true
4500
- // includeMetadata
4342
+ includeCycleTime,
4343
+ includeMetadata
4501
4344
  );
4502
4345
  }
4503
4346
  } catch (error) {
@@ -4568,7 +4411,7 @@ var S3ClipsService = class {
4568
4411
  }
4569
4412
  let cycleTimeSeconds = null;
4570
4413
  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")) {
4414
+ if (includeMetadata) {
4572
4415
  const metadata = await this.getFullMetadata(uri);
4573
4416
  if (metadata) {
4574
4417
  if (metadata.original_task_metadata?.cycle_time) {
@@ -4576,6 +4419,8 @@ var S3ClipsService = class {
4576
4419
  }
4577
4420
  creationTimestamp = metadata.upload_timestamp || metadata.original_task_metadata?.timestamp || metadata.creation_timestamp || metadata[""];
4578
4421
  }
4422
+ } 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")) {
4423
+ cycleTimeSeconds = null;
4579
4424
  }
4580
4425
  const cloudfrontPlaylistUrl = this.s3UriToCloudfront(uri);
4581
4426
  const { type: videoType, timestamp: videoTimestamp } = parsedInfo;
@@ -4647,7 +4492,8 @@ var S3ClipsService = class {
4647
4492
  date,
4648
4493
  shiftId.toString(),
4649
4494
  includeCycleTime || false,
4650
- includeMetadata || (!!timestampStart || !!timestampEnd)
4495
+ includeMetadata || false
4496
+ // Never fetch metadata for timestamp filtering to prevent flooding
4651
4497
  );
4652
4498
  });
4653
4499
  const videoResults = await Promise.all(videoPromises);
@@ -4718,6 +4564,7 @@ var VideoPrefetchManager = class extends events.EventEmitter {
4718
4564
  }
4719
4565
  /**
4720
4566
  * Get or create S3 service instance for dashboard config
4567
+ * Public method to allow sharing the same S3Service instance across components
4721
4568
  */
4722
4569
  getS3Service(dashboardConfig) {
4723
4570
  const configKey = JSON.stringify(dashboardConfig.s3Config);
@@ -4787,75 +4634,80 @@ var VideoPrefetchManager = class extends events.EventEmitter {
4787
4634
  * Perform the actual prefetch work
4788
4635
  */
4789
4636
  async performPrefetchWork(key, params, s3Service, buildIndex) {
4790
- const cacheKey = `clip-counts:${params.workspaceId}:${params.date}:${params.shift}`;
4791
- const cachedResult = await smartVideoCache.getClipCounts(cacheKey);
4792
- if (cachedResult) {
4793
- console.log(`[VideoPrefetchManager] Found cached data for ${key}`);
4794
- this.updateRequestStatus(key, "fully_indexed" /* FULLY_INDEXED */, cachedResult);
4795
- return cachedResult;
4796
- }
4797
- if (buildIndex) {
4798
- const countsOnly = await s3Service.getClipCounts(
4637
+ s3Service.setPrefetchMode(true);
4638
+ try {
4639
+ const cacheKey = `clip-counts:${params.workspaceId}:${params.date}:${params.shift}`;
4640
+ const cachedResult = await smartVideoCache.getClipCounts(cacheKey);
4641
+ if (cachedResult) {
4642
+ console.log(`[VideoPrefetchManager] Found cached data for ${key}`);
4643
+ this.updateRequestStatus(key, "fully_indexed" /* FULLY_INDEXED */, cachedResult);
4644
+ return cachedResult;
4645
+ }
4646
+ if (buildIndex) {
4647
+ const countsOnly = await s3Service.getClipCounts(
4648
+ params.workspaceId,
4649
+ params.date,
4650
+ params.shift
4651
+ );
4652
+ if (typeof countsOnly === "object" && countsOnly !== null) {
4653
+ const renderReadyData = {
4654
+ counts: countsOnly,
4655
+ videoIndex: {
4656
+ byCategory: /* @__PURE__ */ new Map(),
4657
+ allVideos: [],
4658
+ counts: countsOnly,
4659
+ workspaceId: params.workspaceId,
4660
+ date: params.date,
4661
+ shiftId: params.shift,
4662
+ lastUpdated: /* @__PURE__ */ new Date(),
4663
+ _debugId: `vid_RENDER_READY_${Date.now()}_${Math.random().toString(36).substring(7)}`
4664
+ }
4665
+ };
4666
+ this.updateRequestStatus(key, "render_ready" /* RENDER_READY */, renderReadyData);
4667
+ console.log(`[VideoPrefetchManager] Render ready, building full index for ${key}`);
4668
+ }
4669
+ }
4670
+ const fullResult = await s3Service.getClipCountsCacheFirst(
4799
4671
  params.workspaceId,
4800
4672
  params.date,
4801
- params.shift
4673
+ params.shift,
4674
+ true
4802
4675
  );
4803
- if (typeof countsOnly === "object" && countsOnly !== null) {
4804
- const renderReadyData = {
4805
- counts: countsOnly,
4676
+ if (fullResult && typeof fullResult === "object" && "videoIndex" in fullResult) {
4677
+ const clipCountsWithIndex = fullResult;
4678
+ if (clipCountsWithIndex.videoIndex.allVideos.length > 0) {
4679
+ console.log(`[VideoPrefetchManager] Video index properly populated with ${clipCountsWithIndex.videoIndex.allVideos.length} videos`);
4680
+ } else {
4681
+ console.warn(`[VideoPrefetchManager] Video index is empty, but counts available:`, clipCountsWithIndex.counts);
4682
+ }
4683
+ await smartVideoCache.setClipCounts(cacheKey, clipCountsWithIndex, 5 * 60);
4684
+ this.updateRequestStatus(key, "fully_indexed" /* FULLY_INDEXED */, clipCountsWithIndex);
4685
+ console.log(`[VideoPrefetchManager] Fully indexed: ${key} (${clipCountsWithIndex.videoIndex.allVideos.length} videos)`);
4686
+ return clipCountsWithIndex;
4687
+ } else if (fullResult && typeof fullResult === "object") {
4688
+ console.log(`[VideoPrefetchManager] Received counts only, building fallback data structure`);
4689
+ const countsResult = fullResult;
4690
+ const fallbackData = {
4691
+ counts: countsResult,
4806
4692
  videoIndex: {
4807
4693
  byCategory: /* @__PURE__ */ new Map(),
4808
4694
  allVideos: [],
4809
- counts: countsOnly,
4695
+ counts: countsResult,
4810
4696
  workspaceId: params.workspaceId,
4811
4697
  date: params.date,
4812
4698
  shiftId: params.shift,
4813
4699
  lastUpdated: /* @__PURE__ */ new Date(),
4814
- _debugId: `vid_RENDER_READY_${Date.now()}_${Math.random().toString(36).substring(7)}`
4700
+ _debugId: `vid_FALLBACK_${Date.now()}_${Math.random().toString(36).substring(7)}`
4815
4701
  }
4816
4702
  };
4817
- this.updateRequestStatus(key, "render_ready" /* RENDER_READY */, renderReadyData);
4818
- console.log(`[VideoPrefetchManager] Render ready, building full index for ${key}`);
4819
- }
4820
- }
4821
- const fullResult = await s3Service.getClipCountsCacheFirst(
4822
- params.workspaceId,
4823
- params.date,
4824
- params.shift,
4825
- true
4826
- );
4827
- if (fullResult && typeof fullResult === "object" && "videoIndex" in fullResult) {
4828
- const clipCountsWithIndex = fullResult;
4829
- if (clipCountsWithIndex.videoIndex.allVideos.length > 0) {
4830
- console.log(`[VideoPrefetchManager] Video index properly populated with ${clipCountsWithIndex.videoIndex.allVideos.length} videos`);
4703
+ this.updateRequestStatus(key, "render_ready" /* RENDER_READY */, fallbackData);
4704
+ return fallbackData;
4831
4705
  } else {
4832
- console.warn(`[VideoPrefetchManager] Video index is empty, but counts available:`, clipCountsWithIndex.counts);
4833
- }
4834
- await smartVideoCache.setClipCounts(cacheKey, clipCountsWithIndex, 5 * 60);
4835
- this.updateRequestStatus(key, "fully_indexed" /* FULLY_INDEXED */, clipCountsWithIndex);
4836
- console.log(`[VideoPrefetchManager] Fully indexed: ${key} (${clipCountsWithIndex.videoIndex.allVideos.length} videos)`);
4837
- return clipCountsWithIndex;
4838
- } else if (fullResult && typeof fullResult === "object") {
4839
- console.log(`[VideoPrefetchManager] Received counts only, building fallback data structure`);
4840
- const countsResult = fullResult;
4841
- const fallbackData = {
4842
- counts: countsResult,
4843
- videoIndex: {
4844
- byCategory: /* @__PURE__ */ new Map(),
4845
- allVideos: [],
4846
- counts: countsResult,
4847
- workspaceId: params.workspaceId,
4848
- date: params.date,
4849
- shiftId: params.shift,
4850
- lastUpdated: /* @__PURE__ */ new Date(),
4851
- _debugId: `vid_FALLBACK_${Date.now()}_${Math.random().toString(36).substring(7)}`
4852
- }
4853
- };
4854
- this.updateRequestStatus(key, "render_ready" /* RENDER_READY */, fallbackData);
4855
- return fallbackData;
4856
- } else {
4857
- console.error(`[VideoPrefetchManager] Received null/undefined result from S3 service`);
4858
- throw new Error("Failed to fetch clip counts from S3 service");
4706
+ console.error(`[VideoPrefetchManager] Received null/undefined result from S3 service`);
4707
+ throw new Error("Failed to fetch clip counts from S3 service");
4708
+ }
4709
+ } finally {
4710
+ s3Service.setPrefetchMode(false);
4859
4711
  }
4860
4712
  }
4861
4713
  /**
@@ -5118,9 +4970,13 @@ var AuthProvider = ({ children }) => {
5118
4970
  const [loading, setLoading] = React19.useState(true);
5119
4971
  const [error, setError] = React19.useState(null);
5120
4972
  const router$1 = router.useRouter();
5121
- const userProfileTable = authConfig?.userProfileTable;
5122
- const roleColumn = authConfig?.roleColumn || "role";
4973
+ authConfig?.userProfileTable;
4974
+ authConfig?.roleColumn || "role";
5123
4975
  const fetchUserDetails = React19.useCallback(async (supabaseUser) => {
4976
+ console.log("[fetchUserDetails] Called for user:", supabaseUser.id, {
4977
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4978
+ stackTrace: new Error().stack?.split("\n").slice(1, 4).join(" -> ")
4979
+ });
5124
4980
  if (!supabaseUser) return null;
5125
4981
  const basicUser = {
5126
4982
  id: supabaseUser.id,
@@ -5129,42 +4985,34 @@ var AuthProvider = ({ children }) => {
5129
4985
  if (!supabase) return basicUser;
5130
4986
  try {
5131
4987
  const timeoutPromise = new Promise(
5132
- (_, reject) => setTimeout(() => reject(new Error("Profile fetch timeout")), 5e3)
4988
+ (_, reject) => setTimeout(() => {
4989
+ console.log("[fetchUserDetails] Timeout triggered after 2 seconds");
4990
+ reject(new Error("Profile fetch timeout"));
4991
+ }, 2e3)
5133
4992
  );
5134
4993
  const rolePromise = supabase.from("user_roles").select("role_level").eq("user_id", supabaseUser.id).single();
5135
- let profilePromise = null;
5136
- if (userProfileTable) {
5137
- profilePromise = supabase.from(userProfileTable).select(roleColumn).eq("id", supabaseUser.id).single();
5138
- }
5139
- const [roleResult, profileResult] = await Promise.race([
5140
- Promise.all([
5141
- rolePromise,
5142
- profilePromise || Promise.resolve(null)
5143
- ]),
5144
- timeoutPromise.then(() => {
5145
- throw new Error("Timeout");
5146
- })
4994
+ const roleResult = await Promise.race([
4995
+ rolePromise,
4996
+ timeoutPromise
4997
+ // Fixed: removed .then() which was causing the bug
5147
4998
  ]);
5148
4999
  let roleLevel = void 0;
5149
5000
  if (roleResult && !roleResult.error && roleResult.data) {
5150
5001
  roleLevel = roleResult.data.role_level;
5151
- } else if (roleResult?.error) {
5002
+ } else if (roleResult?.error && roleResult.error.code !== "PGRST116") {
5152
5003
  console.log("Error fetching role_level:", roleResult.error.message);
5153
5004
  }
5154
- let roleValue = void 0;
5155
- if (profileResult && !profileResult.error && profileResult.data) {
5156
- roleValue = profileResult.data[roleColumn];
5157
- }
5158
5005
  return {
5159
5006
  ...basicUser,
5160
- role: roleValue,
5161
5007
  role_level: roleLevel
5162
5008
  };
5163
5009
  } catch (err) {
5164
- console.error("Error fetching user details:", err);
5010
+ if (err instanceof Error && err.message.includes("timeout")) {
5011
+ console.warn("Auth fetch timeout - using basic user info");
5012
+ }
5165
5013
  return basicUser;
5166
5014
  }
5167
- }, [supabase, userProfileTable, roleColumn]);
5015
+ }, [supabase]);
5168
5016
  React19.useEffect(() => {
5169
5017
  if (!supabase) return;
5170
5018
  let mounted = true;
@@ -5175,6 +5023,7 @@ var AuthProvider = ({ children }) => {
5175
5023
  }
5176
5024
  }, 1e4);
5177
5025
  const initializeAuth = async () => {
5026
+ const startTime = performance.now();
5178
5027
  try {
5179
5028
  const { data: { session: initialSession }, error: sessionError } = await supabase.auth.getSession();
5180
5029
  if (!mounted) return;
@@ -5217,12 +5066,38 @@ var AuthProvider = ({ children }) => {
5217
5066
  if (mounted) {
5218
5067
  setLoading(false);
5219
5068
  clearTimeout(safetyTimeout);
5069
+ const duration = performance.now() - startTime;
5070
+ if (duration > 3e3) {
5071
+ console.warn(`[Auth] Initialization took ${duration.toFixed(0)}ms - consider optimization`);
5072
+ } else if (process.env.NODE_ENV === "development") {
5073
+ console.log(`[Auth] Initialized in ${duration.toFixed(0)}ms`);
5074
+ }
5220
5075
  }
5221
5076
  }
5222
5077
  };
5223
5078
  initializeAuth();
5224
- const { data: { subscription } } = supabase.auth.onAuthStateChange(async (_event, currentSession) => {
5079
+ const { data: { subscription } } = supabase.auth.onAuthStateChange(async (event, currentSession) => {
5225
5080
  if (!mounted) return;
5081
+ console.log("[AuthContext] Auth event fired:", {
5082
+ event,
5083
+ sessionId: currentSession?.user?.id,
5084
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
5085
+ currentPath: typeof window !== "undefined" ? window.location.pathname : "unknown"
5086
+ });
5087
+ if (event === "TOKEN_REFRESHED") {
5088
+ if (session?.user?.id === currentSession?.user?.id && session?.user?.email === currentSession?.user?.email) {
5089
+ console.log("[AuthContext] Skipping TOKEN_REFRESHED - session unchanged");
5090
+ return;
5091
+ }
5092
+ }
5093
+ if (event !== "TOKEN_REFRESHED" && currentSession?.user) {
5094
+ console.log("[AuthContext] Non-TOKEN_REFRESHED event will trigger fetchUserDetails:", event);
5095
+ }
5096
+ const sessionChanged = session?.user?.id !== currentSession?.user?.id || session?.user?.email !== currentSession?.user?.email;
5097
+ if (!sessionChanged && user) {
5098
+ console.log("[AuthContext] Session and user unchanged, skipping update");
5099
+ return;
5100
+ }
5226
5101
  setSession(currentSession);
5227
5102
  setUser(null);
5228
5103
  setLoading(false);
@@ -5597,7 +5472,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5597
5472
  const [error, setError] = React19.useState(null);
5598
5473
  const updateQueueRef = React19.useRef(false);
5599
5474
  const isFetchingRef = React19.useRef(false);
5600
- const timeoutRef = React19.useRef(null);
5601
5475
  const channelRef = React19.useRef(null);
5602
5476
  const schema = databaseConfig.schema ?? "public";
5603
5477
  const companyId = entityConfig.companyId || "";
@@ -5606,7 +5480,7 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5606
5480
  const defaultTimezone = dateTimeConfig.defaultTimezone;
5607
5481
  const workspaceMetricsBaseTable = databaseConfig.tables?.workspaces ?? "workspace_metrics";
5608
5482
  const workspaceActionsTable = databaseConfig.tables?.actions ?? "workspace_actions";
5609
- const fetchMetrics = React19.useCallback(async (skipCache = false) => {
5483
+ const fetchMetrics = React19.useCallback(async () => {
5610
5484
  if (!workspaceId || isFetchingRef.current) return;
5611
5485
  try {
5612
5486
  isFetchingRef.current = true;
@@ -5615,28 +5489,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5615
5489
  const queryShiftId = shiftId !== void 0 ? shiftId : currentShift.shiftId;
5616
5490
  console.log("[useWorkspaceDetailedMetrics] Using shift ID:", queryShiftId, "from input shift:", shiftId);
5617
5491
  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
5492
  console.log(`[useWorkspaceDetailedMetrics] Querying ${metricsTable} for workspace: ${workspaceId}, date: ${queryDate}, shift: ${queryShiftId}`);
5641
5493
  const { data, error: fetchError } = await supabase.from(metricsTable).select("*").eq("workspace_id", workspaceId).eq("date", queryDate).eq("shift_id", queryShiftId).maybeSingle();
5642
5494
  if (fetchError) throw fetchError;
@@ -5739,18 +5591,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5739
5591
  setIsLoading(false);
5740
5592
  updateQueueRef.current = false;
5741
5593
  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
5594
  return;
5755
5595
  } else {
5756
5596
  console.warn("[useWorkspaceDetailedMetrics] No data found for workspace:", workspaceId, "at all");
@@ -5864,11 +5704,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5864
5704
  ...data.sop_check !== void 0 && { sop_check: data.sop_check }
5865
5705
  };
5866
5706
  setMetrics(transformedData);
5867
- cacheService.set(cacheKey, transformedData, {
5868
- storage: "memory",
5869
- duration: 5 * 60 * 1e3
5870
- // 5 minutes
5871
- });
5872
5707
  } catch (err) {
5873
5708
  console.error("Error fetching workspace metrics:", err);
5874
5709
  setError({ message: err.message, code: err.code });
@@ -5881,12 +5716,7 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5881
5716
  const queueUpdate = React19.useCallback(() => {
5882
5717
  if (!workspaceId || updateQueueRef.current) return;
5883
5718
  updateQueueRef.current = true;
5884
- if (timeoutRef.current) {
5885
- clearTimeout(timeoutRef.current);
5886
- }
5887
- timeoutRef.current = setTimeout(() => {
5888
- fetchMetrics();
5889
- }, 500);
5719
+ fetchMetrics();
5890
5720
  }, [fetchMetrics, workspaceId]);
5891
5721
  const setupSubscription = React19.useCallback(() => {
5892
5722
  if (!workspaceId) return;
@@ -5903,7 +5733,7 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5903
5733
  },
5904
5734
  async (payload) => {
5905
5735
  console.log(`Received ${metricsTablePrefix} update:`, payload);
5906
- await fetchMetrics(true);
5736
+ await fetchMetrics();
5907
5737
  }
5908
5738
  ).subscribe((status) => {
5909
5739
  console.log(`Workspace detailed metrics subscription status:`, status);
@@ -5937,14 +5767,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5937
5767
  matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
5938
5768
  });
5939
5769
  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
5770
  queueUpdate();
5949
5771
  }
5950
5772
  }
@@ -5970,14 +5792,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5970
5792
  matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
5971
5793
  });
5972
5794
  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
5795
  queueUpdate();
5982
5796
  }
5983
5797
  }
@@ -6003,14 +5817,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
6003
5817
  matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
6004
5818
  });
6005
5819
  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
5820
  queueUpdate();
6015
5821
  }
6016
5822
  }
@@ -6021,9 +5827,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
6021
5827
  fetchMetrics();
6022
5828
  setupSubscription();
6023
5829
  return () => {
6024
- if (timeoutRef.current) {
6025
- clearTimeout(timeoutRef.current);
6026
- }
6027
5830
  channels.forEach((channel) => {
6028
5831
  console.log("Cleaning up channel subscription");
6029
5832
  supabase.removeChannel(channel);
@@ -6037,7 +5840,7 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
6037
5840
  metrics: metrics2,
6038
5841
  isLoading,
6039
5842
  error,
6040
- refetch: () => fetchMetrics(true)
5843
+ refetch: () => fetchMetrics()
6041
5844
  // Force refresh without cache
6042
5845
  };
6043
5846
  };
@@ -6482,33 +6285,6 @@ var useLeaderboardMetrics = (lineId, topCount = 10) => {
6482
6285
  refetch: fetchLeaderboardData
6483
6286
  };
6484
6287
  };
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
6288
  var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6513
6289
  const { supabaseUrl, supabaseKey } = useDashboardConfig();
6514
6290
  const entityConfig = useEntityConfig();
@@ -6519,22 +6295,20 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6519
6295
  const configuredLineMetricsTable = databaseConfig?.tables?.lineMetrics ?? "line_metrics";
6520
6296
  const schema = databaseConfig?.schema ?? "public";
6521
6297
  const supabase = useSupabase();
6522
- const [metrics2, setMetrics] = React19.useState(() => getCache(lineId) || { workspaceMetrics: [], lineMetrics: [] });
6523
- const [isLoading, setIsLoading] = React19.useState(() => !getCache(lineId));
6298
+ const [metrics2, setMetrics] = React19.useState({ workspaceMetrics: [], lineMetrics: [] });
6299
+ const [isLoading, setIsLoading] = React19.useState(true);
6524
6300
  const [error, setError] = React19.useState(null);
6525
6301
  const lineIdRef = React19.useRef(lineId);
6526
6302
  const isFetchingRef = React19.useRef(false);
6527
6303
  const updateQueueRef = React19.useRef(false);
6528
- const timeoutRef = React19.useRef(null);
6529
6304
  const companySpecificMetricsTable = React19.useMemo(
6530
6305
  () => getCompanyMetricsTableName(entityConfig.companyId, "performance_metrics"),
6531
6306
  [entityConfig.companyId]
6532
6307
  );
6533
6308
  React19.useEffect(() => {
6534
6309
  lineIdRef.current = lineId;
6535
- const cachedData = getCache(lineId);
6536
- setMetrics(cachedData || { workspaceMetrics: [], lineMetrics: [] });
6537
- setIsLoading(!cachedData);
6310
+ setMetrics({ workspaceMetrics: [], lineMetrics: [] });
6311
+ setIsLoading(true);
6538
6312
  }, [lineId]);
6539
6313
  const fetchAllMetrics = React19.useCallback(async () => {
6540
6314
  const currentLineIdToUse = lineIdRef.current;
@@ -6546,9 +6320,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6546
6320
  return;
6547
6321
  }
6548
6322
  isFetchingRef.current = true;
6549
- if (!getCache(currentLineIdToUse)) {
6550
- setIsLoading(true);
6551
- }
6323
+ setIsLoading(true);
6552
6324
  setError(null);
6553
6325
  try {
6554
6326
  const currentShiftDetails = getCurrentShift(defaultTimezone, shiftConfig);
@@ -6573,7 +6345,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6573
6345
  lineMetrics: []
6574
6346
  };
6575
6347
  setMetrics(newMetricsState2);
6576
- setCache(currentLineIdToUse, newMetricsState2);
6577
6348
  return;
6578
6349
  }
6579
6350
  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 +6396,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6625
6396
  lineMetrics: lineData || []
6626
6397
  };
6627
6398
  setMetrics(newMetricsState);
6628
- setCache(currentLineIdToUse, newMetricsState);
6629
6399
  } catch (err) {
6630
6400
  setError({ message: err.message, code: err.code || "FETCH_ERROR" });
6631
6401
  } finally {
@@ -6651,12 +6421,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6651
6421
  return;
6652
6422
  }
6653
6423
  updateQueueRef.current = true;
6654
- if (timeoutRef.current) {
6655
- clearTimeout(timeoutRef.current);
6656
- }
6657
- timeoutRef.current = setTimeout(() => {
6658
- fetchAllMetrics();
6659
- }, 500);
6424
+ fetchAllMetrics();
6660
6425
  }, [fetchAllMetrics, supabase]);
6661
6426
  React19.useEffect(() => {
6662
6427
  if (lineId && supabase) {
@@ -6701,9 +6466,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6701
6466
  onLineMetricsUpdate?.();
6702
6467
  });
6703
6468
  return () => {
6704
- if (timeoutRef.current) {
6705
- clearTimeout(timeoutRef.current);
6706
- }
6707
6469
  channels.forEach((channel) => {
6708
6470
  supabase?.removeChannel(channel).catch((err) => console.error("[useDashboardMetrics] Error removing channel:", err.message));
6709
6471
  });
@@ -6732,30 +6494,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6732
6494
  refetch: fetchAllMetrics
6733
6495
  };
6734
6496
  };
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
6497
  var useLineKPIs = ({ lineId }) => {
6760
6498
  useDashboardConfig();
6761
6499
  const entityConfig = useEntityConfig();
@@ -6767,13 +6505,12 @@ var useLineKPIs = ({ lineId }) => {
6767
6505
  const dashboardServiceInstance = React19.useMemo(() => {
6768
6506
  return dashboardService;
6769
6507
  }, []);
6770
- const [kpis, setKPIs] = React19.useState(() => getCache2(lineId));
6771
- const [isLoading, setIsLoading] = React19.useState(!getCache2(lineId));
6508
+ const [kpis, setKPIs] = React19.useState(null);
6509
+ const [isLoading, setIsLoading] = React19.useState(true);
6772
6510
  const [error, setError] = React19.useState(null);
6773
6511
  const lineIdRef = React19.useRef(lineId);
6774
6512
  const isFetchingRef = React19.useRef(false);
6775
6513
  const updateQueueRef = React19.useRef(false);
6776
- const timeoutRef = React19.useRef(null);
6777
6514
  const defaultTimezone = dateTimeConfig.defaultTimezone;
6778
6515
  const schema = databaseConfig.schema ?? "public";
6779
6516
  const lineMetricsTable = databaseConfig.tables?.lineMetrics ?? "line_metrics";
@@ -6804,7 +6541,6 @@ var useLineKPIs = ({ lineId }) => {
6804
6541
  if (lineInfo) {
6805
6542
  const newKPIs = dashboardServiceInstance.calculateKPIs(lineInfo);
6806
6543
  setKPIs(newKPIs);
6807
- setCache2(currentLineId, newKPIs);
6808
6544
  } else {
6809
6545
  setKPIs(null);
6810
6546
  }
@@ -6821,12 +6557,7 @@ var useLineKPIs = ({ lineId }) => {
6821
6557
  const queueUpdate = React19.useCallback(() => {
6822
6558
  if (updateQueueRef.current) return;
6823
6559
  updateQueueRef.current = true;
6824
- if (timeoutRef.current) {
6825
- clearTimeout(timeoutRef.current);
6826
- }
6827
- timeoutRef.current = setTimeout(() => {
6828
- fetchKPIs();
6829
- }, 500);
6560
+ fetchKPIs();
6830
6561
  }, [fetchKPIs]);
6831
6562
  React19.useEffect(() => {
6832
6563
  const currentLineId = lineIdRef.current;
@@ -6884,9 +6615,6 @@ var useLineKPIs = ({ lineId }) => {
6884
6615
  activeChannels.push(csChannel);
6885
6616
  }
6886
6617
  return () => {
6887
- if (timeoutRef.current) {
6888
- clearTimeout(timeoutRef.current);
6889
- }
6890
6618
  activeChannels.forEach((ch) => supabase.removeChannel(ch).catch((err) => console.error("[useLineKPIs] Error removing KPI channel:", err)));
6891
6619
  };
6892
6620
  }, [supabase, lineId, fetchKPIs, queueUpdate, dashboardServiceInstance, entityConfig, schema, lineMetricsTable, companySpecificMetricsTable, defaultTimezone, shiftConfig, kpis, isFactoryView]);
@@ -6915,7 +6643,6 @@ var useRealtimeLineMetrics = ({
6915
6643
  const [initialized, setInitialized] = React19.useState(false);
6916
6644
  const lineIdRef = React19.useRef(null);
6917
6645
  const updateQueueRef = React19.useRef(false);
6918
- const timeoutRef = React19.useRef(null);
6919
6646
  const isFetchingRef = React19.useRef(false);
6920
6647
  const channelsRef = React19.useRef([]);
6921
6648
  const currentShift = React19.useMemo(() => getCurrentShift(dateTimeConfig.defaultTimezone || "Asia/Kolkata", shiftConfig), [dateTimeConfig.defaultTimezone, shiftConfig]);
@@ -7128,12 +6855,7 @@ var useRealtimeLineMetrics = ({
7128
6855
  const queueUpdate = React19.useCallback(() => {
7129
6856
  if (updateQueueRef.current) return;
7130
6857
  updateQueueRef.current = true;
7131
- if (timeoutRef.current) {
7132
- clearTimeout(timeoutRef.current);
7133
- }
7134
- timeoutRef.current = setTimeout(() => {
7135
- fetchData();
7136
- }, 500);
6858
+ fetchData();
7137
6859
  }, [fetchData]);
7138
6860
  const setupSubscriptions = React19.useCallback(() => {
7139
6861
  if (channelsRef.current.length > 0) {
@@ -7211,9 +6933,6 @@ var useRealtimeLineMetrics = ({
7211
6933
  }
7212
6934
  setupSubscriptions();
7213
6935
  return () => {
7214
- if (timeoutRef.current) {
7215
- clearTimeout(timeoutRef.current);
7216
- }
7217
6936
  if (channelsRef.current.length > 0) {
7218
6937
  channelsRef.current.forEach((channel) => {
7219
6938
  if (process.env.NODE_ENV === "development") {
@@ -7299,16 +7018,12 @@ var useTargets = (options) => {
7299
7018
  };
7300
7019
  var DEFAULT_SHIFTS_TABLE_NAME = "shift_configurations";
7301
7020
  var useShifts = () => {
7302
- const { supabaseUrl, supabaseKey } = useDashboardConfig();
7303
7021
  const { companyId } = useEntityConfig();
7304
7022
  const { tables } = useDatabaseConfig();
7023
+ const supabase = useSupabase();
7305
7024
  const [shifts, setShifts] = React19.useState([]);
7306
7025
  const [isLoading, setIsLoading] = React19.useState(true);
7307
7026
  const [error, setError] = React19.useState(null);
7308
- const supabase = React19.useMemo(() => {
7309
- if (!supabaseUrl || !supabaseKey) return null;
7310
- return supabaseJs.createClient(supabaseUrl, supabaseKey);
7311
- }, [supabaseUrl, supabaseKey]);
7312
7027
  const shiftsTable = tables?.shiftConfigurations || DEFAULT_SHIFTS_TABLE_NAME;
7313
7028
  const fetchData = React19.useCallback(async () => {
7314
7029
  if (!supabase) {
@@ -8007,6 +7722,7 @@ var runtimeWorkspaceDisplayNames = {};
8007
7722
  var isInitialized = false;
8008
7723
  var isInitializing = false;
8009
7724
  var initializedWithLineIds = [];
7725
+ var initializationPromise = null;
8010
7726
  function getCurrentLineIds() {
8011
7727
  try {
8012
7728
  const config = _getDashboardConfigInstance();
@@ -8027,52 +7743,79 @@ function getCurrentLineIds() {
8027
7743
  }
8028
7744
  async function initializeWorkspaceDisplayNames(explicitLineId) {
8029
7745
  console.log("\u{1F504} initializeWorkspaceDisplayNames called", { isInitialized, isInitializing, explicitLineId });
8030
- if (isInitialized || isInitializing) return;
8031
- isInitializing = true;
8032
- try {
8033
- console.log("\u{1F504} Starting Supabase workspace display names initialization...");
8034
- let targetLineIds = [];
8035
- if (explicitLineId) {
8036
- targetLineIds = [explicitLineId];
8037
- } else {
8038
- targetLineIds = getCurrentLineIds();
7746
+ if (isInitialized) return;
7747
+ if (initializationPromise) {
7748
+ console.log("\u{1F504} Already initializing, waiting for existing initialization...");
7749
+ await initializationPromise;
7750
+ return;
7751
+ }
7752
+ initializationPromise = (async () => {
7753
+ isInitializing = true;
7754
+ try {
7755
+ console.log("\u{1F504} Starting Supabase workspace display names initialization...");
7756
+ let targetLineIds = [];
7757
+ if (explicitLineId) {
7758
+ targetLineIds = [explicitLineId];
7759
+ } else {
7760
+ targetLineIds = getCurrentLineIds();
7761
+ }
7762
+ console.log("\u{1F504} Target line IDs for workspace filtering:", targetLineIds);
7763
+ runtimeWorkspaceDisplayNames = {};
7764
+ if (targetLineIds.length > 0) {
7765
+ for (const lineId of targetLineIds) {
7766
+ console.log(`\u{1F504} Fetching workspaces for line: ${lineId}`);
7767
+ const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
7768
+ runtimeWorkspaceDisplayNames[lineId] = {};
7769
+ lineDisplayNamesMap.forEach((displayName, workspaceId) => {
7770
+ runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
7771
+ });
7772
+ console.log(`\u2705 Stored ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
7773
+ }
7774
+ } else {
7775
+ console.warn("\u26A0\uFE0F No line IDs found, fetching all workspaces (less efficient)");
7776
+ const allWorkspacesMap = await workspaceService.getWorkspaceDisplayNames();
7777
+ runtimeWorkspaceDisplayNames["global"] = {};
7778
+ allWorkspacesMap.forEach((displayName, workspaceId) => {
7779
+ runtimeWorkspaceDisplayNames["global"][workspaceId] = displayName;
7780
+ });
7781
+ }
7782
+ isInitialized = true;
7783
+ initializedWithLineIds = targetLineIds;
7784
+ console.log("\u2705 Workspace display names initialized from Supabase:", runtimeWorkspaceDisplayNames);
7785
+ console.log("\u2705 Initialized with line IDs:", initializedWithLineIds);
7786
+ } catch (error) {
7787
+ console.error("\u274C Failed to initialize workspace display names from Supabase:", error);
7788
+ } finally {
7789
+ isInitializing = false;
7790
+ initializationPromise = null;
8039
7791
  }
8040
- console.log("\u{1F504} Target line IDs for workspace filtering:", targetLineIds);
8041
- runtimeWorkspaceDisplayNames = {};
8042
- if (targetLineIds.length > 0) {
8043
- for (const lineId of targetLineIds) {
8044
- console.log(`\u{1F504} Fetching workspaces for line: ${lineId}`);
7792
+ })();
7793
+ await initializationPromise;
7794
+ }
7795
+ var preInitializeWorkspaceDisplayNames = async (lineId) => {
7796
+ console.log("\u{1F504} preInitializeWorkspaceDisplayNames called for lineId:", lineId);
7797
+ if (isInitialized) {
7798
+ console.log("\u{1F504} Already initialized");
7799
+ if (lineId && !runtimeWorkspaceDisplayNames[lineId]) {
7800
+ console.log(`\u{1F504} Line ${lineId} not in cache, fetching...`);
7801
+ try {
8045
7802
  const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
8046
7803
  runtimeWorkspaceDisplayNames[lineId] = {};
8047
7804
  lineDisplayNamesMap.forEach((displayName, workspaceId) => {
8048
7805
  runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
8049
7806
  });
8050
- console.log(`\u2705 Stored ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
7807
+ console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
7808
+ } catch (error) {
7809
+ console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
8051
7810
  }
8052
- } else {
8053
- console.warn("\u26A0\uFE0F No line IDs found, fetching all workspaces (less efficient)");
8054
- const allWorkspacesMap = await workspaceService.getWorkspaceDisplayNames();
8055
- runtimeWorkspaceDisplayNames["global"] = {};
8056
- allWorkspacesMap.forEach((displayName, workspaceId) => {
8057
- runtimeWorkspaceDisplayNames["global"][workspaceId] = displayName;
8058
- });
8059
7811
  }
8060
- isInitialized = true;
8061
- initializedWithLineIds = targetLineIds;
8062
- console.log("\u2705 Workspace display names initialized from Supabase:", runtimeWorkspaceDisplayNames);
8063
- console.log("\u2705 Initialized with line IDs:", initializedWithLineIds);
8064
- } catch (error) {
8065
- console.error("\u274C Failed to initialize workspace display names from Supabase:", error);
8066
- } finally {
8067
- isInitializing = false;
7812
+ return;
8068
7813
  }
8069
- }
8070
- var preInitializeWorkspaceDisplayNames = async (lineId) => {
8071
- console.log("\u{1F504} preInitializeWorkspaceDisplayNames called for lineId:", lineId);
8072
- if (isInitialized || isInitializing) {
8073
- console.log("\u{1F504} Already initialized or initializing");
7814
+ if (initializationPromise) {
7815
+ console.log("\u{1F504} Already initializing, waiting for completion...");
7816
+ await initializationPromise;
8074
7817
  if (lineId && !runtimeWorkspaceDisplayNames[lineId]) {
8075
- console.log(`\u{1F504} Line ${lineId} not in cache, fetching...`);
7818
+ console.log(`\u{1F504} Line ${lineId} not in cache after init, fetching...`);
8076
7819
  try {
8077
7820
  const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
8078
7821
  runtimeWorkspaceDisplayNames[lineId] = {};
@@ -8090,7 +7833,12 @@ var preInitializeWorkspaceDisplayNames = async (lineId) => {
8090
7833
  };
8091
7834
  var forceRefreshWorkspaceDisplayNames = async (lineId) => {
8092
7835
  console.log("\u{1F504} forceRefreshWorkspaceDisplayNames called for lineId:", lineId);
7836
+ if (initializationPromise) {
7837
+ console.log("\u{1F504} Waiting for existing initialization to complete before refresh...");
7838
+ await initializationPromise;
7839
+ }
8093
7840
  clearWorkspaceDisplayNamesCache();
7841
+ initializationPromise = null;
8094
7842
  await initializeWorkspaceDisplayNames(lineId);
8095
7843
  };
8096
7844
  console.log("\u{1F504} Module loaded, will initialize lazily when first function is called");
@@ -8238,6 +7986,7 @@ var clearWorkspaceDisplayNamesCache = () => {
8238
7986
  isInitialized = false;
8239
7987
  isInitializing = false;
8240
7988
  initializedWithLineIds = [];
7989
+ initializationPromise = null;
8241
7990
  };
8242
7991
 
8243
7992
  // src/lib/hooks/useWorkspaceDisplayNames.ts
@@ -8501,32 +8250,12 @@ var useAllWorkspaceMetrics = (options) => {
8501
8250
  return `${metricsTablePrefix}_${companyId.replace(/-/g, "_")}`;
8502
8251
  }, [entityConfig.companyId]);
8503
8252
  const schema = databaseConfig.schema ?? "public";
8504
- const fetchWorkspaceMetrics = React19.useCallback(async (skipCache = false) => {
8253
+ const fetchWorkspaceMetrics = React19.useCallback(async () => {
8505
8254
  if (!initialized) {
8506
8255
  setLoading(true);
8507
8256
  }
8508
8257
  setError(null);
8509
8258
  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
8259
  console.log("Fetching all workspace metrics with params:", {
8531
8260
  queryDate,
8532
8261
  queryShiftId,
@@ -8576,11 +8305,6 @@ var useAllWorkspaceMetrics = (options) => {
8576
8305
  }));
8577
8306
  setWorkspaces(transformedData);
8578
8307
  setInitialized(true);
8579
- cacheService.set(cacheKey, transformedData, {
8580
- storage: "memory",
8581
- duration: 5 * 60 * 1e3
8582
- // 5 minutes
8583
- });
8584
8308
  } catch (err) {
8585
8309
  console.error("Error fetching all workspace metrics:", err);
8586
8310
  setError({ message: err.message, code: err.code || "FETCH_ERROR" });
@@ -8605,14 +8329,7 @@ var useAllWorkspaceMetrics = (options) => {
8605
8329
  },
8606
8330
  async (payload) => {
8607
8331
  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);
8332
+ await fetchWorkspaceMetrics();
8616
8333
  }
8617
8334
  ).subscribe();
8618
8335
  return channel2;
@@ -8627,7 +8344,7 @@ var useAllWorkspaceMetrics = (options) => {
8627
8344
  React19.useEffect(() => {
8628
8345
  setInitialized(false);
8629
8346
  }, [queryDate, queryShiftId]);
8630
- const refreshWorkspaces = React19.useCallback(() => fetchWorkspaceMetrics(true), [fetchWorkspaceMetrics]);
8347
+ const refreshWorkspaces = React19.useCallback(() => fetchWorkspaceMetrics(), [fetchWorkspaceMetrics]);
8631
8348
  return React19.useMemo(
8632
8349
  () => ({ workspaces, loading, error, refreshWorkspaces }),
8633
8350
  [workspaces, loading, error, refreshWorkspaces]
@@ -19910,11 +19627,13 @@ var withAuth = (WrappedComponent2, options) => {
19910
19627
  requireAuth: true,
19911
19628
  ...options
19912
19629
  };
19913
- return function WithAuthComponent(props) {
19630
+ const WithAuthComponent = React19__namespace.memo(function WithAuthComponent2(props) {
19914
19631
  const { session, loading } = useAuth();
19915
19632
  const router$1 = router.useRouter();
19916
19633
  React19__namespace.useEffect(() => {
19917
- console.log("withAuth state:", { loading, hasSession: !!session, requireAuth: defaultOptions.requireAuth });
19634
+ if (process.env.NODE_ENV === "development" && process.env.DEBUG_AUTH === "true") {
19635
+ console.log("withAuth state:", { loading, hasSession: !!session, requireAuth: defaultOptions.requireAuth });
19636
+ }
19918
19637
  }, [session, loading]);
19919
19638
  React19__namespace.useEffect(() => {
19920
19639
  if (!loading && defaultOptions.requireAuth && !session) {
@@ -19929,7 +19648,9 @@ var withAuth = (WrappedComponent2, options) => {
19929
19648
  return null;
19930
19649
  }
19931
19650
  return /* @__PURE__ */ jsxRuntime.jsx(WrappedComponent2, { ...props });
19932
- };
19651
+ });
19652
+ WithAuthComponent.displayName = `withAuth(${WrappedComponent2.displayName || WrappedComponent2.name || "Component"})`;
19653
+ return WithAuthComponent;
19933
19654
  };
19934
19655
  var LoginPage = ({
19935
19656
  onRateLimitCheck,
@@ -26517,7 +26238,7 @@ var BottlenecksContent = ({
26517
26238
  const [duration, setDuration] = React19.useState(0);
26518
26239
  const [currentIndex, setCurrentIndex] = React19.useState(0);
26519
26240
  const [activeFilter, setActiveFilter] = React19.useState(initialFilter);
26520
- const previousFilterRef = React19.useRef(initialFilter);
26241
+ const previousFilterRef = React19.useRef("");
26521
26242
  const [allVideos, setAllVideos] = React19.useState([]);
26522
26243
  const [isLoading, setIsLoading] = React19.useState(true);
26523
26244
  const [isCategoryLoading, setIsCategoryLoading] = React19.useState(false);
@@ -26567,7 +26288,7 @@ var BottlenecksContent = ({
26567
26288
  console.warn("S3 configuration not found in dashboard config");
26568
26289
  return null;
26569
26290
  }
26570
- return new S3ClipsService(dashboardConfig);
26291
+ return videoPrefetchManager.getS3Service(dashboardConfig);
26571
26292
  }, [dashboardConfig]);
26572
26293
  const {
26573
26294
  data: prefetchData,
@@ -26635,6 +26356,85 @@ var BottlenecksContent = ({
26635
26356
  }, [workspaceId, date, s3ClipsService, shift, dashboardConfig, updateClipCounts, updateVideoIndex]);
26636
26357
  const loadingCategoryRef = React19.useRef(null);
26637
26358
  const videoRetryCountRef = React19.useRef(0);
26359
+ const loadingVideosRef = React19.useRef(/* @__PURE__ */ new Set());
26360
+ const loadedVideosMapRef = React19.useRef(/* @__PURE__ */ new Map());
26361
+ const ensureVideosLoaded = React19.useCallback(async (centerIndex) => {
26362
+ if (!s3ClipsService || !workspaceId || !isMountedRef.current) return;
26363
+ const currentFilter = activeFilterRef.current;
26364
+ const currentVideoIndex = videoIndexRef.current;
26365
+ let effectiveFilter = currentFilter;
26366
+ if (sopCategories && sopCategories.length > 0) {
26367
+ const category = sopCategories.find((cat) => cat.id === currentFilter);
26368
+ if (category && category.s3FolderName) {
26369
+ effectiveFilter = category.s3FolderName;
26370
+ }
26371
+ }
26372
+ const cacheKey = `${effectiveFilter}:${date}:${shift}`;
26373
+ if (!loadedVideosMapRef.current.has(cacheKey)) {
26374
+ loadedVideosMapRef.current.set(cacheKey, /* @__PURE__ */ new Set());
26375
+ }
26376
+ const loadedIndices = loadedVideosMapRef.current.get(cacheKey);
26377
+ const indicesToLoad = [];
26378
+ const rangeBefore = 1;
26379
+ const rangeAfter = 3;
26380
+ for (let i = Math.max(0, centerIndex - rangeBefore); i <= Math.min(clipCounts[effectiveFilter] - 1, centerIndex + rangeAfter); i++) {
26381
+ if (!loadedIndices.has(i) && !loadingVideosRef.current.has(i)) {
26382
+ indicesToLoad.push(i);
26383
+ }
26384
+ }
26385
+ if (indicesToLoad.length === 0) return;
26386
+ console.log(`[ensureVideosLoaded] Preloading ${indicesToLoad.length} videos around index ${centerIndex}: [${indicesToLoad.join(", ")}]`);
26387
+ indicesToLoad.forEach((idx) => loadingVideosRef.current.add(idx));
26388
+ const loadPromises = indicesToLoad.map(async (index) => {
26389
+ try {
26390
+ let video = null;
26391
+ if (currentVideoIndex && currentVideoIndex.byCategory && currentVideoIndex.allVideos.length > 0) {
26392
+ video = await s3ClipsService.getVideoFromIndex(
26393
+ currentVideoIndex,
26394
+ effectiveFilter,
26395
+ index,
26396
+ true,
26397
+ // includeCycleTime - OK for preloading
26398
+ false
26399
+ // includeMetadata - NO metadata during bulk preloading to prevent flooding
26400
+ );
26401
+ }
26402
+ if (!video) {
26403
+ const operationalDate = date || getOperationalDate();
26404
+ const shiftStr = shift?.toString() || "0";
26405
+ video = await s3ClipsService.getClipByIndex(
26406
+ workspaceId,
26407
+ operationalDate,
26408
+ shiftStr,
26409
+ effectiveFilter,
26410
+ index,
26411
+ true,
26412
+ // includeCycleTime - OK for preloading
26413
+ false
26414
+ // includeMetadata - NO metadata during bulk preloading to prevent flooding
26415
+ );
26416
+ }
26417
+ if (video && isMountedRef.current) {
26418
+ setAllVideos((prev) => {
26419
+ const exists = prev.some((v) => v.id === video.id);
26420
+ if (!exists) {
26421
+ return [...prev, video];
26422
+ }
26423
+ return prev;
26424
+ });
26425
+ loadedIndices.add(index);
26426
+ preloadVideoUrl(video.src);
26427
+ }
26428
+ } catch (error2) {
26429
+ console.warn(`[ensureVideosLoaded] Failed to load video at index ${index}:`, error2);
26430
+ } finally {
26431
+ loadingVideosRef.current.delete(index);
26432
+ }
26433
+ });
26434
+ Promise.all(loadPromises).catch((err) => {
26435
+ console.warn("[ensureVideosLoaded] Some videos failed to preload:", err);
26436
+ });
26437
+ }, [s3ClipsService, workspaceId, clipCounts, sopCategories, date, shift]);
26638
26438
  const loadFirstVideoForCategory = React19.useCallback(async (category) => {
26639
26439
  if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
26640
26440
  const targetCategory = category || activeFilterRef.current;
@@ -26671,8 +26471,16 @@ var BottlenecksContent = ({
26671
26471
  );
26672
26472
  if (firstVideo) {
26673
26473
  console.log(`[BottlenecksContent] Successfully loaded first video via direct S3 method`);
26674
- setAllVideos([firstVideo]);
26474
+ setAllVideos((prev) => {
26475
+ const exists = prev.some((v) => v.id === firstVideo.id);
26476
+ if (!exists) {
26477
+ return [...prev, firstVideo];
26478
+ }
26479
+ return prev;
26480
+ });
26675
26481
  preloadVideoUrl(firstVideo.src);
26482
+ setIsLoading(false);
26483
+ setHasInitialLoad(true);
26676
26484
  loadingCategoryRef.current = null;
26677
26485
  setIsCategoryLoading(false);
26678
26486
  return;
@@ -26695,7 +26503,13 @@ var BottlenecksContent = ({
26695
26503
  );
26696
26504
  if (firstVideo && isMountedRef.current) {
26697
26505
  console.log(`[BottlenecksContent] Successfully loaded first video via video index`);
26698
- setAllVideos([firstVideo]);
26506
+ setAllVideos((prev) => {
26507
+ const exists = prev.some((v) => v.id === firstVideo.id);
26508
+ if (!exists) {
26509
+ return [...prev, firstVideo];
26510
+ }
26511
+ return prev;
26512
+ });
26699
26513
  preloadVideoUrl(firstVideo.src);
26700
26514
  setIsCategoryLoading(false);
26701
26515
  return;
@@ -26720,15 +26534,17 @@ var BottlenecksContent = ({
26720
26534
  }
26721
26535
  }, [workspaceId, date, s3ClipsService, clipCounts, videoIndex, shift, updateClipCounts, updateVideoIndex]);
26722
26536
  React19.useEffect(() => {
26723
- if (s3ClipsService) {
26537
+ if (s3ClipsService && !prefetchData) {
26724
26538
  fetchClipCounts();
26725
26539
  }
26726
- }, [workspaceId, date, shift, s3ClipsService, fetchClipCounts, updateClipCounts, updateVideoIndex]);
26540
+ }, [workspaceId, date, shift, s3ClipsService, fetchClipCounts, updateClipCounts, updateVideoIndex, prefetchData]);
26727
26541
  React19.useEffect(() => {
26728
26542
  if (prefetchData) {
26729
26543
  console.log(`[BottlenecksContent] Received prefetch update - status: ${prefetchStatus}, videos: ${prefetchData.videoIndex.allVideos.length}, ID: ${prefetchData.videoIndex._debugId || "NO_ID"}`);
26730
26544
  updateClipCounts(prefetchData.counts);
26731
- updateVideoIndex(prefetchData.videoIndex);
26545
+ if (prefetchData.videoIndex.allVideos.length > 0) {
26546
+ updateVideoIndex(prefetchData.videoIndex);
26547
+ }
26732
26548
  if (!hasInitialLoad && prefetchData.videoIndex.allVideos.length > 0) {
26733
26549
  setIsLoading(false);
26734
26550
  setHasInitialLoad(true);
@@ -26736,10 +26552,45 @@ var BottlenecksContent = ({
26736
26552
  }
26737
26553
  }, [prefetchData, prefetchStatus, updateClipCounts, updateVideoIndex, hasInitialLoad]);
26738
26554
  React19.useEffect(() => {
26739
- if (s3ClipsService && videoIndex && clipCounts[activeFilter] > 0) {
26740
- loadFirstVideoForCategory(activeFilter);
26555
+ if (s3ClipsService && clipCounts[activeFilter] > 0) {
26556
+ const hasVideosForCurrentFilter = allVideos.some((video) => {
26557
+ if (sopCategories && sopCategories.length > 0) {
26558
+ const selectedCategory = sopCategories.find((cat) => cat.id === activeFilter);
26559
+ if (selectedCategory) {
26560
+ return video.type === selectedCategory.id;
26561
+ }
26562
+ }
26563
+ if (activeFilter === "all") return true;
26564
+ if (activeFilter === "low_value") {
26565
+ return video.type === "low_value";
26566
+ }
26567
+ if (activeFilter === "idle_time") {
26568
+ return video.type === "idle_time";
26569
+ }
26570
+ if (activeFilter === "sop_deviations") {
26571
+ return video.type === "missing_quality_check";
26572
+ }
26573
+ if (activeFilter === "best_cycle_time") {
26574
+ return video.type === "best_cycle_time";
26575
+ }
26576
+ if (activeFilter === "worst_cycle_time") {
26577
+ return video.type === "worst_cycle_time";
26578
+ }
26579
+ if (activeFilter === "cycle_completion") {
26580
+ return video.type === "cycle_completion";
26581
+ }
26582
+ if (activeFilter === "long_cycle_time") {
26583
+ return video.type === "long_cycle_time";
26584
+ }
26585
+ return video.type === "bottleneck" && video.severity === activeFilter;
26586
+ });
26587
+ if (!hasVideosForCurrentFilter) {
26588
+ loadFirstVideoForCategory(activeFilter);
26589
+ } else {
26590
+ setIsCategoryLoading(false);
26591
+ }
26741
26592
  }
26742
- }, [activeFilter, s3ClipsService, videoIndex, clipCounts, loadFirstVideoForCategory]);
26593
+ }, [activeFilter, s3ClipsService, videoIndex, clipCounts, loadFirstVideoForCategory, allVideos, sopCategories]);
26743
26594
  React19.useEffect(() => {
26744
26595
  if (previousFilterRef.current !== activeFilter) {
26745
26596
  console.log(`Filter changed from ${previousFilterRef.current} to ${activeFilter} - resetting to first video`);
@@ -26808,26 +26659,25 @@ var BottlenecksContent = ({
26808
26659
  return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
26809
26660
  });
26810
26661
  }, [activeFilter, allVideos, sopCategories]);
26811
- React19.useEffect(() => {
26812
- if (filteredVideos.length === 0) return;
26813
- const upcoming = [];
26814
- if (currentIndex + 1 < filteredVideos.length && filteredVideos[currentIndex + 1]) {
26815
- upcoming.push(filteredVideos[currentIndex + 1].src);
26816
- }
26817
- if (currentIndex - 1 >= 0 && currentIndex - 1 < filteredVideos.length && filteredVideos[currentIndex - 1]) {
26818
- upcoming.push(filteredVideos[currentIndex - 1].src);
26819
- }
26820
- if (upcoming.length > 0) {
26821
- preloadVideosUrl(upcoming);
26822
- }
26823
- }, [currentIndex, filteredVideos]);
26824
26662
  React19.useEffect(() => {
26825
26663
  if (isNavigating && currentIndex < filteredVideos.length) {
26826
26664
  setIsNavigating(false);
26827
26665
  setError(null);
26828
26666
  videoRetryCountRef.current = 0;
26667
+ ensureVideosLoaded(currentIndex);
26829
26668
  }
26830
26669
  }, [isNavigating, currentIndex, filteredVideos.length]);
26670
+ React19.useEffect(() => {
26671
+ if (!isPlaying || !isMountedRef.current) return;
26672
+ const preloadInterval = setInterval(() => {
26673
+ if (isMountedRef.current) {
26674
+ const currentIdx = currentIndexRef.current;
26675
+ console.log(`[Background Preloader] Ensuring videos loaded around index ${currentIdx}`);
26676
+ ensureVideosLoaded(currentIdx);
26677
+ }
26678
+ }, 2e3);
26679
+ return () => clearInterval(preloadInterval);
26680
+ }, [isPlaying]);
26831
26681
  const handleNext = React19.useCallback(async () => {
26832
26682
  if (!isMountedRef.current) return;
26833
26683
  const currentIdx = currentIndexRef.current;
@@ -26848,6 +26698,7 @@ var BottlenecksContent = ({
26848
26698
  }
26849
26699
  if (nextIndex < filteredVideos.length) {
26850
26700
  setIsNavigating(false);
26701
+ ensureVideosLoaded(nextIndex);
26851
26702
  return;
26852
26703
  }
26853
26704
  if (isMountedRef.current) {
@@ -26865,8 +26716,8 @@ var BottlenecksContent = ({
26865
26716
  nextIndex,
26866
26717
  true,
26867
26718
  // includeCycleTime
26868
- true
26869
- // includeMetadata
26719
+ false
26720
+ // includeMetadata - DON'T fetch metadata during navigation to prevent flooding!
26870
26721
  );
26871
26722
  } else {
26872
26723
  console.warn(`[BottlenecksContent] Video index not ready for navigation: ID: ${currentVideoIndex?._debugId || "NO_ID"}, byCategory exists = ${!!currentVideoIndex?.byCategory}, allVideos = ${currentVideoIndex?.allVideos?.length || 0}`);
@@ -26879,7 +26730,11 @@ var BottlenecksContent = ({
26879
26730
  operationalDate,
26880
26731
  shiftStr,
26881
26732
  effectiveFilter,
26882
- nextIndex
26733
+ nextIndex,
26734
+ true,
26735
+ // includeCycleTime - needed for main video display
26736
+ false
26737
+ // includeMetadata - DON'T fetch metadata during navigation to prevent flooding!
26883
26738
  );
26884
26739
  }
26885
26740
  if (video && isMountedRef.current) {
@@ -26892,26 +26747,7 @@ var BottlenecksContent = ({
26892
26747
  return prev;
26893
26748
  });
26894
26749
  preloadVideoUrl(video.src);
26895
- if (nextIndex + 1 < clipCounts[effectiveFilter]) {
26896
- setTimeout(() => {
26897
- const videoIndexForPreload = videoIndexRef.current;
26898
- if (videoIndexForPreload && s3ClipsService && isMountedRef.current) {
26899
- s3ClipsService.getVideoFromIndex(videoIndexForPreload, effectiveFilter, nextIndex + 1, true, true).then((nextVideo) => {
26900
- if (nextVideo && isMountedRef.current) {
26901
- setAllVideos((prev) => {
26902
- if (!prev.some((v) => v.id === nextVideo.id)) {
26903
- const newVideos = [...prev, nextVideo];
26904
- return newVideos;
26905
- }
26906
- return prev;
26907
- });
26908
- preloadVideoUrl(nextVideo.src);
26909
- }
26910
- }).catch(() => {
26911
- });
26912
- }
26913
- }, 100);
26914
- }
26750
+ ensureVideosLoaded(nextIndex);
26915
26751
  }
26916
26752
  } catch (error2) {
26917
26753
  console.error("Error loading next video:", error2);
@@ -26933,15 +26769,43 @@ var BottlenecksContent = ({
26933
26769
  if (prevIndex < filteredVideos.length) {
26934
26770
  setCurrentIndex(prevIndex);
26935
26771
  setError(null);
26772
+ ensureVideosLoaded(prevIndex);
26936
26773
  }
26937
26774
  }
26938
26775
  }, [filteredVideos.length]);
26776
+ const currentVideo = React19.useMemo(() => {
26777
+ if (!filteredVideos || filteredVideos.length === 0 || currentIndex >= filteredVideos.length) {
26778
+ return null;
26779
+ }
26780
+ return filteredVideos[currentIndex];
26781
+ }, [filteredVideos, currentIndex]);
26939
26782
  const handleVideoReady = React19.useCallback((player) => {
26940
26783
  console.log("Video.js player ready");
26941
26784
  }, []);
26942
- const handleVideoPlay = React19.useCallback((player) => {
26785
+ const handleVideoPlay = React19.useCallback(async (player) => {
26943
26786
  setIsPlaying(true);
26944
- }, []);
26787
+ const currentIdx = currentIndexRef.current;
26788
+ ensureVideosLoaded(currentIdx);
26789
+ if (currentVideo && !currentVideo.creation_timestamp && s3ClipsService) {
26790
+ try {
26791
+ const originalUri = currentVideo.originalUri || currentVideo.src;
26792
+ if (originalUri && originalUri.includes("s3://")) {
26793
+ const metadata = await s3ClipsService.getFullMetadata(originalUri);
26794
+ if (metadata && isMountedRef.current) {
26795
+ setAllVideos((prev) => prev.map(
26796
+ (v) => v.id === currentVideo.id ? {
26797
+ ...v,
26798
+ creation_timestamp: metadata.upload_timestamp || metadata.original_task_metadata?.timestamp || metadata.creation_timestamp,
26799
+ cycle_time_seconds: metadata.original_task_metadata?.cycle_time || v.cycle_time_seconds
26800
+ } : v
26801
+ ));
26802
+ }
26803
+ }
26804
+ } catch (error2) {
26805
+ console.warn("[BottlenecksContent] Failed to load metadata for current video:", error2);
26806
+ }
26807
+ }
26808
+ }, [currentVideo, s3ClipsService]);
26945
26809
  const handleVideoPause = React19.useCallback((player) => {
26946
26810
  setIsPlaying(false);
26947
26811
  }, []);
@@ -26968,13 +26832,6 @@ var BottlenecksContent = ({
26968
26832
  fetchInProgressRef.current.clear();
26969
26833
  setIsCategoryLoading(false);
26970
26834
  setIsNavigating(false);
26971
- if (s3ClipsService) {
26972
- try {
26973
- s3ClipsService.dispose();
26974
- } catch (error2) {
26975
- console.warn("[BottlenecksContent] Error disposing S3 service:", error2);
26976
- }
26977
- }
26978
26835
  };
26979
26836
  }, [s3ClipsService]);
26980
26837
  React19.useEffect(() => {
@@ -27015,12 +26872,6 @@ var BottlenecksContent = ({
27015
26872
  counts.bottlenecks = counts.bottleneck || counts.bottlenecks || 0;
27016
26873
  return counts;
27017
26874
  }, [clipCounts]);
27018
- const currentVideo = React19.useMemo(() => {
27019
- if (!filteredVideos || filteredVideos.length === 0 || currentIndex >= filteredVideos.length) {
27020
- return null;
27021
- }
27022
- return filteredVideos[currentIndex];
27023
- }, [filteredVideos, currentIndex]);
27024
26875
  const getClipTypeLabel = (video) => {
27025
26876
  if (!video) return "";
27026
26877
  switch (video.type) {
@@ -27255,8 +27106,8 @@ var BottlenecksContent = ({
27255
27106
  ] }),
27256
27107
  /* Priority 1: Show loading if initial load hasn't completed yet */
27257
27108
  isLoading && !hasInitialLoad ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading clips..." }) }) }) }) : (
27258
- /* Priority 2: Show loading if category is loading (prevents "no matching clips" flash) */
27259
- isCategoryLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading videos..." }) }) }) }) : (
27109
+ /* Priority 2: Show loading if category is loading BUT only if no video is available */
27110
+ isCategoryLoading && (!filteredVideos.length || !currentVideo) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading videos..." }) }) }) }) : (
27260
27111
  /* Priority 3: Show loading if navigating and current video not available */
27261
27112
  isNavigating || currentIndex >= filteredVideos.length && currentIndex < clipCounts[activeFilter] ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }) }) }) : (
27262
27113
  /* Priority 4: Show video if we have filtered videos and current video */
@@ -32054,9 +31905,25 @@ function withWorkspaceDisplayNames(Component3, options = {}) {
32054
31905
  return function WithWorkspaceDisplayNamesWrapper(props) {
32055
31906
  const [isInitialized2, setIsInitialized] = React19.useState(false);
32056
31907
  const [error, setError] = React19.useState(null);
31908
+ const [lastInitKey, setLastInitKey] = React19.useState("");
31909
+ const lineIdsKey = React19.useMemo(() => {
31910
+ if (!props.lineIds) return "";
31911
+ if (Array.isArray(props.lineIds)) {
31912
+ return props.lineIds.sort().join(",");
31913
+ }
31914
+ const values = Object.values(props.lineIds).filter(Boolean);
31915
+ return values.sort().join(",");
31916
+ }, [props.lineIds]);
31917
+ const initKey = React19.useMemo(() => {
31918
+ return `${lineIdsKey}-${props.selectedLineId || ""}-${props.factoryViewId || ""}-${initializeFor}`;
31919
+ }, [lineIdsKey, props.selectedLineId, props.factoryViewId]);
32057
31920
  React19.useEffect(() => {
32058
- setIsInitialized(false);
32059
- setError(null);
31921
+ if (initKey === lastInitKey && isInitialized2) {
31922
+ return;
31923
+ }
31924
+ if (initKey !== lastInitKey) {
31925
+ setError(null);
31926
+ }
32060
31927
  const initializeDisplayNames = async () => {
32061
31928
  try {
32062
31929
  const { lineIds, selectedLineId, factoryViewId } = props;
@@ -32082,20 +31949,17 @@ function withWorkspaceDisplayNames(Component3, options = {}) {
32082
31949
  await preInitializeWorkspaceDisplayNames();
32083
31950
  }
32084
31951
  setIsInitialized(true);
31952
+ setLastInitKey(initKey);
32085
31953
  } catch (err) {
32086
31954
  console.error("Failed to initialize workspace display names:", err);
32087
31955
  setError(err);
32088
31956
  setIsInitialized(true);
31957
+ setLastInitKey(initKey);
32089
31958
  }
32090
31959
  };
32091
31960
  initializeDisplayNames();
32092
- }, [
32093
- Array.isArray(props.lineIds) ? props.lineIds.join(",") : JSON.stringify(props.lineIds),
32094
- props.selectedLineId,
32095
- props.factoryViewId,
32096
- initializeFor
32097
- ]);
32098
- if (!isInitialized2 && showLoading) {
31961
+ }, [initKey]);
31962
+ if (!isInitialized2 && showLoading && lastInitKey === "") {
32099
31963
  return /* @__PURE__ */ jsxRuntime.jsx(LoadingPage, { message: loadingMessage });
32100
31964
  }
32101
31965
  if (error && showLoading) {
@@ -34166,7 +34030,15 @@ var ShiftsView = ({
34166
34030
  className = ""
34167
34031
  }) => {
34168
34032
  const supabase = useSupabase();
34169
- const auth = useAuth();
34033
+ React19.useEffect(() => {
34034
+ console.log("[ShiftsView] Component mounted/re-rendered", {
34035
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
34036
+ lineIds: lineIds.length
34037
+ });
34038
+ return () => {
34039
+ console.log("[ShiftsView] Component unmounting");
34040
+ };
34041
+ }, []);
34170
34042
  const [lineConfigs, setLineConfigs] = React19.useState(
34171
34043
  () => lineIds.map((id3) => ({
34172
34044
  id: id3,
@@ -34264,7 +34136,7 @@ var ShiftsView = ({
34264
34136
  }
34265
34137
  };
34266
34138
  fetchShiftConfigs();
34267
- }, [supabase, lineIds, showToast]);
34139
+ }, [lineIds, showToast]);
34268
34140
  React19.useCallback((lineId) => {
34269
34141
  setLineConfigs((prev) => {
34270
34142
  const typedPrev = prev;
@@ -34457,7 +34329,6 @@ var ShiftsView = ({
34457
34329
  }));
34458
34330
  }, []);
34459
34331
  const handleSaveShifts = React19.useCallback(async (lineId) => {
34460
- if (!auth.user?.id && false) ;
34461
34332
  setLineConfigs((prev) => prev.map(
34462
34333
  (config) => config.id === lineId ? { ...config, isSaving: true, saveSuccess: false } : config
34463
34334
  ));
@@ -34504,7 +34375,7 @@ var ShiftsView = ({
34504
34375
  (config) => config.id === lineId ? { ...config, isSaving: false, saveSuccess: false } : config
34505
34376
  ));
34506
34377
  }
34507
- }, [auth.user?.id, lineConfigs, supabase, showToast]);
34378
+ }, [lineConfigs, supabase, showToast]);
34508
34379
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `min-h-screen bg-slate-50 ${className}`, children: [
34509
34380
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-0 z-10 bg-white border-b border-gray-200/80 shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 sm:px-8 py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center relative", children: [
34510
34381
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -34612,6 +34483,7 @@ var ShiftsView = ({
34612
34483
  ] })
34613
34484
  ] });
34614
34485
  };
34486
+ var AuthenticatedShiftsView = withAuth(React19__namespace.default.memo(ShiftsView));
34615
34487
  var ShiftsView_default = ShiftsView;
34616
34488
 
34617
34489
  // src/lib/constants/actions.ts
@@ -35399,6 +35271,7 @@ var TargetsViewUI = ({
35399
35271
  isLoading,
35400
35272
  lineWorkspaces,
35401
35273
  lineNames,
35274
+ dropdownStates,
35402
35275
  savingLines,
35403
35276
  saveSuccess,
35404
35277
  selectedShift,
@@ -35484,13 +35357,13 @@ var TargetsViewUI = ({
35484
35357
  {
35485
35358
  onClick: () => onToggleLineDropdown(lineId),
35486
35359
  className: "flex items-center gap-3 text-lg font-medium transition-colors duration-200 \n focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 rounded-lg \n hover:bg-blue-50 px-3 py-2 group",
35487
- "aria-expanded": line.isOpen,
35360
+ "aria-expanded": dropdownStates[lineId],
35488
35361
  "aria-controls": `line-${lineId}-content`,
35489
35362
  children: [
35490
35363
  /* @__PURE__ */ jsxRuntime.jsx(
35491
35364
  lucideReact.ChevronDown,
35492
35365
  {
35493
- className: `w-5 h-5 text-blue-500 transform transition-transform duration-200 ${line.isOpen ? "rotate-180" : ""}`
35366
+ className: `w-5 h-5 text-blue-500 transform transition-transform duration-200 ${dropdownStates[lineId] ? "rotate-180" : ""}`
35494
35367
  }
35495
35368
  ),
35496
35369
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5", children: [
@@ -35537,7 +35410,7 @@ var TargetsViewUI = ({
35537
35410
  )
35538
35411
  ] })
35539
35412
  ] }) }),
35540
- line.isOpen && /* @__PURE__ */ jsxRuntime.jsxs("div", { id: `line-${lineId}-content`, className: "border-t border-gray-200", children: [
35413
+ dropdownStates[lineId] && /* @__PURE__ */ jsxRuntime.jsxs("div", { id: `line-${lineId}-content`, className: "border-t border-gray-200", children: [
35541
35414
  skuEnabled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4 border-b border-gray-200 bg-gray-50/50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
35542
35415
  /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: `sku-${lineId}`, className: "text-sm font-medium text-gray-700", children: "Select SKU:" }),
35543
35416
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 max-w-md", children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -35574,68 +35447,71 @@ var TargetsViewUI = ({
35574
35447
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-400", children: "pieces per day" })
35575
35448
  ] })
35576
35449
  ] }) }),
35577
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-gray-100", children: line.workspaces.map((workspace) => /* @__PURE__ */ jsxRuntime.jsx(
35578
- "div",
35579
- {
35580
- className: "px-6 py-4 hover:bg-gray-50 transition-all duration-200",
35581
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-12 gap-6 items-center", children: [
35582
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-900", children: formatWorkspaceName(workspace.name, lineId) }) }),
35583
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
35584
- "select",
35585
- {
35586
- value: workspace.actionType,
35587
- onChange: (e) => {
35588
- const newActionType = e.target.value;
35589
- onActionTypeChange(lineId, workspace.id, newActionType);
35590
- },
35591
- className: "w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm",
35592
- "aria-label": `Action type for ${formatWorkspaceName(workspace.name, lineId)}`,
35593
- children: [
35594
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "assembly", className: "py-2", children: "Assembly" }),
35595
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "packaging", className: "py-2", children: "Packaging" })
35596
- ]
35597
- }
35598
- ) }),
35599
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsxRuntime.jsx(
35600
- "input",
35601
- {
35602
- type: "number",
35603
- value: workspace.targetCycleTime === 0 ? "" : workspace.targetCycleTime,
35604
- onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetCycleTime", Number(e.target.value) || ""),
35605
- className: "block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm\n shadow-sm focus:border-blue-500 focus:ring-blue-500 \n transition-all duration-200 hover:border-gray-400",
35606
- min: "0",
35607
- step: "0.01",
35608
- placeholder: "Enter cycle time"
35609
- }
35610
- ) }),
35611
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsxRuntime.jsx(
35612
- "input",
35613
- {
35614
- type: "number",
35615
- value: workspace.targetPPH === 0 ? "" : workspace.targetPPH,
35616
- onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetPPH", Number(e.target.value) || ""),
35617
- className: "block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm\n shadow-sm focus:border-blue-500 focus:ring-blue-500 \n transition-all duration-200 hover:border-gray-400 \n placeholder:text-gray-400",
35618
- min: "0",
35619
- step: "0.1",
35620
- placeholder: "Enter PPH"
35621
- }
35622
- ) }),
35623
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsxRuntime.jsx(
35624
- "input",
35625
- {
35626
- type: "number",
35627
- value: workspace.targetDayOutput === 0 ? "" : workspace.targetDayOutput,
35628
- onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetDayOutput", Number(e.target.value) || ""),
35629
- className: "block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm\n shadow-sm focus:border-blue-500 focus:ring-blue-500 \n transition-all duration-200 hover:border-gray-400 \n placeholder:text-gray-400",
35630
- min: "0",
35631
- step: "1",
35632
- placeholder: "Enter day output"
35633
- }
35634
- ) })
35635
- ] })
35636
- },
35637
- workspace.id
35638
- )) })
35450
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-gray-100", children: line.workspaces.map((workspace) => {
35451
+ const formattedName = formatWorkspaceName(workspace.name, lineId);
35452
+ return /* @__PURE__ */ jsxRuntime.jsx(
35453
+ "div",
35454
+ {
35455
+ className: "px-6 py-4 hover:bg-gray-50 transition-all duration-200",
35456
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-12 gap-6 items-center", children: [
35457
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-900", children: formattedName }) }),
35458
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
35459
+ "select",
35460
+ {
35461
+ value: workspace.actionType,
35462
+ onChange: (e) => {
35463
+ const newActionType = e.target.value;
35464
+ onActionTypeChange(lineId, workspace.id, newActionType);
35465
+ },
35466
+ className: "w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm",
35467
+ "aria-label": `Action type for ${formattedName}`,
35468
+ children: [
35469
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "assembly", className: "py-2", children: "Assembly" }),
35470
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "packaging", className: "py-2", children: "Packaging" })
35471
+ ]
35472
+ }
35473
+ ) }),
35474
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsxRuntime.jsx(
35475
+ "input",
35476
+ {
35477
+ type: "number",
35478
+ value: workspace.targetCycleTime === 0 ? "" : workspace.targetCycleTime,
35479
+ onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetCycleTime", Number(e.target.value) || ""),
35480
+ className: "block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm\n shadow-sm focus:border-blue-500 focus:ring-blue-500 \n transition-all duration-200 hover:border-gray-400",
35481
+ min: "0",
35482
+ step: "0.01",
35483
+ placeholder: "Enter cycle time"
35484
+ }
35485
+ ) }),
35486
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsxRuntime.jsx(
35487
+ "input",
35488
+ {
35489
+ type: "number",
35490
+ value: workspace.targetPPH === 0 ? "" : workspace.targetPPH,
35491
+ onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetPPH", Number(e.target.value) || ""),
35492
+ className: "block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm\n shadow-sm focus:border-blue-500 focus:ring-blue-500 \n transition-all duration-200 hover:border-gray-400 \n placeholder:text-gray-400",
35493
+ min: "0",
35494
+ step: "0.1",
35495
+ placeholder: "Enter PPH"
35496
+ }
35497
+ ) }),
35498
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsxRuntime.jsx(
35499
+ "input",
35500
+ {
35501
+ type: "number",
35502
+ value: workspace.targetDayOutput === 0 ? "" : workspace.targetDayOutput,
35503
+ onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetDayOutput", Number(e.target.value) || ""),
35504
+ className: "block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm\n shadow-sm focus:border-blue-500 focus:ring-blue-500 \n transition-all duration-200 hover:border-gray-400 \n placeholder:text-gray-400",
35505
+ min: "0",
35506
+ step: "1",
35507
+ placeholder: "Enter day output"
35508
+ }
35509
+ ) })
35510
+ ] })
35511
+ },
35512
+ workspace.id
35513
+ );
35514
+ }) })
35639
35515
  ] })
35640
35516
  ]
35641
35517
  },
@@ -35668,7 +35544,6 @@ var TargetsView = ({
35668
35544
  return lineIds.reduce((acc, lineId) => ({
35669
35545
  ...acc,
35670
35546
  [lineId]: {
35671
- isOpen: getStoredLineState2(lineId),
35672
35547
  productId: "",
35673
35548
  shiftStartTime: "08:00",
35674
35549
  shiftEndTime: "19:00",
@@ -35681,6 +35556,12 @@ var TargetsView = ({
35681
35556
  }
35682
35557
  }), {});
35683
35558
  }, [lineIds]);
35559
+ const [dropdownStates, setDropdownStates] = React19.useState(() => {
35560
+ return lineIds.reduce((acc, lineId) => ({
35561
+ ...acc,
35562
+ [lineId]: getStoredLineState2(lineId)
35563
+ }), {});
35564
+ });
35684
35565
  const [allShiftsData, setAllShiftsData] = React19.useState({
35685
35566
  0: initialLineWorkspaces,
35686
35567
  // Day shift
@@ -35698,6 +35579,8 @@ var TargetsView = ({
35698
35579
  const [isBulkConfigureOpen, setIsBulkConfigureOpen] = React19.useState(false);
35699
35580
  const [selectedWorkspaces, setSelectedWorkspaces] = React19.useState([]);
35700
35581
  const [selectedShift, setSelectedShift] = React19.useState(0);
35582
+ const [dbValues, setDbValues] = React19.useState({ 0: {}, 1: {} });
35583
+ const [userEditedFields, setUserEditedFields] = React19.useState(/* @__PURE__ */ new Set());
35701
35584
  const lineWorkspaces = allShiftsData[selectedShift] || initialLineWorkspaces;
35702
35585
  const setLineWorkspaces = React19.useCallback((updater) => {
35703
35586
  setAllShiftsData((prev) => ({
@@ -35706,18 +35589,123 @@ var TargetsView = ({
35706
35589
  }));
35707
35590
  }, [selectedShift]);
35708
35591
  const supabase = useSupabase();
35709
- const auth = useAuth();
35710
- userId || auth?.user?.id;
35592
+ const effectiveUserId = userId || "6bf6f271-1e55-4a95-9b89-1c3820b58739";
35711
35593
  const dashboardConfig = useDashboardConfig();
35712
35594
  const { skus, isLoading: skusLoading } = useSKUs(companyId);
35713
35595
  const skuEnabled = dashboardConfig?.skuConfig?.enabled || false;
35596
+ const loadOperatingHours = React19.useCallback(async (lineId, shiftId) => {
35597
+ try {
35598
+ if (!supabase) return null;
35599
+ const { data, error } = await supabase.from("line_operating_hours").select("start_time, end_time, breaks").eq("line_id", lineId).eq("shift_id", shiftId).maybeSingle();
35600
+ if (error) {
35601
+ if (error.code === "PGRST116") {
35602
+ console.log(`No operating hours found for line ${lineId}, shift ${shiftId}`);
35603
+ return {
35604
+ startTime: "08:00",
35605
+ // Default values
35606
+ endTime: "19:00",
35607
+ breaks: []
35608
+ };
35609
+ } else {
35610
+ console.error("Error fetching operating hours:", error);
35611
+ return null;
35612
+ }
35613
+ }
35614
+ let breaks = [];
35615
+ if (data?.breaks) {
35616
+ if (Array.isArray(data.breaks)) {
35617
+ breaks = data.breaks.map((breakItem) => {
35618
+ const startTime = breakItem.start || breakItem.startTime || "00:00";
35619
+ const endTime = breakItem.end || breakItem.endTime || "00:00";
35620
+ const calculateDuration = (start, end) => {
35621
+ const [startHour, startMinute] = start.split(":").map(Number);
35622
+ const [endHour, endMinute] = end.split(":").map(Number);
35623
+ let startMinutes = startHour * 60 + startMinute;
35624
+ let endMinutes = endHour * 60 + endMinute;
35625
+ if (endMinutes < startMinutes) {
35626
+ endMinutes += 24 * 60;
35627
+ }
35628
+ return endMinutes - startMinutes;
35629
+ };
35630
+ return {
35631
+ startTime,
35632
+ endTime,
35633
+ duration: breakItem.duration || calculateDuration(startTime, endTime)
35634
+ };
35635
+ });
35636
+ } else if (typeof data.breaks === "object" && data.breaks.breaks) {
35637
+ breaks = data.breaks.breaks.map((breakItem) => {
35638
+ const startTime = breakItem.start || breakItem.startTime || "00:00";
35639
+ const endTime = breakItem.end || breakItem.endTime || "00:00";
35640
+ const calculateDuration = (start, end) => {
35641
+ const [startHour, startMinute] = start.split(":").map(Number);
35642
+ const [endHour, endMinute] = end.split(":").map(Number);
35643
+ let startMinutes = startHour * 60 + startMinute;
35644
+ let endMinutes = endHour * 60 + endMinute;
35645
+ if (endMinutes < startMinutes) {
35646
+ endMinutes += 24 * 60;
35647
+ }
35648
+ return endMinutes - startMinutes;
35649
+ };
35650
+ return {
35651
+ startTime,
35652
+ endTime,
35653
+ duration: breakItem.duration || calculateDuration(startTime, endTime)
35654
+ };
35655
+ });
35656
+ } else if (typeof data.breaks === "string") {
35657
+ try {
35658
+ const parsedBreaks = JSON.parse(data.breaks);
35659
+ if (Array.isArray(parsedBreaks)) {
35660
+ breaks = parsedBreaks.map((breakItem) => ({
35661
+ startTime: breakItem.start || breakItem.startTime || "00:00",
35662
+ endTime: breakItem.end || breakItem.endTime || "00:00",
35663
+ duration: breakItem.duration || 0
35664
+ }));
35665
+ } else if (parsedBreaks.breaks && Array.isArray(parsedBreaks.breaks)) {
35666
+ breaks = parsedBreaks.breaks.map((breakItem) => ({
35667
+ startTime: breakItem.start || breakItem.startTime || "00:00",
35668
+ endTime: breakItem.end || breakItem.endTime || "00:00",
35669
+ duration: breakItem.duration || 0
35670
+ }));
35671
+ }
35672
+ } catch (e) {
35673
+ console.error("Error parsing breaks data:", e);
35674
+ }
35675
+ }
35676
+ }
35677
+ return {
35678
+ startTime: data?.start_time || "08:00",
35679
+ endTime: data?.end_time || "19:00",
35680
+ breaks
35681
+ };
35682
+ } catch (e) {
35683
+ console.error("Exception when loading operating hours:", e);
35684
+ return {
35685
+ startTime: "08:00",
35686
+ endTime: "19:00",
35687
+ breaks: []
35688
+ };
35689
+ }
35690
+ }, [supabase]);
35691
+ React19.useEffect(() => {
35692
+ console.log("[TargetsView] Component mounted/re-rendered", {
35693
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
35694
+ lineIds: lineIds.length,
35695
+ effectiveUserId
35696
+ });
35697
+ return () => {
35698
+ console.log("[TargetsView] Component unmounting");
35699
+ };
35700
+ }, []);
35714
35701
  React19.useEffect(() => {
35715
35702
  let timeoutId;
35716
35703
  let retryCount = 0;
35717
35704
  const MAX_RETRIES2 = 2;
35718
35705
  const LOADING_TIMEOUT = 15e3;
35719
35706
  const fetchInitialData = async () => {
35720
- if (!supabase || lineIds.length === 0) return;
35707
+ if (lineIds.length === 0) return;
35708
+ console.log("[TargetsView] Starting fetchInitialData");
35721
35709
  setIsLoading(true);
35722
35710
  timeoutId = setTimeout(() => {
35723
35711
  console.error("Loading timeout reached");
@@ -35781,10 +35769,32 @@ var TargetsView = ({
35781
35769
  const actionThresholds = await workspaceService.getActionThresholds(
35782
35770
  lineId,
35783
35771
  currentDate,
35784
- selectedShift
35772
+ 0
35773
+ // Always use day shift for initial load
35785
35774
  );
35775
+ const operatingHoursData = await loadOperatingHours(lineId, 0);
35776
+ if (operatingHoursData) {
35777
+ updatedLineWorkspaces[lineId].shiftStartTime = operatingHoursData.startTime;
35778
+ updatedLineWorkspaces[lineId].shiftEndTime = operatingHoursData.endTime;
35779
+ updatedLineWorkspaces[lineId].breaks = operatingHoursData.breaks;
35780
+ updatedLineWorkspaces[lineId].shiftHours = calculateShiftHours2(
35781
+ operatingHoursData.startTime,
35782
+ operatingHoursData.endTime,
35783
+ operatingHoursData.breaks
35784
+ );
35785
+ }
35786
35786
  const mappedWorkspaces = enabledWorkspaces.map((ws) => {
35787
35787
  const threshold = actionThresholds.find((t) => t.workspace_id === ws.id);
35788
+ if (!dbValues[0][lineId]) {
35789
+ dbValues[0][lineId] = {};
35790
+ }
35791
+ if (threshold) {
35792
+ dbValues[0][lineId][ws.id] = {
35793
+ targetPPH: threshold.pph_threshold,
35794
+ targetCycleTime: threshold.ideal_cycle_time,
35795
+ targetDayOutput: threshold.total_day_output
35796
+ };
35797
+ }
35788
35798
  return {
35789
35799
  id: ws.id,
35790
35800
  name: ws.workspace_id,
@@ -35825,90 +35835,7 @@ var TargetsView = ({
35825
35835
  return () => {
35826
35836
  clearTimeout(timeoutId);
35827
35837
  };
35828
- }, [supabase, lineIds, companyId]);
35829
- React19.useCallback(async (shiftId) => {
35830
- try {
35831
- if (!supabase) return;
35832
- const currentDate = getOperationalDate();
35833
- const updatedLineWorkspaces = { ...lineWorkspaces };
35834
- let hasUpdates = false;
35835
- for (const lineId of lineIds) {
35836
- const lineState = lineWorkspaces[lineId];
35837
- if (!lineState || !lineState.factoryId) {
35838
- console.warn(`Skipping line thresholds for ${lineId} as factoryId is not yet available.`);
35839
- continue;
35840
- }
35841
- const currentFactoryId = lineState.factoryId;
35842
- try {
35843
- const { data: lineThresholdsRows, error: thresholdError } = await supabase.from("line_thresholds").select("product_code").eq("line_id", lineId).eq("date", currentDate).eq("shift_id", selectedShift).eq("factory_id", currentFactoryId);
35844
- if (thresholdError) {
35845
- console.error(`Error fetching line threshold for line ${lineId}, factory ${currentFactoryId}:`, thresholdError);
35846
- continue;
35847
- }
35848
- let determinedProductId = updatedLineWorkspaces[lineId]?.productId || "";
35849
- if (lineThresholdsRows && lineThresholdsRows.length > 0) {
35850
- if (lineThresholdsRows.length > 1) {
35851
- console.warn(
35852
- `Multiple line_thresholds records found for line ${lineId}, factory ${currentFactoryId}, date ${currentDate}, shift ${selectedShift}. Using product_code from the first record. Rows:`,
35853
- lineThresholdsRows
35854
- );
35855
- }
35856
- determinedProductId = lineThresholdsRows[0].product_code;
35857
- } else {
35858
- console.log(
35859
- `No line_thresholds record found for line ${lineId}, factory ${currentFactoryId}, date ${currentDate}, shift ${selectedShift}. Using existing/default product ID.`
35860
- );
35861
- }
35862
- const { data: operatingHours, error: hoursError } = await supabase.from("line_operating_hours").select("start_time, end_time, breaks").eq("line_id", lineId).eq("shift_id", selectedShift).maybeSingle();
35863
- if (hoursError) {
35864
- console.error(`Error fetching operating hours for line ${lineId}:`, hoursError);
35865
- continue;
35866
- }
35867
- const startTime = operatingHours?.start_time || updatedLineWorkspaces[lineId]?.shiftStartTime || "08:00";
35868
- const endTime = operatingHours?.end_time || updatedLineWorkspaces[lineId]?.shiftEndTime || "19:00";
35869
- let breaks = [];
35870
- if (operatingHours?.breaks) {
35871
- if (Array.isArray(operatingHours.breaks)) {
35872
- breaks = operatingHours.breaks.map((breakItem) => ({
35873
- startTime: breakItem.start || breakItem.startTime || "00:00",
35874
- endTime: breakItem.end || breakItem.endTime || "00:00",
35875
- duration: breakItem.duration || calculateShiftHours2(breakItem.start || breakItem.startTime || "00:00", breakItem.end || breakItem.endTime || "00:00", []) * 60
35876
- }));
35877
- } else if (typeof operatingHours.breaks === "object" && operatingHours.breaks.breaks) {
35878
- breaks = operatingHours.breaks.breaks.map((breakItem) => ({
35879
- startTime: breakItem.start || breakItem.startTime || "00:00",
35880
- endTime: breakItem.end || breakItem.endTime || "00:00",
35881
- duration: breakItem.duration || calculateShiftHours2(breakItem.start || breakItem.startTime || "00:00", breakItem.end || breakItem.endTime || "00:00", []) * 60
35882
- }));
35883
- }
35884
- }
35885
- const shiftHours = calculateShiftHours2(startTime, endTime, breaks);
35886
- const currentLineStateFromLoop = updatedLineWorkspaces[lineId];
35887
- if (determinedProductId !== currentLineStateFromLoop?.productId || startTime !== currentLineStateFromLoop?.shiftStartTime || endTime !== currentLineStateFromLoop?.shiftEndTime || shiftHours !== currentLineStateFromLoop?.shiftHours || JSON.stringify(breaks) !== JSON.stringify(currentLineStateFromLoop?.breaks)) {
35888
- updatedLineWorkspaces[lineId] = {
35889
- ...currentLineStateFromLoop || {},
35890
- factoryId: currentFactoryId,
35891
- // Ensure factoryId is preserved
35892
- productId: determinedProductId,
35893
- shiftStartTime: startTime,
35894
- shiftEndTime: endTime,
35895
- breaks,
35896
- shiftHours: Number(shiftHours),
35897
- workspaces: currentLineStateFromLoop?.workspaces || []
35898
- };
35899
- hasUpdates = true;
35900
- }
35901
- } catch (lineError) {
35902
- console.error(`Error processing line ${lineId}:`, lineError);
35903
- }
35904
- }
35905
- if (hasUpdates) {
35906
- setLineWorkspaces(updatedLineWorkspaces);
35907
- }
35908
- } catch (error) {
35909
- console.error("Error in fetchLineThresholds outer try-catch:", error);
35910
- }
35911
- }, [selectedShift, supabase, lineIds, lineWorkspaces, allShiftsData]);
35838
+ }, [lineIds, companyId, loadOperatingHours]);
35912
35839
  const fetchAllShiftsData = React19.useCallback(async (currentWorkspaces) => {
35913
35840
  if (!supabase) return;
35914
35841
  const currentDate = getOperationalDate();
@@ -35918,32 +35845,25 @@ var TargetsView = ({
35918
35845
  1: JSON.parse(JSON.stringify(currentWorkspaces))
35919
35846
  // Deep clone for night shift
35920
35847
  };
35848
+ const newDbValues = { 0: {}, 1: {} };
35921
35849
  for (const shiftId of [0, 1]) {
35922
35850
  for (const lineId of lineIds) {
35923
35851
  try {
35924
- const { data: operatingHours, error: hoursError } = await supabase.from("line_operating_hours").select("start_time, end_time, breaks").eq("line_id", lineId).eq("shift_id", shiftId).maybeSingle();
35925
- if (hoursError) {
35926
- console.error(`Error fetching operating hours for line ${lineId}, shift ${shiftId}:`, hoursError);
35852
+ const operatingHoursData = await loadOperatingHours(lineId, shiftId);
35853
+ if (!operatingHoursData) {
35854
+ console.warn(`No operating hours for line ${lineId}, shift ${shiftId} - using defaults`);
35927
35855
  continue;
35928
35856
  }
35929
- let breaks = [];
35930
- if (operatingHours?.breaks) {
35931
- if (Array.isArray(operatingHours.breaks)) {
35932
- breaks = operatingHours.breaks.map((breakItem) => ({
35933
- startTime: breakItem.start || breakItem.startTime || "00:00",
35934
- endTime: breakItem.end || breakItem.endTime || "00:00",
35935
- duration: breakItem.duration || calculateShiftHours2(breakItem.start || breakItem.startTime || "00:00", breakItem.end || breakItem.endTime || "00:00", []) * 60
35936
- }));
35937
- }
35938
- }
35939
- const startTime = operatingHours?.start_time || "08:00";
35940
- const endTime = operatingHours?.end_time || "19:00";
35857
+ const { startTime, endTime, breaks } = operatingHoursData;
35941
35858
  const shiftHours = calculateShiftHours2(startTime, endTime, breaks);
35942
35859
  const actionThresholds = await workspaceService.getActionThresholds(
35943
35860
  lineId,
35944
35861
  currentDate,
35945
35862
  shiftId
35946
35863
  );
35864
+ if (!newDbValues[shiftId][lineId]) {
35865
+ newDbValues[shiftId][lineId] = {};
35866
+ }
35947
35867
  const existingLine = newAllShiftsData[shiftId][lineId];
35948
35868
  if (existingLine) {
35949
35869
  newAllShiftsData[shiftId][lineId] = {
@@ -35955,6 +35875,11 @@ var TargetsView = ({
35955
35875
  workspaces: existingLine.workspaces.map((ws) => {
35956
35876
  const threshold = actionThresholds.find((t) => t.workspace_id === ws.id);
35957
35877
  if (threshold) {
35878
+ newDbValues[shiftId][lineId][ws.id] = {
35879
+ targetPPH: threshold.pph_threshold,
35880
+ targetCycleTime: threshold.ideal_cycle_time,
35881
+ targetDayOutput: threshold.total_day_output
35882
+ };
35958
35883
  return {
35959
35884
  ...ws,
35960
35885
  targetPPH: threshold.pph_threshold,
@@ -35972,114 +35897,17 @@ var TargetsView = ({
35972
35897
  }
35973
35898
  }
35974
35899
  setAllShiftsData(newAllShiftsData);
35975
- }, [supabase, lineIds]);
35976
- const loadOperatingHours = React19.useCallback(async (lineId, shiftId) => {
35977
- try {
35978
- if (!supabase) return null;
35979
- const { data, error } = await supabase.from("line_operating_hours").select("start_time, end_time, breaks").eq("line_id", lineId).eq("shift_id", shiftId).maybeSingle();
35980
- if (error) {
35981
- if (error.code === "PGRST116") {
35982
- console.log(`No operating hours found for line ${lineId}, shift ${shiftId}`);
35983
- return {
35984
- startTime: "08:00",
35985
- // Default values
35986
- endTime: "19:00",
35987
- breaks: []
35988
- };
35989
- } else {
35990
- console.error("Error fetching operating hours:", error);
35991
- return null;
35992
- }
35993
- }
35994
- let breaks = [];
35995
- if (data?.breaks) {
35996
- if (Array.isArray(data.breaks)) {
35997
- breaks = data.breaks.map((breakItem) => {
35998
- const startTime = breakItem.start || breakItem.startTime || "00:00";
35999
- const endTime = breakItem.end || breakItem.endTime || "00:00";
36000
- const calculateDuration = (start, end) => {
36001
- const [startHour, startMinute] = start.split(":").map(Number);
36002
- const [endHour, endMinute] = end.split(":").map(Number);
36003
- let startMinutes = startHour * 60 + startMinute;
36004
- let endMinutes = endHour * 60 + endMinute;
36005
- if (endMinutes < startMinutes) {
36006
- endMinutes += 24 * 60;
36007
- }
36008
- return endMinutes - startMinutes;
36009
- };
36010
- return {
36011
- startTime,
36012
- endTime,
36013
- duration: breakItem.duration || calculateDuration(startTime, endTime)
36014
- };
36015
- });
36016
- } else if (typeof data.breaks === "object" && data.breaks.breaks) {
36017
- breaks = data.breaks.breaks.map((breakItem) => {
36018
- const startTime = breakItem.start || breakItem.startTime || "00:00";
36019
- const endTime = breakItem.end || breakItem.endTime || "00:00";
36020
- const calculateDuration = (start, end) => {
36021
- const [startHour, startMinute] = start.split(":").map(Number);
36022
- const [endHour, endMinute] = end.split(":").map(Number);
36023
- let startMinutes = startHour * 60 + startMinute;
36024
- let endMinutes = endHour * 60 + endMinute;
36025
- if (endMinutes < startMinutes) {
36026
- endMinutes += 24 * 60;
36027
- }
36028
- return endMinutes - startMinutes;
36029
- };
36030
- return {
36031
- startTime,
36032
- endTime,
36033
- duration: breakItem.duration || calculateDuration(startTime, endTime)
36034
- };
36035
- });
36036
- } else if (typeof data.breaks === "string") {
36037
- try {
36038
- const parsedBreaks = JSON.parse(data.breaks);
36039
- if (Array.isArray(parsedBreaks)) {
36040
- breaks = parsedBreaks.map((breakItem) => ({
36041
- startTime: breakItem.start || breakItem.startTime || "00:00",
36042
- endTime: breakItem.end || breakItem.endTime || "00:00",
36043
- duration: breakItem.duration || 0
36044
- }));
36045
- } else if (parsedBreaks.breaks && Array.isArray(parsedBreaks.breaks)) {
36046
- breaks = parsedBreaks.breaks.map((breakItem) => ({
36047
- startTime: breakItem.start || breakItem.startTime || "00:00",
36048
- endTime: breakItem.end || breakItem.endTime || "00:00",
36049
- duration: breakItem.duration || 0
36050
- }));
36051
- }
36052
- } catch (e) {
36053
- console.error("Error parsing breaks data:", e);
36054
- }
36055
- }
36056
- }
36057
- return {
36058
- startTime: data?.start_time || "08:00",
36059
- endTime: data?.end_time || "19:00",
36060
- breaks
36061
- };
36062
- } catch (e) {
36063
- console.error("Exception when loading operating hours:", e);
36064
- return {
36065
- startTime: "08:00",
36066
- endTime: "19:00",
36067
- breaks: []
36068
- };
36069
- }
36070
- }, [supabase]);
35900
+ setDbValues(newDbValues);
35901
+ }, [supabase, lineIds, loadOperatingHours]);
36071
35902
  const toggleLineDropdown = React19.useCallback((lineId) => {
36072
- setLineWorkspaces((prev) => {
36073
- const newIsOpen = !prev[lineId].isOpen;
35903
+ setDropdownStates((prev) => {
35904
+ const newIsOpen = !prev[lineId];
36074
35905
  if (typeof window !== "undefined") {
36075
35906
  localStorage.setItem(`line_${lineId}_open`, JSON.stringify(newIsOpen));
36076
35907
  }
36077
35908
  return {
36078
35909
  ...prev,
36079
- [lineId]: {
36080
- ...prev[lineId],
36081
- isOpen: newIsOpen
36082
- }
35910
+ [lineId]: newIsOpen
36083
35911
  };
36084
35912
  });
36085
35913
  }, []);
@@ -36128,6 +35956,8 @@ var TargetsView = ({
36128
35956
  }
36129
35957
  };
36130
35958
  const updateWorkspaceTarget = (lineId, workspaceId, field, value) => {
35959
+ const fieldKey = `${lineId}-${workspaceId}-${field}`;
35960
+ setUserEditedFields((prev) => new Set(prev).add(fieldKey));
36131
35961
  setLineWorkspaces((prev) => {
36132
35962
  const shiftHours = prev[lineId].shiftHours;
36133
35963
  return {
@@ -36152,11 +35982,7 @@ var TargetsView = ({
36152
35982
  } else if (field === "targetDayOutput") {
36153
35983
  updates.targetDayOutput = value;
36154
35984
  if (value !== "") {
36155
- const breaks = prev[lineId].breaks;
36156
- const totalBreakMinutes = breaks.reduce((total, b) => total + b.duration, 0);
36157
- const totalBreakHours = totalBreakMinutes / 60;
36158
- const realWorkHours = shiftHours - totalBreakHours;
36159
- const calculatedPPH = Math.round(value / realWorkHours);
35985
+ const calculatedPPH = Math.round(value / shiftHours);
36160
35986
  updates.targetPPH = calculatedPPH;
36161
35987
  } else {
36162
35988
  updates.targetPPH = "";
@@ -36171,62 +35997,35 @@ var TargetsView = ({
36171
35997
  };
36172
35998
  const handleShiftChange = (shiftId) => {
36173
35999
  setSelectedShift(shiftId);
36174
- const loadShiftHours = async () => {
36175
- const updatedLineWorkspaces = { ...allShiftsData[shiftId] };
36176
- let hasUpdates = false;
36177
- for (const lineId of lineIds) {
36178
- try {
36179
- const operatingHours = await loadOperatingHours(lineId, shiftId);
36180
- if (!operatingHours) continue;
36181
- const shiftHours = calculateShiftHours2(
36182
- operatingHours.startTime,
36183
- operatingHours.endTime,
36184
- operatingHours.breaks
36185
- );
36186
- updatedLineWorkspaces[lineId] = {
36187
- ...updatedLineWorkspaces[lineId],
36188
- shiftStartTime: operatingHours.startTime,
36189
- shiftEndTime: operatingHours.endTime,
36190
- breaks: operatingHours.breaks,
36191
- shiftHours: Number(shiftHours),
36192
- workspaces: updatedLineWorkspaces[lineId].workspaces.map((ws) => {
36193
- let updatedPPH = ws.targetPPH;
36194
- if (ws.targetCycleTime !== "") {
36195
- const idealPPH = calculatePPH(
36196
- ws.targetCycleTime,
36197
- operatingHours.breaks,
36198
- Number(shiftHours)
36199
- );
36200
- const shouldUpdatePPH = typeof ws.targetPPH === "string" ? ws.targetPPH === "" : ws.targetPPH === 0 || !ws.targetPPH;
36201
- if (shouldUpdatePPH) {
36202
- updatedPPH = idealPPH;
36000
+ setUserEditedFields(/* @__PURE__ */ new Set());
36001
+ if (dbValues[shiftId] && Object.keys(dbValues[shiftId]).length > 0) {
36002
+ setAllShiftsData((prev) => {
36003
+ const updatedShiftData = { ...prev[shiftId] };
36004
+ for (const lineId of Object.keys(updatedShiftData)) {
36005
+ if (dbValues[shiftId][lineId]) {
36006
+ updatedShiftData[lineId] = {
36007
+ ...updatedShiftData[lineId],
36008
+ workspaces: updatedShiftData[lineId].workspaces.map((ws) => {
36009
+ const dbValue = dbValues[shiftId][lineId][ws.id];
36010
+ if (dbValue) {
36011
+ return {
36012
+ ...ws,
36013
+ targetPPH: dbValue.targetPPH,
36014
+ targetCycleTime: dbValue.targetCycleTime,
36015
+ targetDayOutput: dbValue.targetDayOutput
36016
+ };
36203
36017
  }
36204
- }
36205
- const updatedDayOutput = calculateDayOutput(
36206
- updatedPPH,
36207
- Number(shiftHours),
36208
- operatingHours.breaks
36209
- );
36210
- return {
36211
- ...ws,
36212
- targetPPH: updatedPPH,
36213
- targetDayOutput: updatedDayOutput
36214
- };
36215
- })
36216
- };
36217
- hasUpdates = true;
36218
- } catch (e) {
36219
- console.error(`Exception when loading shift hours for line ${lineId}:`, e);
36018
+ return ws;
36019
+ })
36020
+ };
36021
+ }
36220
36022
  }
36221
- }
36222
- if (hasUpdates) {
36223
- setAllShiftsData((prev) => ({
36023
+ return {
36224
36024
  ...prev,
36225
- [shiftId]: updatedLineWorkspaces
36226
- }));
36227
- }
36228
- };
36229
- loadShiftHours();
36025
+ [shiftId]: updatedShiftData
36026
+ };
36027
+ });
36028
+ }
36230
36029
  };
36231
36030
  const handleActionTypeChange = React19.useCallback((lineId, workspaceId, newActionType) => {
36232
36031
  if (!actionIds) return;
@@ -36316,6 +36115,31 @@ var TargetsView = ({
36316
36115
  throw lineUpsertError;
36317
36116
  }
36318
36117
  console.log(`[handleSaveLine] Successfully upserted line_thresholds for ${lineId}`);
36118
+ setDbValues((prev) => ({
36119
+ ...prev,
36120
+ [selectedShift]: {
36121
+ ...prev[selectedShift],
36122
+ [lineId]: lineDataToSave.workspaces.reduce((acc, ws) => ({
36123
+ ...acc,
36124
+ [ws.id]: {
36125
+ targetPPH: ws.targetPPH,
36126
+ targetCycleTime: ws.targetCycleTime,
36127
+ targetDayOutput: ws.targetDayOutput
36128
+ }
36129
+ }), {})
36130
+ }
36131
+ }));
36132
+ console.log(`[handleSaveLine] Updated dbValues for line ${lineId}, shift ${selectedShift}`);
36133
+ setUserEditedFields((prev) => {
36134
+ const newSet = new Set(prev);
36135
+ lineDataToSave.workspaces.forEach((ws) => {
36136
+ newSet.delete(`${lineId}-${ws.id}-targetPPH`);
36137
+ newSet.delete(`${lineId}-${ws.id}-targetCycleTime`);
36138
+ newSet.delete(`${lineId}-${ws.id}-targetDayOutput`);
36139
+ });
36140
+ return newSet;
36141
+ });
36142
+ console.log(`[handleSaveLine] Cleared user edited fields for line ${lineId}`);
36319
36143
  setSaveSuccess((prev) => ({ ...prev, [lineId]: true }));
36320
36144
  sonner.toast.success(`${lineNames[lineId] || lineId} targets saved successfully`);
36321
36145
  if (onSaveChanges) onSaveChanges(lineId);
@@ -36329,7 +36153,7 @@ var TargetsView = ({
36329
36153
  setSavingLines((prev) => ({ ...prev, [lineId]: false }));
36330
36154
  console.log(`[handleSaveLine] Set savingLines to false for ${lineId} in finally block`);
36331
36155
  }
36332
- }, [supabase, lineWorkspaces, selectedShift, lineNames, onSaveChanges]);
36156
+ }, [supabase, lineWorkspaces, selectedShift, lineNames, onSaveChanges, skuEnabled, dashboardConfig]);
36333
36157
  const handleBulkConfigure = async (updates) => {
36334
36158
  if (!actionIds) return;
36335
36159
  if (updates.productId !== void 0) {
@@ -36377,6 +36201,7 @@ var TargetsView = ({
36377
36201
  isLoading: isLoading || skusLoading,
36378
36202
  lineWorkspaces,
36379
36203
  lineNames,
36204
+ dropdownStates,
36380
36205
  savingLines,
36381
36206
  saveSuccess,
36382
36207
  selectedShift,
@@ -36401,7 +36226,7 @@ var TargetsView = ({
36401
36226
  };
36402
36227
  var TargetsViewWithDisplayNames = withAllWorkspaceDisplayNames(TargetsView);
36403
36228
  var TargetsView_default = TargetsViewWithDisplayNames;
36404
- var AuthenticatedTargetsView = withAuth(TargetsViewWithDisplayNames);
36229
+ var AuthenticatedTargetsView = withAuth(React19__namespace.default.memo(TargetsViewWithDisplayNames));
36405
36230
 
36406
36231
  // src/views/workspace-detail-view.utils.ts
36407
36232
  var formatISTDate2 = (date = /* @__PURE__ */ new Date(), options) => {
@@ -37881,6 +37706,7 @@ exports.AuthProvider = AuthProvider;
37881
37706
  exports.AuthenticatedFactoryView = AuthenticatedFactoryView;
37882
37707
  exports.AuthenticatedHelpView = AuthenticatedHelpView;
37883
37708
  exports.AuthenticatedHomeView = AuthenticatedHomeView;
37709
+ exports.AuthenticatedShiftsView = AuthenticatedShiftsView;
37884
37710
  exports.AuthenticatedTargetsView = AuthenticatedTargetsView;
37885
37711
  exports.BarChart = BarChart;
37886
37712
  exports.BaseHistoryCalendar = BaseHistoryCalendar;
@@ -38020,7 +37846,6 @@ exports.apiUtils = apiUtils;
38020
37846
  exports.authCoreService = authCoreService;
38021
37847
  exports.authOTPService = authOTPService;
38022
37848
  exports.authRateLimitService = authRateLimitService;
38023
- exports.cacheService = cacheService;
38024
37849
  exports.checkRateLimit = checkRateLimit2;
38025
37850
  exports.clearAllRateLimits = clearAllRateLimits2;
38026
37851
  exports.clearRateLimit = clearRateLimit2;