@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.mjs CHANGED
@@ -14,9 +14,9 @@ import { noop, warning, invariant, progress, secondsToMilliseconds, milliseconds
14
14
  import { getValueTransition, hover, press, isPrimaryPointer, GroupPlaybackControls, setDragLock, supportsLinearEasing, attachTimeline, isGenerator, calcGeneratorDuration, isWaapiSupportedEasing, mapEasingToNativeEasing, maxGeneratorDuration, generateLinearEasing, isBezierDefinition } from 'motion-dom';
15
15
  import { BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Bar, LabelList, ResponsiveContainer, LineChart as LineChart$1, Line, PieChart, Pie, Cell, ReferenceLine, ComposedChart, Area, ScatterChart, Scatter } from 'recharts';
16
16
  import { Slot } from '@radix-ui/react-slot';
17
- import { Camera, ChevronDown, ChevronUp, Check, ShieldCheck, Star, Award, X, Coffee, Plus, Clock, Minus, ArrowDown, ArrowUp, Search, CheckCircle, AlertTriangle, Info, Share2, Trophy, Target, Download, User, XCircle, ChevronLeft, ChevronRight, AlertCircle, Sun, Moon, MessageSquare, Trash2, ArrowLeft, RefreshCw, Menu, Send, Copy, Edit2, UserCheck, Save, LogOut, Calendar, Package, Settings, LifeBuoy, EyeOff, Eye, Zap, UserCircle, ArrowLeftIcon as ArrowLeftIcon$1, Settings2, CheckCircle2 } from 'lucide-react';
17
+ import { Camera, ChevronDown, ChevronUp, Check, ShieldCheck, Star, Award, X, Coffee, Plus, Clock, ArrowLeft, Calendar, Save, Minus, ArrowDown, ArrowUp, ArrowLeftIcon, Settings2, CheckCircle2, Search, CheckCircle, AlertTriangle, Info, Share2, Trophy, Target, Download, User, XCircle, ChevronLeft, ChevronRight, AlertCircle, Sun, Moon, MessageSquare, Trash2, RefreshCw, Menu, Send, Copy, Edit2, UserCheck, LogOut, Package, Settings, LifeBuoy, EyeOff, Eye, Zap, UserCircle } from 'lucide-react';
18
18
  import { DayPicker, useNavigation as useNavigation$1 } from 'react-day-picker';
19
- import { XMarkIcon, ArrowRightIcon, HomeIcon, TrophyIcon, ChartBarIcon, AdjustmentsHorizontalIcon, ClockIcon, CubeIcon, SparklesIcon, QuestionMarkCircleIcon, UserCircleIcon, ExclamationCircleIcon, EnvelopeIcon, DocumentTextIcon, Bars3Icon, ArrowLeftIcon, CheckCircleIcon, ChatBubbleLeftRightIcon, XCircleIcon, InformationCircleIcon, ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/24/outline';
19
+ import { XMarkIcon, ArrowRightIcon, HomeIcon, TrophyIcon, ChartBarIcon, AdjustmentsHorizontalIcon, ClockIcon, CubeIcon, SparklesIcon, QuestionMarkCircleIcon, UserCircleIcon, ExclamationCircleIcon, EnvelopeIcon, DocumentTextIcon, Bars3Icon, ArrowLeftIcon as ArrowLeftIcon$1, CheckCircleIcon, ChatBubbleLeftRightIcon, XCircleIcon, InformationCircleIcon, ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/24/outline';
20
20
  import html2canvas from 'html2canvas';
21
21
  import jsPDF, { jsPDF as jsPDF$1 } from 'jspdf';
22
22
  import * as SelectPrimitive from '@radix-ui/react-select';
@@ -394,11 +394,11 @@ var actionService = {
394
394
  };
395
395
  function createMemoizedFunction(fn, getCacheKey) {
396
396
  const cache = /* @__PURE__ */ new Map();
397
- const CACHE_DURATION3 = 5 * 60 * 1e3;
397
+ const CACHE_DURATION = 5 * 60 * 1e3;
398
398
  return (...args) => {
399
399
  const key = getCacheKey(...args);
400
400
  const cached = cache.get(key);
401
- if (cached && Date.now() - cached.timestamp < CACHE_DURATION3) {
401
+ if (cached && Date.now() - cached.timestamp < CACHE_DURATION) {
402
402
  return cached.result;
403
403
  }
404
404
  const result = fn(...args);
@@ -2449,199 +2449,6 @@ async function deleteThread(threadId) {
2449
2449
  if (error) throw error;
2450
2450
  }
2451
2451
 
2452
- // src/lib/services/cacheService.ts
2453
- var CacheService = class {
2454
- constructor() {
2455
- this.memoryCache = /* @__PURE__ */ new Map();
2456
- this.DEFAULT_DURATION = 5 * 60 * 1e3;
2457
- }
2458
- // 5 minutes
2459
- /**
2460
- * Generate a cache key from multiple parts
2461
- */
2462
- generateKey(...parts) {
2463
- return parts.filter((p) => p !== void 0 && p !== null).join(":");
2464
- }
2465
- /**
2466
- * Get item from cache
2467
- */
2468
- get(key, options) {
2469
- const storage = options?.storage || "memory";
2470
- try {
2471
- let cacheItem = null;
2472
- if (storage === "memory") {
2473
- cacheItem = this.memoryCache.get(key) || null;
2474
- } else if (storage === "localStorage" || storage === "sessionStorage") {
2475
- const stored = window[storage].getItem(key);
2476
- if (stored) {
2477
- cacheItem = JSON.parse(stored);
2478
- }
2479
- }
2480
- if (!cacheItem) {
2481
- return null;
2482
- }
2483
- if (Date.now() > cacheItem.expiresAt) {
2484
- this.delete(key, options);
2485
- return null;
2486
- }
2487
- return cacheItem.data;
2488
- } catch (error) {
2489
- console.error(`Error getting cache item ${key}:`, error);
2490
- return null;
2491
- }
2492
- }
2493
- /**
2494
- * Set item in cache
2495
- */
2496
- set(key, data, options) {
2497
- const storage = options?.storage || "memory";
2498
- const duration = options?.duration || this.DEFAULT_DURATION;
2499
- const cacheItem = {
2500
- data,
2501
- timestamp: Date.now(),
2502
- expiresAt: Date.now() + duration
2503
- };
2504
- try {
2505
- if (storage === "memory") {
2506
- this.memoryCache.set(key, cacheItem);
2507
- if (this.memoryCache.size > 100) {
2508
- const firstKey = this.memoryCache.keys().next().value;
2509
- if (firstKey) {
2510
- this.memoryCache.delete(firstKey);
2511
- }
2512
- }
2513
- } else if (storage === "localStorage" || storage === "sessionStorage") {
2514
- window[storage].setItem(key, JSON.stringify(cacheItem));
2515
- }
2516
- } catch (error) {
2517
- console.error(`Error setting cache item ${key}:`, error);
2518
- }
2519
- }
2520
- /**
2521
- * Delete item from cache
2522
- */
2523
- delete(key, options) {
2524
- const storage = options?.storage || "memory";
2525
- try {
2526
- if (storage === "memory") {
2527
- this.memoryCache.delete(key);
2528
- } else if (storage === "localStorage" || storage === "sessionStorage") {
2529
- window[storage].removeItem(key);
2530
- }
2531
- } catch (error) {
2532
- console.error(`Error deleting cache item ${key}:`, error);
2533
- }
2534
- }
2535
- /**
2536
- * Clear all items from cache
2537
- */
2538
- clear(options) {
2539
- const storage = options?.storage || "memory";
2540
- try {
2541
- if (storage === "memory") {
2542
- this.memoryCache.clear();
2543
- } else if (storage === "localStorage" || storage === "sessionStorage") {
2544
- const keys = Object.keys(window[storage]);
2545
- keys.forEach((key) => {
2546
- try {
2547
- const item = window[storage].getItem(key);
2548
- if (item) {
2549
- const parsed = JSON.parse(item);
2550
- if (parsed.timestamp && parsed.expiresAt && parsed.data !== void 0) {
2551
- window[storage].removeItem(key);
2552
- }
2553
- }
2554
- } catch {
2555
- }
2556
- });
2557
- }
2558
- } catch (error) {
2559
- console.error("Error clearing cache:", error);
2560
- }
2561
- }
2562
- /**
2563
- * Get or set item in cache with a factory function
2564
- */
2565
- async getOrSet(key, factory, options) {
2566
- const cached = this.get(key, options);
2567
- if (cached !== null) {
2568
- return cached;
2569
- }
2570
- const data = await factory();
2571
- this.set(key, data, options);
2572
- return data;
2573
- }
2574
- /**
2575
- * Invalidate cache entries matching a pattern
2576
- */
2577
- invalidatePattern(pattern, options) {
2578
- const storage = options?.storage || "memory";
2579
- const regex = typeof pattern === "string" ? new RegExp(pattern) : pattern;
2580
- try {
2581
- if (storage === "memory") {
2582
- const keysToDelete = [];
2583
- this.memoryCache.forEach((_, key) => {
2584
- if (regex.test(key)) {
2585
- keysToDelete.push(key);
2586
- }
2587
- });
2588
- keysToDelete.forEach((key) => this.memoryCache.delete(key));
2589
- } else if (storage === "localStorage" || storage === "sessionStorage") {
2590
- const keys = Object.keys(window[storage]);
2591
- keys.forEach((key) => {
2592
- if (regex.test(key)) {
2593
- window[storage].removeItem(key);
2594
- }
2595
- });
2596
- }
2597
- } catch (error) {
2598
- console.error("Error invalidating cache pattern:", error);
2599
- }
2600
- }
2601
- /**
2602
- * Clean up expired items
2603
- */
2604
- cleanup(options) {
2605
- const storage = options?.storage || "memory";
2606
- const now2 = Date.now();
2607
- try {
2608
- if (storage === "memory") {
2609
- const keysToDelete = [];
2610
- this.memoryCache.forEach((item, key) => {
2611
- if (now2 > item.expiresAt) {
2612
- keysToDelete.push(key);
2613
- }
2614
- });
2615
- keysToDelete.forEach((key) => this.memoryCache.delete(key));
2616
- } else if (storage === "localStorage" || storage === "sessionStorage") {
2617
- const keys = Object.keys(window[storage]);
2618
- keys.forEach((key) => {
2619
- try {
2620
- const item = window[storage].getItem(key);
2621
- if (item) {
2622
- const parsed = JSON.parse(item);
2623
- if (parsed.expiresAt && now2 > parsed.expiresAt) {
2624
- window[storage].removeItem(key);
2625
- }
2626
- }
2627
- } catch {
2628
- }
2629
- });
2630
- }
2631
- } catch (error) {
2632
- console.error("Error cleaning up cache:", error);
2633
- }
2634
- }
2635
- };
2636
- var cacheService = new CacheService();
2637
- if (typeof window !== "undefined") {
2638
- setInterval(() => {
2639
- cacheService.cleanup({ storage: "localStorage" });
2640
- cacheService.cleanup({ storage: "sessionStorage" });
2641
- cacheService.cleanup({ storage: "memory" });
2642
- }, 60 * 1e3);
2643
- }
2644
-
2645
2452
  // src/lib/services/subscriptionManager.ts
2646
2453
  var SubscriptionManager = class {
2647
2454
  constructor(supabase) {
@@ -3970,6 +3777,13 @@ var S3ClipsService = class {
3970
3777
  constructor(config) {
3971
3778
  // Request deduplication cache
3972
3779
  this.requestCache = new RequestDeduplicationCache();
3780
+ // Flag to prevent metadata fetching during index building
3781
+ this.isIndexBuilding = false;
3782
+ // Flag to prevent metadata fetching during entire prefetch operation
3783
+ this.isPrefetching = false;
3784
+ // Global safeguard: limit concurrent metadata fetches to prevent flooding
3785
+ this.currentMetadataFetches = 0;
3786
+ this.MAX_CONCURRENT_METADATA = 3;
3973
3787
  this.config = config;
3974
3788
  if (!config.s3Config) {
3975
3789
  throw new Error("S3 configuration is required");
@@ -4117,16 +3931,36 @@ var S3ClipsService = class {
4117
3931
  return null;
4118
3932
  }
4119
3933
  }
3934
+ /**
3935
+ * Control prefetch mode to prevent metadata fetching during background operations
3936
+ */
3937
+ setPrefetchMode(enabled) {
3938
+ this.isPrefetching = enabled;
3939
+ console.log(`[S3ClipsService] Prefetch mode ${enabled ? "enabled" : "disabled"} - metadata fetching ${enabled ? "blocked" : "allowed"}`);
3940
+ }
4120
3941
  /**
4121
3942
  * Fetches full metadata including timestamps with deduplication
4122
3943
  */
4123
3944
  async getFullMetadata(playlistUri) {
4124
- const deduplicationKey = `full-metadata:${playlistUri}`;
4125
- return this.requestCache.deduplicate(
4126
- deduplicationKey,
4127
- () => this.executeGetFullMetadata(playlistUri),
4128
- "FullMetadata"
4129
- );
3945
+ if (this.isIndexBuilding || this.isPrefetching) {
3946
+ console.warn(`[S3ClipsService] Skipping metadata fetch - building: ${this.isIndexBuilding}, prefetching: ${this.isPrefetching}`);
3947
+ return null;
3948
+ }
3949
+ if (this.currentMetadataFetches >= this.MAX_CONCURRENT_METADATA) {
3950
+ console.warn(`[S3ClipsService] Skipping metadata - max concurrent fetches (${this.MAX_CONCURRENT_METADATA}) reached`);
3951
+ return null;
3952
+ }
3953
+ this.currentMetadataFetches++;
3954
+ try {
3955
+ const deduplicationKey = `full-metadata:${playlistUri}`;
3956
+ return await this.requestCache.deduplicate(
3957
+ deduplicationKey,
3958
+ () => this.executeGetFullMetadata(playlistUri),
3959
+ "FullMetadata"
3960
+ );
3961
+ } finally {
3962
+ this.currentMetadataFetches--;
3963
+ }
4130
3964
  }
4131
3965
  /**
4132
3966
  * Internal implementation of full metadata fetching
@@ -4187,141 +4021,152 @@ var S3ClipsService = class {
4187
4021
  * Internal implementation of clip counts fetching
4188
4022
  */
4189
4023
  async executeGetClipCounts(workspaceId, date, shiftId, buildIndex) {
4190
- const basePrefix = `sop_violations/${workspaceId}/${date}/${shiftId}/`;
4191
- const counts = { total: 0 };
4192
- const categoryFolders = [
4193
- "idle_time",
4194
- "low_value",
4195
- "sop_deviation",
4196
- "missing_quality_check",
4197
- "best_cycle_time",
4198
- "worst_cycle_time",
4199
- "long_cycle_time",
4200
- "cycle_completion",
4201
- "bottleneck"
4202
- ];
4203
- console.log(`[S3ClipsService] ${buildIndex ? "Building video index and counting" : "Fast counting"} clips for ${workspaceId} on ${date}, shift ${shiftId}`);
4204
- const startTime = performance.now();
4205
- const videoIndex = buildIndex ? {
4206
- byCategory: /* @__PURE__ */ new Map(),
4207
- allVideos: [],
4208
- counts: {},
4209
- workspaceId,
4210
- date,
4211
- shiftId: shiftId.toString(),
4212
- lastUpdated: /* @__PURE__ */ new Date(),
4213
- _debugId: `vid_${Date.now()}_${Math.random().toString(36).substring(7)}`
4214
- } : null;
4215
- const countPromises = categoryFolders.map(async (category) => {
4216
- const categoryPrefix = `${basePrefix}${category}/videos/`;
4217
- const categoryVideos = [];
4218
- try {
4219
- if (buildIndex) {
4220
- const command = new ListObjectsV2Command({
4221
- Bucket: this.config.s3Config.bucketName,
4222
- Prefix: categoryPrefix,
4223
- MaxKeys: 1e3
4224
- });
4225
- let continuationToken;
4226
- do {
4227
- if (continuationToken) {
4228
- command.input.ContinuationToken = continuationToken;
4229
- }
4230
- const response = await this.s3Client.send(command);
4231
- if (response.Contents) {
4232
- for (const obj of response.Contents) {
4233
- if (obj.Key && obj.Key.endsWith("playlist.m3u8")) {
4234
- if (obj.Key.includes("missed_qchecks")) {
4235
- continue;
4236
- }
4237
- const s3Uri = `s3://${this.config.s3Config.bucketName}/${obj.Key}`;
4238
- const sopCategories = this.getSOPCategories(workspaceId);
4239
- const parsedInfo = parseS3Uri(s3Uri, sopCategories);
4240
- const belongsToCategory = parsedInfo && (parsedInfo.type === category || // Handle specific mismatches between folder names and parsed types
4241
- 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");
4242
- if (belongsToCategory) {
4243
- const videoEntry = {
4244
- uri: s3Uri,
4245
- category: parsedInfo.type,
4246
- // Use the parsed type, not the folder name
4247
- timestamp: parsedInfo.timestamp,
4248
- videoId: `${workspaceId}-${parsedInfo.timestamp}`,
4249
- workspaceId,
4250
- date,
4251
- shiftId: shiftId.toString()
4252
- };
4253
- categoryVideos.push(videoEntry);
4024
+ if (buildIndex) {
4025
+ this.isIndexBuilding = true;
4026
+ console.log(`[S3ClipsService] Starting index building - metadata fetching disabled`);
4027
+ }
4028
+ try {
4029
+ const basePrefix = `sop_violations/${workspaceId}/${date}/${shiftId}/`;
4030
+ const counts = { total: 0 };
4031
+ const categoryFolders = [
4032
+ "idle_time",
4033
+ "low_value",
4034
+ "sop_deviation",
4035
+ "missing_quality_check",
4036
+ "best_cycle_time",
4037
+ "worst_cycle_time",
4038
+ "long_cycle_time",
4039
+ "cycle_completion",
4040
+ "bottleneck"
4041
+ ];
4042
+ console.log(`[S3ClipsService] ${buildIndex ? "Building video index and counting" : "Fast counting"} clips for ${workspaceId} on ${date}, shift ${shiftId}`);
4043
+ const startTime = performance.now();
4044
+ const videoIndex = buildIndex ? {
4045
+ byCategory: /* @__PURE__ */ new Map(),
4046
+ allVideos: [],
4047
+ counts: {},
4048
+ workspaceId,
4049
+ date,
4050
+ shiftId: shiftId.toString(),
4051
+ lastUpdated: /* @__PURE__ */ new Date(),
4052
+ _debugId: `vid_${Date.now()}_${Math.random().toString(36).substring(7)}`
4053
+ } : null;
4054
+ const countPromises = categoryFolders.map(async (category) => {
4055
+ const categoryPrefix = `${basePrefix}${category}/videos/`;
4056
+ const categoryVideos = [];
4057
+ try {
4058
+ if (buildIndex) {
4059
+ const command = new ListObjectsV2Command({
4060
+ Bucket: this.config.s3Config.bucketName,
4061
+ Prefix: categoryPrefix,
4062
+ MaxKeys: 1e3
4063
+ });
4064
+ let continuationToken;
4065
+ do {
4066
+ if (continuationToken) {
4067
+ command.input.ContinuationToken = continuationToken;
4068
+ }
4069
+ const response = await this.s3Client.send(command);
4070
+ if (response.Contents) {
4071
+ for (const obj of response.Contents) {
4072
+ if (obj.Key && obj.Key.endsWith("playlist.m3u8")) {
4073
+ if (obj.Key.includes("missed_qchecks")) {
4074
+ continue;
4075
+ }
4076
+ const s3Uri = `s3://${this.config.s3Config.bucketName}/${obj.Key}`;
4077
+ const sopCategories = this.getSOPCategories(workspaceId);
4078
+ const parsedInfo = parseS3Uri(s3Uri, sopCategories);
4079
+ const belongsToCategory = parsedInfo && (parsedInfo.type === category || // Handle specific mismatches between folder names and parsed types
4080
+ 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");
4081
+ if (belongsToCategory) {
4082
+ const videoEntry = {
4083
+ uri: s3Uri,
4084
+ category: parsedInfo.type,
4085
+ // Use the parsed type, not the folder name
4086
+ timestamp: parsedInfo.timestamp,
4087
+ videoId: `${workspaceId}-${parsedInfo.timestamp}`,
4088
+ workspaceId,
4089
+ date,
4090
+ shiftId: shiftId.toString()
4091
+ };
4092
+ categoryVideos.push(videoEntry);
4093
+ }
4254
4094
  }
4255
4095
  }
4256
4096
  }
4097
+ continuationToken = response.NextContinuationToken;
4098
+ } while (continuationToken);
4099
+ categoryVideos.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
4100
+ if (categoryVideos.length > 0) {
4101
+ console.log(`[S3ClipsService] Found ${categoryVideos.length} videos for category '${category}' (parsed types: ${[...new Set(categoryVideos.map((v) => v.category))].join(", ")})`);
4257
4102
  }
4258
- continuationToken = response.NextContinuationToken;
4259
- } while (continuationToken);
4260
- categoryVideos.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
4261
- if (categoryVideos.length > 0) {
4262
- console.log(`[S3ClipsService] Found ${categoryVideos.length} videos for category '${category}' (parsed types: ${[...new Set(categoryVideos.map((v) => v.category))].join(", ")})`);
4103
+ return { category, count: categoryVideos.length, videos: categoryVideos };
4104
+ } else {
4105
+ const command = new ListObjectsV2Command({
4106
+ Bucket: this.config.s3Config.bucketName,
4107
+ Prefix: categoryPrefix,
4108
+ Delimiter: "/",
4109
+ MaxKeys: 1e3
4110
+ });
4111
+ let folderCount = 0;
4112
+ let continuationToken;
4113
+ do {
4114
+ if (continuationToken) {
4115
+ command.input.ContinuationToken = continuationToken;
4116
+ }
4117
+ const response = await this.s3Client.send(command);
4118
+ if (response.CommonPrefixes) {
4119
+ folderCount += response.CommonPrefixes.length;
4120
+ }
4121
+ continuationToken = response.NextContinuationToken;
4122
+ } while (continuationToken);
4123
+ return { category, count: folderCount, videos: [] };
4263
4124
  }
4264
- return { category, count: categoryVideos.length, videos: categoryVideos };
4265
- } else {
4266
- const command = new ListObjectsV2Command({
4267
- Bucket: this.config.s3Config.bucketName,
4268
- Prefix: categoryPrefix,
4269
- Delimiter: "/",
4270
- MaxKeys: 1e3
4271
- });
4272
- let folderCount = 0;
4273
- let continuationToken;
4274
- do {
4275
- if (continuationToken) {
4276
- command.input.ContinuationToken = continuationToken;
4277
- }
4278
- const response = await this.s3Client.send(command);
4279
- if (response.CommonPrefixes) {
4280
- folderCount += response.CommonPrefixes.length;
4125
+ } catch (error) {
4126
+ console.error(`Error ${buildIndex ? "building index for" : "counting folders for"} category ${category}:`, error);
4127
+ return { category, count: 0, videos: [] };
4128
+ }
4129
+ });
4130
+ const results = await Promise.all(countPromises);
4131
+ for (const { category, count, videos } of results) {
4132
+ counts[category] = count;
4133
+ counts.total += count;
4134
+ if (buildIndex && videoIndex && videos) {
4135
+ if (videos.length > 0) {
4136
+ const parsedType = videos[0].category;
4137
+ videoIndex.byCategory.set(parsedType, videos);
4138
+ console.log(`[S3ClipsService] Indexed ${videos.length} videos under parsed type '${parsedType}'`);
4139
+ if (category !== parsedType) {
4140
+ videoIndex.byCategory.set(category, videos);
4141
+ console.log(`[S3ClipsService] Created alias: S3 folder '${category}' -> parsed type '${parsedType}' (${videos.length} videos)`);
4281
4142
  }
4282
- continuationToken = response.NextContinuationToken;
4283
- } while (continuationToken);
4284
- return { category, count: folderCount, videos: [] };
4143
+ }
4144
+ videoIndex.allVideos.push(...videos);
4285
4145
  }
4286
- } catch (error) {
4287
- console.error(`Error ${buildIndex ? "building index for" : "counting folders for"} category ${category}:`, error);
4288
- return { category, count: 0, videos: [] };
4289
4146
  }
4290
- });
4291
- const results = await Promise.all(countPromises);
4292
- for (const { category, count, videos } of results) {
4293
- counts[category] = count;
4294
- counts.total += count;
4295
- if (buildIndex && videoIndex && videos) {
4296
- if (videos.length > 0) {
4297
- const parsedType = videos[0].category;
4298
- videoIndex.byCategory.set(parsedType, videos);
4299
- console.log(`[S3ClipsService] Indexed ${videos.length} videos under parsed type '${parsedType}'`);
4300
- if (category !== parsedType) {
4301
- videoIndex.byCategory.set(category, videos);
4302
- console.log(`[S3ClipsService] Created alias: S3 folder '${category}' -> parsed type '${parsedType}' (${videos.length} videos)`);
4303
- }
4147
+ if (buildIndex && videoIndex) {
4148
+ videoIndex.allVideos.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
4149
+ videoIndex.counts = { ...counts };
4150
+ }
4151
+ const elapsed = performance.now() - startTime;
4152
+ console.log(`[S3ClipsService] ${buildIndex ? "Video index and counts" : "Clip counts"} completed in ${elapsed.toFixed(2)}ms - Total: ${counts.total}`);
4153
+ if (buildIndex && videoIndex) {
4154
+ console.log(`[S3ClipsService] Final video index summary:`);
4155
+ console.log(` - VideoIndex ID: ${videoIndex._debugId}`);
4156
+ console.log(` - Total videos in allVideos: ${videoIndex.allVideos.length}`);
4157
+ console.log(` - Categories in byCategory Map: ${Array.from(videoIndex.byCategory.keys()).join(", ")}`);
4158
+ for (const [cat, vids] of videoIndex.byCategory.entries()) {
4159
+ console.log(` - '${cat}': ${vids.length} videos`);
4304
4160
  }
4305
- videoIndex.allVideos.push(...videos);
4161
+ return { counts, videoIndex };
4306
4162
  }
4307
- }
4308
- if (buildIndex && videoIndex) {
4309
- videoIndex.allVideos.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
4310
- videoIndex.counts = { ...counts };
4311
- }
4312
- const elapsed = performance.now() - startTime;
4313
- console.log(`[S3ClipsService] ${buildIndex ? "Video index and counts" : "Clip counts"} completed in ${elapsed.toFixed(2)}ms - Total: ${counts.total}`);
4314
- if (buildIndex && videoIndex) {
4315
- console.log(`[S3ClipsService] Final video index summary:`);
4316
- console.log(` - VideoIndex ID: ${videoIndex._debugId}`);
4317
- console.log(` - Total videos in allVideos: ${videoIndex.allVideos.length}`);
4318
- console.log(` - Categories in byCategory Map: ${Array.from(videoIndex.byCategory.keys()).join(", ")}`);
4319
- for (const [cat, vids] of videoIndex.byCategory.entries()) {
4320
- console.log(` - '${cat}': ${vids.length} videos`);
4163
+ return counts;
4164
+ } finally {
4165
+ if (buildIndex) {
4166
+ this.isIndexBuilding = false;
4167
+ console.log(`[S3ClipsService] Index building complete - metadata fetching re-enabled`);
4321
4168
  }
4322
- return { counts, videoIndex };
4323
4169
  }
4324
- return counts;
4325
4170
  }
4326
4171
  async getClipCountsCacheFirst(workspaceId, date, shiftId, buildIndex) {
4327
4172
  const cacheKey = `clip-counts:${workspaceId}:${date}:${shiftId}`;
@@ -4415,18 +4260,18 @@ var S3ClipsService = class {
4415
4260
  * Get a specific clip by index for a category with deduplication
4416
4261
  * @deprecated Use getVideoFromIndex with a pre-built VideoIndex for better performance
4417
4262
  */
4418
- async getClipByIndex(workspaceId, date, shiftId, category, index) {
4419
- const deduplicationKey = `clip-by-index:${workspaceId}:${date}:${shiftId}:${category}:${index}`;
4263
+ async getClipByIndex(workspaceId, date, shiftId, category, index, includeCycleTime = true, includeMetadata = false) {
4264
+ const deduplicationKey = `clip-by-index:${workspaceId}:${date}:${shiftId}:${category}:${index}:${includeCycleTime}:${includeMetadata}`;
4420
4265
  return this.requestCache.deduplicate(
4421
4266
  deduplicationKey,
4422
- () => this.executeGetClipByIndex(workspaceId, date, shiftId, category, index),
4267
+ () => this.executeGetClipByIndex(workspaceId, date, shiftId, category, index, includeCycleTime, includeMetadata),
4423
4268
  "ClipByIndex"
4424
4269
  );
4425
4270
  }
4426
4271
  /**
4427
4272
  * Internal implementation of clip by index fetching
4428
4273
  */
4429
- async executeGetClipByIndex(workspaceId, date, shiftId, category, index) {
4274
+ async executeGetClipByIndex(workspaceId, date, shiftId, category, index, includeCycleTime = true, includeMetadata = false) {
4430
4275
  const categoryPrefix = `sop_violations/${workspaceId}/${date}/${shiftId}/${category}/videos/`;
4431
4276
  try {
4432
4277
  const command = new ListObjectsV2Command({
@@ -4464,10 +4309,8 @@ var S3ClipsService = class {
4464
4309
  workspaceId,
4465
4310
  date,
4466
4311
  shiftId.toString(),
4467
- true,
4468
- // includeCycleTime
4469
- true
4470
- // includeMetadata
4312
+ includeCycleTime,
4313
+ includeMetadata
4471
4314
  );
4472
4315
  }
4473
4316
  } catch (error) {
@@ -4538,7 +4381,7 @@ var S3ClipsService = class {
4538
4381
  }
4539
4382
  let cycleTimeSeconds = null;
4540
4383
  let creationTimestamp = void 0;
4541
- 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")) {
4384
+ if (includeMetadata) {
4542
4385
  const metadata = await this.getFullMetadata(uri);
4543
4386
  if (metadata) {
4544
4387
  if (metadata.original_task_metadata?.cycle_time) {
@@ -4546,6 +4389,8 @@ var S3ClipsService = class {
4546
4389
  }
4547
4390
  creationTimestamp = metadata.upload_timestamp || metadata.original_task_metadata?.timestamp || metadata.creation_timestamp || metadata[""];
4548
4391
  }
4392
+ } 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")) {
4393
+ cycleTimeSeconds = null;
4549
4394
  }
4550
4395
  const cloudfrontPlaylistUrl = this.s3UriToCloudfront(uri);
4551
4396
  const { type: videoType, timestamp: videoTimestamp } = parsedInfo;
@@ -4617,7 +4462,8 @@ var S3ClipsService = class {
4617
4462
  date,
4618
4463
  shiftId.toString(),
4619
4464
  includeCycleTime || false,
4620
- includeMetadata || (!!timestampStart || !!timestampEnd)
4465
+ includeMetadata || false
4466
+ // Never fetch metadata for timestamp filtering to prevent flooding
4621
4467
  );
4622
4468
  });
4623
4469
  const videoResults = await Promise.all(videoPromises);
@@ -4688,6 +4534,7 @@ var VideoPrefetchManager = class extends EventEmitter {
4688
4534
  }
4689
4535
  /**
4690
4536
  * Get or create S3 service instance for dashboard config
4537
+ * Public method to allow sharing the same S3Service instance across components
4691
4538
  */
4692
4539
  getS3Service(dashboardConfig) {
4693
4540
  const configKey = JSON.stringify(dashboardConfig.s3Config);
@@ -4757,75 +4604,80 @@ var VideoPrefetchManager = class extends EventEmitter {
4757
4604
  * Perform the actual prefetch work
4758
4605
  */
4759
4606
  async performPrefetchWork(key, params, s3Service, buildIndex) {
4760
- const cacheKey = `clip-counts:${params.workspaceId}:${params.date}:${params.shift}`;
4761
- const cachedResult = await smartVideoCache.getClipCounts(cacheKey);
4762
- if (cachedResult) {
4763
- console.log(`[VideoPrefetchManager] Found cached data for ${key}`);
4764
- this.updateRequestStatus(key, "fully_indexed" /* FULLY_INDEXED */, cachedResult);
4765
- return cachedResult;
4766
- }
4767
- if (buildIndex) {
4768
- const countsOnly = await s3Service.getClipCounts(
4607
+ s3Service.setPrefetchMode(true);
4608
+ try {
4609
+ const cacheKey = `clip-counts:${params.workspaceId}:${params.date}:${params.shift}`;
4610
+ const cachedResult = await smartVideoCache.getClipCounts(cacheKey);
4611
+ if (cachedResult) {
4612
+ console.log(`[VideoPrefetchManager] Found cached data for ${key}`);
4613
+ this.updateRequestStatus(key, "fully_indexed" /* FULLY_INDEXED */, cachedResult);
4614
+ return cachedResult;
4615
+ }
4616
+ if (buildIndex) {
4617
+ const countsOnly = await s3Service.getClipCounts(
4618
+ params.workspaceId,
4619
+ params.date,
4620
+ params.shift
4621
+ );
4622
+ if (typeof countsOnly === "object" && countsOnly !== null) {
4623
+ const renderReadyData = {
4624
+ counts: countsOnly,
4625
+ videoIndex: {
4626
+ byCategory: /* @__PURE__ */ new Map(),
4627
+ allVideos: [],
4628
+ counts: countsOnly,
4629
+ workspaceId: params.workspaceId,
4630
+ date: params.date,
4631
+ shiftId: params.shift,
4632
+ lastUpdated: /* @__PURE__ */ new Date(),
4633
+ _debugId: `vid_RENDER_READY_${Date.now()}_${Math.random().toString(36).substring(7)}`
4634
+ }
4635
+ };
4636
+ this.updateRequestStatus(key, "render_ready" /* RENDER_READY */, renderReadyData);
4637
+ console.log(`[VideoPrefetchManager] Render ready, building full index for ${key}`);
4638
+ }
4639
+ }
4640
+ const fullResult = await s3Service.getClipCountsCacheFirst(
4769
4641
  params.workspaceId,
4770
4642
  params.date,
4771
- params.shift
4643
+ params.shift,
4644
+ true
4772
4645
  );
4773
- if (typeof countsOnly === "object" && countsOnly !== null) {
4774
- const renderReadyData = {
4775
- counts: countsOnly,
4646
+ if (fullResult && typeof fullResult === "object" && "videoIndex" in fullResult) {
4647
+ const clipCountsWithIndex = fullResult;
4648
+ if (clipCountsWithIndex.videoIndex.allVideos.length > 0) {
4649
+ console.log(`[VideoPrefetchManager] Video index properly populated with ${clipCountsWithIndex.videoIndex.allVideos.length} videos`);
4650
+ } else {
4651
+ console.warn(`[VideoPrefetchManager] Video index is empty, but counts available:`, clipCountsWithIndex.counts);
4652
+ }
4653
+ await smartVideoCache.setClipCounts(cacheKey, clipCountsWithIndex, 5 * 60);
4654
+ this.updateRequestStatus(key, "fully_indexed" /* FULLY_INDEXED */, clipCountsWithIndex);
4655
+ console.log(`[VideoPrefetchManager] Fully indexed: ${key} (${clipCountsWithIndex.videoIndex.allVideos.length} videos)`);
4656
+ return clipCountsWithIndex;
4657
+ } else if (fullResult && typeof fullResult === "object") {
4658
+ console.log(`[VideoPrefetchManager] Received counts only, building fallback data structure`);
4659
+ const countsResult = fullResult;
4660
+ const fallbackData = {
4661
+ counts: countsResult,
4776
4662
  videoIndex: {
4777
4663
  byCategory: /* @__PURE__ */ new Map(),
4778
4664
  allVideos: [],
4779
- counts: countsOnly,
4665
+ counts: countsResult,
4780
4666
  workspaceId: params.workspaceId,
4781
4667
  date: params.date,
4782
4668
  shiftId: params.shift,
4783
4669
  lastUpdated: /* @__PURE__ */ new Date(),
4784
- _debugId: `vid_RENDER_READY_${Date.now()}_${Math.random().toString(36).substring(7)}`
4670
+ _debugId: `vid_FALLBACK_${Date.now()}_${Math.random().toString(36).substring(7)}`
4785
4671
  }
4786
4672
  };
4787
- this.updateRequestStatus(key, "render_ready" /* RENDER_READY */, renderReadyData);
4788
- console.log(`[VideoPrefetchManager] Render ready, building full index for ${key}`);
4789
- }
4790
- }
4791
- const fullResult = await s3Service.getClipCountsCacheFirst(
4792
- params.workspaceId,
4793
- params.date,
4794
- params.shift,
4795
- true
4796
- );
4797
- if (fullResult && typeof fullResult === "object" && "videoIndex" in fullResult) {
4798
- const clipCountsWithIndex = fullResult;
4799
- if (clipCountsWithIndex.videoIndex.allVideos.length > 0) {
4800
- console.log(`[VideoPrefetchManager] Video index properly populated with ${clipCountsWithIndex.videoIndex.allVideos.length} videos`);
4673
+ this.updateRequestStatus(key, "render_ready" /* RENDER_READY */, fallbackData);
4674
+ return fallbackData;
4801
4675
  } else {
4802
- console.warn(`[VideoPrefetchManager] Video index is empty, but counts available:`, clipCountsWithIndex.counts);
4803
- }
4804
- await smartVideoCache.setClipCounts(cacheKey, clipCountsWithIndex, 5 * 60);
4805
- this.updateRequestStatus(key, "fully_indexed" /* FULLY_INDEXED */, clipCountsWithIndex);
4806
- console.log(`[VideoPrefetchManager] Fully indexed: ${key} (${clipCountsWithIndex.videoIndex.allVideos.length} videos)`);
4807
- return clipCountsWithIndex;
4808
- } else if (fullResult && typeof fullResult === "object") {
4809
- console.log(`[VideoPrefetchManager] Received counts only, building fallback data structure`);
4810
- const countsResult = fullResult;
4811
- const fallbackData = {
4812
- counts: countsResult,
4813
- videoIndex: {
4814
- byCategory: /* @__PURE__ */ new Map(),
4815
- allVideos: [],
4816
- counts: countsResult,
4817
- workspaceId: params.workspaceId,
4818
- date: params.date,
4819
- shiftId: params.shift,
4820
- lastUpdated: /* @__PURE__ */ new Date(),
4821
- _debugId: `vid_FALLBACK_${Date.now()}_${Math.random().toString(36).substring(7)}`
4822
- }
4823
- };
4824
- this.updateRequestStatus(key, "render_ready" /* RENDER_READY */, fallbackData);
4825
- return fallbackData;
4826
- } else {
4827
- console.error(`[VideoPrefetchManager] Received null/undefined result from S3 service`);
4828
- throw new Error("Failed to fetch clip counts from S3 service");
4676
+ console.error(`[VideoPrefetchManager] Received null/undefined result from S3 service`);
4677
+ throw new Error("Failed to fetch clip counts from S3 service");
4678
+ }
4679
+ } finally {
4680
+ s3Service.setPrefetchMode(false);
4829
4681
  }
4830
4682
  }
4831
4683
  /**
@@ -5088,9 +4940,13 @@ var AuthProvider = ({ children }) => {
5088
4940
  const [loading, setLoading] = useState(true);
5089
4941
  const [error, setError] = useState(null);
5090
4942
  const router = useRouter();
5091
- const userProfileTable = authConfig?.userProfileTable;
5092
- const roleColumn = authConfig?.roleColumn || "role";
4943
+ authConfig?.userProfileTable;
4944
+ authConfig?.roleColumn || "role";
5093
4945
  const fetchUserDetails = useCallback(async (supabaseUser) => {
4946
+ console.log("[fetchUserDetails] Called for user:", supabaseUser.id, {
4947
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4948
+ stackTrace: new Error().stack?.split("\n").slice(1, 4).join(" -> ")
4949
+ });
5094
4950
  if (!supabaseUser) return null;
5095
4951
  const basicUser = {
5096
4952
  id: supabaseUser.id,
@@ -5099,42 +4955,34 @@ var AuthProvider = ({ children }) => {
5099
4955
  if (!supabase) return basicUser;
5100
4956
  try {
5101
4957
  const timeoutPromise = new Promise(
5102
- (_, reject) => setTimeout(() => reject(new Error("Profile fetch timeout")), 5e3)
4958
+ (_, reject) => setTimeout(() => {
4959
+ console.log("[fetchUserDetails] Timeout triggered after 2 seconds");
4960
+ reject(new Error("Profile fetch timeout"));
4961
+ }, 2e3)
5103
4962
  );
5104
4963
  const rolePromise = supabase.from("user_roles").select("role_level").eq("user_id", supabaseUser.id).single();
5105
- let profilePromise = null;
5106
- if (userProfileTable) {
5107
- profilePromise = supabase.from(userProfileTable).select(roleColumn).eq("id", supabaseUser.id).single();
5108
- }
5109
- const [roleResult, profileResult] = await Promise.race([
5110
- Promise.all([
5111
- rolePromise,
5112
- profilePromise || Promise.resolve(null)
5113
- ]),
5114
- timeoutPromise.then(() => {
5115
- throw new Error("Timeout");
5116
- })
4964
+ const roleResult = await Promise.race([
4965
+ rolePromise,
4966
+ timeoutPromise
4967
+ // Fixed: removed .then() which was causing the bug
5117
4968
  ]);
5118
4969
  let roleLevel = void 0;
5119
4970
  if (roleResult && !roleResult.error && roleResult.data) {
5120
4971
  roleLevel = roleResult.data.role_level;
5121
- } else if (roleResult?.error) {
4972
+ } else if (roleResult?.error && roleResult.error.code !== "PGRST116") {
5122
4973
  console.log("Error fetching role_level:", roleResult.error.message);
5123
4974
  }
5124
- let roleValue = void 0;
5125
- if (profileResult && !profileResult.error && profileResult.data) {
5126
- roleValue = profileResult.data[roleColumn];
5127
- }
5128
4975
  return {
5129
4976
  ...basicUser,
5130
- role: roleValue,
5131
4977
  role_level: roleLevel
5132
4978
  };
5133
4979
  } catch (err) {
5134
- console.error("Error fetching user details:", err);
4980
+ if (err instanceof Error && err.message.includes("timeout")) {
4981
+ console.warn("Auth fetch timeout - using basic user info");
4982
+ }
5135
4983
  return basicUser;
5136
4984
  }
5137
- }, [supabase, userProfileTable, roleColumn]);
4985
+ }, [supabase]);
5138
4986
  useEffect(() => {
5139
4987
  if (!supabase) return;
5140
4988
  let mounted = true;
@@ -5145,6 +4993,7 @@ var AuthProvider = ({ children }) => {
5145
4993
  }
5146
4994
  }, 1e4);
5147
4995
  const initializeAuth = async () => {
4996
+ const startTime = performance.now();
5148
4997
  try {
5149
4998
  const { data: { session: initialSession }, error: sessionError } = await supabase.auth.getSession();
5150
4999
  if (!mounted) return;
@@ -5187,12 +5036,38 @@ var AuthProvider = ({ children }) => {
5187
5036
  if (mounted) {
5188
5037
  setLoading(false);
5189
5038
  clearTimeout(safetyTimeout);
5039
+ const duration = performance.now() - startTime;
5040
+ if (duration > 3e3) {
5041
+ console.warn(`[Auth] Initialization took ${duration.toFixed(0)}ms - consider optimization`);
5042
+ } else if (process.env.NODE_ENV === "development") {
5043
+ console.log(`[Auth] Initialized in ${duration.toFixed(0)}ms`);
5044
+ }
5190
5045
  }
5191
5046
  }
5192
5047
  };
5193
5048
  initializeAuth();
5194
- const { data: { subscription } } = supabase.auth.onAuthStateChange(async (_event, currentSession) => {
5049
+ const { data: { subscription } } = supabase.auth.onAuthStateChange(async (event, currentSession) => {
5195
5050
  if (!mounted) return;
5051
+ console.log("[AuthContext] Auth event fired:", {
5052
+ event,
5053
+ sessionId: currentSession?.user?.id,
5054
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
5055
+ currentPath: typeof window !== "undefined" ? window.location.pathname : "unknown"
5056
+ });
5057
+ if (event === "TOKEN_REFRESHED") {
5058
+ if (session?.user?.id === currentSession?.user?.id && session?.user?.email === currentSession?.user?.email) {
5059
+ console.log("[AuthContext] Skipping TOKEN_REFRESHED - session unchanged");
5060
+ return;
5061
+ }
5062
+ }
5063
+ if (event !== "TOKEN_REFRESHED" && currentSession?.user) {
5064
+ console.log("[AuthContext] Non-TOKEN_REFRESHED event will trigger fetchUserDetails:", event);
5065
+ }
5066
+ const sessionChanged = session?.user?.id !== currentSession?.user?.id || session?.user?.email !== currentSession?.user?.email;
5067
+ if (!sessionChanged && user) {
5068
+ console.log("[AuthContext] Session and user unchanged, skipping update");
5069
+ return;
5070
+ }
5196
5071
  setSession(currentSession);
5197
5072
  setUser(null);
5198
5073
  setLoading(false);
@@ -5567,7 +5442,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5567
5442
  const [error, setError] = useState(null);
5568
5443
  const updateQueueRef = useRef(false);
5569
5444
  const isFetchingRef = useRef(false);
5570
- const timeoutRef = useRef(null);
5571
5445
  const channelRef = useRef(null);
5572
5446
  const schema = databaseConfig.schema ?? "public";
5573
5447
  const companyId = entityConfig.companyId || "";
@@ -5576,7 +5450,7 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5576
5450
  const defaultTimezone = dateTimeConfig.defaultTimezone;
5577
5451
  const workspaceMetricsBaseTable = databaseConfig.tables?.workspaces ?? "workspace_metrics";
5578
5452
  const workspaceActionsTable = databaseConfig.tables?.actions ?? "workspace_actions";
5579
- const fetchMetrics = useCallback(async (skipCache = false) => {
5453
+ const fetchMetrics = useCallback(async () => {
5580
5454
  if (!workspaceId || isFetchingRef.current) return;
5581
5455
  try {
5582
5456
  isFetchingRef.current = true;
@@ -5585,28 +5459,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5585
5459
  const queryShiftId = shiftId !== void 0 ? shiftId : currentShift.shiftId;
5586
5460
  console.log("[useWorkspaceDetailedMetrics] Using shift ID:", queryShiftId, "from input shift:", shiftId);
5587
5461
  console.log("[useWorkspaceDetailedMetrics] Using date:", queryDate, "from input date:", date);
5588
- const cacheKey = cacheService.generateKey(
5589
- "workspace-detailed-metrics",
5590
- workspaceId,
5591
- queryDate,
5592
- queryShiftId,
5593
- companyId
5594
- );
5595
- if (!skipCache) {
5596
- const cachedData = cacheService.get(cacheKey, {
5597
- storage: "memory",
5598
- duration: 5 * 60 * 1e3
5599
- // 5 minutes
5600
- });
5601
- if (cachedData) {
5602
- console.log("[useWorkspaceDetailedMetrics] Using cached data for:", cacheKey);
5603
- setMetrics(cachedData);
5604
- setIsLoading(false);
5605
- updateQueueRef.current = false;
5606
- isFetchingRef.current = false;
5607
- return;
5608
- }
5609
- }
5610
5462
  console.log(`[useWorkspaceDetailedMetrics] Querying ${metricsTable} for workspace: ${workspaceId}, date: ${queryDate}, shift: ${queryShiftId}`);
5611
5463
  const { data, error: fetchError } = await supabase.from(metricsTable).select("*").eq("workspace_id", workspaceId).eq("date", queryDate).eq("shift_id", queryShiftId).maybeSingle();
5612
5464
  if (fetchError) throw fetchError;
@@ -5709,18 +5561,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5709
5561
  setIsLoading(false);
5710
5562
  updateQueueRef.current = false;
5711
5563
  isFetchingRef.current = false;
5712
- const fallbackCacheKey = cacheService.generateKey(
5713
- "workspace-detailed-metrics",
5714
- workspaceId,
5715
- recentData.date,
5716
- recentData.shift_id,
5717
- companyId
5718
- );
5719
- cacheService.set(fallbackCacheKey, transformedData2, {
5720
- storage: "memory",
5721
- duration: 2 * 60 * 1e3
5722
- // 2 minutes for fallback data
5723
- });
5724
5564
  return;
5725
5565
  } else {
5726
5566
  console.warn("[useWorkspaceDetailedMetrics] No data found for workspace:", workspaceId, "at all");
@@ -5834,11 +5674,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5834
5674
  ...data.sop_check !== void 0 && { sop_check: data.sop_check }
5835
5675
  };
5836
5676
  setMetrics(transformedData);
5837
- cacheService.set(cacheKey, transformedData, {
5838
- storage: "memory",
5839
- duration: 5 * 60 * 1e3
5840
- // 5 minutes
5841
- });
5842
5677
  } catch (err) {
5843
5678
  console.error("Error fetching workspace metrics:", err);
5844
5679
  setError({ message: err.message, code: err.code });
@@ -5851,12 +5686,7 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5851
5686
  const queueUpdate = useCallback(() => {
5852
5687
  if (!workspaceId || updateQueueRef.current) return;
5853
5688
  updateQueueRef.current = true;
5854
- if (timeoutRef.current) {
5855
- clearTimeout(timeoutRef.current);
5856
- }
5857
- timeoutRef.current = setTimeout(() => {
5858
- fetchMetrics();
5859
- }, 500);
5689
+ fetchMetrics();
5860
5690
  }, [fetchMetrics, workspaceId]);
5861
5691
  const setupSubscription = useCallback(() => {
5862
5692
  if (!workspaceId) return;
@@ -5873,7 +5703,7 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5873
5703
  },
5874
5704
  async (payload) => {
5875
5705
  console.log(`Received ${metricsTablePrefix} update:`, payload);
5876
- await fetchMetrics(true);
5706
+ await fetchMetrics();
5877
5707
  }
5878
5708
  ).subscribe((status) => {
5879
5709
  console.log(`Workspace detailed metrics subscription status:`, status);
@@ -5907,14 +5737,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5907
5737
  matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
5908
5738
  });
5909
5739
  if (payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId) {
5910
- const cacheKey = cacheService.generateKey(
5911
- "workspace-detailed-metrics",
5912
- workspaceId,
5913
- operationalDate,
5914
- queryShiftId,
5915
- companyId
5916
- );
5917
- cacheService.delete(cacheKey, { storage: "memory" });
5918
5740
  queueUpdate();
5919
5741
  }
5920
5742
  }
@@ -5940,14 +5762,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5940
5762
  matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
5941
5763
  });
5942
5764
  if (payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId) {
5943
- const cacheKey = cacheService.generateKey(
5944
- "workspace-detailed-metrics",
5945
- workspaceId,
5946
- operationalDate,
5947
- queryShiftId,
5948
- companyId
5949
- );
5950
- cacheService.delete(cacheKey, { storage: "memory" });
5951
5765
  queueUpdate();
5952
5766
  }
5953
5767
  }
@@ -5973,14 +5787,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5973
5787
  matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
5974
5788
  });
5975
5789
  if (payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId) {
5976
- const cacheKey = cacheService.generateKey(
5977
- "workspace-detailed-metrics",
5978
- workspaceId,
5979
- operationalDate,
5980
- queryShiftId,
5981
- companyId
5982
- );
5983
- cacheService.delete(cacheKey, { storage: "memory" });
5984
5790
  queueUpdate();
5985
5791
  }
5986
5792
  }
@@ -5991,9 +5797,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5991
5797
  fetchMetrics();
5992
5798
  setupSubscription();
5993
5799
  return () => {
5994
- if (timeoutRef.current) {
5995
- clearTimeout(timeoutRef.current);
5996
- }
5997
5800
  channels.forEach((channel) => {
5998
5801
  console.log("Cleaning up channel subscription");
5999
5802
  supabase.removeChannel(channel);
@@ -6007,7 +5810,7 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
6007
5810
  metrics: metrics2,
6008
5811
  isLoading,
6009
5812
  error,
6010
- refetch: () => fetchMetrics(true)
5813
+ refetch: () => fetchMetrics()
6011
5814
  // Force refresh without cache
6012
5815
  };
6013
5816
  };
@@ -6452,33 +6255,6 @@ var useLeaderboardMetrics = (lineId, topCount = 10) => {
6452
6255
  refetch: fetchLeaderboardData
6453
6256
  };
6454
6257
  };
6455
- var CACHE_KEY_PREFIX = "dashboard_metrics_cache_";
6456
- var CACHE_DURATION = 5 * 60 * 1e3;
6457
- var getCache = (lineId) => {
6458
- if (typeof window === "undefined") return null;
6459
- try {
6460
- const cached = localStorage.getItem(`${CACHE_KEY_PREFIX}${lineId}`);
6461
- if (!cached) return null;
6462
- const parsedCache = JSON.parse(cached);
6463
- if (!parsedCache.lastUpdated || Date.now() - parsedCache.lastUpdated > CACHE_DURATION) {
6464
- localStorage.removeItem(`${CACHE_KEY_PREFIX}${lineId}`);
6465
- return null;
6466
- }
6467
- return parsedCache;
6468
- } catch {
6469
- return null;
6470
- }
6471
- };
6472
- var setCache = (lineId, metrics2) => {
6473
- if (typeof window === "undefined") return;
6474
- try {
6475
- localStorage.setItem(`${CACHE_KEY_PREFIX}${lineId}`, JSON.stringify({
6476
- ...metrics2,
6477
- lastUpdated: Date.now()
6478
- }));
6479
- } catch {
6480
- }
6481
- };
6482
6258
  var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6483
6259
  const { supabaseUrl, supabaseKey } = useDashboardConfig();
6484
6260
  const entityConfig = useEntityConfig();
@@ -6489,22 +6265,20 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6489
6265
  const configuredLineMetricsTable = databaseConfig?.tables?.lineMetrics ?? "line_metrics";
6490
6266
  const schema = databaseConfig?.schema ?? "public";
6491
6267
  const supabase = useSupabase();
6492
- const [metrics2, setMetrics] = useState(() => getCache(lineId) || { workspaceMetrics: [], lineMetrics: [] });
6493
- const [isLoading, setIsLoading] = useState(() => !getCache(lineId));
6268
+ const [metrics2, setMetrics] = useState({ workspaceMetrics: [], lineMetrics: [] });
6269
+ const [isLoading, setIsLoading] = useState(true);
6494
6270
  const [error, setError] = useState(null);
6495
6271
  const lineIdRef = useRef(lineId);
6496
6272
  const isFetchingRef = useRef(false);
6497
6273
  const updateQueueRef = useRef(false);
6498
- const timeoutRef = useRef(null);
6499
6274
  const companySpecificMetricsTable = useMemo(
6500
6275
  () => getCompanyMetricsTableName(entityConfig.companyId, "performance_metrics"),
6501
6276
  [entityConfig.companyId]
6502
6277
  );
6503
6278
  useEffect(() => {
6504
6279
  lineIdRef.current = lineId;
6505
- const cachedData = getCache(lineId);
6506
- setMetrics(cachedData || { workspaceMetrics: [], lineMetrics: [] });
6507
- setIsLoading(!cachedData);
6280
+ setMetrics({ workspaceMetrics: [], lineMetrics: [] });
6281
+ setIsLoading(true);
6508
6282
  }, [lineId]);
6509
6283
  const fetchAllMetrics = useCallback(async () => {
6510
6284
  const currentLineIdToUse = lineIdRef.current;
@@ -6516,9 +6290,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6516
6290
  return;
6517
6291
  }
6518
6292
  isFetchingRef.current = true;
6519
- if (!getCache(currentLineIdToUse)) {
6520
- setIsLoading(true);
6521
- }
6293
+ setIsLoading(true);
6522
6294
  setError(null);
6523
6295
  try {
6524
6296
  const currentShiftDetails = getCurrentShift(defaultTimezone, shiftConfig);
@@ -6543,7 +6315,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6543
6315
  lineMetrics: []
6544
6316
  };
6545
6317
  setMetrics(newMetricsState2);
6546
- setCache(currentLineIdToUse, newMetricsState2);
6547
6318
  return;
6548
6319
  }
6549
6320
  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);
@@ -6595,7 +6366,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6595
6366
  lineMetrics: lineData || []
6596
6367
  };
6597
6368
  setMetrics(newMetricsState);
6598
- setCache(currentLineIdToUse, newMetricsState);
6599
6369
  } catch (err) {
6600
6370
  setError({ message: err.message, code: err.code || "FETCH_ERROR" });
6601
6371
  } finally {
@@ -6621,12 +6391,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6621
6391
  return;
6622
6392
  }
6623
6393
  updateQueueRef.current = true;
6624
- if (timeoutRef.current) {
6625
- clearTimeout(timeoutRef.current);
6626
- }
6627
- timeoutRef.current = setTimeout(() => {
6628
- fetchAllMetrics();
6629
- }, 500);
6394
+ fetchAllMetrics();
6630
6395
  }, [fetchAllMetrics, supabase]);
6631
6396
  useEffect(() => {
6632
6397
  if (lineId && supabase) {
@@ -6671,9 +6436,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6671
6436
  onLineMetricsUpdate?.();
6672
6437
  });
6673
6438
  return () => {
6674
- if (timeoutRef.current) {
6675
- clearTimeout(timeoutRef.current);
6676
- }
6677
6439
  channels.forEach((channel) => {
6678
6440
  supabase?.removeChannel(channel).catch((err) => console.error("[useDashboardMetrics] Error removing channel:", err.message));
6679
6441
  });
@@ -6702,30 +6464,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6702
6464
  refetch: fetchAllMetrics
6703
6465
  };
6704
6466
  };
6705
- var CACHE_KEY_PREFIX2 = "line_kpis_cache_";
6706
- var CACHE_DURATION2 = 5 * 60 * 1e3;
6707
- var getCache2 = (lineId) => {
6708
- if (typeof window === "undefined") return null;
6709
- try {
6710
- const cached = localStorage.getItem(`${CACHE_KEY_PREFIX2}${lineId}`);
6711
- if (!cached) return null;
6712
- const parsedCache = JSON.parse(cached);
6713
- if (!parsedCache.timestamp || Date.now() - parsedCache.timestamp > CACHE_DURATION2) {
6714
- localStorage.removeItem(`${CACHE_KEY_PREFIX2}${lineId}`);
6715
- return null;
6716
- }
6717
- return parsedCache.data;
6718
- } catch (error) {
6719
- return null;
6720
- }
6721
- };
6722
- var setCache2 = (lineId, data) => {
6723
- if (typeof window === "undefined") return;
6724
- try {
6725
- localStorage.setItem(`${CACHE_KEY_PREFIX2}${lineId}`, JSON.stringify({ data, timestamp: Date.now() }));
6726
- } catch (error) {
6727
- }
6728
- };
6729
6467
  var useLineKPIs = ({ lineId }) => {
6730
6468
  useDashboardConfig();
6731
6469
  const entityConfig = useEntityConfig();
@@ -6737,13 +6475,12 @@ var useLineKPIs = ({ lineId }) => {
6737
6475
  const dashboardServiceInstance = useMemo(() => {
6738
6476
  return dashboardService;
6739
6477
  }, []);
6740
- const [kpis, setKPIs] = useState(() => getCache2(lineId));
6741
- const [isLoading, setIsLoading] = useState(!getCache2(lineId));
6478
+ const [kpis, setKPIs] = useState(null);
6479
+ const [isLoading, setIsLoading] = useState(true);
6742
6480
  const [error, setError] = useState(null);
6743
6481
  const lineIdRef = useRef(lineId);
6744
6482
  const isFetchingRef = useRef(false);
6745
6483
  const updateQueueRef = useRef(false);
6746
- const timeoutRef = useRef(null);
6747
6484
  const defaultTimezone = dateTimeConfig.defaultTimezone;
6748
6485
  const schema = databaseConfig.schema ?? "public";
6749
6486
  const lineMetricsTable = databaseConfig.tables?.lineMetrics ?? "line_metrics";
@@ -6774,7 +6511,6 @@ var useLineKPIs = ({ lineId }) => {
6774
6511
  if (lineInfo) {
6775
6512
  const newKPIs = dashboardServiceInstance.calculateKPIs(lineInfo);
6776
6513
  setKPIs(newKPIs);
6777
- setCache2(currentLineId, newKPIs);
6778
6514
  } else {
6779
6515
  setKPIs(null);
6780
6516
  }
@@ -6791,12 +6527,7 @@ var useLineKPIs = ({ lineId }) => {
6791
6527
  const queueUpdate = useCallback(() => {
6792
6528
  if (updateQueueRef.current) return;
6793
6529
  updateQueueRef.current = true;
6794
- if (timeoutRef.current) {
6795
- clearTimeout(timeoutRef.current);
6796
- }
6797
- timeoutRef.current = setTimeout(() => {
6798
- fetchKPIs();
6799
- }, 500);
6530
+ fetchKPIs();
6800
6531
  }, [fetchKPIs]);
6801
6532
  useEffect(() => {
6802
6533
  const currentLineId = lineIdRef.current;
@@ -6854,9 +6585,6 @@ var useLineKPIs = ({ lineId }) => {
6854
6585
  activeChannels.push(csChannel);
6855
6586
  }
6856
6587
  return () => {
6857
- if (timeoutRef.current) {
6858
- clearTimeout(timeoutRef.current);
6859
- }
6860
6588
  activeChannels.forEach((ch) => supabase.removeChannel(ch).catch((err) => console.error("[useLineKPIs] Error removing KPI channel:", err)));
6861
6589
  };
6862
6590
  }, [supabase, lineId, fetchKPIs, queueUpdate, dashboardServiceInstance, entityConfig, schema, lineMetricsTable, companySpecificMetricsTable, defaultTimezone, shiftConfig, kpis, isFactoryView]);
@@ -6885,7 +6613,6 @@ var useRealtimeLineMetrics = ({
6885
6613
  const [initialized, setInitialized] = useState(false);
6886
6614
  const lineIdRef = useRef(null);
6887
6615
  const updateQueueRef = useRef(false);
6888
- const timeoutRef = useRef(null);
6889
6616
  const isFetchingRef = useRef(false);
6890
6617
  const channelsRef = useRef([]);
6891
6618
  const currentShift = useMemo(() => getCurrentShift(dateTimeConfig.defaultTimezone || "Asia/Kolkata", shiftConfig), [dateTimeConfig.defaultTimezone, shiftConfig]);
@@ -7098,12 +6825,7 @@ var useRealtimeLineMetrics = ({
7098
6825
  const queueUpdate = useCallback(() => {
7099
6826
  if (updateQueueRef.current) return;
7100
6827
  updateQueueRef.current = true;
7101
- if (timeoutRef.current) {
7102
- clearTimeout(timeoutRef.current);
7103
- }
7104
- timeoutRef.current = setTimeout(() => {
7105
- fetchData();
7106
- }, 500);
6828
+ fetchData();
7107
6829
  }, [fetchData]);
7108
6830
  const setupSubscriptions = useCallback(() => {
7109
6831
  if (channelsRef.current.length > 0) {
@@ -7181,9 +6903,6 @@ var useRealtimeLineMetrics = ({
7181
6903
  }
7182
6904
  setupSubscriptions();
7183
6905
  return () => {
7184
- if (timeoutRef.current) {
7185
- clearTimeout(timeoutRef.current);
7186
- }
7187
6906
  if (channelsRef.current.length > 0) {
7188
6907
  channelsRef.current.forEach((channel) => {
7189
6908
  if (process.env.NODE_ENV === "development") {
@@ -7269,16 +6988,12 @@ var useTargets = (options) => {
7269
6988
  };
7270
6989
  var DEFAULT_SHIFTS_TABLE_NAME = "shift_configurations";
7271
6990
  var useShifts = () => {
7272
- const { supabaseUrl, supabaseKey } = useDashboardConfig();
7273
6991
  const { companyId } = useEntityConfig();
7274
6992
  const { tables } = useDatabaseConfig();
6993
+ const supabase = useSupabase();
7275
6994
  const [shifts, setShifts] = useState([]);
7276
6995
  const [isLoading, setIsLoading] = useState(true);
7277
6996
  const [error, setError] = useState(null);
7278
- const supabase = useMemo(() => {
7279
- if (!supabaseUrl || !supabaseKey) return null;
7280
- return createClient(supabaseUrl, supabaseKey);
7281
- }, [supabaseUrl, supabaseKey]);
7282
6997
  const shiftsTable = tables?.shiftConfigurations || DEFAULT_SHIFTS_TABLE_NAME;
7283
6998
  const fetchData = useCallback(async () => {
7284
6999
  if (!supabase) {
@@ -7977,6 +7692,7 @@ var runtimeWorkspaceDisplayNames = {};
7977
7692
  var isInitialized = false;
7978
7693
  var isInitializing = false;
7979
7694
  var initializedWithLineIds = [];
7695
+ var initializationPromise = null;
7980
7696
  function getCurrentLineIds() {
7981
7697
  try {
7982
7698
  const config = _getDashboardConfigInstance();
@@ -7997,52 +7713,79 @@ function getCurrentLineIds() {
7997
7713
  }
7998
7714
  async function initializeWorkspaceDisplayNames(explicitLineId) {
7999
7715
  console.log("\u{1F504} initializeWorkspaceDisplayNames called", { isInitialized, isInitializing, explicitLineId });
8000
- if (isInitialized || isInitializing) return;
8001
- isInitializing = true;
8002
- try {
8003
- console.log("\u{1F504} Starting Supabase workspace display names initialization...");
8004
- let targetLineIds = [];
8005
- if (explicitLineId) {
8006
- targetLineIds = [explicitLineId];
8007
- } else {
8008
- targetLineIds = getCurrentLineIds();
7716
+ if (isInitialized) return;
7717
+ if (initializationPromise) {
7718
+ console.log("\u{1F504} Already initializing, waiting for existing initialization...");
7719
+ await initializationPromise;
7720
+ return;
7721
+ }
7722
+ initializationPromise = (async () => {
7723
+ isInitializing = true;
7724
+ try {
7725
+ console.log("\u{1F504} Starting Supabase workspace display names initialization...");
7726
+ let targetLineIds = [];
7727
+ if (explicitLineId) {
7728
+ targetLineIds = [explicitLineId];
7729
+ } else {
7730
+ targetLineIds = getCurrentLineIds();
7731
+ }
7732
+ console.log("\u{1F504} Target line IDs for workspace filtering:", targetLineIds);
7733
+ runtimeWorkspaceDisplayNames = {};
7734
+ if (targetLineIds.length > 0) {
7735
+ for (const lineId of targetLineIds) {
7736
+ console.log(`\u{1F504} Fetching workspaces for line: ${lineId}`);
7737
+ const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
7738
+ runtimeWorkspaceDisplayNames[lineId] = {};
7739
+ lineDisplayNamesMap.forEach((displayName, workspaceId) => {
7740
+ runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
7741
+ });
7742
+ console.log(`\u2705 Stored ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
7743
+ }
7744
+ } else {
7745
+ console.warn("\u26A0\uFE0F No line IDs found, fetching all workspaces (less efficient)");
7746
+ const allWorkspacesMap = await workspaceService.getWorkspaceDisplayNames();
7747
+ runtimeWorkspaceDisplayNames["global"] = {};
7748
+ allWorkspacesMap.forEach((displayName, workspaceId) => {
7749
+ runtimeWorkspaceDisplayNames["global"][workspaceId] = displayName;
7750
+ });
7751
+ }
7752
+ isInitialized = true;
7753
+ initializedWithLineIds = targetLineIds;
7754
+ console.log("\u2705 Workspace display names initialized from Supabase:", runtimeWorkspaceDisplayNames);
7755
+ console.log("\u2705 Initialized with line IDs:", initializedWithLineIds);
7756
+ } catch (error) {
7757
+ console.error("\u274C Failed to initialize workspace display names from Supabase:", error);
7758
+ } finally {
7759
+ isInitializing = false;
7760
+ initializationPromise = null;
8009
7761
  }
8010
- console.log("\u{1F504} Target line IDs for workspace filtering:", targetLineIds);
8011
- runtimeWorkspaceDisplayNames = {};
8012
- if (targetLineIds.length > 0) {
8013
- for (const lineId of targetLineIds) {
8014
- console.log(`\u{1F504} Fetching workspaces for line: ${lineId}`);
7762
+ })();
7763
+ await initializationPromise;
7764
+ }
7765
+ var preInitializeWorkspaceDisplayNames = async (lineId) => {
7766
+ console.log("\u{1F504} preInitializeWorkspaceDisplayNames called for lineId:", lineId);
7767
+ if (isInitialized) {
7768
+ console.log("\u{1F504} Already initialized");
7769
+ if (lineId && !runtimeWorkspaceDisplayNames[lineId]) {
7770
+ console.log(`\u{1F504} Line ${lineId} not in cache, fetching...`);
7771
+ try {
8015
7772
  const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
8016
7773
  runtimeWorkspaceDisplayNames[lineId] = {};
8017
7774
  lineDisplayNamesMap.forEach((displayName, workspaceId) => {
8018
7775
  runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
8019
7776
  });
8020
- console.log(`\u2705 Stored ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
7777
+ console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
7778
+ } catch (error) {
7779
+ console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
8021
7780
  }
8022
- } else {
8023
- console.warn("\u26A0\uFE0F No line IDs found, fetching all workspaces (less efficient)");
8024
- const allWorkspacesMap = await workspaceService.getWorkspaceDisplayNames();
8025
- runtimeWorkspaceDisplayNames["global"] = {};
8026
- allWorkspacesMap.forEach((displayName, workspaceId) => {
8027
- runtimeWorkspaceDisplayNames["global"][workspaceId] = displayName;
8028
- });
8029
7781
  }
8030
- isInitialized = true;
8031
- initializedWithLineIds = targetLineIds;
8032
- console.log("\u2705 Workspace display names initialized from Supabase:", runtimeWorkspaceDisplayNames);
8033
- console.log("\u2705 Initialized with line IDs:", initializedWithLineIds);
8034
- } catch (error) {
8035
- console.error("\u274C Failed to initialize workspace display names from Supabase:", error);
8036
- } finally {
8037
- isInitializing = false;
7782
+ return;
8038
7783
  }
8039
- }
8040
- var preInitializeWorkspaceDisplayNames = async (lineId) => {
8041
- console.log("\u{1F504} preInitializeWorkspaceDisplayNames called for lineId:", lineId);
8042
- if (isInitialized || isInitializing) {
8043
- console.log("\u{1F504} Already initialized or initializing");
7784
+ if (initializationPromise) {
7785
+ console.log("\u{1F504} Already initializing, waiting for completion...");
7786
+ await initializationPromise;
8044
7787
  if (lineId && !runtimeWorkspaceDisplayNames[lineId]) {
8045
- console.log(`\u{1F504} Line ${lineId} not in cache, fetching...`);
7788
+ console.log(`\u{1F504} Line ${lineId} not in cache after init, fetching...`);
8046
7789
  try {
8047
7790
  const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
8048
7791
  runtimeWorkspaceDisplayNames[lineId] = {};
@@ -8060,7 +7803,12 @@ var preInitializeWorkspaceDisplayNames = async (lineId) => {
8060
7803
  };
8061
7804
  var forceRefreshWorkspaceDisplayNames = async (lineId) => {
8062
7805
  console.log("\u{1F504} forceRefreshWorkspaceDisplayNames called for lineId:", lineId);
7806
+ if (initializationPromise) {
7807
+ console.log("\u{1F504} Waiting for existing initialization to complete before refresh...");
7808
+ await initializationPromise;
7809
+ }
8063
7810
  clearWorkspaceDisplayNamesCache();
7811
+ initializationPromise = null;
8064
7812
  await initializeWorkspaceDisplayNames(lineId);
8065
7813
  };
8066
7814
  console.log("\u{1F504} Module loaded, will initialize lazily when first function is called");
@@ -8208,6 +7956,7 @@ var clearWorkspaceDisplayNamesCache = () => {
8208
7956
  isInitialized = false;
8209
7957
  isInitializing = false;
8210
7958
  initializedWithLineIds = [];
7959
+ initializationPromise = null;
8211
7960
  };
8212
7961
 
8213
7962
  // src/lib/hooks/useWorkspaceDisplayNames.ts
@@ -8471,32 +8220,12 @@ var useAllWorkspaceMetrics = (options) => {
8471
8220
  return `${metricsTablePrefix}_${companyId.replace(/-/g, "_")}`;
8472
8221
  }, [entityConfig.companyId]);
8473
8222
  const schema = databaseConfig.schema ?? "public";
8474
- const fetchWorkspaceMetrics = useCallback(async (skipCache = false) => {
8223
+ const fetchWorkspaceMetrics = useCallback(async () => {
8475
8224
  if (!initialized) {
8476
8225
  setLoading(true);
8477
8226
  }
8478
8227
  setError(null);
8479
8228
  try {
8480
- const cacheKey = cacheService.generateKey(
8481
- "all-workspace-metrics",
8482
- entityConfig.companyId,
8483
- queryDate,
8484
- queryShiftId
8485
- );
8486
- if (!skipCache && !loading) {
8487
- const cachedData = cacheService.get(cacheKey, {
8488
- storage: "memory",
8489
- duration: 5 * 60 * 1e3
8490
- // 5 minutes
8491
- });
8492
- if (cachedData) {
8493
- console.log("[useAllWorkspaceMetrics] Using cached data for:", cacheKey);
8494
- setWorkspaces(cachedData);
8495
- setInitialized(true);
8496
- setLoading(false);
8497
- return;
8498
- }
8499
- }
8500
8229
  console.log("Fetching all workspace metrics with params:", {
8501
8230
  queryDate,
8502
8231
  queryShiftId,
@@ -8546,11 +8275,6 @@ var useAllWorkspaceMetrics = (options) => {
8546
8275
  }));
8547
8276
  setWorkspaces(transformedData);
8548
8277
  setInitialized(true);
8549
- cacheService.set(cacheKey, transformedData, {
8550
- storage: "memory",
8551
- duration: 5 * 60 * 1e3
8552
- // 5 minutes
8553
- });
8554
8278
  } catch (err) {
8555
8279
  console.error("Error fetching all workspace metrics:", err);
8556
8280
  setError({ message: err.message, code: err.code || "FETCH_ERROR" });
@@ -8575,14 +8299,7 @@ var useAllWorkspaceMetrics = (options) => {
8575
8299
  },
8576
8300
  async (payload) => {
8577
8301
  console.log("All workspace metrics update received:", payload);
8578
- const cacheKey = cacheService.generateKey(
8579
- "all-workspace-metrics",
8580
- entityConfig.companyId,
8581
- queryDate,
8582
- queryShiftId
8583
- );
8584
- cacheService.delete(cacheKey, { storage: "memory" });
8585
- await fetchWorkspaceMetrics(true);
8302
+ await fetchWorkspaceMetrics();
8586
8303
  }
8587
8304
  ).subscribe();
8588
8305
  return channel2;
@@ -8597,7 +8314,7 @@ var useAllWorkspaceMetrics = (options) => {
8597
8314
  useEffect(() => {
8598
8315
  setInitialized(false);
8599
8316
  }, [queryDate, queryShiftId]);
8600
- const refreshWorkspaces = useCallback(() => fetchWorkspaceMetrics(true), [fetchWorkspaceMetrics]);
8317
+ const refreshWorkspaces = useCallback(() => fetchWorkspaceMetrics(), [fetchWorkspaceMetrics]);
8601
8318
  return useMemo(
8602
8319
  () => ({ workspaces, loading, error, refreshWorkspaces }),
8603
8320
  [workspaces, loading, error, refreshWorkspaces]
@@ -19880,11 +19597,13 @@ var withAuth = (WrappedComponent2, options) => {
19880
19597
  requireAuth: true,
19881
19598
  ...options
19882
19599
  };
19883
- return function WithAuthComponent(props) {
19600
+ const WithAuthComponent = React19.memo(function WithAuthComponent2(props) {
19884
19601
  const { session, loading } = useAuth();
19885
19602
  const router = useRouter();
19886
19603
  React19.useEffect(() => {
19887
- console.log("withAuth state:", { loading, hasSession: !!session, requireAuth: defaultOptions.requireAuth });
19604
+ if (process.env.NODE_ENV === "development" && process.env.DEBUG_AUTH === "true") {
19605
+ console.log("withAuth state:", { loading, hasSession: !!session, requireAuth: defaultOptions.requireAuth });
19606
+ }
19888
19607
  }, [session, loading]);
19889
19608
  React19.useEffect(() => {
19890
19609
  if (!loading && defaultOptions.requireAuth && !session) {
@@ -19899,7 +19618,9 @@ var withAuth = (WrappedComponent2, options) => {
19899
19618
  return null;
19900
19619
  }
19901
19620
  return /* @__PURE__ */ jsx(WrappedComponent2, { ...props });
19902
- };
19621
+ });
19622
+ WithAuthComponent.displayName = `withAuth(${WrappedComponent2.displayName || WrappedComponent2.name || "Component"})`;
19623
+ return WithAuthComponent;
19903
19624
  };
19904
19625
  var LoginPage = ({
19905
19626
  onRateLimitCheck,
@@ -26487,7 +26208,7 @@ var BottlenecksContent = ({
26487
26208
  const [duration, setDuration] = useState(0);
26488
26209
  const [currentIndex, setCurrentIndex] = useState(0);
26489
26210
  const [activeFilter, setActiveFilter] = useState(initialFilter);
26490
- const previousFilterRef = useRef(initialFilter);
26211
+ const previousFilterRef = useRef("");
26491
26212
  const [allVideos, setAllVideos] = useState([]);
26492
26213
  const [isLoading, setIsLoading] = useState(true);
26493
26214
  const [isCategoryLoading, setIsCategoryLoading] = useState(false);
@@ -26537,7 +26258,7 @@ var BottlenecksContent = ({
26537
26258
  console.warn("S3 configuration not found in dashboard config");
26538
26259
  return null;
26539
26260
  }
26540
- return new S3ClipsService(dashboardConfig);
26261
+ return videoPrefetchManager.getS3Service(dashboardConfig);
26541
26262
  }, [dashboardConfig]);
26542
26263
  const {
26543
26264
  data: prefetchData,
@@ -26605,6 +26326,85 @@ var BottlenecksContent = ({
26605
26326
  }, [workspaceId, date, s3ClipsService, shift, dashboardConfig, updateClipCounts, updateVideoIndex]);
26606
26327
  const loadingCategoryRef = useRef(null);
26607
26328
  const videoRetryCountRef = useRef(0);
26329
+ const loadingVideosRef = useRef(/* @__PURE__ */ new Set());
26330
+ const loadedVideosMapRef = useRef(/* @__PURE__ */ new Map());
26331
+ const ensureVideosLoaded = useCallback(async (centerIndex) => {
26332
+ if (!s3ClipsService || !workspaceId || !isMountedRef.current) return;
26333
+ const currentFilter = activeFilterRef.current;
26334
+ const currentVideoIndex = videoIndexRef.current;
26335
+ let effectiveFilter = currentFilter;
26336
+ if (sopCategories && sopCategories.length > 0) {
26337
+ const category = sopCategories.find((cat) => cat.id === currentFilter);
26338
+ if (category && category.s3FolderName) {
26339
+ effectiveFilter = category.s3FolderName;
26340
+ }
26341
+ }
26342
+ const cacheKey = `${effectiveFilter}:${date}:${shift}`;
26343
+ if (!loadedVideosMapRef.current.has(cacheKey)) {
26344
+ loadedVideosMapRef.current.set(cacheKey, /* @__PURE__ */ new Set());
26345
+ }
26346
+ const loadedIndices = loadedVideosMapRef.current.get(cacheKey);
26347
+ const indicesToLoad = [];
26348
+ const rangeBefore = 1;
26349
+ const rangeAfter = 3;
26350
+ for (let i = Math.max(0, centerIndex - rangeBefore); i <= Math.min(clipCounts[effectiveFilter] - 1, centerIndex + rangeAfter); i++) {
26351
+ if (!loadedIndices.has(i) && !loadingVideosRef.current.has(i)) {
26352
+ indicesToLoad.push(i);
26353
+ }
26354
+ }
26355
+ if (indicesToLoad.length === 0) return;
26356
+ console.log(`[ensureVideosLoaded] Preloading ${indicesToLoad.length} videos around index ${centerIndex}: [${indicesToLoad.join(", ")}]`);
26357
+ indicesToLoad.forEach((idx) => loadingVideosRef.current.add(idx));
26358
+ const loadPromises = indicesToLoad.map(async (index) => {
26359
+ try {
26360
+ let video = null;
26361
+ if (currentVideoIndex && currentVideoIndex.byCategory && currentVideoIndex.allVideos.length > 0) {
26362
+ video = await s3ClipsService.getVideoFromIndex(
26363
+ currentVideoIndex,
26364
+ effectiveFilter,
26365
+ index,
26366
+ true,
26367
+ // includeCycleTime - OK for preloading
26368
+ false
26369
+ // includeMetadata - NO metadata during bulk preloading to prevent flooding
26370
+ );
26371
+ }
26372
+ if (!video) {
26373
+ const operationalDate = date || getOperationalDate();
26374
+ const shiftStr = shift?.toString() || "0";
26375
+ video = await s3ClipsService.getClipByIndex(
26376
+ workspaceId,
26377
+ operationalDate,
26378
+ shiftStr,
26379
+ effectiveFilter,
26380
+ index,
26381
+ true,
26382
+ // includeCycleTime - OK for preloading
26383
+ false
26384
+ // includeMetadata - NO metadata during bulk preloading to prevent flooding
26385
+ );
26386
+ }
26387
+ if (video && isMountedRef.current) {
26388
+ setAllVideos((prev) => {
26389
+ const exists = prev.some((v) => v.id === video.id);
26390
+ if (!exists) {
26391
+ return [...prev, video];
26392
+ }
26393
+ return prev;
26394
+ });
26395
+ loadedIndices.add(index);
26396
+ preloadVideoUrl(video.src);
26397
+ }
26398
+ } catch (error2) {
26399
+ console.warn(`[ensureVideosLoaded] Failed to load video at index ${index}:`, error2);
26400
+ } finally {
26401
+ loadingVideosRef.current.delete(index);
26402
+ }
26403
+ });
26404
+ Promise.all(loadPromises).catch((err) => {
26405
+ console.warn("[ensureVideosLoaded] Some videos failed to preload:", err);
26406
+ });
26407
+ }, [s3ClipsService, workspaceId, clipCounts, sopCategories, date, shift]);
26608
26408
  const loadFirstVideoForCategory = useCallback(async (category) => {
26609
26409
  if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
26610
26410
  const targetCategory = category || activeFilterRef.current;
@@ -26641,8 +26441,16 @@ var BottlenecksContent = ({
26641
26441
  );
26642
26442
  if (firstVideo) {
26643
26443
  console.log(`[BottlenecksContent] Successfully loaded first video via direct S3 method`);
26644
- setAllVideos([firstVideo]);
26444
+ setAllVideos((prev) => {
26445
+ const exists = prev.some((v) => v.id === firstVideo.id);
26446
+ if (!exists) {
26447
+ return [...prev, firstVideo];
26448
+ }
26449
+ return prev;
26450
+ });
26645
26451
  preloadVideoUrl(firstVideo.src);
26452
+ setIsLoading(false);
26453
+ setHasInitialLoad(true);
26646
26454
  loadingCategoryRef.current = null;
26647
26455
  setIsCategoryLoading(false);
26648
26456
  return;
@@ -26665,7 +26473,13 @@ var BottlenecksContent = ({
26665
26473
  );
26666
26474
  if (firstVideo && isMountedRef.current) {
26667
26475
  console.log(`[BottlenecksContent] Successfully loaded first video via video index`);
26668
- setAllVideos([firstVideo]);
26476
+ setAllVideos((prev) => {
26477
+ const exists = prev.some((v) => v.id === firstVideo.id);
26478
+ if (!exists) {
26479
+ return [...prev, firstVideo];
26480
+ }
26481
+ return prev;
26482
+ });
26669
26483
  preloadVideoUrl(firstVideo.src);
26670
26484
  setIsCategoryLoading(false);
26671
26485
  return;
@@ -26690,15 +26504,17 @@ var BottlenecksContent = ({
26690
26504
  }
26691
26505
  }, [workspaceId, date, s3ClipsService, clipCounts, videoIndex, shift, updateClipCounts, updateVideoIndex]);
26692
26506
  useEffect(() => {
26693
- if (s3ClipsService) {
26507
+ if (s3ClipsService && !prefetchData) {
26694
26508
  fetchClipCounts();
26695
26509
  }
26696
- }, [workspaceId, date, shift, s3ClipsService, fetchClipCounts, updateClipCounts, updateVideoIndex]);
26510
+ }, [workspaceId, date, shift, s3ClipsService, fetchClipCounts, updateClipCounts, updateVideoIndex, prefetchData]);
26697
26511
  useEffect(() => {
26698
26512
  if (prefetchData) {
26699
26513
  console.log(`[BottlenecksContent] Received prefetch update - status: ${prefetchStatus}, videos: ${prefetchData.videoIndex.allVideos.length}, ID: ${prefetchData.videoIndex._debugId || "NO_ID"}`);
26700
26514
  updateClipCounts(prefetchData.counts);
26701
- updateVideoIndex(prefetchData.videoIndex);
26515
+ if (prefetchData.videoIndex.allVideos.length > 0) {
26516
+ updateVideoIndex(prefetchData.videoIndex);
26517
+ }
26702
26518
  if (!hasInitialLoad && prefetchData.videoIndex.allVideos.length > 0) {
26703
26519
  setIsLoading(false);
26704
26520
  setHasInitialLoad(true);
@@ -26706,10 +26522,45 @@ var BottlenecksContent = ({
26706
26522
  }
26707
26523
  }, [prefetchData, prefetchStatus, updateClipCounts, updateVideoIndex, hasInitialLoad]);
26708
26524
  useEffect(() => {
26709
- if (s3ClipsService && videoIndex && clipCounts[activeFilter] > 0) {
26710
- loadFirstVideoForCategory(activeFilter);
26525
+ if (s3ClipsService && clipCounts[activeFilter] > 0) {
26526
+ const hasVideosForCurrentFilter = allVideos.some((video) => {
26527
+ if (sopCategories && sopCategories.length > 0) {
26528
+ const selectedCategory = sopCategories.find((cat) => cat.id === activeFilter);
26529
+ if (selectedCategory) {
26530
+ return video.type === selectedCategory.id;
26531
+ }
26532
+ }
26533
+ if (activeFilter === "all") return true;
26534
+ if (activeFilter === "low_value") {
26535
+ return video.type === "low_value";
26536
+ }
26537
+ if (activeFilter === "idle_time") {
26538
+ return video.type === "idle_time";
26539
+ }
26540
+ if (activeFilter === "sop_deviations") {
26541
+ return video.type === "missing_quality_check";
26542
+ }
26543
+ if (activeFilter === "best_cycle_time") {
26544
+ return video.type === "best_cycle_time";
26545
+ }
26546
+ if (activeFilter === "worst_cycle_time") {
26547
+ return video.type === "worst_cycle_time";
26548
+ }
26549
+ if (activeFilter === "cycle_completion") {
26550
+ return video.type === "cycle_completion";
26551
+ }
26552
+ if (activeFilter === "long_cycle_time") {
26553
+ return video.type === "long_cycle_time";
26554
+ }
26555
+ return video.type === "bottleneck" && video.severity === activeFilter;
26556
+ });
26557
+ if (!hasVideosForCurrentFilter) {
26558
+ loadFirstVideoForCategory(activeFilter);
26559
+ } else {
26560
+ setIsCategoryLoading(false);
26561
+ }
26711
26562
  }
26712
- }, [activeFilter, s3ClipsService, videoIndex, clipCounts, loadFirstVideoForCategory]);
26563
+ }, [activeFilter, s3ClipsService, videoIndex, clipCounts, loadFirstVideoForCategory, allVideos, sopCategories]);
26713
26564
  useEffect(() => {
26714
26565
  if (previousFilterRef.current !== activeFilter) {
26715
26566
  console.log(`Filter changed from ${previousFilterRef.current} to ${activeFilter} - resetting to first video`);
@@ -26778,26 +26629,25 @@ var BottlenecksContent = ({
26778
26629
  return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
26779
26630
  });
26780
26631
  }, [activeFilter, allVideos, sopCategories]);
26781
- useEffect(() => {
26782
- if (filteredVideos.length === 0) return;
26783
- const upcoming = [];
26784
- if (currentIndex + 1 < filteredVideos.length && filteredVideos[currentIndex + 1]) {
26785
- upcoming.push(filteredVideos[currentIndex + 1].src);
26786
- }
26787
- if (currentIndex - 1 >= 0 && currentIndex - 1 < filteredVideos.length && filteredVideos[currentIndex - 1]) {
26788
- upcoming.push(filteredVideos[currentIndex - 1].src);
26789
- }
26790
- if (upcoming.length > 0) {
26791
- preloadVideosUrl(upcoming);
26792
- }
26793
- }, [currentIndex, filteredVideos]);
26794
26632
  useEffect(() => {
26795
26633
  if (isNavigating && currentIndex < filteredVideos.length) {
26796
26634
  setIsNavigating(false);
26797
26635
  setError(null);
26798
26636
  videoRetryCountRef.current = 0;
26637
+ ensureVideosLoaded(currentIndex);
26799
26638
  }
26800
26639
  }, [isNavigating, currentIndex, filteredVideos.length]);
26640
+ useEffect(() => {
26641
+ if (!isPlaying || !isMountedRef.current) return;
26642
+ const preloadInterval = setInterval(() => {
26643
+ if (isMountedRef.current) {
26644
+ const currentIdx = currentIndexRef.current;
26645
+ console.log(`[Background Preloader] Ensuring videos loaded around index ${currentIdx}`);
26646
+ ensureVideosLoaded(currentIdx);
26647
+ }
26648
+ }, 2e3);
26649
+ return () => clearInterval(preloadInterval);
26650
+ }, [isPlaying]);
26801
26651
  const handleNext = useCallback(async () => {
26802
26652
  if (!isMountedRef.current) return;
26803
26653
  const currentIdx = currentIndexRef.current;
@@ -26818,6 +26668,7 @@ var BottlenecksContent = ({
26818
26668
  }
26819
26669
  if (nextIndex < filteredVideos.length) {
26820
26670
  setIsNavigating(false);
26671
+ ensureVideosLoaded(nextIndex);
26821
26672
  return;
26822
26673
  }
26823
26674
  if (isMountedRef.current) {
@@ -26835,8 +26686,8 @@ var BottlenecksContent = ({
26835
26686
  nextIndex,
26836
26687
  true,
26837
26688
  // includeCycleTime
26838
- true
26839
- // includeMetadata
26689
+ false
26690
+ // includeMetadata - DON'T fetch metadata during navigation to prevent flooding!
26840
26691
  );
26841
26692
  } else {
26842
26693
  console.warn(`[BottlenecksContent] Video index not ready for navigation: ID: ${currentVideoIndex?._debugId || "NO_ID"}, byCategory exists = ${!!currentVideoIndex?.byCategory}, allVideos = ${currentVideoIndex?.allVideos?.length || 0}`);
@@ -26849,7 +26700,11 @@ var BottlenecksContent = ({
26849
26700
  operationalDate,
26850
26701
  shiftStr,
26851
26702
  effectiveFilter,
26852
- nextIndex
26703
+ nextIndex,
26704
+ true,
26705
+ // includeCycleTime - needed for main video display
26706
+ false
26707
+ // includeMetadata - DON'T fetch metadata during navigation to prevent flooding!
26853
26708
  );
26854
26709
  }
26855
26710
  if (video && isMountedRef.current) {
@@ -26862,26 +26717,7 @@ var BottlenecksContent = ({
26862
26717
  return prev;
26863
26718
  });
26864
26719
  preloadVideoUrl(video.src);
26865
- if (nextIndex + 1 < clipCounts[effectiveFilter]) {
26866
- setTimeout(() => {
26867
- const videoIndexForPreload = videoIndexRef.current;
26868
- if (videoIndexForPreload && s3ClipsService && isMountedRef.current) {
26869
- s3ClipsService.getVideoFromIndex(videoIndexForPreload, effectiveFilter, nextIndex + 1, true, true).then((nextVideo) => {
26870
- if (nextVideo && isMountedRef.current) {
26871
- setAllVideos((prev) => {
26872
- if (!prev.some((v) => v.id === nextVideo.id)) {
26873
- const newVideos = [...prev, nextVideo];
26874
- return newVideos;
26875
- }
26876
- return prev;
26877
- });
26878
- preloadVideoUrl(nextVideo.src);
26879
- }
26880
- }).catch(() => {
26881
- });
26882
- }
26883
- }, 100);
26884
- }
26720
+ ensureVideosLoaded(nextIndex);
26885
26721
  }
26886
26722
  } catch (error2) {
26887
26723
  console.error("Error loading next video:", error2);
@@ -26903,15 +26739,43 @@ var BottlenecksContent = ({
26903
26739
  if (prevIndex < filteredVideos.length) {
26904
26740
  setCurrentIndex(prevIndex);
26905
26741
  setError(null);
26742
+ ensureVideosLoaded(prevIndex);
26906
26743
  }
26907
26744
  }
26908
26745
  }, [filteredVideos.length]);
26746
+ const currentVideo = useMemo(() => {
26747
+ if (!filteredVideos || filteredVideos.length === 0 || currentIndex >= filteredVideos.length) {
26748
+ return null;
26749
+ }
26750
+ return filteredVideos[currentIndex];
26751
+ }, [filteredVideos, currentIndex]);
26909
26752
  const handleVideoReady = useCallback((player) => {
26910
26753
  console.log("Video.js player ready");
26911
26754
  }, []);
26912
- const handleVideoPlay = useCallback((player) => {
26755
+ const handleVideoPlay = useCallback(async (player) => {
26913
26756
  setIsPlaying(true);
26914
- }, []);
26757
+ const currentIdx = currentIndexRef.current;
26758
+ ensureVideosLoaded(currentIdx);
26759
+ if (currentVideo && !currentVideo.creation_timestamp && s3ClipsService) {
26760
+ try {
26761
+ const originalUri = currentVideo.originalUri || currentVideo.src;
26762
+ if (originalUri && originalUri.includes("s3://")) {
26763
+ const metadata = await s3ClipsService.getFullMetadata(originalUri);
26764
+ if (metadata && isMountedRef.current) {
26765
+ setAllVideos((prev) => prev.map(
26766
+ (v) => v.id === currentVideo.id ? {
26767
+ ...v,
26768
+ creation_timestamp: metadata.upload_timestamp || metadata.original_task_metadata?.timestamp || metadata.creation_timestamp,
26769
+ cycle_time_seconds: metadata.original_task_metadata?.cycle_time || v.cycle_time_seconds
26770
+ } : v
26771
+ ));
26772
+ }
26773
+ }
26774
+ } catch (error2) {
26775
+ console.warn("[BottlenecksContent] Failed to load metadata for current video:", error2);
26776
+ }
26777
+ }
26778
+ }, [currentVideo, s3ClipsService]);
26915
26779
  const handleVideoPause = useCallback((player) => {
26916
26780
  setIsPlaying(false);
26917
26781
  }, []);
@@ -26938,13 +26802,6 @@ var BottlenecksContent = ({
26938
26802
  fetchInProgressRef.current.clear();
26939
26803
  setIsCategoryLoading(false);
26940
26804
  setIsNavigating(false);
26941
- if (s3ClipsService) {
26942
- try {
26943
- s3ClipsService.dispose();
26944
- } catch (error2) {
26945
- console.warn("[BottlenecksContent] Error disposing S3 service:", error2);
26946
- }
26947
- }
26948
26805
  };
26949
26806
  }, [s3ClipsService]);
26950
26807
  useEffect(() => {
@@ -26985,12 +26842,6 @@ var BottlenecksContent = ({
26985
26842
  counts.bottlenecks = counts.bottleneck || counts.bottlenecks || 0;
26986
26843
  return counts;
26987
26844
  }, [clipCounts]);
26988
- const currentVideo = useMemo(() => {
26989
- if (!filteredVideos || filteredVideos.length === 0 || currentIndex >= filteredVideos.length) {
26990
- return null;
26991
- }
26992
- return filteredVideos[currentIndex];
26993
- }, [filteredVideos, currentIndex]);
26994
26845
  const getClipTypeLabel = (video) => {
26995
26846
  if (!video) return "";
26996
26847
  switch (video.type) {
@@ -27225,8 +27076,8 @@ var BottlenecksContent = ({
27225
27076
  ] }),
27226
27077
  /* Priority 1: Show loading if initial load hasn't completed yet */
27227
27078
  isLoading && !hasInitialLoad ? /* @__PURE__ */ jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading clips..." }) }) }) }) : (
27228
- /* Priority 2: Show loading if category is loading (prevents "no matching clips" flash) */
27229
- isCategoryLoading ? /* @__PURE__ */ jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading videos..." }) }) }) }) : (
27079
+ /* Priority 2: Show loading if category is loading BUT only if no video is available */
27080
+ isCategoryLoading && (!filteredVideos.length || !currentVideo) ? /* @__PURE__ */ jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading videos..." }) }) }) }) : (
27230
27081
  /* Priority 3: Show loading if navigating and current video not available */
27231
27082
  isNavigating || currentIndex >= filteredVideos.length && currentIndex < clipCounts[activeFilter] ? /* @__PURE__ */ jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }) }) }) : (
27232
27083
  /* Priority 4: Show video if we have filtered videos and current video */
@@ -31583,7 +31434,7 @@ var HelpView = ({
31583
31434
  onClick: handleBackClick,
31584
31435
  className: "flex items-center text-gray-600 hover:text-gray-900",
31585
31436
  children: [
31586
- /* @__PURE__ */ jsx(ArrowLeftIcon, { className: "h-5 w-5" }),
31437
+ /* @__PURE__ */ jsx(ArrowLeftIcon$1, { className: "h-5 w-5" }),
31587
31438
  /* @__PURE__ */ jsx("span", { className: "ml-2", children: "Back" })
31588
31439
  ]
31589
31440
  }
@@ -32024,9 +31875,25 @@ function withWorkspaceDisplayNames(Component3, options = {}) {
32024
31875
  return function WithWorkspaceDisplayNamesWrapper(props) {
32025
31876
  const [isInitialized2, setIsInitialized] = useState(false);
32026
31877
  const [error, setError] = useState(null);
31878
+ const [lastInitKey, setLastInitKey] = useState("");
31879
+ const lineIdsKey = useMemo(() => {
31880
+ if (!props.lineIds) return "";
31881
+ if (Array.isArray(props.lineIds)) {
31882
+ return props.lineIds.sort().join(",");
31883
+ }
31884
+ const values = Object.values(props.lineIds).filter(Boolean);
31885
+ return values.sort().join(",");
31886
+ }, [props.lineIds]);
31887
+ const initKey = useMemo(() => {
31888
+ return `${lineIdsKey}-${props.selectedLineId || ""}-${props.factoryViewId || ""}-${initializeFor}`;
31889
+ }, [lineIdsKey, props.selectedLineId, props.factoryViewId]);
32027
31890
  useEffect(() => {
32028
- setIsInitialized(false);
32029
- setError(null);
31891
+ if (initKey === lastInitKey && isInitialized2) {
31892
+ return;
31893
+ }
31894
+ if (initKey !== lastInitKey) {
31895
+ setError(null);
31896
+ }
32030
31897
  const initializeDisplayNames = async () => {
32031
31898
  try {
32032
31899
  const { lineIds, selectedLineId, factoryViewId } = props;
@@ -32052,20 +31919,17 @@ function withWorkspaceDisplayNames(Component3, options = {}) {
32052
31919
  await preInitializeWorkspaceDisplayNames();
32053
31920
  }
32054
31921
  setIsInitialized(true);
31922
+ setLastInitKey(initKey);
32055
31923
  } catch (err) {
32056
31924
  console.error("Failed to initialize workspace display names:", err);
32057
31925
  setError(err);
32058
31926
  setIsInitialized(true);
31927
+ setLastInitKey(initKey);
32059
31928
  }
32060
31929
  };
32061
31930
  initializeDisplayNames();
32062
- }, [
32063
- Array.isArray(props.lineIds) ? props.lineIds.join(",") : JSON.stringify(props.lineIds),
32064
- props.selectedLineId,
32065
- props.factoryViewId,
32066
- initializeFor
32067
- ]);
32068
- if (!isInitialized2 && showLoading) {
31931
+ }, [initKey]);
31932
+ if (!isInitialized2 && showLoading && lastInitKey === "") {
32069
31933
  return /* @__PURE__ */ jsx(LoadingPage, { message: loadingMessage });
32070
31934
  }
32071
31935
  if (error && showLoading) {
@@ -32705,7 +32569,7 @@ var KPIDetailView = ({
32705
32569
  onClick: handleBackClick,
32706
32570
  className: "absolute left-0 flex items-center text-gray-600 hover:text-gray-900 cursor-pointer",
32707
32571
  children: [
32708
- /* @__PURE__ */ jsx(ArrowLeftIcon, { className: "h-5 w-5" }),
32572
+ /* @__PURE__ */ jsx(ArrowLeftIcon$1, { className: "h-5 w-5" }),
32709
32573
  /* @__PURE__ */ jsx("span", { className: "ml-2", children: "Back" })
32710
32574
  ]
32711
32575
  }
@@ -33077,7 +32941,7 @@ var KPIsOverviewView = ({
33077
32941
  onClick: handleBackClick,
33078
32942
  className: "absolute left-0 flex items-center text-gray-600 hover:text-gray-900 cursor-pointer",
33079
32943
  children: [
33080
- /* @__PURE__ */ jsx(ArrowLeftIcon, { className: "h-5 w-5" }),
32944
+ /* @__PURE__ */ jsx(ArrowLeftIcon$1, { className: "h-5 w-5" }),
33081
32945
  /* @__PURE__ */ jsx("span", { className: "ml-2", children: "Back" })
33082
32946
  ]
33083
32947
  }
@@ -33099,7 +32963,7 @@ var KPIsOverviewView = ({
33099
32963
  onClick: handleBackClick,
33100
32964
  className: "absolute left-0 flex items-center text-gray-600 hover:text-gray-900 cursor-pointer",
33101
32965
  children: [
33102
- /* @__PURE__ */ jsx(ArrowLeftIcon, { className: "h-5 w-5" }),
32966
+ /* @__PURE__ */ jsx(ArrowLeftIcon$1, { className: "h-5 w-5" }),
33103
32967
  /* @__PURE__ */ jsx("span", { className: "ml-2", children: "Back" })
33104
32968
  ]
33105
32969
  }
@@ -33124,7 +32988,7 @@ var KPIsOverviewView = ({
33124
32988
  onClick: handleBackClick,
33125
32989
  className: "absolute left-0 flex items-center text-gray-600 hover:text-gray-900 cursor-pointer",
33126
32990
  children: [
33127
- /* @__PURE__ */ jsx(ArrowLeftIcon, { className: "h-5 w-5" }),
32991
+ /* @__PURE__ */ jsx(ArrowLeftIcon$1, { className: "h-5 w-5" }),
33128
32992
  /* @__PURE__ */ jsx("span", { className: "ml-2", children: "Back" })
33129
32993
  ]
33130
32994
  }
@@ -34136,7 +34000,15 @@ var ShiftsView = ({
34136
34000
  className = ""
34137
34001
  }) => {
34138
34002
  const supabase = useSupabase();
34139
- const auth = useAuth();
34003
+ useEffect(() => {
34004
+ console.log("[ShiftsView] Component mounted/re-rendered", {
34005
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
34006
+ lineIds: lineIds.length
34007
+ });
34008
+ return () => {
34009
+ console.log("[ShiftsView] Component unmounting");
34010
+ };
34011
+ }, []);
34140
34012
  const [lineConfigs, setLineConfigs] = useState(
34141
34013
  () => lineIds.map((id3) => ({
34142
34014
  id: id3,
@@ -34234,7 +34106,7 @@ var ShiftsView = ({
34234
34106
  }
34235
34107
  };
34236
34108
  fetchShiftConfigs();
34237
- }, [supabase, lineIds, showToast]);
34109
+ }, [lineIds, showToast]);
34238
34110
  useCallback((lineId) => {
34239
34111
  setLineConfigs((prev) => {
34240
34112
  const typedPrev = prev;
@@ -34427,7 +34299,6 @@ var ShiftsView = ({
34427
34299
  }));
34428
34300
  }, []);
34429
34301
  const handleSaveShifts = useCallback(async (lineId) => {
34430
- if (!auth.user?.id && false) ;
34431
34302
  setLineConfigs((prev) => prev.map(
34432
34303
  (config) => config.id === lineId ? { ...config, isSaving: true, saveSuccess: false } : config
34433
34304
  ));
@@ -34474,7 +34345,7 @@ var ShiftsView = ({
34474
34345
  (config) => config.id === lineId ? { ...config, isSaving: false, saveSuccess: false } : config
34475
34346
  ));
34476
34347
  }
34477
- }, [auth.user?.id, lineConfigs, supabase, showToast]);
34348
+ }, [lineConfigs, supabase, showToast]);
34478
34349
  return /* @__PURE__ */ jsxs("div", { className: `min-h-screen bg-slate-50 ${className}`, children: [
34479
34350
  /* @__PURE__ */ jsx("div", { className: "sticky top-0 z-10 bg-white border-b border-gray-200/80 shadow-sm", children: /* @__PURE__ */ jsx("div", { className: "px-4 sm:px-8 py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center relative", children: [
34480
34351
  /* @__PURE__ */ jsxs(
@@ -34582,6 +34453,7 @@ var ShiftsView = ({
34582
34453
  ] })
34583
34454
  ] });
34584
34455
  };
34456
+ var AuthenticatedShiftsView = withAuth(React19__default.memo(ShiftsView));
34585
34457
  var ShiftsView_default = ShiftsView;
34586
34458
 
34587
34459
  // src/lib/constants/actions.ts
@@ -35369,6 +35241,7 @@ var TargetsViewUI = ({
35369
35241
  isLoading,
35370
35242
  lineWorkspaces,
35371
35243
  lineNames,
35244
+ dropdownStates,
35372
35245
  savingLines,
35373
35246
  saveSuccess,
35374
35247
  selectedShift,
@@ -35401,7 +35274,7 @@ var TargetsViewUI = ({
35401
35274
  onClick: onBack,
35402
35275
  className: "flex items-center text-gray-600 hover:text-gray-900",
35403
35276
  children: [
35404
- /* @__PURE__ */ jsx(ArrowLeftIcon$1, { className: "h-5 w-5" }),
35277
+ /* @__PURE__ */ jsx(ArrowLeftIcon, { className: "h-5 w-5" }),
35405
35278
  /* @__PURE__ */ jsx("span", { className: "ml-2", children: "Back" })
35406
35279
  ]
35407
35280
  }
@@ -35454,13 +35327,13 @@ var TargetsViewUI = ({
35454
35327
  {
35455
35328
  onClick: () => onToggleLineDropdown(lineId),
35456
35329
  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",
35457
- "aria-expanded": line.isOpen,
35330
+ "aria-expanded": dropdownStates[lineId],
35458
35331
  "aria-controls": `line-${lineId}-content`,
35459
35332
  children: [
35460
35333
  /* @__PURE__ */ jsx(
35461
35334
  ChevronDown,
35462
35335
  {
35463
- className: `w-5 h-5 text-blue-500 transform transition-transform duration-200 ${line.isOpen ? "rotate-180" : ""}`
35336
+ className: `w-5 h-5 text-blue-500 transform transition-transform duration-200 ${dropdownStates[lineId] ? "rotate-180" : ""}`
35464
35337
  }
35465
35338
  ),
35466
35339
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5", children: [
@@ -35507,7 +35380,7 @@ var TargetsViewUI = ({
35507
35380
  )
35508
35381
  ] })
35509
35382
  ] }) }),
35510
- line.isOpen && /* @__PURE__ */ jsxs("div", { id: `line-${lineId}-content`, className: "border-t border-gray-200", children: [
35383
+ dropdownStates[lineId] && /* @__PURE__ */ jsxs("div", { id: `line-${lineId}-content`, className: "border-t border-gray-200", children: [
35511
35384
  skuEnabled && /* @__PURE__ */ jsx("div", { className: "px-6 py-4 border-b border-gray-200 bg-gray-50/50", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
35512
35385
  /* @__PURE__ */ jsx("label", { htmlFor: `sku-${lineId}`, className: "text-sm font-medium text-gray-700", children: "Select SKU:" }),
35513
35386
  /* @__PURE__ */ jsx("div", { className: "flex-1 max-w-md", children: /* @__PURE__ */ jsx(
@@ -35544,68 +35417,71 @@ var TargetsViewUI = ({
35544
35417
  /* @__PURE__ */ jsx("span", { className: "text-xs text-gray-400", children: "pieces per day" })
35545
35418
  ] })
35546
35419
  ] }) }),
35547
- /* @__PURE__ */ jsx("div", { className: "divide-y divide-gray-100", children: line.workspaces.map((workspace) => /* @__PURE__ */ jsx(
35548
- "div",
35549
- {
35550
- className: "px-6 py-4 hover:bg-gray-50 transition-all duration-200",
35551
- children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-12 gap-6 items-center", children: [
35552
- /* @__PURE__ */ jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-900", children: formatWorkspaceName(workspace.name, lineId) }) }),
35553
- /* @__PURE__ */ jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsxs(
35554
- "select",
35555
- {
35556
- value: workspace.actionType,
35557
- onChange: (e) => {
35558
- const newActionType = e.target.value;
35559
- onActionTypeChange(lineId, workspace.id, newActionType);
35560
- },
35561
- className: "w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm",
35562
- "aria-label": `Action type for ${formatWorkspaceName(workspace.name, lineId)}`,
35563
- children: [
35564
- /* @__PURE__ */ jsx("option", { value: "assembly", className: "py-2", children: "Assembly" }),
35565
- /* @__PURE__ */ jsx("option", { value: "packaging", className: "py-2", children: "Packaging" })
35566
- ]
35567
- }
35568
- ) }),
35569
- /* @__PURE__ */ jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsx(
35570
- "input",
35571
- {
35572
- type: "number",
35573
- value: workspace.targetCycleTime === 0 ? "" : workspace.targetCycleTime,
35574
- onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetCycleTime", Number(e.target.value) || ""),
35575
- 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",
35576
- min: "0",
35577
- step: "0.01",
35578
- placeholder: "Enter cycle time"
35579
- }
35580
- ) }),
35581
- /* @__PURE__ */ jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsx(
35582
- "input",
35583
- {
35584
- type: "number",
35585
- value: workspace.targetPPH === 0 ? "" : workspace.targetPPH,
35586
- onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetPPH", Number(e.target.value) || ""),
35587
- 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",
35588
- min: "0",
35589
- step: "0.1",
35590
- placeholder: "Enter PPH"
35591
- }
35592
- ) }),
35593
- /* @__PURE__ */ jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsx(
35594
- "input",
35595
- {
35596
- type: "number",
35597
- value: workspace.targetDayOutput === 0 ? "" : workspace.targetDayOutput,
35598
- onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetDayOutput", Number(e.target.value) || ""),
35599
- 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",
35600
- min: "0",
35601
- step: "1",
35602
- placeholder: "Enter day output"
35603
- }
35604
- ) })
35605
- ] })
35606
- },
35607
- workspace.id
35608
- )) })
35420
+ /* @__PURE__ */ jsx("div", { className: "divide-y divide-gray-100", children: line.workspaces.map((workspace) => {
35421
+ const formattedName = formatWorkspaceName(workspace.name, lineId);
35422
+ return /* @__PURE__ */ jsx(
35423
+ "div",
35424
+ {
35425
+ className: "px-6 py-4 hover:bg-gray-50 transition-all duration-200",
35426
+ children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-12 gap-6 items-center", children: [
35427
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-900", children: formattedName }) }),
35428
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsxs(
35429
+ "select",
35430
+ {
35431
+ value: workspace.actionType,
35432
+ onChange: (e) => {
35433
+ const newActionType = e.target.value;
35434
+ onActionTypeChange(lineId, workspace.id, newActionType);
35435
+ },
35436
+ className: "w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm",
35437
+ "aria-label": `Action type for ${formattedName}`,
35438
+ children: [
35439
+ /* @__PURE__ */ jsx("option", { value: "assembly", className: "py-2", children: "Assembly" }),
35440
+ /* @__PURE__ */ jsx("option", { value: "packaging", className: "py-2", children: "Packaging" })
35441
+ ]
35442
+ }
35443
+ ) }),
35444
+ /* @__PURE__ */ jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsx(
35445
+ "input",
35446
+ {
35447
+ type: "number",
35448
+ value: workspace.targetCycleTime === 0 ? "" : workspace.targetCycleTime,
35449
+ onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetCycleTime", Number(e.target.value) || ""),
35450
+ 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",
35451
+ min: "0",
35452
+ step: "0.01",
35453
+ placeholder: "Enter cycle time"
35454
+ }
35455
+ ) }),
35456
+ /* @__PURE__ */ jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsx(
35457
+ "input",
35458
+ {
35459
+ type: "number",
35460
+ value: workspace.targetPPH === 0 ? "" : workspace.targetPPH,
35461
+ onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetPPH", Number(e.target.value) || ""),
35462
+ 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",
35463
+ min: "0",
35464
+ step: "0.1",
35465
+ placeholder: "Enter PPH"
35466
+ }
35467
+ ) }),
35468
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsx(
35469
+ "input",
35470
+ {
35471
+ type: "number",
35472
+ value: workspace.targetDayOutput === 0 ? "" : workspace.targetDayOutput,
35473
+ onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetDayOutput", Number(e.target.value) || ""),
35474
+ 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",
35475
+ min: "0",
35476
+ step: "1",
35477
+ placeholder: "Enter day output"
35478
+ }
35479
+ ) })
35480
+ ] })
35481
+ },
35482
+ workspace.id
35483
+ );
35484
+ }) })
35609
35485
  ] })
35610
35486
  ]
35611
35487
  },
@@ -35638,7 +35514,6 @@ var TargetsView = ({
35638
35514
  return lineIds.reduce((acc, lineId) => ({
35639
35515
  ...acc,
35640
35516
  [lineId]: {
35641
- isOpen: getStoredLineState2(lineId),
35642
35517
  productId: "",
35643
35518
  shiftStartTime: "08:00",
35644
35519
  shiftEndTime: "19:00",
@@ -35651,6 +35526,12 @@ var TargetsView = ({
35651
35526
  }
35652
35527
  }), {});
35653
35528
  }, [lineIds]);
35529
+ const [dropdownStates, setDropdownStates] = useState(() => {
35530
+ return lineIds.reduce((acc, lineId) => ({
35531
+ ...acc,
35532
+ [lineId]: getStoredLineState2(lineId)
35533
+ }), {});
35534
+ });
35654
35535
  const [allShiftsData, setAllShiftsData] = useState({
35655
35536
  0: initialLineWorkspaces,
35656
35537
  // Day shift
@@ -35668,6 +35549,8 @@ var TargetsView = ({
35668
35549
  const [isBulkConfigureOpen, setIsBulkConfigureOpen] = useState(false);
35669
35550
  const [selectedWorkspaces, setSelectedWorkspaces] = useState([]);
35670
35551
  const [selectedShift, setSelectedShift] = useState(0);
35552
+ const [dbValues, setDbValues] = useState({ 0: {}, 1: {} });
35553
+ const [userEditedFields, setUserEditedFields] = useState(/* @__PURE__ */ new Set());
35671
35554
  const lineWorkspaces = allShiftsData[selectedShift] || initialLineWorkspaces;
35672
35555
  const setLineWorkspaces = useCallback((updater) => {
35673
35556
  setAllShiftsData((prev) => ({
@@ -35676,18 +35559,123 @@ var TargetsView = ({
35676
35559
  }));
35677
35560
  }, [selectedShift]);
35678
35561
  const supabase = useSupabase();
35679
- const auth = useAuth();
35680
- userId || auth?.user?.id;
35562
+ const effectiveUserId = userId || "6bf6f271-1e55-4a95-9b89-1c3820b58739";
35681
35563
  const dashboardConfig = useDashboardConfig();
35682
35564
  const { skus, isLoading: skusLoading } = useSKUs(companyId);
35683
35565
  const skuEnabled = dashboardConfig?.skuConfig?.enabled || false;
35566
+ const loadOperatingHours = useCallback(async (lineId, shiftId) => {
35567
+ try {
35568
+ if (!supabase) return null;
35569
+ const { data, error } = await supabase.from("line_operating_hours").select("start_time, end_time, breaks").eq("line_id", lineId).eq("shift_id", shiftId).maybeSingle();
35570
+ if (error) {
35571
+ if (error.code === "PGRST116") {
35572
+ console.log(`No operating hours found for line ${lineId}, shift ${shiftId}`);
35573
+ return {
35574
+ startTime: "08:00",
35575
+ // Default values
35576
+ endTime: "19:00",
35577
+ breaks: []
35578
+ };
35579
+ } else {
35580
+ console.error("Error fetching operating hours:", error);
35581
+ return null;
35582
+ }
35583
+ }
35584
+ let breaks = [];
35585
+ if (data?.breaks) {
35586
+ if (Array.isArray(data.breaks)) {
35587
+ breaks = data.breaks.map((breakItem) => {
35588
+ const startTime = breakItem.start || breakItem.startTime || "00:00";
35589
+ const endTime = breakItem.end || breakItem.endTime || "00:00";
35590
+ const calculateDuration = (start, end) => {
35591
+ const [startHour, startMinute] = start.split(":").map(Number);
35592
+ const [endHour, endMinute] = end.split(":").map(Number);
35593
+ let startMinutes = startHour * 60 + startMinute;
35594
+ let endMinutes = endHour * 60 + endMinute;
35595
+ if (endMinutes < startMinutes) {
35596
+ endMinutes += 24 * 60;
35597
+ }
35598
+ return endMinutes - startMinutes;
35599
+ };
35600
+ return {
35601
+ startTime,
35602
+ endTime,
35603
+ duration: breakItem.duration || calculateDuration(startTime, endTime)
35604
+ };
35605
+ });
35606
+ } else if (typeof data.breaks === "object" && data.breaks.breaks) {
35607
+ breaks = data.breaks.breaks.map((breakItem) => {
35608
+ const startTime = breakItem.start || breakItem.startTime || "00:00";
35609
+ const endTime = breakItem.end || breakItem.endTime || "00:00";
35610
+ const calculateDuration = (start, end) => {
35611
+ const [startHour, startMinute] = start.split(":").map(Number);
35612
+ const [endHour, endMinute] = end.split(":").map(Number);
35613
+ let startMinutes = startHour * 60 + startMinute;
35614
+ let endMinutes = endHour * 60 + endMinute;
35615
+ if (endMinutes < startMinutes) {
35616
+ endMinutes += 24 * 60;
35617
+ }
35618
+ return endMinutes - startMinutes;
35619
+ };
35620
+ return {
35621
+ startTime,
35622
+ endTime,
35623
+ duration: breakItem.duration || calculateDuration(startTime, endTime)
35624
+ };
35625
+ });
35626
+ } else if (typeof data.breaks === "string") {
35627
+ try {
35628
+ const parsedBreaks = JSON.parse(data.breaks);
35629
+ if (Array.isArray(parsedBreaks)) {
35630
+ breaks = parsedBreaks.map((breakItem) => ({
35631
+ startTime: breakItem.start || breakItem.startTime || "00:00",
35632
+ endTime: breakItem.end || breakItem.endTime || "00:00",
35633
+ duration: breakItem.duration || 0
35634
+ }));
35635
+ } else if (parsedBreaks.breaks && Array.isArray(parsedBreaks.breaks)) {
35636
+ breaks = parsedBreaks.breaks.map((breakItem) => ({
35637
+ startTime: breakItem.start || breakItem.startTime || "00:00",
35638
+ endTime: breakItem.end || breakItem.endTime || "00:00",
35639
+ duration: breakItem.duration || 0
35640
+ }));
35641
+ }
35642
+ } catch (e) {
35643
+ console.error("Error parsing breaks data:", e);
35644
+ }
35645
+ }
35646
+ }
35647
+ return {
35648
+ startTime: data?.start_time || "08:00",
35649
+ endTime: data?.end_time || "19:00",
35650
+ breaks
35651
+ };
35652
+ } catch (e) {
35653
+ console.error("Exception when loading operating hours:", e);
35654
+ return {
35655
+ startTime: "08:00",
35656
+ endTime: "19:00",
35657
+ breaks: []
35658
+ };
35659
+ }
35660
+ }, [supabase]);
35661
+ useEffect(() => {
35662
+ console.log("[TargetsView] Component mounted/re-rendered", {
35663
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
35664
+ lineIds: lineIds.length,
35665
+ effectiveUserId
35666
+ });
35667
+ return () => {
35668
+ console.log("[TargetsView] Component unmounting");
35669
+ };
35670
+ }, []);
35684
35671
  useEffect(() => {
35685
35672
  let timeoutId;
35686
35673
  let retryCount = 0;
35687
35674
  const MAX_RETRIES2 = 2;
35688
35675
  const LOADING_TIMEOUT = 15e3;
35689
35676
  const fetchInitialData = async () => {
35690
- if (!supabase || lineIds.length === 0) return;
35677
+ if (lineIds.length === 0) return;
35678
+ console.log("[TargetsView] Starting fetchInitialData");
35691
35679
  setIsLoading(true);
35692
35680
  timeoutId = setTimeout(() => {
35693
35681
  console.error("Loading timeout reached");
@@ -35751,10 +35739,32 @@ var TargetsView = ({
35751
35739
  const actionThresholds = await workspaceService.getActionThresholds(
35752
35740
  lineId,
35753
35741
  currentDate,
35754
- selectedShift
35742
+ 0
35743
+ // Always use day shift for initial load
35755
35744
  );
35745
+ const operatingHoursData = await loadOperatingHours(lineId, 0);
35746
+ if (operatingHoursData) {
35747
+ updatedLineWorkspaces[lineId].shiftStartTime = operatingHoursData.startTime;
35748
+ updatedLineWorkspaces[lineId].shiftEndTime = operatingHoursData.endTime;
35749
+ updatedLineWorkspaces[lineId].breaks = operatingHoursData.breaks;
35750
+ updatedLineWorkspaces[lineId].shiftHours = calculateShiftHours2(
35751
+ operatingHoursData.startTime,
35752
+ operatingHoursData.endTime,
35753
+ operatingHoursData.breaks
35754
+ );
35755
+ }
35756
35756
  const mappedWorkspaces = enabledWorkspaces.map((ws) => {
35757
35757
  const threshold = actionThresholds.find((t) => t.workspace_id === ws.id);
35758
+ if (!dbValues[0][lineId]) {
35759
+ dbValues[0][lineId] = {};
35760
+ }
35761
+ if (threshold) {
35762
+ dbValues[0][lineId][ws.id] = {
35763
+ targetPPH: threshold.pph_threshold,
35764
+ targetCycleTime: threshold.ideal_cycle_time,
35765
+ targetDayOutput: threshold.total_day_output
35766
+ };
35767
+ }
35758
35768
  return {
35759
35769
  id: ws.id,
35760
35770
  name: ws.workspace_id,
@@ -35795,90 +35805,7 @@ var TargetsView = ({
35795
35805
  return () => {
35796
35806
  clearTimeout(timeoutId);
35797
35807
  };
35798
- }, [supabase, lineIds, companyId]);
35799
- useCallback(async (shiftId) => {
35800
- try {
35801
- if (!supabase) return;
35802
- const currentDate = getOperationalDate();
35803
- const updatedLineWorkspaces = { ...lineWorkspaces };
35804
- let hasUpdates = false;
35805
- for (const lineId of lineIds) {
35806
- const lineState = lineWorkspaces[lineId];
35807
- if (!lineState || !lineState.factoryId) {
35808
- console.warn(`Skipping line thresholds for ${lineId} as factoryId is not yet available.`);
35809
- continue;
35810
- }
35811
- const currentFactoryId = lineState.factoryId;
35812
- try {
35813
- 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);
35814
- if (thresholdError) {
35815
- console.error(`Error fetching line threshold for line ${lineId}, factory ${currentFactoryId}:`, thresholdError);
35816
- continue;
35817
- }
35818
- let determinedProductId = updatedLineWorkspaces[lineId]?.productId || "";
35819
- if (lineThresholdsRows && lineThresholdsRows.length > 0) {
35820
- if (lineThresholdsRows.length > 1) {
35821
- console.warn(
35822
- `Multiple line_thresholds records found for line ${lineId}, factory ${currentFactoryId}, date ${currentDate}, shift ${selectedShift}. Using product_code from the first record. Rows:`,
35823
- lineThresholdsRows
35824
- );
35825
- }
35826
- determinedProductId = lineThresholdsRows[0].product_code;
35827
- } else {
35828
- console.log(
35829
- `No line_thresholds record found for line ${lineId}, factory ${currentFactoryId}, date ${currentDate}, shift ${selectedShift}. Using existing/default product ID.`
35830
- );
35831
- }
35832
- 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();
35833
- if (hoursError) {
35834
- console.error(`Error fetching operating hours for line ${lineId}:`, hoursError);
35835
- continue;
35836
- }
35837
- const startTime = operatingHours?.start_time || updatedLineWorkspaces[lineId]?.shiftStartTime || "08:00";
35838
- const endTime = operatingHours?.end_time || updatedLineWorkspaces[lineId]?.shiftEndTime || "19:00";
35839
- let breaks = [];
35840
- if (operatingHours?.breaks) {
35841
- if (Array.isArray(operatingHours.breaks)) {
35842
- breaks = operatingHours.breaks.map((breakItem) => ({
35843
- startTime: breakItem.start || breakItem.startTime || "00:00",
35844
- endTime: breakItem.end || breakItem.endTime || "00:00",
35845
- duration: breakItem.duration || calculateShiftHours2(breakItem.start || breakItem.startTime || "00:00", breakItem.end || breakItem.endTime || "00:00", []) * 60
35846
- }));
35847
- } else if (typeof operatingHours.breaks === "object" && operatingHours.breaks.breaks) {
35848
- breaks = operatingHours.breaks.breaks.map((breakItem) => ({
35849
- startTime: breakItem.start || breakItem.startTime || "00:00",
35850
- endTime: breakItem.end || breakItem.endTime || "00:00",
35851
- duration: breakItem.duration || calculateShiftHours2(breakItem.start || breakItem.startTime || "00:00", breakItem.end || breakItem.endTime || "00:00", []) * 60
35852
- }));
35853
- }
35854
- }
35855
- const shiftHours = calculateShiftHours2(startTime, endTime, breaks);
35856
- const currentLineStateFromLoop = updatedLineWorkspaces[lineId];
35857
- if (determinedProductId !== currentLineStateFromLoop?.productId || startTime !== currentLineStateFromLoop?.shiftStartTime || endTime !== currentLineStateFromLoop?.shiftEndTime || shiftHours !== currentLineStateFromLoop?.shiftHours || JSON.stringify(breaks) !== JSON.stringify(currentLineStateFromLoop?.breaks)) {
35858
- updatedLineWorkspaces[lineId] = {
35859
- ...currentLineStateFromLoop || {},
35860
- factoryId: currentFactoryId,
35861
- // Ensure factoryId is preserved
35862
- productId: determinedProductId,
35863
- shiftStartTime: startTime,
35864
- shiftEndTime: endTime,
35865
- breaks,
35866
- shiftHours: Number(shiftHours),
35867
- workspaces: currentLineStateFromLoop?.workspaces || []
35868
- };
35869
- hasUpdates = true;
35870
- }
35871
- } catch (lineError) {
35872
- console.error(`Error processing line ${lineId}:`, lineError);
35873
- }
35874
- }
35875
- if (hasUpdates) {
35876
- setLineWorkspaces(updatedLineWorkspaces);
35877
- }
35878
- } catch (error) {
35879
- console.error("Error in fetchLineThresholds outer try-catch:", error);
35880
- }
35881
- }, [selectedShift, supabase, lineIds, lineWorkspaces, allShiftsData]);
35808
+ }, [lineIds, companyId, loadOperatingHours]);
35882
35809
  const fetchAllShiftsData = useCallback(async (currentWorkspaces) => {
35883
35810
  if (!supabase) return;
35884
35811
  const currentDate = getOperationalDate();
@@ -35888,32 +35815,25 @@ var TargetsView = ({
35888
35815
  1: JSON.parse(JSON.stringify(currentWorkspaces))
35889
35816
  // Deep clone for night shift
35890
35817
  };
35818
+ const newDbValues = { 0: {}, 1: {} };
35891
35819
  for (const shiftId of [0, 1]) {
35892
35820
  for (const lineId of lineIds) {
35893
35821
  try {
35894
- 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();
35895
- if (hoursError) {
35896
- console.error(`Error fetching operating hours for line ${lineId}, shift ${shiftId}:`, hoursError);
35822
+ const operatingHoursData = await loadOperatingHours(lineId, shiftId);
35823
+ if (!operatingHoursData) {
35824
+ console.warn(`No operating hours for line ${lineId}, shift ${shiftId} - using defaults`);
35897
35825
  continue;
35898
35826
  }
35899
- let breaks = [];
35900
- if (operatingHours?.breaks) {
35901
- if (Array.isArray(operatingHours.breaks)) {
35902
- breaks = operatingHours.breaks.map((breakItem) => ({
35903
- startTime: breakItem.start || breakItem.startTime || "00:00",
35904
- endTime: breakItem.end || breakItem.endTime || "00:00",
35905
- duration: breakItem.duration || calculateShiftHours2(breakItem.start || breakItem.startTime || "00:00", breakItem.end || breakItem.endTime || "00:00", []) * 60
35906
- }));
35907
- }
35908
- }
35909
- const startTime = operatingHours?.start_time || "08:00";
35910
- const endTime = operatingHours?.end_time || "19:00";
35827
+ const { startTime, endTime, breaks } = operatingHoursData;
35911
35828
  const shiftHours = calculateShiftHours2(startTime, endTime, breaks);
35912
35829
  const actionThresholds = await workspaceService.getActionThresholds(
35913
35830
  lineId,
35914
35831
  currentDate,
35915
35832
  shiftId
35916
35833
  );
35834
+ if (!newDbValues[shiftId][lineId]) {
35835
+ newDbValues[shiftId][lineId] = {};
35836
+ }
35917
35837
  const existingLine = newAllShiftsData[shiftId][lineId];
35918
35838
  if (existingLine) {
35919
35839
  newAllShiftsData[shiftId][lineId] = {
@@ -35925,6 +35845,11 @@ var TargetsView = ({
35925
35845
  workspaces: existingLine.workspaces.map((ws) => {
35926
35846
  const threshold = actionThresholds.find((t) => t.workspace_id === ws.id);
35927
35847
  if (threshold) {
35848
+ newDbValues[shiftId][lineId][ws.id] = {
35849
+ targetPPH: threshold.pph_threshold,
35850
+ targetCycleTime: threshold.ideal_cycle_time,
35851
+ targetDayOutput: threshold.total_day_output
35852
+ };
35928
35853
  return {
35929
35854
  ...ws,
35930
35855
  targetPPH: threshold.pph_threshold,
@@ -35942,114 +35867,17 @@ var TargetsView = ({
35942
35867
  }
35943
35868
  }
35944
35869
  setAllShiftsData(newAllShiftsData);
35945
- }, [supabase, lineIds]);
35946
- const loadOperatingHours = useCallback(async (lineId, shiftId) => {
35947
- try {
35948
- if (!supabase) return null;
35949
- const { data, error } = await supabase.from("line_operating_hours").select("start_time, end_time, breaks").eq("line_id", lineId).eq("shift_id", shiftId).maybeSingle();
35950
- if (error) {
35951
- if (error.code === "PGRST116") {
35952
- console.log(`No operating hours found for line ${lineId}, shift ${shiftId}`);
35953
- return {
35954
- startTime: "08:00",
35955
- // Default values
35956
- endTime: "19:00",
35957
- breaks: []
35958
- };
35959
- } else {
35960
- console.error("Error fetching operating hours:", error);
35961
- return null;
35962
- }
35963
- }
35964
- let breaks = [];
35965
- if (data?.breaks) {
35966
- if (Array.isArray(data.breaks)) {
35967
- breaks = data.breaks.map((breakItem) => {
35968
- const startTime = breakItem.start || breakItem.startTime || "00:00";
35969
- const endTime = breakItem.end || breakItem.endTime || "00:00";
35970
- const calculateDuration = (start, end) => {
35971
- const [startHour, startMinute] = start.split(":").map(Number);
35972
- const [endHour, endMinute] = end.split(":").map(Number);
35973
- let startMinutes = startHour * 60 + startMinute;
35974
- let endMinutes = endHour * 60 + endMinute;
35975
- if (endMinutes < startMinutes) {
35976
- endMinutes += 24 * 60;
35977
- }
35978
- return endMinutes - startMinutes;
35979
- };
35980
- return {
35981
- startTime,
35982
- endTime,
35983
- duration: breakItem.duration || calculateDuration(startTime, endTime)
35984
- };
35985
- });
35986
- } else if (typeof data.breaks === "object" && data.breaks.breaks) {
35987
- breaks = data.breaks.breaks.map((breakItem) => {
35988
- const startTime = breakItem.start || breakItem.startTime || "00:00";
35989
- const endTime = breakItem.end || breakItem.endTime || "00:00";
35990
- const calculateDuration = (start, end) => {
35991
- const [startHour, startMinute] = start.split(":").map(Number);
35992
- const [endHour, endMinute] = end.split(":").map(Number);
35993
- let startMinutes = startHour * 60 + startMinute;
35994
- let endMinutes = endHour * 60 + endMinute;
35995
- if (endMinutes < startMinutes) {
35996
- endMinutes += 24 * 60;
35997
- }
35998
- return endMinutes - startMinutes;
35999
- };
36000
- return {
36001
- startTime,
36002
- endTime,
36003
- duration: breakItem.duration || calculateDuration(startTime, endTime)
36004
- };
36005
- });
36006
- } else if (typeof data.breaks === "string") {
36007
- try {
36008
- const parsedBreaks = JSON.parse(data.breaks);
36009
- if (Array.isArray(parsedBreaks)) {
36010
- breaks = parsedBreaks.map((breakItem) => ({
36011
- startTime: breakItem.start || breakItem.startTime || "00:00",
36012
- endTime: breakItem.end || breakItem.endTime || "00:00",
36013
- duration: breakItem.duration || 0
36014
- }));
36015
- } else if (parsedBreaks.breaks && Array.isArray(parsedBreaks.breaks)) {
36016
- breaks = parsedBreaks.breaks.map((breakItem) => ({
36017
- startTime: breakItem.start || breakItem.startTime || "00:00",
36018
- endTime: breakItem.end || breakItem.endTime || "00:00",
36019
- duration: breakItem.duration || 0
36020
- }));
36021
- }
36022
- } catch (e) {
36023
- console.error("Error parsing breaks data:", e);
36024
- }
36025
- }
36026
- }
36027
- return {
36028
- startTime: data?.start_time || "08:00",
36029
- endTime: data?.end_time || "19:00",
36030
- breaks
36031
- };
36032
- } catch (e) {
36033
- console.error("Exception when loading operating hours:", e);
36034
- return {
36035
- startTime: "08:00",
36036
- endTime: "19:00",
36037
- breaks: []
36038
- };
36039
- }
36040
- }, [supabase]);
35870
+ setDbValues(newDbValues);
35871
+ }, [supabase, lineIds, loadOperatingHours]);
36041
35872
  const toggleLineDropdown = useCallback((lineId) => {
36042
- setLineWorkspaces((prev) => {
36043
- const newIsOpen = !prev[lineId].isOpen;
35873
+ setDropdownStates((prev) => {
35874
+ const newIsOpen = !prev[lineId];
36044
35875
  if (typeof window !== "undefined") {
36045
35876
  localStorage.setItem(`line_${lineId}_open`, JSON.stringify(newIsOpen));
36046
35877
  }
36047
35878
  return {
36048
35879
  ...prev,
36049
- [lineId]: {
36050
- ...prev[lineId],
36051
- isOpen: newIsOpen
36052
- }
35880
+ [lineId]: newIsOpen
36053
35881
  };
36054
35882
  });
36055
35883
  }, []);
@@ -36098,6 +35926,8 @@ var TargetsView = ({
36098
35926
  }
36099
35927
  };
36100
35928
  const updateWorkspaceTarget = (lineId, workspaceId, field, value) => {
35929
+ const fieldKey = `${lineId}-${workspaceId}-${field}`;
35930
+ setUserEditedFields((prev) => new Set(prev).add(fieldKey));
36101
35931
  setLineWorkspaces((prev) => {
36102
35932
  const shiftHours = prev[lineId].shiftHours;
36103
35933
  return {
@@ -36122,11 +35952,7 @@ var TargetsView = ({
36122
35952
  } else if (field === "targetDayOutput") {
36123
35953
  updates.targetDayOutput = value;
36124
35954
  if (value !== "") {
36125
- const breaks = prev[lineId].breaks;
36126
- const totalBreakMinutes = breaks.reduce((total, b) => total + b.duration, 0);
36127
- const totalBreakHours = totalBreakMinutes / 60;
36128
- const realWorkHours = shiftHours - totalBreakHours;
36129
- const calculatedPPH = Math.round(value / realWorkHours);
35955
+ const calculatedPPH = Math.round(value / shiftHours);
36130
35956
  updates.targetPPH = calculatedPPH;
36131
35957
  } else {
36132
35958
  updates.targetPPH = "";
@@ -36141,62 +35967,35 @@ var TargetsView = ({
36141
35967
  };
36142
35968
  const handleShiftChange = (shiftId) => {
36143
35969
  setSelectedShift(shiftId);
36144
- const loadShiftHours = async () => {
36145
- const updatedLineWorkspaces = { ...allShiftsData[shiftId] };
36146
- let hasUpdates = false;
36147
- for (const lineId of lineIds) {
36148
- try {
36149
- const operatingHours = await loadOperatingHours(lineId, shiftId);
36150
- if (!operatingHours) continue;
36151
- const shiftHours = calculateShiftHours2(
36152
- operatingHours.startTime,
36153
- operatingHours.endTime,
36154
- operatingHours.breaks
36155
- );
36156
- updatedLineWorkspaces[lineId] = {
36157
- ...updatedLineWorkspaces[lineId],
36158
- shiftStartTime: operatingHours.startTime,
36159
- shiftEndTime: operatingHours.endTime,
36160
- breaks: operatingHours.breaks,
36161
- shiftHours: Number(shiftHours),
36162
- workspaces: updatedLineWorkspaces[lineId].workspaces.map((ws) => {
36163
- let updatedPPH = ws.targetPPH;
36164
- if (ws.targetCycleTime !== "") {
36165
- const idealPPH = calculatePPH(
36166
- ws.targetCycleTime,
36167
- operatingHours.breaks,
36168
- Number(shiftHours)
36169
- );
36170
- const shouldUpdatePPH = typeof ws.targetPPH === "string" ? ws.targetPPH === "" : ws.targetPPH === 0 || !ws.targetPPH;
36171
- if (shouldUpdatePPH) {
36172
- updatedPPH = idealPPH;
35970
+ setUserEditedFields(/* @__PURE__ */ new Set());
35971
+ if (dbValues[shiftId] && Object.keys(dbValues[shiftId]).length > 0) {
35972
+ setAllShiftsData((prev) => {
35973
+ const updatedShiftData = { ...prev[shiftId] };
35974
+ for (const lineId of Object.keys(updatedShiftData)) {
35975
+ if (dbValues[shiftId][lineId]) {
35976
+ updatedShiftData[lineId] = {
35977
+ ...updatedShiftData[lineId],
35978
+ workspaces: updatedShiftData[lineId].workspaces.map((ws) => {
35979
+ const dbValue = dbValues[shiftId][lineId][ws.id];
35980
+ if (dbValue) {
35981
+ return {
35982
+ ...ws,
35983
+ targetPPH: dbValue.targetPPH,
35984
+ targetCycleTime: dbValue.targetCycleTime,
35985
+ targetDayOutput: dbValue.targetDayOutput
35986
+ };
36173
35987
  }
36174
- }
36175
- const updatedDayOutput = calculateDayOutput(
36176
- updatedPPH,
36177
- Number(shiftHours),
36178
- operatingHours.breaks
36179
- );
36180
- return {
36181
- ...ws,
36182
- targetPPH: updatedPPH,
36183
- targetDayOutput: updatedDayOutput
36184
- };
36185
- })
36186
- };
36187
- hasUpdates = true;
36188
- } catch (e) {
36189
- console.error(`Exception when loading shift hours for line ${lineId}:`, e);
35988
+ return ws;
35989
+ })
35990
+ };
35991
+ }
36190
35992
  }
36191
- }
36192
- if (hasUpdates) {
36193
- setAllShiftsData((prev) => ({
35993
+ return {
36194
35994
  ...prev,
36195
- [shiftId]: updatedLineWorkspaces
36196
- }));
36197
- }
36198
- };
36199
- loadShiftHours();
35995
+ [shiftId]: updatedShiftData
35996
+ };
35997
+ });
35998
+ }
36200
35999
  };
36201
36000
  const handleActionTypeChange = useCallback((lineId, workspaceId, newActionType) => {
36202
36001
  if (!actionIds) return;
@@ -36286,6 +36085,31 @@ var TargetsView = ({
36286
36085
  throw lineUpsertError;
36287
36086
  }
36288
36087
  console.log(`[handleSaveLine] Successfully upserted line_thresholds for ${lineId}`);
36088
+ setDbValues((prev) => ({
36089
+ ...prev,
36090
+ [selectedShift]: {
36091
+ ...prev[selectedShift],
36092
+ [lineId]: lineDataToSave.workspaces.reduce((acc, ws) => ({
36093
+ ...acc,
36094
+ [ws.id]: {
36095
+ targetPPH: ws.targetPPH,
36096
+ targetCycleTime: ws.targetCycleTime,
36097
+ targetDayOutput: ws.targetDayOutput
36098
+ }
36099
+ }), {})
36100
+ }
36101
+ }));
36102
+ console.log(`[handleSaveLine] Updated dbValues for line ${lineId}, shift ${selectedShift}`);
36103
+ setUserEditedFields((prev) => {
36104
+ const newSet = new Set(prev);
36105
+ lineDataToSave.workspaces.forEach((ws) => {
36106
+ newSet.delete(`${lineId}-${ws.id}-targetPPH`);
36107
+ newSet.delete(`${lineId}-${ws.id}-targetCycleTime`);
36108
+ newSet.delete(`${lineId}-${ws.id}-targetDayOutput`);
36109
+ });
36110
+ return newSet;
36111
+ });
36112
+ console.log(`[handleSaveLine] Cleared user edited fields for line ${lineId}`);
36289
36113
  setSaveSuccess((prev) => ({ ...prev, [lineId]: true }));
36290
36114
  toast.success(`${lineNames[lineId] || lineId} targets saved successfully`);
36291
36115
  if (onSaveChanges) onSaveChanges(lineId);
@@ -36299,7 +36123,7 @@ var TargetsView = ({
36299
36123
  setSavingLines((prev) => ({ ...prev, [lineId]: false }));
36300
36124
  console.log(`[handleSaveLine] Set savingLines to false for ${lineId} in finally block`);
36301
36125
  }
36302
- }, [supabase, lineWorkspaces, selectedShift, lineNames, onSaveChanges]);
36126
+ }, [supabase, lineWorkspaces, selectedShift, lineNames, onSaveChanges, skuEnabled, dashboardConfig]);
36303
36127
  const handleBulkConfigure = async (updates) => {
36304
36128
  if (!actionIds) return;
36305
36129
  if (updates.productId !== void 0) {
@@ -36347,6 +36171,7 @@ var TargetsView = ({
36347
36171
  isLoading: isLoading || skusLoading,
36348
36172
  lineWorkspaces,
36349
36173
  lineNames,
36174
+ dropdownStates,
36350
36175
  savingLines,
36351
36176
  saveSuccess,
36352
36177
  selectedShift,
@@ -36371,7 +36196,7 @@ var TargetsView = ({
36371
36196
  };
36372
36197
  var TargetsViewWithDisplayNames = withAllWorkspaceDisplayNames(TargetsView);
36373
36198
  var TargetsView_default = TargetsViewWithDisplayNames;
36374
- var AuthenticatedTargetsView = withAuth(TargetsViewWithDisplayNames);
36199
+ var AuthenticatedTargetsView = withAuth(React19__default.memo(TargetsViewWithDisplayNames));
36375
36200
 
36376
36201
  // src/views/workspace-detail-view.utils.ts
36377
36202
  var formatISTDate2 = (date = /* @__PURE__ */ new Date(), options) => {
@@ -37842,4 +37667,4 @@ var streamProxyConfig = {
37842
37667
  }
37843
37668
  };
37844
37669
 
37845
- export { ACTION_NAMES, AIAgentView_default as AIAgentView, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedTargetsView, BarChart, BaseHistoryCalendar, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, CongratulationsOverlay, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, EmptyStateMessage, EncouragementOverlay, FactoryView_default as FactoryView, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HelpView_default as HelpView, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, MainLayout, MetricCard_default as MetricCard, NoWorkspaceData, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, TargetWorkspaceGrid, TargetsView_default as TargetsView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TimeDisplay_default as TimeDisplay, TimePickerDropdown, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, authCoreService, authOTPService, authRateLimitService, cacheService, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createStreamProxyHandler, createSupabaseClient, createThrottledReload, dashboardService, deleteThread, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getOperationalDate, getS3SignedUrl, getS3VideoSrc, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isPrefetchError, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, mergeWithDefaultConfig, migrateLegacyConfiguration, optifyeAgentClient, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, updateThreadTitle, useActiveBreaks, useAllWorkspaceMetrics, useAnalyticsConfig, useAudioService, useAuth, useAuthConfig, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineWorkspaceMetrics, useMessages, useMetrics, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useTargets, useTheme, useThemeConfig, useThreads, useTicketHistory, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, videoPrefetchManager, videoPreloader, whatsappService, withAuth, withRegistry, workspaceService };
37670
+ export { ACTION_NAMES, AIAgentView_default as AIAgentView, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, BarChart, BaseHistoryCalendar, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, CongratulationsOverlay, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, EmptyStateMessage, EncouragementOverlay, FactoryView_default as FactoryView, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HelpView_default as HelpView, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, MainLayout, MetricCard_default as MetricCard, NoWorkspaceData, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, TargetWorkspaceGrid, TargetsView_default as TargetsView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TimeDisplay_default as TimeDisplay, TimePickerDropdown, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, authCoreService, authOTPService, authRateLimitService, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createStreamProxyHandler, createSupabaseClient, createThrottledReload, dashboardService, deleteThread, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getOperationalDate, getS3SignedUrl, getS3VideoSrc, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isPrefetchError, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, mergeWithDefaultConfig, migrateLegacyConfiguration, optifyeAgentClient, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, updateThreadTitle, useActiveBreaks, useAllWorkspaceMetrics, useAnalyticsConfig, useAudioService, useAuth, useAuthConfig, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineWorkspaceMetrics, useMessages, useMetrics, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useTargets, useTheme, useThemeConfig, useThreads, useTicketHistory, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, videoPrefetchManager, videoPreloader, whatsappService, withAuth, withRegistry, workspaceService };