@optifye/dashboard-core 6.3.5 → 6.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3809,6 +3809,11 @@ var S3ClipsService = class {
3809
3809
  this.requestCache = new RequestDeduplicationCache();
3810
3810
  // Flag to prevent metadata fetching during index building
3811
3811
  this.isIndexBuilding = false;
3812
+ // Flag to prevent metadata fetching during entire prefetch operation
3813
+ this.isPrefetching = false;
3814
+ // Global safeguard: limit concurrent metadata fetches to prevent flooding
3815
+ this.currentMetadataFetches = 0;
3816
+ this.MAX_CONCURRENT_METADATA = 3;
3812
3817
  this.config = config;
3813
3818
  if (!config.s3Config) {
3814
3819
  throw new Error("S3 configuration is required");
@@ -3956,20 +3961,36 @@ var S3ClipsService = class {
3956
3961
  return null;
3957
3962
  }
3958
3963
  }
3964
+ /**
3965
+ * Control prefetch mode to prevent metadata fetching during background operations
3966
+ */
3967
+ setPrefetchMode(enabled) {
3968
+ this.isPrefetching = enabled;
3969
+ console.log(`[S3ClipsService] Prefetch mode ${enabled ? "enabled" : "disabled"} - metadata fetching ${enabled ? "blocked" : "allowed"}`);
3970
+ }
3959
3971
  /**
3960
3972
  * Fetches full metadata including timestamps with deduplication
3961
3973
  */
3962
3974
  async getFullMetadata(playlistUri) {
3963
- if (this.isIndexBuilding) {
3964
- console.warn(`[S3ClipsService] Skipping metadata fetch during index building for: ${playlistUri}`);
3975
+ if (this.isIndexBuilding || this.isPrefetching) {
3976
+ console.warn(`[S3ClipsService] Skipping metadata fetch - building: ${this.isIndexBuilding}, prefetching: ${this.isPrefetching}`);
3965
3977
  return null;
3966
3978
  }
3967
- const deduplicationKey = `full-metadata:${playlistUri}`;
3968
- return this.requestCache.deduplicate(
3969
- deduplicationKey,
3970
- () => this.executeGetFullMetadata(playlistUri),
3971
- "FullMetadata"
3972
- );
3979
+ if (this.currentMetadataFetches >= this.MAX_CONCURRENT_METADATA) {
3980
+ console.warn(`[S3ClipsService] Skipping metadata - max concurrent fetches (${this.MAX_CONCURRENT_METADATA}) reached`);
3981
+ return null;
3982
+ }
3983
+ this.currentMetadataFetches++;
3984
+ try {
3985
+ const deduplicationKey = `full-metadata:${playlistUri}`;
3986
+ return await this.requestCache.deduplicate(
3987
+ deduplicationKey,
3988
+ () => this.executeGetFullMetadata(playlistUri),
3989
+ "FullMetadata"
3990
+ );
3991
+ } finally {
3992
+ this.currentMetadataFetches--;
3993
+ }
3973
3994
  }
3974
3995
  /**
3975
3996
  * Internal implementation of full metadata fetching
@@ -4471,7 +4492,8 @@ var S3ClipsService = class {
4471
4492
  date,
4472
4493
  shiftId.toString(),
4473
4494
  includeCycleTime || false,
4474
- includeMetadata || (!!timestampStart || !!timestampEnd)
4495
+ includeMetadata || false
4496
+ // Never fetch metadata for timestamp filtering to prevent flooding
4475
4497
  );
4476
4498
  });
4477
4499
  const videoResults = await Promise.all(videoPromises);
@@ -4542,6 +4564,7 @@ var VideoPrefetchManager = class extends events.EventEmitter {
4542
4564
  }
4543
4565
  /**
4544
4566
  * Get or create S3 service instance for dashboard config
4567
+ * Public method to allow sharing the same S3Service instance across components
4545
4568
  */
4546
4569
  getS3Service(dashboardConfig) {
4547
4570
  const configKey = JSON.stringify(dashboardConfig.s3Config);
@@ -4611,75 +4634,80 @@ var VideoPrefetchManager = class extends events.EventEmitter {
4611
4634
  * Perform the actual prefetch work
4612
4635
  */
4613
4636
  async performPrefetchWork(key, params, s3Service, buildIndex) {
4614
- const cacheKey = `clip-counts:${params.workspaceId}:${params.date}:${params.shift}`;
4615
- const cachedResult = await smartVideoCache.getClipCounts(cacheKey);
4616
- if (cachedResult) {
4617
- console.log(`[VideoPrefetchManager] Found cached data for ${key}`);
4618
- this.updateRequestStatus(key, "fully_indexed" /* FULLY_INDEXED */, cachedResult);
4619
- return cachedResult;
4620
- }
4621
- if (buildIndex) {
4622
- const countsOnly = await s3Service.getClipCounts(
4637
+ s3Service.setPrefetchMode(true);
4638
+ try {
4639
+ const cacheKey = `clip-counts:${params.workspaceId}:${params.date}:${params.shift}`;
4640
+ const cachedResult = await smartVideoCache.getClipCounts(cacheKey);
4641
+ if (cachedResult) {
4642
+ console.log(`[VideoPrefetchManager] Found cached data for ${key}`);
4643
+ this.updateRequestStatus(key, "fully_indexed" /* FULLY_INDEXED */, cachedResult);
4644
+ return cachedResult;
4645
+ }
4646
+ if (buildIndex) {
4647
+ const countsOnly = await s3Service.getClipCounts(
4648
+ params.workspaceId,
4649
+ params.date,
4650
+ params.shift
4651
+ );
4652
+ if (typeof countsOnly === "object" && countsOnly !== null) {
4653
+ const renderReadyData = {
4654
+ counts: countsOnly,
4655
+ videoIndex: {
4656
+ byCategory: /* @__PURE__ */ new Map(),
4657
+ allVideos: [],
4658
+ counts: countsOnly,
4659
+ workspaceId: params.workspaceId,
4660
+ date: params.date,
4661
+ shiftId: params.shift,
4662
+ lastUpdated: /* @__PURE__ */ new Date(),
4663
+ _debugId: `vid_RENDER_READY_${Date.now()}_${Math.random().toString(36).substring(7)}`
4664
+ }
4665
+ };
4666
+ this.updateRequestStatus(key, "render_ready" /* RENDER_READY */, renderReadyData);
4667
+ console.log(`[VideoPrefetchManager] Render ready, building full index for ${key}`);
4668
+ }
4669
+ }
4670
+ const fullResult = await s3Service.getClipCountsCacheFirst(
4623
4671
  params.workspaceId,
4624
4672
  params.date,
4625
- params.shift
4673
+ params.shift,
4674
+ true
4626
4675
  );
4627
- if (typeof countsOnly === "object" && countsOnly !== null) {
4628
- const renderReadyData = {
4629
- counts: countsOnly,
4676
+ if (fullResult && typeof fullResult === "object" && "videoIndex" in fullResult) {
4677
+ const clipCountsWithIndex = fullResult;
4678
+ if (clipCountsWithIndex.videoIndex.allVideos.length > 0) {
4679
+ console.log(`[VideoPrefetchManager] Video index properly populated with ${clipCountsWithIndex.videoIndex.allVideos.length} videos`);
4680
+ } else {
4681
+ console.warn(`[VideoPrefetchManager] Video index is empty, but counts available:`, clipCountsWithIndex.counts);
4682
+ }
4683
+ await smartVideoCache.setClipCounts(cacheKey, clipCountsWithIndex, 5 * 60);
4684
+ this.updateRequestStatus(key, "fully_indexed" /* FULLY_INDEXED */, clipCountsWithIndex);
4685
+ console.log(`[VideoPrefetchManager] Fully indexed: ${key} (${clipCountsWithIndex.videoIndex.allVideos.length} videos)`);
4686
+ return clipCountsWithIndex;
4687
+ } else if (fullResult && typeof fullResult === "object") {
4688
+ console.log(`[VideoPrefetchManager] Received counts only, building fallback data structure`);
4689
+ const countsResult = fullResult;
4690
+ const fallbackData = {
4691
+ counts: countsResult,
4630
4692
  videoIndex: {
4631
4693
  byCategory: /* @__PURE__ */ new Map(),
4632
4694
  allVideos: [],
4633
- counts: countsOnly,
4695
+ counts: countsResult,
4634
4696
  workspaceId: params.workspaceId,
4635
4697
  date: params.date,
4636
4698
  shiftId: params.shift,
4637
4699
  lastUpdated: /* @__PURE__ */ new Date(),
4638
- _debugId: `vid_RENDER_READY_${Date.now()}_${Math.random().toString(36).substring(7)}`
4700
+ _debugId: `vid_FALLBACK_${Date.now()}_${Math.random().toString(36).substring(7)}`
4639
4701
  }
4640
4702
  };
4641
- this.updateRequestStatus(key, "render_ready" /* RENDER_READY */, renderReadyData);
4642
- console.log(`[VideoPrefetchManager] Render ready, building full index for ${key}`);
4643
- }
4644
- }
4645
- const fullResult = await s3Service.getClipCountsCacheFirst(
4646
- params.workspaceId,
4647
- params.date,
4648
- params.shift,
4649
- true
4650
- );
4651
- if (fullResult && typeof fullResult === "object" && "videoIndex" in fullResult) {
4652
- const clipCountsWithIndex = fullResult;
4653
- if (clipCountsWithIndex.videoIndex.allVideos.length > 0) {
4654
- console.log(`[VideoPrefetchManager] Video index properly populated with ${clipCountsWithIndex.videoIndex.allVideos.length} videos`);
4703
+ this.updateRequestStatus(key, "render_ready" /* RENDER_READY */, fallbackData);
4704
+ return fallbackData;
4655
4705
  } else {
4656
- console.warn(`[VideoPrefetchManager] Video index is empty, but counts available:`, clipCountsWithIndex.counts);
4657
- }
4658
- await smartVideoCache.setClipCounts(cacheKey, clipCountsWithIndex, 5 * 60);
4659
- this.updateRequestStatus(key, "fully_indexed" /* FULLY_INDEXED */, clipCountsWithIndex);
4660
- console.log(`[VideoPrefetchManager] Fully indexed: ${key} (${clipCountsWithIndex.videoIndex.allVideos.length} videos)`);
4661
- return clipCountsWithIndex;
4662
- } else if (fullResult && typeof fullResult === "object") {
4663
- console.log(`[VideoPrefetchManager] Received counts only, building fallback data structure`);
4664
- const countsResult = fullResult;
4665
- const fallbackData = {
4666
- counts: countsResult,
4667
- videoIndex: {
4668
- byCategory: /* @__PURE__ */ new Map(),
4669
- allVideos: [],
4670
- counts: countsResult,
4671
- workspaceId: params.workspaceId,
4672
- date: params.date,
4673
- shiftId: params.shift,
4674
- lastUpdated: /* @__PURE__ */ new Date(),
4675
- _debugId: `vid_FALLBACK_${Date.now()}_${Math.random().toString(36).substring(7)}`
4676
- }
4677
- };
4678
- this.updateRequestStatus(key, "render_ready" /* RENDER_READY */, fallbackData);
4679
- return fallbackData;
4680
- } else {
4681
- console.error(`[VideoPrefetchManager] Received null/undefined result from S3 service`);
4682
- throw new Error("Failed to fetch clip counts from S3 service");
4706
+ console.error(`[VideoPrefetchManager] Received null/undefined result from S3 service`);
4707
+ throw new Error("Failed to fetch clip counts from S3 service");
4708
+ }
4709
+ } finally {
4710
+ s3Service.setPrefetchMode(false);
4683
4711
  }
4684
4712
  }
4685
4713
  /**
@@ -4942,9 +4970,13 @@ var AuthProvider = ({ children }) => {
4942
4970
  const [loading, setLoading] = React19.useState(true);
4943
4971
  const [error, setError] = React19.useState(null);
4944
4972
  const router$1 = router.useRouter();
4945
- const userProfileTable = authConfig?.userProfileTable;
4946
- const roleColumn = authConfig?.roleColumn || "role";
4973
+ authConfig?.userProfileTable;
4974
+ authConfig?.roleColumn || "role";
4947
4975
  const fetchUserDetails = React19.useCallback(async (supabaseUser) => {
4976
+ console.log("[fetchUserDetails] Called for user:", supabaseUser.id, {
4977
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4978
+ stackTrace: new Error().stack?.split("\n").slice(1, 4).join(" -> ")
4979
+ });
4948
4980
  if (!supabaseUser) return null;
4949
4981
  const basicUser = {
4950
4982
  id: supabaseUser.id,
@@ -4953,42 +4985,34 @@ var AuthProvider = ({ children }) => {
4953
4985
  if (!supabase) return basicUser;
4954
4986
  try {
4955
4987
  const timeoutPromise = new Promise(
4956
- (_, reject) => setTimeout(() => reject(new Error("Profile fetch timeout")), 5e3)
4988
+ (_, reject) => setTimeout(() => {
4989
+ console.log("[fetchUserDetails] Timeout triggered after 2 seconds");
4990
+ reject(new Error("Profile fetch timeout"));
4991
+ }, 2e3)
4957
4992
  );
4958
4993
  const rolePromise = supabase.from("user_roles").select("role_level").eq("user_id", supabaseUser.id).single();
4959
- let profilePromise = null;
4960
- if (userProfileTable) {
4961
- profilePromise = supabase.from(userProfileTable).select(roleColumn).eq("id", supabaseUser.id).single();
4962
- }
4963
- const [roleResult, profileResult] = await Promise.race([
4964
- Promise.all([
4965
- rolePromise,
4966
- profilePromise || Promise.resolve(null)
4967
- ]),
4968
- timeoutPromise.then(() => {
4969
- throw new Error("Timeout");
4970
- })
4994
+ const roleResult = await Promise.race([
4995
+ rolePromise,
4996
+ timeoutPromise
4997
+ // Fixed: removed .then() which was causing the bug
4971
4998
  ]);
4972
4999
  let roleLevel = void 0;
4973
5000
  if (roleResult && !roleResult.error && roleResult.data) {
4974
5001
  roleLevel = roleResult.data.role_level;
4975
- } else if (roleResult?.error) {
5002
+ } else if (roleResult?.error && roleResult.error.code !== "PGRST116") {
4976
5003
  console.log("Error fetching role_level:", roleResult.error.message);
4977
5004
  }
4978
- let roleValue = void 0;
4979
- if (profileResult && !profileResult.error && profileResult.data) {
4980
- roleValue = profileResult.data[roleColumn];
4981
- }
4982
5005
  return {
4983
5006
  ...basicUser,
4984
- role: roleValue,
4985
5007
  role_level: roleLevel
4986
5008
  };
4987
5009
  } catch (err) {
4988
- console.error("Error fetching user details:", err);
5010
+ if (err instanceof Error && err.message.includes("timeout")) {
5011
+ console.warn("Auth fetch timeout - using basic user info");
5012
+ }
4989
5013
  return basicUser;
4990
5014
  }
4991
- }, [supabase, userProfileTable, roleColumn]);
5015
+ }, [supabase]);
4992
5016
  React19.useEffect(() => {
4993
5017
  if (!supabase) return;
4994
5018
  let mounted = true;
@@ -4999,6 +5023,7 @@ var AuthProvider = ({ children }) => {
4999
5023
  }
5000
5024
  }, 1e4);
5001
5025
  const initializeAuth = async () => {
5026
+ const startTime = performance.now();
5002
5027
  try {
5003
5028
  const { data: { session: initialSession }, error: sessionError } = await supabase.auth.getSession();
5004
5029
  if (!mounted) return;
@@ -5041,12 +5066,38 @@ var AuthProvider = ({ children }) => {
5041
5066
  if (mounted) {
5042
5067
  setLoading(false);
5043
5068
  clearTimeout(safetyTimeout);
5069
+ const duration = performance.now() - startTime;
5070
+ if (duration > 3e3) {
5071
+ console.warn(`[Auth] Initialization took ${duration.toFixed(0)}ms - consider optimization`);
5072
+ } else if (process.env.NODE_ENV === "development") {
5073
+ console.log(`[Auth] Initialized in ${duration.toFixed(0)}ms`);
5074
+ }
5044
5075
  }
5045
5076
  }
5046
5077
  };
5047
5078
  initializeAuth();
5048
- const { data: { subscription } } = supabase.auth.onAuthStateChange(async (_event, currentSession) => {
5079
+ const { data: { subscription } } = supabase.auth.onAuthStateChange(async (event, currentSession) => {
5049
5080
  if (!mounted) return;
5081
+ console.log("[AuthContext] Auth event fired:", {
5082
+ event,
5083
+ sessionId: currentSession?.user?.id,
5084
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
5085
+ currentPath: typeof window !== "undefined" ? window.location.pathname : "unknown"
5086
+ });
5087
+ if (event === "TOKEN_REFRESHED") {
5088
+ if (session?.user?.id === currentSession?.user?.id && session?.user?.email === currentSession?.user?.email) {
5089
+ console.log("[AuthContext] Skipping TOKEN_REFRESHED - session unchanged");
5090
+ return;
5091
+ }
5092
+ }
5093
+ if (event !== "TOKEN_REFRESHED" && currentSession?.user) {
5094
+ console.log("[AuthContext] Non-TOKEN_REFRESHED event will trigger fetchUserDetails:", event);
5095
+ }
5096
+ const sessionChanged = session?.user?.id !== currentSession?.user?.id || session?.user?.email !== currentSession?.user?.email;
5097
+ if (!sessionChanged && user) {
5098
+ console.log("[AuthContext] Session and user unchanged, skipping update");
5099
+ return;
5100
+ }
5050
5101
  setSession(currentSession);
5051
5102
  setUser(null);
5052
5103
  setLoading(false);
@@ -6967,16 +7018,12 @@ var useTargets = (options) => {
6967
7018
  };
6968
7019
  var DEFAULT_SHIFTS_TABLE_NAME = "shift_configurations";
6969
7020
  var useShifts = () => {
6970
- const { supabaseUrl, supabaseKey } = useDashboardConfig();
6971
7021
  const { companyId } = useEntityConfig();
6972
7022
  const { tables } = useDatabaseConfig();
7023
+ const supabase = useSupabase();
6973
7024
  const [shifts, setShifts] = React19.useState([]);
6974
7025
  const [isLoading, setIsLoading] = React19.useState(true);
6975
7026
  const [error, setError] = React19.useState(null);
6976
- const supabase = React19.useMemo(() => {
6977
- if (!supabaseUrl || !supabaseKey) return null;
6978
- return supabaseJs.createClient(supabaseUrl, supabaseKey);
6979
- }, [supabaseUrl, supabaseKey]);
6980
7027
  const shiftsTable = tables?.shiftConfigurations || DEFAULT_SHIFTS_TABLE_NAME;
6981
7028
  const fetchData = React19.useCallback(async () => {
6982
7029
  if (!supabase) {
@@ -7675,6 +7722,7 @@ var runtimeWorkspaceDisplayNames = {};
7675
7722
  var isInitialized = false;
7676
7723
  var isInitializing = false;
7677
7724
  var initializedWithLineIds = [];
7725
+ var initializationPromise = null;
7678
7726
  function getCurrentLineIds() {
7679
7727
  try {
7680
7728
  const config = _getDashboardConfigInstance();
@@ -7695,52 +7743,79 @@ function getCurrentLineIds() {
7695
7743
  }
7696
7744
  async function initializeWorkspaceDisplayNames(explicitLineId) {
7697
7745
  console.log("\u{1F504} initializeWorkspaceDisplayNames called", { isInitialized, isInitializing, explicitLineId });
7698
- if (isInitialized || isInitializing) return;
7699
- isInitializing = true;
7700
- try {
7701
- console.log("\u{1F504} Starting Supabase workspace display names initialization...");
7702
- let targetLineIds = [];
7703
- if (explicitLineId) {
7704
- targetLineIds = [explicitLineId];
7705
- } else {
7706
- targetLineIds = getCurrentLineIds();
7746
+ if (isInitialized) return;
7747
+ if (initializationPromise) {
7748
+ console.log("\u{1F504} Already initializing, waiting for existing initialization...");
7749
+ await initializationPromise;
7750
+ return;
7751
+ }
7752
+ initializationPromise = (async () => {
7753
+ isInitializing = true;
7754
+ try {
7755
+ console.log("\u{1F504} Starting Supabase workspace display names initialization...");
7756
+ let targetLineIds = [];
7757
+ if (explicitLineId) {
7758
+ targetLineIds = [explicitLineId];
7759
+ } else {
7760
+ targetLineIds = getCurrentLineIds();
7761
+ }
7762
+ console.log("\u{1F504} Target line IDs for workspace filtering:", targetLineIds);
7763
+ runtimeWorkspaceDisplayNames = {};
7764
+ if (targetLineIds.length > 0) {
7765
+ for (const lineId of targetLineIds) {
7766
+ console.log(`\u{1F504} Fetching workspaces for line: ${lineId}`);
7767
+ const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
7768
+ runtimeWorkspaceDisplayNames[lineId] = {};
7769
+ lineDisplayNamesMap.forEach((displayName, workspaceId) => {
7770
+ runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
7771
+ });
7772
+ console.log(`\u2705 Stored ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
7773
+ }
7774
+ } else {
7775
+ console.warn("\u26A0\uFE0F No line IDs found, fetching all workspaces (less efficient)");
7776
+ const allWorkspacesMap = await workspaceService.getWorkspaceDisplayNames();
7777
+ runtimeWorkspaceDisplayNames["global"] = {};
7778
+ allWorkspacesMap.forEach((displayName, workspaceId) => {
7779
+ runtimeWorkspaceDisplayNames["global"][workspaceId] = displayName;
7780
+ });
7781
+ }
7782
+ isInitialized = true;
7783
+ initializedWithLineIds = targetLineIds;
7784
+ console.log("\u2705 Workspace display names initialized from Supabase:", runtimeWorkspaceDisplayNames);
7785
+ console.log("\u2705 Initialized with line IDs:", initializedWithLineIds);
7786
+ } catch (error) {
7787
+ console.error("\u274C Failed to initialize workspace display names from Supabase:", error);
7788
+ } finally {
7789
+ isInitializing = false;
7790
+ initializationPromise = null;
7707
7791
  }
7708
- console.log("\u{1F504} Target line IDs for workspace filtering:", targetLineIds);
7709
- runtimeWorkspaceDisplayNames = {};
7710
- if (targetLineIds.length > 0) {
7711
- for (const lineId of targetLineIds) {
7712
- console.log(`\u{1F504} Fetching workspaces for line: ${lineId}`);
7792
+ })();
7793
+ await initializationPromise;
7794
+ }
7795
+ var preInitializeWorkspaceDisplayNames = async (lineId) => {
7796
+ console.log("\u{1F504} preInitializeWorkspaceDisplayNames called for lineId:", lineId);
7797
+ if (isInitialized) {
7798
+ console.log("\u{1F504} Already initialized");
7799
+ if (lineId && !runtimeWorkspaceDisplayNames[lineId]) {
7800
+ console.log(`\u{1F504} Line ${lineId} not in cache, fetching...`);
7801
+ try {
7713
7802
  const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
7714
7803
  runtimeWorkspaceDisplayNames[lineId] = {};
7715
7804
  lineDisplayNamesMap.forEach((displayName, workspaceId) => {
7716
7805
  runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
7717
7806
  });
7718
- console.log(`\u2705 Stored ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
7807
+ console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
7808
+ } catch (error) {
7809
+ console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
7719
7810
  }
7720
- } else {
7721
- console.warn("\u26A0\uFE0F No line IDs found, fetching all workspaces (less efficient)");
7722
- const allWorkspacesMap = await workspaceService.getWorkspaceDisplayNames();
7723
- runtimeWorkspaceDisplayNames["global"] = {};
7724
- allWorkspacesMap.forEach((displayName, workspaceId) => {
7725
- runtimeWorkspaceDisplayNames["global"][workspaceId] = displayName;
7726
- });
7727
7811
  }
7728
- isInitialized = true;
7729
- initializedWithLineIds = targetLineIds;
7730
- console.log("\u2705 Workspace display names initialized from Supabase:", runtimeWorkspaceDisplayNames);
7731
- console.log("\u2705 Initialized with line IDs:", initializedWithLineIds);
7732
- } catch (error) {
7733
- console.error("\u274C Failed to initialize workspace display names from Supabase:", error);
7734
- } finally {
7735
- isInitializing = false;
7812
+ return;
7736
7813
  }
7737
- }
7738
- var preInitializeWorkspaceDisplayNames = async (lineId) => {
7739
- console.log("\u{1F504} preInitializeWorkspaceDisplayNames called for lineId:", lineId);
7740
- if (isInitialized || isInitializing) {
7741
- console.log("\u{1F504} Already initialized or initializing");
7814
+ if (initializationPromise) {
7815
+ console.log("\u{1F504} Already initializing, waiting for completion...");
7816
+ await initializationPromise;
7742
7817
  if (lineId && !runtimeWorkspaceDisplayNames[lineId]) {
7743
- console.log(`\u{1F504} Line ${lineId} not in cache, fetching...`);
7818
+ console.log(`\u{1F504} Line ${lineId} not in cache after init, fetching...`);
7744
7819
  try {
7745
7820
  const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
7746
7821
  runtimeWorkspaceDisplayNames[lineId] = {};
@@ -7758,7 +7833,12 @@ var preInitializeWorkspaceDisplayNames = async (lineId) => {
7758
7833
  };
7759
7834
  var forceRefreshWorkspaceDisplayNames = async (lineId) => {
7760
7835
  console.log("\u{1F504} forceRefreshWorkspaceDisplayNames called for lineId:", lineId);
7836
+ if (initializationPromise) {
7837
+ console.log("\u{1F504} Waiting for existing initialization to complete before refresh...");
7838
+ await initializationPromise;
7839
+ }
7761
7840
  clearWorkspaceDisplayNamesCache();
7841
+ initializationPromise = null;
7762
7842
  await initializeWorkspaceDisplayNames(lineId);
7763
7843
  };
7764
7844
  console.log("\u{1F504} Module loaded, will initialize lazily when first function is called");
@@ -7906,6 +7986,7 @@ var clearWorkspaceDisplayNamesCache = () => {
7906
7986
  isInitialized = false;
7907
7987
  isInitializing = false;
7908
7988
  initializedWithLineIds = [];
7989
+ initializationPromise = null;
7909
7990
  };
7910
7991
 
7911
7992
  // src/lib/hooks/useWorkspaceDisplayNames.ts
@@ -19546,11 +19627,13 @@ var withAuth = (WrappedComponent2, options) => {
19546
19627
  requireAuth: true,
19547
19628
  ...options
19548
19629
  };
19549
- return function WithAuthComponent(props) {
19630
+ const WithAuthComponent = React19__namespace.memo(function WithAuthComponent2(props) {
19550
19631
  const { session, loading } = useAuth();
19551
19632
  const router$1 = router.useRouter();
19552
19633
  React19__namespace.useEffect(() => {
19553
- console.log("withAuth state:", { loading, hasSession: !!session, requireAuth: defaultOptions.requireAuth });
19634
+ if (process.env.NODE_ENV === "development" && process.env.DEBUG_AUTH === "true") {
19635
+ console.log("withAuth state:", { loading, hasSession: !!session, requireAuth: defaultOptions.requireAuth });
19636
+ }
19554
19637
  }, [session, loading]);
19555
19638
  React19__namespace.useEffect(() => {
19556
19639
  if (!loading && defaultOptions.requireAuth && !session) {
@@ -19565,7 +19648,9 @@ var withAuth = (WrappedComponent2, options) => {
19565
19648
  return null;
19566
19649
  }
19567
19650
  return /* @__PURE__ */ jsxRuntime.jsx(WrappedComponent2, { ...props });
19568
- };
19651
+ });
19652
+ WithAuthComponent.displayName = `withAuth(${WrappedComponent2.displayName || WrappedComponent2.name || "Component"})`;
19653
+ return WithAuthComponent;
19569
19654
  };
19570
19655
  var LoginPage = ({
19571
19656
  onRateLimitCheck,
@@ -26153,7 +26238,7 @@ var BottlenecksContent = ({
26153
26238
  const [duration, setDuration] = React19.useState(0);
26154
26239
  const [currentIndex, setCurrentIndex] = React19.useState(0);
26155
26240
  const [activeFilter, setActiveFilter] = React19.useState(initialFilter);
26156
- const previousFilterRef = React19.useRef(initialFilter);
26241
+ const previousFilterRef = React19.useRef("");
26157
26242
  const [allVideos, setAllVideos] = React19.useState([]);
26158
26243
  const [isLoading, setIsLoading] = React19.useState(true);
26159
26244
  const [isCategoryLoading, setIsCategoryLoading] = React19.useState(false);
@@ -26203,7 +26288,7 @@ var BottlenecksContent = ({
26203
26288
  console.warn("S3 configuration not found in dashboard config");
26204
26289
  return null;
26205
26290
  }
26206
- return new S3ClipsService(dashboardConfig);
26291
+ return videoPrefetchManager.getS3Service(dashboardConfig);
26207
26292
  }, [dashboardConfig]);
26208
26293
  const {
26209
26294
  data: prefetchData,
@@ -26310,8 +26395,8 @@ var BottlenecksContent = ({
26310
26395
  index,
26311
26396
  true,
26312
26397
  // includeCycleTime - OK for preloading
26313
- true
26314
- // includeMetadata - OK for just +3/-1 videos, not ALL videos
26398
+ false
26399
+ // includeMetadata - NO metadata during bulk preloading to prevent flooding
26315
26400
  );
26316
26401
  }
26317
26402
  if (!video) {
@@ -26325,8 +26410,8 @@ var BottlenecksContent = ({
26325
26410
  index,
26326
26411
  true,
26327
26412
  // includeCycleTime - OK for preloading
26328
- true
26329
- // includeMetadata - OK for just +3/-1 videos, not ALL videos
26413
+ false
26414
+ // includeMetadata - NO metadata during bulk preloading to prevent flooding
26330
26415
  );
26331
26416
  }
26332
26417
  if (video && isMountedRef.current) {
@@ -26386,8 +26471,16 @@ var BottlenecksContent = ({
26386
26471
  );
26387
26472
  if (firstVideo) {
26388
26473
  console.log(`[BottlenecksContent] Successfully loaded first video via direct S3 method`);
26389
- setAllVideos([firstVideo]);
26474
+ setAllVideos((prev) => {
26475
+ const exists = prev.some((v) => v.id === firstVideo.id);
26476
+ if (!exists) {
26477
+ return [...prev, firstVideo];
26478
+ }
26479
+ return prev;
26480
+ });
26390
26481
  preloadVideoUrl(firstVideo.src);
26482
+ setIsLoading(false);
26483
+ setHasInitialLoad(true);
26391
26484
  loadingCategoryRef.current = null;
26392
26485
  setIsCategoryLoading(false);
26393
26486
  return;
@@ -26410,7 +26503,13 @@ var BottlenecksContent = ({
26410
26503
  );
26411
26504
  if (firstVideo && isMountedRef.current) {
26412
26505
  console.log(`[BottlenecksContent] Successfully loaded first video via video index`);
26413
- setAllVideos([firstVideo]);
26506
+ setAllVideos((prev) => {
26507
+ const exists = prev.some((v) => v.id === firstVideo.id);
26508
+ if (!exists) {
26509
+ return [...prev, firstVideo];
26510
+ }
26511
+ return prev;
26512
+ });
26414
26513
  preloadVideoUrl(firstVideo.src);
26415
26514
  setIsCategoryLoading(false);
26416
26515
  return;
@@ -26435,15 +26534,17 @@ var BottlenecksContent = ({
26435
26534
  }
26436
26535
  }, [workspaceId, date, s3ClipsService, clipCounts, videoIndex, shift, updateClipCounts, updateVideoIndex]);
26437
26536
  React19.useEffect(() => {
26438
- if (s3ClipsService) {
26537
+ if (s3ClipsService && !prefetchData) {
26439
26538
  fetchClipCounts();
26440
26539
  }
26441
- }, [workspaceId, date, shift, s3ClipsService, fetchClipCounts, updateClipCounts, updateVideoIndex]);
26540
+ }, [workspaceId, date, shift, s3ClipsService, fetchClipCounts, updateClipCounts, updateVideoIndex, prefetchData]);
26442
26541
  React19.useEffect(() => {
26443
26542
  if (prefetchData) {
26444
26543
  console.log(`[BottlenecksContent] Received prefetch update - status: ${prefetchStatus}, videos: ${prefetchData.videoIndex.allVideos.length}, ID: ${prefetchData.videoIndex._debugId || "NO_ID"}`);
26445
26544
  updateClipCounts(prefetchData.counts);
26446
- updateVideoIndex(prefetchData.videoIndex);
26545
+ if (prefetchData.videoIndex.allVideos.length > 0) {
26546
+ updateVideoIndex(prefetchData.videoIndex);
26547
+ }
26447
26548
  if (!hasInitialLoad && prefetchData.videoIndex.allVideos.length > 0) {
26448
26549
  setIsLoading(false);
26449
26550
  setHasInitialLoad(true);
@@ -26451,12 +26552,45 @@ var BottlenecksContent = ({
26451
26552
  }
26452
26553
  }, [prefetchData, prefetchStatus, updateClipCounts, updateVideoIndex, hasInitialLoad]);
26453
26554
  React19.useEffect(() => {
26454
- if (s3ClipsService && videoIndex && clipCounts[activeFilter] > 0) {
26455
- if (allVideos.length === 0) {
26555
+ if (s3ClipsService && clipCounts[activeFilter] > 0) {
26556
+ const hasVideosForCurrentFilter = allVideos.some((video) => {
26557
+ if (sopCategories && sopCategories.length > 0) {
26558
+ const selectedCategory = sopCategories.find((cat) => cat.id === activeFilter);
26559
+ if (selectedCategory) {
26560
+ return video.type === selectedCategory.id;
26561
+ }
26562
+ }
26563
+ if (activeFilter === "all") return true;
26564
+ if (activeFilter === "low_value") {
26565
+ return video.type === "low_value";
26566
+ }
26567
+ if (activeFilter === "idle_time") {
26568
+ return video.type === "idle_time";
26569
+ }
26570
+ if (activeFilter === "sop_deviations") {
26571
+ return video.type === "missing_quality_check";
26572
+ }
26573
+ if (activeFilter === "best_cycle_time") {
26574
+ return video.type === "best_cycle_time";
26575
+ }
26576
+ if (activeFilter === "worst_cycle_time") {
26577
+ return video.type === "worst_cycle_time";
26578
+ }
26579
+ if (activeFilter === "cycle_completion") {
26580
+ return video.type === "cycle_completion";
26581
+ }
26582
+ if (activeFilter === "long_cycle_time") {
26583
+ return video.type === "long_cycle_time";
26584
+ }
26585
+ return video.type === "bottleneck" && video.severity === activeFilter;
26586
+ });
26587
+ if (!hasVideosForCurrentFilter) {
26456
26588
  loadFirstVideoForCategory(activeFilter);
26589
+ } else {
26590
+ setIsCategoryLoading(false);
26457
26591
  }
26458
26592
  }
26459
- }, [activeFilter, s3ClipsService, videoIndex, clipCounts, loadFirstVideoForCategory, allVideos.length]);
26593
+ }, [activeFilter, s3ClipsService, videoIndex, clipCounts, loadFirstVideoForCategory, allVideos, sopCategories]);
26460
26594
  React19.useEffect(() => {
26461
26595
  if (previousFilterRef.current !== activeFilter) {
26462
26596
  console.log(`Filter changed from ${previousFilterRef.current} to ${activeFilter} - resetting to first video`);
@@ -26639,14 +26773,39 @@ var BottlenecksContent = ({
26639
26773
  }
26640
26774
  }
26641
26775
  }, [filteredVideos.length]);
26776
+ const currentVideo = React19.useMemo(() => {
26777
+ if (!filteredVideos || filteredVideos.length === 0 || currentIndex >= filteredVideos.length) {
26778
+ return null;
26779
+ }
26780
+ return filteredVideos[currentIndex];
26781
+ }, [filteredVideos, currentIndex]);
26642
26782
  const handleVideoReady = React19.useCallback((player) => {
26643
26783
  console.log("Video.js player ready");
26644
26784
  }, []);
26645
- const handleVideoPlay = React19.useCallback((player) => {
26785
+ const handleVideoPlay = React19.useCallback(async (player) => {
26646
26786
  setIsPlaying(true);
26647
26787
  const currentIdx = currentIndexRef.current;
26648
26788
  ensureVideosLoaded(currentIdx);
26649
- }, []);
26789
+ if (currentVideo && !currentVideo.creation_timestamp && s3ClipsService) {
26790
+ try {
26791
+ const originalUri = currentVideo.originalUri || currentVideo.src;
26792
+ if (originalUri && originalUri.includes("s3://")) {
26793
+ const metadata = await s3ClipsService.getFullMetadata(originalUri);
26794
+ if (metadata && isMountedRef.current) {
26795
+ setAllVideos((prev) => prev.map(
26796
+ (v) => v.id === currentVideo.id ? {
26797
+ ...v,
26798
+ creation_timestamp: metadata.upload_timestamp || metadata.original_task_metadata?.timestamp || metadata.creation_timestamp,
26799
+ cycle_time_seconds: metadata.original_task_metadata?.cycle_time || v.cycle_time_seconds
26800
+ } : v
26801
+ ));
26802
+ }
26803
+ }
26804
+ } catch (error2) {
26805
+ console.warn("[BottlenecksContent] Failed to load metadata for current video:", error2);
26806
+ }
26807
+ }
26808
+ }, [currentVideo, s3ClipsService]);
26650
26809
  const handleVideoPause = React19.useCallback((player) => {
26651
26810
  setIsPlaying(false);
26652
26811
  }, []);
@@ -26673,13 +26832,6 @@ var BottlenecksContent = ({
26673
26832
  fetchInProgressRef.current.clear();
26674
26833
  setIsCategoryLoading(false);
26675
26834
  setIsNavigating(false);
26676
- if (s3ClipsService) {
26677
- try {
26678
- s3ClipsService.dispose();
26679
- } catch (error2) {
26680
- console.warn("[BottlenecksContent] Error disposing S3 service:", error2);
26681
- }
26682
- }
26683
26835
  };
26684
26836
  }, [s3ClipsService]);
26685
26837
  React19.useEffect(() => {
@@ -26720,12 +26872,6 @@ var BottlenecksContent = ({
26720
26872
  counts.bottlenecks = counts.bottleneck || counts.bottlenecks || 0;
26721
26873
  return counts;
26722
26874
  }, [clipCounts]);
26723
- const currentVideo = React19.useMemo(() => {
26724
- if (!filteredVideos || filteredVideos.length === 0 || currentIndex >= filteredVideos.length) {
26725
- return null;
26726
- }
26727
- return filteredVideos[currentIndex];
26728
- }, [filteredVideos, currentIndex]);
26729
26875
  const getClipTypeLabel = (video) => {
26730
26876
  if (!video) return "";
26731
26877
  switch (video.type) {
@@ -26960,8 +27106,8 @@ var BottlenecksContent = ({
26960
27106
  ] }),
26961
27107
  /* Priority 1: Show loading if initial load hasn't completed yet */
26962
27108
  isLoading && !hasInitialLoad ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading clips..." }) }) }) }) : (
26963
- /* Priority 2: Show loading if category is loading (prevents "no matching clips" flash) */
26964
- isCategoryLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading videos..." }) }) }) }) : (
27109
+ /* Priority 2: Show loading if category is loading BUT only if no video is available */
27110
+ isCategoryLoading && (!filteredVideos.length || !currentVideo) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading videos..." }) }) }) }) : (
26965
27111
  /* Priority 3: Show loading if navigating and current video not available */
26966
27112
  isNavigating || currentIndex >= filteredVideos.length && currentIndex < clipCounts[activeFilter] ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }) }) }) : (
26967
27113
  /* Priority 4: Show video if we have filtered videos and current video */
@@ -31759,9 +31905,25 @@ function withWorkspaceDisplayNames(Component3, options = {}) {
31759
31905
  return function WithWorkspaceDisplayNamesWrapper(props) {
31760
31906
  const [isInitialized2, setIsInitialized] = React19.useState(false);
31761
31907
  const [error, setError] = React19.useState(null);
31908
+ const [lastInitKey, setLastInitKey] = React19.useState("");
31909
+ const lineIdsKey = React19.useMemo(() => {
31910
+ if (!props.lineIds) return "";
31911
+ if (Array.isArray(props.lineIds)) {
31912
+ return props.lineIds.sort().join(",");
31913
+ }
31914
+ const values = Object.values(props.lineIds).filter(Boolean);
31915
+ return values.sort().join(",");
31916
+ }, [props.lineIds]);
31917
+ const initKey = React19.useMemo(() => {
31918
+ return `${lineIdsKey}-${props.selectedLineId || ""}-${props.factoryViewId || ""}-${initializeFor}`;
31919
+ }, [lineIdsKey, props.selectedLineId, props.factoryViewId]);
31762
31920
  React19.useEffect(() => {
31763
- setIsInitialized(false);
31764
- setError(null);
31921
+ if (initKey === lastInitKey && isInitialized2) {
31922
+ return;
31923
+ }
31924
+ if (initKey !== lastInitKey) {
31925
+ setError(null);
31926
+ }
31765
31927
  const initializeDisplayNames = async () => {
31766
31928
  try {
31767
31929
  const { lineIds, selectedLineId, factoryViewId } = props;
@@ -31787,20 +31949,17 @@ function withWorkspaceDisplayNames(Component3, options = {}) {
31787
31949
  await preInitializeWorkspaceDisplayNames();
31788
31950
  }
31789
31951
  setIsInitialized(true);
31952
+ setLastInitKey(initKey);
31790
31953
  } catch (err) {
31791
31954
  console.error("Failed to initialize workspace display names:", err);
31792
31955
  setError(err);
31793
31956
  setIsInitialized(true);
31957
+ setLastInitKey(initKey);
31794
31958
  }
31795
31959
  };
31796
31960
  initializeDisplayNames();
31797
- }, [
31798
- Array.isArray(props.lineIds) ? props.lineIds.join(",") : JSON.stringify(props.lineIds),
31799
- props.selectedLineId,
31800
- props.factoryViewId,
31801
- initializeFor
31802
- ]);
31803
- if (!isInitialized2 && showLoading) {
31961
+ }, [initKey]);
31962
+ if (!isInitialized2 && showLoading && lastInitKey === "") {
31804
31963
  return /* @__PURE__ */ jsxRuntime.jsx(LoadingPage, { message: loadingMessage });
31805
31964
  }
31806
31965
  if (error && showLoading) {
@@ -33871,7 +34030,15 @@ var ShiftsView = ({
33871
34030
  className = ""
33872
34031
  }) => {
33873
34032
  const supabase = useSupabase();
33874
- const auth = useAuth();
34033
+ React19.useEffect(() => {
34034
+ console.log("[ShiftsView] Component mounted/re-rendered", {
34035
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
34036
+ lineIds: lineIds.length
34037
+ });
34038
+ return () => {
34039
+ console.log("[ShiftsView] Component unmounting");
34040
+ };
34041
+ }, []);
33875
34042
  const [lineConfigs, setLineConfigs] = React19.useState(
33876
34043
  () => lineIds.map((id3) => ({
33877
34044
  id: id3,
@@ -33969,7 +34136,7 @@ var ShiftsView = ({
33969
34136
  }
33970
34137
  };
33971
34138
  fetchShiftConfigs();
33972
- }, [supabase, lineIds, showToast]);
34139
+ }, [lineIds, showToast]);
33973
34140
  React19.useCallback((lineId) => {
33974
34141
  setLineConfigs((prev) => {
33975
34142
  const typedPrev = prev;
@@ -34162,7 +34329,6 @@ var ShiftsView = ({
34162
34329
  }));
34163
34330
  }, []);
34164
34331
  const handleSaveShifts = React19.useCallback(async (lineId) => {
34165
- if (!auth.user?.id && false) ;
34166
34332
  setLineConfigs((prev) => prev.map(
34167
34333
  (config) => config.id === lineId ? { ...config, isSaving: true, saveSuccess: false } : config
34168
34334
  ));
@@ -34209,7 +34375,7 @@ var ShiftsView = ({
34209
34375
  (config) => config.id === lineId ? { ...config, isSaving: false, saveSuccess: false } : config
34210
34376
  ));
34211
34377
  }
34212
- }, [auth.user?.id, lineConfigs, supabase, showToast]);
34378
+ }, [lineConfigs, supabase, showToast]);
34213
34379
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `min-h-screen bg-slate-50 ${className}`, children: [
34214
34380
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-0 z-10 bg-white border-b border-gray-200/80 shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 sm:px-8 py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center relative", children: [
34215
34381
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -34317,6 +34483,7 @@ var ShiftsView = ({
34317
34483
  ] })
34318
34484
  ] });
34319
34485
  };
34486
+ var AuthenticatedShiftsView = withAuth(React19__namespace.default.memo(ShiftsView));
34320
34487
  var ShiftsView_default = ShiftsView;
34321
34488
 
34322
34489
  // src/lib/constants/actions.ts
@@ -35104,6 +35271,7 @@ var TargetsViewUI = ({
35104
35271
  isLoading,
35105
35272
  lineWorkspaces,
35106
35273
  lineNames,
35274
+ dropdownStates,
35107
35275
  savingLines,
35108
35276
  saveSuccess,
35109
35277
  selectedShift,
@@ -35189,13 +35357,13 @@ var TargetsViewUI = ({
35189
35357
  {
35190
35358
  onClick: () => onToggleLineDropdown(lineId),
35191
35359
  className: "flex items-center gap-3 text-lg font-medium transition-colors duration-200 \n focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 rounded-lg \n hover:bg-blue-50 px-3 py-2 group",
35192
- "aria-expanded": line.isOpen,
35360
+ "aria-expanded": dropdownStates[lineId],
35193
35361
  "aria-controls": `line-${lineId}-content`,
35194
35362
  children: [
35195
35363
  /* @__PURE__ */ jsxRuntime.jsx(
35196
35364
  lucideReact.ChevronDown,
35197
35365
  {
35198
- className: `w-5 h-5 text-blue-500 transform transition-transform duration-200 ${line.isOpen ? "rotate-180" : ""}`
35366
+ className: `w-5 h-5 text-blue-500 transform transition-transform duration-200 ${dropdownStates[lineId] ? "rotate-180" : ""}`
35199
35367
  }
35200
35368
  ),
35201
35369
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5", children: [
@@ -35242,7 +35410,7 @@ var TargetsViewUI = ({
35242
35410
  )
35243
35411
  ] })
35244
35412
  ] }) }),
35245
- line.isOpen && /* @__PURE__ */ jsxRuntime.jsxs("div", { id: `line-${lineId}-content`, className: "border-t border-gray-200", children: [
35413
+ dropdownStates[lineId] && /* @__PURE__ */ jsxRuntime.jsxs("div", { id: `line-${lineId}-content`, className: "border-t border-gray-200", children: [
35246
35414
  skuEnabled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4 border-b border-gray-200 bg-gray-50/50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
35247
35415
  /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: `sku-${lineId}`, className: "text-sm font-medium text-gray-700", children: "Select SKU:" }),
35248
35416
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 max-w-md", children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -35279,68 +35447,71 @@ var TargetsViewUI = ({
35279
35447
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-400", children: "pieces per day" })
35280
35448
  ] })
35281
35449
  ] }) }),
35282
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-gray-100", children: line.workspaces.map((workspace) => /* @__PURE__ */ jsxRuntime.jsx(
35283
- "div",
35284
- {
35285
- className: "px-6 py-4 hover:bg-gray-50 transition-all duration-200",
35286
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-12 gap-6 items-center", children: [
35287
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-900", children: formatWorkspaceName(workspace.name, lineId) }) }),
35288
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
35289
- "select",
35290
- {
35291
- value: workspace.actionType,
35292
- onChange: (e) => {
35293
- const newActionType = e.target.value;
35294
- onActionTypeChange(lineId, workspace.id, newActionType);
35295
- },
35296
- className: "w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm",
35297
- "aria-label": `Action type for ${formatWorkspaceName(workspace.name, lineId)}`,
35298
- children: [
35299
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "assembly", className: "py-2", children: "Assembly" }),
35300
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "packaging", className: "py-2", children: "Packaging" })
35301
- ]
35302
- }
35303
- ) }),
35304
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsxRuntime.jsx(
35305
- "input",
35306
- {
35307
- type: "number",
35308
- value: workspace.targetCycleTime === 0 ? "" : workspace.targetCycleTime,
35309
- onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetCycleTime", Number(e.target.value) || ""),
35310
- 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",
35311
- min: "0",
35312
- step: "0.01",
35313
- placeholder: "Enter cycle time"
35314
- }
35315
- ) }),
35316
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsxRuntime.jsx(
35317
- "input",
35318
- {
35319
- type: "number",
35320
- value: workspace.targetPPH === 0 ? "" : workspace.targetPPH,
35321
- onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetPPH", Number(e.target.value) || ""),
35322
- 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",
35323
- min: "0",
35324
- step: "0.1",
35325
- placeholder: "Enter PPH"
35326
- }
35327
- ) }),
35328
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsxRuntime.jsx(
35329
- "input",
35330
- {
35331
- type: "number",
35332
- value: workspace.targetDayOutput === 0 ? "" : workspace.targetDayOutput,
35333
- onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetDayOutput", Number(e.target.value) || ""),
35334
- 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",
35335
- min: "0",
35336
- step: "1",
35337
- placeholder: "Enter day output"
35338
- }
35339
- ) })
35340
- ] })
35341
- },
35342
- workspace.id
35343
- )) })
35450
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-gray-100", children: line.workspaces.map((workspace) => {
35451
+ const formattedName = formatWorkspaceName(workspace.name, lineId);
35452
+ return /* @__PURE__ */ jsxRuntime.jsx(
35453
+ "div",
35454
+ {
35455
+ className: "px-6 py-4 hover:bg-gray-50 transition-all duration-200",
35456
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-12 gap-6 items-center", children: [
35457
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-900", children: formattedName }) }),
35458
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
35459
+ "select",
35460
+ {
35461
+ value: workspace.actionType,
35462
+ onChange: (e) => {
35463
+ const newActionType = e.target.value;
35464
+ onActionTypeChange(lineId, workspace.id, newActionType);
35465
+ },
35466
+ className: "w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm",
35467
+ "aria-label": `Action type for ${formattedName}`,
35468
+ children: [
35469
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "assembly", className: "py-2", children: "Assembly" }),
35470
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "packaging", className: "py-2", children: "Packaging" })
35471
+ ]
35472
+ }
35473
+ ) }),
35474
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsxRuntime.jsx(
35475
+ "input",
35476
+ {
35477
+ type: "number",
35478
+ value: workspace.targetCycleTime === 0 ? "" : workspace.targetCycleTime,
35479
+ onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetCycleTime", Number(e.target.value) || ""),
35480
+ className: "block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm\n shadow-sm focus:border-blue-500 focus:ring-blue-500 \n transition-all duration-200 hover:border-gray-400",
35481
+ min: "0",
35482
+ step: "0.01",
35483
+ placeholder: "Enter cycle time"
35484
+ }
35485
+ ) }),
35486
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsxRuntime.jsx(
35487
+ "input",
35488
+ {
35489
+ type: "number",
35490
+ value: workspace.targetPPH === 0 ? "" : workspace.targetPPH,
35491
+ onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetPPH", Number(e.target.value) || ""),
35492
+ className: "block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm\n shadow-sm focus:border-blue-500 focus:ring-blue-500 \n transition-all duration-200 hover:border-gray-400 \n placeholder:text-gray-400",
35493
+ min: "0",
35494
+ step: "0.1",
35495
+ placeholder: "Enter PPH"
35496
+ }
35497
+ ) }),
35498
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsxRuntime.jsx(
35499
+ "input",
35500
+ {
35501
+ type: "number",
35502
+ value: workspace.targetDayOutput === 0 ? "" : workspace.targetDayOutput,
35503
+ onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetDayOutput", Number(e.target.value) || ""),
35504
+ className: "block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm\n shadow-sm focus:border-blue-500 focus:ring-blue-500 \n transition-all duration-200 hover:border-gray-400 \n placeholder:text-gray-400",
35505
+ min: "0",
35506
+ step: "1",
35507
+ placeholder: "Enter day output"
35508
+ }
35509
+ ) })
35510
+ ] })
35511
+ },
35512
+ workspace.id
35513
+ );
35514
+ }) })
35344
35515
  ] })
