@optifye/dashboard-core 6.3.5 → 6.4.1

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';
@@ -3779,6 +3779,11 @@ var S3ClipsService = class {
3779
3779
  this.requestCache = new RequestDeduplicationCache();
3780
3780
  // Flag to prevent metadata fetching during index building
3781
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;
3782
3787
  this.config = config;
3783
3788
  if (!config.s3Config) {
3784
3789
  throw new Error("S3 configuration is required");
@@ -3824,7 +3829,12 @@ var S3ClipsService = class {
3824
3829
  */
3825
3830
  async listS3Clips(params) {
3826
3831
  const { workspaceId, date, shiftId, maxKeys } = params;
3832
+ if (!isValidShiftId(shiftId)) {
3833
+ console.error(`[S3ClipsService] Invalid shift ID: ${shiftId}. Must be 0 (day) or 1 (night)`);
3834
+ return [];
3835
+ }
3827
3836
  const prefix = `sop_violations/${workspaceId}/${date}/${shiftId}/`;
3837
+ console.log(`[S3ClipsService] Listing clips for workspace: ${workspaceId}, date: ${date}, shift: ${shiftId}`);
3828
3838
  const deduplicationKey = `list-s3-clips:${prefix}:${maxKeys || "all"}`;
3829
3839
  return this.requestCache.deduplicate(
3830
3840
  deduplicationKey,
@@ -3926,20 +3936,36 @@ var S3ClipsService = class {
3926
3936
  return null;
3927
3937
  }
3928
3938
  }
3939
+ /**
3940
+ * Control prefetch mode to prevent metadata fetching during background operations
3941
+ */
3942
+ setPrefetchMode(enabled) {
3943
+ this.isPrefetching = enabled;
3944
+ console.log(`[S3ClipsService] Prefetch mode ${enabled ? "enabled" : "disabled"} - metadata fetching ${enabled ? "blocked" : "allowed"}`);
3945
+ }
3929
3946
  /**
3930
3947
  * Fetches full metadata including timestamps with deduplication
3931
3948
  */
3932
3949
  async getFullMetadata(playlistUri) {
3933
- if (this.isIndexBuilding) {
3934
- console.warn(`[S3ClipsService] Skipping metadata fetch during index building for: ${playlistUri}`);
3950
+ if (this.isIndexBuilding || this.isPrefetching) {
3951
+ console.warn(`[S3ClipsService] Skipping metadata fetch - building: ${this.isIndexBuilding}, prefetching: ${this.isPrefetching}`);
3935
3952
  return null;
3936
3953
  }
3937
- const deduplicationKey = `full-metadata:${playlistUri}`;
3938
- return this.requestCache.deduplicate(
3939
- deduplicationKey,
3940
- () => this.executeGetFullMetadata(playlistUri),
3941
- "FullMetadata"
3942
- );
3954
+ if (this.currentMetadataFetches >= this.MAX_CONCURRENT_METADATA) {
3955
+ console.warn(`[S3ClipsService] Skipping metadata - max concurrent fetches (${this.MAX_CONCURRENT_METADATA}) reached`);
3956
+ return null;
3957
+ }
3958
+ this.currentMetadataFetches++;
3959
+ try {
3960
+ const deduplicationKey = `full-metadata:${playlistUri}`;
3961
+ return await this.requestCache.deduplicate(
3962
+ deduplicationKey,
3963
+ () => this.executeGetFullMetadata(playlistUri),
3964
+ "FullMetadata"
3965
+ );
3966
+ } finally {
3967
+ this.currentMetadataFetches--;
3968
+ }
3943
3969
  }
3944
3970
  /**
3945
3971
  * Internal implementation of full metadata fetching
@@ -3989,6 +4015,10 @@ var S3ClipsService = class {
3989
4015
  return sopConfig.default;
3990
4016
  }
3991
4017
  async getClipCounts(workspaceId, date, shiftId, buildIndex) {
4018
+ if (!isValidShiftId(shiftId)) {
4019
+ console.error(`[S3ClipsService] getClipCounts - Invalid shift ID: ${shiftId}. Must be 0 (day) or 1 (night)`);
4020
+ return buildIndex ? { counts: {}, videoIndex: { byCategory: /* @__PURE__ */ new Map(), allVideos: [], counts: {}, workspaceId, date, shiftId: "0", lastUpdated: /* @__PURE__ */ new Date() } } : {};
4021
+ }
3992
4022
  const deduplicationKey = `clip-counts:${workspaceId}:${date}:${shiftId}:${buildIndex ? "with-index" : "counts-only"}`;
3993
4023
  return this.requestCache.deduplicate(
3994
4024
  deduplicationKey,
@@ -4018,7 +4048,8 @@ var S3ClipsService = class {
4018
4048
  "cycle_completion",
4019
4049
  "bottleneck"
4020
4050
  ];
4021
- console.log(`[S3ClipsService] ${buildIndex ? "Building video index and counting" : "Fast counting"} clips for ${workspaceId} on ${date}, shift ${shiftId}`);
4051
+ const shiftName = shiftId === 0 || shiftId === "0" ? "Day" : "Night";
4052
+ console.log(`[S3ClipsService] ${buildIndex ? "Building video index and counting" : "Fast counting"} clips for ${workspaceId} on ${date}, shift ${shiftId} (${shiftName} Shift)`);
4022
4053
  const startTime = performance.now();
4023
4054
  const videoIndex = buildIndex ? {
4024
4055
  byCategory: /* @__PURE__ */ new Map(),
@@ -4165,6 +4196,10 @@ var S3ClipsService = class {
4165
4196
  * Get first clip for a specific category with deduplication
4166
4197
  */
4167
4198
  async getFirstClipForCategory(workspaceId, date, shiftId, category) {
4199
+ if (!isValidShiftId(shiftId)) {
4200
+ console.error(`[S3ClipsService] getFirstClipForCategory - Invalid shift ID: ${shiftId}. Must be 0 (day) or 1 (night)`);
4201
+ return null;
4202
+ }
4168
4203
  const deduplicationKey = `first-clip:${workspaceId}:${date}:${shiftId}:${category}`;
4169
4204
  return this.requestCache.deduplicate(
4170
4205
  deduplicationKey,
@@ -4441,7 +4476,8 @@ var S3ClipsService = class {
4441
4476
  date,
4442
4477
  shiftId.toString(),
4443
4478
  includeCycleTime || false,
4444
- includeMetadata || (!!timestampStart || !!timestampEnd)
4479
+ includeMetadata || false
4480
+ // Never fetch metadata for timestamp filtering to prevent flooding
4445
4481
  );
4446
4482
  });
4447
4483
  const videoResults = await Promise.all(videoPromises);
@@ -4512,6 +4548,7 @@ var VideoPrefetchManager = class extends EventEmitter {
4512
4548
  }
4513
4549
  /**
4514
4550
  * Get or create S3 service instance for dashboard config
4551
+ * Public method to allow sharing the same S3Service instance across components
4515
4552
  */
4516
4553
  getS3Service(dashboardConfig) {
4517
4554
  const configKey = JSON.stringify(dashboardConfig.s3Config);
@@ -4581,75 +4618,80 @@ var VideoPrefetchManager = class extends EventEmitter {
4581
4618
  * Perform the actual prefetch work
4582
4619
  */
4583
4620
  async performPrefetchWork(key, params, s3Service, buildIndex) {
4584
- const cacheKey = `clip-counts:${params.workspaceId}:${params.date}:${params.shift}`;
4585
- const cachedResult = await smartVideoCache.getClipCounts(cacheKey);
4586
- if (cachedResult) {
4587
- console.log(`[VideoPrefetchManager] Found cached data for ${key}`);
4588
- this.updateRequestStatus(key, "fully_indexed" /* FULLY_INDEXED */, cachedResult);
4589
- return cachedResult;
4590
- }
4591
- if (buildIndex) {
4592
- const countsOnly = await s3Service.getClipCounts(
4621
+ s3Service.setPrefetchMode(true);
4622
+ try {
4623
+ const cacheKey = `clip-counts:${params.workspaceId}:${params.date}:${params.shift}`;
4624
+ const cachedResult = await smartVideoCache.getClipCounts(cacheKey);
4625
+ if (cachedResult) {
4626
+ console.log(`[VideoPrefetchManager] Found cached data for ${key}`);
4627
+ this.updateRequestStatus(key, "fully_indexed" /* FULLY_INDEXED */, cachedResult);
4628
+ return cachedResult;
4629
+ }
4630
+ if (buildIndex) {
4631
+ const countsOnly = await s3Service.getClipCounts(
4632
+ params.workspaceId,
4633
+ params.date,
4634
+ params.shift
4635
+ );
4636
+ if (typeof countsOnly === "object" && countsOnly !== null) {
4637
+ const renderReadyData = {
4638
+ counts: countsOnly,
4639
+ videoIndex: {
4640
+ byCategory: /* @__PURE__ */ new Map(),
4641
+ allVideos: [],
4642
+ counts: countsOnly,
4643
+ workspaceId: params.workspaceId,
4644
+ date: params.date,
4645
+ shiftId: params.shift,
4646
+ lastUpdated: /* @__PURE__ */ new Date(),
4647
+ _debugId: `vid_RENDER_READY_${Date.now()}_${Math.random().toString(36).substring(7)}`
4648
+ }
4649
+ };
4650
+ this.updateRequestStatus(key, "render_ready" /* RENDER_READY */, renderReadyData);
4651
+ console.log(`[VideoPrefetchManager] Render ready, building full index for ${key}`);
4652
+ }
4653
+ }
4654
+ const fullResult = await s3Service.getClipCountsCacheFirst(
4593
4655
  params.workspaceId,
4594
4656
  params.date,
4595
- params.shift
4657
+ params.shift,
4658
+ true
4596
4659
  );
4597
- if (typeof countsOnly === "object" && countsOnly !== null) {
4598
- const renderReadyData = {
4599
- counts: countsOnly,
4660
+ if (fullResult && typeof fullResult === "object" && "videoIndex" in fullResult) {
4661
+ const clipCountsWithIndex = fullResult;
4662
+ if (clipCountsWithIndex.videoIndex.allVideos.length > 0) {
4663
+ console.log(`[VideoPrefetchManager] Video index properly populated with ${clipCountsWithIndex.videoIndex.allVideos.length} videos`);
4664
+ } else {
4665
+ console.warn(`[VideoPrefetchManager] Video index is empty, but counts available:`, clipCountsWithIndex.counts);
4666
+ }
4667
+ await smartVideoCache.setClipCounts(cacheKey, clipCountsWithIndex, 5 * 60);
4668
+ this.updateRequestStatus(key, "fully_indexed" /* FULLY_INDEXED */, clipCountsWithIndex);
4669
+ console.log(`[VideoPrefetchManager] Fully indexed: ${key} (${clipCountsWithIndex.videoIndex.allVideos.length} videos)`);
4670
+ return clipCountsWithIndex;
4671
+ } else if (fullResult && typeof fullResult === "object") {
4672
+ console.log(`[VideoPrefetchManager] Received counts only, building fallback data structure`);
4673
+ const countsResult = fullResult;
4674
+ const fallbackData = {
4675
+ counts: countsResult,
4600
4676
  videoIndex: {
4601
4677
  byCategory: /* @__PURE__ */ new Map(),
4602
4678
  allVideos: [],
4603
- counts: countsOnly,
4679
+ counts: countsResult,
4604
4680
  workspaceId: params.workspaceId,
4605
4681
  date: params.date,
4606
4682
  shiftId: params.shift,
4607
4683
  lastUpdated: /* @__PURE__ */ new Date(),
4608
- _debugId: `vid_RENDER_READY_${Date.now()}_${Math.random().toString(36).substring(7)}`
4684
+ _debugId: `vid_FALLBACK_${Date.now()}_${Math.random().toString(36).substring(7)}`
4609
4685
  }
4610
4686
  };
4611
- this.updateRequestStatus(key, "render_ready" /* RENDER_READY */, renderReadyData);
4612
- console.log(`[VideoPrefetchManager] Render ready, building full index for ${key}`);
4613
- }
4614
- }
4615
- const fullResult = await s3Service.getClipCountsCacheFirst(
4616
- params.workspaceId,
4617
- params.date,
4618
- params.shift,
4619
- true
4620
- );
4621
- if (fullResult && typeof fullResult === "object" && "videoIndex" in fullResult) {
4622
- const clipCountsWithIndex = fullResult;
4623
- if (clipCountsWithIndex.videoIndex.allVideos.length > 0) {
4624
- console.log(`[VideoPrefetchManager] Video index properly populated with ${clipCountsWithIndex.videoIndex.allVideos.length} videos`);
4687
+ this.updateRequestStatus(key, "render_ready" /* RENDER_READY */, fallbackData);
4688
+ return fallbackData;
4625
4689
  } else {
4626
- console.warn(`[VideoPrefetchManager] Video index is empty, but counts available:`, clipCountsWithIndex.counts);
4627
- }
4628
- await smartVideoCache.setClipCounts(cacheKey, clipCountsWithIndex, 5 * 60);
4629
- this.updateRequestStatus(key, "fully_indexed" /* FULLY_INDEXED */, clipCountsWithIndex);
4630
- console.log(`[VideoPrefetchManager] Fully indexed: ${key} (${clipCountsWithIndex.videoIndex.allVideos.length} videos)`);
4631
- return clipCountsWithIndex;
4632
- } else if (fullResult && typeof fullResult === "object") {
4633
- console.log(`[VideoPrefetchManager] Received counts only, building fallback data structure`);
4634
- const countsResult = fullResult;
4635
- const fallbackData = {
4636
- counts: countsResult,
4637
- videoIndex: {
4638
- byCategory: /* @__PURE__ */ new Map(),
4639
- allVideos: [],
4640
- counts: countsResult,
4641
- workspaceId: params.workspaceId,
4642
- date: params.date,
4643
- shiftId: params.shift,
4644
- lastUpdated: /* @__PURE__ */ new Date(),
4645
- _debugId: `vid_FALLBACK_${Date.now()}_${Math.random().toString(36).substring(7)}`
4646
- }
4647
- };
4648
- this.updateRequestStatus(key, "render_ready" /* RENDER_READY */, fallbackData);
4649
- return fallbackData;
4650
- } else {
4651
- console.error(`[VideoPrefetchManager] Received null/undefined result from S3 service`);
4652
- throw new Error("Failed to fetch clip counts from S3 service");
4690
+ console.error(`[VideoPrefetchManager] Received null/undefined result from S3 service`);
4691
+ throw new Error("Failed to fetch clip counts from S3 service");
4692
+ }
4693
+ } finally {
4694
+ s3Service.setPrefetchMode(false);
4653
4695
  }
4654
4696
  }
4655
4697
  /**
@@ -4912,9 +4954,13 @@ var AuthProvider = ({ children }) => {
4912
4954
  const [loading, setLoading] = useState(true);
4913
4955
  const [error, setError] = useState(null);
4914
4956
  const router = useRouter();
4915
- const userProfileTable = authConfig?.userProfileTable;
4916
- const roleColumn = authConfig?.roleColumn || "role";
4957
+ authConfig?.userProfileTable;
4958
+ authConfig?.roleColumn || "role";
4917
4959
  const fetchUserDetails = useCallback(async (supabaseUser) => {
4960
+ console.log("[fetchUserDetails] Called for user:", supabaseUser.id, {
4961
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4962
+ stackTrace: new Error().stack?.split("\n").slice(1, 4).join(" -> ")
4963
+ });
4918
4964
  if (!supabaseUser) return null;
4919
4965
  const basicUser = {
4920
4966
  id: supabaseUser.id,
@@ -4923,42 +4969,34 @@ var AuthProvider = ({ children }) => {
4923
4969
  if (!supabase) return basicUser;
4924
4970
  try {
4925
4971
  const timeoutPromise = new Promise(
4926
- (_, reject) => setTimeout(() => reject(new Error("Profile fetch timeout")), 5e3)
4972
+ (_, reject) => setTimeout(() => {
4973
+ console.log("[fetchUserDetails] Timeout triggered after 2 seconds");
4974
+ reject(new Error("Profile fetch timeout"));
4975
+ }, 2e3)
4927
4976
  );
4928
4977
  const rolePromise = supabase.from("user_roles").select("role_level").eq("user_id", supabaseUser.id).single();
4929
- let profilePromise = null;
4930
- if (userProfileTable) {
4931
- profilePromise = supabase.from(userProfileTable).select(roleColumn).eq("id", supabaseUser.id).single();
4932
- }
4933
- const [roleResult, profileResult] = await Promise.race([
4934
- Promise.all([
4935
- rolePromise,
4936
- profilePromise || Promise.resolve(null)
4937
- ]),
4938
- timeoutPromise.then(() => {
4939
- throw new Error("Timeout");
4940
- })
4978
+ const roleResult = await Promise.race([
4979
+ rolePromise,
4980
+ timeoutPromise
4981
+ // Fixed: removed .then() which was causing the bug
4941
4982
  ]);
4942
4983
  let roleLevel = void 0;
4943
4984
  if (roleResult && !roleResult.error && roleResult.data) {
4944
4985
  roleLevel = roleResult.data.role_level;
4945
- } else if (roleResult?.error) {
4986
+ } else if (roleResult?.error && roleResult.error.code !== "PGRST116") {
4946
4987
  console.log("Error fetching role_level:", roleResult.error.message);
4947
4988
  }
4948
- let roleValue = void 0;
4949
- if (profileResult && !profileResult.error && profileResult.data) {
4950
- roleValue = profileResult.data[roleColumn];
4951
- }
4952
4989
  return {
4953
4990
  ...basicUser,
4954
- role: roleValue,
4955
4991
  role_level: roleLevel
4956
4992
  };
4957
4993
  } catch (err) {
4958
- console.error("Error fetching user details:", err);
4994
+ if (err instanceof Error && err.message.includes("timeout")) {
4995
+ console.warn("Auth fetch timeout - using basic user info");
4996
+ }
4959
4997
  return basicUser;
4960
4998
  }
4961
- }, [supabase, userProfileTable, roleColumn]);
4999
+ }, [supabase]);
4962
5000
  useEffect(() => {
4963
5001
  if (!supabase) return;
4964
5002
  let mounted = true;
@@ -4969,6 +5007,7 @@ var AuthProvider = ({ children }) => {
4969
5007
  }
4970
5008
  }, 1e4);
4971
5009
  const initializeAuth = async () => {
5010
+ const startTime = performance.now();
4972
5011
  try {
4973
5012
  const { data: { session: initialSession }, error: sessionError } = await supabase.auth.getSession();
4974
5013
  if (!mounted) return;
@@ -5011,12 +5050,38 @@ var AuthProvider = ({ children }) => {
5011
5050
  if (mounted) {
5012
5051
  setLoading(false);
5013
5052
  clearTimeout(safetyTimeout);
5053
+ const duration = performance.now() - startTime;
5054
+ if (duration > 3e3) {
5055
+ console.warn(`[Auth] Initialization took ${duration.toFixed(0)}ms - consider optimization`);
5056
+ } else if (process.env.NODE_ENV === "development") {
5057
+ console.log(`[Auth] Initialized in ${duration.toFixed(0)}ms`);
5058
+ }
5014
5059
  }
5015
5060
  }
5016
5061
  };
5017
5062
  initializeAuth();
5018
- const { data: { subscription } } = supabase.auth.onAuthStateChange(async (_event, currentSession) => {
5063
+ const { data: { subscription } } = supabase.auth.onAuthStateChange(async (event, currentSession) => {
5019
5064
  if (!mounted) return;
5065
+ console.log("[AuthContext] Auth event fired:", {
5066
+ event,
5067
+ sessionId: currentSession?.user?.id,
5068
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
5069
+ currentPath: typeof window !== "undefined" ? window.location.pathname : "unknown"
5070
+ });
5071
+ if (event === "TOKEN_REFRESHED") {
5072
+ if (session?.user?.id === currentSession?.user?.id && session?.user?.email === currentSession?.user?.email) {
5073
+ console.log("[AuthContext] Skipping TOKEN_REFRESHED - session unchanged");
5074
+ return;
5075
+ }
5076
+ }
5077
+ if (event !== "TOKEN_REFRESHED" && currentSession?.user) {
5078
+ console.log("[AuthContext] Non-TOKEN_REFRESHED event will trigger fetchUserDetails:", event);
5079
+ }
5080
+ const sessionChanged = session?.user?.id !== currentSession?.user?.id || session?.user?.email !== currentSession?.user?.email;
5081
+ if (!sessionChanged && user) {
5082
+ console.log("[AuthContext] Session and user unchanged, skipping update");
5083
+ return;
5084
+ }
5020
5085
  setSession(currentSession);
5021
5086
  setUser(null);
5022
5087
  setLoading(false);
@@ -6937,16 +7002,12 @@ var useTargets = (options) => {
6937
7002
  };
6938
7003
  var DEFAULT_SHIFTS_TABLE_NAME = "shift_configurations";
6939
7004
  var useShifts = () => {
6940
- const { supabaseUrl, supabaseKey } = useDashboardConfig();
6941
7005
  const { companyId } = useEntityConfig();
6942
7006
  const { tables } = useDatabaseConfig();
7007
+ const supabase = useSupabase();
6943
7008
  const [shifts, setShifts] = useState([]);
6944
7009
  const [isLoading, setIsLoading] = useState(true);
6945
7010
  const [error, setError] = useState(null);
6946
- const supabase = useMemo(() => {
6947
- if (!supabaseUrl || !supabaseKey) return null;
6948
- return createClient(supabaseUrl, supabaseKey);
6949
- }, [supabaseUrl, supabaseKey]);
6950
7011
  const shiftsTable = tables?.shiftConfigurations || DEFAULT_SHIFTS_TABLE_NAME;
6951
7012
  const fetchData = useCallback(async () => {
6952
7013
  if (!supabase) {
@@ -7645,6 +7706,7 @@ var runtimeWorkspaceDisplayNames = {};
7645
7706
  var isInitialized = false;
7646
7707
  var isInitializing = false;
7647
7708
  var initializedWithLineIds = [];
7709
+ var initializationPromise = null;
7648
7710
  function getCurrentLineIds() {
7649
7711
  try {
7650
7712
  const config = _getDashboardConfigInstance();
@@ -7665,52 +7727,79 @@ function getCurrentLineIds() {
7665
7727
  }
7666
7728
  async function initializeWorkspaceDisplayNames(explicitLineId) {
7667
7729
  console.log("\u{1F504} initializeWorkspaceDisplayNames called", { isInitialized, isInitializing, explicitLineId });
7668
- if (isInitialized || isInitializing) return;
7669
- isInitializing = true;
7670
- try {
7671
- console.log("\u{1F504} Starting Supabase workspace display names initialization...");
7672
- let targetLineIds = [];
7673
- if (explicitLineId) {
7674
- targetLineIds = [explicitLineId];
7675
- } else {
7676
- targetLineIds = getCurrentLineIds();
7730
+ if (isInitialized) return;
7731
+ if (initializationPromise) {
7732
+ console.log("\u{1F504} Already initializing, waiting for existing initialization...");
7733
+ await initializationPromise;
7734
+ return;
7735
+ }
7736
+ initializationPromise = (async () => {
7737
+ isInitializing = true;
7738
+ try {
7739
+ console.log("\u{1F504} Starting Supabase workspace display names initialization...");
7740
+ let targetLineIds = [];
7741
+ if (explicitLineId) {
7742
+ targetLineIds = [explicitLineId];
7743
+ } else {
7744
+ targetLineIds = getCurrentLineIds();
7745
+ }
7746
+ console.log("\u{1F504} Target line IDs for workspace filtering:", targetLineIds);
7747
+ runtimeWorkspaceDisplayNames = {};
7748
+ if (targetLineIds.length > 0) {
7749
+ for (const lineId of targetLineIds) {
7750
+ console.log(`\u{1F504} Fetching workspaces for line: ${lineId}`);
7751
+ const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
7752
+ runtimeWorkspaceDisplayNames[lineId] = {};
7753
+ lineDisplayNamesMap.forEach((displayName, workspaceId) => {
7754
+ runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
7755
+ });
7756
+ console.log(`\u2705 Stored ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
7757
+ }
7758
+ } else {
7759
+ console.warn("\u26A0\uFE0F No line IDs found, fetching all workspaces (less efficient)");
7760
+ const allWorkspacesMap = await workspaceService.getWorkspaceDisplayNames();
7761
+ runtimeWorkspaceDisplayNames["global"] = {};
7762
+ allWorkspacesMap.forEach((displayName, workspaceId) => {
7763
+ runtimeWorkspaceDisplayNames["global"][workspaceId] = displayName;
7764
+ });
7765
+ }
7766
+ isInitialized = true;
7767
+ initializedWithLineIds = targetLineIds;
7768
+ console.log("\u2705 Workspace display names initialized from Supabase:", runtimeWorkspaceDisplayNames);
7769
+ console.log("\u2705 Initialized with line IDs:", initializedWithLineIds);
7770
+ } catch (error) {
7771
+ console.error("\u274C Failed to initialize workspace display names from Supabase:", error);
7772
+ } finally {
7773
+ isInitializing = false;
7774
+ initializationPromise = null;
7677
7775
  }
7678
- console.log("\u{1F504} Target line IDs for workspace filtering:", targetLineIds);
7679
- runtimeWorkspaceDisplayNames = {};
7680
- if (targetLineIds.length > 0) {
7681
- for (const lineId of targetLineIds) {
7682
- console.log(`\u{1F504} Fetching workspaces for line: ${lineId}`);
7776
+ })();
7777
+ await initializationPromise;
7778
+ }
7779
+ var preInitializeWorkspaceDisplayNames = async (lineId) => {
7780
+ console.log("\u{1F504} preInitializeWorkspaceDisplayNames called for lineId:", lineId);
7781
+ if (isInitialized) {
7782
+ console.log("\u{1F504} Already initialized");
7783
+ if (lineId && !runtimeWorkspaceDisplayNames[lineId]) {
7784
+ console.log(`\u{1F504} Line ${lineId} not in cache, fetching...`);
7785
+ try {
7683
7786
  const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
7684
7787
  runtimeWorkspaceDisplayNames[lineId] = {};
7685
7788
  lineDisplayNamesMap.forEach((displayName, workspaceId) => {
7686
7789
  runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
7687
7790
  });
7688
- console.log(`\u2705 Stored ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
7791
+ console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
7792
+ } catch (error) {
7793
+ console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
7689
7794
  }
7690
- } else {
7691
- console.warn("\u26A0\uFE0F No line IDs found, fetching all workspaces (less efficient)");
7692
- const allWorkspacesMap = await workspaceService.getWorkspaceDisplayNames();
7693
- runtimeWorkspaceDisplayNames["global"] = {};
7694
- allWorkspacesMap.forEach((displayName, workspaceId) => {
7695
- runtimeWorkspaceDisplayNames["global"][workspaceId] = displayName;
7696
- });
7697
7795
  }
7698
- isInitialized = true;
7699
- initializedWithLineIds = targetLineIds;
7700
- console.log("\u2705 Workspace display names initialized from Supabase:", runtimeWorkspaceDisplayNames);
7701
- console.log("\u2705 Initialized with line IDs:", initializedWithLineIds);
7702
- } catch (error) {
7703
- console.error("\u274C Failed to initialize workspace display names from Supabase:", error);
7704
- } finally {
7705
- isInitializing = false;
7796
+ return;
7706
7797
  }
7707
- }
7708
- var preInitializeWorkspaceDisplayNames = async (lineId) => {
7709
- console.log("\u{1F504} preInitializeWorkspaceDisplayNames called for lineId:", lineId);
7710
- if (isInitialized || isInitializing) {
7711
- console.log("\u{1F504} Already initialized or initializing");
7798
+ if (initializationPromise) {
7799
+ console.log("\u{1F504} Already initializing, waiting for completion...");
7800
+ await initializationPromise;
7712
7801
  if (lineId && !runtimeWorkspaceDisplayNames[lineId]) {
7713
- console.log(`\u{1F504} Line ${lineId} not in cache, fetching...`);
7802
+ console.log(`\u{1F504} Line ${lineId} not in cache after init, fetching...`);
7714
7803
  try {
7715
7804
  const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
7716
7805
  runtimeWorkspaceDisplayNames[lineId] = {};
@@ -7728,7 +7817,12 @@ var preInitializeWorkspaceDisplayNames = async (lineId) => {
7728
7817
  };
7729
7818
  var forceRefreshWorkspaceDisplayNames = async (lineId) => {
7730
7819
  console.log("\u{1F504} forceRefreshWorkspaceDisplayNames called for lineId:", lineId);
7820
+ if (initializationPromise) {
7821
+ console.log("\u{1F504} Waiting for existing initialization to complete before refresh...");
7822
+ await initializationPromise;
7823
+ }
7731
7824
  clearWorkspaceDisplayNamesCache();
7825
+ initializationPromise = null;
7732
7826
  await initializeWorkspaceDisplayNames(lineId);
7733
7827
  };
7734
7828
  console.log("\u{1F504} Module loaded, will initialize lazily when first function is called");
@@ -7876,6 +7970,7 @@ var clearWorkspaceDisplayNamesCache = () => {
7876
7970
  isInitialized = false;
7877
7971
  isInitializing = false;
7878
7972
  initializedWithLineIds = [];
7973
+ initializationPromise = null;
7879
7974
  };
7880
7975
 
7881
7976
  // src/lib/hooks/useWorkspaceDisplayNames.ts
@@ -11512,7 +11607,18 @@ var usePrefetchClipCounts = ({
11512
11607
  }, [subscriberId]);
11513
11608
  const prefetchParams = useMemo(() => {
11514
11609
  const operationalDate = date || getOperationalDate();
11515
- const shiftStr = shift?.toString() || "0";
11610
+ let shiftStr;
11611
+ if (shift !== void 0 && shift !== null) {
11612
+ shiftStr = shift.toString();
11613
+ console.log(`[usePrefetchClipCounts] Using provided shift: ${shiftStr} for date: ${operationalDate}`);
11614
+ } else if (date) {
11615
+ shiftStr = "0";
11616
+ console.log(`[usePrefetchClipCounts] No shift provided for historical date ${date}, defaulting to day shift (0)`);
11617
+ } else {
11618
+ const currentShift = getCurrentShift2();
11619
+ shiftStr = currentShift.shiftId.toString();
11620
+ console.log(`[usePrefetchClipCounts] Using current operational shift: ${shiftStr} (${currentShift.shiftName})`);
11621
+ }
11516
11622
  return {
11517
11623
  workspaceId: workspaceId || "",
11518
11624
  date: operationalDate,
@@ -19516,11 +19622,13 @@ var withAuth = (WrappedComponent2, options) => {
19516
19622
  requireAuth: true,
19517
19623
  ...options
19518
19624
  };
19519
- return function WithAuthComponent(props) {
19625
+ const WithAuthComponent = React19.memo(function WithAuthComponent2(props) {
19520
19626
  const { session, loading } = useAuth();
19521
19627
  const router = useRouter();
19522
19628
  React19.useEffect(() => {
19523
- console.log("withAuth state:", { loading, hasSession: !!session, requireAuth: defaultOptions.requireAuth });
19629
+ if (process.env.NODE_ENV === "development" && process.env.DEBUG_AUTH === "true") {
19630
+ console.log("withAuth state:", { loading, hasSession: !!session, requireAuth: defaultOptions.requireAuth });
19631
+ }
19524
19632
  }, [session, loading]);
19525
19633
  React19.useEffect(() => {
19526
19634
  if (!loading && defaultOptions.requireAuth && !session) {
@@ -19535,7 +19643,9 @@ var withAuth = (WrappedComponent2, options) => {
19535
19643
  return null;
19536
19644
  }
19537
19645
  return /* @__PURE__ */ jsx(WrappedComponent2, { ...props });
19538
- };
19646
+ });
19647
+ WithAuthComponent.displayName = `withAuth(${WrappedComponent2.displayName || WrappedComponent2.name || "Component"})`;
19648
+ return WithAuthComponent;
19539
19649
  };
19540
19650
  var LoginPage = ({
19541
19651
  onRateLimitCheck,
@@ -26123,7 +26233,7 @@ var BottlenecksContent = ({
26123
26233
  const [duration, setDuration] = useState(0);
26124
26234
  const [currentIndex, setCurrentIndex] = useState(0);
26125
26235
  const [activeFilter, setActiveFilter] = useState(initialFilter);
26126
- const previousFilterRef = useRef(initialFilter);
26236
+ const previousFilterRef = useRef("");
26127
26237
  const [allVideos, setAllVideos] = useState([]);
26128
26238
  const [isLoading, setIsLoading] = useState(true);
26129
26239
  const [isCategoryLoading, setIsCategoryLoading] = useState(false);
@@ -26173,8 +26283,23 @@ var BottlenecksContent = ({
26173
26283
  console.warn("S3 configuration not found in dashboard config");
26174
26284
  return null;
26175
26285
  }
26176
- return new S3ClipsService(dashboardConfig);
26286
+ return videoPrefetchManager.getS3Service(dashboardConfig);
26177
26287
  }, [dashboardConfig]);
26288
+ const effectiveShift = useMemo(() => {
26289
+ if (shift !== void 0 && shift !== null) {
26290
+ const shiftStr = shift.toString();
26291
+ console.log(`[BottlenecksContent] Using provided shift: ${shiftStr} for date: ${date}`);
26292
+ return shiftStr;
26293
+ }
26294
+ if (date) {
26295
+ console.log(`[BottlenecksContent] No shift provided for historical date ${date}, defaulting to day shift (0)`);
26296
+ return "0";
26297
+ } else {
26298
+ const currentShift = getCurrentShift2();
26299
+ console.log(`[BottlenecksContent] Using current operational shift: ${currentShift.shiftId} (${currentShift.shiftName})`);
26300
+ return currentShift.shiftId.toString();
26301
+ }
26302
+ }, [shift, date]);
26178
26303
  const {
26179
26304
  data: prefetchData,
26180
26305
  isFullyIndexed,
@@ -26183,7 +26308,7 @@ var BottlenecksContent = ({
26183
26308
  } = usePrefetchClipCounts({
26184
26309
  workspaceId,
26185
26310
  date: date || getOperationalDate(),
26186
- shift: shift?.toString() || "0",
26311
+ shift: effectiveShift,
26187
26312
  enabled: !!workspaceId && !!s3ClipsService,
26188
26313
  buildIndex: true
26189
26314
  });
@@ -26197,7 +26322,7 @@ var BottlenecksContent = ({
26197
26322
  fetchInProgressRef.current.add(operationKey);
26198
26323
  try {
26199
26324
  const operationalDate = date || getOperationalDate();
26200
- const shiftStr = shift?.toString() || "0";
26325
+ const shiftStr = effectiveShift;
26201
26326
  console.log(`[BottlenecksContent] Fetching clip counts directly for ${workspaceId}`);
26202
26327
  const cacheKey = `clip-counts:${workspaceId}:${operationalDate}:${shiftStr}`;
26203
26328
  const cachedResult = await smartVideoCache.getClipCounts(cacheKey);
@@ -26238,7 +26363,7 @@ var BottlenecksContent = ({
26238
26363
  } finally {
26239
26364
  fetchInProgressRef.current.delete(operationKey);
26240
26365
  }
26241
- }, [workspaceId, date, s3ClipsService, shift, dashboardConfig, updateClipCounts, updateVideoIndex]);
26366
+ }, [workspaceId, date, s3ClipsService, effectiveShift, dashboardConfig, updateClipCounts, updateVideoIndex]);
26242
26367
  const loadingCategoryRef = useRef(null);
26243
26368
  const videoRetryCountRef = useRef(0);
26244
26369
  const loadingVideosRef = useRef(/* @__PURE__ */ new Set());
@@ -26280,13 +26405,13 @@ var BottlenecksContent = ({
26280
26405
  index,
26281
26406
  true,
26282
26407
  // includeCycleTime - OK for preloading
26283
- true
26284
- // includeMetadata - OK for just +3/-1 videos, not ALL videos
26408
+ false
26409
+ // includeMetadata - NO metadata during bulk preloading to prevent flooding
26285
26410
  );
26286
26411
  }
26287
26412
  if (!video) {
26288
26413
  const operationalDate = date || getOperationalDate();
26289
- const shiftStr = shift?.toString() || "0";
26414
+ const shiftStr = effectiveShift;
26290
26415
  video = await s3ClipsService.getClipByIndex(
26291
26416
  workspaceId,
26292
26417
  operationalDate,
@@ -26295,8 +26420,8 @@ var BottlenecksContent = ({
26295
26420
  index,
26296
26421
  true,
26297
26422
  // includeCycleTime - OK for preloading
26298
- true
26299
- // includeMetadata - OK for just +3/-1 videos, not ALL videos
26423
+ false
26424
+ // includeMetadata - NO metadata during bulk preloading to prevent flooding
26300
26425
  );
26301
26426
  }
26302
26427
  if (video && isMountedRef.current) {
@@ -26319,7 +26444,7 @@ var BottlenecksContent = ({
26319
26444
  Promise.all(loadPromises).catch((err) => {
26320
26445
  console.warn("[ensureVideosLoaded] Some videos failed to preload:", err);
26321
26446
  });
26322
- }, [s3ClipsService, workspaceId, clipCounts, sopCategories, date, shift]);
26447
+ }, [s3ClipsService, workspaceId, clipCounts, sopCategories, date, effectiveShift]);
26323
26448
  const loadFirstVideoForCategory = useCallback(async (category) => {
26324
26449
  if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
26325
26450
  const targetCategory = category || activeFilterRef.current;
@@ -26335,7 +26460,7 @@ var BottlenecksContent = ({
26335
26460
  }
26336
26461
  try {
26337
26462
  const operationalDate = date || getOperationalDate();
26338
- const shiftStr = shift?.toString() || "0";
26463
+ const shiftStr = effectiveShift;
26339
26464
  if (!clipCounts[targetCategory] && !videoIndex) {
26340
26465
  const cacheKey = `clip-counts:${workspaceId}:${operationalDate}:${shiftStr}`;
26341
26466
  const cachedResult = await smartVideoCache.getClipCounts(cacheKey);
@@ -26356,8 +26481,16 @@ var BottlenecksContent = ({
26356
26481
  );
26357
26482
  if (firstVideo) {
26358
26483
  console.log(`[BottlenecksContent] Successfully loaded first video via direct S3 method`);
26359
- setAllVideos([firstVideo]);
26484
+ setAllVideos((prev) => {
26485
+ const exists = prev.some((v) => v.id === firstVideo.id);
26486
+ if (!exists) {
26487
+ return [...prev, firstVideo];
26488
+ }
26489
+ return prev;
26490
+ });
26360
26491
  preloadVideoUrl(firstVideo.src);
26492
+ setIsLoading(false);
26493
+ setHasInitialLoad(true);
26361
26494
  loadingCategoryRef.current = null;
26362
26495
  setIsCategoryLoading(false);
26363
26496
  return;
@@ -26380,7 +26513,13 @@ var BottlenecksContent = ({
26380
26513
  );
26381
26514
  if (firstVideo && isMountedRef.current) {
26382
26515
  console.log(`[BottlenecksContent] Successfully loaded first video via video index`);
26383
- setAllVideos([firstVideo]);
26516
+ setAllVideos((prev) => {
26517
+ const exists = prev.some((v) => v.id === firstVideo.id);
26518
+ if (!exists) {
26519
+ return [...prev, firstVideo];
26520
+ }
26521
+ return prev;
26522
+ });
26384
26523
  preloadVideoUrl(firstVideo.src);
26385
26524
  setIsCategoryLoading(false);
26386
26525
  return;
@@ -26403,17 +26542,19 @@ var BottlenecksContent = ({
26403
26542
  loadingCategoryRef.current = null;
26404
26543
  fetchInProgressRef.current.delete(operationKey);
26405
26544
  }
26406
- }, [workspaceId, date, s3ClipsService, clipCounts, videoIndex, shift, updateClipCounts, updateVideoIndex]);
26545
+ }, [workspaceId, date, s3ClipsService, clipCounts, videoIndex, effectiveShift, updateClipCounts, updateVideoIndex]);
26407
26546
  useEffect(() => {
26408
- if (s3ClipsService) {
26547
+ if (s3ClipsService && !prefetchData) {
26409
26548
  fetchClipCounts();
26410
26549
  }
26411
- }, [workspaceId, date, shift, s3ClipsService, fetchClipCounts, updateClipCounts, updateVideoIndex]);
26550
+ }, [workspaceId, date, effectiveShift, s3ClipsService, fetchClipCounts, updateClipCounts, updateVideoIndex, prefetchData]);
26412
26551
  useEffect(() => {
26413
26552
  if (prefetchData) {
26414
26553
  console.log(`[BottlenecksContent] Received prefetch update - status: ${prefetchStatus}, videos: ${prefetchData.videoIndex.allVideos.length}, ID: ${prefetchData.videoIndex._debugId || "NO_ID"}`);
26415
26554
  updateClipCounts(prefetchData.counts);
26416
- updateVideoIndex(prefetchData.videoIndex);
26555
+ if (prefetchData.videoIndex.allVideos.length > 0) {
26556
+ updateVideoIndex(prefetchData.videoIndex);
26557
+ }
26417
26558
  if (!hasInitialLoad && prefetchData.videoIndex.allVideos.length > 0) {
26418
26559
  setIsLoading(false);
26419
26560
  setHasInitialLoad(true);
@@ -26421,12 +26562,45 @@ var BottlenecksContent = ({
26421
26562
  }
26422
26563
  }, [prefetchData, prefetchStatus, updateClipCounts, updateVideoIndex, hasInitialLoad]);
26423
26564
  useEffect(() => {
26424
- if (s3ClipsService && videoIndex && clipCounts[activeFilter] > 0) {
26425
- if (allVideos.length === 0) {
26565
+ if (s3ClipsService && clipCounts[activeFilter] > 0) {
26566
+ const hasVideosForCurrentFilter = allVideos.some((video) => {
26567
+ if (sopCategories && sopCategories.length > 0) {
26568
+ const selectedCategory = sopCategories.find((cat) => cat.id === activeFilter);
26569
+ if (selectedCategory) {
26570
+ return video.type === selectedCategory.id;
26571
+ }
26572
+ }
26573
+ if (activeFilter === "all") return true;
26574
+ if (activeFilter === "low_value") {
26575
+ return video.type === "low_value";
26576
+ }
26577
+ if (activeFilter === "idle_time") {
26578
+ return video.type === "idle_time";
26579
+ }
26580
+ if (activeFilter === "sop_deviations") {
26581
+ return video.type === "missing_quality_check";
26582
+ }
26583
+ if (activeFilter === "best_cycle_time") {
26584
+ return video.type === "best_cycle_time";
26585
+ }
26586
+ if (activeFilter === "worst_cycle_time") {
26587
+ return video.type === "worst_cycle_time";
26588
+ }
26589
+ if (activeFilter === "cycle_completion") {
26590
+ return video.type === "cycle_completion";
26591
+ }
26592
+ if (activeFilter === "long_cycle_time") {
26593
+ return video.type === "long_cycle_time";
26594
+ }
26595
+ return video.type === "bottleneck" && video.severity === activeFilter;
26596
+ });
26597
+ if (!hasVideosForCurrentFilter) {
26426
26598
  loadFirstVideoForCategory(activeFilter);
26599
+ } else {
26600
+ setIsCategoryLoading(false);
26427
26601
  }
26428
26602
  }
26429
- }, [activeFilter, s3ClipsService, videoIndex, clipCounts, loadFirstVideoForCategory, allVideos.length]);
26603
+ }, [activeFilter, s3ClipsService, videoIndex, clipCounts, loadFirstVideoForCategory, allVideos, sopCategories]);
26430
26604
  useEffect(() => {
26431
26605
  if (previousFilterRef.current !== activeFilter) {
26432
26606
  console.log(`Filter changed from ${previousFilterRef.current} to ${activeFilter} - resetting to first video`);
@@ -26560,7 +26734,7 @@ var BottlenecksContent = ({
26560
26734
  }
26561
26735
  if (!video && s3ClipsService) {
26562
26736
  const operationalDate = date || getOperationalDate();
26563
- const shiftStr = shift?.toString() || "0";
26737
+ const shiftStr = effectiveShift;
26564
26738
  video = await s3ClipsService.getClipByIndex(
26565
26739
  workspaceId,
26566
26740
  operationalDate,
@@ -26596,7 +26770,7 @@ var BottlenecksContent = ({
26596
26770
  }
26597
26771
  }
26598
26772
  }
26599
- }, [clipCounts, filteredVideos.length, s3ClipsService, workspaceId, date, shift]);
26773
+ }, [clipCounts, filteredVideos.length, s3ClipsService, workspaceId, date, effectiveShift]);
26600
26774
  const handlePrevious = useCallback(() => {
26601
26775
  if (!isMountedRef.current) return;
26602
26776
  const currentIdx = currentIndexRef.current;
@@ -26609,14 +26783,39 @@ var BottlenecksContent = ({
26609
26783
  }
26610
26784
  }
26611
26785
  }, [filteredVideos.length]);
26786
+ const currentVideo = useMemo(() => {
26787
+ if (!filteredVideos || filteredVideos.length === 0 || currentIndex >= filteredVideos.length) {
26788
+ return null;
26789
+ }
26790
+ return filteredVideos[currentIndex];
26791
+ }, [filteredVideos, currentIndex]);
26612
26792
  const handleVideoReady = useCallback((player) => {
26613
26793
  console.log("Video.js player ready");
26614
26794
  }, []);
26615
- const handleVideoPlay = useCallback((player) => {
26795
+ const handleVideoPlay = useCallback(async (player) => {
26616
26796
  setIsPlaying(true);
26617
26797
  const currentIdx = currentIndexRef.current;
26618
26798
  ensureVideosLoaded(currentIdx);
26619
- }, []);
26799
+ if (currentVideo && !currentVideo.creation_timestamp && s3ClipsService) {
26800
+ try {
26801
+ const originalUri = currentVideo.originalUri || currentVideo.src;
26802
+ if (originalUri && originalUri.includes("s3://")) {
26803
+ const metadata = await s3ClipsService.getFullMetadata(originalUri);
26804
+ if (metadata && isMountedRef.current) {
26805
+ setAllVideos((prev) => prev.map(
26806
+ (v) => v.id === currentVideo.id ? {
26807
+ ...v,
26808
+ creation_timestamp: metadata.upload_timestamp || metadata.original_task_metadata?.timestamp || metadata.creation_timestamp,
26809
+ cycle_time_seconds: metadata.original_task_metadata?.cycle_time || v.cycle_time_seconds
26810
+ } : v
26811
+ ));
26812
+ }
26813
+ }
26814
+ } catch (error2) {
26815
+ console.warn("[BottlenecksContent] Failed to load metadata for current video:", error2);
26816
+ }
26817
+ }
26818
+ }, [currentVideo, s3ClipsService]);
26620
26819
  const handleVideoPause = useCallback((player) => {
26621
26820
  setIsPlaying(false);
26622
26821
  }, []);
@@ -26643,13 +26842,6 @@ var BottlenecksContent = ({
26643
26842
  fetchInProgressRef.current.clear();
26644
26843
  setIsCategoryLoading(false);
26645
26844
  setIsNavigating(false);
26646
- if (s3ClipsService) {
26647
- try {
26648
- s3ClipsService.dispose();
26649
- } catch (error2) {
26650
- console.warn("[BottlenecksContent] Error disposing S3 service:", error2);
26651
- }
26652
- }
26653
26845
  };
26654
26846
  }, [s3ClipsService]);
26655
26847
  useEffect(() => {
@@ -26690,12 +26882,6 @@ var BottlenecksContent = ({
26690
26882
  counts.bottlenecks = counts.bottleneck || counts.bottlenecks || 0;
26691
26883
  return counts;
26692
26884
  }, [clipCounts]);
26693
- const currentVideo = useMemo(() => {
26694
- if (!filteredVideos || filteredVideos.length === 0 || currentIndex >= filteredVideos.length) {
26695
- return null;
26696
- }
26697
- return filteredVideos[currentIndex];
26698
- }, [filteredVideos, currentIndex]);
26699
26885
  const getClipTypeLabel = (video) => {
26700
26886
  if (!video) return "";
26701
26887
  switch (video.type) {
@@ -26930,8 +27116,8 @@ var BottlenecksContent = ({
26930
27116
  ] }),
26931
27117
  /* Priority 1: Show loading if initial load hasn't completed yet */
26932
27118
  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..." }) }) }) }) : (
26933
- /* Priority 2: Show loading if category is loading (prevents "no matching clips" flash) */
26934
- 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..." }) }) }) }) : (
27119
+ /* Priority 2: Show loading if category is loading BUT only if no video is available */
27120
+ 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..." }) }) }) }) : (
26935
27121
  /* Priority 3: Show loading if navigating and current video not available */
26936
27122
  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..." }) }) }) }) : (
26937
27123
  /* Priority 4: Show video if we have filtered videos and current video */
@@ -31288,7 +31474,7 @@ var HelpView = ({
31288
31474
  onClick: handleBackClick,
31289
31475
  className: "flex items-center text-gray-600 hover:text-gray-900",
31290
31476
  children: [
31291
- /* @__PURE__ */ jsx(ArrowLeftIcon, { className: "h-5 w-5" }),
31477
+ /* @__PURE__ */ jsx(ArrowLeftIcon$1, { className: "h-5 w-5" }),
31292
31478
  /* @__PURE__ */ jsx("span", { className: "ml-2", children: "Back" })
31293
31479
  ]
31294
31480
  }
@@ -31729,9 +31915,25 @@ function withWorkspaceDisplayNames(Component3, options = {}) {
31729
31915
  return function WithWorkspaceDisplayNamesWrapper(props) {
31730
31916
  const [isInitialized2, setIsInitialized] = useState(false);
31731
31917
  const [error, setError] = useState(null);
31918
+ const [lastInitKey, setLastInitKey] = useState("");
31919
+ const lineIdsKey = useMemo(() => {
31920
+ if (!props.lineIds) return "";
31921
+ if (Array.isArray(props.lineIds)) {
31922
+ return props.lineIds.sort().join(",");
31923
+ }
31924
+ const values = Object.values(props.lineIds).filter(Boolean);
31925
+ return values.sort().join(",");
31926
+ }, [props.lineIds]);
31927
+ const initKey = useMemo(() => {
31928
+ return `${lineIdsKey}-${props.selectedLineId || ""}-${props.factoryViewId || ""}-${initializeFor}`;
31929
+ }, [lineIdsKey, props.selectedLineId, props.factoryViewId]);
31732
31930
  useEffect(() => {
31733
- setIsInitialized(false);
31734
- setError(null);
31931
+ if (initKey === lastInitKey && isInitialized2) {
31932
+ return;
31933
+ }
31934
+ if (initKey !== lastInitKey) {
31935
+ setError(null);
31936
+ }
31735
31937
  const initializeDisplayNames = async () => {
31736
31938
  try {
31737
31939
  const { lineIds, selectedLineId, factoryViewId } = props;
@@ -31757,20 +31959,17 @@ function withWorkspaceDisplayNames(Component3, options = {}) {
31757
31959
  await preInitializeWorkspaceDisplayNames();
31758
31960
  }
31759
31961
  setIsInitialized(true);
31962
+ setLastInitKey(initKey);
31760
31963
  } catch (err) {
31761
31964
  console.error("Failed to initialize workspace display names:", err);
31762
31965
  setError(err);
31763
31966
  setIsInitialized(true);
31967
+ setLastInitKey(initKey);
31764
31968
  }
31765
31969
  };
31766
31970
  initializeDisplayNames();
31767
- }, [
31768
- Array.isArray(props.lineIds) ? props.lineIds.join(",") : JSON.stringify(props.lineIds),
31769
- props.selectedLineId,
31770
- props.factoryViewId,
31771
- initializeFor
31772
- ]);
31773
- if (!isInitialized2 && showLoading) {
31971
+ }, [initKey]);
31972
+ if (!isInitialized2 && showLoading && lastInitKey === "") {
31774
31973
  return /* @__PURE__ */ jsx(LoadingPage, { message: loadingMessage });
31775
31974
  }
31776
31975
  if (error && showLoading) {
@@ -32410,7 +32609,7 @@ var KPIDetailView = ({
32410
32609
  onClick: handleBackClick,
32411
32610
  className: "absolute left-0 flex items-center text-gray-600 hover:text-gray-900 cursor-pointer",
32412
32611
  children: [
32413
- /* @__PURE__ */ jsx(ArrowLeftIcon, { className: "h-5 w-5" }),
32612
+ /* @__PURE__ */ jsx(ArrowLeftIcon$1, { className: "h-5 w-5" }),
32414
32613
  /* @__PURE__ */ jsx("span", { className: "ml-2", children: "Back" })
32415
32614
  ]
32416
32615
  }
@@ -32782,7 +32981,7 @@ var KPIsOverviewView = ({
32782
32981
  onClick: handleBackClick,
32783
32982
  className: "absolute left-0 flex items-center text-gray-600 hover:text-gray-900 cursor-pointer",
32784
32983
  children: [
32785
- /* @__PURE__ */ jsx(ArrowLeftIcon, { className: "h-5 w-5" }),
32984
+ /* @__PURE__ */ jsx(ArrowLeftIcon$1, { className: "h-5 w-5" }),
32786
32985
  /* @__PURE__ */ jsx("span", { className: "ml-2", children: "Back" })
32787
32986
  ]
32788
32987
  }
@@ -32804,7 +33003,7 @@ var KPIsOverviewView = ({
32804
33003
  onClick: handleBackClick,
32805
33004
  className: "absolute left-0 flex items-center text-gray-600 hover:text-gray-900 cursor-pointer",
32806
33005
  children: [
32807
- /* @__PURE__ */ jsx(ArrowLeftIcon, { className: "h-5 w-5" }),
33006
+ /* @__PURE__ */ jsx(ArrowLeftIcon$1, { className: "h-5 w-5" }),
32808
33007
  /* @__PURE__ */ jsx("span", { className: "ml-2", children: "Back" })
32809
33008
  ]
32810
33009
  }
@@ -32829,7 +33028,7 @@ var KPIsOverviewView = ({
32829
33028
  onClick: handleBackClick,
32830
33029
  className: "absolute left-0 flex items-center text-gray-600 hover:text-gray-900 cursor-pointer",
32831
33030
  children: [
32832
- /* @__PURE__ */ jsx(ArrowLeftIcon, { className: "h-5 w-5" }),
33031
+ /* @__PURE__ */ jsx(ArrowLeftIcon$1, { className: "h-5 w-5" }),
32833
33032
  /* @__PURE__ */ jsx("span", { className: "ml-2", children: "Back" })
32834
33033
  ]
32835
33034
  }
@@ -33841,7 +34040,15 @@ var ShiftsView = ({
33841
34040
  className = ""
33842
34041
  }) => {
33843
34042
  const supabase = useSupabase();
33844
- const auth = useAuth();
34043
+ useEffect(() => {
34044
+ console.log("[ShiftsView] Component mounted/re-rendered", {
34045
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
34046
+ lineIds: lineIds.length
34047
+ });
34048
+ return () => {
34049
+ console.log("[ShiftsView] Component unmounting");
34050
+ };
34051
+ }, []);
33845
34052
  const [lineConfigs, setLineConfigs] = useState(
33846
34053
  () => lineIds.map((id3) => ({
33847
34054
  id: id3,
@@ -33939,7 +34146,7 @@ var ShiftsView = ({
33939
34146
  }
33940
34147
  };
33941
34148
  fetchShiftConfigs();
33942
- }, [supabase, lineIds, showToast]);
34149
+ }, [lineIds, showToast]);
33943
34150
  useCallback((lineId) => {
33944
34151
  setLineConfigs((prev) => {
33945
34152
  const typedPrev = prev;
@@ -34132,7 +34339,6 @@ var ShiftsView = ({
34132
34339
  }));
34133
34340
  }, []);
34134
34341
  const handleSaveShifts = useCallback(async (lineId) => {
34135
- if (!auth.user?.id && false) ;
34136
34342
  setLineConfigs((prev) => prev.map(
34137
34343
  (config) => config.id === lineId ? { ...config, isSaving: true, saveSuccess: false } : config
34138
34344
  ));
@@ -34179,7 +34385,7 @@ var ShiftsView = ({
34179
34385
  (config) => config.id === lineId ? { ...config, isSaving: false, saveSuccess: false } : config
34180
34386
  ));
34181
34387
  }
34182
- }, [auth.user?.id, lineConfigs, supabase, showToast]);
34388
+ }, [lineConfigs, supabase, showToast]);
34183
34389
  return /* @__PURE__ */ jsxs("div", { className: `min-h-screen bg-slate-50 ${className}`, children: [
34184
34390
  /* @__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: [
34185
34391
  /* @__PURE__ */ jsxs(
@@ -34287,6 +34493,7 @@ var ShiftsView = ({
34287
34493
  ] })
34288
34494
  ] });
34289
34495
  };
34496
+ var AuthenticatedShiftsView = withAuth(React19__default.memo(ShiftsView));
34290
34497
  var ShiftsView_default = ShiftsView;
34291
34498
 
34292
34499
  // src/lib/constants/actions.ts
@@ -35074,6 +35281,7 @@ var TargetsViewUI = ({
35074
35281
  isLoading,
35075
35282
  lineWorkspaces,
35076
35283
  lineNames,
35284
+ dropdownStates,
35077
35285
  savingLines,
35078
35286
  saveSuccess,
35079
35287
  selectedShift,
@@ -35106,7 +35314,7 @@ var TargetsViewUI = ({
35106
35314
  onClick: onBack,
35107
35315
  className: "flex items-center text-gray-600 hover:text-gray-900",
35108
35316
  children: [
35109
- /* @__PURE__ */ jsx(ArrowLeftIcon$1, { className: "h-5 w-5" }),
35317
+ /* @__PURE__ */ jsx(ArrowLeftIcon, { className: "h-5 w-5" }),
35110
35318
  /* @__PURE__ */ jsx("span", { className: "ml-2", children: "Back" })
35111
35319
  ]
35112
35320
  }
@@ -35159,13 +35367,13 @@ var TargetsViewUI = ({
35159
35367
  {
35160
35368
  onClick: () => onToggleLineDropdown(lineId),
35161
35369
  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",
35162
- "aria-expanded": line.isOpen,
35370
+ "aria-expanded": dropdownStates[lineId],
35163
35371
  "aria-controls": `line-${lineId}-content`,
35164
35372
  children: [
35165
35373
  /* @__PURE__ */ jsx(
35166
35374
  ChevronDown,
35167
35375
  {
35168
- className: `w-5 h-5 text-blue-500 transform transition-transform duration-200 ${line.isOpen ? "rotate-180" : ""}`
35376
+ className: `w-5 h-5 text-blue-500 transform transition-transform duration-200 ${dropdownStates[lineId] ? "rotate-180" : ""}`
35169
35377
  }
35170
35378
  ),
35171
35379
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5", children: [
@@ -35212,7 +35420,7 @@ var TargetsViewUI = ({
35212
35420
  )
35213
35421
  ] })
35214
35422
  ] }) }),
35215
- line.isOpen && /* @__PURE__ */ jsxs("div", { id: `line-${lineId}-content`, className: "border-t border-gray-200", children: [
35423
+ dropdownStates[lineId] && /* @__PURE__ */ jsxs("div", { id: `line-${lineId}-content`, className: "border-t border-gray-200", children: [
35216
35424
  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: [
35217
35425
  /* @__PURE__ */ jsx("label", { htmlFor: `sku-${lineId}`, className: "text-sm font-medium text-gray-700", children: "Select SKU:" }),
35218
35426
  /* @__PURE__ */ jsx("div", { className: "flex-1 max-w-md", children: /* @__PURE__ */ jsx(
@@ -35249,68 +35457,71 @@ var TargetsViewUI = ({
35249
35457
  /* @__PURE__ */ jsx("span", { className: "text-xs text-gray-400", children: "pieces per day" })
35250
35458
  ] })
35251
35459
  ] }) }),
35252
- /* @__PURE__ */ jsx("div", { className: "divide-y divide-gray-100", children: line.workspaces.map((workspace) => /* @__PURE__ */ jsx(
35253
- "div",
35254
- {
35255
- className: "px-6 py-4 hover:bg-gray-50 transition-all duration-200",
35256
- children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-12 gap-6 items-center", children: [
35257
- /* @__PURE__ */ jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-900", children: formatWorkspaceName(workspace.name, lineId) }) }),
35258
- /* @__PURE__ */ jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsxs(
35259
- "select",
35260
- {
35261
- value: workspace.actionType,
35262
- onChange: (e) => {
35263
- const newActionType = e.target.value;
35264
- onActionTypeChange(lineId, workspace.id, newActionType);
35265
- },
35266
- className: "w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm",
35267
- "aria-label": `Action type for ${formatWorkspaceName(workspace.name, lineId)}`,
35268
- children: [
35269
- /* @__PURE__ */ jsx("option", { value: "assembly", className: "py-2", children: "Assembly" }),
35270
- /* @__PURE__ */ jsx("option", { value: "packaging", className: "py-2", children: "Packaging" })
35271
- ]
35272
- }
35273
- ) }),
35274
- /* @__PURE__ */ jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsx(
35275
- "input",
35276
- {
35277
- type: "number",
35278
- value: workspace.targetCycleTime === 0 ? "" : workspace.targetCycleTime,
35279
- onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetCycleTime", Number(e.target.value) || ""),
35280
- 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",
35281
- min: "0",
35282
- step: "0.01",
35283
- placeholder: "Enter cycle time"
35284
- }
35285
- ) }),
35286
- /* @__PURE__ */ jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsx(
35287
- "input",
35288
- {
35289
- type: "number",
35290
- value: workspace.targetPPH === 0 ? "" : workspace.targetPPH,
35291
- onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetPPH", Number(e.target.value) || ""),
35292
- 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",
35293
- min: "0",
35294
- step: "0.1",
35295
- placeholder: "Enter PPH"
35296
- }
35297
- ) }),
35298
- /* @__PURE__ */ jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsx(
35299
- "input",
35300
- {
35301
- type: "number",
35302
- value: workspace.targetDayOutput === 0 ? "" : workspace.targetDayOutput,
35303
- onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetDayOutput", Number(e.target.value) || ""),
35304
- 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",
35305
- min: "0",
35306
- step: "1",
35307
- placeholder: "Enter day output"
35308
- }
35309
- ) })
35310
- ] })
35311
- },
35312
- workspace.id
35313
- )) })
35460
+ /* @__PURE__ */ jsx("div", { className: "divide-y divide-gray-100", children: line.workspaces.map((workspace) => {
35461
+ const formattedName = formatWorkspaceName(workspace.name, lineId);
35462
+ return /* @__PURE__ */ jsx(
35463
+ "div",
35464
+ {
35465
+ className: "px-6 py-4 hover:bg-gray-50 transition-all duration-200",
35466
+ children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-12 gap-6 items-center", children: [
35467
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-900", children: formattedName }) }),
35468
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsxs(
35469
+ "select",
35470
+ {
35471
+ value: workspace.actionType,
35472
+ onChange: (e) => {
35473
+ const newActionType = e.target.value;
35474
+ onActionTypeChange(lineId, workspace.id, newActionType);
35475
+ },
35476
+ className: "w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm",
35477
+ "aria-label": `Action type for ${formattedName}`,
35478
+ children: [
35479
+ /* @__PURE__ */ jsx("option", { value: "assembly", className: "py-2", children: "Assembly" }),
35480
+ /* @__PURE__ */ jsx("option", { value: "packaging", className: "py-2", children: "Packaging" })
35481
+ ]
35482
+ }
35483
+ ) }),
35484
+ /* @__PURE__ */ jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsx(
35485
+ "input",
35486
+ {
35487
+ type: "number",
35488
+ value: workspace.targetCycleTime === 0 ? "" : workspace.targetCycleTime,
35489
+ onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetCycleTime", Number(e.target.value) || ""),
35490
+ 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",
35491
+ min: "0",
35492
+ step: "0.01",
35493
+ placeholder: "Enter cycle time"
35494
+ }
35495
+ ) }),
35496
+ /* @__PURE__ */ jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsx(
35497
+ "input",
35498
+ {
35499
+ type: "number",
35500
+ value: workspace.targetPPH === 0 ? "" : workspace.targetPPH,
35501
+ onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetPPH", Number(e.target.value) || ""),
35502
+ 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",
35503
+ min: "0",
35504
+ step: "0.1",
35505
+ placeholder: "Enter PPH"
35506
+ }
35507
+ ) }),
35508
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsx(
35509
+ "input",
35510
+ {
35511
+ type: "number",
35512
+ value: workspace.targetDayOutput === 0 ? "" : workspace.targetDayOutput,
35513
+ onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetDayOutput", Number(e.target.value) || ""),
35514
+ 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",
35515
+ min: "0",
35516
+ step: "1",
35517
+ placeholder: "Enter day output"
35518
+ }
35519
+ ) })
35520
+ ] })
35521
+ },
35522
+ workspace.id
35523
+ );
35524
+ }) })
35314
35525
  ] })
35315
35526
  ]
35316
35527
  },
@@ -35343,7 +35554,6 @@ var TargetsView = ({
35343
35554
  return lineIds.reduce((acc, lineId) => ({
35344
35555
  ...acc,
35345
35556
  [lineId]: {
35346
- isOpen: getStoredLineState2(lineId),
35347
35557
  productId: "",
35348
35558
  shiftStartTime: "08:00",
35349
35559
  shiftEndTime: "19:00",
@@ -35356,6 +35566,12 @@ var TargetsView = ({
35356
35566
  }
35357
35567
  }), {});
35358
35568
  }, [lineIds]);
35569
+ const [dropdownStates, setDropdownStates] = useState(() => {
35570
+ return lineIds.reduce((acc, lineId) => ({
35571
+ ...acc,
35572
+ [lineId]: getStoredLineState2(lineId)
35573
+ }), {});
35574
+ });
35359
35575
  const [allShiftsData, setAllShiftsData] = useState({
35360
35576
  0: initialLineWorkspaces,
35361
35577
  // Day shift
@@ -35373,6 +35589,8 @@ var TargetsView = ({
35373
35589
  const [isBulkConfigureOpen, setIsBulkConfigureOpen] = useState(false);
35374
35590
  const [selectedWorkspaces, setSelectedWorkspaces] = useState([]);
35375
35591
  const [selectedShift, setSelectedShift] = useState(0);
35592
+ const [dbValues, setDbValues] = useState({ 0: {}, 1: {} });
35593
+ const [userEditedFields, setUserEditedFields] = useState(/* @__PURE__ */ new Set());
35376
35594
  const lineWorkspaces = allShiftsData[selectedShift] || initialLineWorkspaces;
35377
35595
  const setLineWorkspaces = useCallback((updater) => {
35378
35596
  setAllShiftsData((prev) => ({
@@ -35381,18 +35599,123 @@ var TargetsView = ({
35381
35599
  }));
35382
35600
  }, [selectedShift]);
35383
35601
  const supabase = useSupabase();
35384
- const auth = useAuth();
35385
- userId || auth?.user?.id;
35602
+ const effectiveUserId = userId || "6bf6f271-1e55-4a95-9b89-1c3820b58739";
35386
35603
  const dashboardConfig = useDashboardConfig();
35387
35604
  const { skus, isLoading: skusLoading } = useSKUs(companyId);
35388
35605
  const skuEnabled = dashboardConfig?.skuConfig?.enabled || false;
35606
+ const loadOperatingHours = useCallback(async (lineId, shiftId) => {
35607
+ try {
35608
+ if (!supabase) return null;
35609
+ const { data, error } = await supabase.from("line_operating_hours").select("start_time, end_time, breaks").eq("line_id", lineId).eq("shift_id", shiftId).maybeSingle();
35610
+ if (error) {
35611
+ if (error.code === "PGRST116") {
35612
+ console.log(`No operating hours found for line ${lineId}, shift ${shiftId}`);
35613
+ return {
35614
+ startTime: "08:00",
35615
+ // Default values
35616
+ endTime: "19:00",
35617
+ breaks: []
35618
+ };
35619
+ } else {
35620
+ console.error("Error fetching operating hours:", error);
35621
+ return null;
35622
+ }
35623
+ }
35624
+ let breaks = [];
35625
+ if (data?.breaks) {
35626
+ if (Array.isArray(data.breaks)) {
35627
+ breaks = data.breaks.map((breakItem) => {
35628
+ const startTime = breakItem.start || breakItem.startTime || "00:00";
35629
+ const endTime = breakItem.end || breakItem.endTime || "00:00";
35630
+ const calculateDuration = (start, end) => {
35631
+ const [startHour, startMinute] = start.split(":").map(Number);
35632
+ const [endHour, endMinute] = end.split(":").map(Number);
35633
+ let startMinutes = startHour * 60 + startMinute;
35634
+ let endMinutes = endHour * 60 + endMinute;
35635
+ if (endMinutes < startMinutes) {
35636
+ endMinutes += 24 * 60;
35637
+ }
35638
+ return endMinutes - startMinutes;
35639
+ };
35640
+ return {
35641
+ startTime,
35642
+ endTime,
35643
+ duration: breakItem.duration || calculateDuration(startTime, endTime)
35644
+ };
35645
+ });
35646
+ } else if (typeof data.breaks === "object" && data.breaks.breaks) {
35647
+ breaks = data.breaks.breaks.map((breakItem) => {
35648
+ const startTime = breakItem.start || breakItem.startTime || "00:00";
35649
+ const endTime = breakItem.end || breakItem.endTime || "00:00";
35650
+ const calculateDuration = (start, end) => {
35651
+ const [startHour, startMinute] = start.split(":").map(Number);
35652
+ const [endHour, endMinute] = end.split(":").map(Number);
35653
+ let startMinutes = startHour * 60 + startMinute;
35654
+ let endMinutes = endHour * 60 + endMinute;
35655
+ if (endMinutes < startMinutes) {
35656
+ endMinutes += 24 * 60;
35657
+ }
35658
+ return endMinutes - startMinutes;
35659
+ };
35660
+ return {
35661
+ startTime,
35662
+ endTime,
35663
+ duration: breakItem.duration || calculateDuration(startTime, endTime)
35664
+ };
35665
+ });
35666
+ } else if (typeof data.breaks === "string") {
35667
+ try {
35668
+ const parsedBreaks = JSON.parse(data.breaks);
35669
+ if (Array.isArray(parsedBreaks)) {
35670
+ breaks = parsedBreaks.map((breakItem) => ({
35671
+ startTime: breakItem.start || breakItem.startTime || "00:00",
35672
+ endTime: breakItem.end || breakItem.endTime || "00:00",
35673
+ duration: breakItem.duration || 0
35674
+ }));
35675
+ } else if (parsedBreaks.breaks && Array.isArray(parsedBreaks.breaks)) {
35676
+ breaks = parsedBreaks.breaks.map((breakItem) => ({
35677
+ startTime: breakItem.start || breakItem.startTime || "00:00",
35678
+ endTime: breakItem.end || breakItem.endTime || "00:00",
35679
+ duration: breakItem.duration || 0
35680
+ }));
35681
+ }
35682
+ } catch (e) {
35683
+ console.error("Error parsing breaks data:", e);
35684
+ }
35685
+ }
35686
+ }
35687
+ return {
35688
+ startTime: data?.start_time || "08:00",
35689
+ endTime: data?.end_time || "19:00",
35690
+ breaks
35691
+ };
35692
+ } catch (e) {
35693
+ console.error("Exception when loading operating hours:", e);
35694
+ return {
35695
+ startTime: "08:00",
35696
+ endTime: "19:00",
35697
+ breaks: []
35698
+ };
35699
+ }
35700
+ }, [supabase]);
35701
+ useEffect(() => {
35702
+ console.log("[TargetsView] Component mounted/re-rendered", {
35703
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
35704
+ lineIds: lineIds.length,
35705
+ effectiveUserId
35706
+ });
35707
+ return () => {
35708
+ console.log("[TargetsView] Component unmounting");
35709
+ };
35710
+ }, []);
35389
35711
  useEffect(() => {
35390
35712
  let timeoutId;
35391
35713
  let retryCount = 0;
35392
35714
  const MAX_RETRIES2 = 2;
35393
35715
  const LOADING_TIMEOUT = 15e3;
35394
35716
  const fetchInitialData = async () => {
35395
- if (!supabase || lineIds.length === 0) return;
35717
+ if (lineIds.length === 0) return;
35718
+ console.log("[TargetsView] Starting fetchInitialData");
35396
35719
  setIsLoading(true);
35397
35720
  timeoutId = setTimeout(() => {
35398
35721
  console.error("Loading timeout reached");
@@ -35456,10 +35779,32 @@ var TargetsView = ({
35456
35779
  const actionThresholds = await workspaceService.getActionThresholds(
35457
35780
  lineId,
35458
35781
  currentDate,
35459
- selectedShift
35782
+ 0
35783
+ // Always use day shift for initial load
35460
35784
  );
35785
+ const operatingHoursData = await loadOperatingHours(lineId, 0);
35786
+ if (operatingHoursData) {
35787
+ updatedLineWorkspaces[lineId].shiftStartTime = operatingHoursData.startTime;
35788
+ updatedLineWorkspaces[lineId].shiftEndTime = operatingHoursData.endTime;
35789
+ updatedLineWorkspaces[lineId].breaks = operatingHoursData.breaks;
35790
+ updatedLineWorkspaces[lineId].shiftHours = calculateShiftHours2(
35791
+ operatingHoursData.startTime,
35792
+ operatingHoursData.endTime,
35793
+ operatingHoursData.breaks
35794
+ );
35795
+ }
35461
35796
  const mappedWorkspaces = enabledWorkspaces.map((ws) => {
35462
35797
  const threshold = actionThresholds.find((t) => t.workspace_id === ws.id);
35798
+ if (!dbValues[0][lineId]) {
35799
+ dbValues[0][lineId] = {};
35800
+ }
35801
+ if (threshold) {
35802
+ dbValues[0][lineId][ws.id] = {
35803
+ targetPPH: threshold.pph_threshold,
35804
+ targetCycleTime: threshold.ideal_cycle_time,
35805
+ targetDayOutput: threshold.total_day_output
35806
+ };
35807
+ }
35463
35808
  return {
35464
35809
  id: ws.id,
35465
35810
  name: ws.workspace_id,
@@ -35500,90 +35845,7 @@ var TargetsView = ({
35500
35845
  return () => {
35501
35846
  clearTimeout(timeoutId);
35502
35847
  };
35503
- }, [supabase, lineIds, companyId]);
35504
- useCallback(async (shiftId) => {
35505
- try {
35506
- if (!supabase) return;
35507
- const currentDate = getOperationalDate();
35508
- const updatedLineWorkspaces = { ...lineWorkspaces };
35509
- let hasUpdates = false;
35510
- for (const lineId of lineIds) {
35511
- const lineState = lineWorkspaces[lineId];
35512
- if (!lineState || !lineState.factoryId) {
35513
- console.warn(`Skipping line thresholds for ${lineId} as factoryId is not yet available.`);
35514
- continue;
35515
- }
35516
- const currentFactoryId = lineState.factoryId;
35517
- try {
35518
- 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);
35519
- if (thresholdError) {
35520
- console.error(`Error fetching line threshold for line ${lineId}, factory ${currentFactoryId}:`, thresholdError);
35521
- continue;
35522
- }
35523
- let determinedProductId = updatedLineWorkspaces[lineId]?.productId || "";
35524
- if (lineThresholdsRows && lineThresholdsRows.length > 0) {
35525
- if (lineThresholdsRows.length > 1) {
35526
- console.warn(
35527
- `Multiple line_thresholds records found for line ${lineId}, factory ${currentFactoryId}, date ${currentDate}, shift ${selectedShift}. Using product_code from the first record. Rows:`,
35528
- lineThresholdsRows
35529
- );
35530
- }
35531
- determinedProductId = lineThresholdsRows[0].product_code;
35532
- } else {
35533
- console.log(
35534
- `No line_thresholds record found for line ${lineId}, factory ${currentFactoryId}, date ${currentDate}, shift ${selectedShift}. Using existing/default product ID.`
35535
- );
35536
- }
35537
- 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();
35538
- if (hoursError) {
35539
- console.error(`Error fetching operating hours for line ${lineId}:`, hoursError);
35540
- continue;
35541
- }
35542
- const startTime = operatingHours?.start_time || updatedLineWorkspaces[lineId]?.shiftStartTime || "08:00";
35543
- const endTime = operatingHours?.end_time || updatedLineWorkspaces[lineId]?.shiftEndTime || "19:00";
35544
- let breaks = [];
35545
- if (operatingHours?.breaks) {
35546
- if (Array.isArray(operatingHours.breaks)) {
35547
- breaks = operatingHours.breaks.map((breakItem) => ({
35548
- startTime: breakItem.start || breakItem.startTime || "00:00",
35549
- endTime: breakItem.end || breakItem.endTime || "00:00",
35550
- duration: breakItem.duration || calculateShiftHours2(breakItem.start || breakItem.startTime || "00:00", breakItem.end || breakItem.endTime || "00:00", []) * 60
35551
- }));
35552
- } else if (typeof operatingHours.breaks === "object" && operatingHours.breaks.breaks) {
35553
- breaks = operatingHours.breaks.breaks.map((breakItem) => ({
35554
- startTime: breakItem.start || breakItem.startTime || "00:00",
35555
- endTime: breakItem.end || breakItem.endTime || "00:00",
35556
- duration: breakItem.duration || calculateShiftHours2(breakItem.start || breakItem.startTime || "00:00", breakItem.end || breakItem.endTime || "00:00", []) * 60
35557
- }));
35558
- }
35559
- }
35560
- const shiftHours = calculateShiftHours2(startTime, endTime, breaks);
35561
- const currentLineStateFromLoop = updatedLineWorkspaces[lineId];
35562
- if (determinedProductId !== currentLineStateFromLoop?.productId || startTime !== currentLineStateFromLoop?.shiftStartTime || endTime !== currentLineStateFromLoop?.shiftEndTime || shiftHours !== currentLineStateFromLoop?.shiftHours || JSON.stringify(breaks) !== JSON.stringify(currentLineStateFromLoop?.breaks)) {
35563
- updatedLineWorkspaces[lineId] = {
35564
- ...currentLineStateFromLoop || {},
35565
- factoryId: currentFactoryId,
35566
- // Ensure factoryId is preserved
35567
- productId: determinedProductId,
35568
- shiftStartTime: startTime,
35569
- shiftEndTime: endTime,
35570
- breaks,
35571
- shiftHours: Number(shiftHours),
35572
- workspaces: currentLineStateFromLoop?.workspaces || []
35573
- };
35574
- hasUpdates = true;
35575
- }
35576
- } catch (lineError) {
35577
- console.error(`Error processing line ${lineId}:`, lineError);
35578
- }
35579
- }
35580
- if (hasUpdates) {
35581
- setLineWorkspaces(updatedLineWorkspaces);
35582
- }
35583
- } catch (error) {
35584
- console.error("Error in fetchLineThresholds outer try-catch:", error);
35585
- }
35586
- }, [selectedShift, supabase, lineIds, lineWorkspaces, allShiftsData]);
35848
+ }, [lineIds, companyId, loadOperatingHours]);
35587
35849
  const fetchAllShiftsData = useCallback(async (currentWorkspaces) => {
35588
35850
  if (!supabase) return;
35589
35851
  const currentDate = getOperationalDate();
@@ -35593,32 +35855,25 @@ var TargetsView = ({
35593
35855
  1: JSON.parse(JSON.stringify(currentWorkspaces))
35594
35856
  // Deep clone for night shift
35595
35857
  };
35858
+ const newDbValues = { 0: {}, 1: {} };
35596
35859
  for (const shiftId of [0, 1]) {
35597
35860
  for (const lineId of lineIds) {
35598
35861
  try {
35599
- 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();
35600
- if (hoursError) {
35601
- console.error(`Error fetching operating hours for line ${lineId}, shift ${shiftId}:`, hoursError);
35862
+ const operatingHoursData = await loadOperatingHours(lineId, shiftId);
35863
+ if (!operatingHoursData) {
35864
+ console.warn(`No operating hours for line ${lineId}, shift ${shiftId} - using defaults`);
35602
35865
  continue;
35603
35866
  }
35604
- let breaks = [];
35605
- if (operatingHours?.breaks) {
35606
- if (Array.isArray(operatingHours.breaks)) {
35607
- breaks = operatingHours.breaks.map((breakItem) => ({
35608
- startTime: breakItem.start || breakItem.startTime || "00:00",
35609
- endTime: breakItem.end || breakItem.endTime || "00:00",
35610
- duration: breakItem.duration || calculateShiftHours2(breakItem.start || breakItem.startTime || "00:00", breakItem.end || breakItem.endTime || "00:00", []) * 60
35611
- }));
35612
- }
35613
- }
35614
- const startTime = operatingHours?.start_time || "08:00";
35615
- const endTime = operatingHours?.end_time || "19:00";
35867
+ const { startTime, endTime, breaks } = operatingHoursData;
35616
35868
  const shiftHours = calculateShiftHours2(startTime, endTime, breaks);
35617
35869
  const actionThresholds = await workspaceService.getActionThresholds(
35618
35870
  lineId,
35619
35871
  currentDate,
35620
35872
  shiftId
35621
35873
  );
35874
+ if (!newDbValues[shiftId][lineId]) {
35875
+ newDbValues[shiftId][lineId] = {};
35876
+ }
35622
35877
  const existingLine = newAllShiftsData[shiftId][lineId];
35623
35878
  if (existingLine) {
35624
35879
  newAllShiftsData[shiftId][lineId] = {
@@ -35630,6 +35885,11 @@ var TargetsView = ({
35630
35885
  workspaces: existingLine.workspaces.map((ws) => {
35631
35886
  const threshold = actionThresholds.find((t) => t.workspace_id === ws.id);
35632
35887
  if (threshold) {
35888
+ newDbValues[shiftId][lineId][ws.id] = {
35889
+ targetPPH: threshold.pph_threshold,
35890
+ targetCycleTime: threshold.ideal_cycle_time,
35891
+ targetDayOutput: threshold.total_day_output
35892
+ };
35633
35893
  return {
35634
35894
  ...ws,
35635
35895
  targetPPH: threshold.pph_threshold,
@@ -35647,114 +35907,17 @@ var TargetsView = ({
35647
35907
  }
35648
35908
  }
35649
35909
  setAllShiftsData(newAllShiftsData);
35650
- }, [supabase, lineIds]);
35651
- const loadOperatingHours = useCallback(async (lineId, shiftId) => {
35652
- try {
35653
- if (!supabase) return null;
35654
- const { data, error } = await supabase.from("line_operating_hours").select("start_time, end_time, breaks").eq("line_id", lineId).eq("shift_id", shiftId).maybeSingle();
35655
- if (error) {
35656
- if (error.code === "PGRST116") {
35657
- console.log(`No operating hours found for line ${lineId}, shift ${shiftId}`);
35658
- return {
35659
- startTime: "08:00",
35660
- // Default values
35661
- endTime: "19:00",
35662
- breaks: []
35663
- };
35664
- } else {
35665
- console.error("Error fetching operating hours:", error);
35666
- return null;
35667
- }
35668
- }
35669
- let breaks = [];
35670
- if (data?.breaks) {
35671
- if (Array.isArray(data.breaks)) {
35672
- breaks = data.breaks.map((breakItem) => {
35673
- const startTime = breakItem.start || breakItem.startTime || "00:00";
35674
- const endTime = breakItem.end || breakItem.endTime || "00:00";
35675
- const calculateDuration = (start, end) => {
35676
- const [startHour, startMinute] = start.split(":").map(Number);
35677
- const [endHour, endMinute] = end.split(":").map(Number);
35678
- let startMinutes = startHour * 60 + startMinute;
35679
- let endMinutes = endHour * 60 + endMinute;
35680
- if (endMinutes < startMinutes) {
35681
- endMinutes += 24 * 60;
35682
- }
35683
- return endMinutes - startMinutes;
35684
- };
35685
- return {
35686
- startTime,
35687
- endTime,
35688
- duration: breakItem.duration || calculateDuration(startTime, endTime)
35689
- };
35690
- });
35691
- } else if (typeof data.breaks === "object" && data.breaks.breaks) {
35692
- breaks = data.breaks.breaks.map((breakItem) => {
35693
- const startTime = breakItem.start || breakItem.startTime || "00:00";
35694
- const endTime = breakItem.end || breakItem.endTime || "00:00";
35695
- const calculateDuration = (start, end) => {
35696
- const [startHour, startMinute] = start.split(":").map(Number);
35697
- const [endHour, endMinute] = end.split(":").map(Number);
35698
- let startMinutes = startHour * 60 + startMinute;
35699
- let endMinutes = endHour * 60 + endMinute;
35700
- if (endMinutes < startMinutes) {
35701
- endMinutes += 24 * 60;
35702
- }
35703
- return endMinutes - startMinutes;
35704
- };
35705
- return {
35706
- startTime,
35707
- endTime,
35708
- duration: breakItem.duration || calculateDuration(startTime, endTime)
35709
- };
35710
- });
35711
- } else if (typeof data.breaks === "string") {
35712
- try {
35713
- const parsedBreaks = JSON.parse(data.breaks);
35714
- if (Array.isArray(parsedBreaks)) {
35715
- breaks = parsedBreaks.map((breakItem) => ({
35716
- startTime: breakItem.start || breakItem.startTime || "00:00",
35717
- endTime: breakItem.end || breakItem.endTime || "00:00",
35718
- duration: breakItem.duration || 0
35719
- }));
35720
- } else if (parsedBreaks.breaks && Array.isArray(parsedBreaks.breaks)) {
35721
- breaks = parsedBreaks.breaks.map((breakItem) => ({
35722
- startTime: breakItem.start || breakItem.startTime || "00:00",
35723
- endTime: breakItem.end || breakItem.endTime || "00:00",
35724
- duration: breakItem.duration || 0
35725
- }));
35726
- }
35727
- } catch (e) {
35728
- console.error("Error parsing breaks data:", e);
35729
- }
35730
- }
35731
- }
35732
- return {
35733
- startTime: data?.start_time || "08:00",
35734
- endTime: data?.end_time || "19:00",
35735
- breaks
35736
- };
35737
- } catch (e) {
35738
- console.error("Exception when loading operating hours:", e);
35739
- return {
35740
- startTime: "08:00",
35741
- endTime: "19:00",
35742
- breaks: []
35743
- };
35744
- }
35745
- }, [supabase]);
35910
+ setDbValues(newDbValues);
35911
+ }, [supabase, lineIds, loadOperatingHours]);
35746
35912
  const toggleLineDropdown = useCallback((lineId) => {
35747
- setLineWorkspaces((prev) => {
35748
- const newIsOpen = !prev[lineId].isOpen;
35913
+ setDropdownStates((prev) => {
35914
+ const newIsOpen = !prev[lineId];
35749
35915
  if (typeof window !== "undefined") {
35750
35916
  localStorage.setItem(`line_${lineId}_open`, JSON.stringify(newIsOpen));
35751
35917
  }
35752
35918
  return {
35753
35919
  ...prev,
35754
- [lineId]: {
35755
- ...prev[lineId],
35756
- isOpen: newIsOpen
35757
- }
35920
+ [lineId]: newIsOpen
35758
35921
  };
35759
35922
  });
35760
35923
  }, []);
@@ -35803,6 +35966,8 @@ var TargetsView = ({
35803
35966
  }
35804
35967
  };
35805
35968
  const updateWorkspaceTarget = (lineId, workspaceId, field, value) => {
35969
+ const fieldKey = `${lineId}-${workspaceId}-${field}`;
35970
+ setUserEditedFields((prev) => new Set(prev).add(fieldKey));
35806
35971
  setLineWorkspaces((prev) => {
35807
35972
  const shiftHours = prev[lineId].shiftHours;
35808
35973
  return {
@@ -35827,11 +35992,7 @@ var TargetsView = ({
35827
35992
  } else if (field === "targetDayOutput") {
35828
35993
  updates.targetDayOutput = value;
35829
35994
  if (value !== "") {
35830
- const breaks = prev[lineId].breaks;
35831
- const totalBreakMinutes = breaks.reduce((total, b) => total + b.duration, 0);
35832
- const totalBreakHours = totalBreakMinutes / 60;
35833
- const realWorkHours = shiftHours - totalBreakHours;
35834
- const calculatedPPH = Math.round(value / realWorkHours);
35995
+ const calculatedPPH = Math.round(value / shiftHours);
35835
35996
  updates.targetPPH = calculatedPPH;
35836
35997
  } else {
35837
35998
  updates.targetPPH = "";
@@ -35846,62 +36007,35 @@ var TargetsView = ({
35846
36007
  };
35847
36008
  const handleShiftChange = (shiftId) => {
35848
36009
  setSelectedShift(shiftId);
35849
- const loadShiftHours = async () => {
35850
- const updatedLineWorkspaces = { ...allShiftsData[shiftId] };
35851
- let hasUpdates = false;
35852
- for (const lineId of lineIds) {
35853
- try {
35854
- const operatingHours = await loadOperatingHours(lineId, shiftId);
35855
- if (!operatingHours) continue;
35856
- const shiftHours = calculateShiftHours2(
35857
- operatingHours.startTime,
35858
- operatingHours.endTime,
35859
- operatingHours.breaks
35860
- );
35861
- updatedLineWorkspaces[lineId] = {
35862
- ...updatedLineWorkspaces[lineId],
35863
- shiftStartTime: operatingHours.startTime,
35864
- shiftEndTime: operatingHours.endTime,
35865
- breaks: operatingHours.breaks,
35866
- shiftHours: Number(shiftHours),
35867
- workspaces: updatedLineWorkspaces[lineId].workspaces.map((ws) => {
35868
- let updatedPPH = ws.targetPPH;
35869
- if (ws.targetCycleTime !== "") {
35870
- const idealPPH = calculatePPH(
35871
- ws.targetCycleTime,
35872
- operatingHours.breaks,
35873
- Number(shiftHours)
35874
- );
35875
- const shouldUpdatePPH = typeof ws.targetPPH === "string" ? ws.targetPPH === "" : ws.targetPPH === 0 || !ws.targetPPH;
35876
- if (shouldUpdatePPH) {
35877
- updatedPPH = idealPPH;
36010
+ setUserEditedFields(/* @__PURE__ */ new Set());
36011
+ if (dbValues[shiftId] && Object.keys(dbValues[shiftId]).length > 0) {
36012
+ setAllShiftsData((prev) => {
36013
+ const updatedShiftData = { ...prev[shiftId] };
36014
+ for (const lineId of Object.keys(updatedShiftData)) {
36015
+ if (dbValues[shiftId][lineId]) {
36016
+ updatedShiftData[lineId] = {
36017
+ ...updatedShiftData[lineId],
36018
+ workspaces: updatedShiftData[lineId].workspaces.map((ws) => {
36019
+ const dbValue = dbValues[shiftId][lineId][ws.id];
36020
+ if (dbValue) {
36021
+ return {
36022
+ ...ws,
36023
+ targetPPH: dbValue.targetPPH,
36024
+ targetCycleTime: dbValue.targetCycleTime,
36025
+ targetDayOutput: dbValue.targetDayOutput
36026
+ };
35878
36027
  }
35879
- }
35880
- const updatedDayOutput = calculateDayOutput(
35881
- updatedPPH,
35882
- Number(shiftHours),
35883
- operatingHours.breaks
35884
- );
35885
- return {
35886
- ...ws,
35887
- targetPPH: updatedPPH,
35888
- targetDayOutput: updatedDayOutput
35889
- };
35890
- })
35891
- };
35892
- hasUpdates = true;
35893
- } catch (e) {
35894
- console.error(`Exception when loading shift hours for line ${lineId}:`, e);
36028
+ return ws;
36029
+ })
36030
+ };
36031
+ }
35895
36032
  }
35896
- }
35897
- if (hasUpdates) {
35898
- setAllShiftsData((prev) => ({
36033
+ return {
35899
36034
  ...prev,
35900
- [shiftId]: updatedLineWorkspaces
35901
- }));
35902
- }
35903
- };
35904
- loadShiftHours();
36035
+ [shiftId]: updatedShiftData
36036
+ };
36037
+ });
36038
+ }
35905
36039
  };
35906
36040
  const handleActionTypeChange = useCallback((lineId, workspaceId, newActionType) => {
35907
36041
  if (!actionIds) return;
@@ -35991,6 +36125,31 @@ var TargetsView = ({
35991
36125
  throw lineUpsertError;
35992
36126
  }
35993
36127
  console.log(`[handleSaveLine] Successfully upserted line_thresholds for ${lineId}`);
36128
+ setDbValues((prev) => ({
36129
+ ...prev,
36130
+ [selectedShift]: {
36131
+ ...prev[selectedShift],
36132
+ [lineId]: lineDataToSave.workspaces.reduce((acc, ws) => ({
36133
+ ...acc,
36134
+ [ws.id]: {
36135
+ targetPPH: ws.targetPPH,
36136
+ targetCycleTime: ws.targetCycleTime,
36137
+ targetDayOutput: ws.targetDayOutput
36138
+ }
36139
+ }), {})
36140
+ }
36141
+ }));
36142
+ console.log(`[handleSaveLine] Updated dbValues for line ${lineId}, shift ${selectedShift}`);
36143
+ setUserEditedFields((prev) => {
36144
+ const newSet = new Set(prev);
36145
+ lineDataToSave.workspaces.forEach((ws) => {
36146
+ newSet.delete(`${lineId}-${ws.id}-targetPPH`);
36147
+ newSet.delete(`${lineId}-${ws.id}-targetCycleTime`);
36148
+ newSet.delete(`${lineId}-${ws.id}-targetDayOutput`);
36149
+ });
36150
+ return newSet;
36151
+ });
36152
+ console.log(`[handleSaveLine] Cleared user edited fields for line ${lineId}`);
35994
36153
  setSaveSuccess((prev) => ({ ...prev, [lineId]: true }));
35995
36154
  toast.success(`${lineNames[lineId] || lineId} targets saved successfully`);
35996
36155
  if (onSaveChanges) onSaveChanges(lineId);
@@ -36004,7 +36163,7 @@ var TargetsView = ({
36004
36163
  setSavingLines((prev) => ({ ...prev, [lineId]: false }));
36005
36164
  console.log(`[handleSaveLine] Set savingLines to false for ${lineId} in finally block`);
36006
36165
  }
36007
- }, [supabase, lineWorkspaces, selectedShift, lineNames, onSaveChanges]);
36166
+ }, [supabase, lineWorkspaces, selectedShift, lineNames, onSaveChanges, skuEnabled, dashboardConfig]);
36008
36167
  const handleBulkConfigure = async (updates) => {
36009
36168
  if (!actionIds) return;
36010
36169
  if (updates.productId !== void 0) {
@@ -36052,6 +36211,7 @@ var TargetsView = ({
36052
36211
  isLoading: isLoading || skusLoading,
36053
36212
  lineWorkspaces,
36054
36213
  lineNames,
36214
+ dropdownStates,
36055
36215
  savingLines,
36056
36216
  saveSuccess,
36057
36217
  selectedShift,
@@ -36076,7 +36236,7 @@ var TargetsView = ({
36076
36236
  };
36077
36237
  var TargetsViewWithDisplayNames = withAllWorkspaceDisplayNames(TargetsView);
36078
36238
  var TargetsView_default = TargetsViewWithDisplayNames;
36079
- var AuthenticatedTargetsView = withAuth(TargetsViewWithDisplayNames);
36239
+ var AuthenticatedTargetsView = withAuth(React19__default.memo(TargetsViewWithDisplayNames));
36080
36240
 
36081
36241
  // src/views/workspace-detail-view.utils.ts
36082
36242
  var formatISTDate2 = (date = /* @__PURE__ */ new Date(), options) => {
@@ -37547,4 +37707,4 @@ var streamProxyConfig = {
37547
37707
  }
37548
37708
  };
37549
37709
 
37550
- 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, 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 };
37710
+ 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 };