35345
35516
  ]
35346
35517
  },
@@ -35373,7 +35544,6 @@ var TargetsView = ({
35373
35544
  return lineIds.reduce((acc, lineId) => ({
35374
35545
  ...acc,
35375
35546
  [lineId]: {
35376
- isOpen: getStoredLineState2(lineId),
35377
35547
  productId: "",
35378
35548
  shiftStartTime: "08:00",
35379
35549
  shiftEndTime: "19:00",
@@ -35386,6 +35556,12 @@ var TargetsView = ({
35386
35556
  }
35387
35557
  }), {});
35388
35558
  }, [lineIds]);
35559
+ const [dropdownStates, setDropdownStates] = React19.useState(() => {
35560
+ return lineIds.reduce((acc, lineId) => ({
35561
+ ...acc,
35562
+ [lineId]: getStoredLineState2(lineId)
35563
+ }), {});
35564
+ });
35389
35565
  const [allShiftsData, setAllShiftsData] = React19.useState({
35390
35566
  0: initialLineWorkspaces,
35391
35567
  // Day shift
@@ -35403,6 +35579,8 @@ var TargetsView = ({
35403
35579
  const [isBulkConfigureOpen, setIsBulkConfigureOpen] = React19.useState(false);
35404
35580
  const [selectedWorkspaces, setSelectedWorkspaces] = React19.useState([]);
35405
35581
  const [selectedShift, setSelectedShift] = React19.useState(0);
35582
+ const [dbValues, setDbValues] = React19.useState({ 0: {}, 1: {} });
35583
+ const [userEditedFields, setUserEditedFields] = React19.useState(/* @__PURE__ */ new Set());
35406
35584
  const lineWorkspaces = allShiftsData[selectedShift] || initialLineWorkspaces;
35407
35585
  const setLineWorkspaces = React19.useCallback((updater) => {
35408
35586
  setAllShiftsData((prev) => ({
@@ -35411,18 +35589,123 @@ var TargetsView = ({
35411
35589
  }));
35412
35590
  }, [selectedShift]);
35413
35591
  const supabase = useSupabase();
35414
- const auth = useAuth();
35415
- userId || auth?.user?.id;
35592
+ const effectiveUserId = userId || "6bf6f271-1e55-4a95-9b89-1c3820b58739";
35416
35593
  const dashboardConfig = useDashboardConfig();
35417
35594
  const { skus, isLoading: skusLoading } = useSKUs(companyId);
35418
35595
  const skuEnabled = dashboardConfig?.skuConfig?.enabled || false;
35596
+ const loadOperatingHours = React19.useCallback(async (lineId, shiftId) => {
35597
+ try {
35598
+ if (!supabase) return null;
35599
+ const { data, error } = await supabase.from("line_operating_hours").select("start_time, end_time, breaks").eq("line_id", lineId).eq("shift_id", shiftId).maybeSingle();
35600
+ if (error) {
35601
+ if (error.code === "PGRST116") {
35602
+ console.log(`No operating hours found for line ${lineId}, shift ${shiftId}`);
35603
+ return {
35604
+ startTime: "08:00",
35605
+ // Default values
35606
+ endTime: "19:00",
35607
+ breaks: []
35608
+ };
35609
+ } else {
35610
+ console.error("Error fetching operating hours:", error);
35611
+ return null;
35612
+ }
35613
+ }
35614
+ let breaks = [];
35615
+ if (data?.breaks) {
35616
+ if (Array.isArray(data.breaks)) {
35617
+ breaks = data.breaks.map((breakItem) => {
35618
+ const startTime = breakItem.start || breakItem.startTime || "00:00";
35619
+ const endTime = breakItem.end || breakItem.endTime || "00:00";
35620
+ const calculateDuration = (start, end) => {
35621
+ const [startHour, startMinute] = start.split(":").map(Number);
35622
+ const [endHour, endMinute] = end.split(":").map(Number);
35623
+ let startMinutes = startHour * 60 + startMinute;
35624
+ let endMinutes = endHour * 60 + endMinute;
35625
+ if (endMinutes < startMinutes) {
35626
+ endMinutes += 24 * 60;
35627
+ }
35628
+ return endMinutes - startMinutes;
35629
+ };
35630
+ return {
35631
+ startTime,
35632
+ endTime,
35633
+ duration: breakItem.duration || calculateDuration(startTime, endTime)
35634
+ };
35635
+ });
35636
+ } else if (typeof data.breaks === "object" && data.breaks.breaks) {
35637
+ breaks = data.breaks.breaks.map((breakItem) => {
35638
+ const startTime = breakItem.start || breakItem.startTime || "00:00";
35639
+ const endTime = breakItem.end || breakItem.endTime || "00:00";
35640
+ const calculateDuration = (start, end) => {
35641
+ const [startHour, startMinute] = start.split(":").map(Number);
35642
+ const [endHour, endMinute] = end.split(":").map(Number);
35643
+ let startMinutes = startHour * 60 + startMinute;
35644
+ let endMinutes = endHour * 60 + endMinute;
35645
+ if (endMinutes < startMinutes) {
35646
+ endMinutes += 24 * 60;
35647
+ }
35648
+ return endMinutes - startMinutes;
35649
+ };
35650
+ return {
35651
+ startTime,
35652
+ endTime,
35653
+ duration: breakItem.duration || calculateDuration(startTime, endTime)
35654
+ };
35655
+ });
35656
+ } else if (typeof data.breaks === "string") {
35657
+ try {
35658
+ const parsedBreaks = JSON.parse(data.breaks);
35659
+ if (Array.isArray(parsedBreaks)) {
35660
+ breaks = parsedBreaks.map((breakItem) => ({
35661
+ startTime: breakItem.start || breakItem.startTime || "00:00",
35662
+ endTime: breakItem.end || breakItem.endTime || "00:00",
35663
+ duration: breakItem.duration || 0
35664
+ }));
35665
+ } else if (parsedBreaks.breaks && Array.isArray(parsedBreaks.breaks)) {
35666
+ breaks = parsedBreaks.breaks.map((breakItem) => ({
35667
+ startTime: breakItem.start || breakItem.startTime || "00:00",
35668
+ endTime: breakItem.end || breakItem.endTime || "00:00",
35669
+ duration: breakItem.duration || 0
35670
+ }));
35671
+ }
35672
+ } catch (e) {
35673
+ console.error("Error parsing breaks data:", e);
35674
+ }
35675
+ }
35676
+ }
35677
+ return {
35678
+ startTime: data?.start_time || "08:00",
35679
+ endTime: data?.end_time || "19:00",
35680
+ breaks
35681
+ };
35682
+ } catch (e) {
35683
+ console.error("Exception when loading operating hours:", e);
35684
+ return {
35685
+ startTime: "08:00",
35686
+ endTime: "19:00",
35687
+ breaks: []
35688
+ };
35689
+ }
35690
+ }, [supabase]);
35691
+ React19.useEffect(() => {
35692
+ console.log("[TargetsView] Component mounted/re-rendered", {
35693
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
35694
+ lineIds: lineIds.length,
35695
+ effectiveUserId
35696
+ });
35697
+ return () => {
35698
+ console.log("[TargetsView] Component unmounting");
35699
+ };
35700
+ }, []);
35419
35701
  React19.useEffect(() => {
35420
35702
  let timeoutId;
35421
35703
  let retryCount = 0;
35422
35704
  const MAX_RETRIES2 = 2;
35423
35705
  const LOADING_TIMEOUT = 15e3;
35424
35706
  const fetchInitialData = async () => {
35425
- if (!supabase || lineIds.length === 0) return;
35707
+ if (lineIds.length === 0) return;
35708
+ console.log("[TargetsView] Starting fetchInitialData");
35426
35709
  setIsLoading(true);
35427
35710
  timeoutId = setTimeout(() => {
35428
35711
  console.error("Loading timeout reached");
@@ -35486,10 +35769,32 @@ var TargetsView = ({
35486
35769
  const actionThresholds = await workspaceService.getActionThresholds(
35487
35770
  lineId,
35488
35771
  currentDate,
35489
- selectedShift
35772
+ 0
35773
+ // Always use day shift for initial load
35490
35774
  );
35775
+ const operatingHoursData = await loadOperatingHours(lineId, 0);
35776
+ if (operatingHoursData) {
35777
+ updatedLineWorkspaces[lineId].shiftStartTime = operatingHoursData.startTime;
35778
+ updatedLineWorkspaces[lineId].shiftEndTime = operatingHoursData.endTime;
35779
+ updatedLineWorkspaces[lineId].breaks = operatingHoursData.breaks;
35780
+ updatedLineWorkspaces[lineId].shiftHours = calculateShiftHours2(
35781
+ operatingHoursData.startTime,
35782
+ operatingHoursData.endTime,
35783
+ operatingHoursData.breaks
35784
+ );
35785
+ }
35491
35786
  const mappedWorkspaces = enabledWorkspaces.map((ws) => {
35492
35787
  const threshold = actionThresholds.find((t) => t.workspace_id === ws.id);
35788
+ if (!dbValues[0][lineId]) {
35789
+ dbValues[0][lineId] = {};
35790
+ }
35791
+ if (threshold) {
35792
+ dbValues[0][lineId][ws.id] = {
35793
+ targetPPH: threshold.pph_threshold,
35794
+ targetCycleTime: threshold.ideal_cycle_time,
35795
+ targetDayOutput: threshold.total_day_output
35796
+ };
35797
+ }
35493
35798
  return {
35494
35799
  id: ws.id,
35495
35800
  name: ws.workspace_id,
@@ -35530,90 +35835,7 @@ var TargetsView = ({
35530
35835
  return () => {
35531
35836
  clearTimeout(timeoutId);
35532
35837
  };
35533
- }, [supabase, lineIds, companyId]);
35534
- React19.useCallback(async (shiftId) => {
35535
- try {
35536
- if (!supabase) return;
35537
- const currentDate = getOperationalDate();
35538
- const updatedLineWorkspaces = { ...lineWorkspaces };
35539
- let hasUpdates = false;
35540
- for (const lineId of lineIds) {
35541
- const lineState = lineWorkspaces[lineId];
35542
- if (!lineState || !lineState.factoryId) {
35543
- console.warn(`Skipping line thresholds for ${lineId} as factoryId is not yet available.`);
35544
- continue;
35545
- }
35546
- const currentFactoryId = lineState.factoryId;
35547
- try {
35548
- 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);
35549
- if (thresholdError) {
35550
- console.error(`Error fetching line threshold for line ${lineId}, factory ${currentFactoryId}:`, thresholdError);
35551
- continue;
35552
- }
35553
- let determinedProductId = updatedLineWorkspaces[lineId]?.productId || "";
35554
- if (lineThresholdsRows && lineThresholdsRows.length > 0) {
35555
- if (lineThresholdsRows.length > 1) {
35556
- console.warn(
35557
- `Multiple line_thresholds records found for line ${lineId}, factory ${currentFactoryId}, date ${currentDate}, shift ${selectedShift}. Using product_code from the first record. Rows:`,
35558
- lineThresholdsRows
35559
- );
35560
- }
35561
- determinedProductId = lineThresholdsRows[0].product_code;
35562
- } else {
35563
- console.log(
35564
- `No line_thresholds record found for line ${lineId}, factory ${currentFactoryId}, date ${currentDate}, shift ${selectedShift}. Using existing/default product ID.`
35565
- );
35566
- }
35567
- 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();
35568
- if (hoursError) {
35569
- console.error(`Error fetching operating hours for line ${lineId}:`, hoursError);
35570
- continue;
35571
- }
35572
- const startTime = operatingHours?.start_time || updatedLineWorkspaces[lineId]?.shiftStartTime || "08:00";
35573
- const endTime = operatingHours?.end_time || updatedLineWorkspaces[lineId]?.shiftEndTime || "19:00";
35574
- let breaks = [];
35575
- if (operatingHours?.breaks) {
35576
- if (Array.isArray(operatingHours.breaks)) {
35577
- breaks = operatingHours.breaks.map((breakItem) => ({
35578
- startTime: breakItem.start || breakItem.startTime || "00:00",
35579
- endTime: breakItem.end || breakItem.endTime || "00:00",
35580
- duration: breakItem.duration || calculateShiftHours2(breakItem.start || breakItem.startTime || "00:00", breakItem.end || breakItem.endTime || "00:00", []) * 60
35581
- }));
35582
- } else if (typeof operatingHours.breaks === "object" && operatingHours.breaks.breaks) {
35583
- breaks = operatingHours.breaks.breaks.map((breakItem) => ({
35584
- startTime: breakItem.start || breakItem.startTime || "00:00",
35585
- endTime: breakItem.end || breakItem.endTime || "00:00",
35586
- duration: breakItem.duration || calculateShiftHours2(breakItem.start || breakItem.startTime || "00:00", breakItem.end || breakItem.endTime || "00:00", []) * 60
35587
- }));
35588
- }
35589
- }
35590
- const shiftHours = calculateShiftHours2(startTime, endTime, breaks);
35591
- const currentLineStateFromLoop = updatedLineWorkspaces[lineId];
35592
- if (determinedProductId !== currentLineStateFromLoop?.productId || startTime !== currentLineStateFromLoop?.shiftStartTime || endTime !== currentLineStateFromLoop?.shiftEndTime || shiftHours !== currentLineStateFromLoop?.shiftHours || JSON.stringify(breaks) !== JSON.stringify(currentLineStateFromLoop?.breaks)) {
35593
- updatedLineWorkspaces[lineId] = {
35594
- ...currentLineStateFromLoop || {},
35595
- factoryId: currentFactoryId,
35596
- // Ensure factoryId is preserved
35597
- productId: determinedProductId,
35598
- shiftStartTime: startTime,
35599
- shiftEndTime: endTime,
35600
- breaks,
35601
- shiftHours: Number(shiftHours),
35602
- workspaces: currentLineStateFromLoop?.workspaces || []
35603
- };
35604
- hasUpdates = true;
35605
- }
35606
- } catch (lineError) {
35607
- console.error(`Error processing line ${lineId}:`, lineError);
35608
- }
35609
- }
35610
- if (hasUpdates) {
35611
- setLineWorkspaces(updatedLineWorkspaces);
35612
- }
35613
- } catch (error) {
35614
- console.error("Error in fetchLineThresholds outer try-catch:", error);
35615
- }
35616
- }, [selectedShift, supabase, lineIds, lineWorkspaces, allShiftsData]);
35838
+ }, [lineIds, companyId, loadOperatingHours]);
35617
35839
  const fetchAllShiftsData = React19.useCallback(async (currentWorkspaces) => {
35618
35840
  if (!supabase) return;
35619
35841
  const currentDate = getOperationalDate();
@@ -35623,32 +35845,25 @@ var TargetsView = ({
35623
35845
  1: JSON.parse(JSON.stringify(currentWorkspaces))
35624
35846
  // Deep clone for night shift
35625
35847
  };
35848
+ const newDbValues = { 0: {}, 1: {} };
35626
35849
  for (const shiftId of [0, 1]) {
35627
35850
  for (const lineId of lineIds) {
35628
35851
  try {
35629
- 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();
35630
- if (hoursError) {
35631
- console.error(`Error fetching operating hours for line ${lineId}, shift ${shiftId}:`, hoursError);
35852
+ const operatingHoursData = await loadOperatingHours(lineId, shiftId);
35853
+ if (!operatingHoursData) {
35854
+ console.warn(`No operating hours for line ${lineId}, shift ${shiftId} - using defaults`);
35632
35855
  continue;
35633
35856
  }
35634
- let breaks = [];
35635
- if (operatingHours?.breaks) {
35636
- if (Array.isArray(operatingHours.breaks)) {
35637
- breaks = operatingHours.breaks.map((breakItem) => ({
35638
- startTime: breakItem.start || breakItem.startTime || "00:00",
35639
- endTime: breakItem.end || breakItem.endTime || "00:00",
35640
- duration: breakItem.duration || calculateShiftHours2(breakItem.start || breakItem.startTime || "00:00", breakItem.end || breakItem.endTime || "00:00", []) * 60
35641
- }));
35642
- }
35643
- }
35644
- const startTime = operatingHours?.start_time || "08:00";
35645
- const endTime = operatingHours?.end_time || "19:00";
35857
+ const { startTime, endTime, breaks } = operatingHoursData;
35646
35858
  const shiftHours = calculateShiftHours2(startTime, endTime, breaks);
35647
35859
  const actionThresholds = await workspaceService.getActionThresholds(
35648
35860
  lineId,
35649
35861
  currentDate,
35650
35862
  shiftId
35651
35863
  );
35864
+ if (!newDbValues[shiftId][lineId]) {
35865
+ newDbValues[shiftId][lineId] = {};
35866
+ }
35652
35867
  const existingLine = newAllShiftsData[shiftId][lineId];
35653
35868
  if (existingLine) {
35654
35869
  newAllShiftsData[shiftId][lineId] = {
@@ -35660,6 +35875,11 @@ var TargetsView = ({
35660
35875
  workspaces: existingLine.workspaces.map((ws) => {
35661
35876
  const threshold = actionThresholds.find((t) => t.workspace_id === ws.id);
35662
35877
  if (threshold) {
35878
+ newDbValues[shiftId][lineId][ws.id] = {
35879
+ targetPPH: threshold.pph_threshold,
35880
+ targetCycleTime: threshold.ideal_cycle_time,
35881
+ targetDayOutput: threshold.total_day_output
35882
+ };
35663
35883
  return {
35664
35884
  ...ws,
35665
35885
  targetPPH: threshold.pph_threshold,
@@ -35677,114 +35897,17 @@ var TargetsView = ({
35677
35897
  }
35678
35898
  }
35679
35899
  setAllShiftsData(newAllShiftsData);
35680
- }, [supabase, lineIds]);
35681
- const loadOperatingHours = React19.useCallback(async (lineId, shiftId) => {
35682
- try {
35683
- if (!supabase) return null;
35684
- const { data, error } = await supabase.from("line_operating_hours").select("start_time, end_time, breaks").eq("line_id", lineId).eq("shift_id", shiftId).maybeSingle();
35685
- if (error) {
35686
- if (error.code === "PGRST116") {
35687
- console.log(`No operating hours found for line ${lineId}, shift ${shiftId}`);
35688
- return {
35689
- startTime: "08:00",
35690
- // Default values
35691
- endTime: "19:00",
35692
- breaks: []
35693
- };
35694
- } else {
35695
- console.error("Error fetching operating hours:", error);
35696
- return null;
35697
- }
35698
- }
35699
- let breaks = [];
35700
- if (data?.breaks) {
35701
- if (Array.isArray(data.breaks)) {
35702
- breaks = data.breaks.map((breakItem) => {
35703
- const startTime = breakItem.start || breakItem.startTime || "00:00";
35704
- const endTime = breakItem.end || breakItem.endTime || "00:00";
35705
- const calculateDuration = (start, end) => {
35706
- const [startHour, startMinute] = start.split(":").map(Number);
35707
- const [endHour, endMinute] = end.split(":").map(Number);
35708
- let startMinutes = startHour * 60 + startMinute;
35709
- let endMinutes = endHour * 60 + endMinute;
35710
- if (endMinutes < startMinutes) {
35711
- endMinutes += 24 * 60;
35712
- }
35713
- return endMinutes - startMinutes;
35714
- };
35715
- return {
35716
- startTime,
35717
- endTime,
35718
- duration: breakItem.duration || calculateDuration(startTime, endTime)
35719
- };
35720
- });
35721
- } else if (typeof data.breaks === "object" && data.breaks.breaks) {
35722
- breaks = data.breaks.breaks.map((breakItem) => {
35723
- const startTime = breakItem.start || breakItem.startTime || "00:00";
35724
- const endTime = breakItem.end || breakItem.endTime || "00:00";
35725
- const calculateDuration = (start, end) => {
35726
- const [startHour, startMinute] = start.split(":").map(Number);
35727
- const [endHour, endMinute] = end.split(":").map(Number);
35728
- let startMinutes = startHour * 60 + startMinute;
35729
- let endMinutes = endHour * 60 + endMinute;
35730
- if (endMinutes < startMinutes) {
35731
- endMinutes += 24 * 60;
35732
- }
35733
- return endMinutes - startMinutes;
35734
- };
35735
- return {
35736
- startTime,
35737
- endTime,
35738
- duration: breakItem.duration || calculateDuration(startTime, endTime)
35739
- };
35740
- });
35741
- } else if (typeof data.breaks === "string") {
35742
- try {
35743
- const parsedBreaks = JSON.parse(data.breaks);
35744
- if (Array.isArray(parsedBreaks)) {
35745
- breaks = parsedBreaks.map((breakItem) => ({
35746
- startTime: breakItem.start || breakItem.startTime || "00:00",
35747
- endTime: breakItem.end || breakItem.endTime || "00:00",
35748
- duration: breakItem.duration || 0
35749
- }));
35750
- } else if (parsedBreaks.breaks && Array.isArray(parsedBreaks.breaks)) {
35751
- breaks = parsedBreaks.breaks.map((breakItem) => ({
35752
- startTime: breakItem.start || breakItem.startTime || "00:00",
35753
- endTime: breakItem.end || breakItem.endTime || "00:00",
35754
- duration: breakItem.duration || 0
35755
- }));
35756
- }
35757
- } catch (e) {
35758
- console.error("Error parsing breaks data:", e);
35759
- }
35760
- }
35761
- }
35762
- return {
35763
- startTime: data?.start_time || "08:00",
35764
- endTime: data?.end_time || "19:00",
35765
- breaks
35766
- };
35767
- } catch (e) {
35768
- console.error("Exception when loading operating hours:", e);
35769
- return {
35770
- startTime: "08:00",
35771
- endTime: "19:00",
35772
- breaks: []
35773
- };
35774
- }
35775
- }, [supabase]);
35900
+ setDbValues(newDbValues);
35901
+ }, [supabase, lineIds, loadOperatingHours]);
35776
35902
  const toggleLineDropdown = React19.useCallback((lineId) => {
35777
- setLineWorkspaces((prev) => {
35778
- const newIsOpen = !prev[lineId].isOpen;
35903
+ setDropdownStates((prev) => {
35904
+ const newIsOpen = !prev[lineId];
35779
35905
  if (typeof window !== "undefined") {
35780
35906
  localStorage.setItem(`line_${lineId}_open`, JSON.stringify(newIsOpen));
35781
35907
  }
35782
35908
  return {
35783
35909
  ...prev,
35784
- [lineId]: {
35785
- ...prev[lineId],
35786
- isOpen: newIsOpen
35787
- }
35910
+ [lineId]: newIsOpen
35788
35911
  };
35789
35912
  });
35790
35913
  }, []);
@@ -35833,6 +35956,8 @@ var TargetsView = ({
35833
35956
  }
35834
35957
  };
35835
35958
  const updateWorkspaceTarget = (lineId, workspaceId, field, value) => {
35959
+ const fieldKey = `${lineId}-${workspaceId}-${field}`;
35960
+ setUserEditedFields((prev) => new Set(prev).add(fieldKey));
35836
35961
  setLineWorkspaces((prev) => {
35837
35962
  const shiftHours = prev[lineId].shiftHours;
35838
35963
  return {
@@ -35857,11 +35982,7 @@ var TargetsView = ({
35857
35982
  } else if (field === "targetDayOutput") {
35858
35983
  updates.targetDayOutput = value;
35859
35984
  if (value !== "") {
35860
- const breaks = prev[lineId].breaks;
35861
- const totalBreakMinutes = breaks.reduce((total, b) => total + b.duration, 0);
35862
- const totalBreakHours = totalBreakMinutes / 60;
35863
- const realWorkHours = shiftHours - totalBreakHours;
35864
- const calculatedPPH = Math.round(value / realWorkHours);
35985
+ const calculatedPPH = Math.round(value / shiftHours);
35865
35986
  updates.targetPPH = calculatedPPH;
35866
35987
  } else {
35867
35988
  updates.targetPPH = "";
@@ -35876,62 +35997,35 @@ var TargetsView = ({
35876
35997
  };
35877
35998
  const handleShiftChange = (shiftId) => {
35878
35999
  setSelectedShift(shiftId);
35879
- const loadShiftHours = async () => {
35880
- const updatedLineWorkspaces = { ...allShiftsData[shiftId] };
35881
- let hasUpdates = false;
35882
- for (const lineId of lineIds) {
35883
- try {
35884
- const operatingHours = await loadOperatingHours(lineId, shiftId);
35885
- if (!operatingHours) continue;
35886
- const shiftHours = calculateShiftHours2(
35887
- operatingHours.startTime,
35888
- operatingHours.endTime,
35889
- operatingHours.breaks
35890
- );
35891
- updatedLineWorkspaces[lineId] = {
35892
- ...updatedLineWorkspaces[lineId],
35893
- shiftStartTime: operatingHours.startTime,
35894
- shiftEndTime: operatingHours.endTime,
35895
- breaks: operatingHours.breaks,
35896
- shiftHours: Number(shiftHours),
35897
- workspaces: updatedLineWorkspaces[lineId].workspaces.map((ws) => {
35898
- let updatedPPH = ws.targetPPH;
35899
- if (ws.targetCycleTime !== "") {
35900
- const idealPPH = calculatePPH(
35901
- ws.targetCycleTime,
35902
- operatingHours.breaks,
35903
- Number(shiftHours)
35904
- );
35905
- const shouldUpdatePPH = typeof ws.targetPPH === "string" ? ws.targetPPH === "" : ws.targetPPH === 0 || !ws.targetPPH;
35906
- if (shouldUpdatePPH) {
35907
- updatedPPH = idealPPH;
36000
+ setUserEditedFields(/* @__PURE__ */ new Set());
36001
+ if (dbValues[shiftId] && Object.keys(dbValues[shiftId]).length > 0) {
36002
+ setAllShiftsData((prev) => {
36003
+ const updatedShiftData = { ...prev[shiftId] };
36004
+ for (const lineId of Object.keys(updatedShiftData)) {
36005
+ if (dbValues[shiftId][lineId]) {
36006
+ updatedShiftData[lineId] = {
36007
+ ...updatedShiftData[lineId],
36008
+ workspaces: updatedShiftData[lineId].workspaces.map((ws) => {
36009
+ const dbValue = dbValues[shiftId][lineId][ws.id];
36010
+ if (dbValue) {
36011
+ return {
36012
+ ...ws,
36013
+ targetPPH: dbValue.targetPPH,
36014
+ targetCycleTime: dbValue.targetCycleTime,
36015
+ targetDayOutput: dbValue.targetDayOutput
36016
+ };
35908
36017
  }
35909
- }
35910
- const updatedDayOutput = calculateDayOutput(
35911
- updatedPPH,
35912
- Number(shiftHours),
35913
- operatingHours.breaks
35914
- );
35915
- return {
35916
- ...ws,
35917
- targetPPH: updatedPPH,
35918
- targetDayOutput: updatedDayOutput
35919
- };
35920
- })
35921
- };
35922
- hasUpdates = true;
35923
- } catch (e) {
35924
- console.error(`Exception when loading shift hours for line ${lineId}:`, e);
36018
+ return ws;
36019
+ })
36020
+ };
36021
+ }
35925
36022
  }
35926
- }
35927
- if (hasUpdates) {
35928
- setAllShiftsData((prev) => ({
36023
+ return {
35929
36024
  ...prev,
35930
- [shiftId]: updatedLineWorkspaces
35931
- }));
35932
- }
35933
- };
35934
- loadShiftHours();
36025
+ [shiftId]: updatedShiftData
36026
+ };
36027
+ });
36028
+ }
35935
36029
  };
35936
36030
  const handleActionTypeChange = React19.useCallback((lineId, workspaceId, newActionType) => {
35937
36031
  if (!actionIds) return;
@@ -36021,6 +36115,31 @@ var TargetsView = ({
36021
36115
  throw lineUpsertError;
36022
36116
  }
36023
36117
  console.log(`[handleSaveLine] Successfully upserted line_thresholds for ${lineId}`);
36118
+ setDbValues((prev) => ({
36119
+ ...prev,
36120
+ [selectedShift]: {
36121
+ ...prev[selectedShift],
36122
+ [lineId]: lineDataToSave.workspaces.reduce((acc, ws) => ({
36123
+ ...acc,
36124
+ [ws.id]: {
36125
+ targetPPH: ws.targetPPH,
36126
+ targetCycleTime: ws.targetCycleTime,
36127
+ targetDayOutput: ws.targetDayOutput
36128
+ }
36129
+ }), {})
36130
+ }
36131
+ }));
36132
+ console.log(`[handleSaveLine] Updated dbValues for line ${lineId}, shift ${selectedShift}`);
36133
+ setUserEditedFields((prev) => {
36134
+ const newSet = new Set(prev);
36135
+ lineDataToSave.workspaces.forEach((ws) => {
36136
+ newSet.delete(`${lineId}-${ws.id}-targetPPH`);
36137
+ newSet.delete(`${lineId}-${ws.id}-targetCycleTime`);
36138
+ newSet.delete(`${lineId}-${ws.id}-targetDayOutput`);
36139
+ });
36140
+ return newSet;
36141
+ });
36142
+ console.log(`[handleSaveLine] Cleared user edited fields for line ${lineId}`);
36024
36143
  setSaveSuccess((prev) => ({ ...prev, [lineId]: true }));
36025
36144
  sonner.toast.success(`${lineNames[lineId] || lineId} targets saved successfully`);
36026
36145
  if (onSaveChanges) onSaveChanges(lineId);
@@ -36034,7 +36153,7 @@ var TargetsView = ({
36034
36153
  setSavingLines((prev) => ({ ...prev, [lineId]: false }));
36035
36154
  console.log(`[handleSaveLine] Set savingLines to false for ${lineId} in finally block`);
36036
36155
  }
36037
- }, [supabase, lineWorkspaces, selectedShift, lineNames, onSaveChanges]);
36156
+ }, [supabase, lineWorkspaces, selectedShift, lineNames, onSaveChanges, skuEnabled, dashboardConfig]);
36038
36157
  const handleBulkConfigure = async (updates) => {
36039
36158
  if (!actionIds) return;
36040
36159
  if (updates.productId !== void 0) {
@@ -36082,6 +36201,7 @@ var TargetsView = ({
36082
36201
  isLoading: isLoading || skusLoading,
36083
36202
  lineWorkspaces,
36084
36203
  lineNames,
36204
+ dropdownStates,
36085
36205
  savingLines,
36086
36206
  saveSuccess,
36087
36207
  selectedShift,
@@ -36106,7 +36226,7 @@ var TargetsView = ({
36106
36226
  };
36107
36227
  var TargetsViewWithDisplayNames = withAllWorkspaceDisplayNames(TargetsView);
36108
36228
  var TargetsView_default = TargetsViewWithDisplayNames;
36109
- var AuthenticatedTargetsView = withAuth(TargetsViewWithDisplayNames);
36229
+ var AuthenticatedTargetsView = withAuth(React19__namespace.default.memo(TargetsViewWithDisplayNames));
36110
36230
 
36111
36231
  // src/views/workspace-detail-view.utils.ts
36112
36232
  var formatISTDate2 = (date = /* @__PURE__ */ new Date(), options) => {
@@ -37586,6 +37706,7 @@ exports.AuthProvider = AuthProvider;
37586
37706
  exports.AuthenticatedFactoryView = AuthenticatedFactoryView;
37587
37707
  exports.AuthenticatedHelpView = AuthenticatedHelpView;
37588
37708
  exports.AuthenticatedHomeView = AuthenticatedHomeView;
37709
+ exports.AuthenticatedShiftsView = AuthenticatedShiftsView;
37589
37710
  exports.AuthenticatedTargetsView = AuthenticatedTargetsView;
37590
37711
  exports.BarChart = BarChart;
37591
37712
  exports.BaseHistoryCalendar = BaseHistoryCalendar;